Re: Design by Contract
| From: | Alexander Lisachenko | Date: | Thu, 05 Feb 2015 12:27:23 +0000 |
| Subject: | Re: Design by Contract | ||
| References: | 1 2 3 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-81913@lists.php.net to get a copy of this message | ||
So, is it possible to use annotations for defining such metadata on
engine-level? This will be a good way to do this and to define custom
handlers via AST hooks, that can be able to patch method definition node
with concrete opcodes.
2015-02-05 15:24 GMT+03:00 Alexander Lisachenko <lisachenko.it@gmail.com>:
> Hello, internals!
>
> From my point of view, contracts should not affect execution of source
> code in production env, or can be enabled partially. I have implemented DbC
> paradigm on top of the AOP layer, so each contract can be defined via
> annotation and looks pretty nice, for example :
>
> use PhpDeal\Annotation as Contract;
>
> /**
> * Simple trade account class
> * @Contract\Invariant("$this->balance > 0")
> */
> class Account implements AccountContract
> {
>
> /**
> * Current balance
> *
> * @var float
> */
> protected $balance = 0.0;
>
> /**
> * Deposits fixed amount of money to the account
> *
> * @param float $amount
> *
> * @Contract\Verify("$amount>0 && is_numeric($amount)")
> * @Contract\Ensure("$this->balance == $__old->balance+$amount")
> */
> public function deposit($amount)
> {
> $this->balance += $amount;
> }
>
> /**
> * Returns current balance
> *
> * @Contract\Ensure("$__result == $this->balance")
> * @return float
> */
> public function getBalance()
> {
> return $this->balance;
> }
> }
>
> All contracts (verify|ensure) are enforced only when DbC is enabled and
> doesn't have an impact on execution for production mode. Moreover, all
> annotations are parsed only once, decorators are generated too, so overhead
> is minimal even for debug mode. Engine takes care to insert appropriate
> before/around checks via an aspect
>
> https://github.com/lisachenko/php-deal/blob/master/src/PhpDeal/Aspect/ContractCheckerAspect.php
>
> Here is a simple snippet of pointcut definition and advice body:
>
> /**
> * Verifies pre-condition contract for the method
> *
> * @param MethodInvocation $invocation
> * @Before("@annotation(PhpDeal\Annotation\Verify)")
> *
> * @throws ContractViolation
> */
> public function preConditionContract(MethodInvocation $invocation)
> {
> $object = $invocation->getThis();
> $args = $this->getMethodArguments($invocation);
> $scope = $invocation->getMethod()->getDeclaringClass()->name;
>
> foreach ($invocation->getMethod()->getAnnotations() as $annotation) {
> if (!$annotation instanceof Contract\Verify) {
> continue;
> }
>
> if (!$this->isContractSatisfied($object, $scope, $args, $annotation)) {
> throw new ContractViolation($invocation, $annotation->value);
> };
> }
> }
>
>
>
>
>
> 2015-02-05 15:13 GMT+03:00 Patrick Schaaf <php@bof.de>:
>
>> On Thursday 05 February 2015 15:14:04 Dmitry Stogov wrote:
>> >
>> > function foo()
>> > requre(<input-assert-expression>)
>> > ensure(<output-assert-expression>)
>> > {
>> > ...
>> > }
>> >
>> > It would require only one new reserved word "ensure".
>>
>> Regarding syntax.... This could be another place where the irrationally-
>> dreaded declare would make sense:
>>
>> function foo() {
>> declare(pre) {
>> if (whatever...) return false;
>> // arbitrary PHP code
>> }
>> declare(post=$resultvar) {
>> if ($resultvar == XXX) return true;
>> return false;
>> }
>> }
>>
>> This way, no new reserved words are needed at all, and the programmer can
>> give
>> a name to the "variable that holds the result" locally to avoid clashes
>> with
>> anything else in the function.
>>
>> I'm a bit undecided whether returning true/false there to give the
>> verdict,
>> would be the best way. Maybe better would be another use for declare,
>> without
>> a block, to declare that the pre/postcondition failed - giving a nice
>> place to
>> put a suitable message, too. And again without introducing any extra
>> keywords:
>>
>> function foo() {
>> declare(post=$resultvar) {
>> if ($resultvar == XXX) declare(fail="My $resultvar looks fishy");
>> }
>> }
>>
>> best regards
>> Patrick
>>
>> --
>> PHP Internals - PHP Runtime Development Mailing List
>> To unsubscribe, visit: http://www.php.net/unsub.php
>>
>>
>