From cb881d8d01696ad2775c818106c697845dbc2aba Mon Sep 17 00:00:00 2001 From: Pavel Fedin Date: Sat, 7 Nov 2020 19:23:05 +0300 Subject: [PATCH] + PHP 7.4 typed properties support --- README.md | 7 +- composer.json | 2 +- src/DocTypedProperty.php | 67 ++++++++++--------- src/Property.php | 23 +++++++ .../PropertyFullDataTransferObject.php | 37 ---------- .../TestPlainArrayDataTransferObject.php | 25 ------- tests/DocTypedPropertyTest.php | 11 +++ 7 files changed, 75 insertions(+), 97 deletions(-) delete mode 100644 tests/Classes/PropertyFullDataTransferObject.php delete mode 100644 tests/Classes/TestPlainArrayDataTransferObject.php diff --git a/README.md b/README.md index 2c61e24..6127e43 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,12 @@ class PostData extends DataTransferObject * No type, which allows everything */ public $property; + + /** + * PHP types declaration supported + */ + public ?int $property; + } ``` @@ -208,7 +214,6 @@ composer test ``` ### Limitations -- PHP 7.4 types are not supported: neither type checks are performed on value assign nor value cast - Opcache: types and data sources are resolved through PHP comments so ensure `opcache.save_comments` equals `1` ## License diff --git a/composer.json b/composer.json index 1a2e508..1cf36d5 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "minimum-stability": "stable", "require": { - "php": "^7.0" + "php": "^7.4" }, "require-dev": { "phpunit/phpunit": "^9.2" diff --git a/src/DocTypedProperty.php b/src/DocTypedProperty.php index eeb9449..a65d561 100644 --- a/src/DocTypedProperty.php +++ b/src/DocTypedProperty.php @@ -1,10 +1,10 @@ ])+(?:\[])?)+)/'; - const SCALAR_INT = 'int'; - const SCALAR_INTEGER = 'integer'; - const SCALAR_BOOL = 'bool'; - const SCALAR_BOOLEAN = 'boolean'; - const SCALAR_FLOAT = 'float'; - const SCALAR_DOUBLE = 'double'; - - const SCALAR_TYPES = [ - self::SCALAR_INT, - self::SCALAR_INTEGER, - self::SCALAR_BOOL, - self::SCALAR_BOOLEAN, - self::SCALAR_FLOAT, - self::SCALAR_DOUBLE, - ]; - - const NORMALIZING_TYPE_MAP = [ - self::SCALAR_INT => 'integer', - self::SCALAR_BOOL => 'boolean', - self::SCALAR_FLOAT => 'float', - self::SCALAR_DOUBLE => 'float', - ]; - /** * Type declaration as string * @@ -51,6 +28,28 @@ public function __construct(ReflectionProperty $property) { parent::__construct($property); + $this->types = $this->extractTypes($property); + + $this->typeDeclaration = implode('|', $this->types); + + $this->isNullable = $this->checkIsNullable($this->types); + + $this->isMixed = $this->checkIsMixed($this->types); + + $this->isMixedArray = $this->checkIsMixedArray($this->types); + + $this->arrayTypes = $this->resolveArrayTypes($this->types); + } + + /** + * Get array of property types + * + * @param ReflectionProperty $property + * @return array + */ + private function extractTypes(ReflectionProperty $property): array + { + // extract types from doc comment $docComment = $property->getDocComment(); preg_match( @@ -61,17 +60,19 @@ public function __construct(ReflectionProperty $property) $typeDeclaration = $varStrMatches[1] ?? null; - $this->types = $this->normalizeTypes(explode('|', $typeDeclaration), $property->getDeclaringClass()); - - $this->typeDeclaration = implode('|', $this->types); - - $this->isNullable = $this->checkIsNullable($this->types); + // since PHP 7.4 we should take into account declared type + if (method_exists($property, 'getType') && $reflectionType = $property->getType()) { - $this->isMixed = $this->checkIsMixed($this->types); + if ($reflectionType->allowsNull()) { + $typeDeclaration .= '|null'; + } - $this->isMixedArray = $this->checkIsMixedArray($this->types); + if ($reflectionType instanceof ReflectionNamedType) { + $typeDeclaration .= '|' . $reflectionType->getName(); + } + } - $this->arrayTypes = $this->resolveArrayTypes($this->types); + return $this->normalizeTypes(explode('|', $typeDeclaration), $property->getDeclaringClass()); } diff --git a/src/Property.php b/src/Property.php index bc47bae..8ddc3f6 100644 --- a/src/Property.php +++ b/src/Property.php @@ -18,6 +18,29 @@ abstract class Property implements IProperty { const SOURCE_DOCBLOCK_REGEX = '/@source ((?:(?:[\w?|\\\\<>])+(?:\[])?)+)/'; + const SCALAR_INT = 'int'; + const SCALAR_INTEGER = 'integer'; + const SCALAR_BOOL = 'bool'; + const SCALAR_BOOLEAN = 'boolean'; + const SCALAR_FLOAT = 'float'; + const SCALAR_DOUBLE = 'double'; + + const SCALAR_TYPES = [ + self::SCALAR_INT, + self::SCALAR_INTEGER, + self::SCALAR_BOOL, + self::SCALAR_BOOLEAN, + self::SCALAR_FLOAT, + self::SCALAR_DOUBLE, + ]; + + const NORMALIZING_TYPE_MAP = [ + self::SCALAR_INT => 'integer', + self::SCALAR_BOOL => 'boolean', + self::SCALAR_FLOAT => 'float', + self::SCALAR_DOUBLE => 'float', + ]; + /** * @var string */ diff --git a/tests/Classes/PropertyFullDataTransferObject.php b/tests/Classes/PropertyFullDataTransferObject.php deleted file mode 100644 index 0aea7ba..0000000 --- a/tests/Classes/PropertyFullDataTransferObject.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ - public $docIntegerIterableField; -} diff --git a/tests/Classes/TestPlainArrayDataTransferObject.php b/tests/Classes/TestPlainArrayDataTransferObject.php deleted file mode 100644 index 57199ea..0000000 --- a/tests/Classes/TestPlainArrayDataTransferObject.php +++ /dev/null @@ -1,25 +0,0 @@ -assertEquals(['Amorphine\DataTransferObject\Tests\Classes\DtoC'], $field->getArrayTypes()); } + + public function testNativePropertiesAreIncluded() { + list($prop) = $this->getProperties(new class() { + /** @var string $prop */ + public ?int $prop; + }); + + $this->assertTrue($prop->isNullable()); + $this->assertTrue(in_array('integer', $prop->getTypes())); + $this->assertTrue(in_array('string', $prop->getTypes())); + } }