From b3a3de7919db436b505457b7242b2cbd0bc38f89 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 19 Nov 2018 14:40:10 -0600 Subject: [PATCH 01/35] Update dependencies - Require at least PHP 7.1 - Require 3.X (or latest) versions of ZF components. - Require PHPUnit 7.4 --- .travis.yml | 37 +++++-------------------------------- composer.json | 20 ++++++++++---------- composer.lock | 4 ++-- 3 files changed, 17 insertions(+), 44 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2fb1fd4..c5092bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,44 +13,18 @@ env: matrix: include: - - php: 5.6 - env: - - DEPS=lowest - - php: 5.6 - env: - - DEPS=locked - - LEGACY_DEPS="phpunit/phpunit" - - php: 5.6 - env: - - DEPS=latest - - php: 7 - env: - - DEPS=lowest - - php: 7 - env: - - DEPS=locked - - LEGACY_DEPS="phpunit/phpunit" - - php: 7 - env: - - DEPS=latest - - php: 7.1 + - php: 7.2 env: - DEPS=lowest - - php: 7.1 + - php: 7.2 env: - DEPS=locked - CS_CHECK=true - TEST_COVERAGE=true - - php: 7.1 - env: - - DEPS=latest - php: 7.2 env: - - DEPS=lowest - - php: 7.2 - env: - - DEPS=locked - - php: 7.2 + - DEPS=latest + - php: nightly env: - DEPS=latest @@ -58,8 +32,7 @@ before_install: - if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi install: - - travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs - - if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi + - travis_retry composer install $COMPOSER_ARGS - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi - if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi diff --git a/composer.json b/composer.json index c12c464..24d2b17 100644 --- a/composer.json +++ b/composer.json @@ -16,21 +16,21 @@ "forum": "https://discourse.zendframework.com/c/questions/components" }, "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^3.0" + "php": "^7.2", + "zendframework/zend-stdlib": "^3.2.1" }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "phpunit/phpunit": "^7.4.4", + "phpspec/prophecy": "^1.7.5", "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "zendframework/zend-eventmanager": "^3.2.1", + "zendframework/zend-serializer": "^2.9", + "zendframework/zend-servicemanager": "^3.3.2" }, "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" + "zendframework/zend-eventmanager": "^3.2, to support aggregate hydrator usage", + "zendframework/zend-serializer": "^2.9, to use the SerializableStrategy", + "zendframework/zend-servicemanager": "^3.3, to support hydrator plugin manager usage" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 28204db..2f96fe1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "40664fb5805cab9c5eb269f3150904a2", + "content-hash": "2ec62ba8e3d19528297d0736db3bb8bb", "packages": [ { "name": "zendframework/zend-stdlib", @@ -1885,7 +1885,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^5.6 || ^7.0" + "php": "^7.2" }, "platform-dev": [] } From 7b6d85e1dab219499b4431a598b1065684bcfd95 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 19 Nov 2018 16:53:56 -0600 Subject: [PATCH 02/35] Add typehints wherever possible - Added `declare(strict_types=1)` to all classes - Adds typehints to all parameters and return values. In addition, it expands interfaces that were allowing optional values to provide appropriate typehints. Also uses `object` typehints (nullable in some cases) where appropriate: - `HydrationInterface` - `ExtractionInterface` - `NamingStrategyInterface` - `StrategyInterface` - All classes implementing the above - Calls to `getNamingStrategy()` must be preceded by `hasNamingStrategy()`, to ensure the method does not attempt to return `null` (which is now disallowed by the interface). - Removes type check/throw combinations where typehints now make them obsolete. - Updates test expectations and/or removes tests to correspond with these changes. - Removes data provider entries that are no longer relevant. - Removes annotations made redundant by typehints. - Adds typehints for previously "mixed" types when we now know the type will not vary (e.g., `Reflection::getReflProperties()` can typehint `$input` against `object` now). - Fixes `ClosureStrategy` to check if the composed extraction/hydration callback is actually set before attempting to call it; if not set, it returns the value verbatim instead. - Fixes the `ExplodeStrategy` to cast the `$value` to `string` during hydration when calling `explode()`. This preserves previous behavior. - Removes tests against zend-servicemanager v2 (affects `DelegatingHydratorFactory` and `HydratorPluginManager` and related factory. --- src/AbstractHydrator.php | 102 +++++------- src/Aggregate/AggregateHydrator.php | 26 ++-- src/Aggregate/ExtractEvent.php | 28 ++-- src/Aggregate/HydrateEvent.php | 21 ++- src/Aggregate/HydratorListener.php | 19 +-- src/ArraySerializable.php | 20 +-- src/ClassMethods.php | 146 ++++++------------ src/ConfigProvider.php | 12 +- src/DelegatingHydrator.php | 28 ++-- src/DelegatingHydratorFactory.php | 38 ++--- src/Exception/BadMethodCallException.php | 10 +- src/Exception/DomainException.php | 10 +- src/Exception/ExceptionInterface.php | 14 +- src/Exception/ExtensionNotLoadedException.php | 10 +- src/Exception/InvalidArgumentException.php | 10 +- src/Exception/InvalidCallbackException.php | 10 +- src/Exception/LogicException.php | 10 +- src/Exception/RuntimeException.php | 10 +- src/ExtractionInterface.php | 15 +- src/Filter/FilterComposite.php | 101 +++++------- src/Filter/FilterInterface.php | 16 +- src/Filter/FilterProviderInterface.php | 14 +- src/Filter/GetFilter.php | 17 +- src/Filter/HasFilter.php | 12 +- src/Filter/IsFilter.php | 12 +- src/Filter/MethodMatchFilter.php | 21 ++- src/Filter/NumberOfParameterFilter.php | 23 ++- src/Filter/OptionalParametersFilter.php | 14 +- src/FilterEnabledInterface.php | 23 ++- src/HydrationInterface.php | 16 +- src/HydratorAwareInterface.php | 19 +-- src/HydratorAwareTrait.php | 25 +-- src/HydratorInterface.php | 10 +- src/HydratorOptionsInterface.php | 18 +-- src/HydratorPluginManager.php | 49 ++---- src/HydratorPluginManagerFactory.php | 46 +----- src/HydratorProviderInterface.php | 8 +- src/Iterator/HydratingArrayIterator.php | 12 +- src/Iterator/HydratingIteratorInterface.php | 16 +- src/Iterator/HydratingIteratorIterator.php | 14 +- src/Module.php | 13 +- src/NamingStrategy/ArrayMapNamingStrategy.php | 20 ++- .../CompositeNamingStrategy.php | 16 +- src/NamingStrategy/IdentityNamingStrategy.php | 14 +- src/NamingStrategy/MapNamingStrategy.php | 30 ++-- .../NamingStrategyInterface.php | 22 ++- .../UnderscoreNamingStrategy.php | 16 +- .../CamelCaseToUnderscoreFilter.php | 21 +-- .../StringSupportTrait.php | 12 +- .../UnderscoreToCamelCaseFilter.php | 25 +-- src/NamingStrategyEnabledInterface.php | 25 ++- src/ObjectProperty.php | 32 +--- src/Reflection.php | 43 ++---- src/Strategy/BooleanStrategy.php | 14 +- src/Strategy/ClosureStrategy.php | 57 +++---- src/Strategy/CollectionStrategy.php | 19 ++- src/Strategy/DateTimeFormatterStrategy.php | 33 ++-- src/Strategy/DefaultStrategy.php | 14 +- src/Strategy/Exception/ExceptionInterface.php | 14 +- .../Exception/InvalidArgumentException.php | 10 +- src/Strategy/ExplodeStrategy.php | 39 ++--- src/Strategy/SerializableStrategy.php | 39 ++--- src/Strategy/StrategyChain.php | 25 ++- src/Strategy/StrategyInterface.php | 29 ++-- src/StrategyEnabledInterface.php | 31 ++-- .../AggregateHydratorFunctionalTest.php | 2 + test/Aggregate/AggregateHydratorTest.php | 2 + test/Aggregate/ExtractEventTest.php | 2 + test/Aggregate/HydrateEventTest.php | 2 + test/Aggregate/HydratorListenerTest.php | 2 + test/ArraySerializableTest.php | 17 +- test/ClassMethodsTest.php | 33 ++-- test/DelegatingHydratorFactoryTest.php | 24 +-- test/DelegatingHydratorTest.php | 2 + test/Filter/FilterCompositeTest.php | 2 + test/Filter/MethodMatchFilterTest.php | 2 + test/Filter/NumberOfParameterFilterTest.php | 4 +- test/Filter/OptionalParametersFilterTest.php | 2 + test/HydratorAwareTraitTest.php | 2 + test/HydratorClosureStrategyTest.php | 2 + test/HydratorObjectPropertyTest.php | 2 + ...HydratorPluginManagerCompatibilityTest.php | 4 +- test/HydratorPluginManagerFactoryTest.php | 32 +--- test/HydratorStrategyTest.php | 2 + test/HydratorTest.php | 2 + test/HydratorTestTrait.php | 2 + test/Iterator/HydratingArrayIteratorTest.php | 2 + .../HydratingIteratorIteratorTest.php | 2 + .../ArrayMapNamingStrategyTest.php | 2 + .../CompositeNamingStrategyTest.php | 2 + .../IdentityNamingStrategyTest.php | 8 +- test/NamingStrategy/MapNamingStrategyTest.php | 2 + .../CamelCaseToUnderscoreFilterTest.php | 21 +-- .../UnderscoreToCamelCaseFilterTest.php | 21 +-- .../UnderscoreNamingStrategyTest.php | 2 + test/ObjectPropertyTest.php | 8 +- test/ReflectionTest.php | 22 +-- test/Strategy/BooleanStrategyTest.php | 2 + test/Strategy/CollectionStrategyTest.php | 52 +++---- .../DateTimeFormatterStrategyTest.php | 16 +- test/Strategy/ExplodeStrategyTest.php | 34 ++-- test/Strategy/SerializableStrategyTest.php | 2 + test/Strategy/StrategyChainTest.php | 2 + test/TestAsset/AggregateObject.php | 2 + test/TestAsset/ArrayObjectIterator.php | 2 + test/TestAsset/ArrayObjectObjectVars.php | 2 + test/TestAsset/ArraySerializable.php | 2 + .../ArraySerializableNoGetArrayCopy.php | 2 + test/TestAsset/ClassMethodsCamelCase.php | 2 + .../ClassMethodsCamelCaseMissing.php | 2 + .../ClassMethodsFilterProviderInterface.php | 5 +- .../ClassMethodsInvalidParameter.php | 2 + .../ClassMethodsMagicMethodSetter.php | 2 + .../ClassMethodsOptionalParameters.php | 2 + .../TestAsset/ClassMethodsProtectedSetter.php | 2 + test/TestAsset/ClassMethodsTitleCase.php | 2 + test/TestAsset/ClassMethodsUnderscore.php | 2 + .../ClassWithPublicStaticProperties.php | 2 + .../HydratorClosureStrategyEntity.php | 2 + test/TestAsset/HydratorStrategy.php | 6 +- .../HydratorStrategyContextAware.php | 6 +- test/TestAsset/HydratorStrategyEntityA.php | 2 + test/TestAsset/HydratorStrategyEntityB.php | 2 + test/TestAsset/ObjectProperty.php | 2 + test/TestAsset/Reflection.php | 2 + test/TestAsset/ReflectionFilter.php | 2 + test/TestAsset/SimpleEntity.php | 2 + test/TestAsset/User.php | 2 + 128 files changed, 843 insertions(+), 1200 deletions(-) diff --git a/src/AbstractHydrator.php b/src/AbstractHydrator.php index c399fd2..9c28bdc 100644 --- a/src/AbstractHydrator.php +++ b/src/AbstractHydrator.php @@ -1,12 +1,12 @@ strategies[$name])) { return $this->strategies[$name]; - } elseif ($this->hasNamingStrategy() + } + + if ($this->hasNamingStrategy() && ($hydrated = $this->getNamingStrategy()->hydrate($name)) && isset($this->strategies[$hydrated]) ) { @@ -81,9 +81,8 @@ public function getStrategy($name) * Checks if the strategy with the given name exists. * * @param string $name The name of the strategy to check for. - * @return bool */ - public function hasStrategy($name) + public function hasStrategy(string $name) : bool { if (array_key_exists($name, $this->strategies)) { return true; @@ -103,35 +102,31 @@ public function hasStrategy($name) * * @param string $name The name of the strategy to register. * @param Strategy\StrategyInterface $strategy The strategy to register. - * @return AbstractHydrator */ - public function addStrategy($name, Strategy\StrategyInterface $strategy) + public function addStrategy(string $name, Strategy\StrategyInterface $strategy) : void { $this->strategies[$name] = $strategy; - return $this; } /** * Removes the strategy with the given name. * * @param string $name The name of the strategy to remove. - * @return HydratorInterface */ - public function removeStrategy($name) + public function removeStrategy(string $name) : void { unset($this->strategies[$name]); - return $this; } /** * Converts a value for extraction. If no strategy exists the plain value is returned. * - * @param string $name The name of the strategy to use. - * @param mixed $value The value that should be converted. - * @param mixed $object The object is optionally provided as context. + * @param string $name The name of the strategy to use. + * @param mixed $value The value that should be converted. + * @param null|object $object The object is optionally provided as context. * @return mixed */ - public function extractValue($name, $value, $object = null) + public function extractValue(string $name, $value, ?object $object = null) { if ($this->hasStrategy($name)) { $strategy = $this->getStrategy($name); @@ -143,12 +138,12 @@ public function extractValue($name, $value, $object = null) /** * Converts a value for hydration. If no strategy exists the plain value is returned. * - * @param string $name The name of the strategy to use. - * @param mixed $value The value that should be converted. - * @param array $data The whole data is optionally provided as context. + * @param string $name The name of the strategy to use. + * @param mixed $value The value that should be converted. + * @param null|array $data The whole data is optionally provided as context. * @return mixed */ - public function hydrateValue($name, $value, $data = null) + public function hydrateValue(string $name, $value, ?array $data = null) { if ($this->hasStrategy($name)) { $strategy = $this->getStrategy($name); @@ -160,11 +155,11 @@ public function hydrateValue($name, $value, $data = null) /** * Convert a name for extraction. If no naming strategy exists, the plain value is returned. * - * @param string $name The name to convert. - * @param null $object The object is optionally provided as context. + * @param string $name The name to convert. + * @param null $object The object is optionally provided as context. * @return mixed */ - public function extractName($name, $object = null) + public function extractName(string $name, $object = null) { if ($this->hasNamingStrategy()) { $name = $this->getNamingStrategy()->extract($name, $object); @@ -175,11 +170,10 @@ public function extractName($name, $object = null) /** * Converts a value for hydration. If no naming strategy exists, the plain value is returned. * - * @param string $name The name to convert. - * @param array $data The whole data is optionally provided as context. - * @return mixed + * @param string $name The name to convert. + * @param array $data The whole data is optionally provided as context. */ - public function hydrateName($name, $data = null) + public function hydrateName(string $name, ?array $data = null) : string { if ($this->hasNamingStrategy()) { $name = $this->getNamingStrategy()->hydrate($name, $data); @@ -189,10 +183,8 @@ public function hydrateName($name, $data = null) /** * Get the filter instance - * - * @return Filter\FilterComposite */ - public function getFilter() + public function getFilter() : Filter\FilterInterface { return $this->filterComposite; } @@ -215,83 +207,67 @@ public function getFilter() * * @param string $name Index in the composite * @param callable|Filter\FilterInterface $filter - * @param int $condition - * @return Filter\FilterComposite */ - public function addFilter($name, $filter, $condition = Filter\FilterComposite::CONDITION_OR) + public function addFilter(string $name, $filter, int $condition = Filter\FilterComposite::CONDITION_OR) : void { - return $this->filterComposite->addFilter($name, $filter, $condition); + $this->filterComposite->addFilter($name, $filter, $condition); } /** * Check whether a specific filter exists at key $name or not * - * @param string $name Index in the composite - * @return bool + * @param string $name Index/name in the composite */ - public function hasFilter($name) + public function hasFilter(string $name) : bool { return $this->filterComposite->hasFilter($name); } /** * Remove a filter from the composition. - * To not extract "has" methods, you simply need to unregister it + * + * To not extract "has" methods, unregister the filter. * * * $filterComposite->removeFilter('has'); * - * - * @param $name - * @return Filter\FilterComposite */ - public function removeFilter($name) + public function removeFilter(string $name) : void { - return $this->filterComposite->removeFilter($name); + $this->filterComposite->removeFilter($name); } /** * Adds the given naming strategy * * @param NamingStrategy\NamingStrategyInterface $strategy The naming to register. - * @return self */ - public function setNamingStrategy(NamingStrategy\NamingStrategyInterface $strategy) + public function setNamingStrategy(NamingStrategy\NamingStrategyInterface $strategy) : void { $this->namingStrategy = $strategy; - - return $this; } /** * Gets the naming strategy. - * - * @return NamingStrategy\NamingStrategyInterface */ - public function getNamingStrategy() + public function getNamingStrategy() : NamingStrategy\NamingStrategyInterface { return $this->namingStrategy; } /** * Checks if a naming strategy exists. - * - * @return bool */ - public function hasNamingStrategy() + public function hasNamingStrategy() : bool { return isset($this->namingStrategy); } /** * Removes the naming strategy - * - * @return self */ - public function removeNamingStrategy() + public function removeNamingStrategy() : void { $this->namingStrategy = null; - - return $this; } } diff --git a/src/Aggregate/AggregateHydrator.php b/src/Aggregate/AggregateHydrator.php index 9d2fcc6..5db67a4 100644 --- a/src/Aggregate/AggregateHydrator.php +++ b/src/Aggregate/AggregateHydrator.php @@ -1,12 +1,12 @@ attach($this->getEventManager(), $priority); @@ -41,7 +38,7 @@ public function add(HydratorInterface $hydrator, $priority = self::DEFAULT_PRIOR /** * {@inheritDoc} */ - public function extract($object) + public function extract(object $object) : array { $event = new ExtractEvent($this, $object); @@ -53,7 +50,7 @@ public function extract($object) /** * {@inheritDoc} */ - public function hydrate(array $data, $object) + public function hydrate(array $data, object $object) : object { $event = new HydrateEvent($this, $object, $data); @@ -65,17 +62,16 @@ public function hydrate(array $data, $object) /** * {@inheritDoc} */ - public function setEventManager(EventManagerInterface $eventManager) + public function setEventManager(EventManagerInterface $eventManager) : void { $eventManager->setIdentifiers([__CLASS__, get_class($this)]); - $this->eventManager = $eventManager; } /** * {@inheritDoc} */ - public function getEventManager() + public function getEventManager() : EventManagerInterface { if (null === $this->eventManager) { $this->setEventManager(new EventManager()); diff --git a/src/Aggregate/ExtractEvent.php b/src/Aggregate/ExtractEvent.php index 1395917..253c135 100644 --- a/src/Aggregate/ExtractEvent.php +++ b/src/Aggregate/ExtractEvent.php @@ -1,12 +1,12 @@ extractionObject = $extractionObject; } /** * Retrieves the data that has been extracted - * - * @return array */ - public function getExtractedData() + public function getExtractedData() : array { return $this->extractedData; } /** * @param array $extractedData - * - * @return void */ - public function setExtractedData(array $extractedData) + public function setExtractedData(array $extractedData) : void { $this->extractedData = $extractedData; } @@ -88,10 +82,8 @@ public function setExtractedData(array $extractedData) * Merge provided data with the extracted data * * @param array $additionalData - * - * @return void */ - public function mergeExtractedData(array $additionalData) + public function mergeExtractedData(array $additionalData) : void { $this->extractedData = array_merge($this->extractedData, $additionalData); } diff --git a/src/Aggregate/HydrateEvent.php b/src/Aggregate/HydrateEvent.php index 7865964..5a83d97 100644 --- a/src/Aggregate/HydrateEvent.php +++ b/src/Aggregate/HydrateEvent.php @@ -1,12 +1,12 @@ hydratedObject = $hydratedObject; } /** * Retrieves the data that is being used for hydration - * - * @return array */ - public function getHydrationData() + public function getHydrationData() : array { return $this->hydrationData; } @@ -77,7 +74,7 @@ public function getHydrationData() /** * @param array $hydrationData */ - public function setHydrationData(array $hydrationData) + public function setHydrationData(array $hydrationData) : void { $this->hydrationData = $hydrationData; } diff --git a/src/Aggregate/HydratorListener.php b/src/Aggregate/HydratorListener.php index c8260a4..10c656d 100644 --- a/src/Aggregate/HydratorListener.php +++ b/src/Aggregate/HydratorListener.php @@ -1,12 +1,12 @@ listeners[] = $events->attach(HydrateEvent::EVENT_HYDRATE, [$this, 'onHydrate'], $priority); $this->listeners[] = $events->attach(ExtractEvent::EVENT_EXTRACT, [$this, 'onExtract'], $priority); @@ -45,9 +45,8 @@ public function attach(EventManagerInterface $events, $priority = 1) /** * Callback to be used when {@see HydrateEvent::EVENT_HYDRATE} is triggered * - * @param HydrateEvent $event - * @return object * @internal + * @return object */ public function onHydrate(HydrateEvent $event) { @@ -59,11 +58,9 @@ public function onHydrate(HydrateEvent $event) /** * Callback to be used when {@see ExtractEvent::EVENT_EXTRACT} is triggered * - * @param ExtractEvent $event - * @return array * @internal */ - public function onExtract(ExtractEvent $event) + public function onExtract(ExtractEvent $event) : array { $data = $this->hydrator->extract($event->getExtractionObject()); $event->mergeExtractedData($data); diff --git a/src/ArraySerializable.php b/src/ArraySerializable.php index f47722f..8d8e9c4 100644 --- a/src/ArraySerializable.php +++ b/src/ArraySerializable.php @@ -1,12 +1,12 @@ extractName($name, $object); + // replace the original key with extracted, if differ if ($extractedName !== $name) { unset($data[$name]); $name = $extractedName; } + $data[$name] = $this->extractValue($name, $value, $object); } @@ -54,12 +55,11 @@ public function extract($object) * Hydrates an object by passing $data to either its exchangeArray() or * populate() method. * - * @param array $data * @param object $object * @return object * @throws Exception\BadMethodCallException for an $object not implementing exchangeArray() or populate() */ - public function hydrate(array $data, $object) + public function hydrate(array $data, object $object) : object { $replacement = []; foreach ($data as $key => $value) { diff --git a/src/ClassMethods.php b/src/ClassMethods.php index 9e7be0c..4de8ce7 100644 --- a/src/ClassMethods.php +++ b/src/ClassMethods.php @@ -1,12 +1,12 @@ setUnderscoreSeparatedKeys($underscoreSeparatedKeys); $this->setMethodExistsCheck($methodExistsCheck); @@ -72,70 +72,47 @@ public function __construct($underscoreSeparatedKeys = true, $methodExistsCheck ); } - /** - * @param array|Traversable $options - * @return ClassMethods - * @throws Exception\InvalidArgumentException - */ - public function setOptions($options) + public function setOptions(iterable $options) : void { if ($options instanceof Traversable) { $options = ArrayUtils::iteratorToArray($options); - } elseif (! is_array($options)) { - throw new Exception\InvalidArgumentException( - 'The options parameter must be an array or a Traversable' - ); } + if (isset($options['underscoreSeparatedKeys'])) { $this->setUnderscoreSeparatedKeys($options['underscoreSeparatedKeys']); } + if (isset($options['methodExistsCheck'])) { $this->setMethodExistsCheck($options['methodExistsCheck']); } - - return $this; } - /** - * @param bool $underscoreSeparatedKeys - * @return ClassMethods - */ - public function setUnderscoreSeparatedKeys($underscoreSeparatedKeys) + public function setUnderscoreSeparatedKeys(bool $underscoreSeparatedKeys) : void { - $this->underscoreSeparatedKeys = (bool) $underscoreSeparatedKeys; + $this->underscoreSeparatedKeys = $underscoreSeparatedKeys; if ($this->underscoreSeparatedKeys) { - $this->setNamingStrategy(new NamingStrategy\UnderscoreNamingStrategy); - } elseif ($this->getNamingStrategy() instanceof NamingStrategy\UnderscoreNamingStrategy) { - $this->removeNamingStrategy(); + $this->setNamingStrategy(new NamingStrategy\UnderscoreNamingStrategy()); + return; } - return $this; + if ($this->hasNamingStrategy()) { + $this->removeNamingStrategy(); + return; + } } - /** - * @return bool - */ - public function getUnderscoreSeparatedKeys() + public function getUnderscoreSeparatedKeys() : bool { return $this->underscoreSeparatedKeys; } - /** - * @param bool $methodExistsCheck - * @return ClassMethods - */ - public function setMethodExistsCheck($methodExistsCheck) + public function setMethodExistsCheck(bool $methodExistsCheck) : void { - $this->methodExistsCheck = (bool) $methodExistsCheck; - - return $this; + $this->methodExistsCheck = $methodExistsCheck; } - /** - * @return bool - */ - public function getMethodExistsCheck() + public function getMethodExistsCheck() : bool { return $this->methodExistsCheck; } @@ -144,20 +121,9 @@ public function getMethodExistsCheck() * Extract values from an object with class methods * * Extracts the getter/setter of the given $object. - * - * @param object $object - * @return array - * @throws Exception\BadMethodCallException for a non-object $object */ - public function extract($object) + public function extract(object $object) : array { - if (! is_object($object)) { - throw new Exception\BadMethodCallException(sprintf( - '%s expects the provided $object to be a PHP object)', - __METHOD__ - )); - } - $objectClass = get_class($object); // reset the hydrator's hydrator's cache for this object, as the filter may be per-instance @@ -213,21 +179,9 @@ public function extract($object) * Hydrate an object by populating getter/setter methods * * Hydrates an object by getter/setter methods of the object. - * - * @param array $data - * @param object $object - * @return object - * @throws Exception\BadMethodCallException for a non-object $object */ - public function hydrate(array $data, $object) + public function hydrate(array $data, object $object) : object { - if (! is_object($object)) { - throw new Exception\BadMethodCallException(sprintf( - '%s expects the provided $object to be a PHP object)', - __METHOD__ - )); - } - $objectClass = get_class($object); foreach ($data as $property => $value) { @@ -253,47 +207,43 @@ public function hydrate(array $data, $object) /** * {@inheritDoc} */ - public function addFilter($name, $filter, $condition = Filter\FilterComposite::CONDITION_OR) + public function addFilter(string $name, $filter, int $condition = Filter\FilterComposite::CONDITION_OR) : void { $this->resetCaches(); - - return parent::addFilter($name, $filter, $condition); + parent::addFilter($name, $filter, $condition); } /** * {@inheritDoc} */ - public function removeFilter($name) + public function removeFilter(string $name) : void { $this->resetCaches(); - - return parent::removeFilter($name); + parent::removeFilter($name); } /** * {@inheritDoc} */ - public function setNamingStrategy(NamingStrategy\NamingStrategyInterface $strategy) + public function setNamingStrategy(NamingStrategy\NamingStrategyInterface $strategy) : void { $this->resetCaches(); - - return parent::setNamingStrategy($strategy); + parent::setNamingStrategy($strategy); } /** * {@inheritDoc} */ - public function removeNamingStrategy() + public function removeNamingStrategy() : void { $this->resetCaches(); - - return parent::removeNamingStrategy(); + parent::removeNamingStrategy(); } /** * Reset all local hydration/extraction caches */ - private function resetCaches() + private function resetCaches() : void { $this->hydrationMethodsCache = $this->extractionMethodsCache = []; } diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index cd3d48d..d233618 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -1,20 +1,20 @@ $this->getDependencyConfig(), @@ -23,10 +23,8 @@ public function __invoke() /** * Return dependency mappings for this component. - * - * @return array */ - public function getDependencyConfig() + public function getDependencyConfig() : array { return [ 'aliases' => [ diff --git a/src/DelegatingHydrator.php b/src/DelegatingHydrator.php index 9bcbc30..72cf029 100644 --- a/src/DelegatingHydrator.php +++ b/src/DelegatingHydrator.php @@ -1,15 +1,15 @@ hydrators = $hydrators; @@ -31,7 +26,7 @@ public function __construct(ContainerInterface $hydrators) /** * {@inheritdoc} */ - public function hydrate(array $data, $object) + public function hydrate(array $data, object $object) : object { return $this->getHydrator($object)->hydrate($data, $object); } @@ -39,18 +34,15 @@ public function hydrate(array $data, $object) /** * {@inheritdoc} */ - public function extract($object) + public function extract(object $object) : array { return $this->getHydrator($object)->extract($object); } /** - * Gets hydrator of an object - * - * @param object $object - * @return HydratorInterface + * Gets hydrator for an object */ - protected function getHydrator($object) + protected function getHydrator(object $object) : HydratorInterface { return $this->hydrators->get(get_class($object)); } diff --git a/src/DelegatingHydratorFactory.php b/src/DelegatingHydratorFactory.php index a5cf42b..2d6243c 100644 --- a/src/DelegatingHydratorFactory.php +++ b/src/DelegatingHydratorFactory.php @@ -1,40 +1,22 @@ marshalHydratorPluginManager($container); return new DelegatingHydrator($container); @@ -46,7 +28,7 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o * @param ContainerInterface $container * @return HydratorPluginManager */ - private function marshalHydratorPluginManager(ContainerInterface $container) + private function marshalHydratorPluginManager(ContainerInterface $container) : HydratorPluginManager { // Already one? Return it. if ($container instanceof HydratorPluginManager) { diff --git a/src/Exception/BadMethodCallException.php b/src/Exception/BadMethodCallException.php index a8503e2..c7009e0 100644 --- a/src/Exception/BadMethodCallException.php +++ b/src/Exception/BadMethodCallException.php @@ -1,12 +1,12 @@ * - * @param string $name * @param callable|FilterInterface $filter * @param int $condition Can be either * FilterComposite::CONDITION_OR or FilterComposite::CONDITION_AND * @throws InvalidArgumentException - * @return FilterComposite */ - public function addFilter($name, $filter, $condition = self::CONDITION_OR) + public function addFilter(string $name, $filter, int $condition = self::CONDITION_OR) : void { $this->validateFilter($filter, $name); if ($condition === self::CONDITION_OR) { $this->orFilter[$name] = $filter; - } elseif ($condition === self::CONDITION_AND) { - $this->andFilter[$name] = $filter; + return; } - return $this; + if ($condition === self::CONDITION_AND) { + $this->andFilter[$name] = $filter; + return; + } } /** * Check if $name has a filter registered - * - * @param $name string Identifier for the filter - * @return bool */ - public function hasFilter($name) + public function hasFilter(string $name) : bool { return isset($this->orFilter[$name]) || isset($this->andFilter[$name]); } /** * Remove a filter from the composition - * - * @param $name string Identifier for the filter - * @return FilterComposite */ - public function removeFilter($name) + public function removeFilter(string $name) : void { if (isset($this->orFilter[$name])) { unset($this->orFilter[$name]); @@ -112,31 +104,27 @@ public function removeFilter($name) if (isset($this->andFilter[$name])) { unset($this->andFilter[$name]); } - - return $this; } /** * Filter the composite based on the AND and OR condition + * * Will return true if one from the "or conditions" and all from * the "and condition" returns true. Otherwise false * - * @param $property string Parameter will be e.g. Parent\Namespace\Class::method - * @return bool + * @param string $property Parameter will be e.g. Parent\Namespace\Class::method */ - public function filter($property) + public function filter(string $property) : bool { $andCount = count($this->andFilter); $orCount = count($this->orFilter); // return true if no filters are registered if ($orCount === 0 && $andCount === 0) { return true; - } elseif ($orCount === 0 && $andCount !== 0) { - $returnValue = true; - } else { - $returnValue = false; } + $returnValue = $orCount === 0 && $andCount !== 0; + // Check if 1 from the or filters return true foreach ($this->orFilter as $filter) { if (is_callable($filter)) { @@ -145,11 +133,11 @@ public function filter($property) break; } continue; - } else { - if ($filter->filter($property) === true) { - $returnValue = true; - break; - } + } + + if ($filter->filter($property) === true) { + $returnValue = true; + break; } } @@ -160,10 +148,10 @@ public function filter($property) return false; } continue; - } else { - if ($filter->filter($property) === false) { - return false; - } + } + + if ($filter->filter($property) === false) { + return false; } } @@ -172,21 +160,16 @@ public function filter($property) /** * @param FilterInterface|callable $filter - * @param string $name - * - * @return void * @throws InvalidArgumentException */ - private function validateFilter($filter, $name) + private function validateFilter($filter, string $name) : void { if (! is_callable($filter) && ! $filter instanceof FilterInterface) { - throw new InvalidArgumentException( - sprintf( - 'The value of %s should be either a callable or an ' . - 'instance of Zend\Hydrator\Filter\FilterInterface', - $name - ) - ); + throw new InvalidArgumentException(sprintf( + 'The value of %s should be either a callable or an instance of %s', + $name, + FilterInterface::class + )); } } } diff --git a/src/Filter/FilterInterface.php b/src/Filter/FilterInterface.php index fb49918..b6d1d16 100644 --- a/src/Filter/FilterInterface.php +++ b/src/Filter/FilterInterface.php @@ -1,22 +1,20 @@ method = $method; $this->exclude = $exclude; } - public function filter($property) + public function filter(string $property) : bool { $pos = strpos($property, '::'); if ($pos !== false) { @@ -42,9 +42,8 @@ public function filter($property) $pos = 0; } - if (substr($property, $pos) === $this->method) { - return ! $this->exclude; - } - return $this->exclude; + return substr($property, $pos) === $this->method + ? ! $this->exclude + : $this->exclude; } } diff --git a/src/Filter/NumberOfParameterFilter.php b/src/Filter/NumberOfParameterFilter.php index 7343dc0..68bab73 100644 --- a/src/Filter/NumberOfParameterFilter.php +++ b/src/Filter/NumberOfParameterFilter.php @@ -1,12 +1,12 @@ numberOfParameters = (int) $numberOfParameters; } /** - * @param string $property the name of the property - * @return bool * @throws InvalidArgumentException */ - public function filter($property) + public function filter(string $property) : bool { try { $reflectionMethod = new ReflectionMethod($property); } catch (ReflectionException $exception) { - throw new InvalidArgumentException( - "Method $property doesn't exist" - ); + throw new InvalidArgumentException(sprintf( + 'Method %s does not exist', + $property + )); } return $reflectionMethod->getNumberOfParameters() === $this->numberOfParameters; diff --git a/src/Filter/OptionalParametersFilter.php b/src/Filter/OptionalParametersFilter.php index 25775d7..69c526a 100644 --- a/src/Filter/OptionalParametersFilter.php +++ b/src/Filter/OptionalParametersFilter.php @@ -1,12 +1,12 @@ * $filterComposite->removeFilter('has'); * - * - * @param $name - * @return Filter\FilterComposite */ - public function removeFilter($name); + public function removeFilter(string $name) : void; } diff --git a/src/HydrationInterface.php b/src/HydrationInterface.php index ca28983..a86e871 100644 --- a/src/HydrationInterface.php +++ b/src/HydrationInterface.php @@ -1,22 +1,18 @@ hydrator = $hydrator; - - return $this; } /** * Retrieve hydrator - * - * @param void - * @return null|HydratorInterface - * @access public */ - public function getHydrator() + public function getHydrator() : ?HydratorInterface { return $this->hydrator; } diff --git a/src/HydratorInterface.php b/src/HydratorInterface.php index a48effa..305a78e 100644 --- a/src/HydratorInterface.php +++ b/src/HydratorInterface.php @@ -1,12 +1,12 @@ InvokableFactory::class, - ClassMethods::class => InvokableFactory::class, - DelegatingHydrator::class => DelegatingHydratorFactory::class, - ObjectProperty::class => InvokableFactory::class, - Reflection::class => InvokableFactory::class, - - // v2 normalized FQCNs - 'zendhydratorarrayserializable' => InvokableFactory::class, - 'zendhydratorclassmethods' => InvokableFactory::class, - 'zendhydratordelegatinghydrator' => DelegatingHydratorFactory::class, - 'zendhydratorobjectproperty' => InvokableFactory::class, - 'zendhydratorreflection' => InvokableFactory::class, + ArraySerializable::class => InvokableFactory::class, + ClassMethods::class => InvokableFactory::class, + DelegatingHydrator::class => DelegatingHydratorFactory::class, + ObjectProperty::class => InvokableFactory::class, + Reflection::class => InvokableFactory::class, ]; /** @@ -82,10 +75,9 @@ class HydratorPluginManager extends AbstractPluginManager protected $instanceOf = HydratorInterface::class; /** - * Validate the plugin is of the expected type (v3). + * Validate the plugin is of the expected type. * - * Checks that the filter loaded is either a valid callback or an instance - * of FilterInterface. + * Checks that the filter loaded is a valid hydrator. * * @param mixed $instance * @throws InvalidServiceException @@ -98,20 +90,9 @@ public function validate($instance) } throw new InvalidServiceException(sprintf( - 'Plugin of type %s is invalid; must implement Zend\Hydrator\HydratorInterface', - (is_object($instance) ? get_class($instance) : gettype($instance)) + 'Plugin of type %s is invalid; must implement %s', + (is_object($instance) ? get_class($instance) : gettype($instance)), + HydratorInterface::class )); } - - /** - * {@inheritDoc} (v2) - */ - public function validatePlugin($plugin) - { - try { - $this->validate($plugin); - } catch (InvalidServiceException $e) { - throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e); - } - } } diff --git a/src/HydratorPluginManagerFactory.php b/src/HydratorPluginManagerFactory.php index e357afe..940ead8 100644 --- a/src/HydratorPluginManagerFactory.php +++ b/src/HydratorPluginManagerFactory.php @@ -1,32 +1,23 @@ creationOptions); - } - - /** - * zend-servicemanager v2 support for invocation options. - * - * @param array $options - * @return void - */ - public function setCreationOptions(array $options) - { - $this->creationOptions = $options; - } } diff --git a/src/HydratorProviderInterface.php b/src/HydratorProviderInterface.php index c96caa9..9a8f014 100644 --- a/src/HydratorProviderInterface.php +++ b/src/HydratorProviderInterface.php @@ -1,18 +1,18 @@ prototype = $prototype; @@ -60,7 +60,7 @@ public function setPrototype($prototype) /** * @inheritdoc */ - public function setHydrator(HydratorInterface $hydrator) + public function setHydrator(HydratorInterface $hydrator) : void { $this->hydrator = $hydrator; } diff --git a/src/Module.php b/src/Module.php index b4d675f..535e251 100644 --- a/src/Module.php +++ b/src/Module.php @@ -1,18 +1,22 @@ getEvent(); $container = $event->getParam('ServiceManager'); diff --git a/src/NamingStrategy/ArrayMapNamingStrategy.php b/src/NamingStrategy/ArrayMapNamingStrategy.php index 36611a3..3695c33 100644 --- a/src/NamingStrategy/ArrayMapNamingStrategy.php +++ b/src/NamingStrategy/ArrayMapNamingStrategy.php @@ -1,12 +1,12 @@ hydrationMap[$name]) ? $this->hydrationMap[$name] : $name; } @@ -44,7 +42,7 @@ public function hydrate($name) /** * {@inheritDoc} */ - public function extract($name) + public function extract(string $name, ?object $object = null) : string { return isset($this->extractionMap[$name]) ? $this->extractionMap[$name] : $name; } diff --git a/src/NamingStrategy/CompositeNamingStrategy.php b/src/NamingStrategy/CompositeNamingStrategy.php index 6cd002e..04bdfef 100644 --- a/src/NamingStrategy/CompositeNamingStrategy.php +++ b/src/NamingStrategy/CompositeNamingStrategy.php @@ -1,12 +1,12 @@ namingStrategies = array_map( function (NamingStrategyInterface $strategy) { @@ -41,7 +41,7 @@ function (NamingStrategyInterface $strategy) { /** * {@inheritDoc} */ - public function extract($name) + public function extract(string $name, ?object $object = null) : string { $strategy = isset($this->namingStrategies[$name]) ? $this->namingStrategies[$name] @@ -53,7 +53,7 @@ public function extract($name) /** * {@inheritDoc} */ - public function hydrate($name) + public function hydrate(string $name, ?array $data = null) : string { $strategy = isset($this->namingStrategies[$name]) ? $this->namingStrategies[$name] diff --git a/src/NamingStrategy/IdentityNamingStrategy.php b/src/NamingStrategy/IdentityNamingStrategy.php index e21c08a..6f52ff3 100644 --- a/src/NamingStrategy/IdentityNamingStrategy.php +++ b/src/NamingStrategy/IdentityNamingStrategy.php @@ -1,12 +1,12 @@ mapping = $mapping; $this->reverse = $reverse ?: $this->flipMapping($mapping); @@ -42,11 +40,11 @@ public function __construct(array $mapping, array $reverse = null) /** * Safely flip mapping array. * - * @param array $array Array to flip - * @return array Flipped array + * @param array $array Array to flip + * @return array Flipped array * @throws InvalidArgumentException */ - protected function flipMapping(array $array) + protected function flipMapping(array $array) : array { array_walk($array, function ($value) { if (! is_string($value) && ! is_int($value)) { @@ -59,11 +57,8 @@ protected function flipMapping(array $array) /** * Converts the given name so that it can be extracted by the hydrator. - * - * @param string $name The original name - * @return mixed The hydrated name */ - public function hydrate($name) + public function hydrate(string $name, ?array $data = null) : string { if (array_key_exists($name, $this->mapping)) { return $this->mapping[$name]; @@ -74,11 +69,8 @@ public function hydrate($name) /** * Converts the given name so that it can be hydrated by the hydrator. - * - * @param string $name The original name - * @return mixed The extracted name */ - public function extract($name) + public function extract(string $name, ?object $object = null) : string { if (array_key_exists($name, $this->reverse)) { return $this->reverse[$name]; diff --git a/src/NamingStrategy/NamingStrategyInterface.php b/src/NamingStrategy/NamingStrategyInterface.php index cb7ca94..e6a7745 100644 --- a/src/NamingStrategy/NamingStrategyInterface.php +++ b/src/NamingStrategy/NamingStrategyInterface.php @@ -1,12 +1,12 @@ getUnderscoreToCamelCaseFilter()->filter($name); } /** * Remove capitalized letters and prepend underscores. - * - * @param string $name - * @return string */ - public function extract($name) + public function extract(string $name, ?object $object = null) : string { return $this->getCamelCaseToUnderscoreFilter()->filter($name); } @@ -47,7 +43,7 @@ public function extract($name) /** * @return UnderscoreToCamelCaseFilter */ - private function getUnderscoreToCamelCaseFilter() + private function getUnderscoreToCamelCaseFilter() : UnderscoreToCamelCaseFilter { if (! static::$underscoreToCamelCaseFilter) { static::$underscoreToCamelCaseFilter = new UnderscoreToCamelCaseFilter(); @@ -59,7 +55,7 @@ private function getUnderscoreToCamelCaseFilter() /** * @return CamelCaseToUnderscoreFilter */ - private function getCamelCaseToUnderscoreFilter() + private function getCamelCaseToUnderscoreFilter() : CamelCaseToUnderscoreFilter { if (! static::$camelCaseToUnderscoreFilter) { static::$camelCaseToUnderscoreFilter = new CamelCaseToUnderscoreFilter(); diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilter.php b/src/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilter.php index 22d9cd0..1247aec 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilter.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilter.php @@ -5,6 +5,8 @@ * @license https://github.com/zendframework/zend-hydrator/blob/master/LICENSE.md New BSD License */ +declare(strict_types=1); + namespace Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy; /** @@ -14,17 +16,9 @@ final class CamelCaseToUnderscoreFilter { use StringSupportTrait; - /** - * @param string $value - * @return string - */ - public function filter($value) + public function filter(string $value) : string { - if (! is_scalar($value)) { - return $value; - } - - list($pattern, $replacement) = $this->getPatternAndReplacement(); + [$pattern, $replacement] = $this->getPatternAndReplacement(); $filtered = preg_replace($pattern, $replacement, $value); $lowerFunction = $this->getLowerFunction(); @@ -35,7 +29,7 @@ public function filter($value) * @return string[][] Array with two elements, first the patterns, then the * replacements. Each element is an array of strings. */ - private function getPatternAndReplacement() + private function getPatternAndReplacement() : array { return $this->hasPcreUnicodeSupport() ? [ @@ -64,10 +58,7 @@ private function getPatternAndReplacement() ]; } - /** - * @return callable - */ - private function getLowerFunction() + private function getLowerFunction() : callable { return $this->hasMbStringSupport() ? function ($value) { diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/StringSupportTrait.php b/src/NamingStrategy/UnderscoreNamingStrategy/StringSupportTrait.php index 1a85144..1acaa92 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/StringSupportTrait.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/StringSupportTrait.php @@ -5,6 +5,8 @@ * @license https://github.com/zendframework/zend-hydrator/blob/master/LICENSE.md New BSD License */ +declare(strict_types=1); + namespace Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy; use Zend\Stdlib\StringUtils; @@ -20,10 +22,7 @@ trait StringSupportTrait /** @var bool */ private $mbStringSupport; - /** - * @return bool - */ - private function hasPcreUnicodeSupport() + private function hasPcreUnicodeSupport() : bool { if ($this->pcreUnicodeSupport === null) { $this->pcreUnicodeSupport = StringUtils::hasPcreUnicodeSupport(); @@ -31,10 +30,7 @@ private function hasPcreUnicodeSupport() return $this->pcreUnicodeSupport; } - /** - * @return bool - */ - private function hasMbStringSupport() + private function hasMbStringSupport() : bool { if ($this->mbStringSupport === null) { $this->mbStringSupport = extension_loaded('mbstring'); diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php index 496b7cf..45594c8 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php @@ -5,6 +5,8 @@ * @license https://github.com/zendframework/zend-hydrator/blob/master/LICENSE.md New BSD License */ +declare(strict_types=1); + namespace Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy; /** @@ -14,17 +16,9 @@ final class UnderscoreToCamelCaseFilter { use StringSupportTrait; - /** - * @param string $value - * @return string - */ - public function filter($value) + public function filter(string $value) : string { - if (! is_scalar($value)) { - return $value; - } - - list($pattern, $replacement) = $this->getPatternAndReplacement( + [$pattern, $replacement] = $this->getPatternAndReplacement( // a unicode safe way of converting characters to \x00\x00 notation preg_quote('_', '#') ); @@ -36,11 +30,10 @@ public function filter($value) } /** - * @param string $pregQuotedSeparator * @return array Array with two items: the pattern to match, and the * callback to use for replacement. */ - private function getPatternAndReplacement($pregQuotedSeparator) + private function getPatternAndReplacement(string $pregQuotedSeparator) : array { return $this->hasPcreUnicodeSupport() ? $this->getUnicodePatternAndReplacement($pregQuotedSeparator) @@ -55,11 +48,10 @@ function ($matches) { } /** - * @param string $pregQuotedSeparator * @return array Array with two items: the pattern to match, and the * callback to use for replacement. */ - private function getUnicodePatternAndReplacement($pregQuotedSeparator) + private function getUnicodePatternAndReplacement(string $pregQuotedSeparator) : array { return $this->hasMbStringSupport() ? [ @@ -81,10 +73,7 @@ function ($matches) { ]; } - /** - * @return callable - */ - private function getLcFirstFunction() + private function getLcFirstFunction() : callable { return $this->hasMbStringSupport() ? function ($value) { diff --git a/src/NamingStrategyEnabledInterface.php b/src/NamingStrategyEnabledInterface.php index c01a527..2e4aaae 100644 --- a/src/NamingStrategyEnabledInterface.php +++ b/src/NamingStrategyEnabledInterface.php @@ -1,12 +1,12 @@ getFilter(); @@ -64,18 +56,10 @@ public function extract($object) * Hydrate an object by populating public properties * * Hydrates an object by setting public properties of the object. - * - * @throws Exception\BadMethodCallException for a non-object $object */ - public function hydrate(array $data, $object) + public function hydrate(array $data, object $object) : object { - if (! is_object($object)) { - throw new Exception\BadMethodCallException( - sprintf('%s expects the provided $object to be a PHP object)', __METHOD__) - ); - } - - $properties = & self::$skippedPropertiesCache[get_class($object)]; + $properties =& self::$skippedPropertiesCache[get_class($object)]; if (! isset($properties)) { $reflection = new ReflectionClass($object); diff --git a/src/Reflection.php b/src/Reflection.php index 0b26608..b69b61b 100644 --- a/src/Reflection.php +++ b/src/Reflection.php @@ -1,12 +1,12 @@ $value) { @@ -65,31 +58,25 @@ public function hydrate(array $data, $object) * Get a reflection properties from in-memory cache and lazy-load if * class has not been loaded. * - * @param string|object $input - * @throws Exception\InvalidArgumentException * @return ReflectionProperty[] */ - protected static function getReflProperties($input) + protected static function getReflProperties(object $input) : array { - if (is_object($input)) { - $input = get_class($input); - } elseif (! is_string($input)) { - throw new Exception\InvalidArgumentException('Input must be a string or an object.'); - } + $class = get_class($input); - if (isset(static::$reflProperties[$input])) { - return static::$reflProperties[$input]; + if (isset(static::$reflProperties[$class])) { + return static::$reflProperties[$class]; } - static::$reflProperties[$input] = []; - $reflClass = new ReflectionClass($input); + static::$reflProperties[$class] = []; + $reflClass = new ReflectionClass($class); $reflProperties = $reflClass->getProperties(); foreach ($reflProperties as $property) { $property->setAccessible(true); - static::$reflProperties[$input][$property->getName()] = $property; + static::$reflProperties[$class][$property->getName()] = $property; } - return static::$reflProperties[$input]; + return static::$reflProperties[$class]; } } diff --git a/src/Strategy/BooleanStrategy.php b/src/Strategy/BooleanStrategy.php index c7362de..5a1e97d 100644 --- a/src/Strategy/BooleanStrategy.php +++ b/src/Strategy/BooleanStrategy.php @@ -1,12 +1,12 @@ * - * @param callable $extractFunc - anonymous function, that extract values - * from object - * @param callable $hydrateFunc - anonymous function, that hydrate values - * into object + * @param null|callable $extractFunc function for extracting values from an object + * @param null|callable $hydrateFunc function for hydrating values to an object */ - public function __construct($extractFunc = null, $hydrateFunc = null) + public function __construct(?callable $extractFunc = null, ?callable $hydrateFunc = null) { - if (isset($extractFunc)) { - if (! is_callable($extractFunc)) { - throw new \Exception('$extractFunc must be callable'); - } - - $this->extractFunc = $extractFunc; - } else { - $this->extractFunc = function ($value) { - return $value; - }; - } - - if (isset($hydrateFunc)) { - if (! is_callable($hydrateFunc)) { - throw new \Exception('$hydrateFunc must be callable'); - } - - $this->hydrateFunc = $hydrateFunc; - } else { - $this->hydrateFunc = function ($value) { - return $value; - }; - } + $this->extractFunc = $extractFunc; + $this->hydrateFunc = $hydrateFunc; } /** @@ -90,11 +67,12 @@ public function __construct($extractFunc = null, $hydrateFunc = null) * @param array $object The object is optionally provided as context. * @return mixed Returns the value that should be extracted. */ - public function extract($value, $object = null) + public function extract($value, ?object $object = null) { $func = $this->extractFunc; - - return $func($value, $object); + return $func + ? $func($value, $object) + : $value; } /** @@ -104,10 +82,11 @@ public function extract($value, $object = null) * @param array $data The whole data is optionally provided as context. * @return mixed Returns the value that should be hydrated. */ - public function hydrate($value, $data = null) + public function hydrate($value, ?array $data = null) { $func = $this->hydrateFunc; - - return $func($value, $data); + return $func + ? $func($value, $data) + : $value; } } diff --git a/src/Strategy/CollectionStrategy.php b/src/Strategy/CollectionStrategy.php index 579dc6d..7c5d627 100644 --- a/src/Strategy/CollectionStrategy.php +++ b/src/Strategy/CollectionStrategy.php @@ -1,10 +1,12 @@ format = (string) $format; - $this->timezone = $timezone; + public function __construct( + string $format = DateTime::RFC3339, + ?DateTimeZone $timezone = null, + bool $dateTimeFallback = false + ) { + $this->format = $format; + $this->timezone = $timezone; $this->extractionFormat = preg_replace('/(?format); - $this->dateTimeFallback = (bool) $dateTimeFallback; + $this->dateTimeFallback = $dateTimeFallback; } /** @@ -69,7 +68,7 @@ public function __construct($format = DateTime::RFC3339, DateTimeZone $timezone * @param mixed|DateTimeInterface $value * @return mixed|string */ - public function extract($value) + public function extract($value, ?object $object = null) { if ($value instanceof DateTimeInterface) { return $value->format($this->extractionFormat); @@ -86,7 +85,7 @@ public function extract($value) * @param mixed|string $value * @return mixed|DateTimeInterface */ - public function hydrate($value) + public function hydrate($value, ?array $data = null) { if ($value === '' || $value === null) { return; diff --git a/src/Strategy/DefaultStrategy.php b/src/Strategy/DefaultStrategy.php index 6fb8f45..44c35bd 100644 --- a/src/Strategy/DefaultStrategy.php +++ b/src/Strategy/DefaultStrategy.php @@ -1,12 +1,12 @@ setValueDelimiter($delimiter); - - $this->explodeLimit = ($explodeLimit === null) ? null : (int) $explodeLimit; + $this->explodeLimit = ($explodeLimit === null) ? null : $explodeLimit; } /** * Sets the delimiter string that the values will be split upon - * - * @param string $delimiter - * @return self */ - private function setValueDelimiter($delimiter) + private function setValueDelimiter(string $delimiter) : void { - if (! is_string($delimiter)) { - throw new Exception\InvalidArgumentException(sprintf( - '%s expects Delimiter to be string, %s provided instead', - __METHOD__, - is_object($delimiter) ? get_class($delimiter) : gettype($delimiter) - )); - } - if (empty($delimiter)) { throw new Exception\InvalidArgumentException('Delimiter cannot be empty.'); } @@ -63,12 +51,10 @@ private function setValueDelimiter($delimiter) * Split a string by delimiter * * @param string|null $value - * * @return string[] - * * @throws Exception\InvalidArgumentException */ - public function hydrate($value) + public function hydrate($value, ?array $data = null) { if (null === $value) { return []; @@ -83,10 +69,10 @@ public function hydrate($value) } if ($this->explodeLimit !== null) { - return explode($this->valueDelimiter, $value, $this->explodeLimit); + return explode($this->valueDelimiter, (string) $value, $this->explodeLimit); } - return explode($this->valueDelimiter, $value); + return explode($this->valueDelimiter, (string) $value); } /** @@ -95,10 +81,9 @@ public function hydrate($value) * Join array elements with delimiter * * @param string[] $value The original value. - * * @return string|null */ - public function extract($value) + public function extract($value, ?object $object = null) { if (! is_array($value)) { throw new Exception\InvalidArgumentException(sprintf( diff --git a/src/Strategy/SerializableStrategy.php b/src/Strategy/SerializableStrategy.php index 3f2aa56..0ed461a 100644 --- a/src/Strategy/SerializableStrategy.php +++ b/src/Strategy/SerializableStrategy.php @@ -1,12 +1,12 @@ setSerializer($serializer); if ($serializerOptions) { @@ -44,7 +44,7 @@ public function __construct($serializer, $serializerOptions = null) * @param mixed $value The original value. * @return mixed Returns the value that should be extracted. */ - public function extract($value) + public function extract($value, ?object $object = null) { $serializer = $this->getSerializer(); return $serializer->serialize($value); @@ -56,7 +56,7 @@ public function extract($value) * @param mixed $value The original value. * @return mixed Returns the value that should be hydrated. */ - public function hydrate($value) + public function hydrate($value, ?array $data = null) { $serializer = $this->getSerializer(); return $serializer->unserialize($value); @@ -66,9 +66,9 @@ public function hydrate($value) * Set serializer * * @param string|SerializerAdapter $serializer - * @return SerializableStrategy + * @throws InvalidArgumentException for invalid $serializer values */ - public function setSerializer($serializer) + public function setSerializer($serializer) : void { if (! is_string($serializer) && ! $serializer instanceof SerializerAdapter) { throw new InvalidArgumentException(sprintf( @@ -79,15 +79,12 @@ public function setSerializer($serializer) )); } $this->serializer = $serializer; - return $this; } /** * Get serializer - * - * @return SerializerAdapter */ - public function getSerializer() + public function getSerializer() : SerializerAdapter { if (is_string($this->serializer)) { $options = $this->getSerializerOptions(); @@ -101,22 +98,18 @@ public function getSerializer() /** * Set configuration options for instantiating a serializer adapter - * - * @param mixed $serializerOptions - * @return SerializableStrategy */ - public function setSerializerOptions($serializerOptions) + public function setSerializerOptions(iterable $serializerOptions) : void { - $this->serializerOptions = $serializerOptions; - return $this; + $this->serializerOptions = is_array($serializerOptions) + ? $serializerOptions + : iterator_to_array($serializerOptions); } /** * Get configuration options for instantiating a serializer adapter - * - * @return mixed */ - public function getSerializerOptions() + public function getSerializerOptions() : array { return $this->serializerOptions; } diff --git a/src/Strategy/StrategyChain.php b/src/Strategy/StrategyChain.php index 1390b29..6c19514 100644 --- a/src/Strategy/StrategyChain.php +++ b/src/Strategy/StrategyChain.php @@ -1,12 +1,12 @@ extractionStrategies = array_map( @@ -50,10 +45,10 @@ function (StrategyInterface $strategy) { /** * {@inheritDoc} */ - public function extract($value) + public function extract($value, ?object $object = null) { foreach ($this->extractionStrategies as $strategy) { - $value = $strategy->extract($value); + $value = $strategy->extract($value, $object); } return $value; @@ -62,10 +57,10 @@ public function extract($value) /** * {@inheritDoc} */ - public function hydrate($value) + public function hydrate($value, ?array $data = null) { foreach ($this->hydrationStrategies as $strategy) { - $value = $strategy->hydrate($value); + $value = $strategy->hydrate($value, $data); } return $value; diff --git a/src/Strategy/StrategyInterface.php b/src/Strategy/StrategyInterface.php index a705593..fae9c2f 100644 --- a/src/Strategy/StrategyInterface.php +++ b/src/Strategy/StrategyInterface.php @@ -1,34 +1,31 @@ expectException(BadMethodCallException::class); - $this->expectExceptionMessage( - 'Zend\Hydrator\ArraySerializable::extract expects the provided object to implement getArrayCopy()' - ); + $this->expectException(TypeError::class); + $this->expectExceptionMessage('must be an object'); $this->hydrator->extract('thisIsNotAnObject'); } @@ -54,11 +54,8 @@ public function testHydratorExtractThrowsExceptionOnNonObjectParameter() */ public function testHydratorHydrateThrowsExceptionOnNonObjectParameter() { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessage( - 'Zend\Hydrator\ArraySerializable::hydrate expects the provided object to implement' - . ' exchangeArray() or populate()' - ); + $this->expectException(TypeError::class); + $this->expectExceptionMessage('must be an object'); $this->hydrator->hydrate(['some' => 'data'], 'thisIsNotAnObject'); } diff --git a/test/ClassMethodsTest.php b/test/ClassMethodsTest.php index 9d1b4af..80116ac 100644 --- a/test/ClassMethodsTest.php +++ b/test/ClassMethodsTest.php @@ -7,12 +7,13 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use PHPUnit\Framework\TestCase; +use TypeError; use Zend\Hydrator\ClassMethods; -use Zend\Hydrator\Exception\BadMethodCallException; -use Zend\Hydrator\Exception\InvalidArgumentException; use ZendTest\Hydrator\TestAsset\ClassMethodsCamelCaseMissing; use ZendTest\Hydrator\TestAsset\ClassMethodsOptionalParameters; use ZendTest\Hydrator\TestAsset\ClassMethodsCamelCase; @@ -82,12 +83,10 @@ public function testCanHydratedPromiscuousInstances() /** * Verifies the options must be an array or Traversable */ - public function testSetOptionsThrowsInvalidArgumentException() + public function testSetOptionsThrowsException() { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage( - 'The options parameter must be an array or a Traversable' - ); + $this->expectException(TypeError::class); + $this->expectExceptionMessage('must be iterable'); $this->hydrator->setOptions('invalid options'); } @@ -105,26 +104,22 @@ public function testSetOptionsFromTraversable() } /** - * Verifies a BadMethodCallException is thrown for extracting a non-object + * Verifies a TypeError is thrown for extracting a non-object */ - public function testExtractNonObjectThrowsBadMethodCallException() + public function testExtractNonObjectThrowsTypeError() { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessage( - 'Zend\Hydrator\ClassMethods::extract expects the provided $object to be a PHP object)' - ); + $this->expectException(TypeError::class); + $this->expectExceptionMessage('must be an object'); $this->hydrator->extract('non-object'); } /** - * Verifies a BadMethodCallException is thrown for hydrating a non-object + * Verifies a TypeError is thrown for hydrating a non-object */ - public function testHydrateNonObjectThrowsBadMethodCallException() + public function testHydrateNonObjectThrowsTypeError() { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessage( - 'Zend\Hydrator\ClassMethods::hydrate expects the provided $object to be a PHP object)' - ); + $this->expectException(TypeError::class); + $this->expectExceptionMessage('must be an object'); $this->hydrator->hydrate([], 'non-object'); } } diff --git a/test/DelegatingHydratorFactoryTest.php b/test/DelegatingHydratorFactoryTest.php index 9c7122d..5b7cede 100644 --- a/test/DelegatingHydratorFactoryTest.php +++ b/test/DelegatingHydratorFactoryTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use Interop\Container\ContainerInterface; @@ -22,21 +24,6 @@ */ class DelegatingHydratorFactoryTest extends TestCase { - public function testV2Factory() - { - $hydrators = $this->prophesize(HydratorPluginManager::class)->reveal(); - $prophesy = $this->prophesize(ServiceLocatorInterface::class); - $prophesy->willImplement(ContainerInterface::class); - $prophesy->has(HydratorPluginManager::class)->willReturn(true); - $prophesy->get(HydratorPluginManager::class)->willReturn($hydrators); - - $factory = new DelegatingHydratorFactory(); - $this->assertInstanceOf( - DelegatingHydrator::class, - $factory->createService($prophesy->reveal()) - ); - } - public function testFactoryUsesContainerToSeedDelegatingHydratorWhenItIsAHydratorPluginManager() { $hydrators = $this->prophesize(HydratorPluginManager::class)->reveal(); @@ -92,11 +79,6 @@ public function testFactoryCreatesHydratorPluginManagerToSeedDelegatingHydratorA $hydrators = $r->getValue($hydrator); $this->assertInstanceOf(HydratorPluginManager::class, $hydrators); - - $property = method_exists($hydrators, 'configure') - ? 'creationContext' // v3 - : 'serviceLocator'; // v2 - - $this->assertAttributeSame($container->reveal(), $property, $hydrators); + $this->assertAttributeSame($container->reveal(), 'creationContext', $hydrators); } } diff --git a/test/DelegatingHydratorTest.php b/test/DelegatingHydratorTest.php index 5eae136..51cc204 100644 --- a/test/DelegatingHydratorTest.php +++ b/test/DelegatingHydratorTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use ArrayObject; diff --git a/test/Filter/FilterCompositeTest.php b/test/Filter/FilterCompositeTest.php index 1387919..d0256c3 100644 --- a/test/Filter/FilterCompositeTest.php +++ b/test/Filter/FilterCompositeTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Filter; use PHPUnit\Framework\TestCase; diff --git a/test/Filter/MethodMatchFilterTest.php b/test/Filter/MethodMatchFilterTest.php index b520109..b702d8a 100644 --- a/test/Filter/MethodMatchFilterTest.php +++ b/test/Filter/MethodMatchFilterTest.php @@ -5,6 +5,8 @@ * @license https://github.com/zendframework/zend-hydrator/blob/master/LICENSE.md New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Filter; use PHPUnit\Framework\TestCase; diff --git a/test/Filter/NumberOfParameterFilterTest.php b/test/Filter/NumberOfParameterFilterTest.php index ac29922..bc96eac 100644 --- a/test/Filter/NumberOfParameterFilterTest.php +++ b/test/Filter/NumberOfParameterFilterTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Filter; use PHPUnit\Framework\TestCase; @@ -47,7 +49,7 @@ public function testFilterPropertyDoesNotExist() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage( - 'Method ZendTest\Hydrator\Filter\NumberOfParameterFilterTest::methodDoesNotExist doesn\'t exist' + 'Method ZendTest\Hydrator\Filter\NumberOfParameterFilterTest::methodDoesNotExist does not exist' ); $filter = new NumberOfParameterFilter(1); $filter->filter(__CLASS__ . '::methodDoesNotExist'); diff --git a/test/Filter/OptionalParametersFilterTest.php b/test/Filter/OptionalParametersFilterTest.php index 520818c..d282ef8 100644 --- a/test/Filter/OptionalParametersFilterTest.php +++ b/test/Filter/OptionalParametersFilterTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Filter; use InvalidArgumentException; diff --git a/test/HydratorAwareTraitTest.php b/test/HydratorAwareTraitTest.php index be74767..54e2b93 100644 --- a/test/HydratorAwareTraitTest.php +++ b/test/HydratorAwareTraitTest.php @@ -8,6 +8,8 @@ * @package Zend_Hydrator */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use PHPUnit\Framework\TestCase; diff --git a/test/HydratorClosureStrategyTest.php b/test/HydratorClosureStrategyTest.php index b700228..6dcdea5 100644 --- a/test/HydratorClosureStrategyTest.php +++ b/test/HydratorClosureStrategyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use PHPUnit\Framework\TestCase; diff --git a/test/HydratorObjectPropertyTest.php b/test/HydratorObjectPropertyTest.php index 9422c42..448a41b 100644 --- a/test/HydratorObjectPropertyTest.php +++ b/test/HydratorObjectPropertyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use PHPUnit\Framework\TestCase; diff --git a/test/HydratorPluginManagerCompatibilityTest.php b/test/HydratorPluginManagerCompatibilityTest.php index 7765049..2d6b8ac 100644 --- a/test/HydratorPluginManagerCompatibilityTest.php +++ b/test/HydratorPluginManagerCompatibilityTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use PHPUnit\Framework\TestCase; @@ -27,7 +29,7 @@ protected function getPluginManager() protected function getV2InvalidPluginException() { - return RuntimeException::class; + // no-op } protected function getInstanceOf() diff --git a/test/HydratorPluginManagerFactoryTest.php b/test/HydratorPluginManagerFactoryTest.php index 50beefa..a373f7c 100644 --- a/test/HydratorPluginManagerFactoryTest.php +++ b/test/HydratorPluginManagerFactoryTest.php @@ -5,6 +5,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use Interop\Container\ContainerInterface; @@ -24,14 +26,7 @@ public function testFactoryReturnsPluginManager() $hydrators = $factory($container, HydratorPluginManagerFactory::class); $this->assertInstanceOf(HydratorPluginManager::class, $hydrators); - - if (method_exists($hydrators, 'configure')) { - // zend-servicemanager v3 - $this->assertAttributeSame($container, 'creationContext', $hydrators); - } else { - // zend-servicemanager v2 - $this->assertSame($container, $hydrators->getServiceLocator()); - } + $this->assertAttributeSame($container, 'creationContext', $hydrators); } /** @@ -51,27 +46,6 @@ public function testFactoryConfiguresPluginManagerUnderContainerInterop() $this->assertSame($hydrator, $hydrators->get('test')); } - /** - * @depends testFactoryReturnsPluginManager - */ - public function testFactoryConfiguresPluginManagerUnderServiceManagerV2() - { - $container = $this->prophesize(ServiceLocatorInterface::class); - $container->willImplement(ContainerInterface::class); - - $hydrator = $this->prophesize(HydratorInterface::class)->reveal(); - - $factory = new HydratorPluginManagerFactory(); - $factory->setCreationOptions([ - 'services' => [ - 'test' => $hydrator, - ], - ]); - - $hydrators = $factory->createService($container->reveal()); - $this->assertSame($hydrator, $hydrators->get('test')); - } - public function testConfiguresHydratorServicesWhenFound() { $hydrator = $this->prophesize(HydratorInterface::class)->reveal(); diff --git a/test/HydratorStrategyTest.php b/test/HydratorStrategyTest.php index dc9d727..269a1ee 100644 --- a/test/HydratorStrategyTest.php +++ b/test/HydratorStrategyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use PHPUnit\Framework\TestCase; diff --git a/test/HydratorTest.php b/test/HydratorTest.php index 127825d..1ee3f65 100644 --- a/test/HydratorTest.php +++ b/test/HydratorTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use PHPUnit\Framework\TestCase; diff --git a/test/HydratorTestTrait.php b/test/HydratorTestTrait.php index ed33c9c..ed6432c 100644 --- a/test/HydratorTestTrait.php +++ b/test/HydratorTestTrait.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use Zend\Hydrator\NamingStrategy\NamingStrategyInterface; diff --git a/test/Iterator/HydratingArrayIteratorTest.php b/test/Iterator/HydratingArrayIteratorTest.php index 62042e6..048ad38 100644 --- a/test/Iterator/HydratingArrayIteratorTest.php +++ b/test/Iterator/HydratingArrayIteratorTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Iterator; use ArrayObject; diff --git a/test/Iterator/HydratingIteratorIteratorTest.php b/test/Iterator/HydratingIteratorIteratorTest.php index 7ecf595..2b9cb0b 100644 --- a/test/Iterator/HydratingIteratorIteratorTest.php +++ b/test/Iterator/HydratingIteratorIteratorTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Iterator; use ArrayIterator; diff --git a/test/NamingStrategy/ArrayMapNamingStrategyTest.php b/test/NamingStrategy/ArrayMapNamingStrategyTest.php index 05f3f02..6bf4eb4 100644 --- a/test/NamingStrategy/ArrayMapNamingStrategyTest.php +++ b/test/NamingStrategy/ArrayMapNamingStrategyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\NamingStrategy; use PHPUnit\Framework\TestCase; diff --git a/test/NamingStrategy/CompositeNamingStrategyTest.php b/test/NamingStrategy/CompositeNamingStrategyTest.php index 07102df..60762d4 100644 --- a/test/NamingStrategy/CompositeNamingStrategyTest.php +++ b/test/NamingStrategy/CompositeNamingStrategyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\NamingStrategy; use PHPUnit\Framework\TestCase; diff --git a/test/NamingStrategy/IdentityNamingStrategyTest.php b/test/NamingStrategy/IdentityNamingStrategyTest.php index ac54372..3f15f47 100644 --- a/test/NamingStrategy/IdentityNamingStrategyTest.php +++ b/test/NamingStrategy/IdentityNamingStrategyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\NamingStrategy; use PHPUnit\Framework\TestCase; @@ -51,10 +53,8 @@ public function testExtract($name) public function getTestedNames() { return [ - [123], - [0], - ['foo'], - ['bar'], + 'foo' => ['foo'], + 'bar' => ['bar'], ]; } } diff --git a/test/NamingStrategy/MapNamingStrategyTest.php b/test/NamingStrategy/MapNamingStrategyTest.php index 76596dd..a722fa7 100644 --- a/test/NamingStrategy/MapNamingStrategyTest.php +++ b/test/NamingStrategy/MapNamingStrategyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\NamingStrategy; use InvalidArgumentException; diff --git a/test/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilterTest.php b/test/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilterTest.php index fdf15b6..5767741 100644 --- a/test/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilterTest.php +++ b/test/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilterTest.php @@ -5,6 +5,8 @@ * @license https://github.com/zendframework/zend-hydrator/blob/master/LICENSE.md New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\NamingStrategy\UnderscoreNamingStrategy; use PHPUnit\Framework\TestCase; @@ -163,23 +165,4 @@ public function unicodeProviderWithoutMbStrings() ], ]; } - - public function returnUnfilteredDataProvider() - { - return [ - 'underscore' => ['underscore'], - 'null' => [null], - 'object' => [new stdClass()], - ]; - } - - /** - * @dataProvider returnUnfilteredDataProvider - */ - public function testReturnUnfiltered($input) - { - $filter = new CamelCaseToUnderscoreFilter(); - - $this->assertEquals($input, $filter->filter($input)); - } } diff --git a/test/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilterTest.php b/test/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilterTest.php index 0b0981f..28fe75b 100644 --- a/test/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilterTest.php +++ b/test/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilterTest.php @@ -5,6 +5,8 @@ * @license https://github.com/zendframework/zend-hydrator/blob/master/LICENSE.md New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\NamingStrategy\UnderscoreNamingStrategy; use PHPUnit\Framework\TestCase; @@ -143,23 +145,4 @@ public function unicodeWithoutMbStringsProvider() ] ]; } - - public function returnUnfilteredDataProvider() - { - return [ - 'string' => ['foo'], - 'null' => [null], - 'object' => [new stdClass()], - ]; - } - - /** - * @dataProvider returnUnfilteredDataProvider - */ - public function testReturnUnfiltered($input) - { - $filter = new UnderscoreToCamelCaseFilter(); - - $this->assertEquals($input, $filter->filter($input)); - } } diff --git a/test/NamingStrategy/UnderscoreNamingStrategyTest.php b/test/NamingStrategy/UnderscoreNamingStrategyTest.php index bfe4b93..86bbaa8 100644 --- a/test/NamingStrategy/UnderscoreNamingStrategyTest.php +++ b/test/NamingStrategy/UnderscoreNamingStrategyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\NamingStrategy; use PHPUnit\Framework\TestCase; diff --git a/test/ObjectPropertyTest.php b/test/ObjectPropertyTest.php index cb276c7..4e93cfe 100644 --- a/test/ObjectPropertyTest.php +++ b/test/ObjectPropertyTest.php @@ -7,10 +7,12 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; -use BadMethodCallException; use PHPUnit\Framework\TestCase; +use TypeError; use Zend\Hydrator\ObjectProperty; use ZendTest\Hydrator\TestAsset\ClassWithPublicStaticProperties; use ZendTest\Hydrator\TestAsset\ObjectProperty as ObjectPropertyTestAsset; @@ -42,7 +44,7 @@ protected function setUp() */ public function testHydratorExtractThrowsExceptionOnNonObjectParameter() { - $this->expectException(BadMethodCallException::class); + $this->expectException(TypeError::class); $this->hydrator->extract('thisIsNotAnObject'); } @@ -51,7 +53,7 @@ public function testHydratorExtractThrowsExceptionOnNonObjectParameter() */ public function testHydratorHydrateThrowsExceptionOnNonObjectParameter() { - $this->expectException(BadMethodCallException::class); + $this->expectException(TypeError::class); $this->hydrator->hydrate(['some' => 'data'], 'thisIsNotAnObject'); } diff --git a/test/ReflectionTest.php b/test/ReflectionTest.php index 98cefa5..7b9e518 100644 --- a/test/ReflectionTest.php +++ b/test/ReflectionTest.php @@ -7,11 +7,13 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator; use PHPUnit\Framework\TestCase; use stdClass; -use InvalidArgumentException; +use TypeError; use Zend\Hydrator\Reflection; /** @@ -48,21 +50,23 @@ public function testCanHydrate() $this->assertSame($object, $this->hydrator->hydrate(['foo' => 'bar'], $object)); } - public function testNotStringOrObjectOnExtract() + public function testExtractRaisesExceptionForInvalidInput() { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Input must be a string or an object.'); - $argument = (int) 1; + + $this->expectException(TypeError::class); + $this->expectExceptionMessage('must be an object'); + $this->hydrator->extract($argument); } - public function testNotStringOrObjectOnHydrate() + public function testHydrateRaisesExceptionForInvalidArgument() { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Input must be a string or an object.'); - $argument = (int) 1; + + $this->expectException(TypeError::class); + $this->expectExceptionMessage('must be an object'); + $this->hydrator->hydrate([ 'foo' => 'bar' ], $argument); } } diff --git a/test/Strategy/BooleanStrategyTest.php b/test/Strategy/BooleanStrategyTest.php index 06f469a..21c21d9 100644 --- a/test/Strategy/BooleanStrategyTest.php +++ b/test/Strategy/BooleanStrategyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Strategy; use PHPUnit\Framework\TestCase; diff --git a/test/Strategy/CollectionStrategyTest.php b/test/Strategy/CollectionStrategyTest.php index d91be32..3e4d9c6 100644 --- a/test/Strategy/CollectionStrategyTest.php +++ b/test/Strategy/CollectionStrategyTest.php @@ -5,11 +5,14 @@ * @license https://github.com/zendframework/zend-hydrator/blob/master/LICENSE.md New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Strategy; use PHPUnit\Framework\TestCase; use ReflectionClass; use stdClass; +use TypeError; use Zend\Hydrator\Exception; use Zend\Hydrator\HydratorInterface; use Zend\Hydrator\Reflection; @@ -40,13 +43,13 @@ public function testImplementsStrategyInterface() * * @param mixed $objectClassName */ - public function testConstructorRejectsInvalidObjectClassName($objectClassName) - { - $this->expectException(Exception\InvalidArgumentException::class); - $this->expectExceptionMessage(sprintf( - 'Object class name needs to the name of an existing class, got "%s" instead.', - is_object($objectClassName) ? get_class($objectClassName) : gettype($objectClassName) - )); + public function testConstructorRejectsInvalidObjectClassName( + $objectClassName, + string $expectedExceptionType, + string $expectedExceptionMessage + ) { + $this->expectException($expectedExceptionType); + $this->expectExceptionMessage($expectedExceptionMessage); new CollectionStrategy( $this->createHydratorMock(), @@ -54,26 +57,21 @@ public function testConstructorRejectsInvalidObjectClassName($objectClassName) ); } - /** - * @return \Generator - */ - public function providerInvalidObjectClassName() + public function providerInvalidObjectClassName() : array { - $values = [ - 'array' => [], - 'boolean-false' => false, - 'boolean-true' => true, - 'float' => mt_rand() / mt_getrandmax(), - 'integer' => mt_rand(), - 'null' => null, - 'object' => new stdClass(), - 'resource' => fopen(__FILE__, 'r'), - 'string-non-existent-class' => 'FooBarBaz9000', + // @codingStandardsIgnoreStart + return [ + 'array' => [[], TypeError::class, 'must be of the type string'], + 'boolean-false' => [false, TypeError::class, 'must be of the type string'], + 'boolean-true' => [true, TypeError::class, 'must be of the type string'], + 'float' => [mt_rand() / mt_getrandmax(), TypeError::class, 'must be of the type string'], + 'integer' => [mt_rand(), TypeError::class, 'must be of the type string'], + 'null' => [null, TypeError::class, 'must be of the type string'], + 'object' => [new stdClass(), TypeError::class, 'must be of the type string'], + 'resource' => [fopen(__FILE__, 'r'), TypeError::class, 'must be of the type string'], + 'string-non-existent-class' => ['FooBarBaz9000', Exception\InvalidArgumentException::class, 'class name needs to be the name of an existing class'], ]; - - foreach ($values as $key => $value) { - yield $key => [$value]; - } + // @codingStandardsIgnoreEnd } /** @@ -172,7 +170,9 @@ public function testExtractUsesHydratorToExtractValues() ]; $extraction = function (TestAsset\User $value) { - return spl_object_hash($value); + return [ + 'value' => spl_object_hash($value) + ]; }; $hydrator = $this->createHydratorMock(); diff --git a/test/Strategy/DateTimeFormatterStrategyTest.php b/test/Strategy/DateTimeFormatterStrategyTest.php index bee6330..ee6853e 100644 --- a/test/Strategy/DateTimeFormatterStrategyTest.php +++ b/test/Strategy/DateTimeFormatterStrategyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Strategy; use DateTime; @@ -62,20 +64,6 @@ public function testCanHydrateWithInvalidDateTime() $this->assertSame('foo bar baz', $strategy->hydrate('foo bar baz')); } - public function testAcceptsStringCastableDateTimeFormat() - { - $format = $this->getMockBuilder(stdClass::class) - ->setMethods(['__toString']) - ->getMock(); - - $format->expects($this->once())->method('__toString')->will($this->returnValue('d/m/Y')); - - $strategy = new DateTimeFormatterStrategy($format); - - $this->assertEquals('26/04/2014', $strategy->extract(new \DateTime('2014-04-26'))); - $this->assertEquals('26/04/2015', $strategy->extract(new \DateTime('2015-04-26'))); - } - public function testCanExtractAnyDateTimeInterface() { $dateMock = $this diff --git a/test/Strategy/ExplodeStrategyTest.php b/test/Strategy/ExplodeStrategyTest.php index 7540022..c5caa2c 100644 --- a/test/Strategy/ExplodeStrategyTest.php +++ b/test/Strategy/ExplodeStrategyTest.php @@ -7,9 +7,12 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Strategy; use PHPUnit\Framework\TestCase; +use TypeError; use Zend\Hydrator\Strategy\Exception\InvalidArgumentException; use Zend\Hydrator\Strategy\ExplodeStrategy; @@ -63,7 +66,7 @@ public function testGetExceptionWithEmptyDelimiter() public function testGetExceptionWithInvalidDelimiter() { - $this->expectException(InvalidArgumentException::class); + $this->expectException(TypeError::class); new ExplodeStrategy([]); } @@ -73,7 +76,7 @@ public function testHydrateWithExplodeLimit() $strategy = new ExplodeStrategy('-', 2); $this->assertSame(['foo', 'bar-baz-bat'], $strategy->hydrate('foo-bar-baz-bat')); - $strategy = new ExplodeStrategy('-', '3'); + $strategy = new ExplodeStrategy('-', 3); $this->assertSame(['foo', 'bar', 'baz-bat'], $strategy->hydrate('foo-bar-baz-bat')); } @@ -137,20 +140,21 @@ public function testHydration($value, $delimiter, array $expected) */ public function getValidHydratedValues() { + // @codingStandardsIgnoreStart return [ - [null, ',', []], - ['', ',', ['']], - ['foo', ',', ['foo']], - ['foo,bar', ',', ['foo', 'bar']], - ['foo.bar', '.', ['foo', 'bar']], - ['foo.bar', ',', ['foo.bar']], - [123, ',', ['123']], - [123, '2', ['1', '3']], - [123.456, ',', ['123.456']], - [123.456, '.', ['123', '456']], - ['foo,bar,dev,null', ',', ['foo', 'bar', 'dev', 'null']], - ['foo;bar;dev;null', ';', ['foo', 'bar', 'dev', 'null']], - ['', ',', ['']], + 'null-comma' => [null, ',', []], + 'empty-comma' => ['', ',', ['']], + 'string without delimiter-comma' => ['foo', ',', ['foo']], + 'string with delimiter-comma' => ['foo,bar', ',', ['foo', 'bar']], + 'string with delimiter-period' => ['foo.bar', '.', ['foo', 'bar']], + 'string with mismatched delimiter-comma' => ['foo.bar', ',', ['foo.bar']], + 'integer-comma' => [123, ',', ['123']], + 'integer-numeric delimiter' => [123, '2', ['1', '3']], + 'integer with mismatched delimiter-comma' => [123.456, ',', ['123.456']], + 'float-period' => [123.456, '.', ['123', '456']], + 'string containing null-comma' => ['foo,bar,dev,null', ',', ['foo', 'bar', 'dev', 'null']], + 'string containing null-semicolon' => ['foo;bar;dev;null', ';', ['foo', 'bar', 'dev', 'null']], ]; + // @codingStandardsIgnoreEnd } } diff --git a/test/Strategy/SerializableStrategyTest.php b/test/Strategy/SerializableStrategyTest.php index e58179e..47166fb 100644 --- a/test/Strategy/SerializableStrategyTest.php +++ b/test/Strategy/SerializableStrategyTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Strategy; use PHPUnit\Framework\TestCase as TestCase; diff --git a/test/Strategy/StrategyChainTest.php b/test/Strategy/StrategyChainTest.php index ad8a4e6..052a061 100644 --- a/test/Strategy/StrategyChainTest.php +++ b/test/Strategy/StrategyChainTest.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\Strategy; use PHPUnit\Framework\TestCase; diff --git a/test/TestAsset/AggregateObject.php b/test/TestAsset/AggregateObject.php index f1b8b72..124ba0e 100644 --- a/test/TestAsset/AggregateObject.php +++ b/test/TestAsset/AggregateObject.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; /** diff --git a/test/TestAsset/ArrayObjectIterator.php b/test/TestAsset/ArrayObjectIterator.php index f24b675..06073b7 100644 --- a/test/TestAsset/ArrayObjectIterator.php +++ b/test/TestAsset/ArrayObjectIterator.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ArrayObjectIterator implements \Iterator diff --git a/test/TestAsset/ArrayObjectObjectVars.php b/test/TestAsset/ArrayObjectObjectVars.php index f76fec7..176027d 100644 --- a/test/TestAsset/ArrayObjectObjectVars.php +++ b/test/TestAsset/ArrayObjectObjectVars.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ArrayObjectObjectVars diff --git a/test/TestAsset/ArraySerializable.php b/test/TestAsset/ArraySerializable.php index f7c3b0c..eb70443 100644 --- a/test/TestAsset/ArraySerializable.php +++ b/test/TestAsset/ArraySerializable.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; use Zend\Stdlib\ArraySerializableInterface; diff --git a/test/TestAsset/ArraySerializableNoGetArrayCopy.php b/test/TestAsset/ArraySerializableNoGetArrayCopy.php index d240153..a2d6d4e 100644 --- a/test/TestAsset/ArraySerializableNoGetArrayCopy.php +++ b/test/TestAsset/ArraySerializableNoGetArrayCopy.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ArraySerializableNoGetArrayCopy diff --git a/test/TestAsset/ClassMethodsCamelCase.php b/test/TestAsset/ClassMethodsCamelCase.php index 3f54f4a..b2aa374 100644 --- a/test/TestAsset/ClassMethodsCamelCase.php +++ b/test/TestAsset/ClassMethodsCamelCase.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ClassMethodsCamelCase diff --git a/test/TestAsset/ClassMethodsCamelCaseMissing.php b/test/TestAsset/ClassMethodsCamelCaseMissing.php index 965d7c5..ceb7e6e 100644 --- a/test/TestAsset/ClassMethodsCamelCaseMissing.php +++ b/test/TestAsset/ClassMethodsCamelCaseMissing.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ClassMethodsCamelCaseMissing diff --git a/test/TestAsset/ClassMethodsFilterProviderInterface.php b/test/TestAsset/ClassMethodsFilterProviderInterface.php index 9320f18..1e76ce5 100644 --- a/test/TestAsset/ClassMethodsFilterProviderInterface.php +++ b/test/TestAsset/ClassMethodsFilterProviderInterface.php @@ -7,9 +7,12 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; use Zend\Hydrator\Filter\FilterComposite; +use Zend\Hydrator\Filter\FilterInterface; use Zend\Hydrator\Filter\FilterProviderInterface; use Zend\Hydrator\Filter\MethodMatchFilter; use Zend\Hydrator\Filter\GetFilter; @@ -46,7 +49,7 @@ public function getEventManager() return "eventmanager"; } - public function getFilter() + public function getFilter() : FilterInterface { $filterComposite = new FilterComposite(); diff --git a/test/TestAsset/ClassMethodsInvalidParameter.php b/test/TestAsset/ClassMethodsInvalidParameter.php index 55808ff..8896723 100644 --- a/test/TestAsset/ClassMethodsInvalidParameter.php +++ b/test/TestAsset/ClassMethodsInvalidParameter.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ClassMethodsInvalidParameter diff --git a/test/TestAsset/ClassMethodsMagicMethodSetter.php b/test/TestAsset/ClassMethodsMagicMethodSetter.php index 0d3138c..ee6863c 100644 --- a/test/TestAsset/ClassMethodsMagicMethodSetter.php +++ b/test/TestAsset/ClassMethodsMagicMethodSetter.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ClassMethodsMagicMethodSetter diff --git a/test/TestAsset/ClassMethodsOptionalParameters.php b/test/TestAsset/ClassMethodsOptionalParameters.php index 1202ad0..9a9febb 100644 --- a/test/TestAsset/ClassMethodsOptionalParameters.php +++ b/test/TestAsset/ClassMethodsOptionalParameters.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; /** diff --git a/test/TestAsset/ClassMethodsProtectedSetter.php b/test/TestAsset/ClassMethodsProtectedSetter.php index 964ab04..84293c6 100644 --- a/test/TestAsset/ClassMethodsProtectedSetter.php +++ b/test/TestAsset/ClassMethodsProtectedSetter.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ClassMethodsProtectedSetter diff --git a/test/TestAsset/ClassMethodsTitleCase.php b/test/TestAsset/ClassMethodsTitleCase.php index 510d626..2dd4fa6 100644 --- a/test/TestAsset/ClassMethodsTitleCase.php +++ b/test/TestAsset/ClassMethodsTitleCase.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ClassMethodsTitleCase diff --git a/test/TestAsset/ClassMethodsUnderscore.php b/test/TestAsset/ClassMethodsUnderscore.php index e3d2877..3c59c18 100644 --- a/test/TestAsset/ClassMethodsUnderscore.php +++ b/test/TestAsset/ClassMethodsUnderscore.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ClassMethodsUnderscore diff --git a/test/TestAsset/ClassWithPublicStaticProperties.php b/test/TestAsset/ClassWithPublicStaticProperties.php index 5d0441a..7fd27e0 100644 --- a/test/TestAsset/ClassWithPublicStaticProperties.php +++ b/test/TestAsset/ClassWithPublicStaticProperties.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ClassWithPublicStaticProperties diff --git a/test/TestAsset/HydratorClosureStrategyEntity.php b/test/TestAsset/HydratorClosureStrategyEntity.php index d8c1f54..833034a 100644 --- a/test/TestAsset/HydratorClosureStrategyEntity.php +++ b/test/TestAsset/HydratorClosureStrategyEntity.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class HydratorClosureStrategyEntity diff --git a/test/TestAsset/HydratorStrategy.php b/test/TestAsset/HydratorStrategy.php index f1c175c..986aca8 100644 --- a/test/TestAsset/HydratorStrategy.php +++ b/test/TestAsset/HydratorStrategy.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; use Zend\Hydrator\Strategy\DefaultStrategy; @@ -28,7 +30,7 @@ public function __construct() $this->simulatedStorageDevice[] = new HydratorStrategyEntityB(333, 'CCC'); } - public function extract($value) + public function extract($value, ?object $object = null) { $result = []; foreach ($value as $instance) { @@ -37,7 +39,7 @@ public function extract($value) return $result; } - public function hydrate($value) + public function hydrate($value, ?array $data = null) { $result = $value; if (is_array($value)) { diff --git a/test/TestAsset/HydratorStrategyContextAware.php b/test/TestAsset/HydratorStrategyContextAware.php index 344ac8e..493433d 100644 --- a/test/TestAsset/HydratorStrategyContextAware.php +++ b/test/TestAsset/HydratorStrategyContextAware.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; use Zend\Hydrator\Strategy\DefaultStrategy; @@ -16,13 +18,13 @@ class HydratorStrategyContextAware extends DefaultStrategy public $object; public $data; - public function extract($value, $object = null) + public function extract($value, ?object $object = null) { $this->object = $object; return $value; } - public function hydrate($value, $data = null) + public function hydrate($value, ?array $data = null) { $this->data = $data; return $value; diff --git a/test/TestAsset/HydratorStrategyEntityA.php b/test/TestAsset/HydratorStrategyEntityA.php index f4464d7..30c57c2 100644 --- a/test/TestAsset/HydratorStrategyEntityA.php +++ b/test/TestAsset/HydratorStrategyEntityA.php @@ -5,6 +5,8 @@ * @license https://github.com/zendframework/zend-hydrator/blob/master/LICENSE.md New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class HydratorStrategyEntityA diff --git a/test/TestAsset/HydratorStrategyEntityB.php b/test/TestAsset/HydratorStrategyEntityB.php index 5fef1fc..7fa2f93 100644 --- a/test/TestAsset/HydratorStrategyEntityB.php +++ b/test/TestAsset/HydratorStrategyEntityB.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class HydratorStrategyEntityB diff --git a/test/TestAsset/ObjectProperty.php b/test/TestAsset/ObjectProperty.php index e6004a3..4501741 100644 --- a/test/TestAsset/ObjectProperty.php +++ b/test/TestAsset/ObjectProperty.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ObjectProperty diff --git a/test/TestAsset/Reflection.php b/test/TestAsset/Reflection.php index 4a7fd25..8df5440 100644 --- a/test/TestAsset/Reflection.php +++ b/test/TestAsset/Reflection.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class Reflection diff --git a/test/TestAsset/ReflectionFilter.php b/test/TestAsset/ReflectionFilter.php index 0870eef..a4ff541 100644 --- a/test/TestAsset/ReflectionFilter.php +++ b/test/TestAsset/ReflectionFilter.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class ReflectionFilter diff --git a/test/TestAsset/SimpleEntity.php b/test/TestAsset/SimpleEntity.php index 3cdb426..3532733 100644 --- a/test/TestAsset/SimpleEntity.php +++ b/test/TestAsset/SimpleEntity.php @@ -7,6 +7,8 @@ * @license http://framework.zend.com/license/new-bsd New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; class SimpleEntity diff --git a/test/TestAsset/User.php b/test/TestAsset/User.php index 4ed5570..3744995 100644 --- a/test/TestAsset/User.php +++ b/test/TestAsset/User.php @@ -5,6 +5,8 @@ * @license https://github.com/zendframework/zend-hydrator/blob/master/LICENSE.md New BSD License */ +declare(strict_types=1); + namespace ZendTest\Hydrator\TestAsset; final class User From 583cd2445eec02f8fc76817425e03563bea72558 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 20 Nov 2018 10:09:39 -0600 Subject: [PATCH 03/35] Incorporate changes requested by @vaclavvanik - Return value verbatim when empty string, null, or a `DateTimeInterface` instance. - Raise an exception for other non-string values. Also modified the case when hydration fails to vary how the `DateTime` instance is created based on whether or not a timezone is present. --- src/Strategy/DateTimeFormatterStrategy.php | 15 ++++-- .../DateTimeFormatterStrategyTest.php | 46 +++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/Strategy/DateTimeFormatterStrategy.php b/src/Strategy/DateTimeFormatterStrategy.php index 7c4941f..20e0c8f 100644 --- a/src/Strategy/DateTimeFormatterStrategy.php +++ b/src/Strategy/DateTimeFormatterStrategy.php @@ -87,8 +87,15 @@ public function extract($value, ?object $object = null) */ public function hydrate($value, ?array $data = null) { - if ($value === '' || $value === null) { - return; + if ($value === '' || $value === null || $value instanceof DateTimeInterface) { + return $value; + } + + if (! is_string($value)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Unable to hydrate. Expected null, string, or DateTimeInterface; %s was given.', + is_object($value) ? get_class($value) : gettype($value) + )); } $hydrated = $this->timezone @@ -96,7 +103,9 @@ public function hydrate($value, ?array $data = null) : DateTime::createFromFormat($this->format, $value); if ($hydrated === false && $this->dateTimeFallback) { - $hydrated = new DateTime($value, $this->timezone); + $hydrated = $this->timezone + ? new DateTime($value, $this->timezone) + : new DateTime($value); } return $hydrated ?: $value; diff --git a/test/Strategy/DateTimeFormatterStrategyTest.php b/test/Strategy/DateTimeFormatterStrategyTest.php index ee6853e..2f41743 100644 --- a/test/Strategy/DateTimeFormatterStrategyTest.php +++ b/test/Strategy/DateTimeFormatterStrategyTest.php @@ -16,6 +16,7 @@ use DateTimezone; use PHPUnit\Framework\TestCase; use stdClass; +use Zend\Hydrator\Strategy\Exception\InvalidArgumentException; use Zend\Hydrator\Strategy\DateTimeFormatterStrategy; /** @@ -148,4 +149,49 @@ public function testCanHydrateWithDateTimeFallback() $this->assertSame('Europe/Prague', $date->getTimezone()->getName()); } + + public function invalidValuesForHydration() : iterable + { + return [ + 'zero' => [0], + 'int' => [1], + 'zero-float' => [0.0], + 'float' => [1.1], + 'array' => [['2018-11-20']], + 'object' => [(object) ['date' => '2018-11-20']], + ]; + } + + /** + * @dataProvider invalidValuesForHydration + * @param mixed $value + */ + public function testHydrateRaisesExceptionIfValueIsInvalid($value) + { + $strategy = new DateTimeFormatterStrategy('Y-m-d'); + + $this->expectException(InvalidArgumentException::class); + + $strategy->hydrate($value); + } + + public function validUnhydratableValues() : iterable + { + return [ + 'empty string' => [''], + 'null' => [null], + 'date-time' => [new DateTimeImmutable('now')], + ]; + } + + /** + * @dataProvider validUnhydratableValues + * @param mixed $value + */ + public function testReturnsValueVerbatimUnderSpecificConditions($value) + { + $strategy = new DateTimeFormatterStrategy('Y-m-d'); + $hydrated = $strategy->hydrate($value); + $this->assertSame($value, $hydrated); + } } From 722b1e46fe24f2a060613a7ec9cf10193273c5d6 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 20 Nov 2018 10:36:12 -0600 Subject: [PATCH 04/35] Update mkdocs.yml - Mark the "Home" page - Remove the copyright configuration; no longer needed - Sync site_description to package descripiton --- mkdocs.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index e2283d7..79a1ebd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,7 @@ docs_dir: docs/book site_dir: docs/html pages: - - index.md + - Home: index.md - "Quick Start": quick-start.md - Reference: - "Filters": filter.md @@ -13,6 +13,5 @@ pages: - "Underscore Mapping": naming-strategy/underscore-naming-strategy.md - "Composite": naming-strategy/composite-naming-strategy.md site_name: zend-hydrator -site_description: zend-hydrator +site_description: "Serialize objects to arrays, and vice versa" repo_url: 'https://github.com/zendframework/zend-hydrator' -copyright: 'Copyright (c) 2005-2018 Zend Technologies USA Inc.' From 0d07c0da7a2ceae07a63065cd6bd8944b21df7f0 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 26 Nov 2018 11:20:01 -0600 Subject: [PATCH 05/35] Adds phpstan into CI toolchain - Adds phpstan as a development requirement - Adds phpstan configuration at level 7, scanning the src tree - Incorporates phpstan into the Travis CI workflow --- .travis.yml | 2 + composer.json | 5 +- composer.lock | 1100 ++++++++++++++++++++++++++++++++++++++++++++++++- phpstan.neon | 4 + 4 files changed, 1088 insertions(+), 23 deletions(-) create mode 100644 phpstan.neon diff --git a/.travis.yml b/.travis.yml index c5092bd..b96d746 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ matrix: - DEPS=locked - CS_CHECK=true - TEST_COVERAGE=true + - STATIC_ANALYSIS=true - php: 7.2 env: - DEPS=latest @@ -39,6 +40,7 @@ install: - stty cols 120 && composer show script: + - if [[ $STATIC_ANALYSIS == 'true' ]]; then composer analyse ; fi - if [[ $TEST_COVERAGE == 'true' ]]; then composer test-coverage ; else composer test ; fi - if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi diff --git a/composer.json b/composer.json index 24d2b17..79f209c 100644 --- a/composer.json +++ b/composer.json @@ -20,8 +20,10 @@ "zendframework/zend-stdlib": "^3.2.1" }, "require-dev": { - "phpunit/phpunit": "^7.4.4", "phpspec/prophecy": "^1.7.5", + "phpstan/phpstan": "^0.10.5", + "phpstan/phpstan-strict-rules": "^0.10.1", + "phpunit/phpunit": "^7.4.4", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-eventmanager": "^3.2.1", "zendframework/zend-serializer": "^2.9", @@ -58,6 +60,7 @@ } }, "scripts": { + "analyse": "phpstan analyse --no-progress -c phpstan.neon", "check": [ "@cs-check", "@test" diff --git a/composer.lock b/composer.lock index 2f96fe1..3cfdf1b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "2ec62ba8e3d19528297d0736db3bb8bb", + "content-hash": "676f2a1be3e2b36b99b19ea1b6a3a610", "packages": [ { "name": "zendframework/zend-stdlib", @@ -54,6 +54,50 @@ } ], "packages-dev": [ + { + "name": "composer/xdebug-handler", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/b8e9745fb9b06ea6664d8872c4505fb16df4611c", + "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0", + "psr/log": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "time": "2018-08-31T19:07:57+00:00" + }, { "name": "container-interop/container-interop", "version": "1.2.0", @@ -139,6 +183,57 @@ ], "time": "2017-07-22T11:58:36+00:00" }, + { + "name": "jean85/pretty-package-versions", + "version": "1.2", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "75c7effcf3f77501d0e0caa75111aff4daa0dd48" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/75c7effcf3f77501d0e0caa75111aff4daa0dd48", + "reference": "75c7effcf3f77501d0e0caa75111aff4daa0dd48", + "shasum": "" + }, + "require": { + "ocramius/package-versions": "^1.2.0", + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A wrapper for ocramius/package-versions to get pretty versions strings", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "time": "2018-06-13T13:22:40+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.8.1", @@ -154,38 +249,615 @@ "shasum": "" }, "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2018-06-11T23:09:50+00:00" + }, + { + "name": "nette/bootstrap", + "version": "v2.4.6", + "source": { + "type": "git", + "url": "https://github.com/nette/bootstrap.git", + "reference": "268816e3f1bb7426c3a4ceec2bd38a036b532543" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/bootstrap/zipball/268816e3f1bb7426c3a4ceec2bd38a036b532543", + "reference": "268816e3f1bb7426c3a4ceec2bd38a036b532543", + "shasum": "" + }, + "require": { + "nette/di": "~2.4.7", + "nette/utils": "~2.4", + "php": ">=5.6.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "latte/latte": "~2.2", + "nette/application": "~2.3", + "nette/caching": "~2.3", + "nette/database": "~2.3", + "nette/forms": "~2.3", + "nette/http": "~2.4.0", + "nette/mail": "~2.3", + "nette/robot-loader": "^2.4.2 || ^3.0", + "nette/safe-stream": "~2.2", + "nette/security": "~2.3", + "nette/tester": "~2.0", + "tracy/tracy": "^2.4.1" + }, + "suggest": { + "nette/robot-loader": "to use Configurator::createRobotLoader()", + "tracy/tracy": "to use Configurator::enableTracy()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🅱 Nette Bootstrap: the simple way to configure and bootstrap your Nette application.", + "homepage": "https://nette.org", + "keywords": [ + "bootstrapping", + "configurator", + "nette" + ], + "time": "2018-05-17T12:52:20+00:00" + }, + { + "name": "nette/di", + "version": "v2.4.14", + "source": { + "type": "git", + "url": "https://github.com/nette/di.git", + "reference": "923da3e2c0aa53162ef455472c0ac7787b096c5a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/di/zipball/923da3e2c0aa53162ef455472c0ac7787b096c5a", + "reference": "923da3e2c0aa53162ef455472c0ac7787b096c5a", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "nette/neon": "^2.3.3 || ~3.0.0", + "nette/php-generator": "^2.6.1 || ~3.0.0", + "nette/utils": "^2.4.3 || ~3.0.0", + "php": ">=5.6.0" + }, + "conflict": { + "nette/bootstrap": "<2.4", + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "💎 Nette Dependency Injection Container: Flexible, compiled and full-featured DIC with perfectly usable autowiring and support for all new PHP 7.1 features.", + "homepage": "https://nette.org", + "keywords": [ + "compiled", + "di", + "dic", + "factory", + "ioc", + "nette", + "static" + ], + "time": "2018-09-17T15:47:40+00:00" + }, + { + "name": "nette/finder", + "version": "v2.4.2", + "source": { + "type": "git", + "url": "https://github.com/nette/finder.git", + "reference": "ee951a656cb8ac622e5dd33474a01fd2470505a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/finder/zipball/ee951a656cb8ac622e5dd33474a01fd2470505a0", + "reference": "ee951a656cb8ac622e5dd33474a01fd2470505a0", + "shasum": "" + }, + "require": { + "nette/utils": "~2.4", + "php": ">=5.6.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "~2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🔍 Nette Finder: find files and directories with an intuitive API.", + "homepage": "https://nette.org", + "keywords": [ + "filesystem", + "glob", + "iterator", + "nette" + ], + "time": "2018-06-28T11:49:23+00:00" + }, + { + "name": "nette/neon", + "version": "v2.4.3", + "source": { + "type": "git", + "url": "https://github.com/nette/neon.git", + "reference": "5e72b1dd3e2d34f0863c5561139a19df6a1ef398" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/neon/zipball/5e72b1dd3e2d34f0863c5561139a19df6a1ef398", + "reference": "5e72b1dd3e2d34f0863c5561139a19df6a1ef398", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "ext-json": "*", + "php": ">=5.6.0" + }, + "require-dev": { + "nette/tester": "~2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🍸 Nette NEON: encodes and decodes NEON file format.", + "homepage": "http://ne-on.org", + "keywords": [ + "export", + "import", + "neon", + "nette", + "yaml" + ], + "time": "2018-03-21T12:12:21+00:00" + }, + { + "name": "nette/php-generator", + "version": "v3.0.5", + "source": { + "type": "git", + "url": "https://github.com/nette/php-generator.git", + "reference": "ea90209c2e8a7cd087b2742ca553c047a8df5eff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/php-generator/zipball/ea90209c2e8a7cd087b2742ca553c047a8df5eff", + "reference": "ea90209c2e8a7cd087b2742ca553c047a8df5eff", + "shasum": "" + }, + "require": { + "nette/utils": "^2.4.2 || ~3.0.0", + "php": ">=7.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 7.2 features.", + "homepage": "https://nette.org", + "keywords": [ + "code", + "nette", + "php", + "scaffolding" + ], + "time": "2018-08-09T14:32:27+00:00" + }, + { + "name": "nette/robot-loader", + "version": "v3.1.0", + "source": { + "type": "git", + "url": "https://github.com/nette/robot-loader.git", + "reference": "fc76c70e740b10f091e502b2e393d0be912f38d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/robot-loader/zipball/fc76c70e740b10f091e502b2e393d0be912f38d4", + "reference": "fc76c70e740b10f091e502b2e393d0be912f38d4", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "nette/finder": "^2.3 || ^3.0", + "nette/utils": "^2.4 || ^3.0", + "php": ">=5.6.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🍀 Nette RobotLoader: high performance and comfortable autoloader that will search and autoload classes within your application.", + "homepage": "https://nette.org", + "keywords": [ + "autoload", + "class", + "interface", + "nette", + "trait" + ], + "time": "2018-08-13T14:19:06+00:00" + }, + { + "name": "nette/utils", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "17b9f76f2abd0c943adfb556e56f2165460b15ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/17b9f76f2abd0c943adfb556e56f2165460b15ce", + "reference": "17b9f76f2abd0c943adfb556e56f2165460b15ce", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "~2.0", + "tracy/tracy": "^2.3" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize() and toAscii()", + "ext-intl": "for script transliteration in Strings::webalize() and toAscii()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-xml": "to use Strings::length() etc. when mbstring is not available" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ], + "files": [ + "src/loader.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "time": "2018-09-18T10:22:16+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "d0230c5c77a7e3cfa69446febf340978540958c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/d0230c5c77a7e3cfa69446febf340978540958c0", + "reference": "d0230c5c77a7e3cfa69446febf340978540958c0", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2018-10-10T09:24:14+00:00" + }, + { + "name": "ocramius/package-versions", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/PackageVersions.git", + "reference": "4489d5002c49d55576fa0ba786f42dbb009be46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/4489d5002c49d55576fa0ba786f42dbb009be46f", + "reference": "4489d5002c49d55576fa0ba786f42dbb009be46f", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0.0", + "php": "^7.1.0" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "composer/composer": "^1.6.3", + "ext-zip": "*", + "infection/infection": "^0.7.1", + "phpunit/phpunit": "^7.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "2.0.x-dev" + } }, - "type": "library", "autoload": { "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] + "PackageVersions\\": "src/PackageVersions" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } ], - "time": "2018-06-11T23:09:50+00:00" + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "time": "2018-02-05T13:05:30+00:00" }, { "name": "phar-io/manifest", @@ -504,6 +1176,167 @@ ], "time": "2018-08-05T17:53:17+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "0.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "ed3223362174b8067729930439e139794e9e514a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ed3223362174b8067729930439e139794e9e514a", + "reference": "ed3223362174b8067729930439e139794e9e514a", + "shasum": "" + }, + "require": { + "php": "~7.1" + }, + "require-dev": { + "consistence/coding-standard": "^2.0.0", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "phing/phing": "^2.16.0", + "phpstan/phpstan": "^0.10@dev", + "phpunit/phpunit": "^6.3", + "slevomat/coding-standard": "^3.3.0", + "symfony/process": "^3.4 || ^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.3-dev" + } + }, + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "time": "2018-06-20T17:48:01+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "0.10.5", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "c6a8cd1fe08a23b9d101a55ffa9ff6b91d71ef5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6a8cd1fe08a23b9d101a55ffa9ff6b91d71ef5d", + "reference": "c6a8cd1fe08a23b9d101a55ffa9ff6b91d71ef5d", + "shasum": "" + }, + "require": { + "composer/xdebug-handler": "^1.3.0", + "jean85/pretty-package-versions": "^1.0.3", + "nette/bootstrap": "^2.4 || ^3.0", + "nette/di": "^2.4.7 || ^3.0", + "nette/robot-loader": "^3.0.1", + "nette/utils": "^2.4.5 || ^3.0", + "nikic/php-parser": "^4.0.2", + "php": "~7.1", + "phpstan/phpdoc-parser": "^0.3", + "symfony/console": "~3.2 || ~4.0", + "symfony/finder": "~3.2 || ~4.0" + }, + "require-dev": { + "brianium/paratest": "^2.0", + "consistence/coding-standard": "^3.5", + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4", + "ext-gd": "*", + "ext-intl": "*", + "ext-mysqli": "*", + "ext-zip": "*", + "jakub-onderka/php-parallel-lint": "^1.0", + "localheinz/composer-normalize": "~0.9.0", + "phing/phing": "^2.16.0", + "phpstan/phpstan-deprecation-rules": "^0.10.2", + "phpstan/phpstan-php-parser": "^0.10", + "phpstan/phpstan-phpunit": "^0.10", + "phpstan/phpstan-strict-rules": "^0.10", + "phpunit/phpunit": "^7.0", + "slevomat/coding-standard": "^4.7.2" + }, + "bin": [ + "bin/phpstan" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.10-dev" + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": [ + "src/", + "build/PHPStan" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "time": "2018-10-20T17:24:55+00:00" + }, + { + "name": "phpstan/phpstan-strict-rules", + "version": "0.10.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-strict-rules.git", + "reference": "18c0b6e8899606b127c680402ab473a7b67166db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/18c0b6e8899606b127c680402ab473a7b67166db", + "reference": "18c0b6e8899606b127c680402ab473a7b67166db", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.0", + "php": "~7.1", + "phpstan/phpstan": "^0.10" + }, + "require-dev": { + "consistence/coding-standard": "^3.0.1", + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4", + "jakub-onderka/php-parallel-lint": "^1.0", + "phing/phing": "^2.16.0", + "phpstan/phpstan-phpunit": "^0.10", + "phpunit/phpunit": "^7.0", + "slevomat/coding-standard": "^4.5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.10-dev" + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Extra strict and opinionated rules for PHPStan", + "time": "2018-07-06T20:36:44+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "6.1.4", @@ -889,6 +1722,53 @@ ], "time": "2017-02-14T16:28:37+00:00" }, + { + "name": "psr/log", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2018-11-20T15:27:04+00:00" + }, { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", @@ -1530,6 +2410,182 @@ ], "time": "2018-11-07T22:31:41+00:00" }, + { + "name": "symfony/console", + "version": "v4.1.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "c74f4d1988dfcd8760273e53551694da32b056d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/c74f4d1988dfcd8760273e53551694da32b056d0", + "reference": "c74f4d1988dfcd8760273e53551694da32b056d0", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0" + }, + "suggest": { + "psr/log-implementation": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2018-11-26T14:00:40+00:00" + }, + { + "name": "symfony/finder", + "version": "v4.1.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "68fbdcafe915db67adb13fddaec4532e684f6689" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/68fbdcafe915db67adb13fddaec4532e684f6689", + "reference": "68fbdcafe915db67adb13fddaec4532e684f6689", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2018-11-11T19:51:29+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.10.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2018-09-21T13:07:52+00:00" + }, { "name": "theseer/tokenizer", "version": "1.1.0", diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..bc3376c --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + level: 7 + paths: + - src/ From 554d32d2aa8a167bc6c0c2615504f76feb99f172 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 26 Nov 2018 12:31:39 -0600 Subject: [PATCH 06/35] Fix errors reported by phpstan - A number or properties are stored as `ArrayObject` instances, but we were performing various unsupported array operations on them. - Adds zend-modulemanager as a dependency, to eliminate issues with the `Module` class. - Some property annotations were incorrect; updating them to allow known types fixed errors. - Some parameter annotations were incorrect. - Raise an exception from `getNamingStrategy()` if no naming strategy is present. - Subclasses should generally call the parent constructor. - Replace `is_callable()` with `method_exists()` everywhere. - Remove casting during assignments when the type is known. - Do `$serializer` assignment within `SerializableStrategy::getSerializer()` to prevent incorrect detection of string as a type for `$serializer` after lazy loading I've added three `ignoreErrors` directives: - One property is set correctly, and assignments are correct, but phpstan cannot seem to determine that an assignment is valid. - We are passing a PSR-11 container to plugin managers, which is always valid within this context (it will also fulfill a typehint the plugin manager accepts in all situations), but phpstan does not know the context, so assumes an error is present. - A number of `||` and `&&` conditionals are flagged as always returning true or false, but they can actually vary. --- composer.json | 2 +- composer.lock | 168 +++++++++++++++------ phpstan.neon | 4 + src/AbstractHydrator.php | 21 ++- src/Aggregate/AggregateHydrator.php | 2 +- src/Aggregate/ExtractEvent.php | 1 + src/Aggregate/HydrateEvent.php | 1 + src/ArraySerializable.php | 8 +- src/ClassMethods.php | 2 +- src/Filter/NumberOfParameterFilter.php | 2 +- src/ObjectProperty.php | 6 +- src/Reflection.php | 3 +- src/Strategy/ClosureStrategy.php | 8 +- src/Strategy/DateTimeFormatterStrategy.php | 11 +- src/Strategy/SerializableStrategy.php | 8 +- 15 files changed, 172 insertions(+), 75 deletions(-) diff --git a/composer.json b/composer.json index 79f209c..b3c2ffa 100644 --- a/composer.json +++ b/composer.json @@ -22,10 +22,10 @@ "require-dev": { "phpspec/prophecy": "^1.7.5", "phpstan/phpstan": "^0.10.5", - "phpstan/phpstan-strict-rules": "^0.10.1", "phpunit/phpunit": "^7.4.4", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-eventmanager": "^3.2.1", + "zendframework/zend-modulemanager": "^2.8", "zendframework/zend-serializer": "^2.9", "zendframework/zend-servicemanager": "^3.3.2" }, diff --git a/composer.lock b/composer.lock index 3cfdf1b..2118d54 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "676f2a1be3e2b36b99b19ea1b6a3a610", + "content-hash": "678e404e712e151c7c961386f359a544", "packages": [ { "name": "zendframework/zend-stdlib", @@ -1291,52 +1291,6 @@ "description": "PHPStan - PHP Static Analysis Tool", "time": "2018-10-20T17:24:55+00:00" }, - { - "name": "phpstan/phpstan-strict-rules", - "version": "0.10.1", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "18c0b6e8899606b127c680402ab473a7b67166db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/18c0b6e8899606b127c680402ab473a7b67166db", - "reference": "18c0b6e8899606b127c680402ab473a7b67166db", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.0", - "php": "~7.1", - "phpstan/phpstan": "^0.10" - }, - "require-dev": { - "consistence/coding-standard": "^3.0.1", - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4", - "jakub-onderka/php-parallel-lint": "^1.0", - "phing/phing": "^2.16.0", - "phpstan/phpstan-phpunit": "^0.10", - "phpunit/phpunit": "^7.0", - "slevomat/coding-standard": "^4.5.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.10-dev" - } - }, - "autoload": { - "psr-4": { - "PHPStan\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Extra strict and opinionated rules for PHPStan", - "time": "2018-07-06T20:36:44+00:00" - }, { "name": "phpunit/php-code-coverage", "version": "6.1.4", @@ -2705,6 +2659,66 @@ ], "time": "2016-11-09T21:30:43+00:00" }, + { + "name": "zendframework/zend-config", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-config.git", + "reference": "6796f5dcba52c84ef2501d7313618989b5ef3023" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-config/zipball/6796f5dcba52c84ef2501d7313618989b5ef3023", + "reference": "6796f5dcba52c84ef2501d7313618989b5ef3023", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^5.6 || ^7.0", + "psr/container": "^1.0", + "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + }, + "conflict": { + "container-interop/container-interop": "<1.2.0" + }, + "require-dev": { + "malukenho/docheader": "^0.1.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-filter": "^2.7.2", + "zendframework/zend-i18n": "^2.7.4", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.3" + }, + "suggest": { + "zendframework/zend-filter": "^2.7.2; install if you want to use the Filter processor", + "zendframework/zend-i18n": "^2.7.4; install if you want to use the Translator processor", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.3; if you need an extensible plugin manager for use with the Config Factory" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Config\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a nested object property based user interface for accessing this configuration data within application code", + "keywords": [ + "ZendFramework", + "config", + "zf" + ], + "time": "2018-04-24T19:26:44+00:00" + }, { "name": "zendframework/zend-eventmanager", "version": "3.2.1", @@ -2809,6 +2823,66 @@ ], "time": "2018-01-04T17:51:34+00:00" }, + { + "name": "zendframework/zend-modulemanager", + "version": "2.8.2", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-modulemanager.git", + "reference": "394df6e12248ac430a312d4693f793ee7120baa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", + "reference": "394df6e12248ac430a312d4693f793ee7120baa6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0", + "zendframework/zend-config": "^3.1 || ^2.6", + "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", + "zendframework/zend-stdlib": "^3.1 || ^2.7" + }, + "require-dev": { + "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-console": "^2.6", + "zendframework/zend-di": "^2.6", + "zendframework/zend-loader": "^2.5", + "zendframework/zend-mvc": "^3.0 || ^2.7", + "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" + }, + "suggest": { + "zendframework/zend-console": "Zend\\Console component", + "zendframework/zend-loader": "Zend\\Loader component if you are not using Composer autoloading for your modules", + "zendframework/zend-mvc": "Zend\\Mvc component", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\ModuleManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Modular application system for zend-mvc applications", + "homepage": "https://github.com/zendframework/zend-modulemanager", + "keywords": [ + "ZendFramework", + "modulemanager", + "zf" + ], + "time": "2017-12-02T06:11:18+00:00" + }, { "name": "zendframework/zend-serializer", "version": "2.9.0", diff --git a/phpstan.neon b/phpstan.neon index bc3376c..86f9b57 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,3 +2,7 @@ parameters: level: 7 paths: - src/ + ignoreErrors: + - '#Array \(array\\>\) does not accept null.#' + - '#Parameter \#1 \$configInstanceOrParentLocator.*?Psr\\Container\\ContainerInterface given\.#' + - '#Result of [|&]{2} is always (true|false)#' diff --git a/src/AbstractHydrator.php b/src/AbstractHydrator.php index 9c28bdc..19f23f9 100644 --- a/src/AbstractHydrator.php +++ b/src/AbstractHydrator.php @@ -27,7 +27,7 @@ abstract class AbstractHydrator implements /** * An instance of NamingStrategy\NamingStrategyInterface * - * @var NamingStrategy\NamingStrategyInterface + * @var null|NamingStrategy\NamingStrategyInterface */ protected $namingStrategy; @@ -84,17 +84,17 @@ public function getStrategy(string $name) : Strategy\StrategyInterface */ public function hasStrategy(string $name) : bool { - if (array_key_exists($name, $this->strategies)) { + if ($this->strategies->offsetExists($name)) { return true; } if ($this->hasNamingStrategy() - && array_key_exists($this->getNamingStrategy()->hydrate($name), $this->strategies) + && $this->strategies->offsetExists($this->getNamingStrategy()->hydrate($name)) ) { return true; } - return array_key_exists('*', $this->strategies); + return $this->strategies->offsetExists('*'); } /** @@ -155,11 +155,11 @@ public function hydrateValue(string $name, $value, ?array $data = null) /** * Convert a name for extraction. If no naming strategy exists, the plain value is returned. * - * @param string $name The name to convert. - * @param null $object The object is optionally provided as context. + * @param string $name The name to convert. + * @param null|object $object The object is optionally provided as context. * @return mixed */ - public function extractName(string $name, $object = null) + public function extractName(string $name, ?object $object = null) { if ($this->hasNamingStrategy()) { $name = $this->getNamingStrategy()->extract($name, $object); @@ -249,9 +249,16 @@ public function setNamingStrategy(NamingStrategy\NamingStrategyInterface $strate /** * Gets the naming strategy. + * + * @throws Exception\DomainException if no naming strategy is registered. */ public function getNamingStrategy() : NamingStrategy\NamingStrategyInterface { + if (null === $this->namingStrategy) { + throw new Exception\DomainException( + 'Missing naming strategy; call hasNamingStrategy() to test for presence first' + ); + } return $this->namingStrategy; } diff --git a/src/Aggregate/AggregateHydrator.php b/src/Aggregate/AggregateHydrator.php index 5db67a4..b0e1257 100644 --- a/src/Aggregate/AggregateHydrator.php +++ b/src/Aggregate/AggregateHydrator.php @@ -22,7 +22,7 @@ class AggregateHydrator implements HydratorInterface, EventManagerAwareInterface public const DEFAULT_PRIORITY = 1; /** - * @var EventManagerInterface|null + * @var EventManagerInterface */ protected $eventManager; diff --git a/src/Aggregate/ExtractEvent.php b/src/Aggregate/ExtractEvent.php index 253c135..32e2d88 100644 --- a/src/Aggregate/ExtractEvent.php +++ b/src/Aggregate/ExtractEvent.php @@ -40,6 +40,7 @@ class ExtractEvent extends Event */ public function __construct($target, $extractionObject) { + parent::__construct(); $this->target = $target; $this->extractionObject = $extractionObject; } diff --git a/src/Aggregate/HydrateEvent.php b/src/Aggregate/HydrateEvent.php index 5a83d97..e614083 100644 --- a/src/Aggregate/HydrateEvent.php +++ b/src/Aggregate/HydrateEvent.php @@ -40,6 +40,7 @@ class HydrateEvent extends Event */ public function __construct($target, $hydratedObject, array $hydrationData) { + parent::__construct(); $this->target = $target; $this->hydratedObject = $hydratedObject; $this->hydrationData = $hydrationData; diff --git a/src/ArraySerializable.php b/src/ArraySerializable.php index 8d8e9c4..44189cf 100644 --- a/src/ArraySerializable.php +++ b/src/ArraySerializable.php @@ -20,7 +20,7 @@ class ArraySerializable extends AbstractHydrator */ public function extract(object $object) : array { - if (! is_callable([$object, 'getArrayCopy'])) { + if (! method_exists($object, 'getArrayCopy')) { throw new Exception\BadMethodCallException( sprintf('%s expects the provided object to implement getArrayCopy()', __METHOD__) ); @@ -67,10 +67,10 @@ public function hydrate(array $data, object $object) : object $replacement[$name] = $this->hydrateValue($name, $value, $data); } - if (is_callable([$object, 'exchangeArray'])) { + if (method_exists($object, 'exchangeArray')) { // Ensure any previously populated values not in the replacement // remain following population. - if (is_callable([$object, 'getArrayCopy'])) { + if (method_exists($object, 'getArrayCopy')) { $original = $object->getArrayCopy($object); $replacement = array_merge($original, $replacement); } @@ -78,7 +78,7 @@ public function hydrate(array $data, object $object) : object return $object; } - if (is_callable([$object, 'populate'])) { + if (method_exists($object, 'populate')) { $object->populate($replacement); return $object; } diff --git a/src/ClassMethods.php b/src/ClassMethods.php index 4de8ce7..f4b9536 100644 --- a/src/ClassMethods.php +++ b/src/ClassMethods.php @@ -41,7 +41,7 @@ class ClassMethods extends AbstractHydrator implements HydratorOptionsInterface * A map of extraction methods to property name to be used during extraction, indexed * by class name and method name * - * @var string[][] + * @var (null|string)[][] */ private $extractionMethodsCache = []; diff --git a/src/Filter/NumberOfParameterFilter.php b/src/Filter/NumberOfParameterFilter.php index 68bab73..c773647 100644 --- a/src/Filter/NumberOfParameterFilter.php +++ b/src/Filter/NumberOfParameterFilter.php @@ -26,7 +26,7 @@ class NumberOfParameterFilter implements FilterInterface */ public function __construct(int $numberOfParameters = 0) { - $this->numberOfParameters = (int) $numberOfParameters; + $this->numberOfParameters = $numberOfParameters; } /** diff --git a/src/ObjectProperty.php b/src/ObjectProperty.php index 5a751a4..4a3ae34 100644 --- a/src/ObjectProperty.php +++ b/src/ObjectProperty.php @@ -15,7 +15,7 @@ class ObjectProperty extends AbstractHydrator { /** - * @var array[] indexed by class name and then property name + * @var (null|array)[] indexed by class name and then property name */ private static $skippedPropertiesCache = []; @@ -59,9 +59,9 @@ public function extract(object $object) : array */ public function hydrate(array $data, object $object) : object { - $properties =& self::$skippedPropertiesCache[get_class($object)]; + $properties =& self::$skippedPropertiesCache[get_class($object)] ?? null; - if (! isset($properties)) { + if (null === $properties) { $reflection = new ReflectionClass($object); $properties = array_fill_keys( array_map( diff --git a/src/Reflection.php b/src/Reflection.php index b69b61b..27bf68e 100644 --- a/src/Reflection.php +++ b/src/Reflection.php @@ -16,7 +16,8 @@ class Reflection extends AbstractHydrator { /** * Simple in-memory array cache of ReflectionProperties used. - * @var ReflectionProperty[] + * + * @var ReflectionProperty[][] */ protected static $reflProperties = []; diff --git a/src/Strategy/ClosureStrategy.php b/src/Strategy/ClosureStrategy.php index a8a4886..a702560 100644 --- a/src/Strategy/ClosureStrategy.php +++ b/src/Strategy/ClosureStrategy.php @@ -20,7 +20,7 @@ class ClosureStrategy implements StrategyInterface * }; * * - * @var callable + * @var null|callable */ protected $extractFunc = null; @@ -33,7 +33,7 @@ class ClosureStrategy implements StrategyInterface * }; * * - * @var callable + * @var null|callable */ protected $hydrateFunc = null; @@ -63,8 +63,8 @@ public function __construct(?callable $extractFunc = null, ?callable $hydrateFun /** * Converts the given value so that it can be extracted by the hydrator. * - * @param mixed $value The original value. - * @param array $object The object is optionally provided as context. + * @param mixed $value The original value. + * @param null|object $object The object is optionally provided as context. * @return mixed Returns the value that should be extracted. */ public function extract($value, ?object $object = null) diff --git a/src/Strategy/DateTimeFormatterStrategy.php b/src/Strategy/DateTimeFormatterStrategy.php index 20e0c8f..27ef3dc 100644 --- a/src/Strategy/DateTimeFormatterStrategy.php +++ b/src/Strategy/DateTimeFormatterStrategy.php @@ -56,8 +56,17 @@ public function __construct( ) { $this->format = $format; $this->timezone = $timezone; - $this->extractionFormat = preg_replace('/(?format); $this->dateTimeFallback = $dateTimeFallback; + + $extractionFormat = preg_replace('/(?format); + if (null === $extractionFormat) { + throw new Exception\InvalidArgumentException(sprintf( + 'Format provided (%s) contains invalid characters; please verify the format', + $format + )); + } + + $this->extractionFormat = $extractionFormat; } /** diff --git a/src/Strategy/SerializableStrategy.php b/src/Strategy/SerializableStrategy.php index 0ed461a..8566e93 100644 --- a/src/Strategy/SerializableStrategy.php +++ b/src/Strategy/SerializableStrategy.php @@ -27,8 +27,8 @@ class SerializableStrategy implements StrategyInterface /** * - * @param mixed $serializer string or SerializerAdapter - * @param mixed $serializerOptions + * @param string|SerializerAdapter $serializer + * @param null|iterable $serializerOptions */ public function __construct($serializer, ?iterable $serializerOptions = null) { @@ -88,9 +88,9 @@ public function getSerializer() : SerializerAdapter { if (is_string($this->serializer)) { $options = $this->getSerializerOptions(); - $this->setSerializer(SerializerFactory::factory($this->serializer, $options)); + $this->serializer = SerializerFactory::factory($this->serializer, $options); } elseif (null === $this->serializer) { - $this->setSerializer(SerializerFactory::getDefaultAdapter()); + $this->serializer = SerializerFactory::getDefaultAdapter(); } return $this->serializer; From 553d40bd476c15f17b0cfd05922db48966f876bf Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 26 Nov 2018 13:01:20 -0600 Subject: [PATCH 07/35] Versions documentation - Creates both `v2/` and `v3/` trees in the documentation, based on 2.4.1 docs. - Makes all existing docs into redirects, redirecting to the v2 docs. - Updates the `mkdocs.yml`: - Makes the top-level navigation items point to the v3 artifacts. - Adds a `v2` navigation item, with subitems pointing to the v2 docs. - Adds hidden pages pointing to the legacy locations, to allow redirects. **NOTE**: No `v1/` tree is created, as that version did not have ship documentation within the repository. --- docs/book/aggregate.md | 151 +-------- docs/book/filter.md | 310 +----------------- .../composite-naming-strategy.md | 89 +---- .../identity-naming-strategy.md | 38 +-- .../naming-strategy/map-naming-strategy.md | 47 +-- .../underscore-naming-strategy.md | 42 +-- docs/book/quick-start.md | 127 +------ docs/book/strategy.md | 247 +------------- docs/book/v2/aggregate.md | 145 ++++++++ docs/book/v2/filter.md | 304 +++++++++++++++++ .../composite-naming-strategy.md | 83 +++++ .../identity-naming-strategy.md | 32 ++ .../v2/naming-strategy/map-naming-strategy.md | 41 +++ .../underscore-naming-strategy.md | 36 ++ docs/book/v2/quick-start.md | 121 +++++++ docs/book/v2/strategy.md | 225 +++++++++++++ docs/book/v3/aggregate.md | 145 ++++++++ docs/book/v3/filter.md | 304 +++++++++++++++++ .../composite-naming-strategy.md | 83 +++++ .../identity-naming-strategy.md | 32 ++ .../v3/naming-strategy/map-naming-strategy.md | 41 +++ .../underscore-naming-strategy.md | 36 ++ docs/book/v3/quick-start.md | 121 +++++++ docs/book/v3/strategy.md | 225 +++++++++++++ mkdocs.yml | 38 ++- 25 files changed, 2051 insertions(+), 1012 deletions(-) create mode 100644 docs/book/v2/aggregate.md create mode 100644 docs/book/v2/filter.md create mode 100644 docs/book/v2/naming-strategy/composite-naming-strategy.md create mode 100644 docs/book/v2/naming-strategy/identity-naming-strategy.md create mode 100644 docs/book/v2/naming-strategy/map-naming-strategy.md create mode 100644 docs/book/v2/naming-strategy/underscore-naming-strategy.md create mode 100644 docs/book/v2/quick-start.md create mode 100644 docs/book/v2/strategy.md create mode 100644 docs/book/v3/aggregate.md create mode 100644 docs/book/v3/filter.md create mode 100644 docs/book/v3/naming-strategy/composite-naming-strategy.md create mode 100644 docs/book/v3/naming-strategy/identity-naming-strategy.md create mode 100644 docs/book/v3/naming-strategy/map-naming-strategy.md create mode 100644 docs/book/v3/naming-strategy/underscore-naming-strategy.md create mode 100644 docs/book/v3/quick-start.md create mode 100644 docs/book/v3/strategy.md diff --git a/docs/book/aggregate.md b/docs/book/aggregate.md index 711ad6f..c779adc 100644 --- a/docs/book/aggregate.md +++ b/docs/book/aggregate.md @@ -1,145 +1,6 @@ -# AggregateHydrator - -`Zend\Hydrator\Aggregate\AggregateHydrator` is an implementation of -`Zend\Hydrator\HydratorInterface` that composes multiple hydrators via event listeners. - -You typically want to use an aggregate hydrator when you want to hydrate or extract data from -complex objects that implement multiple interfaces, and therefore need multiple hydrators to handle -that in subsequent steps. - -## Installation requirements - -The `AggregateHydrator` depends on the zend-eventmanager component, so be sure to have it -installed before getting started: - -```bash -$ composer require zendframework/zend-eventmanager -``` - -## Basic usage - -A simple use case may be hydrating a `BlogPost` object, which contains data for the user that -created it, the time it was created, the current publishing status, etc: - -```php -use Zend\Hydrator\Aggregate\AggregateHydrator; - -$hydrator = new AggregateHydrator(); - -// attach the various hydrators capable of handling simpler interfaces -$hydrator->add(new My\BlogPostHydrator()); -$hydrator->add(new My\UserAwareObjectHydrator()); -$hydrator->add(new My\TimestampedObjectHydrator()); -$hydrator->add(new My\PublishableObjectHydrator()); -// ... - -// Now retrieve the BlogPost object -// ... - -// you can now extract complex data from a blogpost -$data = $hydrator->extract($blogPost); - -// or you can fill the object with complex data -$blogPost = $hydrator->hydrate($data, $blogPost); -``` - -> ### Hydrator priorities -> -> `AggregateHydrator::add` has a second optional argument, `$priority`. If you -> have two or more hydrators that conflict with each other for same data keys, -> you may decide which one to execute first or last by passing a higher or lower -> integer priority, respectively, to this argument. - -In order to work with this logic, each of the hydrators that are attached should -ignore any unknown object type passed in: - -```php -namespace My; - -use Zend\Hydrator\HydratorInterface - -class BlogPostHydrator implements HydratorInterface -{ - public function hydrate($data, $object) - { - if (! $object instanceof BlogPost) { - return $object; - } - - // ... continue hydration ... - } - - public function extract($object) - { - if (! $object instanceof BlogPost) { - return array(); - } - - // ... continue extraction ... - } -} -``` - -## Advanced use cases - -Since the `AggregateHydrator` is event-driven, you can use the `EventManager` -API to tweak its behaviour. - -Common use cases include: - -- Removal of hydrated data keys (passwords/confidential information) depending - on business rules. -- Caching of the hydration/extraction process. -- Transformations on extracted data, for compatibility with third-party APIs. - -In the following example, a cache listener is introduced to speed up hydration, which can be -very useful when the same data is requested multiple times: - -```php -use Zend\Hydrator\Aggregate\AggregateHydrator; -use Zend\Hydrator\Aggregate\ExtractEvent; -use Zend\Cache\Storage\Adapter\Memory; - -$hydrator = new AggregateHydrator(); - -// Attach the various hydrators: -$hydrator->add(new My\BlogPostHydrator()); -$hydrator->add(new My\UserAwareObjectHydrator()); -$hydrator->add(new My\TimestampedObjectHydrator()); -$hydrator->add(new My\PublishableObjectHydrator()); -// ... - -$cache = new Memory(); -$cacheReadListener = function (ExtractEvent $event) use ($cache) { - $object = $event->getExtractionObject(); - - if (! $object instanceof BlogPost) { - return; - } - - if ($cache->hasItem($object->getId())) { - $event->setExtractedData($cache->getItem($object->getId())); - $event->stopPropagation(); - } -}; - -$cacheWriteListener = function (ExtractEvent $event) use ($cache) { - $object = $event->getExtractionObject(); - - if (! $object instanceof BlogPost) { - return; - } - - $cache->setItem($object->getId(), $event->getExtractedData()); -}; - -// Attaching a high priority listener executed before extraction logic: -$eventManager = $hydrator->getEventManager(); -$eventManager()->attach(ExtractEvent::EVENT_EXTRACT, $cacheReadListener, 1000); - -// Attaching a low priority listener executed after extraction logic: -$eventManager()->attach(ExtractEvent::EVENT_EXTRACT, $cacheWriteListener, -1000); -``` - -With an aggregate hydrator configured in this way, any -`$hydrator->extract($blogPost)` operation will be cached. + + diff --git a/docs/book/filter.md b/docs/book/filter.md index 679c683..89b003e 100644 --- a/docs/book/filter.md +++ b/docs/book/filter.md @@ -1,304 +1,6 @@ -# Zend\\Hydrator\\Filter - -Hydrator filters allow you to manipulate the behavior of the `extract()` -operation. This is especially useful, if you want to omit some internals (e.g. -`getServiceManager()`) from the array representation. - -It comes with a helpful `Composite` implementation, and several filters for -common use cases. The filters are composed in the `AbstractHydrator`, so you can -start using them immediately in any custom extensions you write that extend that -class. - -```php -namespace Zend\Hydrator\Filter; - -interface FilterInterface -{ - /** - * Should return true, if the given filter - * does not match - * - * @param string $property The name of the property - * @return bool - */ - public function filter($property); -} -``` - -If it returns true, the key/value pairs will be in the extracted arrays - if it -returns false, you'll not see them again. - -## Filter implementations - -### Zend\\Hydrator\\Filter\\GetFilter - -This filter is used in the `ClassMethods` hydrator to decide which getters will -be extracted. It checks if the key to extract starts with `get` or the object -contains a method beginning with `get` (e.g., `Zend\Foo\Bar::getFoo`). - -### Zend\\Hydrator\\Filter\\HasFilter - -This filter is used in the `ClassMethods` hydrator to decide which `has` methods -will be extracted. It checks if the key to extract begins with `has` or the -object contains a method beginning with `has` (e.g., `Zend\Foo\Bar::hasFoo`). - -### Zend\\Hydrator\\Filter\\IsFilter - -This filter is used in the `ClassMethods` hydrator to decide which `is` methods -will be extracted. It checks if the key to extract begins with `is` or the -object contains a method beginning with `is` (e.g., `Zend\Foo\Bar::isFoo`). - -### Zend\\Hydrator\\Filter\\MethodMatchFilter - -This filter allows you to omit methods during extraction that match the -condition defined in the composite. The name of the method is specified in the -constructor of this filter; the second parameter decides whether to use white or -blacklisting to decide (whitelisting retains only the matching method, blacklist -omits any matching method). The default is blacklisting - pass `false` to change -the behavior. - -### Zend\\Hydrator\\Filter\\NumberOfParameterFilter - -This filter is used in the `ClassMethods` hydrator to check the number of -parameters. By convention, the `get`, `has` and `is` methods do not get any -parameters - but it may happen. You can add your own number of required -parameters, simply add the number to the constructor. The default value is 0. If -the method has more or fewer parameters than what the filter accepts, it will be -omitted. - -## Remove filters - -If you want to tell e.g. the `ClassMethods` hydrator, to not extract methods that start with `is`, -remove the related filter: - -```php -$hydrator = new ClassMethods(false); -$hydrator->removeFilter('is'); -``` - -After performing the above, the key/value pairs for `is` methods will not end up -in your extracted array anymore. The filters can be used in any hydrator, but -the `ClassMethods` hydrator is the only one, that has pre-registered filters: - -```php -$this->filterComposite->addFilter('is', new IsFilter()); -$this->filterComposite->addFilter('has', new HasFilter()); -$this->filterComposite->addFilter('get', new GetFilter()); -$this->filterComposite->addFilter( - 'parameter', - new NumberOfParameterFilter(), - FilterComposite::CONDITION_AND -); -``` - -If these are not appropriate for your object, you can unregister them as shown -in the previous example. - -## Add filters - -You can add filters to any hydrator that extends the `AbstractHydrator`. Filters -can either implement `FilterInterface`, or simply be PHP callables: - -```php -$hydrator->addFilter('len', function($property) { - if (strlen($property) !== 3) { - return false; - } - return true; -}); -``` - -By default, every filter you add will be added with a conditional `or`. If you -want to add it with `and` (as the `NumberOfParameterFilter` that is added to the -`ClassMethods` hydrator by default), provide the conditon as the third argument -to `addFilter`: - -```php -$hydrator->addFilter('len', function($property) { - if (strlen($property) !== 3) { - return false; - } - return true; -}, FilterComposite::CONDITION_AND); -``` - -One common use case for filters is to omit getters for values that you do not -want to represent, such as a service manager instance: - -```php -$hydrator->addFilter( - 'servicemanager', - new MethodMatchFilter('getServiceManager'), - FilterComposite::CONDITION_AND -); -``` - -The example above will exclude the `getServiceManager()` method and the -`servicemanager` key from extraction, even if the `get` filter wants to add it. - -## Use FilterComposite for complex filters - -`FilterComposite` implements `FilterInterface` as well, so you can add it as -a regular filter to the hydrator. One benefit of this implementation is that you -can add the filters with a condition and accomplish complex requirements using -different composites with different conditions. You can pass the following -conditions to the 3rd parameter, when you add a filter: - -### Zend\\Hydrator\\Filter\\FilterComposite::CONDITION\_OR - -At the given level of the composite, at least one filter in that condition block -has to return true to extract the value. - -### Zend\\Hydrator\\Filter\\FilterComposite::CONDITION\_AND - -At the given level of the composite, all filters in that condition block must -return true to extract the value. - -### FilterComposite Examples - -This composition will have a similar logic as the if below: - -```php -$composite = new FilterComposite(); - -$composite->addFilter('one', $condition1); -$composite->addFilter('two', $condition2); -$composite->addFilter('three', $condition3); -$composite->addFilter('four', $condition4, FilterComposite::CONDITION_AND); -$composite->addFilter('five', $condition5, FilterComposite::CONDITION_AND); - -// This is what's happening internally -if ( - ($condition1 - || $condition2 - || $condition3 - ) && ($condition4 - && $condition5 - ) -) { - //do extraction -} -``` - -If you only have one condition (e.g., only an `and` or `or`) block, the other -one will be completely ignored. - -A bit more complex filter can look like this: - -```php -$composite = new FilterComposite(); -$composite->addFilter( - 'servicemanager', - new MethodMatchFilter('getServiceManager'), - FilterComposite::CONDITION_AND -); -$composite->addFilter( - 'eventmanager', - new MethodMatchFilter('getEventManager'), - FilterComposite::CONDITION_AND -); - -$hydrator->addFilter('excludes', $composite, FilterComposite::CONDITION_AND); - -// Internal -if (( // default composite inside the hydrator - ($getFilter - || $hasFilter - || $isFilter - ) && ( - $numberOfParameterFilter - ) - ) && ( // new composite, added to the one above - $serviceManagerFilter - && $eventManagerFilter - ) -) { - // do extraction -} -``` - -If you perform this on the `ClassMethods` hydrator, all getters will get -extracted, except for `getServiceManager()` and `getEventManager()`. - -## Using the provider interface - -`FilterProviderInterface` allows you to configure the behavior of the hydrator -inside your objects. - -```php -namespace Zend\Hydrator\Filter; - -interface FilterProviderInterface -{ - /** - * Provides a filter for hydration - * - * @return FilterInterface - */ - public function getFilter(); -} -``` - -(The `getFilter()` method is automatically excluded from `extract()`.) If the -extracted object implements the `Zend\Hydrator\Filter\FilterProviderInterface`, -the returned `FilterInterface` instance can also be a `FilterComposite`. - -For example: - -```php -Class Foo implements FilterProviderInterface -{ - public function getFoo() - { - return 'foo'; - } - - public function hasFoo() - { - return true; - } - - public function getServiceManager() - { - return 'servicemanager'; - } - - public function getEventManager() - { - return 'eventmanager'; - } - - public function getFilter() - { - $composite = new FilterComposite(); - $composite->addFilter('get', new GetFilter()); - - $exclusionComposite = new FilterComposite(); - $exclusionComposite->addFilter( - 'servicemanager', - new MethodMatchFilter('getServiceManager'), - FilterComposite::CONDITION_AND - ); - $exclusionComposite->addFilter( - 'eventmanager', - new MethodMatchFilter('getEventManager'), - FilterComposite::CONDITION_AND - ); - - $composite->addFilter('excludes', $exclusionComposite, FilterComposite::CONDITION_AND); - - return $composite; - } -} - -$hydrator = new ClassMethods(false); -$extractedArray = $hydrator->extract(new Foo()); -``` - -`$extractedArray` will only have 'foo' => 'foo'; all other values are -excluded from extraction. - -> ### Note -> -> All pre-registered filters from the `ClassMethods` hydrator are ignored when -> this interface is used. + + diff --git a/docs/book/naming-strategy/composite-naming-strategy.md b/docs/book/naming-strategy/composite-naming-strategy.md index a62f0ab..8a483bd 100644 --- a/docs/book/naming-strategy/composite-naming-strategy.md +++ b/docs/book/naming-strategy/composite-naming-strategy.md @@ -1,83 +1,6 @@ -# CompositeNamingStrategy - -`Zend\Hydrator\NamingStrategy\CompositeNamingStrategy` allows you to specify which naming -strategy should be used for each key encountered during hydration or extraction. - -# Basic Usage - -When invoked, the following composite strategy will extract the property `bar` -to the array key `foo` (using the `MapNamingStrategy`), and the property -`barBat` to the array key `bar_bat` (using the `UnderscoreNamingStrategy`): - -```php -class Foo -{ - public $bar; - public $barBat; -} - -$mapStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy([ - 'foo' => 'bar' -]); - -$underscoreNamingStrategy = new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy(); - -$namingStrategy = new Zend\Hydrator\NamingStrategy\CompositeNamingStrategy([ - 'bar' => $mapStrategy, - 'barBat' => $underscoreNamingStrategy, -]); - -$hydrator = new Zend\Hydrator\ObjectProperty(); -$hydrator->setNamingStrategy($namingStrategy); - -$foo = new Foo(); -$foo->bar = 123; -$foo->barBat = 42; - -print_r($foo); // Foo Object ( [bar] => 123 [barBat] => 42 ) -print_r($hydrator->extract($foo)); // Array ( [foo] => 123 [bar_bat] => 42 ) -``` - -Unfortunately, the `CompositeNamingStrategy` can only be used for extraction as it will not know how -to handle the keys necessary for hydration (`foo` and `bar_bat`, respectively). To rectify this we -have to cover the keys for both hydration and extraction in our composite strategy: - -```php -class Foo -{ - public $bar; - public $barBat; -} - -$mapStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy([ - 'foo' => 'bar' -]); - -$underscoreNamingStrategy = new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy(); - -$namingStrategy = new Zend\Hydrator\NamingStrategy\CompositeNamingStrategy([ - // Define both directions for the foo => bar mapping - 'bar' => $mapStrategy, - 'foo' => $mapStrategy, - // Define both directions for the barBat => bar_bat mapping - 'barBat' => $underscoreNamingStrategy, - 'bar_bat' => $underscoreNamingStrategy, -]); - -$hydrator = new Zend\Hydrator\ObjectProperty(); -$hydrator->setNamingStrategy($namingStrategy); - -$foo = new Foo(); -$foo->bar = 123; -$foo->barBat = 42; - -$array = $hydrator->extract($foo); - -print_r($foo); // Foo Object ( [bar] => 123 [barBat] => 42 ) -print_r($array); // Array ( [foo] => 123 [bar_bat] => 42 ) - -$foo2 = new Foo(); -$hydrator->hydrate($array, $foo2); - -print_r($foo2); // Foo Object ( [bar] => 123 [barBat] => 42 ) -``` + + diff --git a/docs/book/naming-strategy/identity-naming-strategy.md b/docs/book/naming-strategy/identity-naming-strategy.md index 9ff4a41..d8fa62a 100644 --- a/docs/book/naming-strategy/identity-naming-strategy.md +++ b/docs/book/naming-strategy/identity-naming-strategy.md @@ -1,32 +1,6 @@ -# IdentityNamingStrategy - -`Zend\Hydrator\NamingStrategy\IdentityNamingStrategy` uses the keys provided to -it for hydration and extraction. - -## Basic Usage - -```php -$namingStrategy = new Zend\Hydrator\NamingStrategy\IdentityNamingStrategy(); - -echo $namingStrategy->hydrate('foo'); // outputs: foo -echo $namingStrategy->extract('bar'); // outputs: bar -``` - -This strategy can be used in hydrators as well: - -```php -class Foo -{ - public $foo; -} - -$namingStrategy = new Zend\Hydrator\NamingStrategy\IdentityNamingStrategy(); -$hydrator = new Zend\Hydrator\ObjectProperty(); -$hydrator->setNamingStrategy($namingStrategy); - -$foo = new Foo(); -$hydrator->hydrate(array('foo' => 123), $foo); - -print_r($foo); // Foo Object ( [foo] => 123 ) -print_r($hydrator->extract($foo)); // Array ( [foo] => 123 ) -``` + + diff --git a/docs/book/naming-strategy/map-naming-strategy.md b/docs/book/naming-strategy/map-naming-strategy.md index ac9568c..990d733 100644 --- a/docs/book/naming-strategy/map-naming-strategy.md +++ b/docs/book/naming-strategy/map-naming-strategy.md @@ -1,41 +1,6 @@ -# MapNamingStrategy - -`Zend\Hydrator\NamingStrategy\MapNamingStrategy` allows you to provide a map of -keys to use when hydrating and extracting; the map will translate the key based -on the direction. - -## Basic Usage - -```php -$namingStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy(array( - 'foo' => 'bar', - 'baz' => 'bash' -)); -echo $namingStrategy->hydrate('foo'); // outputs: bar -echo $namingStrategy->hydrate('baz'); // outputs: bash - -echo $namingStrategy->extract('bar'); // outputs: foo -echo $namingStrategy->extract('bash'); // outputs: baz -``` - -This strategy can be used in hydrators to dictate how keys should be mapped: - -```php -class Foo -{ - public $bar; -} - -$namingStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy([ - 'foo' => 'bar', - 'baz' => 'bash' -]); -$hydrator = new Zend\Hydrator\ObjectProperty(); -$hydrator->setNamingStrategy($namingStrategy); - -$foo = new Foo(); -$hydrator->hydrate(['foo' => 123], $foo); - -print_r($foo); // Foo Object ( [bar] => 123 ) -print_r($hydrator->extract($foo)); // Array ( [foo] => 123 ) -``` + + diff --git a/docs/book/naming-strategy/underscore-naming-strategy.md b/docs/book/naming-strategy/underscore-naming-strategy.md index 813449e..2ca9a3f 100644 --- a/docs/book/naming-strategy/underscore-naming-strategy.md +++ b/docs/book/naming-strategy/underscore-naming-strategy.md @@ -1,36 +1,6 @@ -# UnderscoreNamingStrategy - -`Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy` converts snake case strings (e.g. -`foo_bar_baz`) to camel-case strings (e.g. `fooBarBaz`) and vice versa. - -## Basic Usage - -```php -$namingStrategy = new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy(); - -echo $namingStrategy->extract('foo_bar'); // outputs: foo_bar -echo $namingStrategy->extract('Foo_Bar'); // outputs: foo_bar -echo $namingStrategy->extract('FooBar'); // outputs: foo_bar - -echo $namingStrategy->hydrate('fooBar'); // outputs: fooBar -echo $namingStrategy->hydrate('FooBar'); // outputs: fooBar -echo $namingStrategy->hydrate('Foo_Bar'); // outputs: fooBar -``` - -This strategy can be used in hydrators to dictate how keys should be mapped. - -```php -class Foo -{ - public $fooBar; -} - -$hydrator = new Zend\Hydrator\ObjectProperty(); -$hydrator->setNamingStrategy(new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy()); - -$foo = new Foo(); -$hydrator->hydrate(['foo_bar' => 123], $foo); - -print_r($foo); // Foo Object ( [fooBar] => 123 ) -print_r($hydrator->extract($foo)); // Array ( [foo_bar] => 123 ) -``` + + diff --git a/docs/book/quick-start.md b/docs/book/quick-start.md index e504e28..e1b7b69 100644 --- a/docs/book/quick-start.md +++ b/docs/book/quick-start.md @@ -1,121 +1,6 @@ -# zend-hydrator - -Hydration is the act of populating an object from a set of data. - -zend-hydrator is a simple component to provide mechanisms both for hydrating -objects, as well as extracting data sets from them. - -The component consists of interfaces, and several implementations for common use cases. - -## Base Interfaces - -### ExtractionInterface - -```php -namespace Zend\Hydrator; - -interface ExtractionInterface -{ - / - * Extract values from an object - * - * @param object $object - * @return array - */ - public function extract($object); -} -``` - -### HydrationInterface - -```php -namespace Zend\Hydrator; - -interface HydrationInterface -{ - / - * Hydrate $object with the provided $data. - * - * @param array $data - * @param object $object - * @return object - */ - public function hydrate(array $data, $object); -} -``` - -### HydratorInterface - -```php -namespace Zend\Hydrator; - -interface HydratorInterface extends - ExtractionInterface, - HydrationInterface -{ -} -``` - -## Usage - -Usage involves instantiating the hydrator, and then passing information to it. - -```php -use Zend\Hydrator; -$hydrator = new Hydrator\ArraySerializable(); - -// To hydrate an object from values in an array: -$object = $hydrator->hydrate($data, new ArrayObject()); - -// or, going the other way and extracting the values from an object as an array: -$data = $hydrator->extract($object); -``` - -## Available Implementations - -### Zend\\Hydrator\\ArraySerializable - -Follows the definition of `ArrayObject`. Objects must implement either the `exchangeArray()` or -`populate()` methods to support hydration, and the `getArrayCopy()` method to support extraction. - -### Zend\\Hydrator\\ClassMethods - -Any data key matching a setter method will be called in order to hydrate; any method matching a -getter method will be called for extraction. - -### Zend\\Hydrator\\DelegatingHydrator - -Composes a hydrator locator, and will delegate `hydrate()` and `extract()` calls -to the appropriate one based upon the class name of the object being operated -on. - -```php -// Instantiate each hydrator you wish to delegate to -$albumHydrator = new Zend\Hydrator\ClassMethods; -$artistHydrator = new Zend\Hydrator\ClassMethods; - -// Map the entity class name to the hydrator using the HydratorPluginManager. -// In this case we have two entity classes, "Album" and "Artist". -$hydrators = new Zend\Hydrator\HydratorPluginManager; -$hydrators->setService('Album', $albumHydrator); -$hydrators->setService('Artist', $artistHydrator); - -// Create the DelegatingHydrator and tell it to use our configured hydrator locator -$delegating = new Zend\Hydrator\DelegatingHydrator($hydrators); - -// Now we can use $delegating to hydrate or extract any supported object -$array = $delegating->extract(new Artist); -$artist = $delegating->hydrate($data, new Artist); -``` - -### Zend\\Hydrator\\ObjectProperty - -Any data key matching a publicly accessible property will be hydrated; any public properties -will be used for extraction. - -### Zend\\Hydrator\\Reflection - -Similar to the `ObjectProperty` hydrator, but uses [PHP's reflection API](http://php.net/manual/en/intro.reflection.php) -to hydrate or extract properties of any visibility. Any data key matching an -existing property will be hydrated; any existing properties will be used for -extraction. + + diff --git a/docs/book/strategy.md b/docs/book/strategy.md index adf89f8..4360657 100644 --- a/docs/book/strategy.md +++ b/docs/book/strategy.md @@ -1,241 +1,6 @@ -# Zend\\Hydrator\\Strategy - -You can add `Zend\Hydrator\Strategy\StrategyInterface` to any of the hydrators -(except if it extends `Zend\Hydrator\AbstractHydrator` or implements -`Zend\Hydrator\HydratorInterface` and `Zend\Hydrator\Strategy\StrategyEnabledInterface`) -to manipulate the way how they behave on `extract()` and `hydrate()` for -specific key / value pairs. This is the interface that needs to be implemented: - -```php -namespace Zend\Hydrator\Strategy; - -interface StrategyInterface -{ - /** - * Converts the given value so that it can be extracted by the hydrator. - * - * @param mixed $value The original value. - * @return mixed Returns the value that should be extracted. - */ - public function extract($value); - - /** - * Converts the given value so that it can be hydrated by the hydrator. - * - * @param mixed $value The original value. - * @return mixed Returns the value that should be hydrated. - */ - public function hydrate($value); -} -``` - -This interface is similar to `Zend\Hydrator\HydratorInterface`; the reason -is that strategies provide a proxy implementation for `hydrate()` and `extract()`. - -## Adding strategies to the hydrators - -To allow strategies within your hydrator, `Zend\Hydrator\Strategy\StrategyEnabledInterface` -provides the following methods: - -```php -namespace Zend\Hydrator; - -use Zend\Hydrator\Strategy\StrategyInterface; - -interface StrategyEnabledInterface -{ - /** - * Adds the given strategy under the given name. - * - * @param string $name The name of the strategy to register. - * @param StrategyInterface $strategy The strategy to register. - * @return HydratorInterface - */ - public function addStrategy($name, StrategyInterface $strategy); - - /** - * Gets the strategy with the given name. - * - * @param string $name The name of the strategy to get. - * @return StrategyInterface - */ - public function getStrategy($name); - - /** - * Checks if the strategy with the given name exists. - * - * @param string $name The name of the strategy to check for. - * @return bool - */ - public function hasStrategy($name); - - /** - * Removes the strategy with the given name. - * - * @param string $name The name of the strategy to remove. - * @return HydratorInterface - */ - public function removeStrategy($name); -} -``` - -Every hydrator shipped by default provides this functionality; -`AbstractHydrator` fully implements it as well. As such, if you want to use this -functionality in your own hydrators, you should extend `AbstractHydrator`. - -## Available implementations - -### Zend\\Hydrator\\Strategy\\BooleanStrategy - -This strategy converts values into Booleans and vice versa. It expects two -arguments at the constructor, which are used to define value maps for `true` and -`false`. - -### Zend\\Hydrator\\Strategy\\ClosureStrategy - -This is a strategy that allows you to pass in options for: - -- `hydrate`, a callback to be called when hydrating a value, and -- `extract`, a callback to be called when extracting a value. - -### Zend\\Hydrator\\Strategy\\DateTimeFormatterStrategy - -`DateTimeFormatterStrategy` provides bidirectional conversion between strings -and DateTime instances. The input and output formats can be provided as -constructor arguments. - -As of version 2.4.1, this strategy now allows `DateTime` formats that use `!` to -prepend the format, or `|` or `+` to append it; these ensure that, during -hydration, the new `DateTime` instance created will set the time element -accordingly. As a specific example, `Y-m-d|` will drop the time component, -ensuring comparisons are based on a midnight time value. - -Starting in version 2.5.0, the constructor defines a third, optional argument, -`$dateTimeFallback`. If enabled and hydration fails, the given string is parsed -by the `DateTime` constructor, as demonstrated below: - -```php -// Previous behavior: -$strategy = new Zend\Hydrator\Strategy\DateTimeFormatterStrategy('Y-m-d H:i:s.uP'); -$hydrated1 = $strategy->hydrate('2016-03-04 10:29:40.123456+01'); // Format is the same; returns DateTime instance -$hydrated2 = $strategy->hydrate('2016-03-04 10:29:40+01'); // Format is different; value is not hydrated - -// Using new $dateTimeFallback flag; both values are hydrated: -$strategy = new Zend\Hydrator\Strategy\DateTimeFormatterStrategy('Y-m-d H:i:s.uP', null, true); -$hydrated1 = $strategy->hydrate('2016-03-04 10:29:40.123456+01'); -$hydrated2 = $strategy->hydrate('2016-03-04 10:29:40+01'); -``` - -### Zend\\Hydrator\\Strategy\\DefaultStrategy - -The `DefaultStrategy` simply proxies everything through, without performing any -conversion of values. - -### Zend\\Hydrator\\Strategy\\ExplodeStrategy - -This strategy is a wrapper around PHP's `implode()` and `explode()` functions. -The delimiter and a limit can be provided to the constructor; the limit will -only be used for `extract` operations. - -### Zend\\Hydrator\\Strategy\\SerializableStrategy - -`SerializableStrategy` provides the functionality backing -`Zend\Hydrator\ArraySerializable`. You can use it with custom implementations -for `Zend\Serializer\Adapter\AdapterInterface` if you want to as well. - -### Zend\\Hydrator\\Strategy\\StrategyChain - -This strategy takes an array of `StrategyInterface` instances and iterates -over them when performing `extract()` and `hydrate()` operations. Each operates -on the return value of the previous, allowing complex operations based on -smaller, single-purpose strategies. - -## Writing custom strategies - -The following example, while not terribly useful, will provide you with the -basics for writing your own strategies, as well as provide ideas as to where and -when to use them. This strategy simply transforms the value for the defined key -using `str_rot13()` during both the `extract()` and `hydrate()` operations: - -```php -class Rot13Strategy implements StrategyInterface -{ - public function extract($value) - { - return str_rot13($value); - } - - public function hydrate($value) - { - return str_rot13($value); - } -} -``` - -This is the example class with which we want to use the hydrator example: - -```php -class Foo -{ - protected $foo = null; - protected $bar = null; - - public function getFoo() - { - return $this->foo; - } - - public function setFoo($foo) - { - $this->foo = $foo; - } - - public function getBar() - { - return $this->bar; - } - - public function setBar($bar) - { - $this->bar = $bar; - } -} -``` - -Now, we'll add the `rot13` strategy to the method `getFoo()` and `setFoo($foo)`: - -```php -$foo = new Foo(); -$foo->setFoo('bar'); -$foo->setBar('foo'); - -$hydrator = new ClassMethods(); -$hydrator->addStrategy('foo', new Rot13Strategy()); -``` - -When you use the hydrator to extract an array for the object `$foo`, you'll -receive the following: - -```php -$extractedArray = $hydrator->extract($foo); - -// array(2) { -// ["foo"]=> -// string(3) "one" -// ["bar"]=> -// string(3) "foo" -// } -``` - -And when hydrating a new `Foo` instance: - -```php -$hydrator->hydrate($extractedArray, $foo) - -// object(Foo)#2 (2) { -// ["foo":protected]=> -// string(3) "bar" -// ["bar":protected]=> -// string(3) "foo" -// } -``` + + diff --git a/docs/book/v2/aggregate.md b/docs/book/v2/aggregate.md new file mode 100644 index 0000000..711ad6f --- /dev/null +++ b/docs/book/v2/aggregate.md @@ -0,0 +1,145 @@ +# AggregateHydrator + +`Zend\Hydrator\Aggregate\AggregateHydrator` is an implementation of +`Zend\Hydrator\HydratorInterface` that composes multiple hydrators via event listeners. + +You typically want to use an aggregate hydrator when you want to hydrate or extract data from +complex objects that implement multiple interfaces, and therefore need multiple hydrators to handle +that in subsequent steps. + +## Installation requirements + +The `AggregateHydrator` depends on the zend-eventmanager component, so be sure to have it +installed before getting started: + +```bash +$ composer require zendframework/zend-eventmanager +``` + +## Basic usage + +A simple use case may be hydrating a `BlogPost` object, which contains data for the user that +created it, the time it was created, the current publishing status, etc: + +```php +use Zend\Hydrator\Aggregate\AggregateHydrator; + +$hydrator = new AggregateHydrator(); + +// attach the various hydrators capable of handling simpler interfaces +$hydrator->add(new My\BlogPostHydrator()); +$hydrator->add(new My\UserAwareObjectHydrator()); +$hydrator->add(new My\TimestampedObjectHydrator()); +$hydrator->add(new My\PublishableObjectHydrator()); +// ... + +// Now retrieve the BlogPost object +// ... + +// you can now extract complex data from a blogpost +$data = $hydrator->extract($blogPost); + +// or you can fill the object with complex data +$blogPost = $hydrator->hydrate($data, $blogPost); +``` + +> ### Hydrator priorities +> +> `AggregateHydrator::add` has a second optional argument, `$priority`. If you +> have two or more hydrators that conflict with each other for same data keys, +> you may decide which one to execute first or last by passing a higher or lower +> integer priority, respectively, to this argument. + +In order to work with this logic, each of the hydrators that are attached should +ignore any unknown object type passed in: + +```php +namespace My; + +use Zend\Hydrator\HydratorInterface + +class BlogPostHydrator implements HydratorInterface +{ + public function hydrate($data, $object) + { + if (! $object instanceof BlogPost) { + return $object; + } + + // ... continue hydration ... + } + + public function extract($object) + { + if (! $object instanceof BlogPost) { + return array(); + } + + // ... continue extraction ... + } +} +``` + +## Advanced use cases + +Since the `AggregateHydrator` is event-driven, you can use the `EventManager` +API to tweak its behaviour. + +Common use cases include: + +- Removal of hydrated data keys (passwords/confidential information) depending + on business rules. +- Caching of the hydration/extraction process. +- Transformations on extracted data, for compatibility with third-party APIs. + +In the following example, a cache listener is introduced to speed up hydration, which can be +very useful when the same data is requested multiple times: + +```php +use Zend\Hydrator\Aggregate\AggregateHydrator; +use Zend\Hydrator\Aggregate\ExtractEvent; +use Zend\Cache\Storage\Adapter\Memory; + +$hydrator = new AggregateHydrator(); + +// Attach the various hydrators: +$hydrator->add(new My\BlogPostHydrator()); +$hydrator->add(new My\UserAwareObjectHydrator()); +$hydrator->add(new My\TimestampedObjectHydrator()); +$hydrator->add(new My\PublishableObjectHydrator()); +// ... + +$cache = new Memory(); +$cacheReadListener = function (ExtractEvent $event) use ($cache) { + $object = $event->getExtractionObject(); + + if (! $object instanceof BlogPost) { + return; + } + + if ($cache->hasItem($object->getId())) { + $event->setExtractedData($cache->getItem($object->getId())); + $event->stopPropagation(); + } +}; + +$cacheWriteListener = function (ExtractEvent $event) use ($cache) { + $object = $event->getExtractionObject(); + + if (! $object instanceof BlogPost) { + return; + } + + $cache->setItem($object->getId(), $event->getExtractedData()); +}; + +// Attaching a high priority listener executed before extraction logic: +$eventManager = $hydrator->getEventManager(); +$eventManager()->attach(ExtractEvent::EVENT_EXTRACT, $cacheReadListener, 1000); + +// Attaching a low priority listener executed after extraction logic: +$eventManager()->attach(ExtractEvent::EVENT_EXTRACT, $cacheWriteListener, -1000); +``` + +With an aggregate hydrator configured in this way, any +`$hydrator->extract($blogPost)` operation will be cached. diff --git a/docs/book/v2/filter.md b/docs/book/v2/filter.md new file mode 100644 index 0000000..679c683 --- /dev/null +++ b/docs/book/v2/filter.md @@ -0,0 +1,304 @@ +# Zend\\Hydrator\\Filter + +Hydrator filters allow you to manipulate the behavior of the `extract()` +operation. This is especially useful, if you want to omit some internals (e.g. +`getServiceManager()`) from the array representation. + +It comes with a helpful `Composite` implementation, and several filters for +common use cases. The filters are composed in the `AbstractHydrator`, so you can +start using them immediately in any custom extensions you write that extend that +class. + +```php +namespace Zend\Hydrator\Filter; + +interface FilterInterface +{ + /** + * Should return true, if the given filter + * does not match + * + * @param string $property The name of the property + * @return bool + */ + public function filter($property); +} +``` + +If it returns true, the key/value pairs will be in the extracted arrays - if it +returns false, you'll not see them again. + +## Filter implementations + +### Zend\\Hydrator\\Filter\\GetFilter + +This filter is used in the `ClassMethods` hydrator to decide which getters will +be extracted. It checks if the key to extract starts with `get` or the object +contains a method beginning with `get` (e.g., `Zend\Foo\Bar::getFoo`). + +### Zend\\Hydrator\\Filter\\HasFilter + +This filter is used in the `ClassMethods` hydrator to decide which `has` methods +will be extracted. It checks if the key to extract begins with `has` or the +object contains a method beginning with `has` (e.g., `Zend\Foo\Bar::hasFoo`). + +### Zend\\Hydrator\\Filter\\IsFilter + +This filter is used in the `ClassMethods` hydrator to decide which `is` methods +will be extracted. It checks if the key to extract begins with `is` or the +object contains a method beginning with `is` (e.g., `Zend\Foo\Bar::isFoo`). + +### Zend\\Hydrator\\Filter\\MethodMatchFilter + +This filter allows you to omit methods during extraction that match the +condition defined in the composite. The name of the method is specified in the +constructor of this filter; the second parameter decides whether to use white or +blacklisting to decide (whitelisting retains only the matching method, blacklist +omits any matching method). The default is blacklisting - pass `false` to change +the behavior. + +### Zend\\Hydrator\\Filter\\NumberOfParameterFilter + +This filter is used in the `ClassMethods` hydrator to check the number of +parameters. By convention, the `get`, `has` and `is` methods do not get any +parameters - but it may happen. You can add your own number of required +parameters, simply add the number to the constructor. The default value is 0. If +the method has more or fewer parameters than what the filter accepts, it will be +omitted. + +## Remove filters + +If you want to tell e.g. the `ClassMethods` hydrator, to not extract methods that start with `is`, +remove the related filter: + +```php +$hydrator = new ClassMethods(false); +$hydrator->removeFilter('is'); +``` + +After performing the above, the key/value pairs for `is` methods will not end up +in your extracted array anymore. The filters can be used in any hydrator, but +the `ClassMethods` hydrator is the only one, that has pre-registered filters: + +```php +$this->filterComposite->addFilter('is', new IsFilter()); +$this->filterComposite->addFilter('has', new HasFilter()); +$this->filterComposite->addFilter('get', new GetFilter()); +$this->filterComposite->addFilter( + 'parameter', + new NumberOfParameterFilter(), + FilterComposite::CONDITION_AND +); +``` + +If these are not appropriate for your object, you can unregister them as shown +in the previous example. + +## Add filters + +You can add filters to any hydrator that extends the `AbstractHydrator`. Filters +can either implement `FilterInterface`, or simply be PHP callables: + +```php +$hydrator->addFilter('len', function($property) { + if (strlen($property) !== 3) { + return false; + } + return true; +}); +``` + +By default, every filter you add will be added with a conditional `or`. If you +want to add it with `and` (as the `NumberOfParameterFilter` that is added to the +`ClassMethods` hydrator by default), provide the conditon as the third argument +to `addFilter`: + +```php +$hydrator->addFilter('len', function($property) { + if (strlen($property) !== 3) { + return false; + } + return true; +}, FilterComposite::CONDITION_AND); +``` + +One common use case for filters is to omit getters for values that you do not +want to represent, such as a service manager instance: + +```php +$hydrator->addFilter( + 'servicemanager', + new MethodMatchFilter('getServiceManager'), + FilterComposite::CONDITION_AND +); +``` + +The example above will exclude the `getServiceManager()` method and the +`servicemanager` key from extraction, even if the `get` filter wants to add it. + +## Use FilterComposite for complex filters + +`FilterComposite` implements `FilterInterface` as well, so you can add it as +a regular filter to the hydrator. One benefit of this implementation is that you +can add the filters with a condition and accomplish complex requirements using +different composites with different conditions. You can pass the following +conditions to the 3rd parameter, when you add a filter: + +### Zend\\Hydrator\\Filter\\FilterComposite::CONDITION\_OR + +At the given level of the composite, at least one filter in that condition block +has to return true to extract the value. + +### Zend\\Hydrator\\Filter\\FilterComposite::CONDITION\_AND + +At the given level of the composite, all filters in that condition block must +return true to extract the value. + +### FilterComposite Examples + +This composition will have a similar logic as the if below: + +```php +$composite = new FilterComposite(); + +$composite->addFilter('one', $condition1); +$composite->addFilter('two', $condition2); +$composite->addFilter('three', $condition3); +$composite->addFilter('four', $condition4, FilterComposite::CONDITION_AND); +$composite->addFilter('five', $condition5, FilterComposite::CONDITION_AND); + +// This is what's happening internally +if ( + ($condition1 + || $condition2 + || $condition3 + ) && ($condition4 + && $condition5 + ) +) { + //do extraction +} +``` + +If you only have one condition (e.g., only an `and` or `or`) block, the other +one will be completely ignored. + +A bit more complex filter can look like this: + +```php +$composite = new FilterComposite(); +$composite->addFilter( + 'servicemanager', + new MethodMatchFilter('getServiceManager'), + FilterComposite::CONDITION_AND +); +$composite->addFilter( + 'eventmanager', + new MethodMatchFilter('getEventManager'), + FilterComposite::CONDITION_AND +); + +$hydrator->addFilter('excludes', $composite, FilterComposite::CONDITION_AND); + +// Internal +if (( // default composite inside the hydrator + ($getFilter + || $hasFilter + || $isFilter + ) && ( + $numberOfParameterFilter + ) + ) && ( // new composite, added to the one above + $serviceManagerFilter + && $eventManagerFilter + ) +) { + // do extraction +} +``` + +If you perform this on the `ClassMethods` hydrator, all getters will get +extracted, except for `getServiceManager()` and `getEventManager()`. + +## Using the provider interface + +`FilterProviderInterface` allows you to configure the behavior of the hydrator +inside your objects. + +```php +namespace Zend\Hydrator\Filter; + +interface FilterProviderInterface +{ + /** + * Provides a filter for hydration + * + * @return FilterInterface + */ + public function getFilter(); +} +``` + +(The `getFilter()` method is automatically excluded from `extract()`.) If the +extracted object implements the `Zend\Hydrator\Filter\FilterProviderInterface`, +the returned `FilterInterface` instance can also be a `FilterComposite`. + +For example: + +```php +Class Foo implements FilterProviderInterface +{ + public function getFoo() + { + return 'foo'; + } + + public function hasFoo() + { + return true; + } + + public function getServiceManager() + { + return 'servicemanager'; + } + + public function getEventManager() + { + return 'eventmanager'; + } + + public function getFilter() + { + $composite = new FilterComposite(); + $composite->addFilter('get', new GetFilter()); + + $exclusionComposite = new FilterComposite(); + $exclusionComposite->addFilter( + 'servicemanager', + new MethodMatchFilter('getServiceManager'), + FilterComposite::CONDITION_AND + ); + $exclusionComposite->addFilter( + 'eventmanager', + new MethodMatchFilter('getEventManager'), + FilterComposite::CONDITION_AND + ); + + $composite->addFilter('excludes', $exclusionComposite, FilterComposite::CONDITION_AND); + + return $composite; + } +} + +$hydrator = new ClassMethods(false); +$extractedArray = $hydrator->extract(new Foo()); +``` + +`$extractedArray` will only have 'foo' => 'foo'; all other values are +excluded from extraction. + +> ### Note +> +> All pre-registered filters from the `ClassMethods` hydrator are ignored when +> this interface is used. diff --git a/docs/book/v2/naming-strategy/composite-naming-strategy.md b/docs/book/v2/naming-strategy/composite-naming-strategy.md new file mode 100644 index 0000000..a62f0ab --- /dev/null +++ b/docs/book/v2/naming-strategy/composite-naming-strategy.md @@ -0,0 +1,83 @@ +# CompositeNamingStrategy + +`Zend\Hydrator\NamingStrategy\CompositeNamingStrategy` allows you to specify which naming +strategy should be used for each key encountered during hydration or extraction. + +# Basic Usage + +When invoked, the following composite strategy will extract the property `bar` +to the array key `foo` (using the `MapNamingStrategy`), and the property +`barBat` to the array key `bar_bat` (using the `UnderscoreNamingStrategy`): + +```php +class Foo +{ + public $bar; + public $barBat; +} + +$mapStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy([ + 'foo' => 'bar' +]); + +$underscoreNamingStrategy = new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy(); + +$namingStrategy = new Zend\Hydrator\NamingStrategy\CompositeNamingStrategy([ + 'bar' => $mapStrategy, + 'barBat' => $underscoreNamingStrategy, +]); + +$hydrator = new Zend\Hydrator\ObjectProperty(); +$hydrator->setNamingStrategy($namingStrategy); + +$foo = new Foo(); +$foo->bar = 123; +$foo->barBat = 42; + +print_r($foo); // Foo Object ( [bar] => 123 [barBat] => 42 ) +print_r($hydrator->extract($foo)); // Array ( [foo] => 123 [bar_bat] => 42 ) +``` + +Unfortunately, the `CompositeNamingStrategy` can only be used for extraction as it will not know how +to handle the keys necessary for hydration (`foo` and `bar_bat`, respectively). To rectify this we +have to cover the keys for both hydration and extraction in our composite strategy: + +```php +class Foo +{ + public $bar; + public $barBat; +} + +$mapStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy([ + 'foo' => 'bar' +]); + +$underscoreNamingStrategy = new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy(); + +$namingStrategy = new Zend\Hydrator\NamingStrategy\CompositeNamingStrategy([ + // Define both directions for the foo => bar mapping + 'bar' => $mapStrategy, + 'foo' => $mapStrategy, + // Define both directions for the barBat => bar_bat mapping + 'barBat' => $underscoreNamingStrategy, + 'bar_bat' => $underscoreNamingStrategy, +]); + +$hydrator = new Zend\Hydrator\ObjectProperty(); +$hydrator->setNamingStrategy($namingStrategy); + +$foo = new Foo(); +$foo->bar = 123; +$foo->barBat = 42; + +$array = $hydrator->extract($foo); + +print_r($foo); // Foo Object ( [bar] => 123 [barBat] => 42 ) +print_r($array); // Array ( [foo] => 123 [bar_bat] => 42 ) + +$foo2 = new Foo(); +$hydrator->hydrate($array, $foo2); + +print_r($foo2); // Foo Object ( [bar] => 123 [barBat] => 42 ) +``` diff --git a/docs/book/v2/naming-strategy/identity-naming-strategy.md b/docs/book/v2/naming-strategy/identity-naming-strategy.md new file mode 100644 index 0000000..9ff4a41 --- /dev/null +++ b/docs/book/v2/naming-strategy/identity-naming-strategy.md @@ -0,0 +1,32 @@ +# IdentityNamingStrategy + +`Zend\Hydrator\NamingStrategy\IdentityNamingStrategy` uses the keys provided to +it for hydration and extraction. + +## Basic Usage + +```php +$namingStrategy = new Zend\Hydrator\NamingStrategy\IdentityNamingStrategy(); + +echo $namingStrategy->hydrate('foo'); // outputs: foo +echo $namingStrategy->extract('bar'); // outputs: bar +``` + +This strategy can be used in hydrators as well: + +```php +class Foo +{ + public $foo; +} + +$namingStrategy = new Zend\Hydrator\NamingStrategy\IdentityNamingStrategy(); +$hydrator = new Zend\Hydrator\ObjectProperty(); +$hydrator->setNamingStrategy($namingStrategy); + +$foo = new Foo(); +$hydrator->hydrate(array('foo' => 123), $foo); + +print_r($foo); // Foo Object ( [foo] => 123 ) +print_r($hydrator->extract($foo)); // Array ( [foo] => 123 ) +``` diff --git a/docs/book/v2/naming-strategy/map-naming-strategy.md b/docs/book/v2/naming-strategy/map-naming-strategy.md new file mode 100644 index 0000000..ac9568c --- /dev/null +++ b/docs/book/v2/naming-strategy/map-naming-strategy.md @@ -0,0 +1,41 @@ +# MapNamingStrategy + +`Zend\Hydrator\NamingStrategy\MapNamingStrategy` allows you to provide a map of +keys to use when hydrating and extracting; the map will translate the key based +on the direction. + +## Basic Usage + +```php +$namingStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy(array( + 'foo' => 'bar', + 'baz' => 'bash' +)); +echo $namingStrategy->hydrate('foo'); // outputs: bar +echo $namingStrategy->hydrate('baz'); // outputs: bash + +echo $namingStrategy->extract('bar'); // outputs: foo +echo $namingStrategy->extract('bash'); // outputs: baz +``` + +This strategy can be used in hydrators to dictate how keys should be mapped: + +```php +class Foo +{ + public $bar; +} + +$namingStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy([ + 'foo' => 'bar', + 'baz' => 'bash' +]); +$hydrator = new Zend\Hydrator\ObjectProperty(); +$hydrator->setNamingStrategy($namingStrategy); + +$foo = new Foo(); +$hydrator->hydrate(['foo' => 123], $foo); + +print_r($foo); // Foo Object ( [bar] => 123 ) +print_r($hydrator->extract($foo)); // Array ( [foo] => 123 ) +``` diff --git a/docs/book/v2/naming-strategy/underscore-naming-strategy.md b/docs/book/v2/naming-strategy/underscore-naming-strategy.md new file mode 100644 index 0000000..813449e --- /dev/null +++ b/docs/book/v2/naming-strategy/underscore-naming-strategy.md @@ -0,0 +1,36 @@ +# UnderscoreNamingStrategy + +`Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy` converts snake case strings (e.g. +`foo_bar_baz`) to camel-case strings (e.g. `fooBarBaz`) and vice versa. + +## Basic Usage + +```php +$namingStrategy = new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy(); + +echo $namingStrategy->extract('foo_bar'); // outputs: foo_bar +echo $namingStrategy->extract('Foo_Bar'); // outputs: foo_bar +echo $namingStrategy->extract('FooBar'); // outputs: foo_bar + +echo $namingStrategy->hydrate('fooBar'); // outputs: fooBar +echo $namingStrategy->hydrate('FooBar'); // outputs: fooBar +echo $namingStrategy->hydrate('Foo_Bar'); // outputs: fooBar +``` + +This strategy can be used in hydrators to dictate how keys should be mapped. + +```php +class Foo +{ + public $fooBar; +} + +$hydrator = new Zend\Hydrator\ObjectProperty(); +$hydrator->setNamingStrategy(new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy()); + +$foo = new Foo(); +$hydrator->hydrate(['foo_bar' => 123], $foo); + +print_r($foo); // Foo Object ( [fooBar] => 123 ) +print_r($hydrator->extract($foo)); // Array ( [foo_bar] => 123 ) +``` diff --git a/docs/book/v2/quick-start.md b/docs/book/v2/quick-start.md new file mode 100644 index 0000000..e504e28 --- /dev/null +++ b/docs/book/v2/quick-start.md @@ -0,0 +1,121 @@ +# zend-hydrator + +Hydration is the act of populating an object from a set of data. + +zend-hydrator is a simple component to provide mechanisms both for hydrating +objects, as well as extracting data sets from them. + +The component consists of interfaces, and several implementations for common use cases. + +## Base Interfaces + +### ExtractionInterface + +```php +namespace Zend\Hydrator; + +interface ExtractionInterface +{ + / + * Extract values from an object + * + * @param object $object + * @return array + */ + public function extract($object); +} +``` + +### HydrationInterface + +```php +namespace Zend\Hydrator; + +interface HydrationInterface +{ + / + * Hydrate $object with the provided $data. + * + * @param array $data + * @param object $object + * @return object + */ + public function hydrate(array $data, $object); +} +``` + +### HydratorInterface + +```php +namespace Zend\Hydrator; + +interface HydratorInterface extends + ExtractionInterface, + HydrationInterface +{ +} +``` + +## Usage + +Usage involves instantiating the hydrator, and then passing information to it. + +```php +use Zend\Hydrator; +$hydrator = new Hydrator\ArraySerializable(); + +// To hydrate an object from values in an array: +$object = $hydrator->hydrate($data, new ArrayObject()); + +// or, going the other way and extracting the values from an object as an array: +$data = $hydrator->extract($object); +``` + +## Available Implementations + +### Zend\\Hydrator\\ArraySerializable + +Follows the definition of `ArrayObject`. Objects must implement either the `exchangeArray()` or +`populate()` methods to support hydration, and the `getArrayCopy()` method to support extraction. + +### Zend\\Hydrator\\ClassMethods + +Any data key matching a setter method will be called in order to hydrate; any method matching a +getter method will be called for extraction. + +### Zend\\Hydrator\\DelegatingHydrator + +Composes a hydrator locator, and will delegate `hydrate()` and `extract()` calls +to the appropriate one based upon the class name of the object being operated +on. + +```php +// Instantiate each hydrator you wish to delegate to +$albumHydrator = new Zend\Hydrator\ClassMethods; +$artistHydrator = new Zend\Hydrator\ClassMethods; + +// Map the entity class name to the hydrator using the HydratorPluginManager. +// In this case we have two entity classes, "Album" and "Artist". +$hydrators = new Zend\Hydrator\HydratorPluginManager; +$hydrators->setService('Album', $albumHydrator); +$hydrators->setService('Artist', $artistHydrator); + +// Create the DelegatingHydrator and tell it to use our configured hydrator locator +$delegating = new Zend\Hydrator\DelegatingHydrator($hydrators); + +// Now we can use $delegating to hydrate or extract any supported object +$array = $delegating->extract(new Artist); +$artist = $delegating->hydrate($data, new Artist); +``` + +### Zend\\Hydrator\\ObjectProperty + +Any data key matching a publicly accessible property will be hydrated; any public properties +will be used for extraction. + +### Zend\\Hydrator\\Reflection + +Similar to the `ObjectProperty` hydrator, but uses [PHP's reflection API](http://php.net/manual/en/intro.reflection.php) +to hydrate or extract properties of any visibility. Any data key matching an +existing property will be hydrated; any existing properties will be used for +extraction. diff --git a/docs/book/v2/strategy.md b/docs/book/v2/strategy.md new file mode 100644 index 0000000..6e06b8f --- /dev/null +++ b/docs/book/v2/strategy.md @@ -0,0 +1,225 @@ +# Zend\\Hydrator\\Strategy + +You can add `Zend\Hydrator\Strategy\StrategyInterface` to any of the hydrators +(except if it extends `Zend\Hydrator\AbstractHydrator` or implements +`Zend\Hydrator\HydratorInterface` and `Zend\Hydrator\Strategy\StrategyEnabledInterface`) +to manipulate the way how they behave on `extract()` and `hydrate()` for +specific key / value pairs. This is the interface that needs to be implemented: + +```php +namespace Zend\Hydrator\Strategy; + +interface StrategyInterface +{ + /** + * Converts the given value so that it can be extracted by the hydrator. + * + * @param mixed $value The original value. + * @return mixed Returns the value that should be extracted. + */ + public function extract($value); + + /** + * Converts the given value so that it can be hydrated by the hydrator. + * + * @param mixed $value The original value. + * @return mixed Returns the value that should be hydrated. + */ + public function hydrate($value); +} +``` + +This interface is similar to `Zend\Hydrator\HydratorInterface`; the reason +is that strategies provide a proxy implementation for `hydrate()` and `extract()`. + +## Adding strategies to the hydrators + +To allow strategies within your hydrator, `Zend\Hydrator\Strategy\StrategyEnabledInterface` +provides the following methods: + +```php +namespace Zend\Hydrator; + +use Zend\Hydrator\Strategy\StrategyInterface; + +interface StrategyEnabledInterface +{ + /** + * Adds the given strategy under the given name. + * + * @param string $name The name of the strategy to register. + * @param StrategyInterface $strategy The strategy to register. + * @return HydratorInterface + */ + public function addStrategy($name, StrategyInterface $strategy); + + /** + * Gets the strategy with the given name. + * + * @param string $name The name of the strategy to get. + * @return StrategyInterface + */ + public function getStrategy($name); + + /** + * Checks if the strategy with the given name exists. + * + * @param string $name The name of the strategy to check for. + * @return bool + */ + public function hasStrategy($name); + + /** + * Removes the strategy with the given name. + * + * @param string $name The name of the strategy to remove. + * @return HydratorInterface + */ + public function removeStrategy($name); +} +``` + +Every hydrator shipped by default provides this functionality; +`AbstractHydrator` fully implements it as well. As such, if you want to use this +functionality in your own hydrators, you should extend `AbstractHydrator`. + +## Available implementations + +### Zend\\Hydrator\\Strategy\\BooleanStrategy + +This strategy converts values into Booleans and vice versa. It expects two +arguments at the constructor, which are used to define value maps for `true` and +`false`. + +### Zend\\Hydrator\\Strategy\\ClosureStrategy + +This is a strategy that allows you to pass in options for: + +- `hydrate`, a callback to be called when hydrating a value, and +- `extract`, a callback to be called when extracting a value. + +### Zend\\Hydrator\\Strategy\\DateTimeFormatterStrategy + +`DateTimeFormatterStrategy` provides bidirectional conversion between strings +and DateTime instances. The input and output formats can be provided as +constructor arguments. + +As of version 2.4.1, this strategy now allows `DateTime` formats that use `!` to +prepend the format, or `|` or `+` to append it; these ensure that, during +hydration, the new `DateTime` instance created will set the time element +accordingly. As a specific example, `Y-m-d|` will drop the time component, +ensuring comparisons are based on a midnight time value. + +### Zend\\Hydrator\\Strategy\\DefaultStrategy + +The `DefaultStrategy` simply proxies everything through, without performing any +conversion of values. + +### Zend\\Hydrator\\Strategy\\ExplodeStrategy + +This strategy is a wrapper around PHP's `implode()` and `explode()` functions. +The delimiter and a limit can be provided to the constructor; the limit will +only be used for `extract` operations. + +### Zend\\Hydrator\\Strategy\\SerializableStrategy + +`SerializableStrategy` provides the functionality backing +`Zend\Hydrator\ArraySerializable`. You can use it with custom implementations +for `Zend\Serializer\Adapter\AdapterInterface` if you want to as well. + +### Zend\\Hydrator\\Strategy\\StrategyChain + +This strategy takes an array of `StrategyInterface` instances and iterates +over them when performing `extract()` and `hydrate()` operations. Each operates +on the return value of the previous, allowing complex operations based on +smaller, single-purpose strategies. + +## Writing custom strategies + +The following example, while not terribly useful, will provide you with the +basics for writing your own strategies, as well as provide ideas as to where and +when to use them. This strategy simply transforms the value for the defined key +using `str_rot13()` during both the `extract()` and `hydrate()` operations: + +```php +class Rot13Strategy implements StrategyInterface +{ + public function extract($value) + { + return str_rot13($value); + } + + public function hydrate($value) + { + return str_rot13($value); + } +} +``` + +This is the example class with which we want to use the hydrator example: + +```php +class Foo +{ + protected $foo = null; + protected $bar = null; + + public function getFoo() + { + return $this->foo; + } + + public function setFoo($foo) + { + $this->foo = $foo; + } + + public function getBar() + { + return $this->bar; + } + + public function setBar($bar) + { + $this->bar = $bar; + } +} +``` + +Now, we'll add the `rot13` strategy to the method `getFoo()` and `setFoo($foo)`: + +```php +$foo = new Foo(); +$foo->setFoo('bar'); +$foo->setBar('foo'); + +$hydrator = new ClassMethods(); +$hydrator->addStrategy('foo', new Rot13Strategy()); +``` + +When you use the hydrator to extract an array for the object `$foo`, you'll +receive the following: + +```php +$extractedArray = $hydrator->extract($foo); + +// array(2) { +// ["foo"]=> +// string(3) "one" +// ["bar"]=> +// string(3) "foo" +// } +``` + +And when hydrating a new `Foo` instance: + +```php +$hydrator->hydrate($extractedArray, $foo) + +// object(Foo)#2 (2) { +// ["foo":protected]=> +// string(3) "bar" +// ["bar":protected]=> +// string(3) "foo" +// } +``` diff --git a/docs/book/v3/aggregate.md b/docs/book/v3/aggregate.md new file mode 100644 index 0000000..711ad6f --- /dev/null +++ b/docs/book/v3/aggregate.md @@ -0,0 +1,145 @@ +# AggregateHydrator + +`Zend\Hydrator\Aggregate\AggregateHydrator` is an implementation of +`Zend\Hydrator\HydratorInterface` that composes multiple hydrators via event listeners. + +You typically want to use an aggregate hydrator when you want to hydrate or extract data from +complex objects that implement multiple interfaces, and therefore need multiple hydrators to handle +that in subsequent steps. + +## Installation requirements + +The `AggregateHydrator` depends on the zend-eventmanager component, so be sure to have it +installed before getting started: + +```bash +$ composer require zendframework/zend-eventmanager +``` + +## Basic usage + +A simple use case may be hydrating a `BlogPost` object, which contains data for the user that +created it, the time it was created, the current publishing status, etc: + +```php +use Zend\Hydrator\Aggregate\AggregateHydrator; + +$hydrator = new AggregateHydrator(); + +// attach the various hydrators capable of handling simpler interfaces +$hydrator->add(new My\BlogPostHydrator()); +$hydrator->add(new My\UserAwareObjectHydrator()); +$hydrator->add(new My\TimestampedObjectHydrator()); +$hydrator->add(new My\PublishableObjectHydrator()); +// ... + +// Now retrieve the BlogPost object +// ... + +// you can now extract complex data from a blogpost +$data = $hydrator->extract($blogPost); + +// or you can fill the object with complex data +$blogPost = $hydrator->hydrate($data, $blogPost); +``` + +> ### Hydrator priorities +> +> `AggregateHydrator::add` has a second optional argument, `$priority`. If you +> have two or more hydrators that conflict with each other for same data keys, +> you may decide which one to execute first or last by passing a higher or lower +> integer priority, respectively, to this argument. + +In order to work with this logic, each of the hydrators that are attached should +ignore any unknown object type passed in: + +```php +namespace My; + +use Zend\Hydrator\HydratorInterface + +class BlogPostHydrator implements HydratorInterface +{ + public function hydrate($data, $object) + { + if (! $object instanceof BlogPost) { + return $object; + } + + // ... continue hydration ... + } + + public function extract($object) + { + if (! $object instanceof BlogPost) { + return array(); + } + + // ... continue extraction ... + } +} +``` + +## Advanced use cases + +Since the `AggregateHydrator` is event-driven, you can use the `EventManager` +API to tweak its behaviour. + +Common use cases include: + +- Removal of hydrated data keys (passwords/confidential information) depending + on business rules. +- Caching of the hydration/extraction process. +- Transformations on extracted data, for compatibility with third-party APIs. + +In the following example, a cache listener is introduced to speed up hydration, which can be +very useful when the same data is requested multiple times: + +```php +use Zend\Hydrator\Aggregate\AggregateHydrator; +use Zend\Hydrator\Aggregate\ExtractEvent; +use Zend\Cache\Storage\Adapter\Memory; + +$hydrator = new AggregateHydrator(); + +// Attach the various hydrators: +$hydrator->add(new My\BlogPostHydrator()); +$hydrator->add(new My\UserAwareObjectHydrator()); +$hydrator->add(new My\TimestampedObjectHydrator()); +$hydrator->add(new My\PublishableObjectHydrator()); +// ... + +$cache = new Memory(); +$cacheReadListener = function (ExtractEvent $event) use ($cache) { + $object = $event->getExtractionObject(); + + if (! $object instanceof BlogPost) { + return; + } + + if ($cache->hasItem($object->getId())) { + $event->setExtractedData($cache->getItem($object->getId())); + $event->stopPropagation(); + } +}; + +$cacheWriteListener = function (ExtractEvent $event) use ($cache) { + $object = $event->getExtractionObject(); + + if (! $object instanceof BlogPost) { + return; + } + + $cache->setItem($object->getId(), $event->getExtractedData()); +}; + +// Attaching a high priority listener executed before extraction logic: +$eventManager = $hydrator->getEventManager(); +$eventManager()->attach(ExtractEvent::EVENT_EXTRACT, $cacheReadListener, 1000); + +// Attaching a low priority listener executed after extraction logic: +$eventManager()->attach(ExtractEvent::EVENT_EXTRACT, $cacheWriteListener, -1000); +``` + +With an aggregate hydrator configured in this way, any +`$hydrator->extract($blogPost)` operation will be cached. diff --git a/docs/book/v3/filter.md b/docs/book/v3/filter.md new file mode 100644 index 0000000..679c683 --- /dev/null +++ b/docs/book/v3/filter.md @@ -0,0 +1,304 @@ +# Zend\\Hydrator\\Filter + +Hydrator filters allow you to manipulate the behavior of the `extract()` +operation. This is especially useful, if you want to omit some internals (e.g. +`getServiceManager()`) from the array representation. + +It comes with a helpful `Composite` implementation, and several filters for +common use cases. The filters are composed in the `AbstractHydrator`, so you can +start using them immediately in any custom extensions you write that extend that +class. + +```php +namespace Zend\Hydrator\Filter; + +interface FilterInterface +{ + /** + * Should return true, if the given filter + * does not match + * + * @param string $property The name of the property + * @return bool + */ + public function filter($property); +} +``` + +If it returns true, the key/value pairs will be in the extracted arrays - if it +returns false, you'll not see them again. + +## Filter implementations + +### Zend\\Hydrator\\Filter\\GetFilter + +This filter is used in the `ClassMethods` hydrator to decide which getters will +be extracted. It checks if the key to extract starts with `get` or the object +contains a method beginning with `get` (e.g., `Zend\Foo\Bar::getFoo`). + +### Zend\\Hydrator\\Filter\\HasFilter + +This filter is used in the `ClassMethods` hydrator to decide which `has` methods +will be extracted. It checks if the key to extract begins with `has` or the +object contains a method beginning with `has` (e.g., `Zend\Foo\Bar::hasFoo`). + +### Zend\\Hydrator\\Filter\\IsFilter + +This filter is used in the `ClassMethods` hydrator to decide which `is` methods +will be extracted. It checks if the key to extract begins with `is` or the +object contains a method beginning with `is` (e.g., `Zend\Foo\Bar::isFoo`). + +### Zend\\Hydrator\\Filter\\MethodMatchFilter + +This filter allows you to omit methods during extraction that match the +condition defined in the composite. The name of the method is specified in the +constructor of this filter; the second parameter decides whether to use white or +blacklisting to decide (whitelisting retains only the matching method, blacklist +omits any matching method). The default is blacklisting - pass `false` to change +the behavior. + +### Zend\\Hydrator\\Filter\\NumberOfParameterFilter + +This filter is used in the `ClassMethods` hydrator to check the number of +parameters. By convention, the `get`, `has` and `is` methods do not get any +parameters - but it may happen. You can add your own number of required +parameters, simply add the number to the constructor. The default value is 0. If +the method has more or fewer parameters than what the filter accepts, it will be +omitted. + +## Remove filters + +If you want to tell e.g. the `ClassMethods` hydrator, to not extract methods that start with `is`, +remove the related filter: + +```php +$hydrator = new ClassMethods(false); +$hydrator->removeFilter('is'); +``` + +After performing the above, the key/value pairs for `is` methods will not end up +in your extracted array anymore. The filters can be used in any hydrator, but +the `ClassMethods` hydrator is the only one, that has pre-registered filters: + +```php +$this->filterComposite->addFilter('is', new IsFilter()); +$this->filterComposite->addFilter('has', new HasFilter()); +$this->filterComposite->addFilter('get', new GetFilter()); +$this->filterComposite->addFilter( + 'parameter', + new NumberOfParameterFilter(), + FilterComposite::CONDITION_AND +); +``` + +If these are not appropriate for your object, you can unregister them as shown +in the previous example. + +## Add filters + +You can add filters to any hydrator that extends the `AbstractHydrator`. Filters +can either implement `FilterInterface`, or simply be PHP callables: + +```php +$hydrator->addFilter('len', function($property) { + if (strlen($property) !== 3) { + return false; + } + return true; +}); +``` + +By default, every filter you add will be added with a conditional `or`. If you +want to add it with `and` (as the `NumberOfParameterFilter` that is added to the +`ClassMethods` hydrator by default), provide the conditon as the third argument +to `addFilter`: + +```php +$hydrator->addFilter('len', function($property) { + if (strlen($property) !== 3) { + return false; + } + return true; +}, FilterComposite::CONDITION_AND); +``` + +One common use case for filters is to omit getters for values that you do not +want to represent, such as a service manager instance: + +```php +$hydrator->addFilter( + 'servicemanager', + new MethodMatchFilter('getServiceManager'), + FilterComposite::CONDITION_AND +); +``` + +The example above will exclude the `getServiceManager()` method and the +`servicemanager` key from extraction, even if the `get` filter wants to add it. + +## Use FilterComposite for complex filters + +`FilterComposite` implements `FilterInterface` as well, so you can add it as +a regular filter to the hydrator. One benefit of this implementation is that you +can add the filters with a condition and accomplish complex requirements using +different composites with different conditions. You can pass the following +conditions to the 3rd parameter, when you add a filter: + +### Zend\\Hydrator\\Filter\\FilterComposite::CONDITION\_OR + +At the given level of the composite, at least one filter in that condition block +has to return true to extract the value. + +### Zend\\Hydrator\\Filter\\FilterComposite::CONDITION\_AND + +At the given level of the composite, all filters in that condition block must +return true to extract the value. + +### FilterComposite Examples + +This composition will have a similar logic as the if below: + +```php +$composite = new FilterComposite(); + +$composite->addFilter('one', $condition1); +$composite->addFilter('two', $condition2); +$composite->addFilter('three', $condition3); +$composite->addFilter('four', $condition4, FilterComposite::CONDITION_AND); +$composite->addFilter('five', $condition5, FilterComposite::CONDITION_AND); + +// This is what's happening internally +if ( + ($condition1 + || $condition2 + || $condition3 + ) && ($condition4 + && $condition5 + ) +) { + //do extraction +} +``` + +If you only have one condition (e.g., only an `and` or `or`) block, the other +one will be completely ignored. + +A bit more complex filter can look like this: + +```php +$composite = new FilterComposite(); +$composite->addFilter( + 'servicemanager', + new MethodMatchFilter('getServiceManager'), + FilterComposite::CONDITION_AND +); +$composite->addFilter( + 'eventmanager', + new MethodMatchFilter('getEventManager'), + FilterComposite::CONDITION_AND +); + +$hydrator->addFilter('excludes', $composite, FilterComposite::CONDITION_AND); + +// Internal +if (( // default composite inside the hydrator + ($getFilter + || $hasFilter + || $isFilter + ) && ( + $numberOfParameterFilter + ) + ) && ( // new composite, added to the one above + $serviceManagerFilter + && $eventManagerFilter + ) +) { + // do extraction +} +``` + +If you perform this on the `ClassMethods` hydrator, all getters will get +extracted, except for `getServiceManager()` and `getEventManager()`. + +## Using the provider interface + +`FilterProviderInterface` allows you to configure the behavior of the hydrator +inside your objects. + +```php +namespace Zend\Hydrator\Filter; + +interface FilterProviderInterface +{ + /** + * Provides a filter for hydration + * + * @return FilterInterface + */ + public function getFilter(); +} +``` + +(The `getFilter()` method is automatically excluded from `extract()`.) If the +extracted object implements the `Zend\Hydrator\Filter\FilterProviderInterface`, +the returned `FilterInterface` instance can also be a `FilterComposite`. + +For example: + +```php +Class Foo implements FilterProviderInterface +{ + public function getFoo() + { + return 'foo'; + } + + public function hasFoo() + { + return true; + } + + public function getServiceManager() + { + return 'servicemanager'; + } + + public function getEventManager() + { + return 'eventmanager'; + } + + public function getFilter() + { + $composite = new FilterComposite(); + $composite->addFilter('get', new GetFilter()); + + $exclusionComposite = new FilterComposite(); + $exclusionComposite->addFilter( + 'servicemanager', + new MethodMatchFilter('getServiceManager'), + FilterComposite::CONDITION_AND + ); + $exclusionComposite->addFilter( + 'eventmanager', + new MethodMatchFilter('getEventManager'), + FilterComposite::CONDITION_AND + ); + + $composite->addFilter('excludes', $exclusionComposite, FilterComposite::CONDITION_AND); + + return $composite; + } +} + +$hydrator = new ClassMethods(false); +$extractedArray = $hydrator->extract(new Foo()); +``` + +`$extractedArray` will only have 'foo' => 'foo'; all other values are +excluded from extraction. + +> ### Note +> +> All pre-registered filters from the `ClassMethods` hydrator are ignored when +> this interface is used. diff --git a/docs/book/v3/naming-strategy/composite-naming-strategy.md b/docs/book/v3/naming-strategy/composite-naming-strategy.md new file mode 100644 index 0000000..a62f0ab --- /dev/null +++ b/docs/book/v3/naming-strategy/composite-naming-strategy.md @@ -0,0 +1,83 @@ +# CompositeNamingStrategy + +`Zend\Hydrator\NamingStrategy\CompositeNamingStrategy` allows you to specify which naming +strategy should be used for each key encountered during hydration or extraction. + +# Basic Usage + +When invoked, the following composite strategy will extract the property `bar` +to the array key `foo` (using the `MapNamingStrategy`), and the property +`barBat` to the array key `bar_bat` (using the `UnderscoreNamingStrategy`): + +```php +class Foo +{ + public $bar; + public $barBat; +} + +$mapStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy([ + 'foo' => 'bar' +]); + +$underscoreNamingStrategy = new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy(); + +$namingStrategy = new Zend\Hydrator\NamingStrategy\CompositeNamingStrategy([ + 'bar' => $mapStrategy, + 'barBat' => $underscoreNamingStrategy, +]); + +$hydrator = new Zend\Hydrator\ObjectProperty(); +$hydrator->setNamingStrategy($namingStrategy); + +$foo = new Foo(); +$foo->bar = 123; +$foo->barBat = 42; + +print_r($foo); // Foo Object ( [bar] => 123 [barBat] => 42 ) +print_r($hydrator->extract($foo)); // Array ( [foo] => 123 [bar_bat] => 42 ) +``` + +Unfortunately, the `CompositeNamingStrategy` can only be used for extraction as it will not know how +to handle the keys necessary for hydration (`foo` and `bar_bat`, respectively). To rectify this we +have to cover the keys for both hydration and extraction in our composite strategy: + +```php +class Foo +{ + public $bar; + public $barBat; +} + +$mapStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy([ + 'foo' => 'bar' +]); + +$underscoreNamingStrategy = new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy(); + +$namingStrategy = new Zend\Hydrator\NamingStrategy\CompositeNamingStrategy([ + // Define both directions for the foo => bar mapping + 'bar' => $mapStrategy, + 'foo' => $mapStrategy, + // Define both directions for the barBat => bar_bat mapping + 'barBat' => $underscoreNamingStrategy, + 'bar_bat' => $underscoreNamingStrategy, +]); + +$hydrator = new Zend\Hydrator\ObjectProperty(); +$hydrator->setNamingStrategy($namingStrategy); + +$foo = new Foo(); +$foo->bar = 123; +$foo->barBat = 42; + +$array = $hydrator->extract($foo); + +print_r($foo); // Foo Object ( [bar] => 123 [barBat] => 42 ) +print_r($array); // Array ( [foo] => 123 [bar_bat] => 42 ) + +$foo2 = new Foo(); +$hydrator->hydrate($array, $foo2); + +print_r($foo2); // Foo Object ( [bar] => 123 [barBat] => 42 ) +``` diff --git a/docs/book/v3/naming-strategy/identity-naming-strategy.md b/docs/book/v3/naming-strategy/identity-naming-strategy.md new file mode 100644 index 0000000..9ff4a41 --- /dev/null +++ b/docs/book/v3/naming-strategy/identity-naming-strategy.md @@ -0,0 +1,32 @@ +# IdentityNamingStrategy + +`Zend\Hydrator\NamingStrategy\IdentityNamingStrategy` uses the keys provided to +it for hydration and extraction. + +## Basic Usage + +```php +$namingStrategy = new Zend\Hydrator\NamingStrategy\IdentityNamingStrategy(); + +echo $namingStrategy->hydrate('foo'); // outputs: foo +echo $namingStrategy->extract('bar'); // outputs: bar +``` + +This strategy can be used in hydrators as well: + +```php +class Foo +{ + public $foo; +} + +$namingStrategy = new Zend\Hydrator\NamingStrategy\IdentityNamingStrategy(); +$hydrator = new Zend\Hydrator\ObjectProperty(); +$hydrator->setNamingStrategy($namingStrategy); + +$foo = new Foo(); +$hydrator->hydrate(array('foo' => 123), $foo); + +print_r($foo); // Foo Object ( [foo] => 123 ) +print_r($hydrator->extract($foo)); // Array ( [foo] => 123 ) +``` diff --git a/docs/book/v3/naming-strategy/map-naming-strategy.md b/docs/book/v3/naming-strategy/map-naming-strategy.md new file mode 100644 index 0000000..ac9568c --- /dev/null +++ b/docs/book/v3/naming-strategy/map-naming-strategy.md @@ -0,0 +1,41 @@ +# MapNamingStrategy + +`Zend\Hydrator\NamingStrategy\MapNamingStrategy` allows you to provide a map of +keys to use when hydrating and extracting; the map will translate the key based +on the direction. + +## Basic Usage + +```php +$namingStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy(array( + 'foo' => 'bar', + 'baz' => 'bash' +)); +echo $namingStrategy->hydrate('foo'); // outputs: bar +echo $namingStrategy->hydrate('baz'); // outputs: bash + +echo $namingStrategy->extract('bar'); // outputs: foo +echo $namingStrategy->extract('bash'); // outputs: baz +``` + +This strategy can be used in hydrators to dictate how keys should be mapped: + +```php +class Foo +{ + public $bar; +} + +$namingStrategy = new Zend\Hydrator\NamingStrategy\MapNamingStrategy([ + 'foo' => 'bar', + 'baz' => 'bash' +]); +$hydrator = new Zend\Hydrator\ObjectProperty(); +$hydrator->setNamingStrategy($namingStrategy); + +$foo = new Foo(); +$hydrator->hydrate(['foo' => 123], $foo); + +print_r($foo); // Foo Object ( [bar] => 123 ) +print_r($hydrator->extract($foo)); // Array ( [foo] => 123 ) +``` diff --git a/docs/book/v3/naming-strategy/underscore-naming-strategy.md b/docs/book/v3/naming-strategy/underscore-naming-strategy.md new file mode 100644 index 0000000..813449e --- /dev/null +++ b/docs/book/v3/naming-strategy/underscore-naming-strategy.md @@ -0,0 +1,36 @@ +# UnderscoreNamingStrategy + +`Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy` converts snake case strings (e.g. +`foo_bar_baz`) to camel-case strings (e.g. `fooBarBaz`) and vice versa. + +## Basic Usage + +```php +$namingStrategy = new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy(); + +echo $namingStrategy->extract('foo_bar'); // outputs: foo_bar +echo $namingStrategy->extract('Foo_Bar'); // outputs: foo_bar +echo $namingStrategy->extract('FooBar'); // outputs: foo_bar + +echo $namingStrategy->hydrate('fooBar'); // outputs: fooBar +echo $namingStrategy->hydrate('FooBar'); // outputs: fooBar +echo $namingStrategy->hydrate('Foo_Bar'); // outputs: fooBar +``` + +This strategy can be used in hydrators to dictate how keys should be mapped. + +```php +class Foo +{ + public $fooBar; +} + +$hydrator = new Zend\Hydrator\ObjectProperty(); +$hydrator->setNamingStrategy(new Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy()); + +$foo = new Foo(); +$hydrator->hydrate(['foo_bar' => 123], $foo); + +print_r($foo); // Foo Object ( [fooBar] => 123 ) +print_r($hydrator->extract($foo)); // Array ( [foo_bar] => 123 ) +``` diff --git a/docs/book/v3/quick-start.md b/docs/book/v3/quick-start.md new file mode 100644 index 0000000..e504e28 --- /dev/null +++ b/docs/book/v3/quick-start.md @@ -0,0 +1,121 @@ +# zend-hydrator + +Hydration is the act of populating an object from a set of data. + +zend-hydrator is a simple component to provide mechanisms both for hydrating +objects, as well as extracting data sets from them. + +The component consists of interfaces, and several implementations for common use cases. + +## Base Interfaces + +### ExtractionInterface + +```php +namespace Zend\Hydrator; + +interface ExtractionInterface +{ + / + * Extract values from an object + * + * @param object $object + * @return array + */ + public function extract($object); +} +``` + +### HydrationInterface + +```php +namespace Zend\Hydrator; + +interface HydrationInterface +{ + / + * Hydrate $object with the provided $data. + * + * @param array $data + * @param object $object + * @return object + */ + public function hydrate(array $data, $object); +} +``` + +### HydratorInterface + +```php +namespace Zend\Hydrator; + +interface HydratorInterface extends + ExtractionInterface, + HydrationInterface +{ +} +``` + +## Usage + +Usage involves instantiating the hydrator, and then passing information to it. + +```php +use Zend\Hydrator; +$hydrator = new Hydrator\ArraySerializable(); + +// To hydrate an object from values in an array: +$object = $hydrator->hydrate($data, new ArrayObject()); + +// or, going the other way and extracting the values from an object as an array: +$data = $hydrator->extract($object); +``` + +## Available Implementations + +### Zend\\Hydrator\\ArraySerializable + +Follows the definition of `ArrayObject`. Objects must implement either the `exchangeArray()` or +`populate()` methods to support hydration, and the `getArrayCopy()` method to support extraction. + +### Zend\\Hydrator\\ClassMethods + +Any data key matching a setter method will be called in order to hydrate; any method matching a +getter method will be called for extraction. + +### Zend\\Hydrator\\DelegatingHydrator + +Composes a hydrator locator, and will delegate `hydrate()` and `extract()` calls +to the appropriate one based upon the class name of the object being operated +on. + +```php +// Instantiate each hydrator you wish to delegate to +$albumHydrator = new Zend\Hydrator\ClassMethods; +$artistHydrator = new Zend\Hydrator\ClassMethods; + +// Map the entity class name to the hydrator using the HydratorPluginManager. +// In this case we have two entity classes, "Album" and "Artist". +$hydrators = new Zend\Hydrator\HydratorPluginManager; +$hydrators->setService('Album', $albumHydrator); +$hydrators->setService('Artist', $artistHydrator); + +// Create the DelegatingHydrator and tell it to use our configured hydrator locator +$delegating = new Zend\Hydrator\DelegatingHydrator($hydrators); + +// Now we can use $delegating to hydrate or extract any supported object +$array = $delegating->extract(new Artist); +$artist = $delegating->hydrate($data, new Artist); +``` + +### Zend\\Hydrator\\ObjectProperty + +Any data key matching a publicly accessible property will be hydrated; any public properties +will be used for extraction. + +### Zend\\Hydrator\\Reflection + +Similar to the `ObjectProperty` hydrator, but uses [PHP's reflection API](http://php.net/manual/en/intro.reflection.php) +to hydrate or extract properties of any visibility. Any data key matching an +existing property will be hydrated; any existing properties will be used for +extraction. diff --git a/docs/book/v3/strategy.md b/docs/book/v3/strategy.md new file mode 100644 index 0000000..6e06b8f --- /dev/null +++ b/docs/book/v3/strategy.md @@ -0,0 +1,225 @@ +# Zend\\Hydrator\\Strategy + +You can add `Zend\Hydrator\Strategy\StrategyInterface` to any of the hydrators +(except if it extends `Zend\Hydrator\AbstractHydrator` or implements +`Zend\Hydrator\HydratorInterface` and `Zend\Hydrator\Strategy\StrategyEnabledInterface`) +to manipulate the way how they behave on `extract()` and `hydrate()` for +specific key / value pairs. This is the interface that needs to be implemented: + +```php +namespace Zend\Hydrator\Strategy; + +interface StrategyInterface +{ + /** + * Converts the given value so that it can be extracted by the hydrator. + * + * @param mixed $value The original value. + * @return mixed Returns the value that should be extracted. + */ + public function extract($value); + + /** + * Converts the given value so that it can be hydrated by the hydrator. + * + * @param mixed $value The original value. + * @return mixed Returns the value that should be hydrated. + */ + public function hydrate($value); +} +``` + +This interface is similar to `Zend\Hydrator\HydratorInterface`; the reason +is that strategies provide a proxy implementation for `hydrate()` and `extract()`. + +## Adding strategies to the hydrators + +To allow strategies within your hydrator, `Zend\Hydrator\Strategy\StrategyEnabledInterface` +provides the following methods: + +```php +namespace Zend\Hydrator; + +use Zend\Hydrator\Strategy\StrategyInterface; + +interface StrategyEnabledInterface +{ + /** + * Adds the given strategy under the given name. + * + * @param string $name The name of the strategy to register. + * @param StrategyInterface $strategy The strategy to register. + * @return HydratorInterface + */ + public function addStrategy($name, StrategyInterface $strategy); + + /** + * Gets the strategy with the given name. + * + * @param string $name The name of the strategy to get. + * @return StrategyInterface + */ + public function getStrategy($name); + + /** + * Checks if the strategy with the given name exists. + * + * @param string $name The name of the strategy to check for. + * @return bool + */ + public function hasStrategy($name); + + /** + * Removes the strategy with the given name. + * + * @param string $name The name of the strategy to remove. + * @return HydratorInterface + */ + public function removeStrategy($name); +} +``` + +Every hydrator shipped by default provides this functionality; +`AbstractHydrator` fully implements it as well. As such, if you want to use this +functionality in your own hydrators, you should extend `AbstractHydrator`. + +## Available implementations + +### Zend\\Hydrator\\Strategy\\BooleanStrategy + +This strategy converts values into Booleans and vice versa. It expects two +arguments at the constructor, which are used to define value maps for `true` and +`false`. + +### Zend\\Hydrator\\Strategy\\ClosureStrategy + +This is a strategy that allows you to pass in options for: + +- `hydrate`, a callback to be called when hydrating a value, and +- `extract`, a callback to be called when extracting a value. + +### Zend\\Hydrator\\Strategy\\DateTimeFormatterStrategy + +`DateTimeFormatterStrategy` provides bidirectional conversion between strings +and DateTime instances. The input and output formats can be provided as +constructor arguments. + +As of version 2.4.1, this strategy now allows `DateTime` formats that use `!` to +prepend the format, or `|` or `+` to append it; these ensure that, during +hydration, the new `DateTime` instance created will set the time element +accordingly. As a specific example, `Y-m-d|` will drop the time component, +ensuring comparisons are based on a midnight time value. + +### Zend\\Hydrator\\Strategy\\DefaultStrategy + +The `DefaultStrategy` simply proxies everything through, without performing any +conversion of values. + +### Zend\\Hydrator\\Strategy\\ExplodeStrategy + +This strategy is a wrapper around PHP's `implode()` and `explode()` functions. +The delimiter and a limit can be provided to the constructor; the limit will +only be used for `extract` operations. + +### Zend\\Hydrator\\Strategy\\SerializableStrategy + +`SerializableStrategy` provides the functionality backing +`Zend\Hydrator\ArraySerializable`. You can use it with custom implementations +for `Zend\Serializer\Adapter\AdapterInterface` if you want to as well. + +### Zend\\Hydrator\\Strategy\\StrategyChain + +This strategy takes an array of `StrategyInterface` instances and iterates +over them when performing `extract()` and `hydrate()` operations. Each operates +on the return value of the previous, allowing complex operations based on +smaller, single-purpose strategies. + +## Writing custom strategies + +The following example, while not terribly useful, will provide you with the +basics for writing your own strategies, as well as provide ideas as to where and +when to use them. This strategy simply transforms the value for the defined key +using `str_rot13()` during both the `extract()` and `hydrate()` operations: + +```php +class Rot13Strategy implements StrategyInterface +{ + public function extract($value) + { + return str_rot13($value); + } + + public function hydrate($value) + { + return str_rot13($value); + } +} +``` + +This is the example class with which we want to use the hydrator example: + +```php +class Foo +{ + protected $foo = null; + protected $bar = null; + + public function getFoo() + { + return $this->foo; + } + + public function setFoo($foo) + { + $this->foo = $foo; + } + + public function getBar() + { + return $this->bar; + } + + public function setBar($bar) + { + $this->bar = $bar; + } +} +``` + +Now, we'll add the `rot13` strategy to the method `getFoo()` and `setFoo($foo)`: + +```php +$foo = new Foo(); +$foo->setFoo('bar'); +$foo->setBar('foo'); + +$hydrator = new ClassMethods(); +$hydrator->addStrategy('foo', new Rot13Strategy()); +``` + +When you use the hydrator to extract an array for the object `$foo`, you'll +receive the following: + +```php +$extractedArray = $hydrator->extract($foo); + +// array(2) { +// ["foo"]=> +// string(3) "one" +// ["bar"]=> +// string(3) "foo" +// } +``` + +And when hydrating a new `Foo` instance: + +```php +$hydrator->hydrate($extractedArray, $foo) + +// object(Foo)#2 (2) { +// ["foo":protected]=> +// string(3) "bar" +// ["bar":protected]=> +// string(3) "foo" +// } +``` diff --git a/mkdocs.yml b/mkdocs.yml index 79a1ebd..b7d79e8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,17 +1,37 @@ docs_dir: docs/book site_dir: docs/html -pages: +nav: - Home: index.md - - "Quick Start": quick-start.md + - "Quick Start": v3/quick-start.md - Reference: - - "Filters": filter.md - - "Strategies": strategy.md - - "Aggregates": aggregate.md + - "Filters": v3/filter.md + - "Strategies": v3/strategy.md + - "Aggregates": v3/aggregate.md - "Naming Strategies": - - "Identity": naming-strategy/identity-naming-strategy.md - - "Mapping": naming-strategy/map-naming-strategy.md - - "Underscore Mapping": naming-strategy/underscore-naming-strategy.md - - "Composite": naming-strategy/composite-naming-strategy.md + - "Identity": v3/naming-strategy/identity-naming-strategy.md + - "Mapping": v3/naming-strategy/map-naming-strategy.md + - "Underscore Mapping": v3/naming-strategy/underscore-naming-strategy.md + - "Composite": v3/naming-strategy/composite-naming-strategy.md + - v2: + - "Quick Start": v2/quick-start.md + - Reference: + - "Filters": v2/filter.md + - "Strategies": v2/strategy.md + - "Aggregates": v2/aggregate.md + - "Naming Strategies": + - "Identity": v2/naming-strategy/identity-naming-strategy.md + - "Mapping": v2/naming-strategy/map-naming-strategy.md + - "Underscore Mapping": v2/naming-strategy/underscore-naming-strategy.md + - "Composite": v2/naming-strategy/composite-naming-strategy.md + - "_hidden-legacy-page-links": + - "_quick_start": quick-start.md + - "_filters": filter.md + - "_strategies": strategy.md + - "_aggregates": aggregate.md + - "_naming_strategies-identity": naming-strategy/identity-naming-strategy.md + - "_naming_strategies-mapping": naming-strategy/map-naming-strategy.md + - "_naming_strategies-underscore_mapping": naming-strategy/underscore-naming-strategy.md + - "_naming_strategies-composite": naming-strategy/composite-naming-strategy.md site_name: zend-hydrator site_description: "Serialize objects to arrays, and vice versa" repo_url: 'https://github.com/zendframework/zend-hydrator' From 8ab8be95c7764c0820e81f989ec349a81df9ec6a Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 26 Nov 2018 16:36:16 -0600 Subject: [PATCH 08/35] Set explicit `object` param/return types in event implementations Both `ExtractEvent` and `HydrateEvent` were defining `object` parameters and return types via annotations, but could easily define them explicitly as typehints. --- src/Aggregate/ExtractEvent.php | 17 +++-------------- src/Aggregate/HydrateEvent.php | 18 +++--------------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/src/Aggregate/ExtractEvent.php b/src/Aggregate/ExtractEvent.php index 32e2d88..5510791 100644 --- a/src/Aggregate/ExtractEvent.php +++ b/src/Aggregate/ExtractEvent.php @@ -34,11 +34,7 @@ class ExtractEvent extends Event */ protected $extractedData = []; - /** - * @param object $target - * @param object $extractionObject - */ - public function __construct($target, $extractionObject) + public function __construct(object $target, object $extractionObject) { parent::__construct(); $this->target = $target; @@ -47,18 +43,13 @@ public function __construct($target, $extractionObject) /** * Retrieves the object from which data is extracted - * - * @return object */ - public function getExtractionObject() + public function getExtractionObject() : object { return $this->extractionObject; } - /** - * @param object $extractionObject - */ - public function setExtractionObject($extractionObject) : void + public function setExtractionObject(object $extractionObject) : void { $this->extractionObject = $extractionObject; } @@ -81,8 +72,6 @@ public function setExtractedData(array $extractedData) : void /** * Merge provided data with the extracted data - * - * @param array $additionalData */ public function mergeExtractedData(array $additionalData) : void { diff --git a/src/Aggregate/HydrateEvent.php b/src/Aggregate/HydrateEvent.php index e614083..2842bc3 100644 --- a/src/Aggregate/HydrateEvent.php +++ b/src/Aggregate/HydrateEvent.php @@ -34,11 +34,7 @@ class HydrateEvent extends Event */ protected $hydrationData; - /** - * @param object $target - * @param object $hydratedObject - */ - public function __construct($target, $hydratedObject, array $hydrationData) + public function __construct(object $target, object $hydratedObject, array $hydrationData) { parent::__construct(); $this->target = $target; @@ -48,18 +44,13 @@ public function __construct($target, $hydratedObject, array $hydrationData) /** * Retrieves the object that is being hydrated - * - * @return object */ - public function getHydratedObject() + public function getHydratedObject() : object { return $this->hydratedObject; } - /** - * @param object $hydratedObject - */ - public function setHydratedObject($hydratedObject) : void + public function setHydratedObject(object $hydratedObject) : void { $this->hydratedObject = $hydratedObject; } @@ -72,9 +63,6 @@ public function getHydrationData() : array return $this->hydrationData; } - /** - * @param array $hydrationData - */ public function setHydrationData(array $hydrationData) : void { $this->hydrationData = $hydrationData; From 042ce54255f244261fa2cae8687f6ac29734a674 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 26 Nov 2018 16:42:45 -0600 Subject: [PATCH 09/35] Use `object` return type for `HydratorListener::onHydrate()` --- src/Aggregate/HydratorListener.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Aggregate/HydratorListener.php b/src/Aggregate/HydratorListener.php index 10c656d..fa95da9 100644 --- a/src/Aggregate/HydratorListener.php +++ b/src/Aggregate/HydratorListener.php @@ -25,9 +25,6 @@ class HydratorListener extends AbstractListenerAggregate */ protected $hydrator; - /** - * @param HydratorInterface $hydrator - */ public function __construct(HydratorInterface $hydrator) { $this->hydrator = $hydrator; @@ -46,9 +43,8 @@ public function attach(EventManagerInterface $events, $priority = 1) : void * Callback to be used when {@see HydrateEvent::EVENT_HYDRATE} is triggered * * @internal - * @return object */ - public function onHydrate(HydrateEvent $event) + public function onHydrate(HydrateEvent $event) : object { $object = $this->hydrator->hydrate($event->getHydrationData(), $event->getHydratedObject()); $event->setHydratedObject($object); From fbcbebfde72c570f3a1f450f6300ab65094e6ebd Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 26 Nov 2018 17:25:44 -0600 Subject: [PATCH 10/35] Adds migration document for v3 --- docs/book/v3/migration.md | 184 ++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 185 insertions(+) create mode 100644 docs/book/v3/migration.md diff --git a/docs/book/v3/migration.md b/docs/book/v3/migration.md new file mode 100644 index 0000000..84e1fb3 --- /dev/null +++ b/docs/book/v3/migration.md @@ -0,0 +1,184 @@ +# Migration from version 2 + +This document details changes made between version 2 and version 3 that could +affect end-users. + +## Minimum supported versions + +### PHP + +Version 3 bumps the minimum supported PHP version to version 7.2. We chose this +version in particular as it provides the `object` typehint, which allows us to +enforce at the engine level what we were previously enforcing only at the +documentation level. + +Additionally, we have enabled strict types in all class files shipped with this +component. + +### zend-eventmanager + +The minimum supported version of zend-eventmanager (used by the +`AggregateHydrator`)is now 3.2.1. + +### zend-serializer + +The minimum supported version of zend-serializer (used by the +`SerializableStrategy`) is now 2.9.0. + +### zend-servicemanager + +The minimum supported version of zend-servicemanager (used by the +`HydratorPluginManager`) is now 3.3.2. + +## Interface changes + +Each of the interfaces provided by this package have been updated to add +typehints where they were previously omitted (due to engine limitations), both +on parameters and return values. These include: + +- `Zend\Hydrator\ExtractionInterface`: + - `extract($object)` becomes `extract(object $object) : array` +- `Zend\Hydrator\FilterEnabledInterface`: + - `addFilter($name, $filter, $condition = Zend\Hydrator\Filter\FilterComposite::CONDITION_OR)` becomes `addFilter(string $name, $filter, int $condition = Zend\Hydrator\Filter\FilterComposite::CONDITION_OR) : void` + - `hasFilter($name)` becomes `hasFilter(string $name) : bool` + - `removeFilter($name)` becomes `removeFilter(string $name) : void` +- `Zend\Hydrator\HydrationInterface`: + - `hydrate(array $data, $object)` becomes `hydrate(array $data, object $object) : object` +- `Zend\Hydrator\HydratorAwareInterface`: + - `setHydrator(Zend\Hydrator\HydratorInterface $hydrator)` becomes `setHydrator(Zend\Hydrator\HydratorInterface $hydrator) : void` + - `getHydrator()` becomes `getHydrator() : ?Zend\Hydrator\HydratorInterface` + - `Zend\Hydrator\HydratorAwareTrait` was updated to follow the interface; if + you use the trait to implement the interface, no changes will be necessary. +- `Zend\Hydrator\HydratorOptionsInterface`: + - `setOptions($options)` becomes `setOptions(iterable $options) : void` +- `Zend\Hydrator\HydratorProviderInterface`: + - `getHydratorConfig()` becomes `getHydratorConfig() : array` +- `Zend\Hydrator\NamingStrategyEnabledInterface`: + - `setNamingStrategy(Zend\Hydrator\NamingStrategy\NamingStrategyInterface $strategy)` becomes `setNamingStrategy(Zend\Hydrator\NamingStrategy\NamingStrategyInterface $strategy) : void` + - `getNamingStrategy()` becomes `getNamingStrategy() : Zend\Hydrator\NamingStrategy\NamingStrategyInterface` + - `removeNamingStrategy()` becomes `removeNamingStrategy() : void` +- `Zend\Hydrator\StrategyEnabledInterface`: + - `addStrategy($name, Zend\Hydrator\Strategy\StrategyInterface $strategy)` becomes `addStrategy(string $name, Zend\Hydrator\Strategy\StrategyInterface $strategy) : void` + - `getStrategy($name)` becomes `getStrategy(string $name) : Zend\Hydrator\Strategy\StrategyInterface` + - `hasStrategy($name)` becomes `hasStrategy(string $name) : bool` + - `removeStrategy($name)` becomes `removeStrategy(string $name) : void` +- `Zend\Hydrator\Filter\FilterInterface`: + - `filter($property)` becomes `filter(string $property) : bool` +- `Zend\Hydrator\Filter\FilterProviderInterface`: + - `getFilter()` becomes `getFilter() : Zend\Hydrator\Filter\FilterInterface` +- `Zend\Hydrator\Iterator\HydratingIteratorInterface`: + - `setPrototype($prototype)` becomes `setPrototype($prototype) : void` (`$prototype` continues to allow either a `string` or object) + - `setHydrator(Zend\Hydrator\HydratorInterface $hydrator)` becomes `setHydrator(Zend\Hydrator\HydratorInterface $hydrator) : void` +- `Zend\Hydrator\NamingStrategy\NamingStrategyInterface`: + - `extract($name)` becomes `extract(string $name, ?object $object = null) : string` + - `hydrate($name)` becomes `hydrate(string $name, ?array $data = null) : string` +- `Zend\Hydrator\Strategy\StrategyInterface`: + - `hydrate($value)` becomes `hydrate($value, ?array $data = null)` (the `$value` parameter and return value can be any PHP type) + - `extract($value)` becomes `extract($value, ?object $object = null)` (the `$value` parameter and return value can be any PHP type) + +All implementations of these interface shipped in the component have been +updated to ensure signatures match. + +If you are providing custom implementations, or extending the implementations +provided in this package, you may need to update signatures per the above. + +## Typehints + +As noted in the above section, typehints were added to all interfaces. In +addition to those changes, the following methods were also updated to add +typehints: + +- `Zend\Hydrator\Aggregate\AggregateHydrator`: + - `add(Zend\Hydrator\HydratorInterface $hydrator, $priority = self::DEFAULT_PRIORITY)` becomes `add(Zend\Hydrator\HydratorInterface $hydrator, int $priority = self::DEFAULT_PRIORITY) : void` + +- `Zend\Hydrator\Aggregate\ExtractEvent`: + - `__construct($target, $extractionObject)` becomes `__construct(object $target, object $extractionObject)` + - `getExtractionObject()` becomes `getExtractionObject() : object` + - `setExtractionObject($extractionObject)` becomes `setExtractionObject(object $extractionObject) : void` + - `getExtractedData()` becomes `getExtractedData() : array` + - `setExtractedData(array $extractedData)` becomes `setExtractedData(array $extractedData) : void` + - `mergeExtractedData(array $additionalData)` becomes `mergeExtractedData(array $additionalData) : void` + +- `Zend\Hydrator\Aggregate\HydrateEvent`: + - `__construct($target, $hydratedObject, array $hydrationData)` becomes `__construct(object $target, object $hydratedObject, array $hydrationData)` + - `getHydratedObject()` becomes `getHydratedObject() : object` + - `setHydratedObject($hydratedObject)` becomes `setHydratedObject(object $hydratedObject) : void` + - `getHydrationData()` becomes `getHydrationData() : array` + - `setHydrationData(array $hydrationData)` becomes `setHydrationData(array $hydrationData) : void` + +- `Zend\Hydrator\Aggregate\HydratorListener`: + - `onHydrate(HydrateEvent $event)` becomes `onHydrate(HydrateEvent $event) : object` + - `onExtract(ExtractEvent $event)` becomes `onExtract(ExtractEvent $event) : array` + +- `Zend\Hydrator\ClassMethods`: + - `__construct($underscoreSeparatedKeys = true, $methodExistsCheck = false)` becomes `__construct(bool $underscoreSeparatedKeys = true, bool $methodExistsCheck = false)` + - `setUnderscoreSeparatedKeys($underscoreSeparatedKeys)` becomes `setUnderscoreSeparatedKeys(bool $underscoreSeparatedKeys) : void` + - `getUnderscoreSeparatedKeys()` becomes `getUnderscoreSeparatedKeys() : bool` + - `setMethodExistsCheck($methodExistsCheck)` becomes `setMethodExistsCheck(bool $methodExistsCheck) : void` + - `getMethodExistsCheck()` becomes `getMethodExistsCheck() : bool` + +- `Zend\Hydrator\ConfigProvider`: + - `__invoke()` becomes `__invoke() : array` + - `getDependencyConfig()` becomes `getDependencyConfig() : array` + +- `Zend\Hydrator\DelegatingHydratorFactory`: + - no longer implements `Zend\ServiceManager\FactoryInterface` + - `__invoke(Interop\Container\ContainerInterface $container, $requestedName, array $options = null)` becomes `__invoke(Psr\Container\ContainerInterface $container) : Zend\Hydrator\DelegatingHydrator` + +- `Zend\Hydrator\Filter\FilterComposite`: + - `__construct($orFilters = [], $andFilters = [])` becomes `__construct(array $orFilters = [], array $andFilters = [])` + +- `Zend\Hydrator\Filter\MethodMatchFilter`: + - `__construct($method, $exclude = true)` becomes `__construct(string $method, bool $exclude = true)` + +- `Zend\Hydrator\Filter\NumberOfParameterFilter`: + - `__construct($numberOfParameters = 0)` becomes `__construct(int $numberOfParameters = 0)` + +- `Zend\Hydrator\HydratorPluginManagerFactory`: + - no longer implements `Zend\ServiceManager\FactoryInterface` + - `__invoke(Interop\Container\ContainerInterface $container, $requestedName, array $options = null)` becomes `__invoke(Psr\Container\ContainerInterface $container, string $name, array $options = []) : Zend\Hydrator\HydratorPluginManager` + +- `Zend\Hydrator\Module`: + - `getConfig()` becomes `getConfig() : array` + - `init($moduleManager)` becomes `init(Zend\ModuleManager\ModuleManager $moduleManager) : void` + +- `Zend\Hydrator\NamingStrategy\CompositeNamingStrategy`: + - `__construct(array $strategies, Zend\Hydrator\NamingStrategy\NamingStrategyInterface $defaultNamingStrategy = null)` becomes `__construct(array $strategies, ?Zend\Hydrator\NamingStrategy\NamingStrategyInterface $defaultNamingStrategy = null)` + +- `Zend\Hydrator\NamingStrategy\MapNamingStrategy`: + - `__construct(array $mapping, array $reverse = null)` becomes `__construct(array $mapping, ?array $reverse = null)` + +- `Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy\CamelCaseToUnderscoreFilter`: + - `filter($value)` becomes `filter(string $value) : string` + +- `Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy\UnderscoreToCamelCaseFilter`: + - `filter($value)` becomes `filter(string $value) : string` + +- `Zend\Hydrator\Strategy\ClosureStrategy`: + - `__construct($extractFunc = null, $hydrateFunc = null)` becomes `__construct(?callable $extractFunc = null, ?callable $hydrateFunc = null)` + +- `Zend\Hydrator\Strategy\CollectionStrategy`: + - `__construct(Zend\Hydrator\HydratorInterface $objectHydrator, $objectClassName)` becomes `__construct(Zend\Hydrator\HydratorInterface $objectHydrator, string $objectClassName)` + +- `Zend\Hydrator\Strategy\DateTimeFormatterStrategy`: + - `__construct($format = DateTime::RFC3339, DateTimeZone $timezone = null, $dateTimeFallback = false)` becomes `__construct(string $format = DateTime::RFC3339, ?DateTimeZone $timezone = null, bool $dateTimeFallback = false)` + +- `Zend\Hydrator\Strategy\ExplodeStrategy`: + - `__construct($delimiter = ',', $explodeLimit = null)` becomes `__construct(string $delimiter = ',', ?int $explodeLimit = null)` + +- `Zend\Hydrator\Strategy\SerializableStrategy`: + - `__construct($serializer, $serializerOptions = null)` becomes `__construct($serializer, ?iterable $serializerOptions = null)` + - `setSerializer($serializer)` becomes `setSerializer($serializer) : void` + - `getSerializer()` becomes `getSerializer($serializer) : Zend\Serializer\Adapter\AdapterInterface` + - `setSerializerOptions($serializerOptions)` becomes `setSerializerOptions(iterable $serializerOptions) : void` + - `getSerializerOptions()` becomes `getSerializerOptions() : array` + +- `Zend\Hydrator\Strategy\StrategyChain`: + - `__construct($extractionStrategies)` becomes `__construct(iterable $extractionStrategies)` + +## HydratorPluginManager + +This version removes support for zend-servicemanager v2 service names. Under +zend-servicemanager v2, most special characters were removed, and the name +normalized to all lowercase. Now, only fully qualified class names are mapped to +factories, and short names (names omitting the namespace) are mapped as aliases. diff --git a/mkdocs.yml b/mkdocs.yml index b7d79e8..784be5a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -12,6 +12,7 @@ nav: - "Mapping": v3/naming-strategy/map-naming-strategy.md - "Underscore Mapping": v3/naming-strategy/underscore-naming-strategy.md - "Composite": v3/naming-strategy/composite-naming-strategy.md + - Migration: v3/migration.md - v2: - "Quick Start": v2/quick-start.md - Reference: From 6334c289e8714a079e1b47612df07f5f0663e8d7 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 26 Nov 2018 17:27:28 -0600 Subject: [PATCH 11/35] Revert rename of `pages` to `nav` in `mkdocs.yml` Allows greater compatibility with installs of mkdocs. --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 784be5a..f485870 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,6 +1,6 @@ docs_dir: docs/book site_dir: docs/html -nav: +pages: - Home: index.md - "Quick Start": v3/quick-start.md - Reference: From e8cc238930838ed353e235e025a6ad5f9e3d5711 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 27 Nov 2018 08:17:22 -0600 Subject: [PATCH 12/35] Fix errors flagged by phpstan This patch updates the `SerializableStrategy` and `FilterComposite` to reduce the number of `Result of ... is always (true|false)` occurrences; it does this by updating docblocks to indicate `mixed` values where we expect a union of multiple types and then raise an exception in the code. I'm not 100% convinced this is the right approach, as this means the annotations no longer indicate exactly _what is accepted_. --- phpstan.neon | 2 +- src/Filter/FilterComposite.php | 12 +++++++----- src/Strategy/SerializableStrategy.php | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 86f9b57..b5a06d5 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,4 +5,4 @@ parameters: ignoreErrors: - '#Array \(array\\>\) does not accept null.#' - '#Parameter \#1 \$configInstanceOrParentLocator.*?Psr\\Container\\ContainerInterface given\.#' - - '#Result of [|&]{2} is always (true|false)#' + - '#Result of \|{2} is always true#' diff --git a/src/Filter/FilterComposite.php b/src/Filter/FilterComposite.php index 7bfbaa9..79f1232 100644 --- a/src/Filter/FilterComposite.php +++ b/src/Filter/FilterComposite.php @@ -64,9 +64,9 @@ public function __construct(array $orFilters = [], array $andFilters = []) * ); * * - * @param callable|FilterInterface $filter - * @param int $condition Can be either - * FilterComposite::CONDITION_OR or FilterComposite::CONDITION_AND + * @param mixed $filter + * @param int $condition Can be either FilterComposite::CONDITION_OR + * or FilterComposite::CONDITION_AND * @throws InvalidArgumentException */ public function addFilter(string $name, $filter, int $condition = self::CONDITION_OR) : void @@ -159,8 +159,10 @@ public function filter(string $property) : bool } /** - * @param FilterInterface|callable $filter - * @throws InvalidArgumentException + * @param mixed $filter Filters should be callable or + * FilterInterface instances. + * @throws InvalidArgumentException if $filter is neither a + * callable nor FilterInterface */ private function validateFilter($filter, string $name) : void { diff --git a/src/Strategy/SerializableStrategy.php b/src/Strategy/SerializableStrategy.php index 8566e93..68e340b 100644 --- a/src/Strategy/SerializableStrategy.php +++ b/src/Strategy/SerializableStrategy.php @@ -65,7 +65,8 @@ public function hydrate($value, ?array $data = null) /** * Set serializer * - * @param string|SerializerAdapter $serializer + * @param mixed $serializer Should be a string or + * SerializerAdapter instance * @throws InvalidArgumentException for invalid $serializer values */ public function setSerializer($serializer) : void From c867dc0457f53613b33df8defa3fb66effa03a66 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 27 Nov 2018 08:26:55 -0600 Subject: [PATCH 13/35] Use a dist file for phpstan - Per @ocramius --- composer.json | 2 +- phpstan.neon => phpstan.neon.dist | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename phpstan.neon => phpstan.neon.dist (100%) diff --git a/composer.json b/composer.json index b3c2ffa..0afb2f2 100644 --- a/composer.json +++ b/composer.json @@ -60,7 +60,7 @@ } }, "scripts": { - "analyse": "phpstan analyse --no-progress -c phpstan.neon", + "analyse": "phpstan analyse --no-progress", "check": [ "@cs-check", "@test" diff --git a/phpstan.neon b/phpstan.neon.dist similarity index 100% rename from phpstan.neon rename to phpstan.neon.dist From 60f8dc4f4a2856aadb3bda0fb5911db77514a674 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 27 Nov 2018 11:30:10 -0600 Subject: [PATCH 14/35] Do not iterate if value is null Flagged by phpstan, `$extractionMethodsCache[$objectClass]` can be `null`, which would cause iteration to fail. This patch adds an explicit check to see if the value is `null`, and, if so, returns an empty array. The patch also updates the typehint for the property to `null[]|string[][]`. --- phpstan.neon.dist | 1 - src/ClassMethods.php | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index b5a06d5..84b609e 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,6 +3,5 @@ parameters: paths: - src/ ignoreErrors: - - '#Array \(array\\>\) does not accept null.#' - '#Parameter \#1 \$configInstanceOrParentLocator.*?Psr\\Container\\ContainerInterface given\.#' - '#Result of \|{2} is always true#' diff --git a/src/ClassMethods.php b/src/ClassMethods.php index f4b9536..8aab89d 100644 --- a/src/ClassMethods.php +++ b/src/ClassMethods.php @@ -41,7 +41,7 @@ class ClassMethods extends AbstractHydrator implements HydratorOptionsInterface * A map of extraction methods to property name to be used during extraction, indexed * by class name and method name * - * @var (null|string)[][] + * @var null[]|string[][] */ private $extractionMethodsCache = []; @@ -166,6 +166,10 @@ public function extract(object $object) : array $values = []; + if (null === $this->extractionMethodsCache[$objectClass]) { + return $values; + } + // pass 2 - actually extract data foreach ($this->extractionMethodsCache[$objectClass] as $methodName => $attributeName) { $realAttributeName = $this->extractName($attributeName, $object); From 28d63e7a80f080a92f4ca872e4244db26d08ff01 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 27 Nov 2018 11:42:17 -0600 Subject: [PATCH 15/35] Use method_exists + is_callable phpstan did not like bare `is_callable()` on items marked `object`, so I originally changed those to `method_exists()`. However, @Ocramius noted that `method_exists()` returns `true` for non-public methods. As such, I've updated the code to do a combination of `method_exists` + `is_callable` calls, which satisfies phpstan, and makes the code behave correctly. --- src/ArraySerializable.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ArraySerializable.php b/src/ArraySerializable.php index 44189cf..48dc1da 100644 --- a/src/ArraySerializable.php +++ b/src/ArraySerializable.php @@ -20,7 +20,7 @@ class ArraySerializable extends AbstractHydrator */ public function extract(object $object) : array { - if (! method_exists($object, 'getArrayCopy')) { + if (! method_exists($object, 'getArrayCopy') || ! is_callable([$object, 'getArrayCopy'])) { throw new Exception\BadMethodCallException( sprintf('%s expects the provided object to implement getArrayCopy()', __METHOD__) ); @@ -67,10 +67,10 @@ public function hydrate(array $data, object $object) : object $replacement[$name] = $this->hydrateValue($name, $value, $data); } - if (method_exists($object, 'exchangeArray')) { + if (method_exists($object, 'exchangeArray') && is_callable([$object, 'exchangeArray'])) { // Ensure any previously populated values not in the replacement // remain following population. - if (method_exists($object, 'getArrayCopy')) { + if (method_exists($object, 'getArrayCopy') && is_callable([$object, 'getArrayCopy'])) { $original = $object->getArrayCopy($object); $replacement = array_merge($original, $replacement); } @@ -78,7 +78,7 @@ public function hydrate(array $data, object $object) : object return $object; } - if (method_exists($object, 'populate')) { + if (method_exists($object, 'populate') && is_callable([$object, 'populate'])) { $object->populate($replacement); return $object; } From a1225fd9b5aff8c67b71ee3239c9aeda53b25b20 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 27 Nov 2018 13:02:56 -0600 Subject: [PATCH 16/35] Document all iterable and array parameters, return values, and properties By request of @Ocramius, this patch updates docblocks to provide details on all array and iterable properties, parameters, and return values. I extracted `PcreReplacement`, as phpstan did not know what to do with list dereferencing of an array, and this makes it explicit what the expected types are. --- src/AbstractHydrator.php | 6 +-- src/Aggregate/ExtractEvent.php | 8 +++- src/Aggregate/HydrateEvent.php | 10 ++++- src/Aggregate/HydratorListener.php | 1 + src/ArraySerializable.php | 4 +- src/ClassMethods.php | 7 +++ src/ConfigProvider.php | 4 ++ src/ExtractionInterface.php | 2 + src/Filter/FilterComposite.php | 8 ++-- src/HydrationInterface.php | 2 + src/HydratorOptionsInterface.php | 3 ++ src/HydratorPluginManager.php | 4 +- src/HydratorProviderInterface.php | 3 ++ src/Iterator/HydratingArrayIterator.php | 1 + src/Module.php | 2 + src/NamingStrategy/ArrayMapNamingStrategy.php | 2 +- .../CompositeNamingStrategy.php | 2 +- src/NamingStrategy/MapNamingStrategy.php | 16 ++++--- .../NamingStrategyInterface.php | 2 +- .../PcreReplacement.php | 29 ++++++++++++ .../UnderscoreToCamelCaseFilter.php | 44 +++++++------------ src/ObjectProperty.php | 8 ++-- src/Reflection.php | 4 ++ src/Strategy/ClosureStrategy.php | 8 +--- src/Strategy/CollectionStrategy.php | 6 +-- src/Strategy/SerializableStrategy.php | 14 +++--- src/Strategy/StrategyChain.php | 3 ++ 27 files changed, 135 insertions(+), 68 deletions(-) create mode 100644 src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php diff --git a/src/AbstractHydrator.php b/src/AbstractHydrator.php index 19f23f9..198b773 100644 --- a/src/AbstractHydrator.php +++ b/src/AbstractHydrator.php @@ -20,7 +20,7 @@ abstract class AbstractHydrator implements /** * The list with strategies that this hydrator has. * - * @var ArrayObject + * @var ArrayObject */ protected $strategies; @@ -170,8 +170,8 @@ public function extractName(string $name, ?object $object = null) /** * Converts a value for hydration. If no naming strategy exists, the plain value is returned. * - * @param string $name The name to convert. - * @param array $data The whole data is optionally provided as context. + * @param string $name The name to convert. + * @param null|mixed[] $data The whole data is optionally provided as context. */ public function hydrateName(string $name, ?array $data = null) : string { diff --git a/src/Aggregate/ExtractEvent.php b/src/Aggregate/ExtractEvent.php index 5510791..63f65f3 100644 --- a/src/Aggregate/ExtractEvent.php +++ b/src/Aggregate/ExtractEvent.php @@ -30,7 +30,7 @@ class ExtractEvent extends Event protected $extractionObject; /** - * @var array + * @var mixed[] Data being extracted from the $extractionObject */ protected $extractedData = []; @@ -56,6 +56,8 @@ public function setExtractionObject(object $extractionObject) : void /** * Retrieves the data that has been extracted + * + * @return mixed[] */ public function getExtractedData() : array { @@ -63,7 +65,7 @@ public function getExtractedData() : array } /** - * @param array $extractedData + * @param mixed[] $extractedData */ public function setExtractedData(array $extractedData) : void { @@ -72,6 +74,8 @@ public function setExtractedData(array $extractedData) : void /** * Merge provided data with the extracted data + * + * @param mixed[] $additionalData */ public function mergeExtractedData(array $additionalData) : void { diff --git a/src/Aggregate/HydrateEvent.php b/src/Aggregate/HydrateEvent.php index 2842bc3..c644a81 100644 --- a/src/Aggregate/HydrateEvent.php +++ b/src/Aggregate/HydrateEvent.php @@ -30,10 +30,13 @@ class HydrateEvent extends Event protected $hydratedObject; /** - * @var array + * @var mixed[] Data being used to hydrate the $hydratedObject */ protected $hydrationData; + /** + * @param mixed[] $hydrationData Data being used to hydrate the $hydratedObject + */ public function __construct(object $target, object $hydratedObject, array $hydrationData) { parent::__construct(); @@ -57,12 +60,17 @@ public function setHydratedObject(object $hydratedObject) : void /** * Retrieves the data that is being used for hydration + * + * @return mixed[] */ public function getHydrationData() : array { return $this->hydrationData; } + /** + * @param mixed[] $hydrationData + */ public function setHydrationData(array $hydrationData) : void { $this->hydrationData = $hydrationData; diff --git a/src/Aggregate/HydratorListener.php b/src/Aggregate/HydratorListener.php index fa95da9..907ce84 100644 --- a/src/Aggregate/HydratorListener.php +++ b/src/Aggregate/HydratorListener.php @@ -55,6 +55,7 @@ public function onHydrate(HydrateEvent $event) : object * Callback to be used when {@see ExtractEvent::EVENT_EXTRACT} is triggered * * @internal + * @return mixed[] */ public function onExtract(ExtractEvent $event) : array { diff --git a/src/ArraySerializable.php b/src/ArraySerializable.php index 48dc1da..f7c41b5 100644 --- a/src/ArraySerializable.php +++ b/src/ArraySerializable.php @@ -16,6 +16,7 @@ class ArraySerializable extends AbstractHydrator * * Extracts values via the object's getArrayCopy() method. * + * {@inheritDoc} * @throws Exception\BadMethodCallException for an $object not implementing getArrayCopy() */ public function extract(object $object) : array @@ -55,8 +56,7 @@ public function extract(object $object) : array * Hydrates an object by passing $data to either its exchangeArray() or * populate() method. * - * @param object $object - * @return object + * {@inheritDoc} * @throws Exception\BadMethodCallException for an $object not implementing exchangeArray() or populate() */ public function hydrate(array $data, object $object) : object diff --git a/src/ClassMethods.php b/src/ClassMethods.php index 8aab89d..3a29656 100644 --- a/src/ClassMethods.php +++ b/src/ClassMethods.php @@ -72,6 +72,9 @@ public function __construct(bool $underscoreSeparatedKeys = true, bool $methodEx ); } + /** + * @param mixed[] $options + */ public function setOptions(iterable $options) : void { if ($options instanceof Traversable) { @@ -121,6 +124,8 @@ public function getMethodExistsCheck() : bool * Extract values from an object with class methods * * Extracts the getter/setter of the given $object. + * + * {@inheritDoc} */ public function extract(object $object) : array { @@ -183,6 +188,8 @@ public function extract(object $object) : array * Hydrate an object by populating getter/setter methods * * Hydrates an object by getter/setter methods of the object. + * + * {@inheritDoc} */ public function hydrate(array $data, object $object) : object { diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index d233618..e2d72a1 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -13,6 +13,8 @@ class ConfigProvider { /** * Return configuration for this component. + * + * @return mixed[] */ public function __invoke() : array { @@ -23,6 +25,8 @@ public function __invoke() : array /** * Return dependency mappings for this component. + * + * @return string[][] */ public function getDependencyConfig() : array { diff --git a/src/ExtractionInterface.php b/src/ExtractionInterface.php index e7a36c9..018df37 100644 --- a/src/ExtractionInterface.php +++ b/src/ExtractionInterface.php @@ -13,6 +13,8 @@ interface ExtractionInterface { /** * Extract values from an object + * + * @return mixed[] */ public function extract(object $object) : array; } diff --git a/src/Filter/FilterComposite.php b/src/Filter/FilterComposite.php index 79f1232..416acf5 100644 --- a/src/Filter/FilterComposite.php +++ b/src/Filter/FilterComposite.php @@ -37,6 +37,8 @@ class FilterComposite implements FilterInterface /** * We can pass a list of OR/AND filters through construct * + * @param callable[]|FilterInterface[] $orFilters + * @param callable[]|FilterInterface[] $andFilters * @throws InvalidArgumentException */ public function __construct(array $orFilters = [], array $andFilters = []) @@ -64,9 +66,9 @@ public function __construct(array $orFilters = [], array $andFilters = []) * ); * * - * @param mixed $filter - * @param int $condition Can be either FilterComposite::CONDITION_OR - * or FilterComposite::CONDITION_AND + * @param callable|FilterInterface $filter + * @param int $condition Can be either + * FilterComposite::CONDITION_OR or FilterComposite::CONDITION_AND * @throws InvalidArgumentException */ public function addFilter(string $name, $filter, int $condition = self::CONDITION_OR) : void diff --git a/src/HydrationInterface.php b/src/HydrationInterface.php index a86e871..7ed0065 100644 --- a/src/HydrationInterface.php +++ b/src/HydrationInterface.php @@ -13,6 +13,8 @@ interface HydrationInterface { /** * Hydrate $object with the provided $data. + * + * @param mixed[] $data */ public function hydrate(array $data, object $object) : object; } diff --git a/src/HydratorOptionsInterface.php b/src/HydratorOptionsInterface.php index 103dfbf..64993d2 100644 --- a/src/HydratorOptionsInterface.php +++ b/src/HydratorOptionsInterface.php @@ -11,5 +11,8 @@ interface HydratorOptionsInterface { + /** + * @param mixed[] $options + */ public function setOptions(iterable $options) : void; } diff --git a/src/HydratorPluginManager.php b/src/HydratorPluginManager.php index 922d434..62ec3e9 100644 --- a/src/HydratorPluginManager.php +++ b/src/HydratorPluginManager.php @@ -23,7 +23,7 @@ class HydratorPluginManager extends AbstractPluginManager /** * Default aliases * - * @var array + * @var string[] */ protected $aliases = [ 'arrayserializable' => ArraySerializable::class, @@ -45,7 +45,7 @@ class HydratorPluginManager extends AbstractPluginManager /** * Default factory-based adapters * - * @var array + * @var string[]|callable[] */ protected $factories = [ ArraySerializable::class => InvokableFactory::class, diff --git a/src/HydratorProviderInterface.php b/src/HydratorProviderInterface.php index 9a8f014..ae22631 100644 --- a/src/HydratorProviderInterface.php +++ b/src/HydratorProviderInterface.php @@ -13,6 +13,9 @@ interface HydratorProviderInterface { /** * Provide plugin manager configuration for hydrators. + * + * @see https://docs.zendframework.com/zend-expressive/v3/features/container/config/#the-format + * @return mixed[][] */ public function getHydratorConfig() : array; } diff --git a/src/Iterator/HydratingArrayIterator.php b/src/Iterator/HydratingArrayIterator.php index b0fd508..bb2a906 100644 --- a/src/Iterator/HydratingArrayIterator.php +++ b/src/Iterator/HydratingArrayIterator.php @@ -25,6 +25,7 @@ class HydratingArrayIterator extends HydratingIteratorIterator protected $prototype; /** + * @param mixed[] $data Data being used to hydrate the $prototype * @param string|object $prototype Object, or class name to use for prototype. */ public function __construct(HydratorInterface $hydrator, array $data, $prototype) diff --git a/src/Module.php b/src/Module.php index 535e251..e6b43f2 100644 --- a/src/Module.php +++ b/src/Module.php @@ -15,6 +15,8 @@ class Module { /** * Return default zend-hydrator configuration for zend-mvc applications. + * + * @return mixed[] */ public function getConfig() : array { diff --git a/src/NamingStrategy/ArrayMapNamingStrategy.php b/src/NamingStrategy/ArrayMapNamingStrategy.php index 3695c33..8539a3d 100644 --- a/src/NamingStrategy/ArrayMapNamingStrategy.php +++ b/src/NamingStrategy/ArrayMapNamingStrategy.php @@ -22,7 +22,7 @@ final class ArrayMapNamingStrategy implements NamingStrategyInterface private $hydrationMap = []; /** - * @param array $extractionMap A map of string keys and values for + * @param string[] $extractionMap A map of string keys and values for * symmetric translation of hydrated and extracted field names. */ public function __construct(array $extractionMap) diff --git a/src/NamingStrategy/CompositeNamingStrategy.php b/src/NamingStrategy/CompositeNamingStrategy.php index 04bdfef..def3159 100644 --- a/src/NamingStrategy/CompositeNamingStrategy.php +++ b/src/NamingStrategy/CompositeNamingStrategy.php @@ -12,7 +12,7 @@ final class CompositeNamingStrategy implements NamingStrategyInterface { /** - * @var array + * @var NamingStrategyInterface[] */ private $namingStrategies = []; diff --git a/src/NamingStrategy/MapNamingStrategy.php b/src/NamingStrategy/MapNamingStrategy.php index e9f4e02..cad22fa 100644 --- a/src/NamingStrategy/MapNamingStrategy.php +++ b/src/NamingStrategy/MapNamingStrategy.php @@ -16,20 +16,20 @@ class MapNamingStrategy implements NamingStrategyInterface /** * Map for hydrate name conversion. * - * @var array + * @var string[] */ protected $mapping = []; /** * Reversed map for extract name conversion. * - * @var array + * @var string[] */ protected $reverse = []; /** - * @param array $mapping Map for name conversion on hydration - * @param array $reverse Reverse map for name conversion on extraction + * @param string[] $mapping Map for name conversion on hydration + * @param null|string[] $reverse Reverse map for name conversion on extraction */ public function __construct(array $mapping, ?array $reverse = null) { @@ -40,8 +40,8 @@ public function __construct(array $mapping, ?array $reverse = null) /** * Safely flip mapping array. * - * @param array $array Array to flip - * @return array Flipped array + * @param string[] $array Array to flip + * @return string[] Flipped array * @throws InvalidArgumentException */ protected function flipMapping(array $array) : array @@ -57,6 +57,8 @@ protected function flipMapping(array $array) : array /** * Converts the given name so that it can be extracted by the hydrator. + * + * {@inheritDoc} */ public function hydrate(string $name, ?array $data = null) : string { @@ -69,6 +71,8 @@ public function hydrate(string $name, ?array $data = null) : string /** * Converts the given name so that it can be hydrated by the hydrator. + * + * {@inheritDoc} */ public function extract(string $name, ?object $object = null) : string { diff --git a/src/NamingStrategy/NamingStrategyInterface.php b/src/NamingStrategy/NamingStrategyInterface.php index e6a7745..af4154e 100644 --- a/src/NamingStrategy/NamingStrategyInterface.php +++ b/src/NamingStrategy/NamingStrategyInterface.php @@ -17,7 +17,7 @@ interface NamingStrategyInterface /** * Converts the given name so that it can be extracted by the hydrator. * - * @param array $data (optional) The original data for context. + * @param null|mixed[] $data (optional) The original data for context. */ public function hydrate(string $name, ?array $data = null) : string; diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php b/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php new file mode 100644 index 0000000..1e05619 --- /dev/null +++ b/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php @@ -0,0 +1,29 @@ +pattern = $pattern; + $this->replacement = $replacement; + } +} diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php index 45594c8..5cc6c5d 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php @@ -18,59 +18,49 @@ final class UnderscoreToCamelCaseFilter public function filter(string $value) : string { - [$pattern, $replacement] = $this->getPatternAndReplacement( + $pcreInfo = $this->getPatternAndReplacement( // a unicode safe way of converting characters to \x00\x00 notation preg_quote('_', '#') ); - $filtered = preg_replace_callback($pattern, $replacement, $value); + $filtered = preg_replace_callback( + $pcreInfo->pattern, + $pcreInfo->replacement, + $value + ); $lcFirstFunction = $this->getLcFirstFunction(); return $lcFirstFunction($filtered); } - /** - * @return array Array with two items: the pattern to match, and the - * callback to use for replacement. - */ - private function getPatternAndReplacement(string $pregQuotedSeparator) : array + private function getPatternAndReplacement(string $pregQuotedSeparator) : PcreReplacement { return $this->hasPcreUnicodeSupport() ? $this->getUnicodePatternAndReplacement($pregQuotedSeparator) - : [ - // pattern + : new PcreReplacement( '#(' . $pregQuotedSeparator . ')([\S]{1})#', - // replacement function ($matches) { return strtoupper($matches[2]); - }, - ]; + } + ); } - /** - * @return array Array with two items: the pattern to match, and the - * callback to use for replacement. - */ - private function getUnicodePatternAndReplacement(string $pregQuotedSeparator) : array + private function getUnicodePatternAndReplacement(string $pregQuotedSeparator) : PcreReplacement { return $this->hasMbStringSupport() - ? [ - // pattern + ? new PcreReplacement( '#(' . $pregQuotedSeparator . ')(\P{Z}{1})#u', - // replacement function ($matches) { return mb_strtoupper($matches[2], 'UTF-8'); - }, - ] - : [ - // pattern + } + ) + : new PcreReplacement( '#(' . $pregQuotedSeparator . ')' . '([^\p{Z}\p{Ll}]{1}|[a-zA-Z]{1})#u', - // replacement function ($matches) { return strtoupper($matches[2]); - }, - ]; + } + ); } private function getLcFirstFunction() : callable diff --git a/src/ObjectProperty.php b/src/ObjectProperty.php index 4a3ae34..04583d9 100644 --- a/src/ObjectProperty.php +++ b/src/ObjectProperty.php @@ -20,9 +20,9 @@ class ObjectProperty extends AbstractHydrator private static $skippedPropertiesCache = []; /** - * {@inheritDoc} - * * Extracts the accessible non-static properties of the given $object. + * + * {@inheritDoc} */ public function extract(object $object) : array { @@ -51,11 +51,11 @@ public function extract(object $object) : array } /** - * {@inheritDoc} - * * Hydrate an object by populating public properties * * Hydrates an object by setting public properties of the object. + * + * {@inheritDoc} */ public function hydrate(array $data, object $object) : object { diff --git a/src/Reflection.php b/src/Reflection.php index 27bf68e..8a0aca7 100644 --- a/src/Reflection.php +++ b/src/Reflection.php @@ -23,6 +23,8 @@ class Reflection extends AbstractHydrator /** * Extract values from an object + * + * {@inheritDoc} */ public function extract(object $object) : array { @@ -42,6 +44,8 @@ public function extract(object $object) : array /** * Hydrate $object with the provided $data. + * + * {@inheritDoc} */ public function hydrate(array $data, object $object) : object { diff --git a/src/Strategy/ClosureStrategy.php b/src/Strategy/ClosureStrategy.php index a702560..9a954c4 100644 --- a/src/Strategy/ClosureStrategy.php +++ b/src/Strategy/ClosureStrategy.php @@ -63,9 +63,7 @@ public function __construct(?callable $extractFunc = null, ?callable $hydrateFun /** * Converts the given value so that it can be extracted by the hydrator. * - * @param mixed $value The original value. - * @param null|object $object The object is optionally provided as context. - * @return mixed Returns the value that should be extracted. + * {@inheritDoc} */ public function extract($value, ?object $object = null) { @@ -78,9 +76,7 @@ public function extract($value, ?object $object = null) /** * Converts the given value so that it can be hydrated by the hydrator. * - * @param mixed $value The original value. - * @param array $data The whole data is optionally provided as context. - * @return mixed Returns the value that should be hydrated. + * {@inheritDoc} */ public function hydrate($value, ?array $data = null) { diff --git a/src/Strategy/CollectionStrategy.php b/src/Strategy/CollectionStrategy.php index 7c5d627..8c3b80d 100644 --- a/src/Strategy/CollectionStrategy.php +++ b/src/Strategy/CollectionStrategy.php @@ -44,7 +44,7 @@ public function __construct(HydratorInterface $objectHydrator, string $objectCla /** * Converts the given value so that it can be extracted by the hydrator. * - * @param array $value The original value. + * @param mixed[] $value The original value. * @throws Exception\InvalidArgumentException * @return mixed Returns the value that should be extracted. */ @@ -73,9 +73,9 @@ public function extract($value, ?object $object = null) /** * Converts the given value so that it can be hydrated by the hydrator. * - * @param array $value The original value. + * @param mixed[] $value The original value. * @throws Exception\InvalidArgumentException - * @return mixed Returns the value that should be hydrated. + * @return object[] Returns the value that should be hydrated. */ public function hydrate($value, ?array $data = null) { diff --git a/src/Strategy/SerializableStrategy.php b/src/Strategy/SerializableStrategy.php index 68e340b..ccf4468 100644 --- a/src/Strategy/SerializableStrategy.php +++ b/src/Strategy/SerializableStrategy.php @@ -21,14 +21,14 @@ class SerializableStrategy implements StrategyInterface protected $serializer; /** - * @var array + * @var mixed[] */ protected $serializerOptions = []; /** * * @param string|SerializerAdapter $serializer - * @param null|iterable $serializerOptions + * @param null|mixed[] $serializerOptions */ public function __construct($serializer, ?iterable $serializerOptions = null) { @@ -41,8 +41,7 @@ public function __construct($serializer, ?iterable $serializerOptions = null) /** * Serialize the given value so that it can be extracted by the hydrator. * - * @param mixed $value The original value. - * @return mixed Returns the value that should be extracted. + * {@inheritDoc} */ public function extract($value, ?object $object = null) { @@ -53,8 +52,7 @@ public function extract($value, ?object $object = null) /** * Unserialize the given value so that it can be hydrated by the hydrator. * - * @param mixed $value The original value. - * @return mixed Returns the value that should be hydrated. + * {@inheritDoc} */ public function hydrate($value, ?array $data = null) { @@ -99,6 +97,8 @@ public function getSerializer() : SerializerAdapter /** * Set configuration options for instantiating a serializer adapter + * + * @param mixed[] $serializerOptions */ public function setSerializerOptions(iterable $serializerOptions) : void { @@ -109,6 +109,8 @@ public function setSerializerOptions(iterable $serializerOptions) : void /** * Get configuration options for instantiating a serializer adapter + * + * @return mixed[] */ public function getSerializerOptions() : array { diff --git a/src/Strategy/StrategyChain.php b/src/Strategy/StrategyChain.php index 6c19514..2039cdb 100644 --- a/src/Strategy/StrategyChain.php +++ b/src/Strategy/StrategyChain.php @@ -28,6 +28,9 @@ final class StrategyChain implements StrategyInterface */ private $hydrationStrategies; + /** + * @param StrategyInterface[] $extractionStrategies + */ public function __construct(iterable $extractionStrategies) { $extractionStrategies = ArrayUtils::iteratorToArray($extractionStrategies); From b81c4ac8771863a13938a157d9f381df891b8a72 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 27 Nov 2018 13:05:22 -0600 Subject: [PATCH 17/35] Remove unnecessary whitespace in AggregateHydrator --- src/Aggregate/AggregateHydrator.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Aggregate/AggregateHydrator.php b/src/Aggregate/AggregateHydrator.php index b0e1257..01350ae 100644 --- a/src/Aggregate/AggregateHydrator.php +++ b/src/Aggregate/AggregateHydrator.php @@ -41,9 +41,7 @@ public function add(HydratorInterface $hydrator, int $priority = self::DEFAULT_P public function extract(object $object) : array { $event = new ExtractEvent($this, $object); - $this->getEventManager()->triggerEvent($event); - return $event->getExtractedData(); } @@ -53,9 +51,7 @@ public function extract(object $object) : array public function hydrate(array $data, object $object) : object { $event = new HydrateEvent($this, $object, $data); - $this->getEventManager()->triggerEvent($event); - return $event->getHydratedObject(); } From 65a93dc90437d8bd24cbf9b88b5b933cff402e00 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 27 Nov 2018 13:07:35 -0600 Subject: [PATCH 18/35] Use the IdentityNamingStrategy as a default naming strategy if none is registered --- src/AbstractHydrator.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/AbstractHydrator.php b/src/AbstractHydrator.php index 198b773..e63d6d0 100644 --- a/src/AbstractHydrator.php +++ b/src/AbstractHydrator.php @@ -250,14 +250,15 @@ public function setNamingStrategy(NamingStrategy\NamingStrategyInterface $strate /** * Gets the naming strategy. * - * @throws Exception\DomainException if no naming strategy is registered. + * If no naming strategy is registered, registers the + * `IdentityNamingStrategy`, which acts essentially as a no-op. + * + * {@inheritDoc} */ public function getNamingStrategy() : NamingStrategy\NamingStrategyInterface { if (null === $this->namingStrategy) { - throw new Exception\DomainException( - 'Missing naming strategy; call hasNamingStrategy() to test for presence first' - ); + $this->namingStrategy = new NamingStrategy\IdentityNamingStrategy(); } return $this->namingStrategy; } From 085c710d34211741b301313be64c89302421bd6a Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 27 Nov 2018 15:44:36 -0600 Subject: [PATCH 19/35] Import all global functions --- src/AbstractHydrator.php | 2 ++ src/Aggregate/AggregateHydrator.php | 2 ++ src/Aggregate/ExtractEvent.php | 2 ++ src/ArraySerializable.php | 5 +++++ src/ClassMethods.php | 9 +++++++++ src/Filter/FilterComposite.php | 4 ++++ src/Filter/GetFilter.php | 3 +++ src/Filter/HasFilter.php | 2 ++ src/Filter/IsFilter.php | 2 ++ src/Filter/MethodMatchFilter.php | 3 +++ src/Filter/NumberOfParameterFilter.php | 2 ++ src/Filter/OptionalParametersFilter.php | 3 +++ src/HydratorPluginManager.php | 5 +++++ src/HydratorPluginManagerFactory.php | 2 ++ src/Iterator/HydratingIteratorIterator.php | 4 ++++ src/NamingStrategy/CompositeNamingStrategy.php | 2 ++ src/NamingStrategy/MapNamingStrategy.php | 6 ++++++ .../CamelCaseToUnderscoreFilter.php | 5 +++++ .../UnderscoreNamingStrategy/PcreReplacement.php | 5 +++++ .../UnderscoreNamingStrategy/StringSupportTrait.php | 2 ++ .../UnderscoreToCamelCaseFilter.php | 8 ++++++++ src/ObjectProperty.php | 4 ++++ src/Reflection.php | 2 ++ src/Strategy/BooleanStrategy.php | 8 ++++++++ src/Strategy/CollectionStrategy.php | 8 ++++++++ src/Strategy/DateTimeFormatterStrategy.php | 7 +++++++ src/Strategy/ExplodeStrategy.php | 10 ++++++++++ src/Strategy/SerializableStrategy.php | 9 ++++++++- src/Strategy/StrategyChain.php | 2 ++ 29 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/AbstractHydrator.php b/src/AbstractHydrator.php index e63d6d0..98c5c15 100644 --- a/src/AbstractHydrator.php +++ b/src/AbstractHydrator.php @@ -11,6 +11,8 @@ use ArrayObject; +use function sprintf; + abstract class AbstractHydrator implements HydratorInterface, StrategyEnabledInterface, diff --git a/src/Aggregate/AggregateHydrator.php b/src/Aggregate/AggregateHydrator.php index 01350ae..0e09b35 100644 --- a/src/Aggregate/AggregateHydrator.php +++ b/src/Aggregate/AggregateHydrator.php @@ -14,6 +14,8 @@ use Zend\EventManager\EventManagerInterface; use Zend\Hydrator\HydratorInterface; +use function get_class; + /** * Aggregate hydrator that composes multiple hydrators via events */ diff --git a/src/Aggregate/ExtractEvent.php b/src/Aggregate/ExtractEvent.php index 63f65f3..3b59b56 100644 --- a/src/Aggregate/ExtractEvent.php +++ b/src/Aggregate/ExtractEvent.php @@ -11,6 +11,8 @@ use Zend\EventManager\Event; +use function array_merge; + /** * Event triggered when the {@see AggregateHydrator} extracts * data from an object diff --git a/src/ArraySerializable.php b/src/ArraySerializable.php index f7c41b5..823b1f8 100644 --- a/src/ArraySerializable.php +++ b/src/ArraySerializable.php @@ -9,6 +9,11 @@ namespace Zend\Hydrator; +use function array_merge; +use function is_callable; +use function method_exists; +use function sprintf; + class ArraySerializable extends AbstractHydrator { /** diff --git a/src/ClassMethods.php b/src/ClassMethods.php index 3a29656..b680a75 100644 --- a/src/ClassMethods.php +++ b/src/ClassMethods.php @@ -12,6 +12,15 @@ use Traversable; use Zend\Stdlib\ArrayUtils; +use function get_class; +use function get_class_methods; +use function is_callable; +use function lcfirst; +use function method_exists; +use function property_exists; +use function strpos; +use function substr; + class ClassMethods extends AbstractHydrator implements HydratorOptionsInterface { /** diff --git a/src/Filter/FilterComposite.php b/src/Filter/FilterComposite.php index 416acf5..834f11e 100644 --- a/src/Filter/FilterComposite.php +++ b/src/Filter/FilterComposite.php @@ -12,6 +12,10 @@ use ArrayObject; use Zend\Hydrator\Exception\InvalidArgumentException; +use function array_walk; +use function is_callable; +use function sprintf; + class FilterComposite implements FilterInterface { /** diff --git a/src/Filter/GetFilter.php b/src/Filter/GetFilter.php index fbcb302..62ab4d0 100644 --- a/src/Filter/GetFilter.php +++ b/src/Filter/GetFilter.php @@ -9,6 +9,9 @@ namespace Zend\Hydrator\Filter; +use function strpos; +use function substr; + class GetFilter implements FilterInterface { public function filter(string $property) : bool diff --git a/src/Filter/HasFilter.php b/src/Filter/HasFilter.php index 80e998a..4aecd07 100644 --- a/src/Filter/HasFilter.php +++ b/src/Filter/HasFilter.php @@ -9,6 +9,8 @@ namespace Zend\Hydrator\Filter; +use function strpos; + class HasFilter implements FilterInterface { public function filter(string $property) : bool diff --git a/src/Filter/IsFilter.php b/src/Filter/IsFilter.php index 0d13fba..14ca253 100644 --- a/src/Filter/IsFilter.php +++ b/src/Filter/IsFilter.php @@ -9,6 +9,8 @@ namespace Zend\Hydrator\Filter; +use function strpos; + class IsFilter implements FilterInterface { public function filter(string $property) : bool diff --git a/src/Filter/MethodMatchFilter.php b/src/Filter/MethodMatchFilter.php index 66b6732..6032c3b 100644 --- a/src/Filter/MethodMatchFilter.php +++ b/src/Filter/MethodMatchFilter.php @@ -9,6 +9,9 @@ namespace Zend\Hydrator\Filter; +use function strpos; +use function substr; + class MethodMatchFilter implements FilterInterface { /** diff --git a/src/Filter/NumberOfParameterFilter.php b/src/Filter/NumberOfParameterFilter.php index c773647..9264bc5 100644 --- a/src/Filter/NumberOfParameterFilter.php +++ b/src/Filter/NumberOfParameterFilter.php @@ -13,6 +13,8 @@ use ReflectionMethod; use Zend\Hydrator\Exception\InvalidArgumentException; +use function sprintf; + class NumberOfParameterFilter implements FilterInterface { /** diff --git a/src/Filter/OptionalParametersFilter.php b/src/Filter/OptionalParametersFilter.php index 69c526a..48d3e4f 100644 --- a/src/Filter/OptionalParametersFilter.php +++ b/src/Filter/OptionalParametersFilter.php @@ -14,6 +14,9 @@ use ReflectionParameter; use Zend\Hydrator\Exception\InvalidArgumentException; +use function array_filter; +use function sprintf; + /** * Filter that includes methods which have no parameters or only optional parameters */ diff --git a/src/HydratorPluginManager.php b/src/HydratorPluginManager.php index 62ec3e9..2800d06 100644 --- a/src/HydratorPluginManager.php +++ b/src/HydratorPluginManager.php @@ -13,6 +13,11 @@ use Zend\ServiceManager\Exception\InvalidServiceException; use Zend\ServiceManager\Factory\InvokableFactory; +use function gettype; +use function get_class; +use function is_object; +use function sprintf; + /** * Plugin manager implementation for hydrators. * diff --git a/src/HydratorPluginManagerFactory.php b/src/HydratorPluginManagerFactory.php index 940ead8..79bac64 100644 --- a/src/HydratorPluginManagerFactory.php +++ b/src/HydratorPluginManagerFactory.php @@ -12,6 +12,8 @@ use Psr\Container\ContainerInterface; use Zend\ServiceManager\Config; +use function is_array; + class HydratorPluginManagerFactory { /** diff --git a/src/Iterator/HydratingIteratorIterator.php b/src/Iterator/HydratingIteratorIterator.php index 27a3cf3..b308a98 100644 --- a/src/Iterator/HydratingIteratorIterator.php +++ b/src/Iterator/HydratingIteratorIterator.php @@ -14,6 +14,10 @@ use Zend\Hydrator\Exception\InvalidArgumentException; use Zend\Hydrator\HydratorInterface; +use function class_exists; +use function is_object; +use function sprintf; + class HydratingIteratorIterator extends IteratorIterator implements HydratingIteratorInterface { /** diff --git a/src/NamingStrategy/CompositeNamingStrategy.php b/src/NamingStrategy/CompositeNamingStrategy.php index def3159..f65a5aa 100644 --- a/src/NamingStrategy/CompositeNamingStrategy.php +++ b/src/NamingStrategy/CompositeNamingStrategy.php @@ -9,6 +9,8 @@ namespace Zend\Hydrator\NamingStrategy; +use function array_map; + final class CompositeNamingStrategy implements NamingStrategyInterface { /** diff --git a/src/NamingStrategy/MapNamingStrategy.php b/src/NamingStrategy/MapNamingStrategy.php index cad22fa..4660fb6 100644 --- a/src/NamingStrategy/MapNamingStrategy.php +++ b/src/NamingStrategy/MapNamingStrategy.php @@ -11,6 +11,12 @@ use Zend\Hydrator\Exception\InvalidArgumentException; +use function array_flip; +use function array_key_exists; +use function array_walk; +use function is_int; +use function is_string; + class MapNamingStrategy implements NamingStrategyInterface { /** diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilter.php b/src/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilter.php index 1247aec..77ad786 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilter.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/CamelCaseToUnderscoreFilter.php @@ -9,6 +9,11 @@ namespace Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy; +use function mb_strtolower; +use function preg_replace; +use function preg_replace_callback; +use function strtolower; + /** * @internal */ diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php b/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php index 1e05619..bc539e6 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php @@ -9,6 +9,11 @@ namespace Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy; +/** + * Describe a PCRE pattern and a callback for providing a replacement. + * + * @internal + */ class PcreReplacement { /** diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/StringSupportTrait.php b/src/NamingStrategy/UnderscoreNamingStrategy/StringSupportTrait.php index 1acaa92..e4aced5 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/StringSupportTrait.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/StringSupportTrait.php @@ -11,6 +11,8 @@ use Zend\Stdlib\StringUtils; +use function extension_loaded; + /** * @internal */ diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php index 5cc6c5d..683ba87 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php @@ -9,6 +9,14 @@ namespace Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy; +use function lcfirst; +use function mb_strtolower; +use function mb_strtoupper; +use function preg_replace_callback; +use function strlen; +use function strtoupper; +use function substr; + /** * @internal */ diff --git a/src/ObjectProperty.php b/src/ObjectProperty.php index 04583d9..f5e4b62 100644 --- a/src/ObjectProperty.php +++ b/src/ObjectProperty.php @@ -12,6 +12,10 @@ use ReflectionClass; use ReflectionProperty; +use function array_fill_keys; +use function array_map; +use function get_object_vars; + class ObjectProperty extends AbstractHydrator { /** diff --git a/src/Reflection.php b/src/Reflection.php index 8a0aca7..25ed6c6 100644 --- a/src/Reflection.php +++ b/src/Reflection.php @@ -12,6 +12,8 @@ use ReflectionClass; use ReflectionProperty; +use function get_class; + class Reflection extends AbstractHydrator { /** diff --git a/src/Strategy/BooleanStrategy.php b/src/Strategy/BooleanStrategy.php index 5a1e97d..b9801ae 100644 --- a/src/Strategy/BooleanStrategy.php +++ b/src/Strategy/BooleanStrategy.php @@ -11,6 +11,14 @@ use Zend\Hydrator\Exception\InvalidArgumentException; +use function gettype; +use function get_class; +use function is_bool; +use function is_int; +use function is_object; +use function is_string; +use function sprintf; + /** * This Strategy extracts and hydrates int and string values to Boolean values */ diff --git a/src/Strategy/CollectionStrategy.php b/src/Strategy/CollectionStrategy.php index 8c3b80d..f7ceb89 100644 --- a/src/Strategy/CollectionStrategy.php +++ b/src/Strategy/CollectionStrategy.php @@ -13,6 +13,14 @@ use Zend\Hydrator\HydratorInterface; use Zend\Hydrator\Exception; +use function array_map; +use function class_exists; +use function gettype; +use function get_class; +use function is_array; +use function is_object; +use function sprintf; + class CollectionStrategy implements StrategyInterface { /** diff --git a/src/Strategy/DateTimeFormatterStrategy.php b/src/Strategy/DateTimeFormatterStrategy.php index 27ef3dc..6795da7 100644 --- a/src/Strategy/DateTimeFormatterStrategy.php +++ b/src/Strategy/DateTimeFormatterStrategy.php @@ -13,6 +13,13 @@ use DateTimeInterface; use DateTimeZone; +use function gettype; +use function get_class; +use function is_object; +use function is_string; +use function preg_replace; +use function sprintf; + final class DateTimeFormatterStrategy implements StrategyInterface { /** diff --git a/src/Strategy/ExplodeStrategy.php b/src/Strategy/ExplodeStrategy.php index 0ad217e..702638d 100644 --- a/src/Strategy/ExplodeStrategy.php +++ b/src/Strategy/ExplodeStrategy.php @@ -9,6 +9,16 @@ namespace Zend\Hydrator\Strategy; +use function explode; +use function gettype; +use function get_class; +use function implode; +use function is_array; +use function is_numeric; +use function is_object; +use function is_string; +use function sprintf; + final class ExplodeStrategy implements StrategyInterface { /** diff --git a/src/Strategy/SerializableStrategy.php b/src/Strategy/SerializableStrategy.php index ccf4468..0a1079d 100644 --- a/src/Strategy/SerializableStrategy.php +++ b/src/Strategy/SerializableStrategy.php @@ -13,6 +13,13 @@ use Zend\Serializer\Adapter\AdapterInterface as SerializerAdapter; use Zend\Serializer\Serializer as SerializerFactory; +use function gettype; +use function get_class; +use function is_array; +use function is_object; +use function is_string; +use function sprintf; + class SerializableStrategy implements StrategyInterface { /** @@ -74,7 +81,7 @@ public function setSerializer($serializer) : void '%s expects either a string serializer name or Zend\Serializer\Adapter\AdapterInterface instance; ' . 'received "%s"', __METHOD__, - (is_object($serializer) ? get_class($serializer) : gettype($serializer)) + is_object($serializer) ? get_class($serializer) : gettype($serializer) )); } $this->serializer = $serializer; diff --git a/src/Strategy/StrategyChain.php b/src/Strategy/StrategyChain.php index 2039cdb..681a860 100644 --- a/src/Strategy/StrategyChain.php +++ b/src/Strategy/StrategyChain.php @@ -12,6 +12,8 @@ use Traversable; use Zend\Stdlib\ArrayUtils; +use function array_map; + final class StrategyChain implements StrategyInterface { /** From e3fafb1289b4efaacca7310cc1bb4efc066e6dc7 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 08:11:30 -0600 Subject: [PATCH 20/35] Provide documentation for the HydratorPluginManagerFactory Since it no longer implements `FactoryInterface`, the `__invoke()` method had nothing to inherit. This was a good time to document what it uses. --- src/HydratorPluginManagerFactory.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/HydratorPluginManagerFactory.php b/src/HydratorPluginManagerFactory.php index 79bac64..d398e4f 100644 --- a/src/HydratorPluginManagerFactory.php +++ b/src/HydratorPluginManagerFactory.php @@ -17,7 +17,14 @@ class HydratorPluginManagerFactory { /** - * {@inheritDoc} + * Create a HydratorPluginManager instance. + * + * If the `config` service is available, and the top-level key `hydrators` + * exists and is an array, that value will be used to configure the plugin + * manager. In such cases, the array should follow standard container + * configuration. + * + * @see https://docs.zendframework.com/zend-expressive/v3/features/container/config/ */ public function __invoke(ContainerInterface $container, string $name, array $options = []) : HydratorPluginManager { From 03c75991a9bbb9ef64b5b9899e64caa9406c8c93 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 08:13:15 -0600 Subject: [PATCH 21/35] Removes unused import in StrategyChain --- src/Strategy/StrategyChain.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Strategy/StrategyChain.php b/src/Strategy/StrategyChain.php index 681a860..1e982b3 100644 --- a/src/Strategy/StrategyChain.php +++ b/src/Strategy/StrategyChain.php @@ -9,7 +9,6 @@ namespace Zend\Hydrator\Strategy; -use Traversable; use Zend\Stdlib\ArrayUtils; use function array_map; From 87d074b6965ea994ac6beacd82700817f0b4e55e Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 08:13:47 -0600 Subject: [PATCH 22/35] Note that the `$hydrator` property is nullable in HydratorAwareTrait --- src/HydratorAwareTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HydratorAwareTrait.php b/src/HydratorAwareTrait.php index afb9500..f7451fe 100644 --- a/src/HydratorAwareTrait.php +++ b/src/HydratorAwareTrait.php @@ -14,7 +14,7 @@ trait HydratorAwareTrait /** * Hydrator instance * - * @var HydratorInterface + * @var null|HydratorInterface */ protected $hydrator = null; From 5e858414e96a25b077b47deb9faeff175617f288 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 08:36:41 -0600 Subject: [PATCH 23/35] Final CS pass - Found a handful of global functions that had not yet been imported. - Performed some assignment alignments. - Simplified several conditionals to ternary operations. - Removed parens where they were unnecessary. --- src/AbstractHydrator.php | 30 ++++++++----------- src/ClassMethods.php | 1 + src/DelegatingHydrator.php | 2 ++ src/Filter/FilterComposite.php | 1 + src/HydratorPluginManager.php | 2 +- src/HydratorPluginManagerFactory.php | 2 +- src/Module.php | 4 +-- src/NamingStrategy/ArrayMapNamingStrategy.php | 2 ++ .../PcreReplacement.php | 2 +- .../UnderscoreToCamelCaseFilter.php | 1 + src/ObjectProperty.php | 1 + src/Strategy/SerializableStrategy.php | 1 + 12 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/AbstractHydrator.php b/src/AbstractHydrator.php index 98c5c15..e343523 100644 --- a/src/AbstractHydrator.php +++ b/src/AbstractHydrator.php @@ -130,11 +130,9 @@ public function removeStrategy(string $name) : void */ public function extractValue(string $name, $value, ?object $object = null) { - if ($this->hasStrategy($name)) { - $strategy = $this->getStrategy($name); - $value = $strategy->extract($value, $object); - } - return $value; + return $this->hasStrategy($name) + ? $this->getStrategy($name)->extract($value, $object) + : $value; } /** @@ -147,11 +145,9 @@ public function extractValue(string $name, $value, ?object $object = null) */ public function hydrateValue(string $name, $value, ?array $data = null) { - if ($this->hasStrategy($name)) { - $strategy = $this->getStrategy($name); - $value = $strategy->hydrate($value, $data); - } - return $value; + return $this->hasStrategy($name) + ? $this->getStrategy($name)->hydrate($value, $data) + : $value; } /** @@ -163,10 +159,9 @@ public function hydrateValue(string $name, $value, ?array $data = null) */ public function extractName(string $name, ?object $object = null) { - if ($this->hasNamingStrategy()) { - $name = $this->getNamingStrategy()->extract($name, $object); - } - return $name; + return $this->hasNamingStrategy() + ? $this->getNamingStrategy()->extract($name, $object) + : $name; } /** @@ -177,10 +172,9 @@ public function extractName(string $name, ?object $object = null) */ public function hydrateName(string $name, ?array $data = null) : string { - if ($this->hasNamingStrategy()) { - $name = $this->getNamingStrategy()->hydrate($name, $data); - } - return $name; + return $this->hasNamingStrategy() + ? $this->getNamingStrategy()->hydrate($name, $data) + : $name; } /** diff --git a/src/ClassMethods.php b/src/ClassMethods.php index b680a75..b7ba4ed 100644 --- a/src/ClassMethods.php +++ b/src/ClassMethods.php @@ -20,6 +20,7 @@ use function property_exists; use function strpos; use function substr; +use function ucfirst; class ClassMethods extends AbstractHydrator implements HydratorOptionsInterface { diff --git a/src/DelegatingHydrator.php b/src/DelegatingHydrator.php index 72cf029..5a5bf2b 100644 --- a/src/DelegatingHydrator.php +++ b/src/DelegatingHydrator.php @@ -11,6 +11,8 @@ use Psr\Container\ContainerInterface; +use function get_class; + class DelegatingHydrator implements HydratorInterface { /** diff --git a/src/Filter/FilterComposite.php b/src/Filter/FilterComposite.php index 834f11e..6bfd5c5 100644 --- a/src/Filter/FilterComposite.php +++ b/src/Filter/FilterComposite.php @@ -13,6 +13,7 @@ use Zend\Hydrator\Exception\InvalidArgumentException; use function array_walk; +use function count; use function is_callable; use function sprintf; diff --git a/src/HydratorPluginManager.php b/src/HydratorPluginManager.php index 2800d06..ca222d7 100644 --- a/src/HydratorPluginManager.php +++ b/src/HydratorPluginManager.php @@ -96,7 +96,7 @@ public function validate($instance) throw new InvalidServiceException(sprintf( 'Plugin of type %s is invalid; must implement %s', - (is_object($instance) ? get_class($instance) : gettype($instance)), + is_object($instance) ? get_class($instance) : gettype($instance), HydratorInterface::class )); } diff --git a/src/HydratorPluginManagerFactory.php b/src/HydratorPluginManagerFactory.php index d398e4f..46d2bb3 100644 --- a/src/HydratorPluginManagerFactory.php +++ b/src/HydratorPluginManagerFactory.php @@ -28,7 +28,7 @@ class HydratorPluginManagerFactory */ public function __invoke(ContainerInterface $container, string $name, array $options = []) : HydratorPluginManager { - $pluginManager = new HydratorPluginManager($container, $options ?: []); + $pluginManager = new HydratorPluginManager($container, $options); // If this is in a zend-mvc application, the ServiceListener will inject // merged configuration during bootstrap. diff --git a/src/Module.php b/src/Module.php index e6b43f2..5d3f40b 100644 --- a/src/Module.php +++ b/src/Module.php @@ -32,8 +32,8 @@ public function getConfig() : array */ public function init(ModuleManager $moduleManager) : void { - $event = $moduleManager->getEvent(); - $container = $event->getParam('ServiceManager'); + $event = $moduleManager->getEvent(); + $container = $event->getParam('ServiceManager'); $serviceListener = $container->get('ServiceListener'); $serviceListener->addServiceManager( diff --git a/src/NamingStrategy/ArrayMapNamingStrategy.php b/src/NamingStrategy/ArrayMapNamingStrategy.php index 8539a3d..96e7c6f 100644 --- a/src/NamingStrategy/ArrayMapNamingStrategy.php +++ b/src/NamingStrategy/ArrayMapNamingStrategy.php @@ -9,6 +9,8 @@ namespace Zend\Hydrator\NamingStrategy; +use function array_flip; + final class ArrayMapNamingStrategy implements NamingStrategyInterface { /** diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php b/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php index bc539e6..54a741d 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/PcreReplacement.php @@ -28,7 +28,7 @@ class PcreReplacement public function __construct(string $pattern, callable $replacement) { - $this->pattern = $pattern; + $this->pattern = $pattern; $this->replacement = $replacement; } } diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php index 683ba87..7a87439 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php @@ -12,6 +12,7 @@ use function lcfirst; use function mb_strtolower; use function mb_strtoupper; +use function preg_quote; use function preg_replace_callback; use function strlen; use function strtoupper; diff --git a/src/ObjectProperty.php b/src/ObjectProperty.php index f5e4b62..3ffcdb5 100644 --- a/src/ObjectProperty.php +++ b/src/ObjectProperty.php @@ -14,6 +14,7 @@ use function array_fill_keys; use function array_map; +use function get_class; use function get_object_vars; class ObjectProperty extends AbstractHydrator diff --git a/src/Strategy/SerializableStrategy.php b/src/Strategy/SerializableStrategy.php index 0a1079d..0284ca4 100644 --- a/src/Strategy/SerializableStrategy.php +++ b/src/Strategy/SerializableStrategy.php @@ -18,6 +18,7 @@ use function is_array; use function is_object; use function is_string; +use function iterator_to_array; use function sprintf; class SerializableStrategy implements StrategyInterface From deeaeefafb3e6f1278fabffe4232dddfe6eff884 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 08:40:14 -0600 Subject: [PATCH 24/35] Use PHP 7.3, not nightly, in test matrix - Also use the lowest/locked/latest strategy. --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b96d746..d8d853f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,13 @@ matrix: - php: 7.2 env: - DEPS=latest - - php: nightly + - php: 7.3 + env: + - DEPS=lowest + - php: 7.3 + env: + - DEPS=locked + - php: 7.3 env: - DEPS=latest From 4850813bbe5ce834f22a2fe77f796efd8bbac050 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 09:36:25 -0600 Subject: [PATCH 25/35] Remove unnecessary ternary when setting `$explodeLimit` Since we have nullables now, we know it will be exactly null or an integer. --- src/Strategy/ExplodeStrategy.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Strategy/ExplodeStrategy.php b/src/Strategy/ExplodeStrategy.php index 702638d..28648c2 100644 --- a/src/Strategy/ExplodeStrategy.php +++ b/src/Strategy/ExplodeStrategy.php @@ -40,7 +40,7 @@ final class ExplodeStrategy implements StrategyInterface public function __construct(string $delimiter = ',', ?int $explodeLimit = null) { $this->setValueDelimiter($delimiter); - $this->explodeLimit = ($explodeLimit === null) ? null : $explodeLimit; + $this->explodeLimit = $explodeLimit; } /** From b6115d7610ca82929aaf06e4c40f01b1a6e5ca5d Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 09:36:39 -0600 Subject: [PATCH 26/35] DomainException already implements ExceptionInterface --- src/Exception/InvalidCallbackException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Exception/InvalidCallbackException.php b/src/Exception/InvalidCallbackException.php index ca8a81c..3b22f6f 100644 --- a/src/Exception/InvalidCallbackException.php +++ b/src/Exception/InvalidCallbackException.php @@ -12,6 +12,6 @@ /** * Invalid callback exception */ -class InvalidCallbackException extends DomainException implements ExceptionInterface +class InvalidCallbackException extends DomainException { } From 800dc12b2bb7f4d8d631404648656b002754555c Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 09:37:34 -0600 Subject: [PATCH 27/35] Remove unnecessary property declarations in HydratingArrayIterator They were defined in the parent `HydratingIteratorIterator` --- src/Iterator/HydratingArrayIterator.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Iterator/HydratingArrayIterator.php b/src/Iterator/HydratingArrayIterator.php index bb2a906..9efca6e 100644 --- a/src/Iterator/HydratingArrayIterator.php +++ b/src/Iterator/HydratingArrayIterator.php @@ -14,16 +14,6 @@ class HydratingArrayIterator extends HydratingIteratorIterator { - /** - * @var HydratorInterface - */ - protected $hydrator; - - /** - * @var object - */ - protected $prototype; - /** * @param mixed[] $data Data being used to hydrate the $prototype * @param string|object $prototype Object, or class name to use for prototype. From 7f62d77072b7f24ccb40b466e0f6f1f275a285d4 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 09:39:01 -0600 Subject: [PATCH 28/35] Use null coalesce instead of ternary within ArrayMapNamingStrategy --- src/NamingStrategy/ArrayMapNamingStrategy.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NamingStrategy/ArrayMapNamingStrategy.php b/src/NamingStrategy/ArrayMapNamingStrategy.php index 96e7c6f..5693baa 100644 --- a/src/NamingStrategy/ArrayMapNamingStrategy.php +++ b/src/NamingStrategy/ArrayMapNamingStrategy.php @@ -38,7 +38,7 @@ public function __construct(array $extractionMap) */ public function hydrate(string $name, ?array $data = null) : string { - return isset($this->hydrationMap[$name]) ? $this->hydrationMap[$name] : $name; + return $this->hydrationMap[$name] ?? $name; } /** @@ -46,6 +46,6 @@ public function hydrate(string $name, ?array $data = null) : string */ public function extract(string $name, ?object $object = null) : string { - return isset($this->extractionMap[$name]) ? $this->extractionMap[$name] : $name; + return $this->extractionMap[$name] ?? $name; } } From 125194c10dabf60e2ccfc4290a52dea25c7c2e53 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 09:40:04 -0600 Subject: [PATCH 29/35] Use null coalesce within CompositeNamingStrategy instead of ternary --- src/NamingStrategy/CompositeNamingStrategy.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/NamingStrategy/CompositeNamingStrategy.php b/src/NamingStrategy/CompositeNamingStrategy.php index f65a5aa..74fc471 100644 --- a/src/NamingStrategy/CompositeNamingStrategy.php +++ b/src/NamingStrategy/CompositeNamingStrategy.php @@ -45,10 +45,7 @@ function (NamingStrategyInterface $strategy) { */ public function extract(string $name, ?object $object = null) : string { - $strategy = isset($this->namingStrategies[$name]) - ? $this->namingStrategies[$name] - : $this->defaultNamingStrategy; - + $strategy = $this->namingStrategies[$name] ?? $this->defaultNamingStrategy; return $strategy->extract($name); } @@ -57,10 +54,7 @@ public function extract(string $name, ?object $object = null) : string */ public function hydrate(string $name, ?array $data = null) : string { - $strategy = isset($this->namingStrategies[$name]) - ? $this->namingStrategies[$name] - : $this->defaultNamingStrategy; - + $strategy = $this->namingStrategies[$name] ?? $this->defaultNamingStrategy; return $strategy->hydrate($name); } } From 33e18e1b7d924e2dc6c230b7e05679720feca84b Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 09:41:51 -0600 Subject: [PATCH 30/35] Remove third argument to `substr` within UnderscoreToCamelCaseFilter We were capturing to the end of the string already. --- .../UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php index 7a87439..adf5ed5 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php @@ -76,8 +76,7 @@ private function getLcFirstFunction() : callable { return $this->hasMbStringSupport() ? function ($value) { - return mb_strtolower($value[0], 'UTF-8') - . substr($value, 1, strlen($value) - 1); + return mb_strtolower($value[0], 'UTF-8') . substr($value, 1); } : 'lcfirst'; } From b0babc41fe9ef46a6bcf8202fb04dff995a38875 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 12:54:19 -0600 Subject: [PATCH 31/35] Incorporate feedback from @webimpress Incorporates all feedback except ensuring license docblocks are up-to-date with regards to format. --- src/ConfigProvider.php | 6 +++--- src/DelegatingHydratorFactory.php | 3 --- src/Filter/MethodMatchFilter.php | 8 +++++--- src/Filter/NumberOfParameterFilter.php | 3 ++- src/Filter/OptionalParametersFilter.php | 3 +++ src/HydratorAwareTrait.php | 2 +- src/Iterator/HydratingIteratorIterator.php | 3 +++ src/Module.php | 6 +++--- src/NamingStrategy/CompositeNamingStrategy.php | 1 - src/NamingStrategy/NamingStrategyInterface.php | 4 ++-- src/NamingStrategy/UnderscoreNamingStrategy.php | 6 ------ .../UnderscoreToCamelCaseFilter.php | 1 - src/Strategy/ClosureStrategy.php | 4 ++-- src/Strategy/DateTimeFormatterStrategy.php | 7 ++++++- src/Strategy/ExplodeStrategy.php | 1 + src/Strategy/SerializableStrategy.php | 1 - src/Strategy/StrategyChain.php | 1 + 17 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index e2d72a1..c626356 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -1,8 +1,8 @@ method = $method; + $this->method = $method; $this->exclude = $exclude; } diff --git a/src/Filter/NumberOfParameterFilter.php b/src/Filter/NumberOfParameterFilter.php index 9264bc5..32323ae 100644 --- a/src/Filter/NumberOfParameterFilter.php +++ b/src/Filter/NumberOfParameterFilter.php @@ -19,9 +19,10 @@ class NumberOfParameterFilter implements FilterInterface { /** * The number of parameters being accepted + * * @var int */ - protected $numberOfParameters = null; + protected $numberOfParameters; /** * @param int $numberOfParameters Number of accepted parameters diff --git a/src/Filter/OptionalParametersFilter.php b/src/Filter/OptionalParametersFilter.php index 48d3e4f..f65f778 100644 --- a/src/Filter/OptionalParametersFilter.php +++ b/src/Filter/OptionalParametersFilter.php @@ -33,6 +33,9 @@ class OptionalParametersFilter implements FilterInterface /** * {@inheritDoc} + * + * @throws InvalidArgumentException if reflection fails due to the method + * not existing. */ public function filter(string $property) : bool { diff --git a/src/HydratorAwareTrait.php b/src/HydratorAwareTrait.php index f7451fe..939b8fc 100644 --- a/src/HydratorAwareTrait.php +++ b/src/HydratorAwareTrait.php @@ -16,7 +16,7 @@ trait HydratorAwareTrait * * @var null|HydratorInterface */ - protected $hydrator = null; + protected $hydrator; /** * Set hydrator diff --git a/src/Iterator/HydratingIteratorIterator.php b/src/Iterator/HydratingIteratorIterator.php index b308a98..f53bf32 100644 --- a/src/Iterator/HydratingIteratorIterator.php +++ b/src/Iterator/HydratingIteratorIterator.php @@ -44,6 +44,9 @@ public function __construct(HydratorInterface $hydrator, Iterator $data, $protot /** * @inheritdoc + * + * @throws InvalidArgumentException if $prototype is a string, but refers to + * a non-existent class. */ public function setPrototype($prototype) : void { diff --git a/src/Module.php b/src/Module.php index 5d3f40b..1dece20 100644 --- a/src/Module.php +++ b/src/Module.php @@ -1,8 +1,8 @@ getCamelCaseToUnderscoreFilter()->filter($name); } - /** - * @return UnderscoreToCamelCaseFilter - */ private function getUnderscoreToCamelCaseFilter() : UnderscoreToCamelCaseFilter { if (! static::$underscoreToCamelCaseFilter) { @@ -52,9 +49,6 @@ private function getUnderscoreToCamelCaseFilter() : UnderscoreToCamelCaseFilter return static::$underscoreToCamelCaseFilter; } - /** - * @return CamelCaseToUnderscoreFilter - */ private function getCamelCaseToUnderscoreFilter() : CamelCaseToUnderscoreFilter { if (! static::$camelCaseToUnderscoreFilter) { diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php index adf5ed5..c1bc4ad 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php @@ -14,7 +14,6 @@ use function mb_strtoupper; use function preg_quote; use function preg_replace_callback; -use function strlen; use function strtoupper; use function substr; diff --git a/src/Strategy/ClosureStrategy.php b/src/Strategy/ClosureStrategy.php index 9a954c4..c33ec3e 100644 --- a/src/Strategy/ClosureStrategy.php +++ b/src/Strategy/ClosureStrategy.php @@ -22,7 +22,7 @@ class ClosureStrategy implements StrategyInterface * * @var null|callable */ - protected $extractFunc = null; + protected $extractFunc; /** * Function, used in hydrate method, default: @@ -35,7 +35,7 @@ class ClosureStrategy implements StrategyInterface * * @var null|callable */ - protected $hydrateFunc = null; + protected $hydrateFunc; /** * You can describe how your values will extract and hydrate, like this: diff --git a/src/Strategy/DateTimeFormatterStrategy.php b/src/Strategy/DateTimeFormatterStrategy.php index 6795da7..af6015b 100644 --- a/src/Strategy/DateTimeFormatterStrategy.php +++ b/src/Strategy/DateTimeFormatterStrategy.php @@ -55,6 +55,7 @@ final class DateTimeFormatterStrategy implements StrategyInterface /** * @param bool $dateTimeFallback try to parse with DateTime when createFromFormat fails + * @throws Exception\InvalidArgumentException for invalid $format values */ public function __construct( string $format = DateTime::RFC3339, @@ -82,7 +83,9 @@ public function __construct( * Converts to date time string * * @param mixed|DateTimeInterface $value - * @return mixed|string + * @return mixed|string If a non-DateTimeInterface $value is provided, it + * will be returned unmodified; otherwise, it will be extracted to a + * string. */ public function extract($value, ?object $object = null) { @@ -100,6 +103,8 @@ public function extract($value, ?object $object = null) * * @param mixed|string $value * @return mixed|DateTimeInterface + * @throws Exception\InvalidArgumentException if $value is not null, not a + * string, nor a DateTimeInterface. */ public function hydrate($value, ?array $data = null) { diff --git a/src/Strategy/ExplodeStrategy.php b/src/Strategy/ExplodeStrategy.php index 28648c2..8e51e5d 100644 --- a/src/Strategy/ExplodeStrategy.php +++ b/src/Strategy/ExplodeStrategy.php @@ -92,6 +92,7 @@ public function hydrate($value, ?array $data = null) * * @param string[] $value The original value. * @return string|null + * @throws Exception\InvalidArgumentException for non-array $value values */ public function extract($value, ?object $object = null) { diff --git a/src/Strategy/SerializableStrategy.php b/src/Strategy/SerializableStrategy.php index 0284ca4..a65346d 100644 --- a/src/Strategy/SerializableStrategy.php +++ b/src/Strategy/SerializableStrategy.php @@ -34,7 +34,6 @@ class SerializableStrategy implements StrategyInterface protected $serializerOptions = []; /** - * * @param string|SerializerAdapter $serializer * @param null|mixed[] $serializerOptions */ diff --git a/src/Strategy/StrategyChain.php b/src/Strategy/StrategyChain.php index 1e982b3..2c7e5ff 100644 --- a/src/Strategy/StrategyChain.php +++ b/src/Strategy/StrategyChain.php @@ -12,6 +12,7 @@ use Zend\Stdlib\ArrayUtils; use function array_map; +use function array_reverse; final class StrategyChain implements StrategyInterface { From 046a2eab3ce479ac46c37394007ce4eeb1a151bd Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 13:37:29 -0600 Subject: [PATCH 32/35] Correct typehint for NamingStrategyInterface::extract `$object` argument --- src/NamingStrategy/NamingStrategyInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NamingStrategy/NamingStrategyInterface.php b/src/NamingStrategy/NamingStrategyInterface.php index e2adfa0..17beb64 100644 --- a/src/NamingStrategy/NamingStrategyInterface.php +++ b/src/NamingStrategy/NamingStrategyInterface.php @@ -24,7 +24,7 @@ public function hydrate(string $name, ?array $data = null) : string; /** * Converts the given name so that it can be hydrated by the hydrator. * - * @param object $object The original object for context. + * @param null|object $object The original object for context. */ public function extract(string $name, ?object $object = null) : string; } From 1f2e3f7a3893783fb27cffe9f3e14eb01a4587a2 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 13:48:54 -0600 Subject: [PATCH 33/35] Updates license docblocks for all test classes and assets --- test/Aggregate/AggregateHydratorFunctionalTest.php | 8 +++----- test/Aggregate/AggregateHydratorTest.php | 8 +++----- test/Aggregate/ExtractEventTest.php | 8 +++----- test/Aggregate/HydrateEventTest.php | 8 +++----- test/Aggregate/HydratorListenerTest.php | 8 +++----- test/ArraySerializableTest.php | 9 +++------ test/ClassMethodsTest.php | 8 +++----- test/DelegatingHydratorFactoryTest.php | 8 +++----- test/DelegatingHydratorTest.php | 8 +++----- test/Filter/FilterCompositeTest.php | 8 +++----- test/Filter/NumberOfParameterFilterTest.php | 8 +++----- test/Filter/OptionalParametersFilterTest.php | 8 +++----- test/HydratorAwareTraitTest.php | 9 +++------ test/HydratorClosureStrategyTest.php | 8 +++----- test/HydratorObjectPropertyTest.php | 8 +++----- test/HydratorPluginManagerCompatibilityTest.php | 8 +++----- test/HydratorPluginManagerFactoryTest.php | 6 +++--- test/HydratorStrategyTest.php | 8 +++----- test/HydratorTest.php | 8 +++----- test/HydratorTestTrait.php | 8 +++----- test/Iterator/HydratingArrayIteratorTest.php | 8 +++----- test/Iterator/HydratingIteratorIteratorTest.php | 8 +++----- test/NamingStrategy/ArrayMapNamingStrategyTest.php | 8 +++----- .../NamingStrategy/CompositeNamingStrategyTest.php | 8 +++----- test/NamingStrategy/IdentityNamingStrategyTest.php | 8 +++----- test/NamingStrategy/MapNamingStrategyTest.php | 8 +++----- .../UnderscoreNamingStrategyTest.php | 8 +++----- test/ObjectPropertyTest.php | 8 +++----- test/ReflectionTest.php | 8 +++----- test/Strategy/BooleanStrategyTest.php | 8 +++----- test/Strategy/DateTimeFormatterStrategyTest.php | 8 +++----- test/Strategy/ExplodeStrategyTest.php | 8 +++----- test/Strategy/SerializableStrategyTest.php | 8 +++----- test/Strategy/StrategyChainTest.php | 8 +++----- test/TestAsset/AggregateObject.php | 8 +++----- test/TestAsset/ArrayObjectIterator.php | 8 +++----- test/TestAsset/ArrayObjectObjectVars.php | 8 +++----- test/TestAsset/ArraySerializable.php | 14 ++++++-------- test/TestAsset/ArraySerializableNoGetArrayCopy.php | 8 +++----- test/TestAsset/ClassMethodsCamelCase.php | 8 +++----- test/TestAsset/ClassMethodsCamelCaseMissing.php | 8 +++----- .../ClassMethodsFilterProviderInterface.php | 8 +++----- test/TestAsset/ClassMethodsInvalidParameter.php | 8 +++----- test/TestAsset/ClassMethodsMagicMethodSetter.php | 8 +++----- test/TestAsset/ClassMethodsOptionalParameters.php | 8 +++----- test/TestAsset/ClassMethodsProtectedSetter.php | 8 +++----- test/TestAsset/ClassMethodsTitleCase.php | 8 +++----- test/TestAsset/ClassMethodsUnderscore.php | 8 +++----- test/TestAsset/ClassWithPublicStaticProperties.php | 8 +++----- test/TestAsset/HydratorClosureStrategyEntity.php | 8 +++----- test/TestAsset/HydratorStrategy.php | 8 +++----- test/TestAsset/HydratorStrategyContextAware.php | 8 +++----- test/TestAsset/HydratorStrategyEntityB.php | 8 +++----- test/TestAsset/ObjectProperty.php | 8 +++----- test/TestAsset/Reflection.php | 8 +++----- test/TestAsset/ReflectionFilter.php | 8 +++----- test/TestAsset/SimpleEntity.php | 8 +++----- 57 files changed, 174 insertions(+), 288 deletions(-) diff --git a/test/Aggregate/AggregateHydratorFunctionalTest.php b/test/Aggregate/AggregateHydratorFunctionalTest.php index e974195..5fc5435 100644 --- a/test/Aggregate/AggregateHydratorFunctionalTest.php +++ b/test/Aggregate/AggregateHydratorFunctionalTest.php @@ -1,10 +1,8 @@ data = [ - "foo" => "bar", - "bar" => "foo", + "foo" => "bar", + "bar" => "foo", "blubb" => "baz", - "quo" => "blubb" + "quo" => "blubb" ]; } diff --git a/test/TestAsset/ArraySerializableNoGetArrayCopy.php b/test/TestAsset/ArraySerializableNoGetArrayCopy.php index a2d6d4e..3e0f1d8 100644 --- a/test/TestAsset/ArraySerializableNoGetArrayCopy.php +++ b/test/TestAsset/ArraySerializableNoGetArrayCopy.php @@ -1,10 +1,8 @@ Date: Wed, 28 Nov 2018 15:46:56 -0600 Subject: [PATCH 34/35] Remove `lcfirst` import in UnderscoreToCamelCaseFilter Referencing a function by string automatically assumes it is globally qualified. --- .../UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php index c1bc4ad..c8803c0 100644 --- a/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php +++ b/src/NamingStrategy/UnderscoreNamingStrategy/UnderscoreToCamelCaseFilter.php @@ -9,7 +9,6 @@ namespace Zend\Hydrator\NamingStrategy\UnderscoreNamingStrategy; -use function lcfirst; use function mb_strtolower; use function mb_strtoupper; use function preg_quote; From b7b5b9a9a1a59644c4e258b94714f07d510614e6 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 28 Nov 2018 15:49:04 -0600 Subject: [PATCH 35/35] Sorting of imports when get_class/gettype are used In English, we typically sort such that letters have precedence over symbols; however, in computers, this is evidently exactly the opposite. Since computers do the automation, we go with what they do. *sigh* --- src/HydratorPluginManager.php | 2 +- src/Strategy/BooleanStrategy.php | 2 +- src/Strategy/CollectionStrategy.php | 2 +- src/Strategy/DateTimeFormatterStrategy.php | 2 +- src/Strategy/ExplodeStrategy.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/HydratorPluginManager.php b/src/HydratorPluginManager.php index ca222d7..4024a82 100644 --- a/src/HydratorPluginManager.php +++ b/src/HydratorPluginManager.php @@ -13,8 +13,8 @@ use Zend\ServiceManager\Exception\InvalidServiceException; use Zend\ServiceManager\Factory\InvokableFactory; -use function gettype; use function get_class; +use function gettype; use function is_object; use function sprintf; diff --git a/src/Strategy/BooleanStrategy.php b/src/Strategy/BooleanStrategy.php index b9801ae..6ba65a0 100644 --- a/src/Strategy/BooleanStrategy.php +++ b/src/Strategy/BooleanStrategy.php @@ -11,8 +11,8 @@ use Zend\Hydrator\Exception\InvalidArgumentException; -use function gettype; use function get_class; +use function gettype; use function is_bool; use function is_int; use function is_object; diff --git a/src/Strategy/CollectionStrategy.php b/src/Strategy/CollectionStrategy.php index f7ceb89..885aa42 100644 --- a/src/Strategy/CollectionStrategy.php +++ b/src/Strategy/CollectionStrategy.php @@ -15,8 +15,8 @@ use function array_map; use function class_exists; -use function gettype; use function get_class; +use function gettype; use function is_array; use function is_object; use function sprintf; diff --git a/src/Strategy/DateTimeFormatterStrategy.php b/src/Strategy/DateTimeFormatterStrategy.php index af6015b..9a94e16 100644 --- a/src/Strategy/DateTimeFormatterStrategy.php +++ b/src/Strategy/DateTimeFormatterStrategy.php @@ -13,8 +13,8 @@ use DateTimeInterface; use DateTimeZone; -use function gettype; use function get_class; +use function gettype; use function is_object; use function is_string; use function preg_replace; diff --git a/src/Strategy/ExplodeStrategy.php b/src/Strategy/ExplodeStrategy.php index 8e51e5d..cfa1375 100644 --- a/src/Strategy/ExplodeStrategy.php +++ b/src/Strategy/ExplodeStrategy.php @@ -10,8 +10,8 @@ namespace Zend\Hydrator\Strategy; use function explode; -use function gettype; use function get_class; +use function gettype; use function implode; use function is_array; use function is_numeric;