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:29 UTC

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

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/tests/CachePutGetTest.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/tests/CachePutGetTest.php b/modules/platforms/php/tests/CachePutGetTest.php
new file mode 100644
index 0000000..9d15ab2
--- /dev/null
+++ b/modules/platforms/php/tests/CachePutGetTest.php
@@ -0,0 +1,646 @@
+<?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\Tests;
+
+use Ds\Map;
+use Ds\Set;
+use PHPUnit\Framework\TestCase;
+use Apache\Ignite\Type\MapObjectType;
+use Apache\Ignite\Type\CollectionObjectType;
+use Apache\Ignite\Type\ObjectArrayType;
+use Apache\Ignite\Type\ComplexObjectType;
+use Apache\Ignite\Data\BinaryObject;
+
+class TstComplObjectWithPrimitiveFields
+{
+    public $field1;
+    public $field2;
+    public $field3;
+    public $field4;
+    public $field5;
+    public $field6;
+    public $field7;
+    public $field8;
+    public $field9;
+    public $field10;
+    public $field11;
+    public $field30;
+    public $field33;
+    public $field36;
+}
+
+class TstComplObjectWithDefaultFieldTypes
+{
+    public $field3;
+    public $field6;
+    public $field8;
+    public $field9;
+    public $field11;
+    public $field30;
+    public $field33;
+    public $field36;
+}
+
+final class CachePutGetTestCase extends TestCase
+{
+    const CACHE_NAME = '__php_test_cache';
+
+    private static $cache;
+
+    public static function setUpBeforeClass(): void
+    {
+        TestingHelper::init();
+        self::cleanUp();
+        self::$cache = TestingHelper::$client->getOrCreateCache(self::CACHE_NAME);
+    }
+
+    public static function tearDownAfterClass(): void
+    {
+        self::cleanUp();
+        TestingHelper::cleanUp();
+    }
+
+    public function testPutGetPrimitiveValues(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $typeCode1 => $typeInfo1) {
+            foreach (TestingHelper::$primitiveValues as $typeCode2 => $typeInfo2) {
+                foreach ($typeInfo1['values'] as $value1) {
+                    foreach ($typeInfo2['values'] as $value2) {
+                        $this->putGetPrimitiveValues($typeCode1, $typeCode2, $value1, $value2);
+                        if (array_key_exists('typeOptional', $typeInfo1)) {
+                            $this->putGetPrimitiveValues(null, $typeCode2, $value1, $value2);
+                        }
+                        if (array_key_exists('typeOptional', $typeInfo2)) {
+                            $this->putGetPrimitiveValues($typeCode1, null, $value1, $value2);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public function testPutGetArraysOfPrimitives(): void
+    {
+        foreach (TestingHelper::$arrayValues as $type => $typeInfo) {
+            $primitiveType = $typeInfo['elemType'];
+            $values = TestingHelper::$primitiveValues[$primitiveType]['values'];
+            $this->putGetArrays($primitiveType, $type, $values[0], $values);
+            $this->putGetArrays($primitiveType, $type, $values[0], []);
+            if (array_key_exists('typeOptional', $typeInfo)) {
+                $this->putGetArrays($primitiveType, $type, $values[0], $values);
+            }
+        }
+    }
+
+    public function testPutGetMaps(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $type1 => $typeInfo1) {
+            if (!$typeInfo1['isMapKey']) {
+                continue;
+            }
+            foreach (TestingHelper::$primitiveValues as $type2 => $typeInfo2) {
+                $map = new Map();
+                $index2 = 0;
+                foreach ($typeInfo1['values'] as $value1) {
+                    $value2 = $typeInfo2['values'][$index2];
+                    $index2++;
+                    if ($index2 >= count($typeInfo2['values'])) {
+                        $index2 = 0;
+                    }
+                    $map->put($value1, $value2);
+                }
+                $this->putGetMaps(new MapObjectType(MapObjectType::HASH_MAP, $type1, $type2), $map);
+                $this->putGetMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, $type1, $type2), $map);
+                if (array_key_exists('typeOptional', $typeInfo1)) {
+                    $this->putGetMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, null, $type2), $map);
+                }
+                if (array_key_exists('typeOptional', $typeInfo2)) {
+                    $this->putGetMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, $type1), $map);
+                }
+                if (array_key_exists('typeOptional', $typeInfo1) && array_key_exists('typeOptional', $typeInfo2)) {
+                    $this->putGetMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP), $map);
+                    $this->putGetMaps(null, $map);
+                }
+            }
+        }
+    }
+
+    public function testPutGetArrayMaps(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $type1 => $typeInfo1) {
+            if (!$typeInfo1['isArrayKey']) {
+                continue;
+            }
+            foreach (TestingHelper::$primitiveValues as $type2 => $typeInfo2) {
+                $map = [];
+                $index2 = 0;
+                foreach ($typeInfo1['values'] as $value1) {
+                    $value2 = $typeInfo2['values'][$index2];
+                    $index2++;
+                    if ($index2 >= count($typeInfo2['values'])) {
+                        $index2 = 0;
+                    }
+                    $map[$value1] = $value2;
+                }
+                $this->putGetArrayMaps(new MapObjectType(MapObjectType::HASH_MAP, $type1, $type2), $map);
+                $this->putGetArrayMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, $type1, $type2), $map);
+                if (array_key_exists('typeOptional', $typeInfo1)) {
+                    $this->putGetArrayMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, null, $type2), $map);
+                }
+                if (array_key_exists('typeOptional', $typeInfo2)) {
+                    $this->putGetArrayMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, $type1), $map);
+                }
+                if (array_key_exists('typeOptional', $typeInfo1) && array_key_exists('typeOptional', $typeInfo2)) {
+                    $this->putGetArrayMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP), $map);
+                    $this->putGetArrayMaps(null, $map);
+                }
+            }
+        }
+    }
+
+    public function testPutGetMapsWithArrays(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $type1 => $typeInfo1) {
+            if (!$typeInfo1['isMapKey']) {
+                continue;
+            }
+            foreach (TestingHelper::$arrayValues as $type2 => $typeInfo2) {
+                $primitiveType2 = $typeInfo2['elemType'];
+                $values2 = TestingHelper::$primitiveValues[$primitiveType2]['values'];
+                $map = new Map();
+                $index2 = 0;
+                $arrayValues2 = [$values2, null, array_reverse($values2)];
+                foreach ($typeInfo1['values'] as $value1) {
+                    $map->put($value1, $arrayValues2[$index2]);
+                    $index2++;
+                    if ($index2 >= count($arrayValues2)) {
+                        $index2 = 0;
+                    }
+                }
+                $this->putGetMaps(new MapObjectType(MapObjectType::HASH_MAP, $type1, $type2), $map);
+                $this->putGetMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, $type1, $type2), $map);
+                if (array_key_exists('typeOptional', $typeInfo1)) {
+                    $this->putGetMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, null, $type2), $map);
+                }
+                if (array_key_exists('typeOptional', $typeInfo2)) {
+                    $this->putGetMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, $type1), $map);
+                }
+                if (array_key_exists('typeOptional', $typeInfo1) && array_key_exists('typeOptional', $typeInfo2)) {
+                    $this->putGetMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP), $map);
+                    $this->putGetMaps(null, $map);
+                }
+            }
+        }
+    }
+
+    public function testPutGetArrayMapsWithArrays(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $type1 => $typeInfo1) {
+            if (!$typeInfo1['isArrayKey']) {
+                continue;
+            }
+            foreach (TestingHelper::$arrayValues as $type2 => $typeInfo2) {
+                $primitiveType2 = $typeInfo2['elemType'];
+                $values2 = TestingHelper::$primitiveValues[$primitiveType2]['values'];
+                $map = [];
+                $index2 = 0;
+                $arrayValues2 = [$values2, null, array_reverse($values2)];
+                foreach ($typeInfo1['values'] as $value1) {
+                    $map[$value1] = $arrayValues2[$index2];
+                    $index2++;
+                    if ($index2 >= count($arrayValues2)) {
+                        $index2 = 0;
+                    }
+                }
+                $this->putGetArrayMaps(new MapObjectType(MapObjectType::HASH_MAP, $type1, $type2), $map);
+                $this->putGetArrayMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, $type1, $type2), $map);
+                if (array_key_exists('typeOptional', $typeInfo1)) {
+                    $this->putGetArrayMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, null, $type2), $map);
+                }
+                if (array_key_exists('typeOptional', $typeInfo2)) {
+                    $this->putGetArrayMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP, $type1), $map);
+                }
+                if (array_key_exists('typeOptional', $typeInfo1) && array_key_exists('typeOptional', $typeInfo2)) {
+                    $this->putGetArrayMaps(new MapObjectType(MapObjectType::LINKED_HASH_MAP), $map);
+                    $this->putGetArrayMaps(null, $map);
+                }
+            }
+        }
+    }
+
+    public function testPutGetSets(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+            $set = new Set();
+            foreach ($typeInfo['values'] as $value) {
+                $set->add($value);
+            }
+            $this->putGetSets(new CollectionObjectType(CollectionObjectType::USER_SET, $type), $set);
+            $this->putGetSets(new CollectionObjectType(CollectionObjectType::HASH_SET, $type), $set);
+            $this->putGetSets(new CollectionObjectType(CollectionObjectType::LINKED_HASH_SET, $type), $set);
+            if (array_key_exists('typeOptional', $typeInfo)) {
+                $this->putGetSets(new CollectionObjectType(CollectionObjectType::LINKED_HASH_SET), $set);
+                $this->putGetSets(null, $set);
+            }
+        }
+    }
+
+    public function testPutGetLists(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+            $list = array();
+            foreach ($typeInfo['values'] as $value) {
+                array_push($list, $value);
+            }
+            $this->putGetLists(new CollectionObjectType(CollectionObjectType::USER_COL, $type), $list);
+            $this->putGetLists(new CollectionObjectType(CollectionObjectType::ARRAY_LIST, $type), $list);
+            $this->putGetLists(new CollectionObjectType(CollectionObjectType::LINKED_LIST, $type), $list);
+            if (array_key_exists('typeOptional', $typeInfo)) {
+                $this->putGetLists(new CollectionObjectType(CollectionObjectType::ARRAY_LIST), $list);
+            }
+//            $singletonList = [$typeInfo['values'][0]];
+//            $this->putGetLists(new CollectionObjectType(CollectionObjectType::SINGLETON_LIST, $type), $singletonList);
+        }
+    }
+
+    public function testPutGetObjectArrayOfMaps(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $type1 => $typeInfo1) {
+            if (!$typeInfo1['isMapKey']) {
+                continue;
+            }
+            foreach (TestingHelper::$primitiveValues as $type2 => $typeInfo2) {
+                $map = new Map();
+                $index2 = 0;
+                foreach ($typeInfo1['values'] as $value1) {
+                    $value2 = $typeInfo2['values'][$index2];
+                    $index2++;
+                    if ($index2 >= count($typeInfo2['values'])) {
+                        $index2 = 0;
+                    }
+                    $map->put($value1, $value2);
+                }
+                $array = array();
+                for ($i = 0; $i < 5; $i++) {
+                    $map->reverse();
+                    array_push($array, $map);
+                }
+                $this->putGetObjectArrays(new ObjectArrayType(new MapObjectType(MapObjectType::HASH_MAP, $type1, $type2)), $array);
+                if (array_key_exists('typeOptional', $typeInfo1)) {
+                    $this->putGetObjectArrays(new ObjectArrayType(new MapObjectType(MapObjectType::LINKED_HASH_MAP, null, $type2)), $array);
+                }
+                if (array_key_exists('typeOptional', $typeInfo2)) {
+                    $this->putGetObjectArrays(new ObjectArrayType(new MapObjectType(MapObjectType::LINKED_HASH_MAP, $type1)), $array);
+                }
+                if (array_key_exists('typeOptional', $typeInfo1) && array_key_exists('typeOptional', $typeInfo2)) {
+                    $this->putGetObjectArrays(new ObjectArrayType(), $array);
+                    $this->putGetObjectArrays(null, $array);
+                }
+            }
+        }
+    }
+
+    public function testPutGetObjectArrayOfPrimitives(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+            $array = $typeInfo['values'];
+            $this->putGetObjectArrays(new ObjectArrayType($type), $array);
+            if (array_key_exists('typeOptional', $typeInfo)) {
+                $this->putGetObjectArrays(new ObjectArrayType(), $array);
+            }
+        }
+    }
+
+    public function testPutGetObjectArrayOfPrimitiveArrays(): void
+    {
+        foreach (TestingHelper::$arrayValues as $type => $typeInfo) {
+            $primitiveType = $typeInfo['elemType'];
+            $values = TestingHelper::$primitiveValues[$primitiveType]['values'];
+            $array = [];
+            for ($i = 0; $i < 5; $i++) {
+                $values = array_reverse($values);
+                array_push($array, $values);
+            }
+            $this->putGetObjectArrays(new ObjectArrayType($type), $array);
+            if (array_key_exists('typeOptional', $typeInfo)) {
+                $this->putGetObjectArrays(new ObjectArrayType(), $array);
+                $this->putGetObjectArrays(null, $array);
+            }
+        }
+    }
+
+    public function testPutGetObjectArrayOfSets(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+            $set = new Set();
+            $values = $typeInfo['values'];
+            $array = array();
+            for ($i = 0; $i < 5; $i++) {
+                $values = array_reverse($values);
+                array_push($array, new Set($values));
+            }
+            $this->putGetObjectArrays(new ObjectArrayType(new CollectionObjectType(CollectionObjectType::USER_SET, $type)), $array);
+            $this->putGetObjectArrays(new ObjectArrayType(new CollectionObjectType(CollectionObjectType::HASH_SET, $type)), $array);
+            $this->putGetObjectArrays(new ObjectArrayType(new CollectionObjectType(CollectionObjectType::LINKED_HASH_SET, $type)), $array);
+            if (array_key_exists('typeOptional', $typeInfo)) {
+                $this->putGetObjectArrays(new ObjectArrayType(), $array);
+                $this->putGetObjectArrays(null, $array);
+            }
+        }
+    }
+
+    public function testPutGetObjectArrayOfLists(): void
+    {
+        foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+            $set = new Set();
+            $values = $typeInfo['values'];
+            $array = array();
+            for ($i = 0; $i < 5; $i++) {
+                $values = array_reverse($values);
+                array_push($array, $values);
+            }
+            $this->putGetObjectArrays(new ObjectArrayType(new CollectionObjectType(CollectionObjectType::USER_COL, $type)), $array);
+            $this->putGetObjectArrays(new ObjectArrayType(new CollectionObjectType(CollectionObjectType::ARRAY_LIST, $type)), $array);
+            $this->putGetObjectArrays(new ObjectArrayType(new CollectionObjectType(CollectionObjectType::LINKED_LIST, $type)), $array);
+            if (array_key_exists('typeOptional', $typeInfo)) {
+                $this->putGetObjectArrays(new ObjectArrayType(new CollectionObjectType(CollectionObjectType::ARRAY_LIST)), $array);
+            }
+        }
+    }
+
+    public function testPutGetObjectArrayOfComplexObjects(): void
+    {
+        $array = array();
+        for ($i = 0; $i < 5; $i++) {
+            $object = new TstComplObjectWithPrimitiveFields();
+            foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+                $fieldName = 'field' . $type;
+                $index = ($i < count($typeInfo['values'])) ? $i : $i % count($typeInfo['values']);
+                $object->$fieldName = $typeInfo['values'][$index];
+            }
+            array_push($array, $object);
+        }
+
+        $fullComplexObjectType = new ComplexObjectType();
+        $partComplexObjectType = new ComplexObjectType();
+        foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+            $fullComplexObjectType->setFieldType('field' . $type, $type);
+            if (!array_key_exists('typeOptional', $typeInfo)) {
+                $partComplexObjectType->setFieldType('field' . $type, $type);
+            }
+        }
+        $this->putGetObjectArrays(new ObjectArrayType($fullComplexObjectType), $array);
+        $this->putGetObjectArrays(new ObjectArrayType($partComplexObjectType), $array);
+    }
+
+    public function testPutGetObjectArrayOfComplexObjectsWithDefaultFieldTypes(): void
+    {
+        $array = array();
+        for ($i = 0; $i < 5; $i++) {
+            $object = new TstComplObjectWithDefaultFieldTypes();
+            foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+                if (!array_key_exists('typeOptional', $typeInfo)) {
+                    continue;
+                }
+                $fieldName = 'field' . $type;
+                $index = ($i < count($typeInfo['values'])) ? $i : $i % count($typeInfo['values']);
+                $object->$fieldName = $typeInfo['values'][$index];
+            }
+            array_push($array, $object);
+        }
+        $this->putGetObjectArrays(new ObjectArrayType(new ComplexObjectType()), $array);
+    }
+
+    public function testPutGetObjectArrayOfBinaryObjects(): void
+    {
+        $array = array();
+        for ($i = 0; $i < 5; $i++) {
+            $binaryObject = new BinaryObject('tstBinaryObj');
+            foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+                $fieldName = 'field' . $type;
+                $index = ($i < count($typeInfo['values'])) ? $i : $i % count($typeInfo['values']);
+                $binaryObject->setField($fieldName, $typeInfo['values'][$index], $type);
+            }
+            array_push($array, $binaryObject);
+        }
+        $this->putGetObjectArrays(new ObjectArrayType(), $array);
+        $this->putGetObjectArrays(null, $array);
+    }
+
+    public function testPutGetObjectArrayOfObjectArrays(): void
+    {
+        $complexObjectType = new ComplexObjectType();
+        foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+            $complexObjectType->setFieldType('field' . $type, $type);
+        }
+        $array = array();
+        for ($i = 0; $i < 2; $i++) {
+            $innerArray = array();
+            for ($j = 0; $j < 2; $j++) {
+                $object = new TstComplObjectWithPrimitiveFields();
+                foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+                    $fieldName = 'field' . $type;
+                    $index = $i * 10 + $j;
+                    $index = ($index < count($typeInfo['values'])) ? $index : $index % count($typeInfo['values']);
+                    $object->$fieldName = $typeInfo['values'][$index];
+                }
+                array_push($innerArray, $object);
+            }
+            array_push($array, $innerArray);
+        }
+        $this->putGetObjectArrays(new ObjectArrayType(new ObjectArrayType($complexObjectType)), $array);
+    }
+
+    public function testPutGetObjectArrayOfObjectArraysOfComplexObjectsWithDefaultFieldTypes(): void
+    {
+        $array = array();
+        for ($i = 0; $i < 2; $i++) {
+            $innerArray = array();
+            for ($j = 0; $j < 2; $j++) {
+                $object = new TstComplObjectWithDefaultFieldTypes();
+                foreach (TestingHelper::$primitiveValues as $type => $typeInfo) {
+                    if (!array_key_exists('typeOptional', $typeInfo)) {
+                        continue;
+                    }
+                    $fieldName = 'field' . $type;
+                    $index = $i * 10 + $j;
+                    $index = ($index < count($typeInfo['values'])) ? $index : $index % count($typeInfo['values']);
+                    $object->$fieldName = $typeInfo['values'][$index];
+                }
+                array_push($innerArray, $object);
+            }
+            array_push($array, $innerArray);
+        }
+        $this->putGetObjectArrays(new ObjectArrayType(new ObjectArrayType(new ComplexObjectType())), $array);
+    }
+
+    private function putGetObjectArrays(?ObjectArrayType $arrayType, array $value): void
+    {
+        $key = microtime();
+        self::$cache->
+            setKeyType(null)->
+            setValueType($arrayType);
+        try {
+            self::$cache->put($key, $value);
+            $result = self::$cache->get($key);
+            $this->assertTrue(is_array($result));
+            $this->assertTrue(TestingHelper::compare($value, $result));
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
+    private function putGetLists(?CollectionObjectType $listType, array $value): void
+    {
+        $key = microtime();
+        self::$cache->
+            setKeyType(null)->
+            setValueType($listType);
+        try {
+            self::$cache->put($key, $value);
+            $result = self::$cache->get($key);
+            $strResult = TestingHelper::printValue($result);
+            $strValue = TestingHelper::printValue($value);
+            $strValueType = TestingHelper::printValue($listType ? $listType->getElementType() : null);
+            $this->assertTrue(
+                is_array($result),
+                "result is not array: result={$strResult}");
+            $this->assertTrue(
+                TestingHelper::compare($value, $result),
+                "Lists are not equal: valueType={$strValueType}, put value={$strValue}, get value={$strResult}");
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
+    private function putGetSets(?CollectionObjectType $setType, Set $value): void
+    {
+        $key = microtime();
+        self::$cache->
+            setKeyType(null)->
+            setValueType($setType);
+        try {
+            self::$cache->put($key, $value);
+            $result = self::$cache->get($key);
+            $strResult = TestingHelper::printValue($result);
+            $strValue = TestingHelper::printValue($value);
+            $strValueType = TestingHelper::printValue($setType ? $setType->getElementType() : null);
+            $this->assertTrue(
+                $result instanceof Set,
+                "result is not Set: result={$strResult}");
+            $this->assertTrue(
+                TestingHelper::compare($value, $result),
+                "Sets are not equal: valueType={$strValueType}, put value={$strValue}, get value={$strResult}");
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
+    private function putGetArrayMaps(?MapObjectType $mapType, array $value): void
+    {
+        self::$cache->
+            setKeyType(null)->
+            setValueType($mapType);
+        try {
+            $key = microtime();
+            self::$cache->put($key, $value);
+            $result = self::$cache->get($key);
+            $strResult = TestingHelper::printValue($result);
+            $strValue = TestingHelper::printValue($value);
+            $strValueType = TestingHelper::printValue($mapType ? $mapType->getValueType() : null);
+            $this->assertTrue(
+                $result instanceof Map,
+                "result is not Map: result={$strResult}");
+            $this->assertTrue(
+                TestingHelper::compare(new Map($value), $result),
+                "Maps are not equal: valueType={$strValueType}, put value={$strValue}, get value={$strResult}");
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
+    private function putGetMaps(?MapObjectType $mapType, Map $value): void
+    {
+        self::$cache->
+            setKeyType(null)->
+            setValueType($mapType);
+        try {
+            $key = microtime();
+            self::$cache->put($key, $value);
+            $result = self::$cache->get($key);
+            $strResult = TestingHelper::printValue($result);
+            $strValue = TestingHelper::printValue($value);
+            $strValueType = TestingHelper::printValue($mapType ? $mapType->getValueType() : null);
+            $this->assertTrue(
+                $result instanceof Map,
+                "result is not Map: result={$strResult}");
+            $this->assertTrue(
+                TestingHelper::compare($value, $result),
+                "Maps are not equal: valueType={$strValueType}, put value={$strValue}, get value={$strResult}");
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
+    private function putGetPrimitiveValues(?int $typeCode1, ?int $typeCode2, $value1, $value2): void
+    {
+        self::$cache->
+            setKeyType($typeCode1)->
+            setValueType($typeCode2);
+        try {
+            self::$cache->put($value1, $value2);
+            $result = self::$cache->get($value1);
+            $strValue1 = TestingHelper::printValue($value1);
+            $strValue2 = TestingHelper::printValue($value2);
+            $strResult = TestingHelper::printValue($result);
+            $this->assertTrue(
+                TestingHelper::compare($value2, $result),
+                "values are not equal: keyType={$typeCode1}, key={$strValue1}, valueType={$typeCode2}, put value={$strValue2}, get value={$strResult}");
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
+    private function putGetArrays(int $keyType, int $valueType, $key, $value): void
+    {
+        self::$cache->
+            setKeyType($keyType)->
+            setValueType($valueType);
+        try {
+            self::$cache->put($key, $value);
+            $result = self::$cache->get($key);
+            self::$cache->clearKey($key);
+            $strValue = TestingHelper::printValue($value);
+            $strResult = TestingHelper::printValue($result);
+            $this->assertTrue(
+                is_array($result),
+                "result is not Array: arrayType={$valueType}, result={$strResult}");
+            $this->assertTrue(
+                TestingHelper::compare($value, $result),
+                "Arrays are not equal: arrayType={$valueType}, put array={$strValue}, get array={$strResult}");
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
+    private static function cleanUp(): void
+    {
+        TestingHelper::destroyCache(self::CACHE_NAME);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/tests/CacheTest.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/tests/CacheTest.php b/modules/platforms/php/tests/CacheTest.php
new file mode 100644
index 0000000..20627cb
--- /dev/null
+++ b/modules/platforms/php/tests/CacheTest.php
@@ -0,0 +1,240 @@
+<?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\Tests;
+
+use Apache\Ignite\Cache\CacheKeyConfiguration;
+use Apache\Ignite\Cache\QueryField;
+use Apache\Ignite\Cache\QueryEntity;
+use Apache\Ignite\Cache\QueryIndex;
+use PHPUnit\Framework\TestCase;
+use Apache\Ignite\Cache\CacheInterface;
+use Apache\Ignite\Cache\CacheConfiguration;
+use Apache\Ignite\Exception\OperationException;
+use Apache\Ignite\Exception\ClientException;
+
+final class CacheTestCase extends TestCase
+{
+    const CACHE_NAME = '__php_test_cache';
+    const CACHE_NAME2 = '__php_test_cache2';
+    const CACHE_NAME3 = '__php_test_cache3';
+    const CACHE_NAME4 = '__php_test_cache4';
+
+    public static function setUpBeforeClass(): void
+    {
+        TestingHelper::init();
+        self::cleanUp();
+    }
+
+    public static function tearDownAfterClass(): void
+    {
+        self::cleanUp();
+        TestingHelper::cleanUp();
+    }
+    
+    public function testCreateCache(): void
+    {
+        $client = TestingHelper::$client;
+        $cache = $client->getCache(self::CACHE_NAME);
+        $this->checkCache($cache, false);
+        $cache = $client->createCache(self::CACHE_NAME);
+        $this->checkCache($cache, true);
+        $cache = $client->getCache(self::CACHE_NAME);
+        $this->checkCache($cache, true);
+        $client->destroyCache(self::CACHE_NAME);
+    }
+    
+    public function testCreateCacheTwice(): void
+    {
+        $client = TestingHelper::$client;
+        try {
+            $client->getOrCreateCache(self::CACHE_NAME);
+            $this->expectException(OperationException::class);
+            $client->createCache(self::CACHE_NAME);
+        } finally {
+            $client->destroyCache(self::CACHE_NAME);
+        }
+    }
+    
+    public function testGetOrCreateCache(): void
+    {
+        $client = TestingHelper::$client;
+        $cache = $client->getCache(self::CACHE_NAME);
+        $this->checkCache($cache, false);
+        $cache = $client->getOrCreateCache(self::CACHE_NAME);
+        $this->checkCache($cache, true);
+        $cache = $client->getCache(self::CACHE_NAME);
+        $this->checkCache($cache, true);
+        $client->destroyCache(self::CACHE_NAME);
+    }
+
+    public function testGetCacheNames(): void
+    {
+        $client = TestingHelper::$client;
+        $client->getOrCreateCache(self::CACHE_NAME);
+        $client->getOrCreateCache(self::CACHE_NAME2);
+        $cacheNames = $client->cacheNames();
+        $this->assertContains(self::CACHE_NAME, $cacheNames);
+        $this->assertContains(self::CACHE_NAME2, $cacheNames);
+        $client->destroyCache(self::CACHE_NAME);
+        $client->destroyCache(self::CACHE_NAME2);
+    }
+    
+    public function testDestroyCache(): void
+    {
+        $client = TestingHelper::$client;
+        $client->getOrCreateCache(self::CACHE_NAME);
+        $client->destroyCache(self::CACHE_NAME);
+        
+        $this->expectException(OperationException::class);
+        $client->destroyCache(self::CACHE_NAME);
+    }
+    
+    public function testCreateCacheWithConfiguration(): void
+    {
+        $client = TestingHelper::$client;
+        $cacheCfg = (new CacheConfiguration())->
+            setQueryEntities(
+                (new QueryEntity())->
+                        setKeyTypeName('INT')->
+                        setValueTypeName('Person')->
+                        setTableName('Person')->
+                        setKeyFieldName('id')->
+                        setValueFieldName('salary')->
+                        setFields(
+                            (new QueryField('id', 'INT'))->setIsKeyField(true),
+                            (new QueryField('firstName', 'VARCHAR'))->setIsNotNull(true),
+                            (new QueryField('lastName', 'VARCHAR'))->setDefaultValue('lastName'),
+                            (new QueryField('salary', 'DOUBLE'))->setPrecision(10)->setScale(10))->
+                        setAliases(['id' => 'id', 'firstName' => 'firstName'])->
+                        setIndexes(
+                            (new QueryIndex('id_idx', QueryIndex::TYPE_SORTED))->
+                                setName('id_idx')->
+                                setType(QueryIndex::TYPE_SORTED)->
+                                setInlineSize(10)->
+                                setFields(['id' => true, 'firstName' => false])))->
+            setKeyConfigurations((new CacheKeyConfiguration('Person', 'Person'))->
+                setTypeName('Person')->
+                setAffinityKeyFieldName('Person'));
+
+        $cache = $client->createCache(self::CACHE_NAME3, $cacheCfg);
+        $cfg = $client->getCacheConfiguration(self::CACHE_NAME3);
+        $client->destroyCache(self::CACHE_NAME3);
+        $keyConfig = $cfg->getKeyConfigurations()[0];
+        $queryEntity = $cfg->getQueryEntities()[0];
+        $queryField = $queryEntity->getFields()[0];
+        $queryIndex = $queryEntity->getIndexes()[0];
+        $cfg->
+            setName($cfg->getName())->
+            setAtomicityMode($cfg->getAtomicityMode())->
+            setBackups($cfg->getBackups())->
+            setCacheMode($cfg->getCacheMode())->
+            setCopyOnRead($cfg->getCopyOnRead())->
+            setDataRegionName($cfg->getDataRegionName())->
+            setEagerTtl($cfg->getEagerTtl())->
+            setStatisticsEnabled($cfg->getStatisticsEnabled())->
+            setGroupName($cfg->getGroupName())->
+            setDefaultLockTimeout($cfg->getDefaultLockTimeout())->
+            setMaxConcurrentAsyncOperations($cfg->getMaxConcurrentAsyncOperations())->
+            setMaxQueryIterators($cfg->getMaxQueryIterators())->
+            setIsOnheapCacheEnabled($cfg->getIsOnheapCacheEnabled())->
+            setPartitionLossPolicy($cfg->getPartitionLossPolicy())->
+            setQueryDetailMetricsSize($cfg->getQueryDetailMetricsSize())->
+            setQueryParallelism($cfg->getQueryParallelism())->
+            setReadFromBackup($cfg->getReadFromBackup())->
+            setRebalanceBatchSize($cfg->getRebalanceBatchSize())->
+            setRebalanceBatchesPrefetchCount($cfg->getRebalanceBatchesPrefetchCount())->
+            setRebalanceDelay($cfg->getRebalanceDelay())->
+            setRebalanceMode($cfg->getRebalanceMode())->
+            setRebalanceOrder($cfg->getRebalanceOrder())->
+            setRebalanceThrottle($cfg->getRebalanceThrottle())->
+            setRebalanceTimeout($cfg->getRebalanceTimeout())->
+            setSqlEscapeAll($cfg->getSqlEscapeAll())->
+            setSqlIndexInlineMaxSize($cfg->getSqlIndexInlineMaxSize())->
+            setSqlSchema($cfg->getSqlSchema())->
+            setWriteSynchronizationMode($cfg->getWriteSynchronizationMode())->
+            setKeyConfigurations((new CacheKeyConfiguration())->
+                setTypeName($keyConfig->getTypeName())->
+                setAffinityKeyFieldName($keyConfig->getAffinityKeyFieldName()))->
+            setQueryEntities((new QueryEntity())->
+                setKeyTypeName($queryEntity->getKeyTypeName())->
+                setValueTypeName($queryEntity->getValueTypeName())->
+                setTableName($queryEntity->getTableName())->
+                setKeyFieldName($queryEntity->getKeyFieldName())->
+                setValueFieldName($queryEntity->getValueFieldName())->
+                setFields(
+                    (new QueryField())->
+                        setName($queryField->getName())->
+                        setTypeName($queryField->getTypeName())->
+                        setIsKeyField($queryField->getIsKeyField())->
+                        setIsNotNull($queryField->getIsNotNull())->
+                        setDefaultValue($queryField->getDefaultValue())->
+                        setPrecision($queryField->getPrecision())->
+                        setScale($queryField->getScale()),
+                    $queryEntity->getFields()[1],
+                    $queryEntity->getFields()[2],
+                    $queryEntity->getFields()[3])->
+                setAliases($queryEntity->getAliases())->
+                setIndexes((new QueryIndex())->
+                    setName($queryIndex->getName())->
+                    setType($queryIndex->getType())->
+                    setInlineSize($queryIndex->getInlineSize())->
+                    setFields($queryIndex->getFields())));
+        $cache = $client->getOrCreateCache(self::CACHE_NAME4, $cfg);
+        $cfg2 = $client->getCacheConfiguration(self::CACHE_NAME4);
+        $client->destroyCache(self::CACHE_NAME4);
+        $this->assertTrue(true);
+    }
+    
+    public function testCreateCacheWithWrongArgs(): void
+    {
+        $client = TestingHelper::$client;
+        $this->expectException(ClientException::class);
+        $client->createCache('');
+    }
+
+    public function testGetOrCreateCacheWithWrongArgs(): void
+    {
+        $client = TestingHelper::$client;
+        $this->expectException(ClientException::class);
+        $client->getOrCreateCache('');
+    }
+    
+    public function testGetCacheWithWrongArgs(): void
+    {
+        $client = TestingHelper::$client;
+        $this->expectException(ClientException::class);
+        $client->getCache('');
+    }
+    
+    private function checkCache(CacheInterface $cache, bool $cacheExists): void
+    {
+        if (!$cacheExists) {
+            $this->expectException(OperationException::class);
+        }
+        $cache->put(0, 0);
+    }
+    
+    private static function cleanUp(): void
+    {
+        TestingHelper::destroyCache(self::CACHE_NAME);
+        TestingHelper::destroyCache(self::CACHE_NAME2);
+        TestingHelper::destroyCache(self::CACHE_NAME3);
+        TestingHelper::destroyCache(self::CACHE_NAME4);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/tests/ComplexObjectTest.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/tests/ComplexObjectTest.php b/modules/platforms/php/tests/ComplexObjectTest.php
new file mode 100644
index 0000000..430c068
--- /dev/null
+++ b/modules/platforms/php/tests/ComplexObjectTest.php
@@ -0,0 +1,428 @@
+<?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\Tests;
+
+use Ds\Map;
+use PHPUnit\Framework\TestCase;
+use Apache\Ignite\Type\ObjectType;
+use Apache\Ignite\Type\MapObjectType;
+use Apache\Ignite\Type\ComplexObjectType;
+use Apache\Ignite\Data\BinaryObject;
+
+class Class1
+{
+    public $field_1_1;
+    public $field_1_2;
+    public $field_1_3;
+    
+    public function __construct()
+    {
+        $this->field_1_1 = null;
+        $this->field_1_2 = new Class2();
+        $this->field_1_3 = null;
+    }
+}
+
+class SubClass1 extends Class1
+{
+    public $field_1_4;
+    public $field_1_5;
+    public $field_1_6;
+    public $field_1_7;
+    public $field_1_8;
+    
+    public function __construct()
+    {
+        parent::__construct();
+        $this->field_1_4 = null;
+        $this->field_1_5 = new Class3();
+        $this->field_1_6 = null;
+        $this->field_1_7 = null;
+        $this->field_1_8 = null;
+    }
+}
+
+class Class2
+{
+    public $field_2_1;
+    public $field_2_2;
+}
+
+class Class3 {
+    public $field_3_1;
+    public $field_3_2;
+}
+
+final class ComplexObjectTestCase extends TestCase
+{
+    const CACHE_NAME = '__php_test_compl_obj_cache';
+    
+    private static $cache;
+
+    public static function setUpBeforeClass(): void
+    {
+        TestingHelper::init();
+        self::cleanUp();
+        self::$cache = TestingHelper::$client->getOrCreateCache(self::CACHE_NAME);
+    }
+
+    public static function tearDownAfterClass(): void
+    {
+        self::cleanUp();
+        TestingHelper::cleanUp();
+    }
+    
+    public function testPutGetComplexObjects(): void
+    {
+        $value1 = new Class1();
+        $value1->field_1_1 = $this->getPrimitiveValue(ObjectType::BYTE);
+        $value1->field_1_2->field_2_1 = $this->getPrimitiveValue(ObjectType::SHORT);
+        $value1->field_1_2->field_2_2 = $this->getPrimitiveValue(ObjectType::INTEGER);
+        $value1->field_1_3 = $this->getPrimitiveValue(ObjectType::LONG);
+
+        $valueType1 = (new ComplexObjectType())->
+            setFieldType('field_1_1', ObjectType::BYTE)->
+            setFieldType('field_1_2', (new ComplexObjectType())->
+                setIgniteTypeName('Class2ShortInteger')->
+                setPhpClassName(Class2::class)->
+                setFieldType('field_2_1', ObjectType::SHORT)->
+                setFieldType('field_2_2', ObjectType::INTEGER))->
+            setFieldType('field_1_3', ObjectType::LONG);
+
+        $value2 = new SubClass1();
+        $value2->field_1_1 = $this->getPrimitiveValue(ObjectType::FLOAT);
+        $value2->field_1_2->field_2_1 = $this->getPrimitiveValue(ObjectType::DOUBLE);
+        $value2->field_1_2->field_2_2 = $this->getPrimitiveValue(ObjectType::CHAR);
+        $value2->field_1_3 = $this->getPrimitiveValue(ObjectType::BOOLEAN);
+        $value2->field_1_4 = $this->getPrimitiveValue(ObjectType::STRING);
+        $value2->field_1_5->field_3_1 = $this->getPrimitiveValue(ObjectType::DATE);
+        $value2->field_1_5->field_3_2 = $this->getPrimitiveValue(ObjectType::UUID);
+        $value2->field_1_6 = $this->getPrimitiveValue(ObjectType::DECIMAL);
+        $value2->field_1_7 = $this->getPrimitiveValue(ObjectType::TIMESTAMP);
+        $value2->field_1_8 = $this->getPrimitiveValue(ObjectType::TIME);
+
+        $valueType2 = (new ComplexObjectType())->
+            setFieldType('field_1_1', ObjectType::FLOAT)->
+            setFieldType('field_1_2', (new ComplexObjectType())->
+                setIgniteTypeName('Class2DoubleChar')->
+                setPhpClassName(Class2::class)->
+                setFieldType('field_2_1', ObjectType::DOUBLE)->
+                setFieldType('field_2_2', ObjectType::CHAR))->
+            setFieldType('field_1_3', ObjectType::BOOLEAN)->
+            setFieldType('field_1_4', ObjectType::STRING)->
+            setFieldType('field_1_5', (new ComplexObjectType())->
+                setFieldType('field_3_1', ObjectType::DATE)->
+                setFieldType('field_3_2', ObjectType::UUID))->
+            setFieldType('field_1_6', ObjectType::DECIMAL)->
+            setFieldType('field_1_7', ObjectType::TIMESTAMP)->
+            setFieldType('field_1_8', ObjectType::TIME);
+
+        $this->putGetComplexObjectsWithDifferentTypes(
+            $value1, $value2, $valueType1, $valueType2, Class1::class, SubClass1::class);
+    }
+    
+    public function testPutGetComplexObjectsWithArrays(): void
+    {
+        $value1 = new Class1();
+        $value1->field_1_1 = $this->getArrayValues(ObjectType::BYTE_ARRAY);
+        $value1->field_1_2->field_2_1 = $this->getArrayValues(ObjectType::SHORT_ARRAY);
+        $value1->field_1_2->field_2_2 = $this->getArrayValues(ObjectType::INTEGER_ARRAY);
+        $value1->field_1_3 = $this->getArrayValues(ObjectType::LONG_ARRAY);
+
+        $valueType1 = (new ComplexObjectType())->
+            setIgniteTypeName('Class1WithArrays')->
+            setPhpClassName(Class1::class)->
+            setFieldType('field_1_1', ObjectType::BYTE_ARRAY)->
+            setFieldType('field_1_2', (new ComplexObjectType())->
+                setIgniteTypeName('Class2WithShortIntegerArrays')->
+                setPhpClassName(Class2::class)->
+                setFieldType('field_2_1', ObjectType::SHORT_ARRAY)->
+                setFieldType('field_2_2', ObjectType::INTEGER_ARRAY))->
+            setFieldType('field_1_3', ObjectType::LONG_ARRAY);
+
+        $value2 = new SubClass1();
+        $value2->field_1_1 = $this->getArrayValues(ObjectType::FLOAT_ARRAY);
+        $value2->field_1_2->field_2_1 = $this->getArrayValues(ObjectType::DOUBLE_ARRAY);
+        $value2->field_1_2->field_2_2 = $this->getArrayValues(ObjectType::CHAR_ARRAY);
+        $value2->field_1_3 = $this->getArrayValues(ObjectType::BOOLEAN_ARRAY);
+        $value2->field_1_4 = $this->getArrayValues(ObjectType::STRING_ARRAY);
+        $value2->field_1_5->field_3_1 = $this->getArrayValues(ObjectType::DATE_ARRAY);
+        $value2->field_1_5->field_3_2 = $this->getArrayValues(ObjectType::UUID_ARRAY);
+        $value2->field_1_6 = $this->getArrayValues(ObjectType::DECIMAL_ARRAY);
+        $value2->field_1_7 = $this->getArrayValues(ObjectType::TIMESTAMP_ARRAY);
+        $value2->field_1_8 = $this->getArrayValues(ObjectType::TIME_ARRAY);
+
+        $valueType2 = (new ComplexObjectType())->
+            setIgniteTypeName('SubClass1WithArrays')->
+            setPhpClassName(SubClass1::class)->
+            setFieldType('field_1_1', ObjectType::FLOAT_ARRAY)->
+            setFieldType('field_1_2', (new ComplexObjectType())->
+                setIgniteTypeName('Class2WithDoubleCharArrays')->
+                setPhpClassName(Class2::class)->
+                setFieldType('field_2_1', ObjectType::DOUBLE_ARRAY)->
+                setFieldType('field_2_2', ObjectType::CHAR_ARRAY))->
+            setFieldType('field_1_3', ObjectType::BOOLEAN_ARRAY)->
+            setFieldType('field_1_4', ObjectType::STRING_ARRAY)->
+            setFieldType('field_1_5', (new ComplexObjectType())->
+                setIgniteTypeName('Class3WithArrays')->
+                setPhpClassName(Class3::class)->
+                setFieldType('field_3_1', ObjectType::DATE_ARRAY)->
+                setFieldType('field_3_2', ObjectType::UUID_ARRAY))->
+            setFieldType('field_1_6', ObjectType::DECIMAL_ARRAY)->
+            setFieldType('field_1_7', ObjectType::TIMESTAMP_ARRAY)->
+            setFieldType('field_1_8', ObjectType::TIME_ARRAY);
+
+        $this->putGetComplexObjectsWithDifferentTypes(
+            $value1, $value2, $valueType1, $valueType2, Class1::class, SubClass1::class, true);
+    }
+
+    public function testPutGetComplexObjectsWithMaps(): void
+    {
+        $value1 = new Class1();
+        $value1->field_1_1 = $this->getMapValue(ObjectType::BYTE);
+        $value1->field_1_2->field_2_1 = $this->getMapValue(ObjectType::SHORT);
+        $value1->field_1_2->field_2_2 = $this->getMapValue(ObjectType::INTEGER);
+        $value1->field_1_3 = $this->getMapValue(ObjectType::LONG);
+
+        $valueType1 = (new ComplexObjectType())->
+            setIgniteTypeName('Class1WithMaps')->
+            setPhpClassName(Class1::class)->
+            setFieldType('field_1_1', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::BYTE, ObjectType::BYTE))->
+            setFieldType('field_1_2', (new ComplexObjectType())->
+                setIgniteTypeName('Class2WithShortIntegerMaps')->
+                setPhpClassName(Class2::class)->
+                setFieldType('field_2_1', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::SHORT, ObjectType::SHORT))->
+                setFieldType('field_2_2', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::INTEGER, ObjectType::INTEGER)))->
+            setFieldType('field_1_3', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::STRING, ObjectType::LONG));
+
+        $value2 = new SubClass1();
+        $value2->field_1_1 = $this->getMapValue(ObjectType::FLOAT);
+        $value2->field_1_2->field_2_1 = $this->getMapValue(ObjectType::DOUBLE);
+        $value2->field_1_2->field_2_2 = $this->getMapValue(ObjectType::CHAR);
+        $value2->field_1_3 = $this->getMapValue(ObjectType::BOOLEAN);
+        $value2->field_1_4 = $this->getMapValue(ObjectType::STRING);
+        $value2->field_1_5->field_3_1 = $this->getMapValue(ObjectType::DATE);
+        $value2->field_1_5->field_3_2 = $this->getMapValue(ObjectType::UUID);
+        $value2->field_1_6 = $this->getMapValue(ObjectType::DECIMAL);
+        $value2->field_1_7 = $this->getMapValue(ObjectType::TIMESTAMP);
+        $value2->field_1_8 = $this->getMapValue(ObjectType::TIME);
+
+        $valueType2 = (new ComplexObjectType())->
+            setIgniteTypeName('SubClass1WithArrays')->
+            setPhpClassName(SubClass1::class)->
+            setFieldType('field_1_1', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::STRING, ObjectType::FLOAT))->
+            setFieldType('field_1_2', (new ComplexObjectType())->
+                setIgniteTypeName('Class2WithDoubleCharArrays')->
+                setPhpClassName(Class2::class)->
+                setFieldType('field_2_1', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::STRING, ObjectType::DOUBLE))->
+                setFieldType('field_2_2', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::CHAR, ObjectType::CHAR)))->
+            setFieldType('field_1_3', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::BOOLEAN, ObjectType::BOOLEAN))->
+            setFieldType('field_1_4', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::STRING, ObjectType::STRING))->
+            setFieldType('field_1_5', (new ComplexObjectType())->
+                setIgniteTypeName('Class3WithArrays')->
+                setPhpClassName(Class3::class)->
+                setFieldType('field_3_1', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::STRING, ObjectType::DATE))->
+                setFieldType('field_3_2', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::STRING, ObjectType::UUID)))->
+            setFieldType('field_1_6', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::STRING, ObjectType::DECIMAL))->
+            setFieldType('field_1_7', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::STRING, ObjectType::TIMESTAMP))->
+            setFieldType('field_1_8', new MapObjectType(MapObjectType::HASH_MAP, ObjectType::STRING, ObjectType::TIME));
+
+        $this->putGetComplexObjectsWithMaps(
+            $value1, $value2, $valueType1, $valueType2, Class1::class, SubClass1::class);
+    }
+
+    public function testPutGetBinaryObjectsFromObjects(): void
+    {
+        $valueType = (new ComplexObjectType())->
+            setIgniteTypeName('Class1WithStringObjStringArray')->
+            setPhpClassName(Class1::class)->
+            setFieldType('field_1_1', ObjectType::STRING)->
+            setFieldType('field_1_2', (new ComplexObjectType())->
+                setIgniteTypeName('Class2WithShortBoolean')->
+                setPhpClassName(Class2::class)->
+                setFieldType('field_2_1', ObjectType::SHORT)->
+                setFieldType('field_2_2', ObjectType::BOOLEAN))->
+            setFieldType('field_1_3', ObjectType::STRING_ARRAY);
+        $this->putGetBinaryObjects($valueType);
+        $defaultValueType = (new ComplexObjectType())->
+            setIgniteTypeName('Class1Default')->
+            setPhpClassName(Class1::class)->
+            setFieldType('field_1_2', (new ComplexObjectType())->
+                setIgniteTypeName('Class2Default')->
+                setPhpClassName(Class2::class));
+        $this->putGetBinaryObjects($defaultValueType);
+    }
+    
+    private function putGetComplexObjectsWithDifferentTypes(
+        $key, $value, $keyType, $valueType, $keyClass, $valueClass, $isNullable = false)
+    {
+        $this->putGetComplexObjects($key, $value, $keyType, $valueType, $value);
+
+        $binaryKey = BinaryObject::fromObject($key, $keyType);
+        $binaryValue = BinaryObject::fromObject($value, $valueType);
+        $this->putGetComplexObjects($binaryKey, $binaryValue, null, null, $value);
+
+        if ($isNullable) {
+            $this->putGetComplexObjects(new $keyClass(), new $valueClass(), $keyType, $valueType, new $valueClass());
+        }
+
+        if ($isNullable) {
+            $binaryKey = BinaryObject::fromObject(new $keyClass(), $keyType);
+            $binaryValue = BinaryObject::fromObject(new $valueClass(), $valueType);
+            $this->putGetComplexObjects($binaryKey, $binaryValue, null, null, new $valueClass());
+        }
+    }
+
+    private function putGetComplexObjectsWithMaps(
+        $key, $value, $keyType, $valueType, $keyClass, $valueClass)
+    {
+        $this->putGetComplexObjects($key, $value, $keyType, $valueType, $value);
+
+        $this->putGetComplexObjects(new $keyClass(), new $valueClass(), $keyType, $valueType, new $valueClass());
+    }
+
+    private function putGetComplexObjects($key, $value, $keyType, $valueType, $valuePattern)
+    {
+        self::$cache->
+            setKeyType($keyType)->
+            setValueType($valueType);
+        try {
+            self::$cache->put($key, $value);
+            $result = self::$cache->get($key);
+            $strResult = TestingHelper::printValue($result);
+            $strValue = TestingHelper::printValue($valuePattern);
+            $this->assertTrue(
+                TestingHelper::compare($valuePattern, $result),
+                "values are not equal: put value={$strValue}, get value={$strResult}");
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+    
+    private function putGetBinaryObjects($valueType): void
+    {
+        $value1 = new Class1();
+        $value1->field_1_1 = 'abc';
+        $value1->field_1_2->field_2_1 = 1234;
+        $value1->field_1_2->field_2_2 = true;
+        $value1->field_1_3 = ['a', 'bb', 'ccc'];
+
+        $value2 = new Class1();
+        $value2->field_1_1 = 'def';
+        $value2->field_1_2->field_2_1 = 5432;
+        $value2->field_1_2->field_2_2 = false;
+        $value2->field_1_3 = ['a', 'bb', 'ccc', 'dddd'];
+
+        $value3 = new Class1();
+        $value3->field_1_1 = 'defdef';
+        $value3->field_1_2->field_2_1 = 543;
+        $value3->field_1_2->field_2_2 = false;
+        $value3->field_1_3 = ['a', 'bb', 'ccc', 'dddd', 'eeeee'];
+
+        $field_1_2_Type = $valueType ? $valueType->getFieldType('field_1_2') : null;
+
+        $binaryValue1 = BinaryObject::fromObject($value1, $valueType);
+        $binaryValue2 = BinaryObject::fromObject($value2, $valueType);
+        $binaryValue3 = BinaryObject::fromObject($value3, $valueType);
+
+        self::$cache->
+            setKeyType(null)->
+            setValueType(null);
+        $cache = self::$cache;
+        try {
+            $cache->put($binaryValue1, $binaryValue2);
+            $result = $cache->get($binaryValue1);
+            $this->binaryObjectEquals($result, $value2, $valueType);
+
+            $binaryValue1->setField('field_1_1', 'abcde');
+            $result = $cache->get($binaryValue1);
+            $this->assertTrue($result === null);
+
+            $binaryValue2->setField('field_1_1', $value3->field_1_1);
+            $binaryValue2->setField('field_1_2', $value3->field_1_2, $field_1_2_Type);
+            $binaryValue2->setField('field_1_3', $value3->field_1_3);
+            $cache->put($binaryValue1, $binaryValue2);
+            $result = $cache->get($binaryValue1);
+            $this->binaryObjectEquals($result, $value3, $valueType);
+
+            $binaryValue1->setField('field_1_1', 'abc');
+            $binaryValue1->setField('field_1_3', $binaryValue1->getField('field_1_3'));
+            $result = $cache->get($binaryValue1);
+            $this->binaryObjectEquals($result, $value2, $valueType);
+
+            $result = $cache->get($binaryValue1);
+            $this->binaryObjectEquals($result, $value2, $valueType);
+
+            $binaryValue3->setField('field_1_1', $result->getField('field_1_1'));
+            $binaryValue3->setField('field_1_2', $result->getField('field_1_2', $field_1_2_Type), $field_1_2_Type);
+            $binaryValue3->setField('field_1_3', $result->getField('field_1_3'));
+            $cache->put($binaryValue1, $binaryValue3);
+            $result = $cache->get($binaryValue1);
+            $this->binaryObjectEquals($result, $value2, $valueType);
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+    
+    private function binaryObjectEquals(BinaryObject $binaryObj, $valuePattern, $valueType): void
+    {
+        $strBinObj = TestingHelper::printValue($binaryObj);
+        $strValue = TestingHelper::printValue($valuePattern);
+        $this->assertTrue(
+            TestingHelper::compare($valuePattern, $binaryObj),
+            "binary values are not equal: put value={$strValue}, get value={$strBinObj}");
+        if ($valueType) {
+            $object = $binaryObj->toObject($valueType);
+            $strObject = TestingHelper::printValue($object);
+            $this->assertTrue(
+                TestingHelper::compare($valuePattern, $object),
+                "values are not equal: put value={$strValue}, get value={$strObject}");
+        }
+    }
+    
+    private function getPrimitiveValue(int $typeCode)
+    {
+        return TestingHelper::$primitiveValues[$typeCode]['values'][0];
+    }
+    
+    private function getArrayValues(int $typeCode)
+    {
+        return TestingHelper::$primitiveValues[TestingHelper::$arrayValues[$typeCode]['elemType']]['values'];
+    }
+    
+    private function getMapValue(int $typeCode)
+    {
+        $map = new Map();
+        $values = TestingHelper::$primitiveValues[$typeCode]['values'];
+        $length = count($values);
+        for ($i = 0; $i < $length; $i++) {
+            $value = $values[$i];
+            if (!TestingHelper::$primitiveValues[$typeCode]['isMapKey']) {
+                $value = print_r($value, true);
+            }
+            $map->put($value, $values[$length - $i - 1]);
+        }
+        return $map;
+    }
+    
+    private static function cleanUp(): void
+    {
+        TestingHelper::destroyCache(self::CACHE_NAME);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/tests/ScanQueryTest.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/tests/ScanQueryTest.php b/modules/platforms/php/tests/ScanQueryTest.php
new file mode 100644
index 0000000..07b9f61
--- /dev/null
+++ b/modules/platforms/php/tests/ScanQueryTest.php
@@ -0,0 +1,167 @@
+<?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\Tests;
+
+use Ds\Set;
+use PHPUnit\Framework\TestCase;
+use Apache\Ignite\Query\ScanQuery;
+use Apache\Ignite\Type\ObjectType;
+use Apache\Ignite\Cache\CacheEntry;
+
+final class ScanQueryTestCase extends TestCase
+{
+    const CACHE_NAME = '__php_test_cache_scan_query';
+    const CACHE_NAME2 = '__php_test_cache_scan_query_2';
+    const ELEMENTS_NUMBER = 10;
+
+    private static $cache;
+
+    public static function setUpBeforeClass(): void
+    {
+        TestingHelper::init();
+        self::cleanUp();
+        self::$cache = TestingHelper::$client->getOrCreateCache(self::CACHE_NAME);
+        self::generateData();
+    }
+
+    public static function tearDownAfterClass(): void
+    {
+        self::cleanUp();
+        TestingHelper::cleanUp();
+    }
+    
+    public function testGetAll(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query(new ScanQuery());
+        $set = new Set();
+        foreach ($cursor->getAll() as $cacheEntry) {
+            $this->checkCursorResult($cacheEntry);
+            $set->add($cacheEntry->getKey());
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testGetAllWithPageSize(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new ScanQuery())->setPageSize(1));
+        $set = new Set();
+        foreach ($cursor->getAll() as $cacheEntry) {
+            $this->checkCursorResult($cacheEntry);
+            $set->add($cacheEntry->getKey());
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testIterateCursor(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query(new ScanQuery());
+        $set = new Set();
+        foreach ($cursor as $cacheEntry) {
+            $this->checkCursorResult($cacheEntry);
+            $set->add($cacheEntry->getKey());
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testIterateCursorWithPageSize(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new ScanQuery())->setPageSize(2));
+        $set = new Set();
+        foreach ($cursor as $cacheEntry) {
+            $this->checkCursorResult($cacheEntry);
+            $set->add($cacheEntry->getKey());
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testCloseCursor(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new ScanQuery())->setPageSize(1));
+        $cursor->rewind();
+        $this->assertTrue($cursor->valid());
+        $this->checkCursorResult($cursor->current());
+        $cursor->next();
+        $cursor->close();
+    }
+
+    public function testCloseCursorAfterGetAll(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new ScanQuery())->setPageSize(1));
+        $cursor->getAll();
+        $cursor->close();
+        $this->assertTrue(true);
+    }
+
+    public function testScanQuerySettings(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new ScanQuery())->
+            setPartitionNumber(0)->
+            setPageSize(2)->
+            setLocal(true));
+        $cursor->getAll();
+        $this->assertTrue(true);
+    }
+
+    public function testScanEmptyCache(): void
+    {
+        $cache = TestingHelper::$client->getOrCreateCache(self::CACHE_NAME2);
+        $cache->removeAll();
+        $cursor = $cache->query(new ScanQuery());
+        $cacheEntries = $cursor->getAll();
+        $this->assertEquals(count($cacheEntries), 0);
+
+        $cursor = $cache->query(new ScanQuery());
+        foreach ($cursor as $entry) {
+            $this->assertTrue(false);
+        }
+        $cursor->close();
+    }
+
+    private function checkCursorResult(CacheEntry $cacheEntry): void
+    {
+        $this->assertEquals($cacheEntry->getValue(), self::generateValue($cacheEntry->getKey()));
+        $this->assertTrue($cacheEntry->getKey() >= 0 && $cacheEntry->getKey() < self::ELEMENTS_NUMBER);
+    }
+
+    private static function generateData(): void
+    {
+        $cache = self::$cache;
+        $cache->setKeyType(ObjectType::INTEGER);
+        for ($i = 0; $i < self::ELEMENTS_NUMBER; $i++) {
+            $cache->put($i, self::generateValue($i));
+        }
+    }
+
+    private static function generateValue(int $key): string
+    {
+        return 'value' . $key;
+    }
+
+    private static function cleanUp(): void
+    {
+        TestingHelper::destroyCache(self::CACHE_NAME);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/tests/SqlFieldsQueryTest.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/tests/SqlFieldsQueryTest.php b/modules/platforms/php/tests/SqlFieldsQueryTest.php
new file mode 100644
index 0000000..3e74ca0
--- /dev/null
+++ b/modules/platforms/php/tests/SqlFieldsQueryTest.php
@@ -0,0 +1,200 @@
+<?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\Tests;
+
+use Ds\Set;
+use Apache\Ignite\Cache\CacheConfiguration;
+use Apache\Ignite\Query\SqlFieldsQuery;
+use PHPUnit\Framework\TestCase;
+use Apache\Ignite\Type\ObjectType;
+
+final class SqlFieldsQueryTestCase extends TestCase
+{
+    const CACHE_NAME = '__php_test_sql_fields_query';
+    const ELEMENTS_NUMBER = 10;
+    const TABLE_NAME = '__php_test_SqlFieldsQuery_table';
+
+    private static $cache;
+    private static $selectFromTable;
+
+    public static function setUpBeforeClass(): void
+    {
+        TestingHelper::init();
+        self::cleanUp();
+        self::$cache = TestingHelper::$client->getOrCreateCache(
+            self::CACHE_NAME,
+            (new CacheConfiguration())->setSqlSchema('PUBLIC'));
+        self::generateData();
+        $tableName = self::TABLE_NAME;
+        self::$selectFromTable = "SELECT * FROM {$tableName}";
+    }
+
+    public static function tearDownAfterClass(): void
+    {
+        self::dropTables();
+        self::cleanUp();
+        TestingHelper::cleanUp();
+    }
+    
+    public function testGetAll(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query(new SqlFieldsQuery(self::$selectFromTable));
+        $set = new Set();
+        foreach ($cursor->getAll() as $fields) {
+            $this->checkCursorResult($fields);
+            $set->add($fields[0]);
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testGetAllWithPageSize(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new SqlFieldsQuery(self::$selectFromTable))->setPageSize(1));
+        $set = new Set();
+        foreach ($cursor->getAll() as $fields) {
+            $this->checkCursorResult($fields);
+            $set->add($fields[0]);
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testIterateCursor(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query(new SqlFieldsQuery(self::$selectFromTable));
+        $set = new Set();
+        foreach ($cursor as $fields) {
+            $this->checkCursorResult($fields);
+            $set->add($fields[0]);
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testIterateCursorWithPageSize(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new SqlFieldsQuery(self::$selectFromTable))->setPageSize(2));
+        $set = new Set();
+        foreach ($cursor as $fields) {
+            $this->checkCursorResult($fields);
+            $set->add($fields[0]);
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testCloseCursor(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new SqlFieldsQuery(self::$selectFromTable))->setPageSize(1));
+        $cursor->rewind();
+        $this->assertTrue($cursor->valid());
+        $this->checkCursorResult($cursor->current());
+        $cursor->next();
+        $cursor->close();
+    }
+
+    public function testCloseCursorAfterGetAll(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query(new SqlFieldsQuery(self::$selectFromTable));
+        $cursor->getAll();
+        $cursor->close();
+        $this->assertTrue(true);
+    }
+
+    public function testSqlFieldsQuerySettings(): void
+    {
+        $tableName = self::TABLE_NAME;
+        $cache = self::$cache;
+        $cursor = $cache->query((new SqlFieldsQuery(self::$selectFromTable))->
+            setPageSize(2)->
+            setLocal(false)->
+            setSql("INSERT INTO {$tableName} (field1, field2) VALUES (?, ?)")->
+            setArgTypes(ObjectType::INTEGER, ObjectType::STRING)->
+            setArgs(50, 'test')->
+            setDistributedJoins(true)->
+            setReplicatedOnly(false)->
+            setTimeout(10000)->
+            setSchema('PUBLIC')->
+            setMaxRows(20)->
+            setStatementType(SqlFieldsQuery::STATEMENT_TYPE_ANY)->
+            setEnforceJoinOrder(true)->
+            setCollocated(false)->
+            setLazy(true)->
+            setIncludeFieldNames(true));
+        $cursor->getAll();
+        $this->assertTrue(true);
+    }
+
+    public function testGetEmptyResults(): void
+    {
+        $tableName = self::TABLE_NAME;
+        $cache = self::$cache;
+        $cursor = $cache->query(new SqlFieldsQuery("SELECT * FROM {$tableName} WHERE field1 > 100"));
+        $entries = $cursor->getAll();
+        $this->assertEquals(count($entries), 0);
+        $cursor->close();
+
+        $cursor = $cache->query(new SqlFieldsQuery("SELECT * FROM {$tableName} WHERE field1 > 100"));
+        foreach ($cursor as $fields) {
+            $this->assertTrue(false);
+        }
+        $cursor->close();
+    }
+
+    private function checkCursorResult(array $fields): void
+    {
+        $this->assertEquals(count($fields), 2);
+        $this->assertEquals($fields[1], self::generateValue($fields[0]));
+        $this->assertTrue($fields[0] >= 0 && $fields[0] < self::ELEMENTS_NUMBER);
+    }
+
+    private static function dropTables(): void
+    {
+        $tableName = self::TABLE_NAME;
+        self::$cache->query(new SqlFieldsQuery("DROP TABLE {$tableName}"))->getAll();
+    }
+
+    private static function generateData(): void
+    {
+        $tableName = self::TABLE_NAME;
+        $cache = self::$cache;
+        $cache->query(new SqlFieldsQuery(
+            "CREATE TABLE IF NOT EXISTS {$tableName} (field1 INT, field2 VARCHAR, PRIMARY KEY (field1))"
+        ))->getAll();
+        $insertQuery = (new SqlFieldsQuery("INSERT INTO {$tableName} (field1, field2) VALUES (?, ?)"))->
+            setArgTypes(ObjectType::INTEGER);
+
+        for ($i = 0; $i < self::ELEMENTS_NUMBER; $i++) {
+            $cache->query($insertQuery->setArgs($i, self::generateValue($i)))->getAll();
+        }
+    }
+
+    private static function generateValue(int $key): string
+    {
+        return 'value' . $key;
+    }
+
+    private static function cleanUp(): void
+    {
+        TestingHelper::destroyCache(self::CACHE_NAME);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/tests/SqlQueryTest.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/tests/SqlQueryTest.php b/modules/platforms/php/tests/SqlQueryTest.php
new file mode 100644
index 0000000..7c272c7
--- /dev/null
+++ b/modules/platforms/php/tests/SqlQueryTest.php
@@ -0,0 +1,204 @@
+<?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\Tests;
+
+use Ds\Set;
+use PHPUnit\Framework\TestCase;
+use Apache\Ignite\Cache\CacheEntry;
+use Apache\Ignite\Cache\QueryEntity;
+use Apache\Ignite\Cache\QueryField;
+use Apache\Ignite\Cache\CacheConfiguration;
+use Apache\Ignite\Query\SqlQuery;
+use Apache\Ignite\Query\SqlFieldsQuery;
+use Apache\Ignite\Type\ObjectType;
+use Apache\Ignite\Type\ComplexObjectType;
+
+class SqlQueryTestClass
+{
+    public $field1;
+    public $field2;
+}
+
+final class SqlQueryTestCase extends TestCase
+{
+    const CACHE_NAME = '__php_test_sql_query';
+    const ELEMENTS_NUMBER = 10;
+    const TABLE_NAME = '__php_test_SqlQuery_table';
+
+    private static $cache;
+    private static $selectFromTable;
+
+    public static function setUpBeforeClass(): void
+    {
+        TestingHelper::init();
+        self::cleanUp();
+        self::$cache = (TestingHelper::$client->getOrCreateCache(
+            self::CACHE_NAME,
+            (new CacheConfiguration())->
+                setQueryEntities((new QueryEntity())->
+                    setKeyTypeName('java.lang.Integer')->
+                    setValueTypeName(self::TABLE_NAME)->
+                    setFields(
+                        new QueryField('field1', 'java.lang.Integer'),
+                        new QueryField('field2', 'java.lang.String')))))->
+            setKeyType(ObjectType::INTEGER)->
+            setValueType((new ComplexObjectType())->
+            setIgniteTypeName(self::TABLE_NAME)->
+            setPhpClassName(SqlQueryTestClass::class)->
+            setFieldType('field1', ObjectType::INTEGER));
+        self::generateData();
+        $tableName = self::TABLE_NAME;
+        self::$selectFromTable = "SELECT * FROM {$tableName}";
+    }
+
+    public static function tearDownAfterClass(): void
+    {
+        self::cleanUp();
+        TestingHelper::cleanUp();
+    }
+    
+    public function testGetAll(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new SqlQuery(self::TABLE_NAME, self::$selectFromTable)));
+        $set = new Set();
+        foreach ($cursor->getAll() as $cacheEntry) {
+            $this->checkCursorResult($cacheEntry);
+            $set->add($cacheEntry->getKey());
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testGetAllWithPageSize(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new SqlQuery(self::TABLE_NAME, self::$selectFromTable))->setPageSize(1));
+        $set = new Set();
+        foreach ($cursor->getAll() as $cacheEntry) {
+            $this->checkCursorResult($cacheEntry);
+            $set->add($cacheEntry->getKey());
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testIterateCursor(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query(new SqlQuery(self::TABLE_NAME, self::$selectFromTable));
+        $set = new Set();
+        foreach ($cursor as $cacheEntry) {
+            $this->checkCursorResult($cacheEntry);
+            $set->add($cacheEntry->getKey());
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testIterateCursorWithPageSize(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new SqlQuery(self::TABLE_NAME, self::$selectFromTable))->setPageSize(2));
+        $set = new Set();
+        foreach ($cursor as $cacheEntry) {
+            $this->checkCursorResult($cacheEntry);
+            $set->add($cacheEntry->getKey());
+        }
+        $this->assertEquals($set->count(), self::ELEMENTS_NUMBER);
+    }
+
+    public function testCloseCursor(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new SqlQuery(self::TABLE_NAME, self::$selectFromTable))->setPageSize(1));
+        $cursor->rewind();
+        $this->assertTrue($cursor->valid());
+        $this->checkCursorResult($cursor->current());
+        $cursor->next();
+        $cursor->close();
+    }
+
+    public function testCloseCursorAfterGetAll(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query(new SqlQuery(self::TABLE_NAME, self::$selectFromTable));
+        $cursor->getAll();
+        $cursor->close();
+        $this->assertTrue(true);
+    }
+
+    public function testSqlQuerySettings(): void
+    {
+        $cache = self::$cache;
+        $cursor = $cache->query((new SqlQuery(self::TABLE_NAME, self::$selectFromTable))->
+            setType(self::TABLE_NAME)->
+            setPageSize(2)->
+            setLocal(false)->
+            setSql('field1 > ? and field1 <= ?')->
+            setArgTypes(ObjectType::INTEGER, ObjectType::INTEGER)->
+            setArgs(3, 7)->
+            setDistributedJoins(true)->
+            setReplicatedOnly(false)->
+            setTimeout(10000));
+        $cursor->getAll();
+        $this->assertTrue(true);
+    }
+
+    public function testGetEmptyResults(): void
+    {
+        $tableName = self::TABLE_NAME;
+        $cache = self::$cache;
+        $cursor = $cache->query((new SqlQuery(self::TABLE_NAME, 'field1 > ?'))->setArgs(self::ELEMENTS_NUMBER));
+        $entries = $cursor->getAll();
+        $this->assertEquals(count($entries), 0);
+        $cursor->close();
+
+        $cursor = $cache->query((new SqlQuery(self::TABLE_NAME, 'field1 > ?'))->setArgs(self::ELEMENTS_NUMBER));
+        foreach ($cursor as $entry) {
+            $this->assertTrue(false);
+        }
+        $cursor->close();
+    }
+
+    private function checkCursorResult(CacheEntry $cacheEntry): void
+    {
+        $this->assertEquals($cacheEntry->getValue()->field2, self::generateValue($cacheEntry->getKey()));
+        $this->assertTrue($cacheEntry->getKey() >= 0 && $cacheEntry->getKey() < self::ELEMENTS_NUMBER);
+    }
+
+    private static function generateData(): void
+    {
+        $tableName = self::TABLE_NAME;
+        $cache = self::$cache;
+        $insertQuery = (new SqlFieldsQuery("INSERT INTO {$tableName} (_key, field1, field2) VALUES (?, ?, ?)"))->
+            setArgTypes(ObjectType::INTEGER, ObjectType::INTEGER);
+
+        for ($i = 0; $i < self::ELEMENTS_NUMBER; $i++) {
+            $cache->query($insertQuery->setArgs($i, $i, self::generateValue($i)))->getAll();
+        }
+    }
+
+    private static function generateValue(int $key): string
+    {
+        return 'value' . $key;
+    }
+
+    private static function cleanUp(): void
+    {
+        TestingHelper::destroyCache(self::CACHE_NAME);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f28e4f3f/modules/platforms/php/tests/TestConfig.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/tests/TestConfig.php b/modules/platforms/php/tests/TestConfig.php
new file mode 100644
index 0000000..b38f890
--- /dev/null
+++ b/modules/platforms/php/tests/TestConfig.php
@@ -0,0 +1,37 @@
+<?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\Tests;
+
+class TestConfig
+{
+    public static $endpoints = ['127.0.0.1:10800'];
+    public static $debug = false;
+    
+    public static function init(): void
+    {
+        $endpoints = getenv('APACHE_IGNITE_CLIENT_ENDPOINTS');
+        if ($endpoints) {
+            TestConfig::$endpoints = explode(',', $endpoints);
+        }
+        $debug = getenv('APACHE_IGNITE_CLIENT_DEBUG');
+        TestConfig::$debug = ($debug === 'true' || $debug === '1');
+    }
+}
+
+TestConfig::init();
\ No newline at end of file