/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
https://gitlab.com/mdabutaleb/bitm-laravel-1 · PHP · 243 lines · 141 code · 36 blank · 66 comment · 13 complexity · 35c386febd2f1ab960661a700389eb00 MD5 · raw file
- <?php
- namespace DeepCopy;
- use DeepCopy\Exception\CloneException;
- use DeepCopy\Filter\Filter;
- use DeepCopy\Matcher\Matcher;
- use DeepCopy\TypeFilter\TypeFilter;
- use DeepCopy\TypeMatcher\TypeMatcher;
- use ReflectionProperty;
- use DeepCopy\Reflection\ReflectionHelper;
- /**
- * DeepCopy
- */
- class DeepCopy
- {
- /**
- * @var array
- */
- private $hashMap = [];
- /**
- * Filters to apply.
- * @var array
- */
- private $filters = [];
- /**
- * Type Filters to apply.
- * @var array
- */
- private $typeFilters = [];
- 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;
- }
- /**
- * Cloning uncloneable properties won't throw exception.
- * @param $skipUncloneable
- * @return $this
- */
- public function skipUncloneable($skipUncloneable = true)
- {
- $this->skipUncloneable = $skipUncloneable;
- return $this;
- }
- /**
- * Perform a deep copy of the 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 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;
- }
- // 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;
- }
- /**
- * Copy an object
- * @param object $object
- * @return object
- */
- private function copyObject($object)
- {
- $objectHash = spl_object_hash($object);
- if (isset($this->hashMap[$objectHash])) {
- return $this->hashMap[$objectHash];
- }
- $reflectedObject = new \ReflectionObject($object);
- if (false === $isCloneable = $reflectedObject->isCloneable() and $this->skipUncloneable) {
- $this->hashMap[$objectHash] = $object;
- return $object;
- }
- if (false === $isCloneable) {
- throw new CloneException(sprintf(
- '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) {
- 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);
- $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
- * @param callable $predicate Predicate arguments are: element.
- * @return mixed|null
- */
- private function first(array $elements, callable $predicate)
- {
- foreach ($elements as $element) {
- if (call_user_func($predicate, $element)) {
- return $element;
- }
- }
- return null;
- }
- }