eniams / spy
处理程序,以了解对象是否被修改
v0.1-beta.5
2020-05-10 15:51 UTC
Requires
- php: ^7.2
- myclabs/deep-copy: 1.9.1
- symfony/config: ^4.3|^5.0
- symfony/http-kernel: ^4.3|^5.0
- symfony/proxy-manager-bridge: ^3.4|^4.4|^5.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.17@dev
- phpstan/phpstan: ^0.12.0@dev
- phpunit/phpunit: 8.0
- symfony/dependency-injection: ^4.3|^5.0
- symfony/phpunit-bridge: ^5.0
This package is auto-updated.
Last update: 2024-09-12 05:31:38 UTC
README
Spy 帮助您了解对象是否被修改,并允许您在给定的对象被修改时触发/监听事件。
⚠️ 此项目正在开发中。 ⚠️
安装
$ composer require eniams/spy
它是如何工作的?
幕后
初始对象将使用特定的克隆器进行复制,然后根据需要比较初始(复制的)对象和操作(当前)对象的值,以检查是否有修改。
假设您想监视一个 foo
对象,以了解它是否被修改
- 为要监视的类标记一个接口,该接口将对应于选择的克隆器,有 2 个内置克隆器
Eniams\Spy\Cloner\DeepCopyClonerInterface
,它使用著名的库 DeepCopy
或
Eniams\Spy\Cloner\SpyClonerLoadPropertyObjectInterface
或Eniams\Spy\Cloner\SpyClonerInterface
,它允许您更深入地克隆存储在属性中的对象/数组
<?php namespace App\Entity\Foo; class Foo implements \Eniams\Spy\Cloner\DeepCopyClonerInterface // or SpyClonerLoadPropertyObjectInterface // or SpyClonerInterface {}
您可以创建一个自定义的克隆器来复制您的对象
- 创建一个应实现
Eniams\Spy\Cloner\ClonerInterface
的克隆器。 - 创建一个与创建的克隆器相关的接口,该接口应实现
Eniams\Spy\SpyInterface
。
<?php namespace App\Service; // Create the Cloner class UserLandCloner implements \Eniams\Spy\Cloner\ClonerInterface { public function doClone($object) { // Stuff to clone the given $object. } public function supports($object): bool { return $object instanceof UserLandClonerInterface; } } // Create the Interface interface UserLandClonerInterface extends \Eniams\Spy\SpyInterface // Use your Cloner (Implement the created interface in the class) class Foo implements \Eniams\Spy\Cloner\UserLandClonerInterface
如果您使用 Symfony,则可利用 自动配置标签,您无需执行下一步,创建的克隆器将被注册到 ChainCloner
中,该克隆器负责克隆要监视的对象。因此,您可以进入第 3 步。
- 对于纯 PHP,如果您不想使用默认的克隆器,您可以在
Eniams\Spy\ClonerChainCloner
中注册您的克隆器
<?php $chainCloner = new \Eniams\Spy\Cloner\ChainCloner([new UserLandCloner()]);
- 现在是监视对象的时候了
<?php // $chainCloner is optional and need to be use only if you want to use a custom cloners, // for Symfony remember that your custom cloner is already registered in the `ChainCloner $chainCloner` and it is a public service that can be retrieve from the container. $spied = new \Eniams\Spy\Spy($foo, $chainCloner); $spied->isModified(); $spied->isNotModified();
现在,您想了解是否修改了特定的属性,并获取初始值和当前值。
<?php $foo = (new \App\Entity\Foo())->setName('Smaone'); $spied = new \Eniams\Spy\Spy($foo, $chainCloner); $foo->setName('Dude'); $spied->isPropertyModified('name'); // output true $propertyState = $spied->getPropertyState('name'); $propertyState->getFqcn(); // App\Entity\Foo $propertyState->getProperty(); // 'name' $propertyState->getInitialValue(); // 'Smaone' $propertyState->getCurrentValue(); // 'Dude'
使用服务容器,您可以将对象存储在 SpyBase
中,以便稍后在您的应用程序中检索它
Symfony
<?php class FooController extends AbstractController { /** * @Route("/foo", name="foo") */ public function index(SpyBase $spyBase) { $user = (new \Foo\Entity\User())->setName('Smaone'); $spyBase = (new \Eniams\Spy\SpyBase()); $spyBase->add('your_key', $user);
纯 PHP
<?php $user = (new \Foo\Entity\User())->setName('Smaone'); $spyBase = (new \Eniams\Spy\SpyBase()); $spyBase->add('your_key', $user); // behind the scene $object is converted to a \Eniams\Spy\Spy object and the cloner class will be resolve by the interface implemented by the $object. $yourContainer ->register('spy_base_service', $spyBase); $spyBase = $yourContainer->get('spy_base_service'); // fetch the object $spyBase->get('your_key'); // remove $spyBase->remove('your_key');
对于不需要克隆对象的简单用例,您也可以检查两个“相同”类之间的差异。
<?php $firstUser = (new App\Entity\User())->setName('Smaone'); $secondUser = (new App\Entity\User())->setName('Dude'); $propertyState = Eniams\Spy\Property\PropertyStateFactory::createPropertyState('name', $firstUser, $secondUser); $propertyState->getFqcn(); // App\Entity\User $propertyState->getProperty(); // 'name' $propertyState->getInitialValue(); // 'Smaone' $propertyState->getCurrentValue(); // 'Dude'
更高级的用例
您可以定义一个上下文来检查一些属性。
<?php class User implements SpyClonerInterface, PropertyCheckerContextInterface { private $age; private $adresse; private $firstname; public static function propertiesInContext(): array { return [ 'context_check_firstname' => ['firstname', 'age'], 'context_check_adresse' => ['adresse'], ]; } } // index.php $spied->isModifiedInContext(['context_check_firstname']); // true only if 'firstname', 'age' were modified $spied->isModifiedInContext(['context_check_adresse']); // true only if 'adresse' is modified $spied->getPropertiesModifiedInContext(['context_check_adresse']); // return modified properties for context context_check_adresse
您可以动态地定义要检查的属性。
<?php class User implements SpyClonerInterface{ private $age; private $adresse; private $firstname; } // index.php $spied->isModifiedForProperties(['age']); // true only if age was modified
您可以排除一些属性。
<?php class User implements SpyClonerInterface, PropertyCheckerBlackListInterface { private $age; private $adresse; private $firstname; public static function propertiesBlackList(): array { return ['age']; } } // index.php $user->setAge(33); $spied->isModified(); // return false because $age is blacklisted $spied->getPropertiesModifiedWithoutBlackListContext(); // return age even it's blacklisted