...
 
Commits (43)
......@@ -22,9 +22,6 @@ use Hgraca\AppMapper\Core\Port\Parser\AstMapInterface;
use Hgraca\AppMapper\Core\Port\Parser\Node\AdapterNodeCollection;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Node\NodeAdapterFactory;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\FindingVisitor;
use PhpParser\NodeVisitor\FirstFindingVisitor;
use function array_values;
final class AstMap implements AstMapInterface
......@@ -49,8 +46,20 @@ final class AstMap implements AstMapInterface
*/
private $hasTypeInformation = false;
/**
* @var NodeAdapterFactory
*/
private $nodeAdapterFactory;
/**
* @var NodeFinder
*/
private $nodeFinder;
private function __construct()
{
$this->nodeAdapterFactory = new NodeAdapterFactory();
$this->nodeFinder = new NodeFinder();
}
public static function constructFromNodeCollectionList(NodeCollection ...$nodeCollectionList): self
......@@ -109,8 +118,8 @@ final class AstMap implements AstMapInterface
);
$parserNodeList = $query->shouldReturnSingleResult()
? [$this->findFirst($this->createFilter($query), ...$nodeList)]
: $this->find($this->createFilter($query), ...$nodeList);
? [$this->nodeFinder->findFirst($this->createFilter($query), ...$nodeList)]
: $this->nodeFinder->find($this->createFilter($query), ...$nodeList);
return $this->mapNodeList($parserNodeList);
}
......@@ -119,7 +128,7 @@ final class AstMap implements AstMapInterface
{
$nodeList = [];
foreach ($parserNodeList as $parserNode) {
$nodeList[] = NodeAdapterFactory::constructFromNode($parserNode);
$nodeList[] = $this->nodeAdapterFactory->constructFromNode($parserNode);
}
return new AdapterNodeCollection(...$nodeList);
......@@ -138,26 +147,6 @@ final class AstMap implements AstMapInterface
};
}
private function find(callable $filter, Node ...$nodes): array
{
$traverser = new NodeTraverser();
$visitor = new FindingVisitor($filter);
$traverser->addVisitor($visitor);
$traverser->traverse($nodes);
return $visitor->getFoundNodes();
}
private function findFirst(callable $filter, Node ...$nodes): ?Node
{
$traverser = new NodeTraverser();
$visitor = new FirstFindingVisitor($filter);
$traverser->addVisitor($visitor);
$traverser->traverse($nodes);
return $visitor->getFoundNode();
}
private function getComponentAstCollection(string $componentName): NodeCollection
{
return $this->componentNodeCollectionList[$componentName];
......
......@@ -18,16 +18,19 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeTypeManagerTrait;
use PhpParser\Node;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\AbstractNodeDecorator;
use function json_encode;
use const JSON_PRETTY_PRINT;
final class CircularReferenceDetectedException extends ParserException
{
public function __construct(Node $node, string $fqcn)
public function __construct(AbstractNodeDecorator $nodeDecorator, array $circularBacktrace = [])
{
parent::__construct(
"Circular reference detected when adding type '$fqcn' to collection in node:\n"
. NodeTypeManagerTrait::resolveNodeTreeAsJson($node)
"Circular reference detected:\n"
. $nodeDecorator->resolveNodeTreeAsJson() . "\n"
. json_encode($circularBacktrace, JSON_PRETTY_PRINT) . "\n"
. $this->getTraceAsString()
);
}
}
......@@ -18,17 +18,16 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeTypeManagerTrait;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\AbstractNodeDecorator;
use Hgraca\PhpExtension\Type\TypeHelper;
use PhpParser\Node;
final class NotImplementedException extends ParserException
{
public static function constructFromNode(Node $node)
public static function constructFromNode(AbstractNodeDecorator $nodeDecorator)
{
return new self(
'Can\'t build Type from ' . TypeHelper::getType($node) . "\n"
. NodeTypeManagerTrait::resolveNodeTreeAsJson($node)
'Can\'t build Type from ' . TypeHelper::getType($nodeDecorator) . "\n"
. $nodeDecorator->resolveNodeTreeAsJson()
);
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the Application mapper application,
* following the Explicit Architecture principles.
*
* @link https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together
* @link https://herbertograca.com/2018/07/07/more-than-concentric-layers/
*
* (c) Herberto Graça
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\AbstractNodeDecorator;
final class ParentNodeNotFoundException extends ParserException
{
public function __construct(string $type, AbstractNodeDecorator $nodeDecorator)
{
$class = get_class($nodeDecorator);
$msg = "Could not find node of type '$type' as parent of '$class': \n"
. $nodeDecorator->resolveNodeTreeAsJson();
parent::__construct($msg);
}
}
......@@ -19,7 +19,7 @@ namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
final class UnknownVariableException extends ParserException
final class UnknownNodeNameException extends ParserException
{
public function __construct(string $variableName)
{
......
<?php
declare(strict_types=1);
/*
* This file is part of the Application mapper application,
* following the Explicit Architecture principles.
*
* @link https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together
* @link https://herbertograca.com/2018/07/07/more-than-concentric-layers/
*
* (c) Herberto Graça
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\ParamNodeDecorator;
final class UnknownParameterException extends ParserException
{
public function __construct(ParamNodeDecorator $searchParamDecorator)
{
$parameterName = $searchParamDecorator->getName();
$method = $searchParamDecorator->getEnclosingMethodNode();
$methodName = $method->getName();
$className = $method->getEnclosingClassLikeNode()->getName();
parent::__construct(
"Unknown parameter '$parameterName' in method '$methodName' in class '$className'.\n"
. $searchParamDecorator->resolveNodeTreeAsJson()
);
}
}
......@@ -18,13 +18,12 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeTypeManagerTrait;
use PhpParser\Node;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\AbstractNodeDecorator;
final class UnresolvableNodeTypeException extends ParserException
{
public function __construct(Node $node)
public function __construct(AbstractNodeDecorator $nodeDecorator)
{
parent::__construct(NodeTypeManagerTrait::resolveNodeTreeAsJson($node));
parent::__construct($nodeDecorator->resolveNodeTreeAsJson());
}
}
......@@ -20,35 +20,29 @@ namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Node;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
use Hgraca\AppMapper\Core\Port\Parser\Node\ClassInterface;
use Hgraca\AppMapper\Core\Port\Parser\Node\MethodInterface;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\MethodNotFoundInClassException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeTypeManagerTrait;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\TypeCollection;
use PhpParser\Node;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Interface_;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\AbstractNodeDecorator;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\InterfaceNodeDecorator;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\NameNodeDecorator;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\StmtClassNodeDecorator;
use Hgraca\PhpExtension\String\ClassHelper;
use function array_keys;
use function array_merge;
use function get_class;
use function is_array;
final class ClassAdapter implements ClassInterface
{
use NodeTypeManagerTrait;
/**
* @var Class_
* @var StmtClassNodeDecorator
*/
private $class;
private $classNodeDecorator;
/**
* @var Class_[]|null[]
* @var StmtClassNodeDecorator[]|null[]
*/
private $parentList;
/**
* @var Interface_[]|null[]
* @var InterfaceNodeDecorator[]|null[]
*/
private $implementedList;
......@@ -56,45 +50,28 @@ final class ClassAdapter implements ClassInterface
{
}
public static function constructFromClassNode(Class_ $class): self
public static function constructFromClassNode(StmtClassNodeDecorator $classNodeDecorator): self
{
$self = new self();
$self->class = $class;
$self->classNodeDecorator = $classNodeDecorator;
return $self;
}
public static function constructFromNew(New_ $newExpression): self
{
/** @var Class_ $class */
$class = self::getTypeCollectionFromNode($newExpression)->getAst();
return self::constructFromClassNode($class);
}
public function getFullyQualifiedType(): string
{
return ltrim($this->class->namespacedName->toCodeString(), '\\');
return $this->classNodeDecorator->getTypeCollection()->getUniqueType()->getFqn();
}
public function getCanonicalType(): string
{
return $this->class->name->toString();
return ClassHelper::extractCanonicalClassName($this->getFullyQualifiedType());
}
public function getMethod(string $methodName): MethodInterface
{
foreach ($this->class->stmts as $stmt) {
if (
$stmt instanceof ClassMethod
&& $stmt->name->toString() === $methodName
) {
return new MethodAdapter($stmt);
}
}
throw MethodNotFoundInClassException::constructFromFqcn($methodName, $this->getFullyQualifiedType());
return new MethodAdapter($this->classNodeDecorator->getMethod($methodName));
}
/**
......@@ -103,10 +80,8 @@ final class ClassAdapter implements ClassInterface
public function getMethodList(): array
{
$methodList = [];
foreach ($this->class->stmts as $stmt) {
if ($stmt instanceof ClassMethod) {
$methodList[] = new MethodAdapter($stmt);
}
foreach ($this->classNodeDecorator->getMethods() as $methodNodeDecorator) {
$methodList[] = new MethodAdapter($methodNodeDecorator);
}
return $methodList;
......@@ -120,7 +95,7 @@ final class ClassAdapter implements ClassInterface
return array_keys(
array_merge(
$this->getAllParentsFullyQualifiedNameList(),
$this->getAllInterfacesFullyQualifiedNameList($this->class),
$this->getAllInterfacesFullyQualifiedNameList($this->classNodeDecorator),
$this->getAllParentsInterfacesFullyQualifiedNameList()
)
);
......@@ -134,26 +109,24 @@ final class ClassAdapter implements ClassInterface
private function getAllParentsFullyQualifiedNameList(): array
{
if ($this->parentList === null) {
$this->parentList = $this->findAllParentsFullyQualifiedNameListRecursively($this->class);
$this->parentList = $this->findAllParentsFullyQualifiedNameListRecursively($this->classNodeDecorator);
}
return $this->parentList;
}
private function getAllInterfacesFullyQualifiedNameList(Class_ $class): array
private function getAllInterfacesFullyQualifiedNameList(StmtClassNodeDecorator $classNodeDecorator): array
{
if ($this->implementedList === null) {
$implementedList = [];
foreach ($class->implements as $interfaceNameNode) {
/** @var TypeCollection $interfaceTypeCollection */
$interfaceTypeCollection = $interfaceNameNode->getAttribute(TypeCollection::getName());
$interfaceType = $interfaceTypeCollection->getUniqueType();
foreach ($classNodeDecorator->getInterfaces() as $interfaceNameNodeDecorator) {
$interfaceType = $interfaceNameNodeDecorator->getTypeCollection()->getUniqueType();
$implementedList[] = [
$interfaceType->toString() => $interfaceType->hasAst() ? $interfaceType->getAst() : null,
$interfaceType->toString() => $interfaceType->hasNode() ? $interfaceType->getNodeDecorator() : null,
];
if ($interfaceType->hasAst()) {
if ($interfaceType->hasNode()) {
$implementedList[] = $this->findAllParentsFullyQualifiedNameListRecursively(
$interfaceType->getAst()
$interfaceType->getNodeDecorator()
);
}
}
......@@ -166,49 +139,54 @@ final class ClassAdapter implements ClassInterface
}
/**
* @return string[]
* TODO this method is dirty, it should be split in 2, one for classes and another one for interfaces
*
* @return AbstractNodeDecorator[]
*/
private function findAllParentsFullyQualifiedNameListRecursively(Node $node): array
private function findAllParentsFullyQualifiedNameListRecursively(AbstractNodeDecorator $nodeDecorator): array
{
if (!$node instanceof Class_ && !$node instanceof Interface_) {
if (!$nodeDecorator instanceof StmtClassNodeDecorator && !$nodeDecorator instanceof InterfaceNodeDecorator) {
throw new ParserException(
'Only classes and interfaces can have parents, the given node is of type ' . get_class($node)
'Only classes and interfaces can have parents, the given node is of type ' . get_class($nodeDecorator)
);
}
$parentNameNodeList = $node->extends;
if (!is_array($parentNameNodeList)) {
$parentNameNodeList = [$parentNameNodeList];
}
$parentNameNodeList = $nodeDecorator->getParentNameList();
$parentList = [];
/** @var NameNodeDecorator $parentNameNode */
foreach ($parentNameNodeList as $parentNameNode) {
/** @var TypeCollection $parentTypeCollection */
$parentTypeCollection = $parentNameNode->getAttribute(TypeCollection::getName());
$parentType = $parentTypeCollection->getUniqueType();
$parentType = $parentNameNode->getTypeCollection()->getUniqueType();
$parentList[] = [
$parentType->toString() => $parentType->hasAst() ? $parentType->getAst() : null,
$parentType->toString() => $parentType->hasNode() ? $parentType->getNodeDecorator() : null,
];
if (!$parentType->hasAst()) {
if (!$parentType->hasNode()) {
continue;
}
$parentAst = $parentType->getAst();
if ($node instanceof Class_ && !$parentAst instanceof Class_) {
$parentNodeDecorator = $parentType->getNodeDecorator();
if (
$nodeDecorator instanceof StmtClassNodeDecorator
&& !$parentNodeDecorator instanceof StmtClassNodeDecorator
) {
throw new ParserException(
'A class can only be extend another class, the given parent is of type ' . get_class($parentAst)
'A class can only be extend another class, the given parent is of type '
. get_class($parentNodeDecorator)
);
}
if ($node instanceof Interface_ && !$parentAst instanceof Interface_) {
if (
$nodeDecorator instanceof InterfaceNodeDecorator
&& !$parentNodeDecorator instanceof InterfaceNodeDecorator
) {
throw new ParserException(
'A interface can only be extend another interface, the given parent is of type ' . get_class(
$parentAst
$parentNodeDecorator
)
);
}
$parentList[] = $this->findAllParentsFullyQualifiedNameListRecursively($parentAst);
$parentList[] = $this->findAllParentsFullyQualifiedNameListRecursively($parentNodeDecorator);
}
return !empty($parentList) ? array_merge(...$parentList) : [];
......
......@@ -20,40 +20,42 @@ namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Node;
use Hgraca\AppMapper\Core\Port\Parser\Node\AdapterNodeCollection;
use Hgraca\AppMapper\Core\Port\Parser\Node\MethodInterface;
use Hgraca\AppMapper\Core\Port\Parser\Node\MethodParameterInterface;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeTypeManagerTrait;
use PhpParser\Node\Stmt\ClassMethod;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeDecoratorAccessorTrait;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\ClassMethodNodeDecorator;
final class MethodAdapter implements MethodInterface
{
use NodeTypeManagerTrait;
use NodeDecoratorAccessorTrait;
/**
* @var ClassMethod
* @var ClassMethodNodeDecorator
*/
private $classMethod;
public function __construct(ClassMethod $classMethod)
/**
* @var NodeAdapterFactory
*/
private $nodeAdapterFactory;
public function __construct(ClassMethodNodeDecorator $classMethod)
{
$this->classMethod = $classMethod;
$this->nodeAdapterFactory = new NodeAdapterFactory();
}
public function getCanonicalName(): string
{
return $this->classMethod->name->toString();
return $this->classMethod->getName();
}
public function getReturnTypeCollection(): AdapterNodeCollection
{
$returnType = $this->classMethod->getReturnType();
return NodeAdapterFactory::constructFromTypeCollection(
self::getTypeCollectionFromNode($returnType)
);
return $this->nodeAdapterFactory->constructFromTypeCollection($this->classMethod->getReturnTypeCollection());
}
public function getParameter(int $index): MethodParameterInterface
{
return new MethodParameterAdapter($this->classMethod->params[$index]);
return new MethodParameterAdapter($this->classMethod->getParameter($index));
}
public function isConstructor(): bool
......
......@@ -17,42 +17,24 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Node;
use Hgraca\AppMapper\Core\Port\Logger\StaticLoggerFacade;
use Hgraca\AppMapper\Core\Port\Parser\Node\AdapterNodeInterface;
use Hgraca\AppMapper\Core\Port\Parser\Node\MethodArgumentInterface;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\UnresolvableNodeTypeException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeTypeManagerTrait;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\Type;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\TypeCollection;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeDecoratorAccessorTrait;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\ArgNodeDecorator;
use Hgraca\PhpExtension\Collection\Collection;
use PhpParser\Node\Arg;
final class MethodArgumentAdapter extends Collection implements MethodArgumentInterface
{
use NodeTypeManagerTrait;
use NodeDecoratorAccessorTrait;
public function __construct(Arg $argument)
public function __construct(ArgNodeDecorator $argNodeDecorator)
{
$argumentValue = $argument->value;
$nodeAdapterFactory = new NodeAdapterFactory();
$argumentValueDecorator = $argNodeDecorator->getValue();
try {
$this->itemList = array_unique(
NodeAdapterFactory::constructFromTypeCollection(
self::getTypeCollectionFromNode($argumentValue)
)->toArray()
);
} catch (UnresolvableNodeTypeException $e) {
StaticLoggerFacade::warning(
"Silently ignoring a UnresolvableNodeTypeException in this adapter.\n"
. "This should be fixed in the type addition visitors.\n"
. $e->getMessage(),
[__METHOD__]
);
$this->itemList =
NodeAdapterFactory::constructFromTypeCollection(
new TypeCollection(Type::constructUnknownFromNode($argumentValue))
)->toArray();
}
$this->itemList = $nodeAdapterFactory
->constructFromTypeCollection($argumentValueDecorator->getTypeCollection())
->toArray();
}
/**
......
......@@ -21,18 +21,18 @@ use Hgraca\AppMapper\Core\Port\Parser\Node\ClassInterface;
use Hgraca\AppMapper\Core\Port\Parser\Node\MethodArgumentInterface;
use Hgraca\AppMapper\Core\Port\Parser\Node\MethodCallInterface;
use Hgraca\AppMapper\Core\Port\Parser\Node\MethodInterface;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\ParentConnectorVisitor;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeDecoratorAccessorTrait;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\ArgNodeDecorator;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\MethodCallNodeDecorator;
final class MethodCallAdapter implements MethodCallInterface
{
use NodeDecoratorAccessorTrait;
/**
* @var MethodCall
* @var MethodCallNodeDecorator
*/
private $methodCall;
private $methodCallNodeDecorator;
/**
* @var ClassInterface
......@@ -49,12 +49,12 @@ final class MethodCallAdapter implements MethodCallInterface
*/
private $argumentList = [];
public function __construct(MethodCall $methodCall)
public function __construct(MethodCallNodeDecorator $methodCallNodeDecorator)
{
$this->methodCall = $methodCall;
/** @var Arg $argument */
foreach ($methodCall->args as $argument) {
$this->argumentList[] = new MethodArgumentAdapter($argument);
$this->methodCallNodeDecorator = $methodCallNodeDecorator;
/** @var ArgNodeDecorator $argumentDecorator */
foreach ($methodCallNodeDecorator->getArguments() as $argumentDecorator) {
$this->argumentList[] = new MethodArgumentAdapter($argumentDecorator);
}
}
......@@ -80,15 +80,15 @@ final class MethodCallAdapter implements MethodCallInterface
public function getLine(): int
{
return (int) $this->methodCall->getAttribute('startLine');
return $this->methodCallNodeDecorator->getLine();
}
private function getEnclosingClass(): ClassInterface
{
if ($this->enclosingClass === null) {
/** @var Class_ $node */
$node = ParentConnectorVisitor::getFirstParentNodeOfType($this->methodCall, Class_::class);
$this->enclosingClass = ClassAdapter::constructFromClassNode($node);
$this->enclosingClass = ClassAdapter::constructFromClassNode(
$this->methodCallNodeDecorator->getEnclosingClassLikeNode()
);
}
return $this->enclosingClass;
......@@ -97,9 +97,7 @@ final class MethodCallAdapter implements MethodCallInterface
private function getEnclosingMethod(): MethodInterface
{
if ($this->enclosingMethod === null) {
/** @var ClassMethod $node */
$node = ParentConnectorVisitor::getFirstParentNodeOfType($this->methodCall, ClassMethod::class);
$this->enclosingMethod = new MethodAdapter($node);
$this->enclosingMethod = new MethodAdapter($this->methodCallNodeDecorator->getEnclosingMethodNode());
}
return $this->enclosingMethod;
......
......@@ -20,28 +20,29 @@ namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Node;
use Hgraca\AppMapper\Core\Port\Parser\Node\MethodParameterInterface;
use Hgraca\AppMapper\Core\Port\Parser\Node\TypeNodeInterface;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\NotImplementedException;
use PhpParser\Node\Param;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\ParamNodeDecorator;
use Hgraca\PhpExtension\String\ClassHelper;
final class MethodParameterAdapter implements TypeNodeInterface, MethodParameterInterface
{
/**
* @var TypeNameAdapter
* @var ParamNodeDecorator
*/
private $parameterType;
private $parameterNodeDecorator;
public function __construct(Param $parameter)
public function __construct(ParamNodeDecorator $parameterNodeDecorator)
{
$this->parameterType = new TypeNameAdapter($parameter->type);
$this->parameterNodeDecorator = $parameterNodeDecorator;
}
public function getFullyQualifiedType(): string
{
return $this->parameterType->getFullyQualifiedType();
return $this->parameterNodeDecorator->getDeclaredType()->getTypeCollection()->getUniqueType()->getFqn();
}
public function getCanonicalType(): string
{
return $this->parameterType->getCanonicalType();
return ClassHelper::extractCanonicalClassName($this->getFullyQualifiedType());
}
public function getAllFamilyFullyQualifiedNameList(): array
......
......@@ -19,43 +19,50 @@ namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Node;
use Hgraca\AppMapper\Core\Port\Parser\Node\AdapterNodeCollection;
use Hgraca\AppMapper\Core\Port\Parser\Node\AdapterNodeInterface;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeDecoratorAccessorTrait;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\ClassMethodNodeDecorator;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\MethodCallNodeDecorator;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\StmtClassNodeDecorator;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\Type;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\TypeCollection;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
final class NodeAdapterFactory
{
use NodeDecoratorAccessorTrait;
/**
* @param string|Node|null $parserNode
* @param string|Node|null $node
*/
public static function constructFromNode($parserNode): AdapterNodeInterface
public function constructFromNode($node): AdapterNodeInterface
{
if ($parserNode instanceof ClassMethod) { // this needs to be above the `Expr`
return new MethodAdapter($parserNode);
$nodeDecorator = $node instanceof Node
? $this->getNodeDecorator($node)
: $node;
if ($nodeDecorator instanceof ClassMethodNodeDecorator) { // this needs to be above the `Expr`
return new MethodAdapter($nodeDecorator);
}
if ($parserNode instanceof MethodCall) {
return new MethodCallAdapter($parserNode);
if ($nodeDecorator instanceof MethodCallNodeDecorator) {
return new MethodCallAdapter($nodeDecorator);
}
if ($parserNode instanceof Class_) {
return ClassAdapter::constructFromClassNode($parserNode);
if ($nodeDecorator instanceof StmtClassNodeDecorator) {
return ClassAdapter::constructFromClassNode($nodeDecorator);
}
return new UnknownTypeNode($parserNode);
return new UnknownTypeNode($nodeDecorator);
}
public static function constructFromTypeCollection(TypeCollection $typeCollection): AdapterNodeCollection
public function constructFromTypeCollection(TypeCollection $typeCollection): AdapterNodeCollection
{
$adapterNodeList = [];
/** @var Type $type */
foreach ($typeCollection as $type) {
$adapterNodeList[] = $type->hasAst()
? self::constructFromNode($type->getAst())
$adapterNodeList[] = $type->hasNode()
? $this->constructFromNode($type->getNodeDecorator())
: new FullyQualifiedTypeNode((string) $type);
}
......
......@@ -19,7 +19,7 @@ namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\AstNodeNotFoundException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\UnitNotFoundInNamespaceException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\ParentConnectorVisitor;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\DecoratorVisitor;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\TypeResolverInjectorVisitor;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\TypeResolverVisitor;
use Hgraca\PhpExtension\String\JsonEncoder;
......@@ -122,13 +122,14 @@ final class NodeCollection
{
$traverser = new NodeTraverser();
$traverser->addVisitor(new NameResolver(null, ['preserveOriginalNames' => true, 'replaceNodes' => false]));
$traverser->addVisitor(new ParentConnectorVisitor());
$traverser->addVisitor(new TypeResolverInjectorVisitor($this));
$traverser->addVisitor(new DecoratorVisitor($this));
$traverser->addVisitor(new TypeResolverInjectorVisitor());
$traverser->traverse(array_values($this->nodeList));
}
public function resolveAllTypes(): void
{
$GLOBALS['nodeList'] = $this->nodeList;
$traverser = new NodeTraverser();
$traverser->addVisitor(new TypeResolverVisitor());
$traverser->traverse(array_values($this->nodeList));
......
<?php
declare(strict_types=1);
/*
* This file is part of the Application mapper application,
* following the Explicit Architecture principles.
*
* @link https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together
* @link https://herbertograca.com/2018/07/07/more-than-concentric-layers/
*
* (c) Herberto Graça
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\AbstractNodeDecorator;
use PhpParser\Node;
trait NodeDecoratorAccessorTrait
{
private static $DECORATOR_ATTRIBUTE = 'decorator';
protected function setNodeDecorator(Node $node, AbstractNodeDecorator $nodeDecorator): void
{
$node->setAttribute(self::$DECORATOR_ATTRIBUTE, $nodeDecorator);
}
protected function getNodeDecorator(Node $node): AbstractNodeDecorator
{
return $node->getAttribute(self::$DECORATOR_ATTRIBUTE);
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the Application mapper application,
* following the Explicit Architecture principles.
*
* @link https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together
* @link https://herbertograca.com/2018/07/07/more-than-concentric-layers/
*
* (c) Herberto Graça
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\FindingVisitor;
use PhpParser\NodeVisitor\FirstFindingVisitor;
final class NodeFinder
{
public function find(callable $filter, Node ...$nodes): array
{
$traverser = new NodeTraverser();
$visitor = new FindingVisitor($filter);
$traverser->addVisitor($visitor);
$traverser->traverse($nodes);
return $visitor->getFoundNodes();
}
public function findFirst(callable $filter, Node ...$nodes): ?Node
{
$traverser = new NodeTraverser();
$visitor = new FirstFindingVisitor($filter);
$traverser->addVisitor($visitor);
$traverser->traverse($nodes);
return $visitor->getFoundNode();
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the Application mapper application,
* following the Explicit Architecture principles.
*
* @link https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together
* @link https://herbertograca.com/2018/07/07/more-than-concentric-layers/
*
* (c) Herberto Graça
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser;
use Hgraca\AppMapper\Core\Port\Logger\StaticLoggerFacade;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\UnresolvableNodeTypeException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\ResolverCollection;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\Type;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\TypeCollection;
use Hgraca\PhpExtension\Type\TypeHelper;
use PhpParser\Node;
trait NodeTypeManagerTrait
{
public static function addTypeCollectionToNode(Node $node, TypeCollection $newTypeCollection): void
{
if (!$node->hasAttribute(TypeCollection::getName())) {
$node->setAttribute(TypeCollection::getName(), $newTypeCollection);
return;
}
/** @var TypeCollection $typeCollection */
$typeCollection = $node->getAttribute(TypeCollection::getName())->addTypeCollection($newTypeCollection);
$node->setAttribute(TypeCollection::getName(), $typeCollection);
}
public static function addTypeToNode(Node $node, Type ...$typeList): void
{
if (!$node->hasAttribute(TypeCollection::getName())) {
$typeCollection = new TypeCollection();
$node->setAttribute(TypeCollection::getName(), $typeCollection);
} else {
$typeCollection = $node->getAttribute(TypeCollection::getName());
}
foreach ($typeList as $type) {
$typeCollection = $typeCollection->addType($type);
}
}
public static function getTypeCollectionFromNode(?Node $node): TypeCollection
{
if (!$node) {
return new TypeCollection(Type::constructVoid());
}
if (!self::hasTypeCollection($node)) {
if (!self::hasTypeResolver($node)) {
throw new UnresolvableNodeTypeException($node);
}
self::addTypeCollectionToNode($node, self::resolveType($node));
}
return $node->getAttribute(TypeCollection::getName());
}
public static function hasTypeCollection(?Node $node): bool
{
if (!$node) {
return true; // because getTypeCollectionFromNode returns an empty collection in case of null
}
return $node->hasAttribute(TypeCollection::getName());
}
public static function addTypeResolver(Node $node, callable $typeResolver): void
{
$node->setAttribute(
ResolverCollection::getName(),
self::getNodeResolverCollection($node)->addResolver($typeResolver)
);
}
public static function addTypeResolverCollection(Node $node, ResolverCollection $typeResolverCollection): void
{
$node->setAttribute(
ResolverCollection::getName(),
self::getNodeResolverCollection($node)->addResolverCollection($typeResolverCollection)
);
}
public static function hasTypeResolver(Node $node): bool
{
return $node->hasAttribute(ResolverCollection::getName());
}
public static function resolveType(Node $node): TypeCollection
{
StaticLoggerFacade::debug(
'Resolving type ' . TypeHelper::getType($node) . "\n" . self::resolveNodeTreeAsJson($node)
);
$resolverCollection = $node->getAttribute(ResolverCollection::getName());
return $resolverCollection->resolve();
}
private static function getNodeResolverCollection(Node $node): ResolverCollection
{
if (!$node->hasAttribute(ResolverCollection::getName())) {
$resolverCollection = new ResolverCollection();
$node->setAttribute(ResolverCollection::getName(), $resolverCollection);
}
return $node->getAttribute(ResolverCollection::getName());
}
public static function resolveNodeTreeAsJson(Node $node): string
{
return json_encode(self::resolveNodeTree($node), JSON_PRETTY_PRINT);
}
private static function resolveNodeTree(Node $node): array
{
$nodeTree = [];
$loopNode = $node;
while ($loopNode->hasAttribute('parentNode')) {
$nodeTree[] = get_class($loopNode) . ' => '
. (property_exists($loopNode, 'name')
? $loopNode->name
: (property_exists($loopNode, 'var') && property_exists($loopNode->var, 'name')
? $loopNode->var->name
: 'no_name')
);
$loopNode = $loopNode->getAttribute('parentNode');
}
return $nodeTree;
}
}
......@@ -18,14 +18,14 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser;
use Hgraca\AppMapper\Core\Port\Logger\StaticLoggerFacade;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\UnresolvableNodeTypeException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\MethodCallNodeDecorator;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
final class QueryBuilder
{
use NodeTypeManagerTrait;
use NodeDecoratorAccessorTrait;
/** @var Query */
private $currentQuery;
......@@ -124,29 +124,17 @@ final class QueryBuilder
if (!$node instanceof MethodCall) {
return false;
}
$methodCall = $node;
try {
$dispatcherTypeCollection = $this->getTypeCollectionFromNode($methodCall->var);
foreach ($dispatcherTypeCollection as $type) {
$dispatcherFqcn = (string) $type;
$dispatcherMethodName = (string) $methodCall->name;
if (
preg_match($eventDispatcherTypeRegex, $dispatcherFqcn)
&& preg_match($eventDispatcherMethodRegex, $dispatcherMethodName)
) {
return true;
}
/** @var MethodCallNodeDecorator $methodCallDecorator */
$methodCallDecorator = $this->getNodeDecorator($node);
$calleeTypeCollection = $methodCallDecorator->getCallee()->getTypeCollection();
foreach ($calleeTypeCollection as $type) {
if (
preg_match($eventDispatcherMethodRegex, $methodCallDecorator->getMethodName())
&& preg_match($eventDispatcherTypeRegex, $type->getFqn())
) {
return true;
}
} catch (UnresolvableNodeTypeException $e) {
StaticLoggerFacade::warning(
"Silently ignoring a UnresolvableNodeTypeException in this filter.\n"
. "The type is not in the node so it can't pass the filter.\n"
. "This should be fixed in the type addition visitors.\n"
. $e->getMessage(),
[__METHOD__]
);
}
return false;
......
<?php
declare(strict_types=1);
/*
* This file is part of the Application mapper application,
* following the Explicit Architecture principles.
*
* @link https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together
* @link https://herbertograca.com/2018/07/07/more-than-concentric-layers/
*
* (c) Herberto Graça
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeCollection;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeDecoratorAccessorTrait;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\AbstractNodeDecorator;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\DefaultNodeDecorator;
use Hgraca\PhpExtension\String\ClassHelper;
use Hgraca\PhpExtension\String\StringHelper;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use function class_exists;
use function count;
use function get_class;
class DecoratorVisitor extends NodeVisitorAbstract
{
use NodeDecoratorAccessorTrait;
/**
* @var AbstractNodeDecorator[]
*/
private $stack;
/**
* @var NodeCollection
*/
private $nodeCollection;
public function __construct(NodeCollection $nodeCollection)
{
$this->nodeCollection = $nodeCollection;
}
public function beforeTraverse(array $nodes): void
{
$this->stack = [];
}
public function enterNode(Node $node): void
{
$decoratorNode = $this->createDecoratorFor($node);
$this->setNodeDecorator($node, $decoratorNode);
$this->stack[] = $decoratorNode;
}
public function leaveNode(Node $node): void
{
array_pop($this->stack);
}
private function createDecoratorFor(Node $node): AbstractNodeDecorator
{
$parent = !empty($this->stack) ? $this->stack[count($this->stack) - 1] : null;
$decoratorClassName = $this->getDecoratorNamespace() . $this->getDecoratorCanonicalName($node);
if (class_exists($decoratorClassName)) {
return new $decoratorClassName($node, $parent, $this->nodeCollection);
}
$decoratorClassName = $this->getDecoratorNamespace() . $this->getDecoratorCanonicalNameIncludingLeafNamespace(
$node
);
if (class_exists($decoratorClassName)) {
return new $decoratorClassName($node, $parent, $this->nodeCollection);
}
return new DefaultNodeDecorator($node, $parent);
}
private function getDecoratorNamespace(): string
{
return StringHelper::removeFromEnd(
ClassHelper::extractCanonicalClassName(AbstractNodeDecorator::class),
AbstractNodeDecorator::class
);
}
private function getDecoratorCanonicalName(Node $node): string
{
return rtrim(ClassHelper::extractCanonicalClassName(get_class($node)), '_') . 'NodeDecorator';
}
private function getDecoratorCanonicalNameIncludingLeafNamespace(Node $node): string
{
$explodedNamespace = explode('\\', get_class($node));
$leafNamespace = $explodedNamespace[count($explodedNamespace) - 2];
return $leafNamespace . $this->getDecoratorCanonicalName($node);
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the Application mapper application,
* following the Explicit Architecture principles.
*
* @link https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together
* @link https://herbertograca.com/2018/07/07/more-than-concentric-layers/
*
* (c) Herberto Graça
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\TypeCollection;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\TypeNodeCollector;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\Node\Stmt\TraitUse;
use function array_key_exists;
/**
* @property Class_|Trait_ $node
*/
abstract class AbstractClassLikeNodeDecorator extends AbstractInterfaceLikeNodeDecorator implements NamedNodeDecoratorInterface
{
/**
* @var TypeNodeCollector
*/
private $propertyNodesSiblingCollection;
/**
* @var TypeCollection[]
*/
private $propertyNodesTypeCollections = [];
/**
* @var PropertyNodeDecorator[]
*/
private $propertyList = [];
public function __construct(Node $node, AbstractNodeDecorator $parentNode = null)
{
parent::__construct($node, $parentNode);
$this->propertyNodesSiblingCollection = new TypeNodeCollector();
}
abstract protected function getPropertyTypeCollectionFromHierarchy(
NamedNodeDecoratorInterface $nodeDecorator
): TypeCollection;
public function getParentName(): ?NameNodeDecorator
{
return $this->node->extends
? $this->getNodeDecorator($this->node->extends)
: null;
}
/**
* @return NameNodeDecorator[]
*/
public function getParentNameList(): array
{
$parentName = $this->getParentName();
return $parentName ? [$parentName] : [];
}
public function getPropertyTypeCollection(NamedNodeDecoratorInterface $nodeDecorator): TypeCollection
{
if (!$this->hasDeclaredProperty($nodeDecorator)) {
return $this->getPropertyTypeCollectionFromHierarchy($nodeDecorator);
}
$propertyName = $nodeDecorator->getName();
if (!$this->isPropertyTypeCollectionResolved($nodeDecorator)) {
$this->resolvePropertyTypeCollection($nodeDecorator);
}
return $this->propertyNodesTypeCollections[$propertyName];
}
/**
* @return PropertyNodeDecorator[]
*/
public function getDeclaredProperties(): array
{
if (empty($this->propertyList)) {
foreach ($this->node->stmts as $stmt) {
if ($stmt instanceof Property) {
/** @var PropertyNodeDecorator $propertyDecorator */
$propertyDecorator = $this->getNodeDecorator($stmt);
$this->propertyList[$propertyDecorator->getName()] = $propertyDecorator;
}
}
}
return $this->propertyList;
}
protected function getPropertyTypeCollectionFromTraits(NamedNodeDecoratorInterface $nodeDecorator): TypeCollection
{
/** @var TraitNodeDecorator[] $traitList */
$traitList = $this->getTraitList();
$typeCollection = new TypeCollection();
foreach ($traitList as $trait) {
$typeCollection = $typeCollection->addTypeCollection($trait->getPropertyTypeCollection($nodeDecorator));
}
return $typeCollection;
}
/**
* @return TraitNodeDecorator[]
*/
protected function getTraitList(): array
{
$nestedTraitList = [];
foreach ($this->node->stmts as $stmt) {
if ($stmt instanceof TraitUse) {
/** @var TraitUseNodeDecorator $traitUseNodeDecorator */
$traitUseNodeDecorator = $this->getNodeDecorator($stmt);
$nestedTraitList[] = $traitUseNodeDecorator->getTraitNodeDecoratorList();
}
}
return !empty($nestedTraitList)
? array_merge(...$nestedTraitList)
: [];
}
private function isPropertyTypeCollectionResolved(NamedNodeDecoratorInterface $nodeDecorator): bool
{
return array_key_exists($nodeDecorator->getName(), $this->propertyNodesTypeCollections);
}
private function hasDeclaredProperty(NamedNodeDecoratorInterface $nodeDecorator): bool
{
return array_key_exists($nodeDecorator->getName(), $this->getDeclaredProperties());
}
private function getDeclaredProperty(NamedNodeDecoratorInterface $nodeDecorator): PropertyNodeDecorator
{
$declaredProperties = $this->getDeclaredProperties();
return $declaredProperties[$nodeDecorator->getName()];
}
public function storePropertiesSiblings(TypeNodeCollector $nodeCollector): void
{
$this->propertyNodesSiblingCollection = $nodeCollector;
}
private function resolvePropertyTypeCollection(NamedNodeDecoratorInterface $nodeDecorator): void
{
$typeCollection = $this->getDeclaredProperty($nodeDecorator)->getTypeCollection();
foreach ($this->propertyNodesSiblingCollection->getNodesFor($nodeDecorator) as $siblingNode) {
$typeCollection = $typeCollection->addTypeCollection($siblingNode->getTypeCollection());
}