giritli / contract
此包最新版本(v0.1.2)没有提供许可证信息。
易于使用的合约类。
v0.1.2
2015-07-12 02:39 UTC
Requires
- php: >=5.4.0
Requires (Dev)
- phpunit/phpunit: ~4.0
This package is auto-updated.
Last update: 2024-09-21 20:10:37 UTC
README
合约
合约是PHP实现的合约模式。它允许您围绕现有类包裹一个合约,拦截结果和类状态的要求和结果。如果违反合约,则合约类还会回滚状态。
安装
要使用composer安装此包,请运行以下命令
composer require giritli/contract
示例用法
创建一个将被合约包裹的初始类。
// Create your normal class class Account { public $balance = 0; public function deposit($amount) { $this->balance += (int) $amount; } public function getBalance() { return $this->balance; } }
要使一个类成为合约,它必须扩展要包裹的类,并实现\Giritli\Contract\Contract。接口的实现包含为特质的\Giritli\Contract\ContractTrait。
要在方法上创建合约,定义一个存在于父类中的方法。使用接口可以使这更容易。合约接口提供了3个方法
requires(\Closure $callback, $message = null)- 传递给此方法的任何闭包都将传递所有方法参数。它应该与父方法具有相同的参数定义。
- 闭包必须返回true或false,如果参数上的合约通过。
- 消息是在合约失败时抛出的异常消息。
- 这可以用于参数验证的良好用途。
ensures(\Closure $callback, $message = null)- 传递给此方法的任何闭包都将具有初始父方法的返回值。闭包还绑定到对象,以便可以使用
$this变量检查对象的状态。 - 消息是在合约失败时抛出的异常消息。
- 传递给此方法的任何闭包都将具有初始父方法的返回值。闭包还绑定到对象,以便可以使用
enforce()- 此方法指定父方法何时被调用。必须在调用
enforce()之前定义所有合约。
- 此方法指定父方法何时被调用。必须在调用
示例用法
// Create the contract class class AccountContract extends Account implements \Giritli\Contract\Contract { use \Giritli\Contract\ContractTrait; public function deposit($amount) { $this->requires(function($amount) { return is_numeric($amount) && $amount >= 0; }, 'Amount must be a number and non negative.'); $this->ensures(function($result) { return $this->balance !== 5; }, 'Account cannot have a balance of 5.'); return $this->enforce(); } }
创建您的类和合约后,您可以使用它就像一个常规类一样
$account = new AccountContract(); try { $account->deposit(4); } catch (\Giritli\Contract\Exception\ContractFailedException $e) { } $account->getBalance(); // 4 /** * AccountContract is always instance of Account, * conforms to Liskov substitution principle */ $account instanceof Account; // true
如果合约失败,对象将回滚到最后一个良好状态
$account = new AccountContract(); try { $account->deposit(3); $account->deposit(2); // Will fail ensures contract as balance is now 5 } catch (\Giritli\Contract\Exception\EnsureContractFailedException $e) { echo $e->getMessage(); // Account cannot have a balance of 5. } $account->getBalance(); // 3
它如何工作?
合约特质欺骗性地简单。当您使用require()或ensure()和enforce()定义合约方法时,它会跟踪调用堆栈以获取传递给初始方法(旨在传递给父方法)的参数。然后将这些参数传递给require合约并执行。如果任何合约失败,将抛出异常,方法不会执行。
执行完require合约后,该类然后克隆自己,并在克隆上调用enforce方法。此方法运行父方法然后运行ensure合约。如果任何ensure合约失败,将抛出异常并退出方法。如果没有抛出异常,对克隆对象的调用完成并继续到非克隆的ensure方法,然后将其应用于活动对象的所有更改。