Re: Design by Contract
| From: | Dmitry Stogov | Date: | Thu, 05 Feb 2015 12:35:18 +0000 |
| Subject: | Re: Design by Contract | ||
| References: | 1 2 3 4 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-81918@lists.php.net to get a copy of this message | ||
For now, we don't have any mechanism for annotations or attributes except
for doc-comments.
Thanks. Dmitry.
On Thu, Feb 5, 2015 at 3:27 PM, Alexander Lisachenko <
lisachenko.it@gmail.com> wrote:
> 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
>>>
>>>
>>
>