You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by is...@apache.org on 2018/10/09 14:47:32 UTC

[17/21] ignite git commit: IGNITE-7783: PHP thin client

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
new file mode 100644
index 0000000..520063c
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
@@ -0,0 +1,490 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+use Ds\Map;
+use Ds\Set;
+use Brick\Math\BigDecimal;
+use Brick\Math\BigInteger;
+use Apache\Ignite\Internal\Connection\ClientFailoverSocket;
+use Apache\Ignite\Internal\Utils\ArgumentChecker;
+use Apache\Ignite\Type\ObjectType;
+use Apache\Ignite\Type\CollectionObjectType;
+use Apache\Ignite\Type\ComplexObjectType;
+use Apache\Ignite\Type\MapObjectType;
+use Apache\Ignite\Data\BinaryObject;
+use Apache\Ignite\Data\Date;
+use Apache\Ignite\Data\EnumItem;
+use Apache\Ignite\Data\Time;
+use Apache\Ignite\Data\Timestamp;
+use Apache\Ignite\Exception\ClientException;
+
+class BinaryCommunicator
+{
+    private $socket;
+    private $typeStorage;
+    
+    public function __construct(ClientFailoverSocket $socket)
+    {
+        $this->socket = $socket;
+        $this->typeStorage = new BinaryTypeStorage($this);
+    }
+
+    public function send(int $opCode, ?callable $payloadWriter, callable $payloadReader = null): void
+    {
+        $this->socket->send($opCode, $payloadWriter, $payloadReader);
+    }
+    
+    public function getTypeStorage(): BinaryTypeStorage
+    {
+        return $this->typeStorage;
+    }
+
+    public static function readString(MessageBuffer $buffer): ?string
+    {
+        $typeCode = $buffer->readByte();
+        BinaryUtils::checkTypesCompatibility(ObjectType::STRING, $typeCode);
+        if ($typeCode === ObjectType::NULL) {
+            return null;
+        }
+        return $buffer->readString();
+    }
+
+    public static function writeString(MessageBuffer $buffer, ?string $str): void
+    {
+        if ($str === null) {
+            $buffer->writeByte(ObjectType::NULL);
+        } else {
+            $buffer->writeByte(ObjectType::STRING);
+            $buffer->writeString($str);
+        }
+    }
+
+    public function readObject(MessageBuffer $buffer, $expectedType = null)
+    {
+        $typeCode = $buffer->readByte();
+        BinaryUtils::checkTypesCompatibility($expectedType, $typeCode);
+        return $this->readTypedObject($buffer, $typeCode, $expectedType);
+    }
+
+    public function readStringArray(MessageBuffer $buffer): array
+    {
+        return $this->readTypedObject($buffer, ObjectType::STRING_ARRAY);
+    }
+
+    public function writeObject(MessageBuffer $buffer, $object, $objectType = null, bool $writeObjectType = true): void
+    {
+        BinaryUtils::checkCompatibility($object, $objectType);
+        if ($object === null) {
+            $buffer->writeByte(ObjectType::NULL);
+            return;
+        }
+
+        $objectType = $objectType ? $objectType : BinaryUtils::calcObjectType($object);
+        $objectTypeCode = BinaryUtils::getTypeCode($objectType);
+
+        if ($writeObjectType) {
+            $buffer->writeByte($objectTypeCode);
+        }
+        switch ($objectTypeCode) {
+            case ObjectType::BYTE:
+            case ObjectType::SHORT:
+            case ObjectType::INTEGER:
+            case ObjectType::LONG:
+            case ObjectType::FLOAT:
+            case ObjectType::DOUBLE:
+                $buffer->writeNumber($object, $objectTypeCode);
+                break;
+            case ObjectType::CHAR:
+                $buffer->writeChar($object);
+                break;
+            case ObjectType::BOOLEAN:
+                $buffer->writeBoolean($object);
+                break;
+            case ObjectType::STRING:
+                $buffer->writeString($object);
+                break;
+            case ObjectType::UUID:
+                $this->writeUUID($buffer, $object);
+                break;
+            case ObjectType::DATE:
+                $this->writeDate($buffer, $object);
+                break;
+            case ObjectType::ENUM:
+                $this->writeEnum($buffer, $object);
+                break;
+            case ObjectType::DECIMAL:
+                $this->writeDecimal($buffer, $object);
+                break;
+            case ObjectType::TIME:
+                $this->writeTime($buffer, $object);
+                break;
+            case ObjectType::TIMESTAMP:
+                $this->writeTimestamp($buffer, $object);
+                break;
+            case ObjectType::BYTE_ARRAY:
+            case ObjectType::SHORT_ARRAY:
+            case ObjectType::INTEGER_ARRAY:
+            case ObjectType::LONG_ARRAY:
+            case ObjectType::FLOAT_ARRAY:
+            case ObjectType::DOUBLE_ARRAY:
+            case ObjectType::CHAR_ARRAY:
+            case ObjectType::BOOLEAN_ARRAY:
+            case ObjectType::STRING_ARRAY:
+            case ObjectType::UUID_ARRAY:
+            case ObjectType::DATE_ARRAY:
+            case ObjectType::OBJECT_ARRAY:
+            case ObjectType::ENUM_ARRAY:
+            case ObjectType::DECIMAL_ARRAY:
+            case ObjectType::TIMESTAMP_ARRAY:
+            case ObjectType::TIME_ARRAY:
+                $this->writeArray($buffer, $object, $objectType, $objectTypeCode);
+                break;
+            case ObjectType::COLLECTION:
+                $this->writeCollection($buffer, $object, $objectType);
+                break;
+            case ObjectType::MAP:
+                $this->writeMap($buffer, $object, $objectType);
+                break;
+            case ObjectType::BINARY_OBJECT:
+                $this->writeBinaryObject($buffer, $object);
+                break;
+            case ObjectType::COMPLEX_OBJECT:
+                $this->writeComplexObject($buffer, $object, $objectType);
+                break;
+            default:
+                BinaryUtils::unsupportedType($objectType);
+        }
+    }
+
+    public function readTypedObject(MessageBuffer $buffer, int $objectTypeCode, $expectedType = null)
+    {
+        switch ($objectTypeCode) {
+            case ObjectType::BYTE:
+            case ObjectType::SHORT:
+            case ObjectType::INTEGER:
+            case ObjectType::LONG:
+            case ObjectType::FLOAT:
+            case ObjectType::DOUBLE:
+                return $buffer->readNumber($objectTypeCode);
+            case ObjectType::CHAR:
+                return $buffer->readChar();
+            case ObjectType::BOOLEAN:
+                return $buffer->readBoolean();
+            case ObjectType::STRING:
+                return $buffer->readString();
+            case ObjectType::UUID:
+                return $this->readUUID($buffer);
+            case ObjectType::DATE:
+                return $this->readDate($buffer);
+            case ObjectType::ENUM:
+            case ObjectType::BINARY_ENUM:
+                return $this->readEnum($buffer);
+            case ObjectType::DECIMAL:
+                return $this->readDecimal($buffer);
+            case ObjectType::TIME:
+                return $this->readTime($buffer);
+            case ObjectType::TIMESTAMP:
+                return $this->readTimestamp($buffer);
+            case ObjectType::BYTE_ARRAY:
+            case ObjectType::SHORT_ARRAY:
+            case ObjectType::INTEGER_ARRAY:
+            case ObjectType::LONG_ARRAY:
+            case ObjectType::FLOAT_ARRAY:
+            case ObjectType::DOUBLE_ARRAY:
+            case ObjectType::CHAR_ARRAY:
+            case ObjectType::BOOLEAN_ARRAY:
+            case ObjectType::STRING_ARRAY:
+            case ObjectType::UUID_ARRAY:
+            case ObjectType::DATE_ARRAY:
+            case ObjectType::OBJECT_ARRAY:
+            case ObjectType::ENUM_ARRAY:
+            case ObjectType::DECIMAL_ARRAY:
+            case ObjectType::TIMESTAMP_ARRAY:
+            case ObjectType::TIME_ARRAY:
+                return $this->readArray($buffer, $objectTypeCode, $expectedType);
+            case ObjectType::COLLECTION:
+                return $this->readCollection($buffer, $expectedType);
+            case ObjectType::MAP:
+                return $this->readMap($buffer, $expectedType);
+            case ObjectType::BINARY_OBJECT:
+                return $this->readBinaryObject($buffer, $expectedType);
+            case ObjectType::NULL:
+                return null;
+            case ObjectType::COMPLEX_OBJECT:
+                return $this->readComplexObject($buffer, $expectedType);
+            default:
+                BinaryUtils::unsupportedType($objectTypeCode);
+        }
+        return null;
+    }
+
+    private function readDate(MessageBuffer $buffer): Date
+    {
+        return new Date($buffer->readLong());
+    }
+
+    private function readTime(MessageBuffer $buffer): Time
+    {
+        return new Time($buffer->readLong());
+    }
+
+    private function readTimestamp(MessageBuffer $buffer): Timestamp
+    {
+        return new Timestamp($buffer->readLong(), $buffer->readInteger());
+    }
+
+    private function readUUID(MessageBuffer $buffer): array
+    {
+        $result = [];
+        for ($i = 0; $i < BinaryUtils::getSize(ObjectType::UUID); $i++) {
+            array_push($result, $buffer->readByte(false));
+        }
+        return $result;
+    }
+
+    private function readEnum(MessageBuffer $buffer): EnumItem
+    {
+        $enumItem = new EnumItem($buffer->readInteger());
+        $ordinal = $buffer->readInteger();
+        $enumItem->setOrdinal($ordinal);
+        $type = $this->typeStorage->getType($enumItem->getTypeId());
+        if (!$type->isEnum() || !$type->getEnumValues() || count($type->getEnumValues()) <= $ordinal) {
+            BinaryUtils::serializationError(false, 'EnumItem can not be deserialized: type mismatch');
+        }
+        $enumValues = $type->getEnumValues();
+        $enumItem->setName($enumValues[$ordinal][0]);
+        $enumItem->setValue($enumValues[$ordinal][1]);
+        return $enumItem;
+    }
+
+    private function readDecimal(MessageBuffer $buffer): BigDecimal
+    {
+        $scale = $buffer->readInteger();
+        $value = $buffer->readString(false);
+        $isNegative = (ord($value[0]) & 0x80) !== 0;
+        if ($isNegative) {
+            $value[0] = chr(ord($value[0]) & 0x7F);
+        }
+        $hexValue = '';
+        for ($i = 0; $i < strlen($value); $i++) {
+            $hex = dechex(ord($value[$i]));
+            if (strlen($hex) < 2) {
+                $hex = str_repeat('0', 2 - strlen($hex)) . $hex;
+            }
+            $hexValue .= $hex;
+        }
+        $result = BigDecimal::ofUnscaledValue(BigInteger::parse($hexValue, 16), $scale >= 0 ? $scale : 0);
+        if ($isNegative) {
+            $result = $result->negated();
+        }
+        if ($scale < 0) {
+            $result = $result->multipliedBy((BigInteger::of(10))->power(-$scale));
+        }
+        return $result;
+    }
+
+    private function readArray(MessageBuffer $buffer, int $arrayTypeCode, $arrayType): array
+    {
+        if ($arrayTypeCode === ObjectType::OBJECT_ARRAY) {
+            $buffer->readInteger();
+        }
+        $length = $buffer->readInteger();
+        $elementType = BinaryUtils::getArrayElementType($arrayType ? $arrayType : $arrayTypeCode);
+        $keepElementType = $elementType === null ? true : TypeInfo::getTypeInfo($arrayTypeCode)->keepElementType();
+        $result = array();
+        for ($i = 0; $i < $length; $i++) {
+            array_push(
+                $result,
+                $keepElementType ?
+                    $this->readObject($buffer, $elementType) :
+                    $this->readTypedObject($buffer, $elementType));
+        }
+        return $result;
+    }
+
+    private function readCollection(MessageBuffer $buffer, CollectionObjectType $expectedColType = null)
+    {
+        $size = $buffer->readInteger();
+        $subType = $buffer->readByte();
+        $isSet = CollectionObjectType::isSet($subType);
+        $result = $isSet ? new Set() : [];
+        for ($i = 0; $i < $size; $i++) {
+            $element = $this->readObject($buffer, $expectedColType ? $expectedColType->getElementType() : null);
+            if ($isSet) {
+                $result->add($element);
+            } else {
+                array_push($result, $element);
+            }
+        }
+        return $result;
+    }
+
+    private function readMap(MessageBuffer $buffer, MapObjectType $expectedMapType = null): Map
+    {
+        $size = $buffer->readInteger();
+        // map sub-type
+        $buffer->readByte();
+        $result = new Map();
+        $result->allocate($size);
+        for ($i = 0; $i < $size; $i++) {
+            $key = $this->readObject($buffer, $expectedMapType ? $expectedMapType->getKeyType() : null);
+            $value = $this->readObject($buffer, $expectedMapType ? $expectedMapType->getValueType() : null);
+            $result->put($key, $value);
+        }
+        return $result;
+    }
+
+    private function readBinaryObject(MessageBuffer $buffer, ?ComplexObjectType $expectedType): object
+    {
+        $size = $buffer->readInteger();
+        $startPos = $buffer->getPosition();
+        $buffer->setPosition($startPos + $size);
+        $offset = $buffer->readInteger();
+        $endPos = $buffer->getPosition();
+        $buffer->setPosition($startPos + $offset);
+        $result = $this->readObject($buffer, $expectedType);
+        $buffer->setPosition($endPos);
+        return $result;
+    }
+
+    private function readComplexObject(MessageBuffer $buffer, ?ComplexObjectType $expectedType): object
+    {
+        $buffer->setPosition($buffer->getPosition() - 1);
+        $binaryObject = BinaryObject::fromBuffer($this, $buffer);
+        return $expectedType ? $binaryObject->toObject($expectedType) : $binaryObject;
+    }
+
+    private function writeDate(MessageBuffer $buffer, Date $date): void
+    {
+        $buffer->writeLong($date->getMillis());
+    }
+
+    private function writeTime(MessageBuffer $buffer, Time $time): void
+    {
+        $buffer->writeLong($time->getMillis());
+    }
+
+    private function writeTimestamp(MessageBuffer $buffer, Timestamp $timestamp): void
+    {
+        $buffer->writeLong($timestamp->getMillis());
+        $buffer->writeInteger($timestamp->getNanos());
+    }
+
+    private function writeUUID(MessageBuffer $buffer, array $value): void
+    {
+        for ($i = 0; $i < count($value); $i++) {
+            $buffer->writeByte($value[$i], false);
+        }
+    }
+
+    private function writeEnum(MessageBuffer $buffer, EnumItem $enumValue): void
+    {
+        $buffer->writeInteger($enumValue->getTypeId());
+        if ($enumValue->getOrdinal() !== null) {
+            $buffer->writeInteger($enumValue->getOrdinal());
+            return;
+        } elseif ($enumValue->getName() !== null || $enumValue->getValue() !== null) {
+            $type = $this->typeStorage->getType($enumValue->getTypeId());
+            if ($type && $type->isEnum()) {
+                $enumValues = $type->getEnumValues();
+                if ($enumValues) {
+                    for ($i = 0; $i < count($enumValues); $i++) {
+                        if ($enumValue->getName() === $enumValues[$i][0] ||
+                            $enumValue->getValue() === $enumValues[$i][1]) {
+                            $buffer->writeInteger($i);
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+        ArgumentChecker::illegalArgument('Proper ordinal, name or value must be specified for EnumItem');
+    }
+
+    private function writeDecimal(MessageBuffer $buffer, BigDecimal $decimal): void
+    {
+        $scale = $decimal->getScale();
+        $isNegative = $decimal->isNegative();
+        $hexValue = $decimal->getUnscaledValue()->abs()->toBase(16);
+        $hexValue = ((strlen($hexValue) % 2 !== 0) ? '000' : '00') . $hexValue;
+        if ($isNegative) {
+            $hexValue[0] = '8';
+        }
+        $value = '';
+        for ($i = 0; $i < strlen($hexValue); $i += 2) {
+            $value .= chr(hexdec(substr($hexValue, $i, 2)));
+        }
+        $buffer->writeInteger($scale);
+        $buffer->writeString($value, false);
+    }
+
+    private function writeArray(MessageBuffer $buffer, array $array, $arrayType, int $arrayTypeCode): void
+    {
+        $elementType = BinaryUtils::getArrayElementType($arrayType);
+        $keepElementType = !$elementType || TypeInfo::getTypeInfo($arrayTypeCode)->keepElementType();
+        if ($arrayTypeCode === ObjectType::OBJECT_ARRAY) {
+            $typeId = -1;
+            if ($elementType instanceof ComplexObjectType) {
+                $typeName = BinaryTypeBuilder::calcTypeName($elementType, count($array) > 0 ? $array[0] : null);
+                if ($typeName) {
+                    $typeId = BinaryType::calculateId($typeName);
+                }
+            }
+            $buffer->writeInteger($typeId);
+        }
+        $buffer->writeInteger(count($array));
+        foreach ($array as $elem) {
+            $this->writeObject($buffer, $elem, $elementType, $keepElementType);
+        }
+    }
+
+    private function writeCollection(MessageBuffer $buffer, $collection, CollectionObjectType $collectionType): void
+    {
+        $buffer->writeInteger($collection instanceof Set ? $collection->count() : count($collection));
+        $buffer->writeByte($collectionType->getSubType());
+        foreach ($collection as $element) {
+            $this->writeObject($buffer, $element, $collectionType->getElementType());
+        }
+    }
+
+    private function writeMap(MessageBuffer $buffer, $map, MapObjectType $mapType): void
+    {
+        if (!($map instanceof Map)) {
+            $map = new Map($map);
+        }
+        $buffer->writeInteger($map->count());
+        $buffer->writeByte($mapType->getSubType());
+        foreach ($map->pairs() as $pair) {
+            $this->writeObject($buffer, $pair->key, $mapType->getKeyType());
+            $this->writeObject($buffer, $pair->value, $mapType->getValueType());
+        }
+    }
+
+    private function writeBinaryObject(MessageBuffer $buffer, BinaryObject $binaryObject): void
+    {
+        $buffer->setPosition($buffer->getPosition() - 1);
+        $binaryObject->write($this, $buffer);
+    }
+
+    private function writeComplexObject(MessageBuffer $buffer, object $object, ?ComplexObjectType $objectType): void
+    {
+        $this->writeBinaryObject($buffer, BinaryObject::fromObject($object, $objectType));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryField.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryField.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryField.php
new file mode 100644
index 0000000..8fdeab3
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryField.php
@@ -0,0 +1,78 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+class BinaryField
+{
+    private $name;
+    private $id;
+    private $typeCode;
+    
+    public function __construct(string $name = null, int $typeCode = 0)
+    {
+        $this->name = $name;
+        $this->id = BinaryField::calculateId($name);
+        $this->typeCode = $typeCode;
+    }
+    
+    public static function calculateId(?string $name): int
+    {
+        return BinaryUtils::hashCodeLowerCase($name);
+    }
+
+    public function getId(): int
+    {
+        return $this->id;
+    }
+
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    public function getTypeCode(): int
+    {
+        return $this->typeCode;
+    }
+
+    public function isValid(): bool
+    {
+        return $this->name !== null;
+    }
+
+    public function write(MessageBuffer $buffer): void
+    {
+        // field name
+        BinaryCommunicator::writeString($buffer, $this->name);
+        // type code
+        $buffer->writeInteger($this->typeCode);
+        // field id
+        $buffer->writeInteger($this->id);
+    }
+
+    public function read(MessageBuffer $buffer): void
+    {
+        // field name
+        $this->name = BinaryCommunicator::readString($buffer);
+        // type code
+        $this->typeCode = $buffer->readInteger();
+        // field id
+        $this->id = $buffer->readInteger();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryObjectField.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryObjectField.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryObjectField.php
new file mode 100644
index 0000000..cbe24d2
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryObjectField.php
@@ -0,0 +1,113 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+use Apache\Ignite\Type\ObjectType;
+use Apache\Ignite\Exception\ClientException;
+
+class BinaryObjectField
+{
+    private $name;
+    private $id;
+    private $value;
+    private $type;
+    private $typeCode;
+
+    private $communicator;
+    private $buffer;
+    private $offset;
+    private $length;
+
+    public function __construct(?string $name, $value = null, $type = null)
+    {
+        $this->name = $name;
+        $this->id = BinaryField::calculateId($name);
+        $this->value = $value;
+        $this->type = $type;
+        if (!$type && $value !== null) {
+            $this->type = BinaryUtils::calcObjectType($value);
+        }
+        $this->typeCode = $this->type ? BinaryUtils::getTypeCode($this->type) : 0;
+    }
+    
+    public function getId(): int
+    {
+        return $this->id;
+    }
+    
+    public function getTypeCode(): int
+    {
+        return $this->typeCode;
+    }
+
+    public function getValue($type = null)
+    {
+        if ($this->buffer && ($this->value === null || $this->type !== $type)) {
+            $this->buffer->setPosition($this->offset);
+            $this->value = $this->communicator->readObject($this->buffer, $type);
+            $this->type = $type;
+        }
+        return $this->value;
+    }
+
+    public static function fromBuffer(BinaryCommunicator $communicator, MessageBuffer $buffer, int $offset, int $length, int $id): BinaryObjectField
+    {
+        $result = new BinaryObjectField(null);
+        $result->id = $id;
+        $result->communicator = $communicator;
+        $result->buffer = $buffer;
+        $result->offset = $offset;
+        $result->length = $length;
+        return $result;
+    }
+
+    public function writeValue(BinaryCommunicator $communicator, MessageBuffer $buffer, int $expectedTypeCode): void
+    {
+        $offset = $buffer->getPosition();
+        if ($this->buffer && $this->communicator === $communicator) {
+            $buffer->writeBuffer($this->buffer, $this->offset, $this->length);
+        } else {
+            if (!$this->value) {
+                $this->getValue($expectedTypeCode);
+            }
+            BinaryUtils::checkCompatibility($this->value, $expectedTypeCode);
+            $communicator->writeObject($buffer, $this->value, $this->type);
+        }
+        $this->communicator = $communicator;
+        $this->buffer = $buffer;
+        $this->length = $buffer->getPosition() - $offset;
+        $this->offset = $offset;
+    }
+
+    public function writeOffset(MessageBuffer $buffer, int $headerStartPos, int $offsetType): void
+    {
+        $buffer->writeNumber($this->offset - $headerStartPos, $offsetType, false);
+    }
+
+    public function getOffsetType(int $headerStartPos): int
+    {
+        $offset = $this->offset - $headerStartPos;
+        if ($offset < TypeInfo::getTypeInfo(ObjectType::BYTE)->getMaxUnsignedValue()) {
+            return ObjectType::BYTE;
+        } elseif ($offset < TypeInfo::getTypeInfo(ObjectType::SHORT)->getMaxUnsignedValue()) {
+            return ObjectType::SHORT;
+        }
+        return ObjectType::INTEGER;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinarySchema.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinarySchema.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinarySchema.php
new file mode 100644
index 0000000..cff6ae8
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinarySchema.php
@@ -0,0 +1,145 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+use Brick\Math\BigInteger;
+use Apache\Ignite\Type\ObjectType;
+
+class BinarySchema
+{
+    /** FNV1 hash offset basis. */
+    const FNV1_OFFSET_BASIS = 0x811C9DC5;
+    /** FNV1 hash prime. */
+    const FNV1_PRIME = 0x01000193;
+    
+    private $id;
+    private $fieldIds;
+    private $isValid;
+    
+    public function __construct()
+    {
+        $this->id = BinarySchema::FNV1_OFFSET_BASIS;
+        $this->fieldIds = [];
+        $this->isValid = true;
+    }
+    
+    public function getId(): int
+    {
+        return $this->id;
+    }
+    
+    public function getFieldIds(): array
+    {
+        return array_keys($this->fieldIds);
+    }
+
+    public function finalize(): void
+    {
+        if (!$this->isValid) {
+            $this->id = BinarySchema::FNV1_OFFSET_BASIS;
+            foreach ($this->fieldIds as $key => $value) {
+                BinarySchema::updateSchemaId($key);
+            }
+            $this->isValid = true;
+        }
+    }
+
+    public function cloneSchema(): BinarySchema
+    {
+        $result = new BinarySchema();
+        $result->id = $this->id;
+        $result->fieldIds = [];
+        foreach($this->fieldIds as $key => $value) {
+            $result->fieldIds[$key] = $value;
+        }
+        $result->isValid = $this->isValid;
+        return $result;
+    }
+    
+    public function addField(int $fieldId): void
+    {
+        if (!$this->hasField($fieldId)) {
+            $this->fieldIds[$fieldId] = true;
+            if ($this->isValid) {
+                $this->updateSchemaId($fieldId);
+            }
+        }
+    }
+
+    public function removeField(int $fieldId): void
+    {
+        if ($this->hasField($fieldId)) {
+            unset($this->fieldIds[$fieldId]);
+            $this->isValid = false;
+        }
+    }
+
+    public function hasField(int $fieldId): bool
+    {
+        return array_key_exists($fieldId, $this->fieldIds);
+    }
+
+    private function updateSchemaId(int $fieldId): void
+    {
+        $this->updateSchemaIdPart($fieldId & 0xFF);
+        $this->updateSchemaIdPart(($fieldId >> 8) & 0xFF);
+        $this->updateSchemaIdPart(($fieldId >> 16) & 0xFF);
+        $this->updateSchemaIdPart(($fieldId >> 24) & 0xFF);
+        $this->id = BinaryUtils::intVal32($this->id);
+    }
+
+    private function updateSchemaIdPart(int $fieldIdPart): void
+    {
+        $this->id = $this->id ^ $fieldIdPart;
+        if (BinaryUtils::$is32BitInt) {
+            $hexValue = BinaryUtils::getLongHex(BigInteger::of(abs($this->id))->multipliedBy(BinarySchema::FNV1_PRIME), $this->id < 0);
+            $len = strlen($hexValue);
+            $size = TypeInfo::getTypeInfo(ObjectType::INTEGER)->getSize() * 2;
+            $this->id = hexdec($len > $size ? substr($hexValue, $len - $size) : $hexValue);
+        } else {
+            $this->id = $this->id * BinarySchema::FNV1_PRIME;
+        }
+        $this->id &= 0xFFFFFFFF; // Convert to 32bit integer
+    }
+
+    public function write(MessageBuffer $buffer): void
+    {
+        $this->finalize();
+        // schema id
+        $buffer->writeInteger($this->id);
+        // fields count
+        $buffer->writeInteger(count($this->fieldIds));
+        // field ids
+        foreach ($this->fieldIds as $key => $value) {
+            $buffer->writeInteger($key);
+        }
+    }
+
+    public function read(MessageBuffer $buffer): void
+    {
+        // schema id
+        $this->id = $buffer->readInteger();
+        // fields count
+        $fieldsCount = $buffer->readInteger();
+        // field ids
+        for ($i = 0; $i < $fieldsCount; $i++) {
+            $this->fieldIds[$buffer->readInteger()] = true;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryType.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryType.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryType.php
new file mode 100644
index 0000000..8cd1c82
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryType.php
@@ -0,0 +1,233 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+class BinaryType
+{
+    private $name;
+    private $id;
+    private $fields;
+    private $schemas;
+    private $isEnum;
+    private $enumValues;
+    
+    public function __construct(?string $name)
+    {
+        $this->name = $name;
+        $this->id = BinaryType::calculateId($name);
+        $this->fields = [];
+        $this->schemas = [];
+        $this->isEnum = false;
+        $this->enumValues = null;
+    }
+    
+    public static function calculateId($name)
+    {
+        return BinaryUtils::hashCodeLowerCase($name);
+    }
+
+    public function getId(): int
+    {
+        return $this->id;
+    }
+    
+    public function setId(int $id): void
+    {
+        $this->id = $id;
+    }
+    
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    public function getFields(): array
+    {
+        return array_values($this->fields);
+    }
+
+    public function getField(int $fieldId): ?BinaryField
+    {
+        return $this->hasField($fieldId) ? $this->fields[$fieldId] : null;
+    }
+
+    public function hasField(int $fieldId): bool
+    {
+        return array_key_exists($fieldId, $this->fields);
+    }
+
+    public function removeField(int $fieldId): void
+    {
+        if ($this->hasField($fieldId)) {
+            unset($this->fields[$fieldId]);
+        }
+    }
+
+    public function setField(BinaryField $field): void
+    {
+        $this->fields[$field->getId()] = $field;
+    }
+
+    public function hasSchema(int $schemaId): bool
+    {
+        return array_key_exists($schemaId, $this->schemas);
+    }
+
+    public function addSchema(BinarySchema $schema): void
+    {
+        if (!$this->hasSchema($schema->getId())) {
+            $this->schemas[$schema->getId()] = $schema;
+        }
+    }
+
+    public function getSchema($schemaId): ?BinarySchema
+    {
+        return $this->hasSchema($schemaId) ? $this->schemas[$schemaId] : null;
+    }
+    
+    public function isEnum(): bool
+    {
+        return $this->isEnum;
+    }
+    
+    public function getEnumValues(): ?array
+    {
+        return $this->enumValues;
+    }
+
+    public function merge(BinaryType $binaryType, BinarySchema $binarySchema): void
+    {
+        foreach ($binaryType->getFields() as $field) {
+            $fieldId = $field->getId();
+            if ($this->hasField($fieldId)) {
+                if ($this->getField($fieldId)->getTypeCode() !== $field->getTypeCode()) {
+                    BinaryUtils::serializationError(
+                        true, sprintf('type conflict for field "%s" of complex object type "%s"',
+                        $field->getName(), $this->name));
+                }
+            } else {
+                $this->setField($field);
+            }
+        }
+        $this->addSchema($binarySchema);
+    }
+
+    public function cloneType(): BinaryType
+    {
+        $result = new BinaryType(null);
+        $result->name = $this->name;
+        $result->id = $this->id;
+        $result->fields = [];
+        foreach($this->fields as $key => $value) {
+            $result->fields[$key] = $value;
+        }
+        $result->schemas = [];
+        foreach($this->schemas as $key => $value) {
+            $result->schemas[$key] = $value;
+        }
+        $result->isEnum = $this->isEnum;
+        return $result;
+    }
+
+    public function isValid(): bool
+    {
+        foreach ($this->fields as $field) {
+            if (!$field->isValid()) {
+                return false;
+            }
+        }
+        return $this->name !== null;
+    }
+
+    public function write(MessageBuffer $buffer): void
+    {
+        // type id
+        $buffer->writeInteger($this->id);
+        // type name
+        BinaryCommunicator::writeString($buffer, $this->name);
+        // affinity key field name
+        BinaryCommunicator::writeString($buffer, null);
+        // fields count
+        $buffer->writeInteger(count($this->fields));
+        // fields
+        foreach ($this->fields as $field) {
+            $field->write($buffer);
+        }
+        $this->writeEnum($buffer);
+        // schemas count
+        $buffer->writeInteger(count($this->schemas));
+        foreach ($this->schemas as $schema) {
+            $schema->write($buffer);
+        }
+    }
+
+    private function writeEnum(MessageBuffer $buffer): void
+    {
+        $buffer->writeBoolean($this->isEnum);
+        if ($this->isEnum) {
+            $length = $this->enumValues ? count($this->enumValues) : 0;
+            $buffer->writeInteger($length);
+            if ($length > 0) {
+                foreach ($this->enumValues as $key => $value) {
+                    BinaryCommunicator::writeString($buffer, $key);
+                    $buffer->writeInteger($value);
+                }
+            }
+        }
+    }
+
+    public function read(MessageBuffer $buffer): void
+    {
+        // type id
+        $this->id = $buffer->readInteger();
+        // type name
+        $this->name = BinaryCommunicator::readString($buffer);
+        // affinity key field name
+        BinaryCommunicator::readString($buffer);
+        // fields count
+        $fieldsCount = $buffer->readInteger();
+        // fields
+        for ($i = 0; $i < $fieldsCount; $i++) {
+            $field = new BinaryField();
+            $field->read($buffer);
+            $this->setField($field);
+        }
+        $this->readEnum($buffer);
+        // schemas count
+        $schemasCount = $buffer->readInteger();
+        // schemas
+        for ($i = 0; $i < $schemasCount; $i++) {
+            $schema = new BinarySchema();
+            $schema->read($buffer);
+            $this->addSchema($schema);
+        }
+    }
+
+    private function readEnum(MessageBuffer $buffer): void
+    {
+        $this->isEnum = $buffer->readBoolean();
+        if ($this->isEnum) {
+            $valuesCount = $buffer->readInteger();
+            $this->enumValues = [];
+            for ($i = 0; $i < $valuesCount; $i++) {
+                array_push($this->enumValues, [BinaryCommunicator::readString($buffer), $buffer->readInteger()]);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryTypeBuilder.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryTypeBuilder.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryTypeBuilder.php
new file mode 100644
index 0000000..f13eddb
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryTypeBuilder.php
@@ -0,0 +1,207 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+use Apache\Ignite\Type\ComplexObjectType;
+
+class BinaryTypeBuilder
+{
+    private $type;
+    private $schema;
+    private $fromStorage;
+    
+    private function __construct()
+    {
+        $this->type = null;
+        $this->schema = null;
+        $this->fromStorage = false;
+    }
+            
+    public static function fromTypeName(string $typeName): BinaryTypeBuilder
+    {
+        $result = new BinaryTypeBuilder();
+        $result->init($typeName);
+        return $result;
+    }
+
+    public static function fromTypeId(BinaryCommunicator $communicator, int $typeId, ?int $schemaId): BinaryTypeBuilder
+    {
+        $result = new BinaryTypeBuilder();
+        $type = $communicator->getTypeStorage()->getType($typeId, $schemaId);
+        if ($type) {
+            $result->type = $type;
+            if ($schemaId !== null) {
+                $result->schema = $type->getSchema($schemaId);
+                if (!$result->schema) {
+                    BinaryUtils::serializationError(
+                        false,
+                        sprintf('schema id "%d" specified for complex object of type "%s" not found', $schemaId, $type->getName()));
+                }
+                $result->fromStorage = true;
+            } else {
+                $result->schema = new BinarySchema();
+            }
+            return $result;
+        }
+        $result->init(null);
+        $result->getType()->setId($typeId);
+        return $result;
+    }
+
+    public static function fromObject(object $object, ComplexObjectType $complexObjectType = null): BinaryTypeBuilder
+    {
+        if ($complexObjectType) {
+            return BinaryTypeBuilder::fromComplexObjectType($complexObjectType, $object);
+        } else {
+            $result = new BinaryTypeBuilder();
+            $result->fromComplexObject(new ComplexObjectType(), $object);
+            return $result;
+        }
+    }
+
+    public static function fromComplexObjectType(ComplexObjectType $complexObjectType, object $object): BinaryTypeBuilder
+    {
+        $result = new BinaryTypeBuilder();
+        $typeInfo = BinaryTypeStorage::getByComplexObjectType($complexObjectType);
+        if ($typeInfo) {
+            $result->type = $typeInfo[0];
+            $result->schema = $typeInfo[1];
+            $result->fromStorage = true;
+        } else {
+            $result->fromComplexObject($complexObjectType, $object);
+            BinaryTypeStorage::setByComplexObjectType($complexObjectType, $result->type, $result->schema);
+        }
+        return $result;
+    }
+    
+    public static function calcTypeName(ComplexObjectType $complexObjectType, object $object): string
+    {
+        $typeName = $complexObjectType->getIgniteTypeName();
+        if (!$typeName) {
+            $typeName = $object ? get_class($object) : null;
+        }
+        return $typeName;
+    }
+
+    public function getType(): BinaryType
+    {
+        return $this->type;
+    }
+
+    public function getTypeId(): int
+    {
+        return $this->type->getId();
+    }
+
+    public function getTypeName(): string
+    {
+        return $this->type->getName();
+    }
+
+    public function getSchema(): BinarySchema
+    {
+        return $this->schema;
+    }
+    
+    public function getSchemaId(): int
+    {
+        return $this->schema->getId();
+    }
+
+    public function getFields(): array
+    {
+        return $this->type->getFields();
+    }
+
+    public function getField(int $fieldId): ?BinaryField
+    {
+        return $this->type->getField($fieldId);
+    }
+
+    public function setField(string $fieldName, int $fieldTypeCode = 0): void
+    {
+        $fieldId = BinaryField::calculateId($fieldName);
+        if (!$this->type->hasField($fieldId) || !$this->schema->hasField($fieldId) ||
+            $this->type->getField($fieldId)->getTypeCode() !== $fieldTypeCode) {
+            $this->beforeModify();
+            $this->type->setField(new BinaryField($fieldName, $fieldTypeCode));
+            $this->schema->addField($fieldId);
+        }
+    }
+
+    public function removeField(string $fieldName): void
+    {
+        $fieldId = BinaryField::calculateId($fieldName);
+        if ($this->type->hasField($fieldId)) {
+            $this->beforeModify();
+            $this->type->removeField($fieldId);
+            $this->schema->removeField($fieldId);
+        }
+    }
+
+    public function finalize(BinaryCommunicator $communicator): void
+    {
+        $this->schema->finalize();
+        $communicator->getTypeStorage()->addType($this->type, $this->schema);
+    }
+
+    private function fromComplexObject(ComplexObjectType $complexObjectType, object $object): void
+    {
+        $typeName = BinaryTypeBuilder::calcTypeName($complexObjectType, $object);
+        $this->init($typeName);
+        $this->setFields($complexObjectType, $object);
+    }
+
+    private function init(?string $typeName): void
+    {
+        $this->type = new BinaryType($typeName);
+        $this->schema = new BinarySchema();
+    }
+
+    private function beforeModify(): void
+    {
+        if ($this->fromStorage) {
+            $this->type = $this->type->cloneType();
+            $this->schema = $this->schema->cloneSchema();
+            $this->fromStorage = false;
+        }
+    }
+
+    private function setFields(ComplexObjectType $complexObjectType, object $object): void
+    {
+        try {
+            $reflect = new \ReflectionClass($object);
+            $properties  = $reflect->getProperties(\ReflectionProperty::IS_PUBLIC);
+            foreach ($properties as $property) {
+                if ($property->isStatic()) {
+                    continue;
+                }
+                $fieldName = $property->getName();
+                $fieldType = $complexObjectType->getFieldType($fieldName);
+                if (!$fieldType) {
+                    $fieldValue = $property->getValue($object);
+                    $fieldType = BinaryUtils::calcObjectType($fieldValue);
+                }
+                $this->setField($fieldName, BinaryUtils::getTypeCode($fieldType));
+            }
+        } catch (\ReflectionException $e) {
+            BinaryUtils::serializationError(true, sprintf('class "%s" does not exist', get_class($object)));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryTypeStorage.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryTypeStorage.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryTypeStorage.php
new file mode 100644
index 0000000..05d53cc
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryTypeStorage.php
@@ -0,0 +1,123 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+use Ds\Map;
+use Apache\Ignite\Type\ComplexObjectType;
+
+class BinaryTypeStorage
+{
+    private $communicator;
+    private $types;
+    private static $complexObjectTypes = null;
+
+    public function __construct(BinaryCommunicator $communicator)
+    {
+        $this->communicator = $communicator;
+        $this->types = [];
+    }
+
+    public function addType(BinaryType $binaryType, BinarySchema $binarySchema): void
+    {
+        $typeId = $binaryType->getId();
+        $schemaId = $binarySchema->getId();
+        $storageType = $this->getStorageType($typeId);
+        if (!$storageType || !$storageType->hasSchema($schemaId)) {
+            $binaryType->addSchema($binarySchema);
+            if (!$storageType) {
+                $this->types[$typeId] = $binaryType;
+            } else {
+                $storageType->merge($binaryType, $binarySchema);
+            }
+            $this->putBinaryType($binaryType);
+        }
+    }
+
+    public function getType(int $typeId, int $schemaId = null): ?BinaryType
+    {
+        $storageType = $this->getStorageType($typeId);
+        if (!$storageType || $schemaId && !$storageType->hasSchema($schemaId)) {
+            $storageType = $this->getBinaryType($typeId);
+            if ($storageType) {
+                $this->types[$storageType->getId()] = $storageType;
+            }
+        }
+        return $storageType;
+    }
+    
+    public static function getByComplexObjectType(ComplexObjectType $complexObjectType): ?array
+    {
+        return BinaryTypeStorage::getComplexObjectTypes()->get($complexObjectType, null);
+    }
+
+    public static function setByComplexObjectType(ComplexObjectType $complexObjectType, BinaryType $type, BinarySchema $schema): void
+    {
+        if (!BinaryTypeStorage::getComplexObjectTypes()->hasKey($complexObjectType)) {
+            BinaryTypeStorage::getComplexObjectTypes()->put($complexObjectType, [$type, $schema]);
+        }
+    }
+
+    private static function getComplexObjectTypes(): Map
+    {
+        if (!BinaryTypeStorage::$complexObjectTypes) {
+            BinaryTypeStorage::$complexObjectTypes = new Map();
+        }
+        return BinaryTypeStorage::$complexObjectTypes;
+    }
+
+    private function getBinaryType(int $typeId): ?BinaryType
+    {
+        $binaryType = new BinaryType(null);
+        $binaryType->setId($typeId);
+        $this->communicator->send(
+            ClientOperation::GET_BINARY_TYPE,
+            function (MessageBuffer $payload) use ($typeId)
+            {
+                $payload->writeInteger($typeId);
+            },
+            function (MessageBuffer $payload) use (&$binaryType)
+            {
+                $exist = $payload->readBoolean();
+                if ($exist) {
+                    $binaryType->read($payload);
+                } else {
+                    $binaryType = null;
+                }
+            });
+        return $binaryType;
+    }
+
+    private function putBinaryType(BinaryType $binaryType): void
+    {
+        if (!$binaryType->isValid()) {
+            BinaryUtils::serializationError(true, sprintf('type "%d" can not be registered', $binaryType->getId()));
+        }
+        $this->communicator->send(
+            ClientOperation::PUT_BINARY_TYPE,
+            function (MessageBuffer $payload) use ($binaryType)
+            {
+                $binaryType->write($payload);
+            });
+    }
+
+    private function getStorageType(int $typeId): ?BinaryType
+    {
+        return array_key_exists($typeId, $this->types) ? $this->types[$typeId] : null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryUtils.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryUtils.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryUtils.php
new file mode 100644
index 0000000..ad0bf56
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryUtils.php
@@ -0,0 +1,438 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+use Ds\Map;
+use Ds\Set;
+use Brick\Math\BigDecimal;
+use Brick\Math\BigInteger;
+use Apache\Ignite\Exception\ClientException;
+use Apache\Ignite\Type\ObjectType;
+use Apache\Ignite\Type\MapObjectType;
+use Apache\Ignite\Type\CollectionObjectType;
+use Apache\Ignite\Type\ComplexObjectType;
+use Apache\Ignite\Type\ObjectArrayType;
+use Apache\Ignite\Data\Date;
+use Apache\Ignite\Data\Time;
+use Apache\Ignite\Data\Timestamp;
+use Apache\Ignite\Data\EnumItem;
+use Apache\Ignite\Data\BinaryObject;
+use Apache\Ignite\Internal\Utils\ArgumentChecker;
+
+class BinaryUtils
+{
+    const FLOAT_EPSILON = 0.00001;
+    public static $is32BitInt = PHP_INT_MAX === TypeInfo::MAX_INT_VALUE;
+
+    public static function getSize(int $typeCode): int
+    {
+        return TypeInfo::getTypeInfo($typeCode)->getSize();
+    }
+
+    public static function checkCompatibility($value, $type): void
+    {
+        if (!$type) {
+            return;
+        }
+        $typeCode = BinaryUtils::getTypeCode($type);
+        if ($value === null) {
+            if (!TypeInfo::getTypeInfo($typeCode)->isNullable()) {
+                BinaryUtils::typeCastError(ObjectType::NULL, $typeCode);
+            }
+            return;
+        } elseif (BinaryUtils::isStandardType($typeCode)) {
+            BinaryUtils::checkStandardTypeCompatibility($value, $typeCode, $type);
+            return;
+        }
+        $valueTypeCode = BinaryUtils::getTypeCode(BinaryUtils::calcObjectType($value));
+        if ($typeCode !== $valueTypeCode) {
+            BinaryUtils::typeCastError($valueTypeCode, $typeCode);
+        }
+    }
+
+    public static function isStandardType($typeCode): bool
+    {
+        return $typeCode !== ObjectType::BINARY_OBJECT &&
+            $typeCode !== ObjectType::COMPLEX_OBJECT;
+    }
+
+    public static function checkStandardTypeCompatibility($value, $typeCode, $type = null, $signed = true)
+    {
+        $valueType = BinaryUtils::getPhpTypeName($value);
+        switch ($typeCode) {
+            case ObjectType::BYTE:
+            case ObjectType::SHORT:
+            case ObjectType::INTEGER:
+                if (!is_integer($value)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                $typeInfo = TypeInfo::getTypeInfo($typeCode);
+                $min = $typeInfo->getMinValue();
+                $max = $typeInfo->getMaxValue();
+                if ($signed && ($min && $value < $min || $max && $value > $max) ||
+                    !$signed && ($value < 0 || $value > $max - $min)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                return;
+            case ObjectType::LONG:
+            case ObjectType::FLOAT:
+            case ObjectType::DOUBLE:
+                if (!is_integer($value) && !is_float($value)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                return;
+            case ObjectType::CHAR:
+                if (!is_string($value) || mb_strlen($value) !== 1) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                return;
+            case ObjectType::BOOLEAN:
+                if (!is_bool($value)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                return;
+            case ObjectType::STRING:
+                if (!is_string($value)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                return;
+            case ObjectType::UUID:
+                if (!BinaryUtils::isIndexedArray($value) ||
+                    count($value) !== BinaryUtils::getSize(ObjectType::UUID)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                foreach ($value as $element) {
+                    BinaryUtils::checkStandardTypeCompatibility($element, ObjectType::BYTE, null, false);
+                }
+                return;
+            case ObjectType::DATE:
+                if (!($value instanceof Date)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                return;
+            case ObjectType::ENUM:
+                if (!($value instanceof EnumItem)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                return;
+            case ObjectType::DECIMAL:
+                if (!($value instanceof BigDecimal)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                return;
+            case ObjectType::TIMESTAMP:
+                if (!($value instanceof Timestamp)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                return;
+            case ObjectType::TIME:
+                if (!($value instanceof Time)) {
+                    BinaryUtils::valueCastError($value, $typeCode);
+                }
+                return;
+            case ObjectType::BYTE_ARRAY:
+            case ObjectType::SHORT_ARRAY:
+            case ObjectType::INTEGER_ARRAY:
+            case ObjectType::LONG_ARRAY:
+            case ObjectType::FLOAT_ARRAY:
+            case ObjectType::DOUBLE_ARRAY:
+            case ObjectType::CHAR_ARRAY:
+            case ObjectType::BOOLEAN_ARRAY:
+            case ObjectType::STRING_ARRAY:
+            case ObjectType::UUID_ARRAY:
+            case ObjectType::DATE_ARRAY:
+            case ObjectType::OBJECT_ARRAY:
+            case ObjectType::ENUM_ARRAY:
+            case ObjectType::DECIMAL_ARRAY:
+            case ObjectType::TIMESTAMP_ARRAY:
+            case ObjectType::TIME_ARRAY:
+                if (!BinaryUtils::isIndexedArray($value)) {
+                    BinaryUtils::typeCastError($valueType, $typeCode);
+                }
+                return;
+            case ObjectType::MAP:
+                if (!($value instanceof Map) && !is_array($value)) {
+                    BinaryUtils::typeCastError($valueType, $typeCode);
+                }
+                return;
+            case ObjectType::COLLECTION:
+                $isSet = $type && CollectionObjectType::isSet($type->getSubType());
+                if (!($isSet && $value instanceof Set || BinaryUtils::isIndexedArray($value))) {
+                    BinaryUtils::typeCastError($valueType, $isSet ? 'set' : $typeCode);
+                }
+                return;
+            case ObjectType::NULL:
+                if ($value !== null) {
+                    BinaryUtils::typeCastError('not null', $typeCode);
+                }
+                return;
+            default:
+                $valueTypeCode = BinaryUtils::getTypeCode(BinaryUtils::calcObjectType($value));
+                if ($valueTypeCode === ObjectType::BINARY_OBJECT) {
+                    BinaryUtils::typeCastError($valueTypeCode, $typeCode);
+                }
+                return;
+        }
+    }
+
+    public static function checkTypesCompatibility($expectedType, int $actualTypeCode): void
+    {
+        if ($expectedType === null) {
+            return;
+        }
+        $expectedTypeCode = BinaryUtils::getTypeCode($expectedType);
+        if ($actualTypeCode === ObjectType::NULL) {
+            return;
+        } elseif ($expectedTypeCode === ObjectType::BINARY_OBJECT ||
+            $actualTypeCode === ObjectType::BINARY_OBJECT &&
+            $expectedTypeCode === ObjectType::COMPLEX_OBJECT) {
+            return;
+        } elseif ($actualTypeCode !== $expectedTypeCode) {
+            BinaryUtils::typeCastError($actualTypeCode, $expectedTypeCode);
+        }
+    }
+
+    public static function calcObjectType($object)
+    {
+        if (is_integer($object)) {
+            return ObjectType::INTEGER;
+        } elseif (is_float($object)) {
+            return ObjectType::DOUBLE;
+        } elseif (is_string($object)) {
+            return ObjectType::STRING;
+        } elseif (is_bool($object)) {
+            return ObjectType::BOOLEAN;
+        } elseif (is_array($object)) {
+            if (count($object) > 0) {
+                if (BinaryUtils::isIndexedArray($object)) {
+                    if ($object[0] !== null) {
+                        // indexed array
+                        return BinaryUtils::getArrayType(BinaryUtils::calcObjectType($object[0]));
+                    }
+                } else {
+                    // associative array
+                    return new MapObjectType();
+                }
+            } else {
+                BinaryUtils::noDefaultMapping("empty array");
+            }
+        } elseif ($object instanceof Time) {
+            return ObjectType::TIME;
+        } elseif ($object instanceof Timestamp) {
+            return ObjectType::TIMESTAMP;
+        } elseif ($object instanceof Date) {
+            return ObjectType::DATE;
+        } elseif ($object instanceof EnumItem) {
+            return ObjectType::ENUM;
+        } elseif ($object instanceof BigDecimal) {
+            return ObjectType::DECIMAL;
+        } elseif ($object instanceof Set) {
+            return new CollectionObjectType(CollectionObjectType::HASH_SET);
+        } elseif ($object instanceof Map) {
+            return new MapObjectType();
+        } elseif ($object instanceof BinaryObject) {
+            return ObjectType::BINARY_OBJECT;
+        } elseif (is_object($object)) {
+            return new ComplexObjectType();
+        }
+        BinaryUtils::noDefaultMapping(BinaryUtils::getPhpTypeName($object));
+        return null;
+    }
+
+    public static function getArrayType($elementType)
+    {
+        switch (BinaryUtils::getTypeCode($elementType)) {
+            case ObjectType::BYTE:
+                return ObjectType::BYTE_ARRAY;
+            case ObjectType::SHORT:
+                return ObjectType::SHORT_ARRAY;
+            case ObjectType::INTEGER:
+                return ObjectType::INTEGER_ARRAY;
+            case ObjectType::LONG:
+                return ObjectType::LONG_ARRAY;
+            case ObjectType::FLOAT:
+                return ObjectType::FLOAT_ARRAY;
+            case ObjectType::DOUBLE:
+                return ObjectType::DOUBLE_ARRAY;
+            case ObjectType::CHAR:
+                return ObjectType::CHAR_ARRAY;
+            case ObjectType::BOOLEAN:
+                return ObjectType::BOOLEAN_ARRAY;
+            case ObjectType::STRING:
+                return ObjectType::STRING_ARRAY;
+            case ObjectType::UUID:
+                return ObjectType::UUID_ARRAY;
+            case ObjectType::DATE:
+                return ObjectType::DATE_ARRAY;
+            case ObjectType::ENUM:
+                return ObjectType::ENUM_ARRAY;
+            case ObjectType::DECIMAL:
+                return ObjectType::DECIMAL_ARRAY;
+            case ObjectType::TIMESTAMP:
+                return ObjectType::TIMESTAMP_ARRAY;
+            case ObjectType::TIME:
+                return ObjectType::TIME_ARRAY;
+            case ObjectType::BINARY_OBJECT:
+                return new ObjectArrayType();
+            default:
+                return new ObjectArrayType($elementType);
+        }
+    }
+
+    public static function getArrayElementType($arrayType)
+    {
+        if ($arrayType instanceof ObjectArrayType) {
+            return $arrayType->getElementType();
+        } elseif ($arrayType === ObjectType::OBJECT_ARRAY) {
+            return null;
+        }
+        $info = TypeInfo::getTypeInfo($arrayType);
+        if (!$info || !$info->getElementTypeCode()) {
+            BinaryUtils::internalError();
+        }
+        return $info->getElementTypeCode();
+    }
+    
+    public static function getTypeCode($objectType): int
+    {
+        return $objectType instanceof ObjectType ? $objectType->getTypeCode() : $objectType;
+    }
+    
+    public static function getTypeName($objectType): string
+    {
+        if (is_string($objectType)) {
+            return $objectType;
+        }
+        $typeCode = BinaryUtils::getTypeCode($objectType);
+        $info = TypeInfo::getTypeInfo($typeCode);
+        return $info ? $info->getName() : 'type code ' . $typeCode;
+    }
+
+    public static function getPhpTypeName($object): string
+    {
+        if (is_array($object) && !BinaryUtils::isIndexedArray($object)) {
+            return 'associative array';
+        }
+        return gettype($object);
+    }
+
+    public static function checkObjectType($type, string $argName): void
+    {
+        if ($type === null || $type instanceof ObjectType) {
+            return;
+        }
+        ArgumentChecker::hasValueFrom($type, $argName, false, TypeInfo::getPrimitiveTypes());
+    }
+    
+    public static function floatEquals(float $val1, float $val2): bool
+    {
+        return abs($val1 - $val2) < BinaryUtils::FLOAT_EPSILON;
+    }
+
+    public static function getLongHex(BigInteger $value, bool $isNegative): string
+    {
+        $size = TypeInfo::getTypeInfo(ObjectType::LONG)->getSize();
+        if ($isNegative) {
+            $value = BigInteger::parse(str_pad('1', $size * 2 + 1, '0'), 16)->minus($value);
+        }
+        return str_pad($value->toBase(16), $size * 2, '0', STR_PAD_LEFT);
+    }
+
+    public static function hashCode(?string $str): int
+    {
+        $hash = 0;
+        $length = strlen($str);
+        if ($str && $length > 0) {
+            for ($i = 0; $i < $length; $i++) {
+                $hash = (($hash << 5) - $hash) + ord($str[$i]);
+                $hash &= 0xFFFFFFFF; // Convert to 32bit integer
+            }
+        }
+        return BinaryUtils::intVal32($hash);
+    }
+    
+    public static function hashCodeLowerCase(?string $str): int
+    {
+        return BinaryUtils::hashCode($str ? strtolower($str) : $str);
+    }
+
+    public static function contentHashCode(MessageBuffer $buffer, int $startPos, int $endPos): int
+    {
+        $hash = 1;
+        $length = $endPos - $startPos + 1;
+        $content = $buffer->getSlice($startPos, $length);
+        for ($i = 0; $i < $length; $i++) {
+            $hash = 31 * $hash + ord($content[$i]);
+            $hash &= 0xFFFFFFFF; // Convert to 32bit integer
+        }
+        return BinaryUtils::intVal32($hash);
+    }
+    
+    public static function intVal32(int $value): int
+    {
+        if (!BinaryUtils::$is32BitInt) {
+            $value = ($value & 0xFFFFFFFF);
+            if ($value & 0x80000000) {
+                $value = -((~$value & 0xFFFFFFFF) + 1);
+            }
+        }
+        return $value;
+    }
+
+    public static function internalError(string $message = null): void
+    {
+        throw new ClientException($message ? $message : 'Internal library error');
+    }
+
+    public static function unsupportedType($type): void
+    {
+        throw new ClientException(sprintf('Type %s is not supported', BinaryUtils::getTypeName($type)));
+    }
+
+    public static function noDefaultMapping($type): void
+    {
+        throw new ClientException(sprintf('%s has no default type mapping', BinaryUtils::getTypeName($type)));
+    }
+
+    public static function serializationError(bool $serialize, string $message = null): void
+    {
+        $msg = $serialize ? 'Complex object can not be serialized' : 'Complex object can not be deserialized';
+        if ($message) {
+            $msg = $msg . ': ' . $message;
+        }
+        throw new ClientException($msg);
+    }
+
+    public static function typeCastError($fromType, $toType): void
+    {
+        throw new ClientException(sprintf('Type "%s" can not be cast to %s',
+            BinaryUtils::getTypeName($fromType), BinaryUtils::getTypeName($toType)));
+    }
+
+    public static function valueCastError($value, $toType): void
+    {
+        throw new ClientException(sprintf('Value "%s" can not be cast to %s',
+            print_r($value, true), BinaryUtils::getTypeName($toType)));
+    }
+
+    private static function isIndexedArray(array $object): bool
+    {
+        return $object === array_values($object);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/ClientOperation.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/ClientOperation.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/ClientOperation.php
new file mode 100644
index 0000000..bfc6c17
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/ClientOperation.php
@@ -0,0 +1,64 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+class ClientOperation
+{
+    // Key-Value Queries
+    const CACHE_GET = 1000;
+    const CACHE_PUT = 1001;
+    const CACHE_PUT_IF_ABSENT = 1002;
+    const CACHE_GET_ALL = 1003;
+    const CACHE_PUT_ALL = 1004;
+    const CACHE_GET_AND_PUT = 1005;
+    const CACHE_GET_AND_REPLACE = 1006;
+    const CACHE_GET_AND_REMOVE = 1007;
+    const CACHE_GET_AND_PUT_IF_ABSENT = 1008;
+    const CACHE_REPLACE = 1009;
+    const CACHE_REPLACE_IF_EQUALS = 1010;
+    const CACHE_CONTAINS_KEY = 1011;
+    const CACHE_CONTAINS_KEYS = 1012;
+    const CACHE_CLEAR = 1013;
+    const CACHE_CLEAR_KEY = 1014;
+    const CACHE_CLEAR_KEYS = 1015;
+    const CACHE_REMOVE_KEY = 1016;
+    const CACHE_REMOVE_IF_EQUALS = 1017;
+    const CACHE_REMOVE_KEYS = 1018;
+    const CACHE_REMOVE_ALL = 1019;
+    const CACHE_GET_SIZE = 1020;
+    // Cache Configuration
+    const CACHE_GET_NAMES = 1050;
+    const CACHE_CREATE_WITH_NAME = 1051;
+    const CACHE_GET_OR_CREATE_WITH_NAME = 1052;
+    const CACHE_CREATE_WITH_CONFIGURATION = 1053;
+    const CACHE_GET_OR_CREATE_WITH_CONFIGURATION = 1054;
+    const CACHE_GET_CONFIGURATION = 1055;
+    const CACHE_DESTROY = 1056;
+    // SQL and Scan Queries
+    const QUERY_SCAN = 2000;
+    const QUERY_SCAN_CURSOR_GET_PAGE = 2001;
+    const QUERY_SQL = 2002;
+    const QUERY_SQL_CURSOR_GET_PAGE = 2003;
+    const QUERY_SQL_FIELDS = 2004;
+    const QUERY_SQL_FIELDS_CURSOR_GET_PAGE = 2005;
+    const RESOURCE_CLOSE = 0;
+    // Binary Types
+    const GET_BINARY_TYPE = 3002;
+    const PUT_BINARY_TYPE = 3003;
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/MessageBuffer.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/MessageBuffer.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/MessageBuffer.php
new file mode 100644
index 0000000..920df65
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/MessageBuffer.php
@@ -0,0 +1,307 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+use Apache\Ignite\Type\ObjectType;
+use Brick\Math\BigInteger;
+
+class MessageBuffer
+{
+    const BYTE_ZERO = 0;
+    const BYTE_ONE = 1;
+    const BUFFER_CAPACITY_DEFAULT = 256;
+
+    const PROTOCOL_STRING_ENCODING = 'UTF-8';
+
+    private $buffer;
+    private $position;
+    private $length;
+
+    private static $isLittleEndian;
+    private static $defaultEncoding;
+
+    public static function init(): void
+    {
+        MessageBuffer::$isLittleEndian = pack('L', 1) === pack('V', 1);
+        MessageBuffer::$defaultEncoding = ini_get('default_charset');
+    }
+    
+    public function __construct(int $capacity = MessageBuffer::BUFFER_CAPACITY_DEFAULT)
+    {
+        $this->buffer = '';
+        $this->position = 0;
+        $this->length = 0;
+        $this->ensureCapacity($capacity);
+    }
+    
+    public function getLength(): int
+    {
+        return $this->length;
+    }
+    
+    public function getBuffer(): string
+    {
+        return $this->getSlice(0, $this->getLength());
+    }
+    
+    public function getSlice(int $startPos, int $length): string
+    {
+        return substr($this->buffer, $startPos, $length);
+    }
+    
+    public function getPosition(): int
+    {
+        return $this->position;
+    }
+    
+    public function setPosition(int $position): void
+    {
+        $this->ensureCapacity($position);
+        $this->position = $position;
+    }
+    
+    public function append(string &$buffer): void
+    {
+        $this->buffer .= $buffer;
+        $this->length += strlen($buffer);
+    }
+
+    public function writeByte(int $value, $signed = true): void
+    {
+        $this->writeNumber($value, ObjectType::BYTE, $signed);
+    }
+
+    public function writeShort(int $value): void
+    {
+        $this->writeNumber($value, ObjectType::SHORT);
+    }
+
+    public function writeInteger(int $value): void
+    {
+        $this->writeNumber($value, ObjectType::INTEGER);
+    }
+
+    public function writeLong(float $value): void
+    {
+        $this->writeNumber($value, ObjectType::LONG);
+    }
+
+    public function writeFloat(float $value): void
+    {
+        $this->writeNumber($value, ObjectType::FLOAT);
+    }
+
+    public function writeDouble(float $value): void
+    {
+        $this->writeNumber($value, ObjectType::DOUBLE);
+    }
+
+    public function writeNumber($value, int $type, bool $signed = true): void
+    {
+        $size = TypeInfo::getTypeInfo($type)->getSize();
+        if ($type === ObjectType::LONG && BinaryUtils::$is32BitInt) {
+            // pack longs doesn't work on 32-bit versions of PHP
+            $strValue = strrev(hex2bin(BinaryUtils::getLongHex(BigInteger::of(abs($value)), $value < 0)));
+        } else {
+            $format = $this->getNumberFormat($type, $signed);
+            $strValue = pack($format, $value);
+            $this->convertEndianness($strValue, $type);
+        }
+        if (strlen($strValue) !== $size) {
+            BinaryUtils::unsupportedType(BinaryUtils::getTypeName($type));
+        }
+        $this->writeStr($strValue);
+    }
+
+    public function writeBoolean(bool $value): void
+    {
+        $this->writeByte($value ? MessageBuffer::BYTE_ONE : MessageBuffer::BYTE_ZERO);
+    }
+
+    public function writeChar(string $value): void
+    {
+        $this->writeShort(mb_ord($value));
+    }
+
+    public function writeString(string $value, bool $encode = true): void
+    {
+        if ($encode) {
+            $value = mb_convert_encoding($value, self::PROTOCOL_STRING_ENCODING, self::$defaultEncoding);
+        }
+        $length = strlen($value);
+        $this->writeInteger($length);
+        if ($length > 0) {
+            $this->writeStr($value);
+        }
+    }
+    
+    public function writeBuffer(MessageBuffer $buffer, int $startPos, int $length): void
+    {
+        $this->writeStr($buffer->buffer, $startPos, $length);
+    }
+
+    public function readByte(bool $signed = true): int
+    {
+        return $this->readNumber(ObjectType::BYTE, $signed);
+    }
+
+    public function readShort(): int
+    {
+        return $this->readNumber(ObjectType::SHORT);
+    }
+
+    public function readInteger(): int
+    {
+        return $this->readNumber(ObjectType::INTEGER);
+    }
+
+    public function readLong(): float
+    {
+        return $this->readNumber(ObjectType::LONG);
+    }
+
+    public function readFloat(): float
+    {
+        return $this->readNumber(ObjectType::FLOAT);
+    }
+
+    public function readDouble(): float
+    {
+        return $this->readNumber(ObjectType::DOUBLE);
+    }
+
+    public function readNumber(int $type, bool $signed = true)
+    {
+        $size = BinaryUtils::getSize($type);
+        $this->ensureSize($size);
+        $strValue = substr($this->buffer, $this->position, $size);
+        if ($type === ObjectType::LONG && BinaryUtils::$is32BitInt) {
+            // unpack longs doesn't work on 32-bit versions of PHP
+            $binValue = strrev($strValue);
+            $isNegative = ord($binValue[0]) & 0x80;
+            $hexValue = bin2hex($binValue);
+            $bigIntValue = BigInteger::parse($hexValue, 16);
+            if ($isNegative) {
+                $bigIntValue = BigInteger::parse(str_pad('1', $size * 2 + 1, '0'), 16)->minus($bigIntValue);
+            }
+            $value = $bigIntValue->toFloat();
+            if ($isNegative) {
+                $value = -$value;
+            }
+        } else {
+            $this->convertEndianness($strValue, $type);
+            $value = unpack($this->getNumberFormat($type, $signed), $strValue);
+            $value = $value[1];
+        }
+        $this->position += $size;
+        return $value;
+    }
+
+    public function readBoolean(): bool
+    {
+        return $this->readByte() === MessageBuffer::BYTE_ONE;
+    }
+
+    public function readChar(): string
+    {
+        return mb_chr($this->readShort());
+    }
+
+    public function readString(bool $decode = true): string
+    {
+        $bytesCount = $this->readInteger();
+        $this->ensureSize($bytesCount);
+        $result = substr($this->buffer, $this->position, $bytesCount);
+        if ($decode) {
+            $result = mb_convert_encoding($result, self::$defaultEncoding, self::PROTOCOL_STRING_ENCODING);
+        }
+        $this->position += $bytesCount;
+        return $result;
+    }
+
+    private function getNumberFormat(int $type, bool $signed): string
+    {
+        switch ($type) {
+            case ObjectType::BYTE:
+                return $signed ? 'c' : 'C';
+            case ObjectType::SHORT:
+                return $signed ? 's' : 'S';
+            case ObjectType::INTEGER:
+                return $signed ? 'l' : 'L';
+            case ObjectType::LONG:
+                return $signed ? 'q' : 'Q';
+            case ObjectType::FLOAT:
+                return 'g';
+            case ObjectType::DOUBLE:
+                return 'e';
+            default:
+                BinaryUtils::internalError();
+        }
+        return null;
+    }
+
+    private function convertEndianness(string &$value, int $type): void
+    {
+        if (!MessageBuffer::$isLittleEndian &&
+            ($type === ObjectType::SHORT ||
+             $type === ObjectType::INTEGER ||
+             $type === ObjectType::LONG)) {
+            $value = strrev($value);
+        }
+    }
+    
+    private function writeStr(string &$buffer, int $startPos = 0, int $length = -1): void
+    {
+        if ($length < 0) {
+            $length = strlen($buffer);
+        }
+        $this->ensureCapacity($length);
+        for ($i = 0; $i < $length; $i++) {
+            $this->buffer[$this->position + $i] = $buffer[$startPos + $i];
+        }
+        if ($this->position + $length > $this->length) {
+            $this->length = $this->position + $length;
+        }
+        $this->position += $length;
+    }
+    
+    private function ensureCapacity(int $size): void
+    {
+        if ($size <= 0) {
+            return;
+        }
+        $capacity = strlen($this->buffer);
+        $newCapacity = $capacity > 0 ? $capacity : $size;
+        while ($this->position + $size > $newCapacity) {
+            $newCapacity = $newCapacity * 2;
+        }
+        if ($capacity < $newCapacity) {
+            $this->buffer .= str_repeat('0', $newCapacity - $capacity);
+        }
+    }
+
+    private function ensureSize(int $size): void
+    {
+        if ($this->position + $size > $this->getLength()) {
+            BinaryUtils::internalError('Unexpected format of response');
+        }
+    }
+}
+
+MessageBuffer::init();

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/src/Apache/Ignite/Internal/Binary/Request.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/Request.php b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/Request.php
new file mode 100644
index 0000000..2d22646
--- /dev/null
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/Request.php
@@ -0,0 +1,85 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache\Ignite\Internal\Binary;
+
+use Apache\Ignite\Type\ObjectType;
+
+class Request
+{
+    private $id;
+    private $opCode;
+    private $payloadWriter;
+    private $payloadReader;
+    private $isHandshake;
+    
+    private static $requestId = 0;
+            
+    public function __construct(int $opCode, ?callable $payloadWriter, callable $payloadReader = null, bool $isHandshake = false)
+    {
+        $this->id = Request::getRequestId();
+        $this->opCode = $opCode;
+        $this->payloadWriter = $payloadWriter;
+        $this->payloadReader = $payloadReader;
+        $this->isHandshake = $isHandshake;
+    }
+    
+    public function getId(): int
+    {
+        return $this->id;
+    }
+    
+    public function isHandshake(): bool
+    {
+        return $this->isHandshake;
+    }
+
+    public function getMessage(): MessageBuffer
+    {
+        $message = new MessageBuffer();
+        // Skip message length
+        $messageStartPos = BinaryUtils::getSize(ObjectType::INTEGER);
+        $message->setPosition($messageStartPos);
+        if ($this->opCode >= 0) {
+            // Op code
+            $message->writeShort($this->opCode);
+            // Request id
+            $message->writeLong($this->id);
+        }
+        if ($this->payloadWriter !== null) {
+            // Payload
+            call_user_func($this->payloadWriter, $message);
+        }
+        // Message length
+        $message->setPosition(0);
+        $message->writeInteger($message->getLength() - $messageStartPos);
+        return $message;
+    }
+    
+    public function getPayloadReader(): ?callable
+    {
+        return $this->payloadReader;
+    }
+    
+    private static function getRequestId(): int
+    {
+        $result = Request::$requestId;
+        Request::$requestId++;
+        return $result;
+    }
+}