From 99fc5372d69e773a623bec78b91061857d0b6832 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Mon, 13 Jun 2022 13:54:54 +0500 Subject: [PATCH] Added functions for validation of oneof/anyof params by type (#27) * added methods to checkForType and getType for any value in JsonMapper * style fixes * added tests for newly added function * added more tests for edge cases and fix a bug * done changes recommended by sufyan * addition of type checking for anyof/oneof types in typeGroups along with test cases * addition of apply factory methods in library along with unit tests * fix a test case failure in PHP 7.0 * fix a test case failure in PHP 7.2 --- src/JsonMapper.php | 306 ++++++++++- src/JsonMapperException.php | 25 +- src/TypeCombination.php | 92 +++- tests/multitypetest/MultiTypeJsonMapper.php | 19 + tests/multitypetest/MultiTypeTest.php | 478 ++++++++++++++++-- tests/multitypetest/model/DaysEnum.php | 66 +++ tests/multitypetest/model/MonthNameEnum.php | 78 +++ tests/multitypetest/model/MonthNumberEnum.php | 78 +++ 8 files changed, 1066 insertions(+), 76 deletions(-) create mode 100644 tests/multitypetest/MultiTypeJsonMapper.php create mode 100644 tests/multitypetest/model/DaysEnum.php create mode 100644 tests/multitypetest/model/MonthNameEnum.php create mode 100644 tests/multitypetest/model/MonthNumberEnum.php diff --git a/src/JsonMapper.php b/src/JsonMapper.php index 2102572bf..f7a5396ae 100644 --- a/src/JsonMapper.php +++ b/src/JsonMapper.php @@ -302,7 +302,8 @@ public function getArrayTypeAndDimensions($type, $dimensions = 0) /** * Try calling the factory method if exists, otherwise throw JsonMapperException * - * @param string $factoryMethod factory method in the format "type method()" + * @param string $factoryMethod factory method in the format + * 'path/to/callable/function argType' * @param mixed $value value to be passed in as param into factory * method. * @param string $strClassName strClassName referencing this factory method @@ -324,36 +325,41 @@ protected function callFactoryMethod($factoryMethod, $value, $strClassName) } /** - * Try calling deserializer function with value, return true if call successful. + * Try calling the given function with value, return [true, updatedValue] + * if call successful. * - * @param mixed $value value to be checked if deserializable. - * @param string $deserializer deserializer function string in the format - * "pathToCallableFunction typeOfValue". + * @param mixed $value value to be passed in argument of factory method. + * @param string $factoryMethod factory method string in the format + * 'path/to/callable/function argType'. * - * @return bool Return true if is value Deserializable, false otherwise. + * @return array Return an array [bool $success, $value] and value will be the + * failure cause if not success. */ - protected function isValueDeserializable($value, $deserializer) + protected function callFactoryWithErrorHandling($value, $factoryMethod) { + $success = true; if (version_compare(phpversion(), '7.0', '<')) { try { - $this->callFactoryMethod($deserializer, $value, ''); + $value = $this->callFactoryMethod($factoryMethod, $value, ''); } catch (Exception $e) { // In Php versions < 7.0 catching only exceptions but not typeErrors // since strict types were not available for php < 7.0 // also we can't use throwable since its only available after php 7.0 - return false; + $success = false; + $value = $e->getMessage(); } } else { try { - $this->callFactoryMethod($deserializer, $value, ''); + $value = $this->callFactoryMethod($factoryMethod, $value, ''); } catch (\Throwable $e) { // In Php versions >= 7.0 catching exceptions including typeErrors // using Throwable since its base interface for Exceptions & Errors // since types can be strict for php >= 7.0 - return false; + $success = false; + $value = $e->getMessage(); } } - return true; + return [$success, $value]; } /** @@ -507,6 +513,239 @@ protected function isAssociativeOrIndexed($value) return [false, true]; } + /** + * Gets not nested type for the given value + * + * @param mixed $value Value to be checked for type + * + * @return string|false Return flat PHP types for the given value + * and if not flat type return false. + */ + protected function getFlatType($value) + { + $type = gettype($value); + if (!$this->isFlatType($type)) { + return false; + } + switch ($type) { + case 'integer': + $type = 'int'; + break; + case 'double': + $type = 'float'; + break; + case 'boolean': + $type = 'bool'; + break; + case 'NULL': + $type = 'null'; + break; + } + return $type; + } + + /** + * Check all given factory methods that can be called with given value. + * + * @param mixed $value Any value to be checked with factoryMethods. + * @param mixed $newVal A copy of value to be updated. + * @param string $type Extracted type of the value. + * @param string[] $factoryMethods Methods in the format 'path/to/method argType' + * which will be converting $value into any + * desirable type. + * + * @return string Returns the type or typeGroup of value based on + * given factory methods. + * @throws JsonMapperException + */ + protected function applyFactoryMethods($value, &$newVal, $type, $factoryMethods) + { + $errorMsg = []; + $types = [$type]; // list of possible types + foreach ($factoryMethods as $m) { + // checking each provided factory method + $method = explode(' ', $m); + // try calling factory method + list($success, $val) = $this->callFactoryWithErrorHandling($value, $m); + if ($success) { + if ($type == $method[1]) { + // if method call is successful + // and given type equals to argType of factory method + // update the value with returned $val of factory method + // and return with type early + $newVal = $val; + return $type; + } + // if method call is successful + // and given type is not same as argType of factory method + // then add argType in list of possible types for $value + array_push($types, $method[1]); + } elseif ($type == $method[1]) { + // if method call is failure given type equals to argType of + // factory method then add reason $val as an error message + array_push($errorMsg, "$method[0]: $val"); + } + } + if (!empty($errorMsg)) { + // if any error msg is added then throw exception + throw JsonMapperException::invalidArgumentFactoryMethodException( + $type, + join("\n", $errorMsg) + ); + } + // converting possible types array into the string format + // of an anyof typeGroup + $types = array_unique($types); + asort($types); + $type = join(',', $types); + if (count($types) > 1) { + // wrap in brackets for multiple types + $type = "($type)"; + } + return $type; + } + + /** + * Extract type from any given value. + * + * @param mixed $value Any value to be checked for type, should be + * an array if checking for inner type + * @param string[] $factory Methods in the format 'path/to/method argType' + * which will be converting $value into any + * desirable type, Default: [] + * @param string $start string to be appended at the start of the + * extracted type, Default: '' + * @param string $end string to be appended at the end of the + * extracted type, Default: '' + * + * @return string Returns the type that could be mapped on the given value. + * @throws JsonMapperException + */ + protected function getType(&$value, $factory = [], $start = '', $end = '') + { + $type = $this->getFlatType($value); + $newVal = $value; + if (!$type && is_array($value)) { + if ($this->isAssociativeOrIndexed($value)[0]) { + // if value is associative array + $start .= 'array $v) { + array_push($types, $this->getType($v, $factory)); + $newVal[$k] = $v; + } + $types = array_unique($types); + asort($types); + $isOneOfOrAnyOf = !empty($types) && substr($types[0], -1) === ')'; + if (count($types) > 1 || $isOneOfOrAnyOf) { + // wrap in brackets for multiple types or oneof/anyof type + $start .= '('; + $end = ')' . $end; + } + $type = join(',', $types); + } elseif (!$type && is_object($value)) { + $class = get_class($value); // returns full path of class + $slashPos = strrpos($class, '\\'); + if (!$slashPos) { + // if slash not found then replace with -1 + $slashPos = -1; + } + $type = substr($class, ++$slashPos); + } + $type = "$start$type$end"; + if (!empty($factory)) { + $type = $this->applyFactoryMethods($value, $newVal, $type, $factory); + } + $value = $newVal; + return $type; + } + + /** + * Check the given type/types in the provided typeGroup, return true if + * type(s) exists in the typeGroup + * + * @param TypeCombination|string $typeGroup TypesCombination object or string + * format for grouped types. All kind + * of groups are allowed here. + * @param TypeCombination|string $type Can be a normal type like string[], + * int, Car, etc. or a combination of + * types like (CarA,CarB)[], (int,Enum), + * or array. + * @param string $start prefix used by string $type, + * Default: "" + * @param string $end postfix used by string $type, + * Default: "" + * + * @return bool + */ + protected function checkForType($typeGroup, $type, $start = '', $end = '') + { + if (is_string($typeGroup)) { + // convert into TypeCombination object + $typeGroup = TypeCombination::withFormat($typeGroup); + } + if (is_string($type) && strpos($type, '(') !== false) { + // for combination of types like: (A,B)[] or array + // convert into TypeCombination object + $type = TypeCombination::withFormat($type); + } + $checkAllInner = false; // required when $type instance of TypeCombination. + if (is_string($type)) { + // for checking simple types like: string, int[] or Car[] + if ($typeGroup->getGroupName() == 'map') { + $start .= 'arraygetGroupName() == 'array') { + $end = '[]' . $end; + } + foreach ($typeGroup->getTypes() as $t) { + if (is_string($t)) { + $matched = $type === "$start$t$end"; + } else { + $matched = $this->checkForType($t, $type, $start, $end); + } + if ($matched) { + // if any type in the typeGroup matched with given type, + // then early return true + return true; + } + } + return false; + } elseif (in_array($type->getGroupName(), ['array','map'])) { + // To handle type if its array/map group of types + // extract all internal groups from the given typeGroup that + // are similar to $type + $typeGroup = TypeCombination::with($typeGroup->extractSimilar($type)); + // update type to the innermost level of oneof/anyof + $type = $type->extractOneOfAnyOfGroup(); + // check all inner elements of $type + $checkAllInner = true; + } + // To handle type if its oneof/anyof group of types + foreach ($type->getTypes() as $t) { + $contains = $this->checkForType($typeGroup, $t); + if (!$checkAllInner && $contains) { + // if any type is found then + // type is matched with $typeGroup + return true; + } + if ($checkAllInner && !$contains) { + // if any type is missing then + // type is not matched with $typeGroup + return false; + } + } + return $checkAllInner; + } + /** * Converts the given typeCombination into its string format. * @@ -514,11 +753,39 @@ protected function isAssociativeOrIndexed($value) * * @return string */ - protected static function formatType($type) + protected function formatType($type) { return is_string($type) ? $type : $type->getFormat(); } + /** + * Checks if type of the given value is present in the type group, + * also updates the value when necessary. + * + * @param string $typeGroup String format for grouped types, i.e. + * oneof(Car,Atom) + * @param mixed $value Any value to be checked in type group + * @param array $factoryMethods Callable factory methods for the value, that + * are required to serialize it into any of the + * provided types in typeGroup in the format: + * 'path/to/method argType', Default: [] + * + * @return mixed Returns the same value or updated one if any factory method + * is applied + * @throws JsonMapperException Throws exception if a factory method is provided + * but applicable on value, or also throws an + * exception if type of value didn't match with type + * group + */ + public function checkTypeGroupFor($typeGroup, $value, $factoryMethods = []) + { + $type = self::getType($value, $factoryMethods); + if ($this->checkForType($typeGroup, $type)) { + return $value; + } + throw JsonMapperException::unableToMapException('Type', $type, $typeGroup); + } + /** * Map the data in $value by the provided $typeGroup i.e. oneOf(A,B) * will try to map value with only one of A or B, that matched. While @@ -536,6 +803,7 @@ protected static function formatType($type) * the value, that are required * to deserialize it into any of * the provided types in typeGroup + * like ['path/to/method argType'] * @param string|null $className Name of the parent class that's * holding this property (if any) * @@ -551,7 +819,7 @@ public function mapFor( ) { if (is_string($typeGroup)) { // convert into TypeCombination object - $typeGroup = TypeCombination::generateTypeCombination( + $typeGroup = TypeCombination::withFormat( $typeGroup, isset($factoryMethods) ? $factoryMethods : [] ); @@ -723,7 +991,7 @@ protected function isValueOfType( foreach ($deserializers as $method) { if (isset($method) && explode(' ', $method)[1] == $type) { $methodFound = true; - if ($this->isValueDeserializable($value, $method)) { + if ($this->callFactoryWithErrorHandling($value, $method)[0]) { return array(true, $method); } } @@ -1232,9 +1500,9 @@ protected function getParameterType(\ReflectionParameter $param) if (is_callable([$param, 'hasType']) && $param->hasType()) { $type = $param->getType(); if ($type->isBuiltIn()) { - $typeName = static::reflectionTypeToString($type); + $typeName = $this->reflectionTypeToString($type); } else { - $typeName = "\\" . static::reflectionTypeToString($type); + $typeName = "\\" . $this->reflectionTypeToString($type); } return $type->allowsNull() ? "$typeName|null" : $typeName; } @@ -1249,7 +1517,7 @@ protected function getParameterType(\ReflectionParameter $param) * * @return string */ - protected static function reflectionTypeToString($type) + protected function reflectionTypeToString($type) { if (\class_exists('ReflectionNamedType') && $type instanceof \ReflectionNamedType @@ -1557,7 +1825,7 @@ protected function removeNullable($type) * * @return array */ - protected static function parseAnnotations($docblock) + protected function parseAnnotations($docblock) { $annotations = array(); // Strip away the docblock header and footer diff --git a/src/JsonMapperException.php b/src/JsonMapperException.php index 4ecaab6b6..7789c8331 100644 --- a/src/JsonMapperException.php +++ b/src/JsonMapperException.php @@ -79,26 +79,43 @@ static function missingTypePropertyException($key, $strClassName) /** * Exception for an unCallable Factory Method. - * + * * @param string $factoryMethod The concerned factory method. * @param string $strClassName Related class name. - * + * * @return JsonMapperException */ static function unCallableFactoryMethodException($factoryMethod, $strClassName) { return new self( - "Factory method '$factoryMethod' referenced by ". + "Factory method '$factoryMethod' referenced by " . "'$strClassName' is not callable." ); } + /** + * Exception for not able to call factory method with the given value. + * + * @param string $argType Type of the argument passed in method. + * @param string $reasons Exception message received from factory method. + * + * @return JsonMapperException + */ + static function invalidArgumentFactoryMethodException($argType, $reasons) + { + return new self( + "Provided factory methods are not callable with " . + "the value of Type: $argType\n$reasons" + ); + } + /** * Exception when it is not possible to map an object to a specific type. * * @param string $typeName Name of type to map json object on. * @param string $typeGroup Group name of the type provided. - * @param string $value JSON string. + * @param string $value Value that should be mapped by typeGroup + * i.e. JSON string. * * @return JsonMapperException */ diff --git a/src/TypeCombination.php b/src/TypeCombination.php index 68fc824cf..0c680ae69 100644 --- a/src/TypeCombination.php +++ b/src/TypeCombination.php @@ -109,6 +109,59 @@ public function getDeserializers() return $this->_deserializers; } + /** + * Extract innermost oneof/anyof group hidden inside array/map + * type group + * + * @return TypeCombination + */ + public function extractOneOfAnyOfGroup() + { + $innerType = $this->getTypes()[0]; + if (in_array($this->getGroupName(), ["array", "map"]) + && $innerType instanceof TypeCombination + ) { + return $innerType->extractOneOfAnyOfGroup(); + } + return $this; + } + + /** + * Extract all internal groups similar to the given group as a list of + * TypeCombination objects, it will only return similar array/map groups + * + * @param TypeCombination $group All inner groups similar to this array/map + * type group will be extracted + * + * @return TypeCombination[] A list of similar TypeCombination objects + */ + public function extractSimilar($group) + { + $result = []; + if (!in_array($this->getGroupName(), ["array", "map"])) { + // if group is neither array nor map then call extractSimilar for + // each of the internal groups + foreach ($this->getTypes() as $typ) { + if ($typ instanceof TypeCombination) { + $result = array_merge($result, $typ->extractSimilar($group)); + } + } + } elseif ($group->getGroupName() == $this->getGroupName()) { + // if groupName is same then check inner group type + $internal = $this->getTypes()[0]; + $group = $group->getTypes()[0]; + if (in_array($group->getGroupName(), ["array", "map"])) { + // if inner group is array/map then return result after + // extraction of groups similar to innerGroup + $result = $internal->extractSimilar($group); + } else { + // if inner group is oneof/anyof then only extract $internal + $result = [$internal]; + } + } + return $result; + } + /** * Extract type info like: isMap, isArray, and inner type for maps/arrays. * @@ -130,6 +183,35 @@ public static function extractTypeInfo($type) return [$isMap, $isArray, $innerType]; } + /** + * Create an oneof/anyof TypeCombination instance, by specifying inner types + * + * @param array $types types array: (TypeCombination,string)[] + * @param string $gName group name value (anyof, oneof), + * Default: anyof + * @param string[] $deserializers deserializers array, Default: [] + * + * @return TypeCombination + */ + public static function with($types, $gName = 'anyof', array $deserializers = []) + { + $format = join( + ',', + array_map( + function ($t) { + return is_string($t) ? $t : $t->getFormat(); + }, + $types + ) + ); + return new self( + "$gName($format)", + $gName, + $types, + $deserializers + ); + } + /** * Wrap the given typeGroup string in the TypeCombination class, * i.e. getTypes() method will return all the grouped types, @@ -142,11 +224,12 @@ public static function extractTypeInfo($type) * represents array types, and array * represents map types, oneOf/anyOf are group * names, while default group name is anyOf. - * @param string[] $deserializers Callable factory methods for the property + * @param string[] $deserializers Callable factory methods for the property, + * Default: [] * * @return TypeCombination */ - public static function generateTypeCombination($typeGroup, $deserializers) + public static function withFormat($typeGroup, $deserializers = []) { $groupName = 'anyOf'; $start = strpos($typeGroup, '('); @@ -203,7 +286,7 @@ private static function _createTypeGroup($name, $innerGroup, $deserializers) return new self( $format, $name, - [self::generateTypeCombination($innerGroup, $deserializers)], + [self::withFormat($innerGroup, $deserializers)], $deserializers ); } @@ -220,9 +303,10 @@ private static function _createTypeGroup($name, $innerGroup, $deserializers) */ private static function _insertType(&$types, $type, $deserializers) { + $type = trim($type); if (strpos($type, '(') !== false && strrpos($type, ')') !== false) { // If type is Grouped, creating TypeCombination instance for it - $type = self::generateTypeCombination($type, $deserializers); + $type = self::withFormat($type, $deserializers); } if (!empty($type)) { array_push($types, $type); diff --git a/tests/multitypetest/MultiTypeJsonMapper.php b/tests/multitypetest/MultiTypeJsonMapper.php new file mode 100644 index 000000000..1fb596487 --- /dev/null +++ b/tests/multitypetest/MultiTypeJsonMapper.php @@ -0,0 +1,19 @@ +mapClass(json_decode($json), '\multitypetest\model\SimpleCaseA'); $this->assertInstanceOf('\multitypetest\model\SimpleCaseA', $res); } - + public function testSimpleCaseAWithFieldIntArray() { $mapper = new JsonMapper(); @@ -47,7 +55,7 @@ public function testSimpleCaseAWithFieldIntArray() $res = $mapper->mapClass(json_decode($json), '\multitypetest\model\SimpleCaseA'); $this->assertInstanceOf('\multitypetest\model\SimpleCaseA', $res); } - + public function testSimpleCaseAWithFieldBoolean() { $mapper = new JsonMapper(); @@ -56,7 +64,6 @@ public function testSimpleCaseAWithFieldBoolean() $this->assertInstanceOf('\multitypetest\model\SimpleCaseA', $res); } - public function testSimpleCaseAFailWithConstructorArgumentMissing() { $this->expectException(JsonMapperException::class); @@ -66,7 +73,6 @@ public function testSimpleCaseAFailWithConstructorArgumentMissing() $mapper->mapClass(json_decode($json), '\multitypetest\model\SimpleCaseA'); } - public function testSimpleCaseAFailWithFieldBoolArray() { $this->expectException(JsonMapperException::class); @@ -76,7 +82,6 @@ public function testSimpleCaseAFailWithFieldBoolArray() $mapper->mapClass(json_decode($json), '\multitypetest\model\SimpleCaseA'); } - public function testSimpleCaseAFailWithFieldString() { $this->expectException(JsonMapperException::class); @@ -86,7 +91,6 @@ public function testSimpleCaseAFailWithFieldString() $mapper->mapClass(json_decode($json), '\multitypetest\model\SimpleCaseA'); } - public function testSimpleCaseBWithFieldBoolean() { $mapper = new JsonMapper(); @@ -95,7 +99,6 @@ public function testSimpleCaseBWithFieldBoolean() $this->assertInstanceOf('\multitypetest\model\SimpleCaseB', $res); } - public function testSimpleCaseBWithFieldArray() { $mapper = new JsonMapper(); @@ -104,7 +107,6 @@ public function testSimpleCaseBWithFieldArray() $this->assertInstanceOf('\multitypetest\model\SimpleCaseB', $res); } - public function testSimpleCaseBFailWithFieldIntArray() { $this->expectException(JsonMapperException::class); @@ -114,7 +116,6 @@ public function testSimpleCaseBFailWithFieldIntArray() $mapper->mapClass(json_decode($json), '\multitypetest\model\SimpleCaseB'); } - public function testStringOrStringList() { $mapper = new JsonMapper(); @@ -127,7 +128,6 @@ public function testStringOrStringList() $this->assertEquals('value', $res[1]); } - public function testNeitherStringNorStringList() { $this->expectException(JsonMapperException::class); @@ -137,7 +137,6 @@ public function testNeitherStringNorStringList() $mapper->mapFor(json_decode($json), 'anyOf(string[],string)'); } - public function testAssociativeArray() { $mapper = new JsonMapper(); @@ -148,7 +147,6 @@ public function testAssociativeArray() self::assertTrue(is_string($res['key1'])); } - public function testEmptyArrayAndMap() { $mapper = new JsonMapper(); @@ -161,7 +159,6 @@ public function testEmptyArrayAndMap() self::assertTrue(is_array($res)); } - public function testEmptyArrayFail() { $this->expectException(JsonMapperException::class); @@ -171,7 +168,6 @@ public function testEmptyArrayFail() $mapper->mapFor(json_decode($json), 'oneOf(string[],int[],array)'); } - public function testEmptyMapFail() { $this->expectException(JsonMapperException::class); @@ -181,7 +177,6 @@ public function testEmptyMapFail() $mapper->mapFor(json_decode($json), 'oneOf(array,array,string[])'); } - public function testNullableObjectOrBool() { $mapper = new JsonMapper(); @@ -201,7 +196,6 @@ public function testNullableObjectOrBool() $this->assertEquals(null, $res); } - public function testNullableOnNonNullable() { $mapper = new JsonMapper(); @@ -210,7 +204,6 @@ public function testNullableOnNonNullable() $mapper->mapFor(null, 'oneOf(array,bool)'); } - public function testMixedOrInt() { $mapper = new JsonMapper(); @@ -321,7 +314,6 @@ public function testStringOrSimpleCaseA() $this->assertEquals('{"value":[1.2]}', $res); } - public function testOneOfSimpleCases() { $mapper = new JsonMapper(); @@ -334,7 +326,6 @@ public function testOneOfSimpleCases() $this->assertInstanceOf('\multitypetest\model\SimpleCaseB', $res); } - public function testOneOfSimpleCasesWithFieldArrayAndFloatArrayFail() { $this->expectException(JsonMapperException::class); @@ -348,7 +339,6 @@ public function testOneOfSimpleCasesWithFieldArrayAndFloatArrayFail() ); } - public function testAnyOfSimpleCases() { $mapper = new JsonMapper(); @@ -377,7 +367,6 @@ public function testAnyOfSimpleCases() $this->assertInstanceOf('\multitypetest\model\SimpleCaseB', $res); } - public function testAnyOfSimpleCasesFailWithFieldString() { $this->expectException(JsonMapperException::class); @@ -391,7 +380,6 @@ public function testAnyOfSimpleCasesFailWithFieldString() ); } - public function testArrayAndObject() { $mapper = new JsonMapper(); @@ -405,7 +393,6 @@ public function testArrayAndObject() $this->assertInstanceOf('\multitypetest\model\Atom', $res); } - public function testMapAndObject() { $mapper = new JsonMapper(); @@ -420,7 +407,6 @@ public function testMapAndObject() ); } - public function testArrayOfMapAndArrayOfObject() { $mapper = new JsonMapper(); @@ -435,7 +421,6 @@ public function testArrayOfMapAndArrayOfObject() ); } - public function testArrayOfMapOrArrayOfObject() { $mapper = new JsonMapper(); @@ -449,7 +434,6 @@ public function testArrayOfMapOrArrayOfObject() $this->assertInstanceOf('\multitypetest\model\Atom', $res[0]); } - public function testOneOfObjectsFailWithSameRequiredFields() { $this->expectException(JsonMapperException::class); @@ -464,7 +448,6 @@ public function testOneOfObjectsFailWithSameRequiredFields() ); } - public function testComplexCases() { $mapper = new JsonMapper(); @@ -491,7 +474,6 @@ public function testComplexCases() $this->assertTrue(is_int($res->getOptional()->getOptional()->getOptional()[0])); } - public function testComplexCasesWithDiscriminators() { $mapper = new JsonMapper(); @@ -563,7 +545,6 @@ public function testComplexCasesWithDiscriminators() $this->assertInstanceOf('\multitypetest\model\Morning', $res->getValue()[1]); } - public function testDiscriminatorsFailWithDiscriminatorMatchesParent() { $mapper = new JsonMapper(); @@ -582,7 +563,6 @@ public function testDiscriminatorsFailWithDiscriminatorMatchesParent() ); } - public function testDiscriminatorsMatchedButFailedWithOneOf() { $mapper = new JsonMapper(); @@ -596,7 +576,6 @@ public function testDiscriminatorsMatchedButFailedWithOneOf() ); } - public function testArrays() { $mapper = new JsonMapper(); @@ -607,7 +586,6 @@ public function testArrays() $this->assertTrue(is_bool($res[1])); } - public function testMaps() { $mapper = new JsonMapper(); @@ -618,7 +596,6 @@ public function testMaps() $this->assertTrue(is_int($res['value2'])); } - public function testMapOfArrays() { $mapper = new JsonMapper(); @@ -639,7 +616,6 @@ public function testMapOfArrays() $this->assertInstanceOf('\multitypetest\model\Atom', $res['value'][0]); } - public function testArrayOfMaps() { $mapper = new JsonMapper(); @@ -668,7 +644,6 @@ public function testArrayOfMaps() $this->assertInstanceOf('\multitypetest\model\Atom', $res[0]['atom2']); } - public function testMultiDimensionalMaps() { $mapper = new JsonMapper(); @@ -697,7 +672,6 @@ public function testMultiDimensionalMaps() $this->assertInstanceOf('\multitypetest\model\Atom', $res['key']['atom2']); } - public function testMultiDimensionalArrays() { $mapper = new JsonMapper(); @@ -726,7 +700,6 @@ public function testMultiDimensionalArrays() $this->assertInstanceOf('\multitypetest\model\Atom', $res[1][0]); } - public function testOuterArrayInModelField() { $mapper = new JsonMapper(); @@ -738,7 +711,6 @@ public function testOuterArrayInModelField() $this->assertInstanceOf('\multitypetest\model\OuterArrayCase', $res); } - public function testOuterArray() { $mapper = new JsonMapper(); @@ -754,7 +726,6 @@ public function testOuterArray() $this->assertTrue($res[2] === false); } - public function testOuterMap() { $mapper = new JsonMapper(); @@ -770,7 +741,6 @@ public function testOuterMap() $this->assertTrue($res['key3'] === false); } - public function testOuterMapOfArrays() { $mapper = new JsonMapper(); @@ -820,7 +790,6 @@ public function testOuterMapOfArrays() $this->assertTrue($res['key2'][0] === false); } - public function testOuterArrayOfMaps() { $mapper = new JsonMapper(); @@ -874,7 +843,6 @@ public function testOuterArrayOfMaps() $this->assertTrue($res[2]['key1'] === true); } - public function testOuterMultiDimensionalMaps() { $mapper = new JsonMapper(); @@ -928,7 +896,6 @@ public function testOuterMultiDimensionalMaps() $this->assertTrue($res['item2']['key1'] === true); } - public function testOuterMultiDimensionalArray() { $mapper = new JsonMapper(); @@ -978,7 +945,6 @@ public function testOuterMultiDimensionalArray() $this->assertTrue($res[2][0] === false); } - public function testOuterArrayFailWithAnyOf() { $mapper = new JsonMapper(); @@ -993,7 +959,6 @@ public function testOuterArrayFailWithAnyOf() ); } - public function testOuterArrayFailWith2DMapOfAtomAnd2DMapOfIntArray() { $mapper = new JsonMapper(); @@ -1007,7 +972,6 @@ public function testOuterArrayFailWith2DMapOfAtomAnd2DMapOfIntArray() ); } - public function testOuterArrayFailWithStringInsteadOfArrayOfString() { $mapper = new JsonMapper(); @@ -1022,7 +986,6 @@ public function testOuterArrayFailWithStringInsteadOfArrayOfString() ); } - public function testOuterArrayFailWithMapOfStringInsteadOfArrayOfString() { $mapper = new JsonMapper(); @@ -1037,7 +1000,6 @@ public function testOuterArrayFailWithMapOfStringInsteadOfArrayOfString() ); } - public function testOuterMapFailWithStringInsteadOfMapOfString() { $mapper = new JsonMapper(); @@ -1052,7 +1014,6 @@ public function testOuterMapFailWithStringInsteadOfMapOfString() ); } - public function testOuterMapFailWithArrayOfStringInsteadOfMapOfString() { $mapper = new JsonMapper(); @@ -1066,4 +1027,423 @@ public function testOuterMapFailWithArrayOfStringInsteadOfMapOfString() 'multitypetest\model' ); } + + public function testCheckTypeGroupFor() + { + $mapper = new JsonMapper(); + $res = $mapper->checkTypeGroupFor('oneof(string,int)', "this is string"); + $this->assertTrue(is_string($res)); + $this->assertEquals("this is string", $res); + + $res = $mapper->checkTypeGroupFor('oneof(Car,Atom)', new Car("3", true)); + $this->assertInstanceOf(Car::class, $res); + + $res = $mapper->checkTypeGroupFor('oneof(Car,Atom)[]', [ + new Car("3", true), + new Atom(34) + ]); + $this->assertInstanceOf(Car::class, $res[0]); + $this->assertInstanceOf(Atom::class, $res[1]); + + $res = $mapper->checkTypeGroupFor('oneof(int,DaysEnum)', "Monday", [ + 'multitypetest\model\DaysEnum::checkValue DaysEnum' + ]); + $this->assertEquals("Monday", $res); + + $res = $mapper->checkTypeGroupFor('oneof(int,DaysEnum)[]', ["Monday", "Tuesday"], [ + 'multitypetest\model\DaysEnum::checkValue DaysEnum[]' + ]); + $this->assertTrue(is_array($res)); + $this->assertEquals("Monday", $res[0]); + $this->assertEquals("Tuesday", $res[1]); + + $res = $mapper->checkTypeGroupFor('oneof(DateTime,null)', null); + $this->assertTrue(is_null($res)); + + $res = $mapper->checkTypeGroupFor('anyof(string,DateTime)[]', [null, null], [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime' + ]); + $this->assertTrue(is_array($res)); + $this->assertTrue(is_null($res[0])); + $this->assertTrue(is_null($res[1])); + + $res = $mapper->checkTypeGroupFor('anyof(string,DateTime)[]', ["some string"], [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTimeArray DateTime[]' + ]); + $this->assertTrue(is_array($res)); + $this->assertEquals("some string", $res[0]); + + $res = $mapper->checkTypeGroupFor('oneof(string,DateTime)[]', [new \DateTime("2022-06-10")], [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTimeArray DateTime[]' + ]); + $this->assertTrue(is_array($res)); + $this->assertEquals('Fri, 10 Jun 2022 00:00:00 GMT', $res[0]); + + $res = $mapper->checkTypeGroupFor('oneof(string,DateTime)[]', [new \DateTime("2022-06-10")], [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime' + ]); + $this->assertTrue(is_array($res)); + $this->assertEquals('Fri, 10 Jun 2022 00:00:00 GMT', $res[0]); + + $res = $mapper->checkTypeGroupFor('oneof(DateTime,null)', null, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime' + ]); + $this->assertTrue(is_null($res)); + } + + public function testCheckTypeGroupFailure() { + $this->expectException(JsonMapperException::class); + $this->expectExceptionMessage("Unable to map Type: Vehicle on: oneof(Atom,Car)"); + $mapper = new JsonMapper(); + + $mapper->checkTypeGroupFor('oneof(Atom,Car)', new Vehicle("6")); + } + + public function testCheckTypeGroupFailure2() { + $this->expectException(JsonMapperException::class); + $this->expectExceptionMessage("Unable to map Type: ((DaysEnum,string),string)[] on: oneof(int,DaysEnum)[]"); + $mapper = new JsonMapper(); + + $mapper->checkTypeGroupFor('oneof(int,DaysEnum)[]', ["Monday", "string"], [ + 'multitypetest\model\DaysEnum::checkValue DaysEnum' + ]); + } + + /** + * This is a test for protected method JsonMapper->getType + */ + public function testGetTypeOfSimpleValues() + { + $mapper = new MultiTypeJsonMapper(); + $value = "this is string"; + $this->assertEquals('string', $mapper->getType($value)); + $value = "23"; + $this->assertEquals('string', $mapper->getType($value)); + $value = 23.98; + $this->assertEquals('float', $mapper->getType($value)); + $value = 23; + $this->assertEquals('int', $mapper->getType($value)); + $value = false; + $this->assertEquals('bool', $mapper->getType($value)); + $value = []; + $this->assertEquals('array', $mapper->getType($value)); + $value = [false, true]; + $this->assertEquals('bool[]', $mapper->getType($value)); + $value = ["key1" => 23, "key2" => 34]; + $this->assertEquals('array', $mapper->getType($value)); + $value = ["key1" => 23, "key2" => 34.9]; + $this->assertEquals('array', $mapper->getType($value)); + $value = [false, true, null, 23]; + $this->assertEquals('(bool,int,null)[]', $mapper->getType($value)); + $value = [null, null]; + $this->assertEquals('null[]', $mapper->getType($value)); + + $value = ["key1" => 23, "key2" => [34, 46]]; + $this->assertEquals('array', $mapper->getType($value)); + $value = [23, [34, "46"], [[true, "46"]]]; + $this->assertEquals('((bool,string)[][],(int,string)[],int)[]', $mapper->getType($value)); + $value = ["key1" => ["string", 2.3], "key2" => [21.3, "some"]]; + $this->assertEquals('array', $mapper->getType($value)); + $value = [["key1" => ["some string"]], ["key1" => [false]]]; + $this->assertEquals('(array,array)[]', $mapper->getType($value)); + $value = [["key1" => ["some string"]], ["key1" => ["false"]]]; + $this->assertEquals('array[]', $mapper->getType($value)); + $value = ["key" => [["some string"]], ["key" => ["some string"]]]; + $this->assertEquals('array,string[][])>', $mapper->getType($value)); + $value = ["key" => [["key" => 23]], [["key" => 34]]]; + $this->assertEquals('array[]>', $mapper->getType($value)); + } + + /** + * This is a test for protected method JsonMapper->getType + */ + public function testGetTypeOfComplexValues() + { + $mapper = new MultiTypeJsonMapper(); + $value = new \DateTime(); + $this->assertEquals('DateTime', $mapper->getType($value)); + $value = new Car("3", true); + $this->assertEquals('Car', $mapper->getType($value)); + $value = [new \DateTime(), new \DateTime()]; + $this->assertEquals('DateTime[]', $mapper->getType($value)); + $value = [new Car("3", true), new \DateTime()]; + $this->assertEquals('(Car,DateTime)[]', $mapper->getType($value)); + $value = [new Car("3", true), true, new Car("6", false)]; + $this->assertEquals('(Car,bool)[]', $mapper->getType($value)); + $value = ["key1" => true, "key2" => new Car("6", false)]; + $this->assertEquals('array', $mapper->getType($value)); + $value = [new Car("3", true), new Atom(6), null]; + $this->assertEquals('(Atom,Car,null)[]', $mapper->getType($value)); + } + + /** + * This is a test for protected method JsonMapper->getType + */ + public function testGetTypeWithFactoryMethods() + { + $mapper = new MultiTypeJsonMapper(); + // a value that did not require factory methods + $value = "this is string"; + $this->assertEquals('string', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toSimpleDate DateTime', + 'multitypetest\model\DateTimeHelper::toSimpleDateArray DateTime[]' + ])); + $this->assertTrue(is_string($value)); + $this->assertEquals("this is string", $value); + + // a string value that is also an enum + $value = "Friday"; + $this->assertEquals('(DaysEnum,string)', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toSimpleDate DateTime', + 'multitypetest\model\DaysEnum::checkValue DaysEnum' + ])); + $this->assertTrue(is_string($value)); + $this->assertEquals("Friday", $value); + + // a string value that can be in 2 Enums + $value = "December"; + $this->assertEquals('(DaysEnum,MonthNameEnum,string)', $mapper->getType($value, [ + 'multitypetest\model\MonthNameEnum::checkValue MonthNameEnum', + 'multitypetest\model\DaysEnum::checkValue DaysEnum' + ])); + $this->assertTrue(is_string($value)); + $this->assertEquals("December", $value); + + // an int value that can be also be an Enum + $value = 12; + $this->assertEquals('(MonthNumberEnum,int)', $mapper->getType($value, [ + 'multitypetest\model\MonthNameEnum::checkValue MonthNameEnum', + 'multitypetest\model\MonthNumberEnum::checkValue MonthNumberEnum' + ])); + $this->assertTrue(is_int($value)); + $this->assertEquals(12, $value); + + // an int array which can also be Enum array + $value = [12,1]; + $this->assertEquals('(((MonthNumberEnum[],int))[],MonthNumberEnum[])', $mapper->getType($value, [ + 'multitypetest\model\MonthNameEnum::checkValue MonthNameEnum', + 'multitypetest\model\MonthNumberEnum::checkValue MonthNumberEnum[]' + ])); + $this->assertTrue(is_array($value)); + $this->assertEquals(12, $value[0]); + $this->assertEquals(1, $value[1]); + + // an int 2D array which can also be Enum 2D array + $value = [[12,1]]; + $this->assertEquals('(((((MonthNumberEnum[][],int))[],MonthNumberEnum[][]))[],MonthNumberEnum[][])', $mapper->getType($value, [ + 'multitypetest\model\MonthNameEnum::checkValue MonthNameEnum', + 'multitypetest\model\MonthNumberEnum::checkValue MonthNumberEnum[][]' + ])); + $this->assertTrue(is_array($value)); + $this->assertTrue(is_array($value[0])); + $this->assertEquals(12, $value[0][0]); + $this->assertEquals(1, $value[0][1]); + + // an int array whose inner values can be also be Enum + $value = [12,1]; + $this->assertEquals('(((MonthNumberEnum,int))[],MonthNumberEnum)', $mapper->getType($value, [ + 'multitypetest\model\MonthNameEnum::checkValue MonthNameEnum', + 'multitypetest\model\MonthNumberEnum::checkValue MonthNumberEnum' + ])); + $this->assertTrue(is_array($value)); + $this->assertEquals(12, $value[0]); + $this->assertEquals(1, $value[1]); + + // an array whose inner values can be also be Enum + $value = [12,"1"]; + $this->assertEquals('((MonthNumberEnum,int),string)[]', $mapper->getType($value, [ + 'multitypetest\model\MonthNameEnum::checkValue MonthNameEnum', + 'multitypetest\model\MonthNumberEnum::checkValue MonthNumberEnum' + ])); + $this->assertTrue(is_array($value)); + $this->assertEquals(12, $value[0]); + $this->assertEquals("1", $value[1]); + + // an array whose inner values can be also be Enum of 2 types + $value = [12,"January"]; + $this->assertEquals('((MonthNameEnum,string),(MonthNumberEnum,int))[]', $mapper->getType($value, [ + 'multitypetest\model\MonthNameEnum::checkValue MonthNameEnum', + 'multitypetest\model\MonthNumberEnum::checkValue MonthNumberEnum' + ])); + $this->assertTrue(is_array($value)); + $this->assertEquals(12, $value[0]); + $this->assertEquals("January", $value[1]); + + // a type that require factory methods, can be mapped by both factory methods + // mapped by first one + $value = new \DateTime("2022-06-10"); + $this->assertEquals('DateTime', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toSimpleDate DateTime', + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime' + ])); + $this->assertTrue(is_string($value)); + $this->assertEquals("2022-06-10", $value); + + // a type that require factory methods, can be mapped by both factory methods + // mapped by first one + $value = new \DateTime("2022-06-10"); + $this->assertEquals('DateTime', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime', + 'multitypetest\model\DateTimeHelper::toSimpleDate DateTime' + ])); + $this->assertTrue(is_string($value)); + $this->assertEquals("Fri, 10 Jun 2022 00:00:00 GMT", $value); + + // a datetime array, whose inner items can be mapped by both factory methods, mapped by first one + $value = [new \DateTime("2022-06-10"), new \DateTime("2022-06-10")]; + $this->assertEquals('DateTime[]', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toSimpleDate DateTime', + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime' + ])); + $this->assertTrue(is_array($value)); + $this->assertTrue(is_string($value[0])); + $this->assertEquals("2022-06-10", $value[0]); + + // a datetime array, can be mapped by both factory methods + $value = [new \DateTime("2022-06-10"), new \DateTime("2022-06-10")]; + $this->assertEquals('DateTime[]', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTimeArray DateTime[]', + 'multitypetest\model\DateTimeHelper::toSimpleDateArray DateTime[]' + ])); + $this->assertTrue(is_array($value)); + $this->assertEquals("Fri, 10 Jun 2022 00:00:00 GMT", $value[0]); + $this->assertEquals("Fri, 10 Jun 2022 00:00:00 GMT", $value[1]); + + // a datetime array, inner item can be mapped by 1st factory method, while + // outer array will be mapped by 2nd factory method + $value = [new \DateTime("2022-06-10"), new \DateTime("2022-06-10")]; + $this->assertEquals('DateTime[]', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime', + 'multitypetest\model\DateTimeHelper::toSimpleDateArray DateTime[]' + ])); + $this->assertTrue(is_array($value)); + $this->assertEquals("2022-06-10", $value[0]); + $this->assertEquals("2022-06-10", $value[1]); + + // a datetime mixed array + $value = [new \DateTime("2022-06-10"), ["key" => new \DateTime("2022-06-10")]]; + $this->assertEquals('((DateTime[],array),DateTime)[]', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime', + 'multitypetest\model\DateTimeHelper::toSimpleDateArray DateTime[]' + ])); + $this->assertTrue(is_array($value)); + $this->assertEquals("Fri, 10 Jun 2022 00:00:00 GMT", $value[0]); + $this->assertTrue(is_array($value[1])); + $this->assertEquals("Fri, 10 Jun 2022 00:00:00 GMT", $value[1]["key"]); + + // a datetime mixed array, + $value = [new \DateTime("2022-06-10"), ["key" => new \DateTime("2022-06-10")]]; + $this->assertEquals('(DateTime,array)[]', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime', + 'multitypetest\model\DateTimeHelper::toSimpleDateArray array' + ])); + $this->assertTrue(is_array($value)); + $this->assertEquals("Fri, 10 Jun 2022 00:00:00 GMT", $value[0]); + $this->assertTrue(is_array($value[1])); + $this->assertEquals("2022-06-10", $value[1]["key"]); + + // a datetime and enum array + $value = [new \DateTime("2022-06-10"), "December"]; + $this->assertEquals('((MonthNameEnum,string),DateTime)[]', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime', + 'multitypetest\model\MonthNameEnum::checkValue MonthNameEnum' + ])); + $this->assertTrue(is_array($value)); + $this->assertEquals("Fri, 10 Jun 2022 00:00:00 GMT", $value[0]); + $this->assertEquals("December", $value[1]); + + // a datetime and enum null value + $value = null; + $this->assertEquals('(DateTime,MonthNameEnum,null)', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime', + 'multitypetest\model\MonthNameEnum::checkValue MonthNameEnum' + ])); + $this->assertTrue(is_null($value)); + + // a datetime null values array + $value = [null,null]; + $this->assertEquals('((DateTime,null))[]', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime' + ])); + $this->assertTrue(is_array($value)); + $this->assertTrue(is_null($value[0])); + $this->assertTrue(is_null($value[1])); + + // a datetime and enum null mix array + $value = [null,"some string"]; + $this->assertEquals('((DateTime,MonthNameEnum,null),string)[]', $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTime DateTime', + 'multitypetest\model\MonthNameEnum::checkValue MonthNameEnum' + ])); + $this->assertTrue(is_array($value)); + $this->assertTrue(is_null($value[0])); + $this->assertEquals('some string', $value[1]); + } + + /** + * This is a test for protected method JsonMapper->checkForType + */ + public function testGetTypeFailure() + { + $this->expectException(JsonMapperException::class); + $this->expectExceptionMessage("Provided factory methods are not callable with the value of Type: DateTime\nmultitypetest\model\DateTimeHelper::toRfc1123DateTimeArray: "); + $mapper = new MultiTypeJsonMapper(); + $value = new \DateTime(); + $mapper->getType($value, [ + 'multitypetest\model\DateTimeHelper::toRfc1123DateTimeArray DateTime', + ]); + } + + /** + * This is a test for protected method JsonMapper->checkForType + */ + public function testCheckForSimpleTypes() + { + $mapper = new MultiTypeJsonMapper(); + $this->assertTrue($mapper->checkForType('oneof(string,int)', 'string')); + $this->assertTrue($mapper->checkForType('oneof(string, anyof(bool, int))', 'int')); + $this->assertTrue($mapper->checkForType('oneof(string,int)[]', 'int[]')); + $this->assertFalse($mapper->checkForType('oneof(string,int)[]', 'array')); + $this->assertTrue($mapper->checkForType('array', 'array')); + $this->assertTrue($mapper->checkForType('array', 'array')); + $this->assertTrue($mapper->checkForType('oneof(string,int,bool)[]', '(bool,int)[]')); + $this->assertTrue($mapper->checkForType('oneof(string,anyof(int,bool))[]', '(bool,int)[]')); + $this->assertTrue($mapper->checkForType('oneof(string,anyof(int,bool)[])', '(bool,int)[]')); + $this->assertFalse($mapper->checkForType('oneof(string,anyof(int,bool[]))', '(bool,int)[]')); + $this->assertTrue($mapper->checkForType('oneof(string,anyof(int,bool[]))', 'bool[]')); + + $this->assertTrue($mapper->checkForType('oneof(anyof(a,oneof(a[],b)[]),anyof(a,b,c)[])', '(a,b)[]')); + $this->assertTrue($mapper->checkForType('oneof(anyof(a,oneof(a[],b)),anyof(a,b,c)[])', '((a,b)[],c[])')); + $this->assertTrue($mapper->checkForType('oneof(anyof(a,oneof(a[],b)),anyof(a,b,c)[])', '(c,c[])')); + $this->assertTrue($mapper->checkForType('oneof(anyof(a,b),anyof(a,b,c)[])', '(b,c)')); + $this->assertFalse($mapper->checkForType('oneof(anyof(a,b),anyof(a,b,c)[])[]', '(b,c)')); + $this->assertFalse($mapper->checkForType('oneof(anyof(a,b),anyof(a,b,c)[])[]', '(b,c)[]')); + + } + + /** + * This is a test for protected method JsonMapper->checkForType + */ + public function testCheckForComplexTypes() + { + $mapper = new MultiTypeJsonMapper(); + + $this->assertTrue($mapper->checkForType('oneof(Car,anyof(Atom,null)[])', 'Atom[]')); + $this->assertTrue($mapper->checkForType('oneof(Car,anyof(Atom,null)[])', 'Car')); + + $this->assertTrue($mapper->checkForType('oneof(Car,anyof(Atom,null))[]', '(Atom,Car,null)[]')); + $this->assertTrue($mapper->checkForType('oneof(Car,anyof(Atom,null))[]', '(Atom,null)[]')); + $this->assertFalse($mapper->checkForType('oneof(Car[],anyof(Atom,null))[]', '(Atom,Car,null)[]')); + $this->assertFalse($mapper->checkForType('oneof(Car,oneof(Atom,Orbit)[])[]', '(Atom,null)[]')); + $this->assertTrue($mapper->checkForType('oneof(Car,oneof(Atom,Orbit,null)[])', '(Atom,null)[]')); + $this->assertTrue($mapper->checkForType('oneof(oneof(Atom,Orbit)[],oneof(Atom,Orbit,null)[])', '(Atom,null)[]')); + + $this->assertTrue($mapper->checkForType('array', 'array')); + $this->assertTrue($mapper->checkForType('array', 'array')); + $this->assertFalse($mapper->checkForType('array', 'array')); + $this->assertFalse($mapper->checkForType('array', 'array')); + $this->assertTrue($mapper->checkForType('oneof(Car,array)', 'array')); + $this->assertTrue($mapper->checkForType('oneof(array,array)', 'array')); + $this->assertTrue($mapper->checkForType('array', 'array')); + } } diff --git a/tests/multitypetest/model/DaysEnum.php b/tests/multitypetest/model/DaysEnum.php new file mode 100644 index 000000000..1eab35991 --- /dev/null +++ b/tests/multitypetest/model/DaysEnum.php @@ -0,0 +1,66 @@ +