myclabs/deep-copy
Composer 安装命令:
composer require myclabs/deep-copy
包简介
Create deep copies (clones) of your objects
README 文档
README
DeepCopy helps you create deep copies (clones) of your objects. It is designed to handle cycles in the association graph.
Table of Contents
How?
Install with Composer:
composer require myclabs/deep-copy
Use it:
use DeepCopy\DeepCopy; $copier = new DeepCopy(); $myCopy = $copier->copy($myObject);
Why?
- How do you create copies of your objects?
$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() and implement the behavior
yourself.
- But how do you handle cycles in the association graph?
Now you're in for a big mess :(
Using simply clone
Overriding __clone()
With DeepCopy
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.
To use it:
use function DeepCopy\deep_copy; $copy = deep_copy($var);
Alternatively, you can create your own DeepCopy instance to configure it differently for example:
use DeepCopy\DeepCopy; $copier = new DeepCopy(true); $copy = $copier->copy($var);
You may want to roll your own deep copy function:
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\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\Matcherapplies on a object attribute.DeepCopy\TypeMatcherapplies on any element found in graph, including array elements.
Property name
The PropertyNameMatcher will match a property by its name:
use DeepCopy\Matcher\PropertyNameMatcher; // Will apply a filter to any property of any objects named "id" $matcher = new PropertyNameMatcher('id');
Specific property
The PropertyMatcher will match a specific property of a specific class:
use DeepCopy\Matcher\PropertyMatcher; // Will apply a filter to the property "id" of any objects of the class "MyClass" $matcher = new PropertyMatcher('MyClass', 'id');
Type
The TypeMatcher will match any element by its type (instance of a class or any value that could be parameter of
gettype() function):
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');
Filters
DeepCopy\Filterapplies a transformation to the object attribute matched byDeepCopy\MatcherDeepCopy\TypeFilterapplies a transformation to any element matched byDeepCopy\TypeMatcher
By design, matching a filter will stop the chain of filters (i.e. the next ones will not be applied).
Using the (ChainableFilter) won't stop the chain of filters.
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:
use DeepCopy\DeepCopy; use DeepCopy\Filter\SetNullFilter; use DeepCopy\Matcher\PropertyNameMatcher; $object = MyClass::load(123); echo $object->id; // 123 $copier = new DeepCopy(); $copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id')); $copy = $copier->copy($object); echo $copy->id; // null
KeepFilter (filter)
If you want a property to remain untouched (for example, an association to an object):
use DeepCopy\DeepCopy; use DeepCopy\Filter\KeepFilter; use DeepCopy\Matcher\PropertyMatcher; $copier = new DeepCopy(); $copier->addFilter(new KeepFilter(), new PropertyMatcher('MyClass', 'category')); $copy = $copier->copy($object); // $copy->category has not been touched
ChainableFilter (filter)
If you use cloning on proxy classes, you might want to apply two filters for:
- loading the data
- applying a transformation
You can use the ChainableFilter as a decorator of the proxy loader filter, which won't stop the chain of filters (i.e.
the next ones may be applied).
use DeepCopy\DeepCopy; use DeepCopy\Filter\ChainableFilter; use DeepCopy\Filter\Doctrine\DoctrineProxyFilter; use DeepCopy\Filter\SetNullFilter; use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher; use DeepCopy\Matcher\PropertyNameMatcher; $copier = new DeepCopy(); $copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher()); $copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id')); $copy = $copier->copy($object); echo $copy->id; // null
DoctrineCollectionFilter (filter)
If you use Doctrine and want to copy an entity, you will need to use the DoctrineCollectionFilter:
use DeepCopy\DeepCopy; use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter; use DeepCopy\Matcher\PropertyTypeMatcher; $copier = new DeepCopy(); $copier->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection')); $copy = $copier->copy($object);
DoctrineEmptyCollectionFilter (filter)
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
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!
We recommend to decorate the DoctrineProxyFilter with the ChainableFilter to allow applying other filters to the
cloned lazy loaded entities.
use DeepCopy\DeepCopy; use DeepCopy\Filter\Doctrine\DoctrineProxyFilter; use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher; $copier = new DeepCopy(); $copier->addFilter(new ChainableFilter(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)
- If you want to replace the value of a property:
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)'
- If you want to replace whole element:
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 (type filter)
Stop DeepCopy from recursively copying element, using standard clone instead:
use DeepCopy\DeepCopy; use DeepCopy\TypeFilter\ShallowCopyFilter; use DeepCopy\TypeMatcher\TypeMatcher; use Mockery as m; $this->deepCopy = new DeepCopy(); $this->deepCopy->addTypeFilter( new ShallowCopyFilter, new TypeMatcher(m\MockInterface::class) ); $myServiceWithMocks = new MyService(m::mock(MyDependency1::class), m::mock(MyDependency2::class)); // All mocks will be just cloned, not deep copied
Edge cases
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:
- Implement your own
__clone()method - Use a filter with a type matcher
Contributing
DeepCopy is distributed under the MIT license.
Tests
Running the tests is simple:
vendor/bin/phpunit
Support
Get professional support via the Tidelift Subscription.
myclabs/deep-copy 适用场景与选型建议
myclabs/deep-copy 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 892.9M 次下载、GitHub Stars 达 8.9k, 最近一次更新时间为 2013 年 07 月 22 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「object」 「clone」 「object graph」 「duplicate」 「copy」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 myclabs/deep-copy 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 myclabs/deep-copy 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 myclabs/deep-copy 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A set of useful PHP classes.
Netgen Open Graph Bundle is an Ibexa Platform bundle that allows simple integration with Open Graph protocol.
Runn Me! Value Objects Library
Laravel SDK mapping data in object
A tool for scraping URL resources (oEmbed, OpenGraph, Twitter cards, JSON-LD)
Traits gathering fluent syntax common methods
统计信息
- 总下载量: 892.9M
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 8919
- 点击次数: 29
- 依赖项目数: 174
- 推荐数: 1
其他信息
- 授权协议: MIT
- 更新时间: 2013-07-22



