Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:81918 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 72732 invoked from network); 5 Feb 2015 12:35:22 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 5 Feb 2015 12:35:22 -0000 Authentication-Results: pb1.pair.com header.from=dmitry@zend.com; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=dmitry@zend.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain zend.com designates 209.85.220.177 as permitted sender) X-PHP-List-Original-Sender: dmitry@zend.com X-Host-Fingerprint: 209.85.220.177 mail-vc0-f177.google.com Received: from [209.85.220.177] ([209.85.220.177:51576] helo=mail-vc0-f177.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 0D/29-27691-88363D45 for ; Thu, 05 Feb 2015 07:35:21 -0500 Received: by mail-vc0-f177.google.com with SMTP id hy10so322578vcb.8 for ; Thu, 05 Feb 2015 04:35:18 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:date :message-id:subject:from:to:cc:content-type; bh=aXzaSz4W3NKY2CcnDFQe9TuJpAYGUUL+VIjiFWtggJY=; b=XJOfWhsSNSXjs1JIJgMO3pnlsOioe/qPOGynfxFYQqGvyPIxNCsH+xIxg4I4F7xVOH mVbN5a+aSZ3x43efKEZKWE6wh3YlSsQO+4Aqa1yTtMv8CGV9paJx5V7dIXDF7MfkgUnK AfV/JcmV6dnSWxhatmss9DdtXh7t5q16wuwvcC+aMmVDtGm5CEZRJXPqhNmkwgSbrMzo waV7lWONLs37ENpGe+CEFhH9UJe62d/o8B/eXwam/31N6eoA5xTwoUCwRAcdVp16CI5l 4mnoZ9mFU/zckjIxaB8Y7pbvda6AB7zsKVMM4lLOjvwg8Z+6eHG0yqEFL1h4ZSUHyWwe 5Dig== X-Gm-Message-State: ALoCoQlBb7Azi9kh9Qb8UT45YdQIHsPW7P0o8isVa+pNbS6Dwqd4q+io+/+MGu1vQtcej02jzf00Uj/dlNV1FbwOksx9GEh8qKZF0iCQj79BwAxIqWtpXYtTGbBKB6x/PHMS/UQm78Ipt0XboElW8WwNhZIJRSrv+A== MIME-Version: 1.0 X-Received: by 10.52.103.75 with SMTP id fu11mr1609221vdb.5.1423139718367; Thu, 05 Feb 2015 04:35:18 -0800 (PST) Received: by 10.52.74.73 with HTTP; Thu, 5 Feb 2015 04:35:18 -0800 (PST) In-Reply-To: References: <7740874.yAST26hVNS@rofl> Date: Thu, 5 Feb 2015 16:35:18 +0400 Message-ID: To: Alexander Lisachenko Cc: Patrick Schaaf , PHP internals list , Yasuo Ohgaki Content-Type: multipart/alternative; boundary=047d7b86d990611009050e568962 Subject: Re: [PHP-DEV] Design by Contract From: dmitry@zend.com (Dmitry Stogov) --047d7b86d990611009050e568962 Content-Type: text/plain; charset=UTF-8 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 : > >> 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 : >> >>> On Thursday 05 February 2015 15:14:04 Dmitry Stogov wrote: >>> > >>> > function foo() >>> > requre() >>> > ensure() >>> > { >>> > ... >>> > } >>> > >>> > 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 >>> >>> >> > --047d7b86d990611009050e568962--