...
 
Commits (63)
......@@ -43,7 +43,10 @@ return PhpCsFixer\Config::create()
'no_unreachable_default_argument_value' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'ordered_imports' => true,
'ordered_imports' => [
'sort_algorithm' => 'alpha',
'imports_order' => ['class', 'function', 'const'],
],
'phpdoc_align' => false,
'phpdoc_order' => true,
'phpdoc_summary' => false,
......
This diff is collapsed.
......@@ -4,7 +4,7 @@
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/7.1/phpunit.xsd"
colors="true"
bootstrap="vendor/autoload.php"
bootstrap="tests/Framework/bootstrap.php"
>
<php>
<ini name="error_reporting" value="-1" />
......
......@@ -28,9 +28,9 @@ final class ConsoleLogger implements LoggerInterface
{
private const DEFAULT_LOGGING_LEVEL_MAP = [
OutputInterface::VERBOSITY_QUIET => [],
OutputInterface::VERBOSITY_NORMAL => ['info', 'error', 'critical', 'alert', 'emergency'],
OutputInterface::VERBOSITY_VERBOSE => ['warning', 'info', 'error', 'critical', 'alert', 'emergency'],
OutputInterface::VERBOSITY_VERY_VERBOSE => ['notice', 'info', 'error', 'critical', 'alert', 'emergency'],
OutputInterface::VERBOSITY_NORMAL => ['debug', 'info', 'error', 'critical', 'alert', 'emergency'],
OutputInterface::VERBOSITY_VERBOSE => ['warning', 'debug', 'info', 'error', 'critical', 'alert', 'emergency'],
OutputInterface::VERBOSITY_VERY_VERBOSE => ['notice', 'debug', 'info', 'error', 'critical', 'alert', 'emergency'],
OutputInterface::VERBOSITY_DEBUG => ['debug', 'notice', 'warning', 'info', 'error', 'critical', 'alert', 'emergency'],
];
......
<?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\PhpExtension\Collection\Collection;
abstract class AbstractCollection extends Collection
{
public function implodeKeys(string $glue): string
{
return implode($glue, array_keys($this->itemList));
}
}
......@@ -49,8 +49,14 @@ final class AstMap implements AstMapInterface
*/
private $hasTypeInformation = false;
/**
* @var NodeAdapterFactory
*/
private $nodeAdapterFactory;
private function __construct()
{
$this->nodeAdapterFactory = new NodeAdapterFactory();
}
public static function constructFromNodeCollectionList(NodeCollection ...$nodeCollectionList): self
......@@ -119,7 +125,7 @@ final class AstMap implements AstMapInterface
{
$nodeList = [];
foreach ($parserNodeList as $parserNode) {
$nodeList[] = NodeAdapterFactory::constructFromNode($parserNode);
$nodeList[] = $this->nodeAdapterFactory->constructFromNode($parserNode);
}
return new AdapterNodeCollection(...$nodeList);
......@@ -169,6 +175,7 @@ final class AstMap implements AstMapInterface
return;
}
$this->completeNodeCollection->enhance();
$this->completeNodeCollection->resolveAllTypes();
$this->hasTypeInformation = true;
}
}
......@@ -17,8 +17,20 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\AppMapper\Core\SharedKernel\Exception\AppMapperRuntimeException;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\AbstractNodeDecorator;
use function json_encode;
use const JSON_PRETTY_PRINT;
final class CircularReferenceDetectedException extends AppMapperRuntimeException
final class CircularReferenceDetectedException extends ParserException
{
public function __construct(AbstractNodeDecorator $nodeDecorator, array $circularBacktrace = [])
{
parent::__construct(
"Circular reference detected:\n"
. $nodeDecorator->resolveNodeTreeAsJson() . "\n"
. json_encode($circularBacktrace, JSON_PRETTY_PRINT) . "\n"
. $this->getTraceAsString()
);
}
}
......@@ -17,8 +17,8 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\AppMapper\Core\SharedKernel\Exception\AppMapperLogicException;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
final class EmptyCollectionException extends AppMapperLogicException
final class EmptyCollectionException extends ParserException
{
}
<?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;
final class InvalidArgumentException extends ParserException
{
}
......@@ -18,11 +18,20 @@ 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\Visitor\TypeCollection;
final class MethodNotFoundInClassException extends ParserException
{
public function __construct(string $methodName, string $classFqcn)
public static function constructFromFqcn(string $methodName, string $classFqcn): self
{
parent::__construct("Method '$methodName' not found in class '$classFqcn'.");
return new self("Method '$methodName' not found in class '$classFqcn'.");
}
public static function constructFromCollection(string $methodName, TypeCollection $typeCollection): self
{
return new self(
"Method '$methodName' not found in any of the classes '{$typeCollection->implodeKeys(', ')}'. "
. 'It should have been found in at least one.'
);
}
}
......@@ -17,17 +17,16 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\AppMapper\Core\SharedKernel\Exception\AppMapperLogicException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\TypeCollection;
use function array_keys;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\AbstractCollection;
final class NonUniqueTypeCollectionException extends AppMapperLogicException
final class NonUniqueTypeCollectionException extends ParserException
{
public function __construct(TypeCollection $typeCollection)
public function __construct(AbstractCollection $collection)
{
parent::__construct(
"The type collection contains more than one type: \n"
. implode("\n", array_keys($typeCollection->toArray()))
. $collection->implodeKeys("\n")
);
}
}
<?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;
use Hgraca\PhpExtension\Type\TypeHelper;
final class NotImplementedException extends ParserException
{
public static function constructFromNode(AbstractNodeDecorator $nodeDecorator)
{
return new self(
'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);
}
}
......@@ -17,8 +17,8 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\PhpExtension\Exception\RuntimeException;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
final class UnknownFqcnException extends RuntimeException
final class UnknownFqcnException extends ParserException
{
}
......@@ -17,9 +17,9 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\PhpExtension\Exception\RuntimeException;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
final class UnknownVariableException extends RuntimeException
final class UnknownNodeNameException extends ParserException
{
public function __construct(string $variableName)
{
......
......@@ -15,22 +15,15 @@ declare(strict_types=1);
* file that was distributed with this source code.
*/
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor;
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use PhpParser\Node\Expr\Assign;
use Hgraca\AppMapper\Core\Port\Parser\Exception\ParserException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\NodeDecorator\AbstractNodeDecorator;
/**
* @mixin AbstractTypeInjectorVisitor
*/
trait AssignVisitorTrait
final class UnresolvableNodeTypeException extends ParserException
{
private function leaveAssignNode(Assign $assignNode): void
public function __construct(AbstractNodeDecorator $nodeDecorator)
{
if (!self::hasTypeCollection($assignNode->expr)) {
// TODO stop ignoring unresolved and resolve all detected
return;
}
$this->addTypeCollectionToNode($assignNode->var, self::getTypeCollectionFromNode($assignNode->expr));
$this->collectVariableTypes($assignNode->var);
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 new MethodNotFoundInClassException($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\TypeNotFoundInNodeException;
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 (TypeNotFoundInNodeException $e) {
StaticLoggerFacade::warning(
"Silently ignoring a TypeNotFoundInNodeException in this adapter.\n"
. "This should be fixed in the type addition visitors.\n"
. $e->getMessage(),
[__METHOD__]
);
$this->itemList =
NodeAdapterFactory::constructFromTypeCollection(
new TypeCollection($argument, 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,18 +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) {
$node = $this->methodCall;
do {
$node = $node->getAttribute(ParentConnectorVisitor::PARENT_NODE);
} while (!$node instanceof Class_);
$this->enclosingClass = ClassAdapter::constructFromClassNode($node);
$this->enclosingClass = ClassAdapter::constructFromClassNode(
$this->methodCallNodeDecorator->getEnclosingClassLikeNode()
);
}
return $this->enclosingClass;
......@@ -100,12 +97,7 @@ final class MethodCallAdapter implements MethodCallInterface
private function getEnclosingMethod(): MethodInterface
{
if ($this->enclosingMethod === null) {
$node = $this->methodCall;
do {
$node = $node->getAttribute(ParentConnectorVisitor::PARENT_NODE);
} while (!$node instanceof ClassMethod);
$this->enclosingMethod = new MethodAdapter($node);
$this->enclosingMethod = new MethodAdapter($this->methodCallNodeDecorator->getEnclosingMethodNode());
}
return $this->enclosingMethod;
......
......@@ -19,29 +19,30 @@ 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\Core\SharedKernel\Exception\NotImplementedException;
use PhpParser\Node\Param;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\NotImplementedException;
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->getType()->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 null|string|Node $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);
}
......
......@@ -35,7 +35,7 @@ final class UnknownTypeNode implements TypeNodeInterface
private $phpParserNodeType = '';
/**
* @param null|string|Node $node
* @param string|Node|null $node
*/
public function __construct($node = null)
{
......
......@@ -17,13 +17,11 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser;
use Hgraca\AppMapper\Core\Port\Logger\StaticLoggerFacade;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\AstNodeNotFoundException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\UnitNotFoundInNamespaceException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AssignmentFromMethodCallTypeInjectorVisitor;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\ParentConnectorVisitor;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\PropertyFetchTypeInjectorVisitor;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\TypeInjectorVisitor;
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;
use PhpParser\JsonDecoder;
use PhpParser\Node;
......@@ -122,43 +120,18 @@ final class NodeCollection
public function enhance(): void
{
StaticLoggerFacade::notice(
"TODO This whole method can and should be refactored to a better design. \n"
. "We can: \n"
. " 1. Use resolver `callable`s instead of injecting the type \n"
. " 2. Make better use of the Visitor::leaveNode(), when we need to first visit inner \n"
. ' nodes to resolve an outer node, as in the Assign nodes',
[__METHOD__]
);
$nodeList = array_values($this->nodeList);
// Add all nodes into the collection
// Add visitors here if they don't need the final collection
$traverser = new NodeTraverser();
$traverser->addVisitor(new NameResolver(null, ['preserveOriginalNames' => true, 'replaceNodes' => false]));
$traverser->addVisitor(new ParentConnectorVisitor());
$traverser->traverse($nodeList);
$traverser = new NodeTraverser();
$traverser->addVisitor(new TypeInjectorVisitor($this));
$traverser->traverse($nodeList);
// After setting the type in the properties declaration, we can copy it to every property call
// We need a separate traverse because a property might be set only in the end of the file,
// after the property is used
$traverser = new NodeTraverser();
$traverser->addVisitor(new PropertyFetchTypeInjectorVisitor());
$traverser->traverse($nodeList);
$traverser = new NodeTraverser();
// This one needs the properties to already be set
$traverser->addVisitor(new AssignmentFromMethodCallTypeInjectorVisitor($this));
$traverser->traverse($nodeList);
$traverser->addVisitor(new DecoratorVisitor($this));
$traverser->addVisitor(new TypeResolverInjectorVisitor());
$traverser->traverse(array_values($this->nodeList));
}
// Make a second pass to make sure we got all properties, including the ones captured in the last visitor
public function resolveAllTypes(): void
{
$traverser = new NodeTraverser();
$traverser->addVisitor(new PropertyFetchTypeInjectorVisitor());
$traverser->traverse($nodeList);
$traverser->addVisitor(new TypeResolverVisitor());
$traverser->traverse(array_values($this->nodeList));
}
private function addCollections(self ...$nodeCollectionList): void
......
<?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);
}
}
......@@ -18,14 +18,15 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser;
use Hgraca\AppMapper\Core\Port\Logger\StaticLoggerFacade;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\TypeNotFoundInNodeException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AbstractTypeInjectorVisitor;
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 NodeDecoratorAccessorTrait;
/** @var Query */
private $currentQuery;
......@@ -123,31 +124,17 @@ final class QueryBuilder
if (!$node instanceof MethodCall) {
return false;
}
$methodCall = $node;
try {
$dispatcherTypeCollection = AbstractTypeInjectorVisitor::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 (TypeNotFoundInNodeException $e) {
StaticLoggerFacade::warning(
"Silently ignoring a TypeNotFoundInNodeException 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\Core\SharedKernel\Exception\NotImplementedException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\AstNodeNotFoundException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeCollection;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeTypeManagerTrait;
use Hgraca\PhpExtension\Type\TypeHelper;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\NullableType;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use PhpParser\NodeVisitorAbstract;
use function implode;
use function is_string;
abstract class AbstractTypeInjectorVisitor extends NodeVisitorAbstract
{
use NodeTypeManagerTrait;
use PropertyCollectorTrait;
use VariableTypeCollectorTrait;
/**
* @var NodeCollection
*/
protected $astCollection;
public function __construct(NodeCollection $astCollection)
{
/* @noinspection UnusedConstructorDependenciesInspection Used in trait */
$this->astCollection = $astCollection;
}
public function leaveNode(Node $node): void
{
switch (true) {
case $node instanceof ClassMethod:
$this->resetCollectedVariableTypes();
break;
case $node instanceof Class_:
$this->resetCollectedPropertyType();
break;
}
}
protected function buildType($node): Type
{
switch (true) {
case $node instanceof Class_:
return new Type($this->buildFqcn($node->namespacedName), $node);
break;
case $node instanceof Identifier:
return $this->buildTypeFromIdentifier($node);
break;
case $node instanceof Name:
return $this->buildTypeFromName($node);
break;
case $node instanceof New_:
return $this->buildTypeFromNew($node);
break;
case $node instanceof NullableType:
return $this->buildTypeFromNullable($node);
break;
case $node instanceof Param:
return $this->buildTypeFromParam($node);
break;
case is_string($node):
return new Type($node);
break;
case $node === null:
return Type::constructNull();
break;
default:
throw new NotImplementedException('Can\'t build Type from ' . TypeHelper::getType($node));
}
}
protected function buildTypeFromIdentifier(Identifier $identifier): Type
{
return new Type($identifier->name, null);
}
protected function buildTypeFromName(Name $name): Type
{
$fqcn = $this->buildFqcn($name);
if ($fqcn === 'self' || $fqcn === 'this') {
return $this->buildSelfType($name);
}
try {
return new Type($fqcn, $this->astCollection->getAstNode($fqcn));
} catch (AstNodeNotFoundException $e) {
return new Type($fqcn);
}
}
protected function buildSelfType(Node $node): Type
{
$classAst = $this->getParentClassAst($node);
return new Type($this->buildFqcn($classAst->namespacedName), $classAst);
}
protected function getParentClassAst(Node $node): Class_
{
$classNode = $node;
while (!$classNode instanceof Class_) {
$classNode = $classNode->getAttribute('parentNode');
}
return $classNode;
}
protected function buildTypeFromNew(New_ $new): Type
{
return $this->buildType($new->class);
}
protected function buildTypeFromNullable(NullableType $nullableTypeNode): Type
{
return $this->buildType($nullableTypeNode->type);
}
private function buildTypeFromParam(Param $param): Type
{
return $this->buildType($param->type);
}
protected function buildFqcn(Name $name): string
{
if ($name->hasAttribute('resolvedName')) {
/** @var FullyQualified $fullyQualified */
$fullyQualified = $name->getAttribute('resolvedName');
return $fullyQualified->toCodeString();
}
return implode('\\', $name->parts);
}
/**
* TODO We are only adding properties types in the class itself.
* We should fix this by adding them also to the super classes and traits.
*/
protected function addCollectedPropertiesTypeToTheirDeclaration(Class_ $node): void
{
// After collecting app possible class properties, we inject them in their declaration
foreach ($node->stmts as $property) {
if (
$property instanceof Property
&& $this->hasCollectedPropertyType($propertyName = $this->getPropertyName($property))
) {
$this->addTypeCollectionToNode($property, $this->getCollectedPropertyType($propertyName));
}
}
$this->resetCollectedPropertyType();
}
protected function collectVariableTypes(Expr $var): void
{
$typeCollection = self::getTypeCollectionFromNode($var);
switch (true) {
case $var instanceof Variable: // Assignment to variable
$this->collectVariableType($this->getVariableName($var), $typeCollection);
break;
case $var instanceof PropertyFetch: // Assignment to property
$this->collectPropertyType($this->getPropertyName($var), $typeCollection);
break;
}
}
protected function addCollectedVariableTypes(Variable $variable): void
{
$variableName = $this->getVariableName($variable);