Skip to content

Commit

Permalink
Merge pull request #13 from lisachenko/simplify-code-after-changes
Browse files Browse the repository at this point in the history
Simplify code after PR
  • Loading branch information
lisachenko committed Apr 3, 2016
2 parents 56c4bea + e1abbae commit a1e3bd9
Show file tree
Hide file tree
Showing 13 changed files with 285 additions and 469 deletions.
97 changes: 97 additions & 0 deletions src/Aspect/AbstractContractAspect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* PHP Deal framework
*
* @copyright Copyright 2014, Lisachenko Alexander <lisachenko.it@gmail.com>
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/

namespace PhpDeal\Aspect;

use Doctrine\Common\Annotations\Annotation;
use Doctrine\Common\Annotations\Reader;
use DomainException;
use Go\Aop\Intercept\MethodInvocation;
use PhpDeal\Exception\ContractViolation;

abstract class AbstractContractAspect
{
/**
* @var Reader
*/
protected $reader;

/**
* @param Reader $reader Annotation reader
*/
public function __construct(Reader $reader)
{
$this->reader = $reader;
}

/**
* Returns an associative list of arguments for the method invocation
*
* @param MethodInvocation $invocation
* @return array
*/
protected function fetchMethodArguments(MethodInvocation $invocation)
{
$parameters = $invocation->getMethod()->getParameters();
$argumentNames = array_map(function (\ReflectionParameter $parameter) {
return $parameter->name;
}, $parameters);
$parameters = array_combine($argumentNames, $invocation->getArguments());

return $parameters;
}

/**
* Performs verification of contracts for given invocation
*
* @param MethodInvocation $invocation Current invocation
* @param array|Annotation[] $contracts Contract annotation
* @param object|string $instance Invocation instance or string for static class
* @param string $scope Scope of method
* @param array $args List of arguments for the method
*
* @throws DomainException
*/
protected function ensureContracts(MethodInvocation $invocation, array $contracts, $instance, $scope, array $args)
{
static $invoker = null;
if (!$invoker) {
$invoker = function () {
extract(func_get_arg(0));

return eval('return ' . func_get_arg(1) . '; ?>');
};
}

$instance = is_object($instance) ? $instance : null;
$boundInvoker = $invoker->bindTo($instance, $scope);

foreach ($contracts as $contract) {
$contractExpression = $contract->value;
try {
$invocationResult = $boundInvoker->__invoke($args, $contractExpression);

// we accept as a result only true or null
// null may be a result of assertions from beberlei/assert which passed
if ($invocationResult !== null && $invocationResult !== true) {
$errorMessage = 'Invalid return value received from the assertion body,'
. ' only boolean or void can be returned';
throw new DomainException($errorMessage);
}

} catch (\Error $internalError) {
// PHP-7 friendly interceptor for fatal errors
throw new ContractViolation($invocation, $contractExpression, $internalError);
} catch (\Exception $internalException) {
throw new ContractViolation($invocation, $contractExpression, $internalException);
}
}
}
}
44 changes: 38 additions & 6 deletions src/Aspect/InvariantCheckerAspect.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,23 @@
use Doctrine\Common\Annotations\Reader;
use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use PhpDeal\Contract\InvariantContract;
use PhpDeal\Annotation\Invariant;
use PhpDeal\Contract\Fetcher\ParentClass\InvariantFetcher;
use PhpDeal\Exception\ContractViolation;
use Go\Lang\Annotation\Around;
use ReflectionClass;

class InvariantCheckerAspect implements Aspect
class InvariantCheckerAspect extends AbstractContractAspect implements Aspect
{
/**
* @var InvariantContract
* @var InvariantFetcher
*/
private $contractChecker;
private $invariantFetcher;

public function __construct(Reader $reader)
{
$this->contractChecker = new InvariantContract($reader);
parent::__construct($reader);
$this->invariantFetcher = new InvariantFetcher(Invariant::class, $reader);
}

/**
Expand All @@ -40,6 +43,35 @@ public function __construct(Reader $reader)
*/
public function invariantContract(MethodInvocation $invocation)
{
return $this->contractChecker->check($invocation);
$object = $invocation->getThis();
$args = $this->fetchMethodArguments($invocation);
$class = $invocation->getMethod()->getDeclaringClass();
if ($class->isCloneable()) {
$args['__old'] = clone $object;
}

$result = $invocation->proceed();
$args['__result'] = $result;

$allContracts = $this->fetchAllContracts($class);
$this->ensureContracts($invocation, $allContracts, $object, $class->name, $args);

return $result;
}

/**
* @param ReflectionClass $class
* @return array
*/
private function fetchAllContracts(ReflectionClass $class)
{
$allContracts = $this->invariantFetcher->getConditions($class);
foreach ($this->reader->getClassAnnotations($class) as $annotation) {
if ($annotation instanceof Invariant) {
$allContracts[] = $annotation;
}
}

return array_unique($allContracts);
}
}
55 changes: 49 additions & 6 deletions src/Aspect/PostconditionCheckerAspect.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,22 @@
use Doctrine\Common\Annotations\Reader;
use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use PhpDeal\Contract\PostconditionContract;
use PhpDeal\Annotation\Ensure;
use PhpDeal\Contract\Fetcher\ParentClass\MethodConditionFetcher;
use PhpDeal\Exception\ContractViolation;
use Go\Lang\Annotation\Around;

