This commit is contained in:
Torsten 2018-08-07 10:42:11 +02:00
parent 98419bd18d
commit 8a69e45efa
4919 changed files with 28311 additions and 224136 deletions

View file

@ -1,6 +1,3 @@
.DS_Store
.idea/*
vendor/*
composer.phar
composer.lock
/composer.phar
/composer.lock
/vendor/*

View file

@ -1,30 +1,33 @@
language: php
sudo: false
php:
- '5.5'
- '5.6'
- '7.0'
- '7.1'
- nightly
- hhvm
matrix:
fast_finish: true
include:
- php: '5.4'
- php: '5.6'
env: COMPOSER_FLAGS="--prefer-lowest"
allow_failures:
- php: nightly
- php: hhvm
cache:
directories:
- $HOME/.composer/cache/files
before_install:
- |
if [ "$TRAVIS_PHP_VERSION" = "nightly" ] || "$TRAVIS_PHP_VERSION" = "7.1" ]; then
if [ "$TRAVIS_PHP_VERSION" = "nightly" ]; then
COMPOSER_FLAGS="$COMPOSER_FLAGS --ignore-platform-reqs"
fi;
install:
- composer update -n --prefer-dist $COMPOSER_FLAGS
- composer update --no-interaction --no-progress --no-suggest --prefer-dist $COMPOSER_FLAGS
- wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.0/coveralls.phar
before_script:

0
pfosten/vendor/myclabs/deep-copy/LICENSE vendored Executable file → Normal file
View file

292
pfosten/vendor/myclabs/deep-copy/README.md vendored Executable file → Normal file
View file

@ -2,7 +2,9 @@
DeepCopy helps you create deep copies (clones) of your objects. It is designed to handle cycles in the association graph.
[![Build Status](https://travis-ci.org/myclabs/DeepCopy.png?branch=master)](https://travis-ci.org/myclabs/DeepCopy) [![Coverage Status](https://coveralls.io/repos/myclabs/DeepCopy/badge.png?branch=master)](https://coveralls.io/r/myclabs/DeepCopy?branch=master) [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/myclabs/DeepCopy/badges/quality-score.png?s=2747100c19b275f93a777e3297c6c12d1b68b934)](https://scrutinizer-ci.com/g/myclabs/DeepCopy/)
[![Build Status](https://travis-ci.org/myclabs/DeepCopy.png?branch=master)](https://travis-ci.org/myclabs/DeepCopy)
[![Coverage Status](https://coveralls.io/repos/myclabs/DeepCopy/badge.png?branch=master)](https://coveralls.io/r/myclabs/DeepCopy?branch=master)
[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/myclabs/DeepCopy/badges/quality-score.png?s=2747100c19b275f93a777e3297c6c12d1b68b934)](https://scrutinizer-ci.com/g/myclabs/DeepCopy/)
[![Total Downloads](https://poser.pugx.org/myclabs/deep-copy/downloads.svg)](https://packagist.org/packages/myclabs/deep-copy)
@ -20,15 +22,18 @@ DeepCopy helps you create deep copies (clones) of your objects. It is designed t
1. [Specific property](#specific-property)
1. [Type](#type)
1. [Filters](#filters)
1. [`SetNullFilter`](#setnullfilter)
1. [`KeepFilter`](#keepfilter)
1. [`ReplaceFilter`](#replacefilter)
1. [`ShallowCopyFilter`](#doctrinecollectionfilter)
1. [`DoctrineCollectionFilter`](#doctrinecollectionfilter)
1. [`DoctrineEmptyCollectionFilter`](#doctrineemptycollectionfilter)
1. [`SetNullFilter`](#setnullfilter-filter)
1. [`KeepFilter`](#keepfilter-filter)
1. [`DoctrineCollectionFilter`](#doctrinecollectionfilter-filter)
1. [`DoctrineEmptyCollectionFilter`](#doctrineemptycollectionfilter-filter)
1. [`DoctrineProxyFilter`](#doctrineproxyfilter-filter)
1. [`ReplaceFilter`](#replacefilter-type-filter)
1. [`ShallowCopyFilter`](#doctrinecollectionfilter-type-filter)
1. [Edge cases](#edge-cases)
1. [Contributing](#contributing)
1. [Tests](#tests)
## How?
Install with Composer:
@ -42,8 +47,8 @@ Use simply:
```php
use DeepCopy\DeepCopy;
$deepCopy = new DeepCopy();
$myCopy = $deepCopy->copy($myObject);
$copier = new DeepCopy();
$myCopy = $copier->copy($myObject);
```
@ -57,7 +62,8 @@ $myCopy = clone $myObject;
- How do you create **deep** copies of your objects (i.e. copying also all the objects referenced in the properties)?
You use [`__clone()`](http://www.php.net/manual/en/language.oop5.cloning.php#object.clone) and implement the behavior yourself.
You use [`__clone()`](http://www.php.net/manual/en/language.oop5.cloning.php#object.clone) and implement the behavior
yourself.
- But how do you handle **cycles** in the association graph?
@ -65,37 +71,82 @@ Now you're in for a big mess :(
![association graph](doc/graph.png)
### Using simply `clone`
![Using clone](doc/clone.png)
### Overridding `__clone()`
![Overridding __clone](doc/deep-clone.png)
### With `DeepCopy`
![With DeepCopy](doc/deep-copy.png)
## How it works
DeepCopy recursively traverses all the object's properties and clones them. To avoid cloning the same object twice it keeps a hash map of all instances and thus preserves the object graph.
DeepCopy recursively traverses all the object's properties and clones them. To avoid cloning the same object twice it
keeps a hash map of all instances and thus preserves the object graph.
To use it:
```php
use function DeepCopy\deep_copy;
$copy = deep_copy($var);
```
Alternatively, you can create your own `DeepCopy` instance to configure it differently for example:
```php
use DeepCopy\DeepCopy;
$copier = new DeepCopy(true);
$copy = $copier->copy($var);
```
You may want to roll your own deep copy function:
```php
namespace Acme;
use DeepCopy\DeepCopy;
function deep_copy($var)
{
static $copier = null;
if (null === $copier) {
$copier = new DeepCopy(true);
}
return $copier->copy($var);
}
```
## Going further
You can add filters to customize the copy process.
The method to add a filter is `$deepCopy->addFilter($filter, $matcher)`,
The method to add a filter is `DeepCopy\DeepCopy::addFilter($filter, $matcher)`,
with `$filter` implementing `DeepCopy\Filter\Filter`
and `$matcher` implementing `DeepCopy\Matcher\Matcher`.
We provide some generic filters and matchers.
### Matchers
- `DeepCopy\Matcher` applies on a object attribute.
- `DeepCopy\TypeMatcher` applies on any element found in graph, including array elements.
#### Property name
The `PropertyNameMatcher` will match a property by its name:
@ -103,10 +154,11 @@ The `PropertyNameMatcher` will match a property by its name:
```php
use DeepCopy\Matcher\PropertyNameMatcher;
// Will apply a filter to any property of any objects named "id"
$matcher = new PropertyNameMatcher('id');
// will apply a filter to any property of any objects named "id"
```
#### Specific property
The `PropertyMatcher` will match a specific property of a specific class:
@ -114,46 +166,53 @@ The `PropertyMatcher` will match a specific property of a specific class:
```php
use DeepCopy\Matcher\PropertyMatcher;
// Will apply a filter to the property "id" of any objects of the class "MyClass"
$matcher = new PropertyMatcher('MyClass', 'id');
// will apply a filter to the property "id" of any objects of the class "MyClass"
```
#### Type
The `TypeMatcher` will match any element by its type (instance of a class or any value that could be parameter of [gettype()](http://php.net/manual/en/function.gettype.php) function):
The `TypeMatcher` will match any element by its type (instance of a class or any value that could be parameter of
[gettype()](http://php.net/manual/en/function.gettype.php) function):
```php
use DeepCopy\TypeMatcher\TypeMatcher;
// Will apply a filter to any object that is an instance of Doctrine\Common\Collections\Collection
$matcher = new TypeMatcher('Doctrine\Common\Collections\Collection');
// will apply a filter to any object that is an instance of Doctrine\Common\Collections\Collection
```
### Filters
- `DeepCopy\Filter` applies a transformation to the object attribute matched by `DeepCopy\Matcher`.
- `DeepCopy\TypeFilter` applies a transformation to any element matched by `DeepCopy\TypeMatcher`.
- `DeepCopy\Filter` applies a transformation to the object attribute matched by `DeepCopy\Matcher`
- `DeepCopy\TypeFilter` applies a transformation to any element matched by `DeepCopy\TypeMatcher`
#### `SetNullFilter`
Let's say for example that you are copying a database record (or a Doctrine entity), so you want the copy not to have any ID:
#### `SetNullFilter` (filter)
Let's say for example that you are copying a database record (or a Doctrine entity), so you want the copy not to have
any ID:
```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\SetNullFilter;
use DeepCopy\Matcher\PropertyNameMatcher;
$myObject = MyClass::load(123);
echo $myObject->id; // 123
$object = MyClass::load(123);
echo $object->id; // 123
$deepCopy = new DeepCopy();
$deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
$myCopy = $deepCopy->copy($myObject);
$copier = new DeepCopy();
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
echo $myCopy->id; // null
$copy = $copier->copy($object);
echo $copy->id; // null
```
#### `KeepFilter`
#### `KeepFilter` (filter)
If you want a property to remain untouched (for example, an association to an object):
@ -162,53 +221,114 @@ use DeepCopy\DeepCopy;
use DeepCopy\Filter\KeepFilter;
use DeepCopy\Matcher\PropertyMatcher;
$deepCopy = new DeepCopy();
$deepCopy->addFilter(new KeepFilter(), new PropertyMatcher('MyClass', 'category'));
$myCopy = $deepCopy->copy($myObject);
$copier = new DeepCopy();
$copier->addFilter(new KeepFilter(), new PropertyMatcher('MyClass', 'category'));
// $myCopy->category has not been touched
$copy = $copier->copy($object);
// $copy->category has not been touched
```
#### `ReplaceFilter`
1. If you want to replace the value of a property:
#### `DoctrineCollectionFilter` (filter)
```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\ReplaceFilter;
use DeepCopy\Matcher\PropertyMatcher;
If you use Doctrine and want to copy an entity, you will need to use the `DoctrineCollectionFilter`:
$deepCopy = new DeepCopy();
$callback = function ($currentValue) {
return $currentValue . ' (copy)'
};
$deepCopy->addFilter(new ReplaceFilter($callback), new PropertyMatcher('MyClass', 'title'));
$myCopy = $deepCopy->copy($myObject);
```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter;
use DeepCopy\Matcher\PropertyTypeMatcher;
// $myCopy->title will contain the data returned by the callback, e.g. 'The title (copy)'
```
$copier = new DeepCopy();
$copier->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
2. If you want to replace whole element:
$copy = $copier->copy($object);
```
```php
use DeepCopy\DeepCopy;
use DeepCopy\TypeFilter\ReplaceFilter;
use DeepCopy\TypeMatcher\TypeMatcher;
$deepCopy = new DeepCopy();
$callback = function (MyClass $myClass) {
return get_class($myClass);
};
$deepCopy->addTypeFilter(new ReplaceFilter($callback), new TypeMatcher('MyClass'));
$myCopy = $deepCopy->copy(array(new MyClass, 'some string', new MyClass));
#### `DoctrineEmptyCollectionFilter` (filter)
// $myCopy will contain ['MyClass', 'some string', 'MyClass']
```
If you use Doctrine and want to copy an entity who contains a `Collection` that you want to be reset, you can use the
`DoctrineEmptyCollectionFilter`
```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\Doctrine\DoctrineEmptyCollectionFilter;
use DeepCopy\Matcher\PropertyMatcher;
$copier = new DeepCopy();
$copier->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty'));
$copy = $copier->copy($object);
// $copy->myProperty will return an empty collection
```
#### `DoctrineProxyFilter` (filter)
If you use Doctrine and use cloning on lazy loaded entities, you might encounter errors mentioning missing fields on a
Doctrine proxy class (...\\\_\_CG\_\_\Proxy).
You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class.
**Make sure, though, to put this as one of your very first filters in the filter chain so that the entity is loaded
before other filters are applied!**
```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
$copier = new DeepCopy();
$copier->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher());
$copy = $copier->copy($object);
// $copy should now contain a clone of all entities, including those that were not yet fully loaded.
```
#### `ReplaceFilter` (type filter)
1. If you want to replace the value of a property:
```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\ReplaceFilter;
use DeepCopy\Matcher\PropertyMatcher;
$copier = new DeepCopy();
$callback = function ($currentValue) {
return $currentValue . ' (copy)'
};
$copier->addFilter(new ReplaceFilter($callback), new PropertyMatcher('MyClass', 'title'));
$copy = $copier->copy($object);
// $copy->title will contain the data returned by the callback, e.g. 'The title (copy)'
```
2. If you want to replace whole element:
```php
use DeepCopy\DeepCopy;
use DeepCopy\TypeFilter\ReplaceFilter;
use DeepCopy\TypeMatcher\TypeMatcher;
$copier = new DeepCopy();
$callback = function (MyClass $myClass) {
return get_class($myClass);
};
$copier->addTypeFilter(new ReplaceFilter($callback), new TypeMatcher('MyClass'));
$copy = $copier->copy([new MyClass, 'some string', new MyClass]);
// $copy will contain ['MyClass', 'some string', 'MyClass']
```
The `$callback` parameter of the `ReplaceFilter` constructor accepts any PHP callable.
#### `ShallowCopyFilter`
#### `ShallowCopyFilter` (type filter)
Stop *DeepCopy* from recursively copying element, using standard `clone` instead:
@ -225,66 +345,28 @@ $this->deepCopy->addTypeFilter(
);
$myServiceWithMocks = new MyService(m::mock(MyDependency1::class), m::mock(MyDependency2::class));
// all mocks will be just cloned, not deep-copied
// All mocks will be just cloned, not deep copied
```
#### `DoctrineCollectionFilter`
If you use Doctrine and want to copy an entity, you will need to use the `DoctrineCollectionFilter`:
## Edge cases
```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter;
use DeepCopy\Matcher\PropertyTypeMatcher;
The following structures cannot be deep-copied with PHP Reflection. As a result they are shallow cloned and filters are
not applied. There is two ways for you to handle them:
$deepCopy = new DeepCopy();
$deepCopy->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
$myCopy = $deepCopy->copy($myObject);
```
- Implement your own `__clone()` method
- Use a filter with a type matcher
#### `DoctrineEmptyCollectionFilter`
If you use Doctrine and want to copy an entity who contains a `Collection` that you want to be reset, you can use the `DoctrineEmptyCollectionFilter`
```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\Doctrine\DoctrineEmptyCollectionFilter;
use DeepCopy\Matcher\PropertyMatcher;
$deepCopy = new DeepCopy();
$deepCopy->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty'));
$myCopy = $deepCopy->copy($myObject);
// $myCopy->myProperty will return an empty collection
```
#### `DoctrineProxyFilter`
If you use Doctrine and use cloning on lazy loaded entities, you might encounter errors mentioning missing fields on a
Doctrine proxy class (...\\\_\_CG\_\_\Proxy).
You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class.
**Make sure, though, to put this as one of your very first filters in the filter chain so that the entity is loaded before other filters are applied!**
```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
$deepCopy = new DeepCopy();
$deepCopy->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher());
$myCopy = $deepCopy->copy($myObject);
// $myCopy should now contain a clone of all entities, including those that were not yet fully loaded.
```
## Contributing
DeepCopy is distributed under the MIT license.
### Tests
Running the tests is simple:
```php
phpunit
vendor/bin/phpunit
```

26
pfosten/vendor/myclabs/deep-copy/composer.json vendored Executable file → Normal file
View file

@ -3,19 +3,33 @@
"type": "library",
"description": "Create deep copies (clones) of your objects",
"keywords": ["clone", "copy", "duplicate", "object", "object graph"],
"homepage": "https://github.com/myclabs/DeepCopy",
"license": "MIT",
"autoload": {
"psr-4": { "DeepCopy\\": "src/DeepCopy/" }
"psr-4": {
"DeepCopy\\": "src/DeepCopy/"
},
"files": [
"src/DeepCopy/deep_copy.php"
]
},
"autoload-dev": {
"psr-4": { "DeepCopyTest\\": "tests/DeepCopyTest/" }
"psr-4": {
"DeepCopy\\": "fixtures/",
"DeepCopyTest\\": "tests/DeepCopyTest/"
}
},
"require": {
"php": ">=5.4.0"
"php": "^5.6 || ^7.0"
},
"require-dev": {
"doctrine/collections": "1.*",
"phpunit/phpunit": "~4.1"
"doctrine/collections": "^1.0",
"doctrine/common": "^2.6",
"phpunit/phpunit": "^4.1"
},
"config": {
"sort-packages": true
}
}

0
pfosten/vendor/myclabs/deep-copy/doc/clone.png vendored Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

0
pfosten/vendor/myclabs/deep-copy/doc/deep-clone.png vendored Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

0
pfosten/vendor/myclabs/deep-copy/doc/deep-copy.png vendored Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

0
pfosten/vendor/myclabs/deep-copy/doc/graph.png vendored Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Before After
Before After

91
pfosten/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php vendored Executable file → Normal file
View file

@ -2,37 +2,48 @@
namespace DeepCopy;
use DateInterval;
use DateTimeInterface;
use DateTimeZone;
use DeepCopy\Exception\CloneException;
use DeepCopy\Filter\Filter;
use DeepCopy\Matcher\Matcher;
use DeepCopy\TypeFilter\Spl\SplDoublyLinkedList;
use DeepCopy\TypeFilter\Date\DateIntervalFilter;
use DeepCopy\TypeFilter\Spl\SplDoublyLinkedListFilter;
use DeepCopy\TypeFilter\TypeFilter;
use DeepCopy\TypeMatcher\TypeMatcher;
use ReflectionObject;
use ReflectionProperty;
use DeepCopy\Reflection\ReflectionHelper;
use SplDoublyLinkedList;
/**
* DeepCopy
* @final
*/
class DeepCopy
{
/**
* @var array
* @var object[] List of objects copied.
*/
private $hashMap = [];
/**
* Filters to apply.
* @var array
*
* @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
*/
private $filters = [];
/**
* Type Filters to apply.
* @var array
*
* @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
*/
private $typeFilters = [];
/**
* @var bool
*/
private $skipUncloneable = false;
/**
@ -48,23 +59,29 @@ class DeepCopy
{
$this->useCloneMethod = $useCloneMethod;
$this->addTypeFilter(new SplDoublyLinkedList($this), new TypeMatcher('\SplDoublyLinkedList'));
$this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class));
$this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class));
}
/**
* Cloning uncloneable properties won't throw exception.
* If enabled, will not throw an exception when coming across an uncloneable property.
*
* @param $skipUncloneable
*
* @return $this
*/
public function skipUncloneable($skipUncloneable = true)
{
$this->skipUncloneable = $skipUncloneable;
return $this;
}
/**
* Perform a deep copy of the object.
* Deep copies the given object.
*
* @param mixed $object
*
* @return mixed
*/
public function copy($object)
@ -90,7 +107,6 @@ class DeepCopy
];
}
private function recursiveCopy($var)
{
// Matches Type Filter
@ -102,14 +118,17 @@ class DeepCopy
if (is_resource($var)) {
return $var;
}
// Array
if (is_array($var)) {
return $this->copyArray($var);
}
// Scalar
if (! is_object($var)) {
return $var;
}
// Object
return $this->copyObject($var);
}
@ -129,8 +148,12 @@ class DeepCopy
}
/**
* Copy an object
* Copies an object.
*
* @param object $object
*
* @throws CloneException
*
* @return object
*/
private function copyObject($object)
@ -141,29 +164,35 @@ class DeepCopy
return $this->hashMap[$objectHash];
}
$reflectedObject = new \ReflectionObject($object);
if (false === $isCloneable = $reflectedObject->isCloneable() and $this->skipUncloneable) {
$this->hashMap[$objectHash] = $object;
return $object;
}
$reflectedObject = new ReflectionObject($object);
$isCloneable = $reflectedObject->isCloneable();
if (false === $isCloneable) {
throw new CloneException(sprintf(
'Class "%s" is not cloneable.',
$reflectedObject->getName()
));
if ($this->skipUncloneable) {
$this->hashMap[$objectHash] = $object;
return $object;
}
throw new CloneException(
sprintf(
'The class "%s" is not cloneable.',
$reflectedObject->getName()
)
);
}
$newObject = clone $object;
$this->hashMap[$objectHash] = $newObject;
if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) {
return $object;
}
if ($newObject instanceof \DateTimeInterface) {
if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) {
return $newObject;
}
if ($newObject instanceof DateTimeInterface || $newObject instanceof DateTimeZone) {
return $newObject;
}
foreach (ReflectionHelper::getProperties($reflectedObject) as $property) {
$this->copyObjectProperty($newObject, $property);
}
@ -193,6 +222,7 @@ class DeepCopy
return $this->recursiveCopy($object);
}
);
// If a filter matches, we stop processing this property
return;
}
@ -206,10 +236,12 @@ class DeepCopy
}
/**
* Returns first filter that matches variable, NULL if no such filter found.
* Returns first filter that matches variable, `null` if no such filter found.
*
* @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and
* 'matcher' with value of type {@see TypeMatcher}
* @param mixed $var
*
* @return TypeFilter|null
*/
private function getFirstMatchedTypeFilter(array $filterRecords, $var)
@ -228,10 +260,13 @@ class DeepCopy
}
/**
* Returns first element that matches predicate, NULL if no such element found.
* @param array $elements
* Returns first element that matches predicate, `null` if no such element found.
*
* @param array $elements Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
* @param callable $predicate Predicate arguments are: element.
* @return mixed|null
*
* @return array|null Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and 'matcher'
* with value of type {@see TypeMatcher} or `null`.
*/
private function first(array $elements, callable $predicate)
{

View file

@ -1,6 +1,9 @@
<?php
namespace DeepCopy\Exception;
class CloneException extends \UnexpectedValueException
use UnexpectedValueException;
class CloneException extends UnexpectedValueException
{
}

View file

@ -3,19 +3,21 @@
namespace DeepCopy\Filter\Doctrine;
use DeepCopy\Filter\Filter;
use ReflectionProperty;
use DeepCopy\Reflection\ReflectionHelper;
/**
* Set a null value for a property
* @final
*/
class DoctrineCollectionFilter implements Filter
{
/**
* Copies the object property doctrine collection.
*
* {@inheritdoc}
*/
public function apply($object, $property, $objectCopier)
{
$reflectionProperty = new ReflectionProperty($object, $property);
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
$reflectionProperty->setAccessible(true);
$oldCollection = $reflectionProperty->getValue($object);

View file

@ -3,12 +3,16 @@
namespace DeepCopy\Filter\Doctrine;
use DeepCopy\Filter\Filter;
use DeepCopy\Reflection\ReflectionHelper;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @final
*/
class DoctrineEmptyCollectionFilter implements Filter
{
/**
* Apply the filter to the object.
* Sets the object property to an empty doctrine collection.
*
* @param object $object
* @param string $property
@ -16,7 +20,7 @@ class DoctrineEmptyCollectionFilter implements Filter
*/
public function apply($object, $property, $objectCopier)
{
$reflectionProperty = new \ReflectionProperty($object, $property);
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($object, new ArrayCollection());

View file

@ -5,12 +5,14 @@ namespace DeepCopy\Filter\Doctrine;
use DeepCopy\Filter\Filter;
/**
* Trigger the magic method __load() on a Doctrine Proxy class to load the
* actual entity from the database.
* @final
*/
class DoctrineProxyFilter implements Filter
{
/**
* Triggers the magic method __load() on a Doctrine Proxy class to load the
* actual entity from the database.
*
* {@inheritdoc}
*/
public function apply($object, $property, $objectCopier)

3
pfosten/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php vendored Executable file → Normal file
View file

@ -8,7 +8,8 @@ namespace DeepCopy\Filter;
interface Filter
{
/**
* Apply the filter to the object.
* Applies the filter to the object.
*
* @param object $object
* @param string $property
* @param callable $objectCopier

5
pfosten/vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php vendored Executable file → Normal file
View file

@ -2,12 +2,11 @@
namespace DeepCopy\Filter;
/**
* Keep the value of a property
*/
class KeepFilter implements Filter
{
/**
* Keeps the value of the object property.
*
* {@inheritdoc}
*/
public function apply($object, $property, $objectCopier)

View file

@ -2,8 +2,10 @@
namespace DeepCopy\Filter;
use DeepCopy\Reflection\ReflectionHelper;
/**
* Replace the value of a property
* @final
*/
class ReplaceFilter implements Filter
{
@ -21,11 +23,13 @@ class ReplaceFilter implements Filter
}
/**
* Replaces the object property by the result of the callback called with the object property.
*
* {@inheritdoc}
*/
public function apply($object, $property, $objectCopier)
{
$reflectionProperty = new \ReflectionProperty($object, $property);
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
$reflectionProperty->setAccessible(true);
$value = call_user_func($this->callback, $reflectionProperty->getValue($object));

View file

@ -2,19 +2,21 @@
namespace DeepCopy\Filter;
use ReflectionProperty;
use DeepCopy\Reflection\ReflectionHelper;
/**
* Set a null value for a property
* @final
*/
class SetNullFilter implements Filter
{
/**
* Sets the object property to null.
*
* {@inheritdoc}
*/
public function apply($object, $property, $objectCopier)
{
$reflectionProperty = new ReflectionProperty($object, $property);
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($object, null);

View file

@ -6,11 +6,13 @@ use DeepCopy\Matcher\Matcher;
use Doctrine\Common\Persistence\Proxy;
/**
* Match a Doctrine Proxy class.
* @final
*/
class DoctrineProxyMatcher implements Matcher
{
/**
* Matches a Doctrine Proxy class.
*
* {@inheritdoc}
*/
public function matches($object, $property)

4
pfosten/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php vendored Executable file → Normal file
View file

@ -2,14 +2,12 @@
namespace DeepCopy\Matcher;
/**
* Matcher interface
*/
interface Matcher
{
/**
* @param object $object
* @param string $property
*
* @return boolean
*/
public function matches($object, $property);

View file

@ -3,7 +3,7 @@
namespace DeepCopy\Matcher;
/**
* Match a specific property of a specific class
* @final
*/
class PropertyMatcher implements Matcher
{
@ -28,10 +28,12 @@ class PropertyMatcher implements Matcher
}
/**
* Matches a specific property of a specific class.
*
* {@inheritdoc}
*/
public function matches($object, $property)
{
return ($object instanceof $this->class) && ($property == $this->property);
return ($object instanceof $this->class) && $property == $this->property;
}
}

View file

@ -3,7 +3,7 @@
namespace DeepCopy\Matcher;
/**
* Match a property by its name
* @final
*/
class PropertyNameMatcher implements Matcher
{
@ -21,6 +21,8 @@ class PropertyNameMatcher implements Matcher
}
/**
* Matches a property by its name.
*
* {@inheritdoc}
*/
public function matches($object, $property)

View file

@ -2,13 +2,16 @@
namespace DeepCopy\Matcher;
use ReflectionProperty;
use DeepCopy\Reflection\ReflectionHelper;
use ReflectionException;
/**
* Match a property by its type
* Matches a property by its type.
*
* It is recommended to use {@see DeepCopy\TypeFilter\TypeFilter} instead, as it applies on all occurrences
* of given type in copied context (eg. array elements), not just on object properties.
*
* @final
*/
class PropertyTypeMatcher implements Matcher
{
@ -30,7 +33,12 @@ class PropertyTypeMatcher implements Matcher
*/
public function matches($object, $property)
{
$reflectionProperty = new ReflectionProperty($object, $property);
try {
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
} catch (ReflectionException $exception) {
return false;
}
$reflectionProperty->setAccessible(true);
return $reflectionProperty->getValue($object) instanceof $this->propertyType;

View file

@ -2,6 +2,12 @@
namespace DeepCopy\Reflection;
use DeepCopy\Exception\PropertyException;
use ReflectionClass;
use ReflectionException;
use ReflectionObject;
use ReflectionProperty;
class ReflectionHelper
{
/**
@ -12,10 +18,11 @@ class ReflectionHelper
* @author muratyaman@gmail.com
* @see http://php.net/manual/en/reflectionclass.getproperties.php
*
* @param \ReflectionClass $ref
* @return \ReflectionProperty[]
* @param ReflectionClass $ref
*
* @return ReflectionProperty[]
*/
public static function getProperties(\ReflectionClass $ref)
public static function getProperties(ReflectionClass $ref)
{
$props = $ref->getProperties();
$propsArr = array();
@ -36,4 +43,36 @@ class ReflectionHelper
return $propsArr;
}
/**
* Retrieves property by name from object and all its ancestors.
*
* @param object|string $object
* @param string $name
*
* @throws PropertyException
* @throws ReflectionException
*
* @return ReflectionProperty
*/
public static function getProperty($object, $name)
{
$reflection = is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object);
if ($reflection->hasProperty($name)) {
return $reflection->getProperty($name);
}
if ($parentClass = $reflection->getParentClass()) {
return self::getProperty($parentClass->getName(), $name);
}
throw new PropertyException(
sprintf(
'The class "%s" doesn\'t have a property with the given name: "%s".',
is_object($object) ? get_class($object) : $object,
$name
)
);
}
}

View file

@ -2,6 +2,9 @@
namespace DeepCopy\TypeFilter;
/**
* @final
*/
class ReplaceFilter implements TypeFilter
{
/**

View file

@ -2,6 +2,9 @@
namespace DeepCopy\TypeFilter;
/**
* @final
*/
class ShallowCopyFilter implements TypeFilter
{
/**

View file

@ -2,36 +2,9 @@
namespace DeepCopy\TypeFilter\Spl;
use DeepCopy\DeepCopy;
use DeepCopy\TypeFilter\TypeFilter;
class SplDoublyLinkedList implements TypeFilter
/**
* @deprecated Use {@see SplDoublyLinkedListFilter} instead.
*/
class SplDoublyLinkedList extends SplDoublyLinkedListFilter
{
/**
* @var DeepCopy
*/
private $deepCopy;
public function __construct(DeepCopy $deepCopy)
{
$this->deepCopy = $deepCopy;
}
/**
* {@inheritdoc}
*/
public function apply($element)
{
$newElement = clone $element;
if ($element instanceof \SplDoublyLinkedList) {
// Replace each element in the list with a deep copy of itself
for ($i = 1; $i <= $newElement->count(); $i++) {
$newElement->push($this->deepCopy->copy($newElement->shift()));
}
}
return $newElement;
}
}

View file

@ -5,7 +5,8 @@ namespace DeepCopy\TypeFilter;
interface TypeFilter
{
/**
* Apply the filter to the object.
* Applies the filter to the object.
*
* @param mixed $element
*/
public function apply($element);

View file

@ -2,9 +2,6 @@
namespace DeepCopy\TypeMatcher;
/**
* TypeMatcher class
*/
class TypeMatcher
{
/**
@ -21,7 +18,8 @@ class TypeMatcher
}
/**
* @param $element
* @param mixed $element
*
* @return boolean
*/
public function matches($element)