summaryrefslogtreecommitdiff
path: root/vendor/myclabs/deep-copy/src
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2022-03-22 12:24:31 +0300
committerAndrew Dolgov <[email protected]>2022-03-22 12:24:31 +0300
commit1c4f7ab3b838b23afb2ee4dab14acbf75956e952 (patch)
tree0a19274107d717efe92d2c0376cd3105fead5a11 /vendor/myclabs/deep-copy/src
parent711662948768492e8d05b778a7d80eacaec368d2 (diff)
* add phpunit as a dev dependency
* add some basic tests for UrlHelper::rewrite_relative() * fix UrlHelper::rewrite_relative() to work better on non-absolute relative URL paths
Diffstat (limited to 'vendor/myclabs/deep-copy/src')
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php303
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php9
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php9
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php33
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php28
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php22
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php18
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php16
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php39
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php24
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php22
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php14
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php39
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php32
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php52
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php78
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php33
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php30
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php17
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php36
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php10
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php51
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php13
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php29
-rw-r--r--vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php20
25 files changed, 977 insertions, 0 deletions
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php b/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
new file mode 100644
index 000000000..5e68c64e4
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
@@ -0,0 +1,303 @@
+<?php
+
+namespace DeepCopy;
+
+use ArrayObject;
+use DateInterval;
+use DateTimeInterface;
+use DateTimeZone;
+use DeepCopy\Exception\CloneException;
+use DeepCopy\Filter\Filter;
+use DeepCopy\Matcher\Matcher;
+use DeepCopy\Reflection\ReflectionHelper;
+use DeepCopy\TypeFilter\Date\DateIntervalFilter;
+use DeepCopy\TypeFilter\Spl\ArrayObjectFilter;
+use DeepCopy\TypeFilter\Spl\SplDoublyLinkedListFilter;
+use DeepCopy\TypeFilter\TypeFilter;
+use DeepCopy\TypeMatcher\TypeMatcher;
+use ReflectionObject;
+use ReflectionProperty;
+use SplDoublyLinkedList;
+
+/**
+ * @final
+ */
+class DeepCopy
+{
+ /**
+ * @var object[] List of objects copied.
+ */
+ private $hashMap = [];
+
+ /**
+ * Filters to apply.
+ *
+ * @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
+ */
+ private $filters = [];
+
+ /**
+ * Type Filters to apply.
+ *
+ * @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
+ */
+ private $typeFilters = [];
+
+ /**
+ * @var bool
+ */
+ private $skipUncloneable = false;
+
+ /**
+ * @var bool
+ */
+ 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.
+ */
+ public function __construct($useCloneMethod = false)
+ {
+ $this->useCloneMethod = $useCloneMethod;
+
+ $this->addTypeFilter(new ArrayObjectFilter($this), new TypeMatcher(ArrayObject::class));
+ $this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class));
+ $this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class));
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Deep copies the given object.
+ *
+ * @param mixed $object
+ *
+ * @return mixed
+ */
+ public function copy($object)
+ {
+ $this->hashMap = [];
+
+ return $this->recursiveCopy($object);
+ }
+
+ public function addFilter(Filter $filter, Matcher $matcher)
+ {
+ $this->filters[] = [
+ 'matcher' => $matcher,
+ 'filter' => $filter,
+ ];
+ }
+
+ public function prependFilter(Filter $filter, Matcher $matcher)
+ {
+ array_unshift($this->filters, [
+ 'matcher' => $matcher,
+ 'filter' => $filter,
+ ]);
+ }
+
+ public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher)
+ {
+ $this->typeFilters[] = [
+ 'matcher' => $matcher,
+ 'filter' => $filter,
+ ];
+ }
+
+ private function recursiveCopy($var)
+ {
+ // Matches Type Filter
+ if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) {
+ return $filter->apply($var);
+ }
+
+ // Resource
+ if (is_resource($var)) {
+ return $var;
+ }
+
+ // Array
+ if (is_array($var)) {
+ return $this->copyArray($var);
+ }
+
+ // Scalar
+ if (! is_object($var)) {
+ return $var;
+ }
+
+ // Enum
+ if (PHP_VERSION_ID >= 80100 && enum_exists(get_class($var))) {
+ return $var;
+ }
+
+ // Object
+ return $this->copyObject($var);
+ }
+
+ /**
+ * Copy an array
+ * @param array $array
+ * @return array
+ */
+ private function copyArray(array $array)
+ {
+ foreach ($array as $key => $value) {
+ $array[$key] = $this->recursiveCopy($value);
+ }
+
+ return $array;
+ }
+
+ /**
+ * Copies an object.
+ *
+ * @param object $object
+ *
+ * @throws CloneException
+ *
+ * @return object
+ */
+ private function copyObject($object)
+ {
+ $objectHash = spl_object_hash($object);
+
+ if (isset($this->hashMap[$objectHash])) {
+ return $this->hashMap[$objectHash];
+ }
+
+ $reflectedObject = new ReflectionObject($object);
+ $isCloneable = $reflectedObject->isCloneable();
+
+ if (false === $isCloneable) {
+ 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 $newObject;
+ }
+
+ if ($newObject instanceof DateTimeInterface || $newObject instanceof DateTimeZone) {
+ return $newObject;
+ }
+
+ foreach (ReflectionHelper::getProperties($reflectedObject) as $property) {
+ $this->copyObjectProperty($newObject, $property);
+ }
+
+ return $newObject;
+ }
+
+ private function copyObjectProperty($object, ReflectionProperty $property)
+ {
+ // Ignore static properties
+ if ($property->isStatic()) {
+ return;
+ }
+
+ // Apply the filters
+ foreach ($this->filters as $item) {
+ /** @var Matcher $matcher */
+ $matcher = $item['matcher'];
+ /** @var Filter $filter */
+ $filter = $item['filter'];
+
+ if ($matcher->matches($object, $property->getName())) {
+ $filter->apply(
+ $object,
+ $property->getName(),
+ function ($object) {
+ return $this->recursiveCopy($object);
+ }
+ );
+
+ // If a filter matches, we stop processing this property
+ return;
+ }
+ }
+
+ $property->setAccessible(true);
+
+ // Ignore uninitialized properties (for PHP >7.4)
+ if (method_exists($property, 'isInitialized') && !$property->isInitialized($object)) {
+ return;
+ }
+
+ $propertyValue = $property->getValue($object);
+
+ // Copy the property
+ $property->setValue($object, $this->recursiveCopy($propertyValue));
+ }
+
+ /**
+ * 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)
+ {
+ $matched = $this->first(
+ $filterRecords,
+ function (array $record) use ($var) {
+ /* @var TypeMatcher $matcher */
+ $matcher = $record['matcher'];
+
+ return $matcher->matches($var);
+ }
+ );
+
+ return isset($matched) ? $matched['filter'] : null;
+ }
+
+ /**
+ * 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 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)
+ {
+ foreach ($elements as $element) {
+ if (call_user_func($predicate, $element)) {
+ return $element;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php b/vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php
new file mode 100644
index 000000000..c046706a2
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace DeepCopy\Exception;
+
+use UnexpectedValueException;
+
+class CloneException extends UnexpectedValueException
+{
+} \ No newline at end of file
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php b/vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php
new file mode 100644
index 000000000..9702101a9
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace DeepCopy\Exception;
+
+use ReflectionException;
+
+class PropertyException extends ReflectionException
+{
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php
new file mode 100644
index 000000000..e6d937710
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace DeepCopy\Filter\Doctrine;
+
+use DeepCopy\Filter\Filter;
+use DeepCopy\Reflection\ReflectionHelper;
+
+/**
+ * @final
+ */
+class DoctrineCollectionFilter implements Filter
+{
+ /**
+ * Copies the object property doctrine collection.
+ *
+ * {@inheritdoc}
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ $reflectionProperty = ReflectionHelper::getProperty($object, $property);
+
+ $reflectionProperty->setAccessible(true);
+ $oldCollection = $reflectionProperty->getValue($object);
+
+ $newCollection = $oldCollection->map(
+ function ($item) use ($objectCopier) {
+ return $objectCopier($item);
+ }
+ );
+
+ $reflectionProperty->setValue($object, $newCollection);
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php
new file mode 100644
index 000000000..7b33fd547
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace DeepCopy\Filter\Doctrine;
+
+use DeepCopy\Filter\Filter;
+use DeepCopy\Reflection\ReflectionHelper;
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @final
+ */
+class DoctrineEmptyCollectionFilter implements Filter
+{
+ /**
+ * Sets the object property to an empty doctrine collection.
+ *
+ * @param object $object
+ * @param string $property
+ * @param callable $objectCopier
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ $reflectionProperty = ReflectionHelper::getProperty($object, $property);
+ $reflectionProperty->setAccessible(true);
+
+ $reflectionProperty->setValue($object, new ArrayCollection());
+ }
+} \ No newline at end of file
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php
new file mode 100644
index 000000000..8bee8f769
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace DeepCopy\Filter\Doctrine;
+
+use DeepCopy\Filter\Filter;
+
+/**
+ * @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)
+ {
+ $object->__load();
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php
new file mode 100644
index 000000000..85ba18ce1
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace DeepCopy\Filter;
+
+/**
+ * Filter to apply to a property while copying an object
+ */
+interface Filter
+{
+ /**
+ * Applies the filter to the object.
+ *
+ * @param object $object
+ * @param string $property
+ * @param callable $objectCopier
+ */
+ public function apply($object, $property, $objectCopier);
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php
new file mode 100644
index 000000000..4b11a081b
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace DeepCopy\Filter;
+
+class KeepFilter implements Filter
+{
+ /**
+ * Keeps the value of the object property.
+ *
+ * {@inheritdoc}
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ // Nothing to do
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php
new file mode 100644
index 000000000..7aca593bb
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace DeepCopy\Filter;
+
+use DeepCopy\Reflection\ReflectionHelper;
+
+/**
+ * @final
+ */
+class ReplaceFilter implements Filter
+{
+ /**
+ * @var callable
+ */
+ protected $callback;
+
+ /**
+ * @param callable $callable Will be called to get the new value for each property to replace
+ */
+ public function __construct(callable $callable)
+ {
+ $this->callback = $callable;
+ }
+
+ /**
+ * Replaces the object property by the result of the callback called with the object property.
+ *
+ * {@inheritdoc}
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ $reflectionProperty = ReflectionHelper::getProperty($object, $property);
+ $reflectionProperty->setAccessible(true);
+
+ $value = call_user_func($this->callback, $reflectionProperty->getValue($object));
+
+ $reflectionProperty->setValue($object, $value);
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php
new file mode 100644
index 000000000..bea86b884
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace DeepCopy\Filter;
+
+use DeepCopy\Reflection\ReflectionHelper;
+
+/**
+ * @final
+ */
+class SetNullFilter implements Filter
+{
+ /**
+ * Sets the object property to null.
+ *
+ * {@inheritdoc}
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ $reflectionProperty = ReflectionHelper::getProperty($object, $property);
+
+ $reflectionProperty->setAccessible(true);
+ $reflectionProperty->setValue($object, null);
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php b/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php
new file mode 100644
index 000000000..c5887b19c
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace DeepCopy\Matcher\Doctrine;
+
+use DeepCopy\Matcher\Matcher;
+use Doctrine\Persistence\Proxy;
+
+/**
+ * @final
+ */
+class DoctrineProxyMatcher implements Matcher
+{
+ /**
+ * Matches a Doctrine Proxy class.
+ *
+ * {@inheritdoc}
+ */
+ public function matches($object, $property)
+ {
+ return $object instanceof Proxy;
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php b/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php
new file mode 100644
index 000000000..d67f3cac6
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace DeepCopy\Matcher;
+
+interface Matcher
+{
+ /**
+ * @param object $object
+ * @param string $property
+ *
+ * @return boolean
+ */
+ public function matches($object, $property);
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php b/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php
new file mode 100644
index 000000000..073b20cb8
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace DeepCopy\Matcher;
+
+/**
+ * @final
+ */
+class PropertyMatcher implements Matcher
+{
+ /**
+ * @var string
+ */
+ private $class;
+
+ /**
+ * @var string
+ */
+ private $property;
+
+ /**
+ * @param string $class Class name
+ * @param string $property Property name
+ */
+ public function __construct($class, $property)
+ {
+ $this->class = $class;
+ $this->property = $property;
+ }
+
+ /**
+ * Matches a specific property of a specific class.
+ *
+ * {@inheritdoc}
+ */
+ public function matches($object, $property)
+ {
+ return ($object instanceof $this->class) && $property == $this->property;
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php b/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php
new file mode 100644
index 000000000..c8ec0d2bc
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace DeepCopy\Matcher;
+
+/**
+ * @final
+ */
+class PropertyNameMatcher implements Matcher
+{
+ /**
+ * @var string
+ */
+ private $property;
+
+ /**
+ * @param string $property Property name
+ */
+ public function __construct($property)
+ {
+ $this->property = $property;
+ }
+
+ /**
+ * Matches a property by its name.
+ *
+ * {@inheritdoc}
+ */
+ public function matches($object, $property)
+ {
+ return $property == $this->property;
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php b/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php
new file mode 100644
index 000000000..c7f46908c
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace DeepCopy\Matcher;
+
+use DeepCopy\Reflection\ReflectionHelper;
+use ReflectionException;
+
+/**
+ * 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
+{
+ /**
+ * @var string
+ */
+ private $propertyType;
+
+ /**
+ * @param string $propertyType Property type
+ */
+ public function __construct($propertyType)
+ {
+ $this->propertyType = $propertyType;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function matches($object, $property)
+ {
+ try {
+ $reflectionProperty = ReflectionHelper::getProperty($object, $property);
+ } catch (ReflectionException $exception) {
+ return false;
+ }
+
+ $reflectionProperty->setAccessible(true);
+
+ // Uninitialized properties (for PHP >7.4)
+ if (method_exists($reflectionProperty, 'isInitialized') && !$reflectionProperty->isInitialized($object)) {
+ // null instanceof $this->propertyType
+ return false;
+ }
+
+ return $reflectionProperty->getValue($object) instanceof $this->propertyType;
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php b/vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php
new file mode 100644
index 000000000..742410cb2
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace DeepCopy\Reflection;
+
+use DeepCopy\Exception\PropertyException;
+use ReflectionClass;
+use ReflectionException;
+use ReflectionObject;
+use ReflectionProperty;
+
+class ReflectionHelper
+{
+ /**
+ * Retrieves all properties (including private ones), from object and all its ancestors.
+ *
+ * Standard \ReflectionClass->getProperties() does not return private properties from ancestor classes.
+ *
+ * @author [email protected]
+ * @see http://php.net/manual/en/reflectionclass.getproperties.php
+ *
+ * @param ReflectionClass $ref
+ *
+ * @return ReflectionProperty[]
+ */
+ public static function getProperties(ReflectionClass $ref)
+ {
+ $props = $ref->getProperties();
+ $propsArr = array();
+
+ foreach ($props as $prop) {
+ $propertyName = $prop->getName();
+ $propsArr[$propertyName] = $prop;
+ }
+
+ if ($parentClass = $ref->getParentClass()) {
+ $parentPropsArr = self::getProperties($parentClass);
+ foreach ($propsArr as $key => $property) {
+ $parentPropsArr[$key] = $property;
+ }
+
+ return $parentPropsArr;
+ }
+
+ 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
+ )
+ );
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php
new file mode 100644
index 000000000..becd1cfff
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace DeepCopy\TypeFilter\Date;
+
+use DateInterval;
+use DeepCopy\TypeFilter\TypeFilter;
+
+/**
+ * @final
+ *
+ * @deprecated Will be removed in 2.0. This filter will no longer be necessary in PHP 7.1+.
+ */
+class DateIntervalFilter implements TypeFilter
+{
+
+ /**
+ * {@inheritdoc}
+ *
+ * @param DateInterval $element
+ *
+ * @see http://news.php.net/php.bugs/205076
+ */
+ public function apply($element)
+ {
+ $copy = new DateInterval('P0D');
+
+ foreach ($element as $propertyName => $propertyValue) {
+ $copy->{$propertyName} = $propertyValue;
+ }
+
+ return $copy;
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php
new file mode 100644
index 000000000..164f8b8e2
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace DeepCopy\TypeFilter;
+
+/**
+ * @final
+ */
+class ReplaceFilter implements TypeFilter
+{
+ /**
+ * @var callable
+ */
+ protected $callback;
+
+ /**
+ * @param callable $callable Will be called to get the new value for each element to replace
+ */
+ public function __construct(callable $callable)
+ {
+ $this->callback = $callable;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($element)
+ {
+ return call_user_func($this->callback, $element);
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php
new file mode 100644
index 000000000..a5fbd7a2b
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace DeepCopy\TypeFilter;
+
+/**
+ * @final
+ */
+class ShallowCopyFilter implements TypeFilter
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($element)
+ {
+ return clone $element;
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php
new file mode 100644
index 000000000..178460179
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php
@@ -0,0 +1,36 @@
+<?php
+namespace DeepCopy\TypeFilter\Spl;
+
+use DeepCopy\DeepCopy;
+use DeepCopy\TypeFilter\TypeFilter;
+
+/**
+ * In PHP 7.4 the storage of an ArrayObject isn't returned as
+ * ReflectionProperty. So we deep copy its array copy.
+ */
+final class ArrayObjectFilter implements TypeFilter
+{
+ /**
+ * @var DeepCopy
+ */
+ private $copier;
+
+ public function __construct(DeepCopy $copier)
+ {
+ $this->copier = $copier;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($arrayObject)
+ {
+ $clone = clone $arrayObject;
+ foreach ($arrayObject->getArrayCopy() as $k => $v) {
+ $clone->offsetSet($k, $this->copier->copy($v));
+ }
+
+ return $clone;
+ }
+}
+
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php
new file mode 100644
index 000000000..c5644cff8
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace DeepCopy\TypeFilter\Spl;
+
+/**
+ * @deprecated Use {@see SplDoublyLinkedListFilter} instead.
+ */
+class SplDoublyLinkedList extends SplDoublyLinkedListFilter
+{
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php
new file mode 100644
index 000000000..c33be4580
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace DeepCopy\TypeFilter\Spl;
+
+use Closure;
+use DeepCopy\DeepCopy;
+use DeepCopy\TypeFilter\TypeFilter;
+use SplDoublyLinkedList;
+
+/**
+ * @final
+ */
+class SplDoublyLinkedListFilter implements TypeFilter
+{
+ private $copier;
+
+ public function __construct(DeepCopy $copier)
+ {
+ $this->copier = $copier;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($element)
+ {
+ $newElement = clone $element;
+
+ $copy = $this->createCopyClosure();
+
+ return $copy($newElement);
+ }
+
+ private function createCopyClosure()
+ {
+ $copier = $this->copier;
+
+ $copy = function (SplDoublyLinkedList $list) use ($copier) {
+ // Replace each element in the list with a deep copy of itself
+ for ($i = 1; $i <= $list->count(); $i++) {
+ $copy = $copier->recursiveCopy($list->shift());
+
+ $list->push($copy);
+ }
+
+ return $list;
+ };
+
+ return Closure::bind($copy, null, DeepCopy::class);
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php
new file mode 100644
index 000000000..5785a7da9
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace DeepCopy\TypeFilter;
+
+interface TypeFilter
+{
+ /**
+ * Applies the filter to the object.
+ *
+ * @param mixed $element
+ */
+ public function apply($element);
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php b/vendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php
new file mode 100644
index 000000000..a563cb294
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace DeepCopy\TypeMatcher;
+
+class TypeMatcher
+{
+ /**
+ * @var string
+ */
+ private $type;
+
+ /**
+ * @param string $type
+ */
+ public function __construct($type)
+ {
+ $this->type = $type;
+ }
+
+ /**
+ * @param mixed $element
+ *
+ * @return boolean
+ */
+ public function matches($element)
+ {
+ return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type;
+ }
+}
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php b/vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php
new file mode 100644
index 000000000..55dcc9261
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace DeepCopy;
+
+use function function_exists;
+
+if (false === function_exists('DeepCopy\deep_copy')) {
+ /**
+ * Deep copies the given value.
+ *
+ * @param mixed $value
+ * @param bool $useCloneMethod
+ *
+ * @return mixed
+ */
+ function deep_copy($value, $useCloneMethod = false)
+ {
+ return (new DeepCopy($useCloneMethod))->copy($value);
+ }
+}