class PostconditionCheckerAspect implements Aspect
class PostconditionCheckerAspect extends AbstractContractAspect implements Aspect
{
/**
* @var PostconditionContract
* @var MethodConditionFetcher
*/
private $contractChecker;
private $methodConditionFetcher;

public function __construct(Reader $reader)
{
$this->contractChecker = new PostconditionContract($reader);
parent::__construct($reader);
$this->methodConditionFetcher = new MethodConditionFetcher(Ensure::class, $reader);
}

/**
Expand All @@ -40,6 +42,47 @@ public function __construct(Reader $reader)
*/
public function postConditionContract(MethodInvocation $invocation)
{
return $this->contractChecker->check($invocation);
$object = $invocation->getThis();
$args = $this->fetchMethodArguments($invocation);
$class = $invocation->getMethod()->getDeclaringClass();
if ($class->isCloneable()) {
$args['__old'] = clone $object;
}

$result = $invocation->proceed();
$args['__result'] = $result;
$allContracts = $this->fetchAllContracts($invocation);

$this->ensureContracts($invocation, $allContracts, $object, $class->name, $args);

return $result;
}

/**
* @param MethodInvocation $invocation
* @return array
*/
private function fetchAllContracts(MethodInvocation $invocation)
{
$allContracts = $this->fetchParentsContracts($invocation);
foreach ($invocation->getMethod()->getAnnotations() as $annotation) {
if ($annotation instanceof Ensure) {
$allContracts[] = $annotation;
}
}

return array_unique($allContracts);
}

/**
* @param MethodInvocation $invocation
* @return array
*/
private function fetchParentsContracts(MethodInvocation $invocation)
{
return $this->methodConditionFetcher->getConditions(
$invocation->getMethod()->getDeclaringClass(),
$invocation->getMethod()->name
);
}
}
48 changes: 42 additions & 6 deletions src/Aspect/PreconditionCheckerAspect.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,22 @@
use Doctrine\Common\Annotations\Reader;
use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use PhpDeal\Contract\PreconditionContract;
use PhpDeal\Annotation\Verify;
use PhpDeal\Contract\Fetcher\ParentClass\MethodConditionWithInheritDocFetcher;
use PhpDeal\Exception\ContractViolation;
use Go\Lang\Annotation\Before;

class PreconditionCheckerAspect implements Aspect
class PreconditionCheckerAspect extends AbstractContractAspect implements Aspect
{
/**
* @var PreconditionContract
* @var MethodConditionWithInheritDocFetcher
*/
private $contractChecker;
private $methodConditionFetcher;

public function __construct(Reader $reader)
{
$this->contractChecker = new PreconditionContract($reader);
parent::__construct($reader);
$this->methodConditionFetcher = new MethodConditionWithInheritDocFetcher(Verify::class, $reader);
}

/**
Expand All @@ -39,6 +41,40 @@ public function __construct(Reader $reader)
*/
public function preConditionContract(MethodInvocation $invocation)
{
$this->contractChecker->check($invocation);
$object = $invocation->getThis();
$args = $this->fetchMethodArguments($invocation);
$scope = $invocation->getMethod()->getDeclaringClass()->name;

$allContracts = $this->fetchAllContracts($invocation);
$this->ensureContracts($invocation, $allContracts, $object, $scope, $args);
}

/**
* @param MethodInvocation $invocation
* @return array
*/
private function fetchAllContracts(MethodInvocation $invocation)
{
$allContracts = $this->fetchParentsContracts($invocation);

foreach ($invocation->getMethod()->getAnnotations() as $annotation) {
if ($annotation instanceof Verify) {
$allContracts[] = $annotation;
}
}

return array_unique($allContracts);
}

/**
* @param MethodInvocation $invocation
* @return array
*/
private function fetchParentsContracts(MethodInvocation $invocation)
{
return $this->methodConditionFetcher->getConditions(
$invocation->getMethod()->getDeclaringClass(),
$invocation->getMethod()->name
);
}
}
Loading

0 comments on commit a1e3bd9

Please sign in to comment.