From d3df4c399d3a56f0235afb1ef560656881812565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Tue, 12 Jun 2018 00:23:07 +0100 Subject: [PATCH 1/6] Make DeepCopy API final and immutable Closes #68 --- src/DeepCopy/DeepCopy.php | 52 ++++++++++++----------- src/DeepCopy/Exception/CloneException.php | 9 ++++ src/DeepCopy/deep_copy.php | 20 ++++++--- 3 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/DeepCopy/DeepCopy.php b/src/DeepCopy/DeepCopy.php index b9c2075..a3ea9fc 100644 --- a/src/DeepCopy/DeepCopy.php +++ b/src/DeepCopy/DeepCopy.php @@ -44,31 +44,38 @@ final class DeepCopy private $useCloneMethod; /** - * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used - * instead of the regular deep cloning. + * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will + * be used instead of the regular deep cloning. + * @param bool $skipUncloneable If enabled, will not throw an exception when coming across an uncloneable + * property. + * @param Array List of filter-matcher pairs + * @param Array List of type filter-matcher pairs */ - public function __construct(bool $useCloneMethod = false) - { + public function __construct( + bool $useCloneMethod = false, + bool $skipUncloneable = true, + array $filters = [], + array $typeFilters = [] + ) { $this->useCloneMethod = $useCloneMethod; - $this->addTypeFilter( + $filters[] = [ new DateIntervalFilter(), new TypeMatcher(DateInterval::class) - ); - $this->addTypeFilter( + ]; + + foreach ($filters as [$filter, $matcher]) { + $this->addFilter($filter, $matcher); + } + + $typeFilters[] = [ new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class) - ); - } + ]; - /** - * If enabled, will not throw an exception when coming across an uncloneable property. - */ - public function skipUncloneable(bool $skipUncloneable = true): self - { - $this->skipUncloneable = $skipUncloneable; - - return $this; + foreach ($typeFilters as [$filter, $matcher]) { + $this->addTypeFilter($filter, $matcher); + } } /** @@ -85,12 +92,12 @@ public function copy($value) return $this->recursiveCopy($value); } - public function addFilter(Filter $filter, Matcher $matcher): void + private function addFilter(Filter $filter, Matcher $matcher): void { $this->filters[] = [$matcher, $filter]; } - public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher): void + private function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher): void { $this->typeFilters[] = [$matcher, $filter]; } @@ -146,12 +153,7 @@ private function copyObject(object $object): object return $object; } - throw new CloneException( - sprintf( - 'The class "%s" is not cloneable.', - $reflectedObject->getName() - ) - ); + throw CloneException::unclonableClass($reflectedObject->getName()); } $newObject = clone $object; diff --git a/src/DeepCopy/Exception/CloneException.php b/src/DeepCopy/Exception/CloneException.php index d7a2a27..7d2c80c 100644 --- a/src/DeepCopy/Exception/CloneException.php +++ b/src/DeepCopy/Exception/CloneException.php @@ -6,4 +6,13 @@ class CloneException extends UnexpectedValueException { + final public static function unclonableClass(string $class): self + { + return new self( + sprintf( + 'The class "%s" is not cloneable.', + $class + ) + ); + } } diff --git a/src/DeepCopy/deep_copy.php b/src/DeepCopy/deep_copy.php index 030a54f..0f58d44 100644 --- a/src/DeepCopy/deep_copy.php +++ b/src/DeepCopy/deep_copy.php @@ -6,11 +6,19 @@ * Deep copies the given value. * * @param mixed $value - * @param bool $useCloneMethod - * - * @return mixed + * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will + * be used instead of the regular deep cloning. + * @param bool $skipUncloneable If enabled, will not throw an exception when coming across an uncloneable + * property. + * @param Array List of filter-matcher pairs + * @param Array List of type filter-matcher pairs */ -function deep_copy($value, $useCloneMethod = false) -{ - return (new DeepCopy($useCloneMethod))->copy($value); +public function deep_copy( + $value, + bool $useCloneMethod = false, + bool $skipUncloneable = true, + array $filters = [], + array $typeFilters = [] +) { + return (new DeepCopy($useCloneMethod, $skipUncloneable, $filters, $typeFilters))->copy($value); } From 4352fd21271e8d704d3f9f1aeb237e04188ab2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Thu, 21 Jun 2018 14:23:11 +0100 Subject: [PATCH 2/6] Fix tests --- src/DeepCopy/DeepCopy.php | 15 ++-- src/DeepCopy/deep_copy.php | 4 +- tests/DeepCopyTest/DeepCopyTest.php | 118 ++++++++++++++++++++-------- 3 files changed, 95 insertions(+), 42 deletions(-) diff --git a/src/DeepCopy/DeepCopy.php b/src/DeepCopy/DeepCopy.php index a3ea9fc..15ae3e3 100644 --- a/src/DeepCopy/DeepCopy.php +++ b/src/DeepCopy/DeepCopy.php @@ -40,7 +40,7 @@ final class DeepCopy */ private $typeFilters = []; - private $skipUncloneable = false; + private $skipUncloneable; private $useCloneMethod; /** @@ -53,21 +53,20 @@ final class DeepCopy */ public function __construct( bool $useCloneMethod = false, - bool $skipUncloneable = true, + bool $skipUncloneable = false, array $filters = [], array $typeFilters = [] ) { $this->useCloneMethod = $useCloneMethod; - $filters[] = [ - new DateIntervalFilter(), - new TypeMatcher(DateInterval::class) - ]; - foreach ($filters as [$filter, $matcher]) { $this->addFilter($filter, $matcher); } + $typeFilters[] = [ + new DateIntervalFilter(), + new TypeMatcher(DateInterval::class) + ]; $typeFilters[] = [ new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class) @@ -76,6 +75,8 @@ public function __construct( foreach ($typeFilters as [$filter, $matcher]) { $this->addTypeFilter($filter, $matcher); } + + $this->skipUncloneable = $skipUncloneable; } /** diff --git a/src/DeepCopy/deep_copy.php b/src/DeepCopy/deep_copy.php index 0f58d44..a76abd0 100644 --- a/src/DeepCopy/deep_copy.php +++ b/src/DeepCopy/deep_copy.php @@ -13,10 +13,10 @@ * @param Array List of filter-matcher pairs * @param Array List of type filter-matcher pairs */ -public function deep_copy( +function deep_copy( $value, bool $useCloneMethod = false, - bool $skipUncloneable = true, + bool $skipUncloneable = false, array $filters = [], array $typeFilters = [] ) { diff --git a/tests/DeepCopyTest/DeepCopyTest.php b/tests/DeepCopyTest/DeepCopyTest.php index 8bff7de..3004fa0 100644 --- a/tests/DeepCopyTest/DeepCopyTest.php +++ b/tests/DeepCopyTest/DeepCopyTest.php @@ -166,10 +166,15 @@ public function test_it_can_copy_an_object_with_a_date_object_property() */ public function test_it_skips_the_copy_for_userland_datetimezone() { - $deepCopy = new DeepCopy(); - $deepCopy->addFilter( - new SetNullFilter(), - new PropertyNameMatcher('cloned') + $deepCopy = new DeepCopy( + false, + true, + [ + [ + new SetNullFilter(), + new PropertyNameMatcher('cloned') + ] + ] ); $object = new stdClass(); @@ -186,10 +191,15 @@ public function test_it_skips_the_copy_for_userland_datetimezone() */ public function test_it_skips_the_copy_for_userland_dateinterval() { - $deepCopy = new DeepCopy(); - $deepCopy->addFilter( - new SetNullFilter(), - new PropertyNameMatcher('cloned') + $deepCopy = new DeepCopy( + false, + true, + [ + [ + new SetNullFilter(), + new PropertyNameMatcher('cloned') + ] + ] ); $object = new stdClass(); @@ -295,8 +305,7 @@ public function test_it_can_skip_uncloneable_objects() { $object = new f004\UnclonableItem(); - $deepCopy = new DeepCopy(); - $deepCopy->skipUncloneable(true); + $deepCopy = new DeepCopy(false, true); $copy = $deepCopy->copy($object); @@ -325,10 +334,16 @@ public function test_it_only_uses_the_userland_defined_cloned_method_when_config public function test_it_uses_type_filter_to_copy_objects_if_matcher_matches() { - $deepCopy = new DeepCopy(); - $deepCopy->addTypeFilter( - new ShallowCopyFilter(), - new TypeMatcher(f006\A::class) + $deepCopy = new DeepCopy( + false, + false, + [], + [ + [ + new ShallowCopyFilter(), + new TypeMatcher(f006\A::class), + ] + ] ); $a = new f006\A; @@ -346,10 +361,15 @@ public function test_it_uses_type_filter_to_copy_objects_if_matcher_matches() public function test_it_uses_filters_to_copy_object_properties_if_matcher_matches() { - $deepCopy = new DeepCopy(); - $deepCopy->addFilter( - new SetNullFilter(), - new PropertyNameMatcher('cloned') + $deepCopy = new DeepCopy( + false, + false, + [ + [ + new SetNullFilter(), + new PropertyNameMatcher('cloned') + ] + ] ); $a = new f006\A; @@ -366,14 +386,19 @@ public function test_it_uses_filters_to_copy_object_properties_if_matcher_matche public function test_it_uses_the_first_filter_matching_for_copying_object_properties() { - $deepCopy = new DeepCopy(); - $deepCopy->addFilter( - new SetNullFilter(), - new PropertyNameMatcher('cloned') - ); - $deepCopy->addFilter( - new KeepFilter(), - new PropertyNameMatcher('cloned') + $deepCopy = new DeepCopy( + false, + false, + [ + [ + new SetNullFilter(), + new PropertyNameMatcher('cloned') + ], + [ + new KeepFilter(), + new PropertyNameMatcher('cloned') + ] + ] ); $a = new f006\A; @@ -417,8 +442,16 @@ public function test_it_can_copy_a_SplDoublyLinkedList() */ public function test_matchers_can_access_to_parent_private_properties() { - $deepCopy = new DeepCopy(); - $deepCopy->addFilter(new SetNullFilter(), new PropertyTypeMatcher(stdClass::class)); + $deepCopy = new DeepCopy( + false, + false, + [ + [ + new SetNullFilter(), + new PropertyTypeMatcher(stdClass::class), + ] + ] + ); $object = new f008\B(new stdClass()); @@ -434,8 +467,16 @@ public function test_private_property_of_parent_object_copy_with_filters_and_mat $object->setAProp(new stdClass()); $object->setBProp(new stdClass()); - $deepCopy = new DeepCopy(); - $deepCopy->addFilter(new ReplaceFilter(function() {return 'foo';}), new PropertyTypeMatcher(stdClass::class)); + $deepCopy = new DeepCopy( + false, + false, + [ + [ + new ReplaceFilter(function() {return 'foo';}), + new PropertyTypeMatcher(stdClass::class), + ] + ] + ); $new = $deepCopy->copy($object); @@ -450,9 +491,20 @@ public function test_it_can_apply_two_filters() { $object = new f009\A(); - $deepCopy = new DeepCopy(); - $deepCopy->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher()); - $deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('foo')); + $deepCopy = new DeepCopy( + false, + false, + [ + [ + new DoctrineProxyFilter(), + new DoctrineProxyMatcher(), + ], + [ + new SetNullFilter(), + new PropertyNameMatcher('foo'), + ] + ] + ); $copy = $deepCopy->copy($object); From 2d7c913a4d79ae6040ce7287cc14289184db9407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Thu, 21 Jun 2018 14:25:16 +0100 Subject: [PATCH 3/6] Fix CS --- src/DeepCopy/DeepCopy.php | 57 +++++++++---------- src/DeepCopy/deep_copy.php | 7 ++- tests/DeepCopyTest/DeepCopyTest.php | 40 ++++++------- .../Doctrine/DoctrineCollectionFilterTest.php | 5 +- .../DoctrineEmptyCollectionFilterTest.php | 5 +- .../Doctrine/DoctrineProxyFilterTest.php | 2 +- tests/DeepCopyTest/Filter/KeepFilterTest.php | 6 +- .../DeepCopyTest/Filter/ReplaceFilterTest.php | 5 +- .../DeepCopyTest/Filter/SetNullFilterTest.php | 6 +- .../Doctrine/DoctrineProxyMatcherTest.php | 3 +- .../Matcher/PropertyNameMatcherTest.php | 3 +- .../Matcher/PropertyTypeMatcherTest.php | 3 +- .../Reflection/ReflectionHelperTest.php | 17 +++--- .../TypeMatcher/TypeMatcherTest.php | 8 +-- 14 files changed, 88 insertions(+), 79 deletions(-) diff --git a/src/DeepCopy/DeepCopy.php b/src/DeepCopy/DeepCopy.php index 15ae3e3..25a86c6 100644 --- a/src/DeepCopy/DeepCopy.php +++ b/src/DeepCopy/DeepCopy.php @@ -21,7 +21,6 @@ use function is_object; use function is_resource; use function spl_object_id; -use function sprintf; final class DeepCopy { @@ -44,9 +43,9 @@ final class DeepCopy private $useCloneMethod; /** - * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will + * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will * be used instead of the regular deep cloning. - * @param bool $skipUncloneable If enabled, will not throw an exception when coming across an uncloneable + * @param bool $skipUncloneable If enabled, will not throw an exception when coming across an uncloneable * property. * @param Array List of filter-matcher pairs * @param Array List of type filter-matcher pairs @@ -79,6 +78,16 @@ public function __construct( $this->skipUncloneable = $skipUncloneable; } + private function addFilter(Filter $filter, Matcher $matcher): void + { + $this->filters[] = [$matcher, $filter]; + } + + private function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher): void + { + $this->typeFilters[] = [$matcher, $filter]; + } + /** * Deep copies the given value * @@ -93,16 +102,6 @@ public function copy($value) return $this->recursiveCopy($value); } - private function addFilter(Filter $filter, Matcher $matcher): void - { - $this->filters[] = [$matcher, $filter]; - } - - private function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher): void - { - $this->typeFilters[] = [$matcher, $filter]; - } - /** * @return mixed */ @@ -128,6 +127,22 @@ private function recursiveCopy($value) return $this->copyObject($value); } + /** + * @return TypeFilter|null The first filter that matches variable or `null` if no such filter found + */ + private function getFirstMatchedTypeFilter($value): ?TypeFilter + { + foreach ($this->typeFilters as [$matcher, $typeFilter]) { + /** @var TypeMatcher $matcher */ + /** @var TypeFilter $typeFilter */ + if ($matcher->matches($value)) { + return $typeFilter; + } + } + + return null; + } + private function copyArray(array $array): array { foreach ($array as $key => $value) { @@ -219,20 +234,4 @@ function ($object) { // Copy the property $property->setValue($object, $this->recursiveCopy($propertyValue)); } - - /** - * @return TypeFilter|null The first filter that matches variable or `null` if no such filter found - */ - private function getFirstMatchedTypeFilter($value): ?TypeFilter - { - foreach ($this->typeFilters as [$matcher, $typeFilter]) { - /** @var TypeMatcher $matcher */ - /** @var TypeFilter $typeFilter */ - if ($matcher->matches($value)) { - return $typeFilter; - } - } - - return null; - } } diff --git a/src/DeepCopy/deep_copy.php b/src/DeepCopy/deep_copy.php index a76abd0..9ecf0dd 100644 --- a/src/DeepCopy/deep_copy.php +++ b/src/DeepCopy/deep_copy.php @@ -6,9 +6,9 @@ * Deep copies the given value. * * @param mixed $value - * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will + * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will * be used instead of the regular deep cloning. - * @param bool $skipUncloneable If enabled, will not throw an exception when coming across an uncloneable + * @param bool $skipUncloneable If enabled, will not throw an exception when coming across an uncloneable * property. * @param Array List of filter-matcher pairs * @param Array List of type filter-matcher pairs @@ -19,6 +19,7 @@ function deep_copy( bool $skipUncloneable = false, array $filters = [], array $typeFilters = [] -) { +) +{ return (new DeepCopy($useCloneMethod, $skipUncloneable, $filters, $typeFilters))->copy($value); } diff --git a/tests/DeepCopyTest/DeepCopyTest.php b/tests/DeepCopyTest/DeepCopyTest.php index 3004fa0..7e3892e 100644 --- a/tests/DeepCopyTest/DeepCopyTest.php +++ b/tests/DeepCopyTest/DeepCopyTest.php @@ -46,18 +46,6 @@ public function test_it_can_copy_scalar_values($value) $this->assertSame($value, $copy); } - public function provideScalarValues() - { - return [ - [true], - ['string'], - [null], - [10], - [-1], - [.5], - ]; - } - public function test_it_can_copy_an_array_of_scalar_values() { $copy = deep_copy([10, 20]); @@ -74,6 +62,12 @@ public function test_it_can_copy_an_object() $this->assertEqualButNotSame($object, $copy); } + private function assertEqualButNotSame($expected, $val) + { + $this->assertEquals($expected, $val); + $this->assertNotSame($expected, $val); + } + public function test_it_can_copy_an_array_of_objects() { $object = [new stdClass()]; @@ -113,6 +107,18 @@ function (array $vals) use ($createObject) { ); } + public function provideScalarValues() + { + return [ + [true], + ['string'], + [null], + [10], + [-1], + [.5], + ]; + } + public function test_it_can_copy_an_object_with_an_object_property() { $foo = new stdClass(); @@ -472,7 +478,9 @@ public function test_private_property_of_parent_object_copy_with_filters_and_mat false, [ [ - new ReplaceFilter(function() {return 'foo';}), + new ReplaceFilter(function () { + return 'foo'; + }), new PropertyTypeMatcher(stdClass::class), ] ] @@ -510,10 +518,4 @@ public function test_it_can_apply_two_filters() $this->assertNull($copy->foo); } - - private function assertEqualButNotSame($expected, $val) - { - $this->assertEquals($expected, $val); - $this->assertNotSame($expected, $val); - } } diff --git a/tests/DeepCopyTest/Filter/Doctrine/DoctrineCollectionFilterTest.php b/tests/DeepCopyTest/Filter/Doctrine/DoctrineCollectionFilterTest.php index b8ef634..bbd6fbe 100644 --- a/tests/DeepCopyTest/Filter/Doctrine/DoctrineCollectionFilterTest.php +++ b/tests/DeepCopyTest/Filter/Doctrine/DoctrineCollectionFilterTest.php @@ -16,7 +16,8 @@ class DoctrineCollectionFilterTest extends TestCase { public function test_it_copies_the_object_property_array_collection() { - $object = new class { + $object = new class + { public $foo; }; $oldCollection = new ArrayCollection(); @@ -28,7 +29,7 @@ public function test_it_copies_the_object_property_array_collection() $filter->apply( $object, new ReflectionProperty($object, 'foo'), - function($item) { + function ($item) { return null; } ); diff --git a/tests/DeepCopyTest/Filter/Doctrine/DoctrineEmptyCollectionFilterTest.php b/tests/DeepCopyTest/Filter/Doctrine/DoctrineEmptyCollectionFilterTest.php index f95128c..a77e6bf 100644 --- a/tests/DeepCopyTest/Filter/Doctrine/DoctrineEmptyCollectionFilterTest.php +++ b/tests/DeepCopyTest/Filter/Doctrine/DoctrineEmptyCollectionFilterTest.php @@ -16,7 +16,8 @@ class DoctrineEmptyCollectionFilterTest extends TestCase { public function test_it_sets_the_object_property_to_an_empty_doctrine_collection() { - $object = new class { + $object = new class + { public $foo; }; $collection = new ArrayCollection(); @@ -29,7 +30,7 @@ public function test_it_sets_the_object_property_to_an_empty_doctrine_collection $filter->apply( $object, new ReflectionProperty($object, 'foo'), - function($item) { + function ($item) { return null; } ); diff --git a/tests/DeepCopyTest/Filter/Doctrine/DoctrineProxyFilterTest.php b/tests/DeepCopyTest/Filter/Doctrine/DoctrineProxyFilterTest.php index 7c75ac6..a2d5871 100644 --- a/tests/DeepCopyTest/Filter/Doctrine/DoctrineProxyFilterTest.php +++ b/tests/DeepCopyTest/Filter/Doctrine/DoctrineProxyFilterTest.php @@ -22,7 +22,7 @@ public function test_it_loads_the_doctrine_proxy() $filter->apply( $foo, new ReflectionProperty($foo, 'bar'), - function($item) { + function ($item) { throw new BadMethodCallException('Did not expect to be called.'); } ); diff --git a/tests/DeepCopyTest/Filter/KeepFilterTest.php b/tests/DeepCopyTest/Filter/KeepFilterTest.php index 89fce27..9dc132b 100644 --- a/tests/DeepCopyTest/Filter/KeepFilterTest.php +++ b/tests/DeepCopyTest/Filter/KeepFilterTest.php @@ -14,7 +14,8 @@ class KeepFilterTest extends TestCase { public function test_it_does_not_change_the_filtered_object_property() { - $object = new class { + $object = new class + { public $foo; }; $keepObject = new stdClass(); @@ -22,7 +23,8 @@ public function test_it_does_not_change_the_filtered_object_property() $filter = new KeepFilter(); - $filter->apply($object, new ReflectionProperty($object, 'foo'), function () {}); + $filter->apply($object, new ReflectionProperty($object, 'foo'), function () { + }); $this->assertSame($keepObject, $object->foo); } diff --git a/tests/DeepCopyTest/Filter/ReplaceFilterTest.php b/tests/DeepCopyTest/Filter/ReplaceFilterTest.php index fe8e0e9..08fc872 100644 --- a/tests/DeepCopyTest/Filter/ReplaceFilterTest.php +++ b/tests/DeepCopyTest/Filter/ReplaceFilterTest.php @@ -16,7 +16,8 @@ class ReplaceFilterTest extends TestCase */ public function test_it_applies_the_callback_to_the_specified_property(callable $callback, array $expected) { - $object = new class { + $object = new class + { public $data; }; $object->data = [ @@ -32,7 +33,7 @@ public function test_it_applies_the_callback_to_the_specified_property(callable function () { return null; } - ); + ); $this->assertEquals($expected, $object->data); } diff --git a/tests/DeepCopyTest/Filter/SetNullFilterTest.php b/tests/DeepCopyTest/Filter/SetNullFilterTest.php index 37981ba..0486520 100644 --- a/tests/DeepCopyTest/Filter/SetNullFilterTest.php +++ b/tests/DeepCopyTest/Filter/SetNullFilterTest.php @@ -15,14 +15,16 @@ public function test_it_sets_the_given_property_to_null() { $filter = new SetNullFilter(); - $object = new class { + $object = new class + { public $foo; public $bim; }; $object->foo = 'bar'; $object->bim = 'bam'; - $filter->apply($object, new ReflectionProperty($object, 'foo'), function () {}); + $filter->apply($object, new ReflectionProperty($object, 'foo'), function () { + }); $this->assertNull($object->foo); $this->assertEquals('bam', $object->bim); diff --git a/tests/DeepCopyTest/Matcher/Doctrine/DoctrineProxyMatcherTest.php b/tests/DeepCopyTest/Matcher/Doctrine/DoctrineProxyMatcherTest.php index 0df5b2d..e3e35b6 100644 --- a/tests/DeepCopyTest/Matcher/Doctrine/DoctrineProxyMatcherTest.php +++ b/tests/DeepCopyTest/Matcher/Doctrine/DoctrineProxyMatcherTest.php @@ -30,7 +30,8 @@ public function providePairs() return [ [new FooProxy(), true], [ - new class { + new class + { public $foo; }, false diff --git a/tests/DeepCopyTest/Matcher/PropertyNameMatcherTest.php b/tests/DeepCopyTest/Matcher/PropertyNameMatcherTest.php index 6760cc7..12f1018 100644 --- a/tests/DeepCopyTest/Matcher/PropertyNameMatcherTest.php +++ b/tests/DeepCopyTest/Matcher/PropertyNameMatcherTest.php @@ -16,7 +16,8 @@ class PropertyNameMatcherTest extends TestCase */ public function test_it_matches_the_given_property($prop, $expected) { - $object = new class { + $object = new class + { public $foo; public $bar; }; diff --git a/tests/DeepCopyTest/Matcher/PropertyTypeMatcherTest.php b/tests/DeepCopyTest/Matcher/PropertyTypeMatcherTest.php index ad3e5fa..a7278e2 100644 --- a/tests/DeepCopyTest/Matcher/PropertyTypeMatcherTest.php +++ b/tests/DeepCopyTest/Matcher/PropertyTypeMatcherTest.php @@ -41,7 +41,8 @@ public function providePairs() [$object2, false], [$object3, false], [ - new class { + new class + { public $foo; }, false diff --git a/tests/DeepCopyTest/Reflection/ReflectionHelperTest.php b/tests/DeepCopyTest/Reflection/ReflectionHelperTest.php index fa429f3..f358772 100644 --- a/tests/DeepCopyTest/Reflection/ReflectionHelperTest.php +++ b/tests/DeepCopyTest/Reflection/ReflectionHelperTest.php @@ -39,26 +39,23 @@ public function test_it_retrieves_the_object_properties() class ReflectionHelperTestParent { + public static $a20; + protected static $a21; + private static $a22; public $a1; - protected $a2; - private $a3; - public $a10; + protected $a2; protected $a11; + private $a3; private $a12; - - public static $a20; - protected static $a21; - private static $a22; } class ReflectionHelperTestChild extends ReflectionHelperTestParent { public $a1; - protected $a2; - private $a3; - public $a100; + protected $a2; protected $a101; + private $a3; private $a102; } diff --git a/tests/DeepCopyTest/TypeMatcher/TypeMatcherTest.php b/tests/DeepCopyTest/TypeMatcher/TypeMatcherTest.php index cfb061b..1cd8552 100644 --- a/tests/DeepCopyTest/TypeMatcher/TypeMatcherTest.php +++ b/tests/DeepCopyTest/TypeMatcher/TypeMatcherTest.php @@ -6,6 +6,10 @@ use PHPUnit\Framework\TestCase; use stdClass; +interface IA +{ +} + /** * @covers \DeepCopy\TypeMatcher\TypeMatcher */ @@ -45,10 +49,6 @@ class Bar extends Foo { } -interface IA -{ -} - class A implements IA { } From ce7515cfdad7c91055958c254044beebec82cb8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Thu, 21 Jun 2018 15:55:33 +0100 Subject: [PATCH 4/6] Fix CS --- src/DeepCopy/DeepCopy.php | 56 +++++++++---------- src/DeepCopy/deep_copy.php | 4 +- .../Reflection/ReflectionHelperTest.php | 17 +++--- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/DeepCopy/DeepCopy.php b/src/DeepCopy/DeepCopy.php index 25a86c6..d9fcd4c 100644 --- a/src/DeepCopy/DeepCopy.php +++ b/src/DeepCopy/DeepCopy.php @@ -43,9 +43,9 @@ final class DeepCopy private $useCloneMethod; /** - * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will + * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will * be used instead of the regular deep cloning. - * @param bool $skipUncloneable If enabled, will not throw an exception when coming across an uncloneable + * @param bool $skipUncloneable If enabled, will not throw an exception when coming across an uncloneable * property. * @param Array List of filter-matcher pairs * @param Array List of type filter-matcher pairs @@ -78,16 +78,6 @@ public function __construct( $this->skipUncloneable = $skipUncloneable; } - private function addFilter(Filter $filter, Matcher $matcher): void - { - $this->filters[] = [$matcher, $filter]; - } - - private function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher): void - { - $this->typeFilters[] = [$matcher, $filter]; - } - /** * Deep copies the given value * @@ -127,22 +117,6 @@ private function recursiveCopy($value) return $this->copyObject($value); } - /** - * @return TypeFilter|null The first filter that matches variable or `null` if no such filter found - */ - private function getFirstMatchedTypeFilter($value): ?TypeFilter - { - foreach ($this->typeFilters as [$matcher, $typeFilter]) { - /** @var TypeMatcher $matcher */ - /** @var TypeFilter $typeFilter */ - if ($matcher->matches($value)) { - return $typeFilter; - } - } - - return null; - } - private function copyArray(array $array): array { foreach ($array as $key => $value) { @@ -234,4 +208,30 @@ function ($object) { // Copy the property $property->setValue($object, $this->recursiveCopy($propertyValue)); } + + private function addFilter(Filter $filter, Matcher $matcher): void + { + $this->filters[] = [$matcher, $filter]; + } + + private function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher): void + { + $this->typeFilters[] = [$matcher, $filter]; + } + + /** + * @return TypeFilter|null The first filter that matches variable or `null` if no such filter found + */ + private function getFirstMatchedTypeFilter($value): ?TypeFilter + { + foreach ($this->typeFilters as [$matcher, $typeFilter]) { + /** @var TypeMatcher $matcher */ + /** @var TypeFilter $typeFilter */ + if ($matcher->matches($value)) { + return $typeFilter; + } + } + + return null; + } } diff --git a/src/DeepCopy/deep_copy.php b/src/DeepCopy/deep_copy.php index 9ecf0dd..45b40f6 100644 --- a/src/DeepCopy/deep_copy.php +++ b/src/DeepCopy/deep_copy.php @@ -6,9 +6,9 @@ * Deep copies the given value. * * @param mixed $value - * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will + * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will * be used instead of the regular deep cloning. - * @param bool $skipUncloneable If enabled, will not throw an exception when coming across an uncloneable + * @param bool $skipUncloneable If enabled, will not throw an exception when coming across an uncloneable * property. * @param Array List of filter-matcher pairs * @param Array List of type filter-matcher pairs diff --git a/tests/DeepCopyTest/Reflection/ReflectionHelperTest.php b/tests/DeepCopyTest/Reflection/ReflectionHelperTest.php index f358772..fa429f3 100644 --- a/tests/DeepCopyTest/Reflection/ReflectionHelperTest.php +++ b/tests/DeepCopyTest/Reflection/ReflectionHelperTest.php @@ -39,23 +39,26 @@ public function test_it_retrieves_the_object_properties() class ReflectionHelperTestParent { - public static $a20; - protected static $a21; - private static $a22; public $a1; - public $a10; protected $a2; - protected $a11; private $a3; + + public $a10; + protected $a11; private $a12; + + public static $a20; + protected static $a21; + private static $a22; } class ReflectionHelperTestChild extends ReflectionHelperTestParent { public $a1; - public $a100; protected $a2; - protected $a101; private $a3; + + public $a100; + protected $a101; private $a102; } From d7af28f7aa5424145b91f0c56b08c000323e60b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Thu, 21 Jun 2018 16:04:52 +0100 Subject: [PATCH 5/6] Update readme --- README.md | 102 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 426a3dd..a80339d 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ function deep_copy($var) static $copier = null; if (null === $copier) { - $copier = new DeepCopy(true); + $copier = new DeepCopy(); } return $copier->copy($var); @@ -124,8 +124,11 @@ function deep_copy($var) You can add filters to customize the copy process by adding filters: ```php -$copier = new DeepCopy(); -$copier->addFilter($filter, $matcher); +$copier = new DeepCopy( + false, + false, + [[$filter, $matcher]] +); ``` During the copy process, when a property is matched by a [matcher][matcher], then the [filter][filter] associated to @@ -203,8 +206,11 @@ use DeepCopy\Filter\SetNullFilter; $object = MyClass::load(123); echo $object->id; // 123 -$copier = new DeepCopy(); -$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id')); +$copier = new DeepCopy( + false, + false, + [[new SetNullFilter(), new PropertyNameMatcher('id')]] +); $copy = $copier->copy($object); @@ -221,10 +227,10 @@ use DeepCopy\DeepCopy; use DeepCopy\Filter\KeepFilter; use DeepCopy\Matcher\PropertyMatcher; -$copier = new DeepCopy(); -$copier->addFilter( - new KeepFilter(), - new PropertyMatcher(MyClass::class, 'category') +$copier = new DeepCopy( + false, + false, + [[new KeepFilter(), new PropertyMatcher(MyClass::class, 'category')]] ); $copy = $copier->copy($object); // $object is an instance of MyClass @@ -242,10 +248,10 @@ use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter; use DeepCopy\Matcher\PropertyTypeMatcher; use Doctrine\Common\Collections\Collection; -$copier = new DeepCopy(); -$copier->addFilter( - new DoctrineCollectionFilter(), - new PropertyTypeMatcher(Collection::class) +$copier = new DeepCopy( + false, + false, + [[new DoctrineCollectionFilter(), new PropertyTypeMatcher(Collection::class)]] ); $copy = $copier->copy($object); @@ -262,10 +268,10 @@ use DeepCopy\DeepCopy; use DeepCopy\Filter\Doctrine\DoctrineEmptyCollectionFilter; use DeepCopy\Matcher\PropertyMatcher; -$copier = new DeepCopy(); -$copier->addFilter( - new DoctrineEmptyCollectionFilter(), - new PropertyMatcher(MyClass::class, 'myProperty') +$copier = new DeepCopy( + false, + false, + [[new DoctrineEmptyCollectionFilter(), new PropertyMatcher(MyClass::class, 'myProperty')]] ); $copy = $copier->copy($object); @@ -292,9 +298,14 @@ use DeepCopy\Filter\SetNullFilter; use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher; use DeepCopy\Matcher\PropertyNameMatcher; -$copier = new DeepCopy(); -$copier->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher()); -$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id')); +$copier = new DeepCopy( + false, + false, + [ + [new DoctrineProxyFilter(), new DoctrineProxyMatcher()], + [new SetNullFilter(), new PropertyNameMatcher('id')], + ] +); $copy = $copier->copy($object); @@ -311,14 +322,19 @@ use DeepCopy\DeepCopy; use DeepCopy\Filter\ReplaceFilter; use DeepCopy\Matcher\PropertyMatcher; -$copier = new DeepCopy(); -$copier->addFilter( - new ReplaceFilter( - function ($currentValue): string { - return $currentValue . ' (copy)' - } - ), - new PropertyMatcher(MyClass::class, 'title') +$copier = new DeepCopy( + false, + false, + [ + [ + new ReplaceFilter( + function ($currentValue): string { + return $currentValue . ' (copy)' + } + ), + new PropertyMatcher(MyClass::class, 'title'), + ], + ] ); $copy = $copier->copy($object); // $object is an instance of MyClass @@ -333,14 +349,19 @@ use DeepCopy\DeepCopy; use DeepCopy\TypeFilter\ReplaceFilter; use DeepCopy\TypeMatcher\TypeMatcher; -$copier = new DeepCopy(); -$copier->addFilter( - new ReplaceFilter( - function (MyClass $myClass): string { - return get_class($myClass) - } - ), - new TypeMatcher(MyClass::class) +$copier = new DeepCopy( + false, + false, + [ + [ + new ReplaceFilter( + function (MyClass $myClass): string { + return get_class($myClass) + } + ), + new TypeMatcher(MyClass::class), + ], + ] ); $copy = $copier->copy([new MyClass, 'some string', new MyClass]); @@ -359,10 +380,11 @@ use DeepCopy\TypeFilter\ShallowCopyFilter; use DeepCopy\TypeMatcher\TypeMatcher; use Mockery as m; -$copier = new DeepCopy(); -$copier->addTypeFilter( - new ShallowCopyFilter, - new TypeMatcher(m\MockInterface::class) +$copier = new DeepCopy( + false, + false, + [], + [[new ShallowCopyFilter, new TypeMatcher(m\MockInterface::class)]] ); $myServiceWithMocks = new MyService( From 078bf74190a519db4514ef5493086021c3aaaf7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Thu, 21 Jun 2018 16:36:31 +0100 Subject: [PATCH 6/6] Fix phpstan warnings --- .travis.yml | 2 +- phpstan.neon | 4 ++++ tests/DeepCopyTest/TypeMatcher/TypeMatcherTest.php | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 phpstan.neon diff --git a/.travis.yml b/.travis.yml index f2bdd83..a9422ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ before_script: - mkdir -p build/logs script: - - vendor/bin/phpstan analyse -l 7 src/ tests/ fixtures/ + - vendor/bin/phpstan analyse -l7 -cphpstan.neon src/ tests/ fixtures/ - vendor/bin/phpunit --coverage-clover build/logs/clover.xml after_script: diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..ef31a6e --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + ignoreErrors: + - '#PHPDoc tag \@param has invalid value \(Array\#' + - '#PHPDoc tag \@param has invalid value \(Array\#' diff --git a/tests/DeepCopyTest/TypeMatcher/TypeMatcherTest.php b/tests/DeepCopyTest/TypeMatcher/TypeMatcherTest.php index 1cd8552..33d32e6 100644 --- a/tests/DeepCopyTest/TypeMatcher/TypeMatcherTest.php +++ b/tests/DeepCopyTest/TypeMatcher/TypeMatcherTest.php @@ -6,10 +6,6 @@ use PHPUnit\Framework\TestCase; use stdClass; -interface IA -{ -} - /** * @covers \DeepCopy\TypeMatcher\TypeMatcher */ @@ -52,3 +48,7 @@ class Bar extends Foo class A implements IA { } + +interface IA +{ +}