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;
+ }
+}