Refactor visitors to add type resolvers instead

It was previously adding already resolved types.
parent 7dfd7f17
......@@ -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'],
];
......
......@@ -169,6 +169,7 @@ final class AstMap implements AstMapInterface
return;
}
$this->completeNodeCollection->enhance();
$this->completeNodeCollection->resolveAllTypes();
$this->hasTypeInformation = true;
}
}
......@@ -15,36 +15,32 @@ 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\FuncCall;
use PhpParser\Node\Name;
use function array_key_exists;
use Hgraca\AppMapper\Core\SharedKernel\Exception\AppMapperLogicException;
use Hgraca\PhpExtension\Type\TypeHelper;
use PhpParser\Node;
trait NativeFunctionsTrait
final class NotImplementedException extends AppMapperLogicException
{
private $functionList = [
'sprintf' => [
'return' => 'string',
],
'array_unique' => [
'return' => 'array',
],
];
private function isNative(FuncCall $funcCall): bool
public static function constructFromNode(Node $node)
{
if ($funcCall->name instanceof Name) {
$name = (string) $funcCall->name;
} elseif (property_exists($funcCall->name, 'name')) {
$name = $funcCall->name->name;
$relevantInfo = [];
$loopNode = $node;
while ($loopNode->hasAttribute('parentNode')) {
$relevantInfo[] = 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 array_key_exists($name, $this->functionList);
}
$message = 'Can\'t build Type from ' . TypeHelper::getType($node) . "\n"
. json_encode($relevantInfo, JSON_PRETTY_PRINT);
private function getReturnType(FuncCall $funcCall): string
{
return $this->functionList[(string) $funcCall->name]['return'];
return new self($message);
}
}
<?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\PhpExtension\Exception\RuntimeException;
final class UnknownPropertyException extends RuntimeException
{
public function __construct(string $propertyName)
{
parent::__construct("Unknown property $propertyName");
}
}
......@@ -20,7 +20,7 @@ namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\AppMapper\Core\SharedKernel\Exception\AppMapperRuntimeException;
use PhpParser\Node;
final class TypeNotFoundInNodeException extends AppMapperRuntimeException
final class UnresolvableNodeTypeException extends AppMapperRuntimeException
{
public function __construct(Node $node)
{
......@@ -37,8 +37,7 @@ final class TypeNotFoundInNodeException extends AppMapperRuntimeException
$loopNode = $loopNode->getAttribute('parentNode');
}
$message = 'Class: ' . static::class . "\n"
. "Can't find type collection in node:\n"
$message = "Can't find type collection in node:\n"
. json_encode($relevantInfo, JSON_PRETTY_PRINT);
parent::__construct($message);
......
......@@ -20,7 +20,7 @@ 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\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;
......@@ -41,9 +41,9 @@ final class MethodArgumentAdapter extends Collection implements MethodArgumentIn
self::getTypeCollectionFromNode($argumentValue)
)->toArray()
);
} catch (TypeNotFoundInNodeException $e) {
} catch (UnresolvableNodeTypeException $e) {
StaticLoggerFacade::warning(
"Silently ignoring a TypeNotFoundInNodeException in this adapter.\n"
"Silently ignoring a UnresolvableNodeTypeException in this adapter.\n"
. "This should be fixed in the type addition visitors.\n"
. $e->getMessage(),
[__METHOD__]
......
......@@ -19,7 +19,7 @@ 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 Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\NotImplementedException;
use PhpParser\Node\Param;
final class MethodParameterAdapter implements TypeNodeInterface, MethodParameterInterface
......
......@@ -18,7 +18,7 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Node;
use Hgraca\AppMapper\Core\Port\Parser\Node\TypeNodeInterface;
use Hgraca\AppMapper\Core\SharedKernel\Exception\NotImplementedException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\NotImplementedException;
use Hgraca\PhpExtension\String\ClassHelper;
use PhpParser\Node;
use PhpParser\Node\Name;
......
......@@ -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\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 TypeResolverInjectorVisitor($this));
$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
......
......@@ -17,14 +17,17 @@ declare(strict_types=1);
namespace Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\TypeNotFoundInNodeException;
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 function addTypeCollectionToNode(Node $node, TypeCollection $newTypeCollection): void
public static function addTypeCollectionToNode(Node $node, TypeCollection $newTypeCollection): void
{
if (!$node->hasAttribute(TypeCollection::getName())) {
$node->setAttribute(TypeCollection::getName(), $newTypeCollection);
......@@ -33,11 +36,12 @@ trait NodeTypeManagerTrait
}
/** @var TypeCollection $typeCollection */
$typeCollection = $node->getAttribute(TypeCollection::getName());
$typeCollection->addTypeCollection($newTypeCollection);
$typeCollection = $node->getAttribute(TypeCollection::getName())->addTypeCollection($newTypeCollection);
$node->setAttribute(TypeCollection::getName(), $typeCollection);
}
public function addTypeToNode(Node $node, Type ...$typeList): void
public static function addTypeToNode(Node $node, Type ...$typeList): void
{
if (!$node->hasAttribute(TypeCollection::getName())) {
$typeCollection = new TypeCollection();
......@@ -47,17 +51,20 @@ trait NodeTypeManagerTrait
}
foreach ($typeList as $type) {
$typeCollection->addType($type);
$typeCollection = $typeCollection->addType($type);
}
}
public static function getTypeCollectionFromNode(?Node $node): TypeCollection
{
if (!$node) {
return new TypeCollection();
return new TypeCollection(Type::constructVoid());
}
if (!$node->hasAttribute(TypeCollection::getName())) {
throw new TypeNotFoundInNodeException($node);
if (!self::hasTypeCollection($node)) {
if (!self::hasTypeResolver($node)) {
throw new UnresolvableNodeTypeException($node);
}
self::addTypeCollectionToNode($node, self::resolveType($node));
}
return $node->getAttribute(TypeCollection::getName());
......@@ -71,4 +78,58 @@ trait NodeTypeManagerTrait
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
{
$relevantInfo = [];
$loopNode = $node;
while ($loopNode->hasAttribute('parentNode')) {
$relevantInfo[] = 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');
}
$message = 'Resolving type ' . TypeHelper::getType($node) . "\n"
. json_encode($relevantInfo, JSON_PRETTY_PRINT);
StaticLoggerFacade::debug($message);
return $node->getAttribute(ResolverCollection::getName())->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());
}
}
......@@ -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\Exception\UnresolvableNodeTypeException;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
final class QueryBuilder
{
use NodeTypeManagerTrait;
/** @var Query */
private $currentQuery;
......@@ -125,9 +126,7 @@ final class QueryBuilder
}
$methodCall = $node;
try {
$dispatcherTypeCollection = AbstractTypeInjectorVisitor::getTypeCollectionFromNode(
$methodCall->var
);
$dispatcherTypeCollection = $this->getTypeCollectionFromNode($methodCall->var);
foreach ($dispatcherTypeCollection as $type) {
$dispatcherFqcn = (string) $type;
......@@ -140,9 +139,9 @@ final class QueryBuilder
return true;
}
}
} catch (TypeNotFoundInNodeException $e) {
} catch (UnresolvableNodeTypeException $e) {
StaticLoggerFacade::warning(
"Silently ignoring a TypeNotFoundInNodeException in this filter.\n"
"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(),
......
<?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\Port\Logger\StaticLoggerFacade;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\MethodNotFoundInClassException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\TypeNotFoundInNodeException;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Class_;
final class AssignmentFromMethodCallTypeInjectorVisitor extends AbstractTypeInjectorVisitor
{
use AssignVisitorTrait;
public function leaveNode(Node $node): void
{
switch (true) {
case $node instanceof MethodCall:
$this->leaveMethodCall($node);
break;
case $node instanceof Assign:
try {
$this->leaveAssignNode($node);
} catch (TypeNotFoundInNodeException $e) {
StaticLoggerFacade::warning(
"Silently ignoring a TypeNotFoundInNodeException in this filter.\n"
. 'This is failing, at least, for nested method calls like'
. '`$invoice->transactions->first()->getServicePro();`.' . "\n"
. "This should be fixed in the type addition visitors.\n"
. $e->getMessage(),
[__METHOD__]
);
}
break;
case $node instanceof Variable:
// After collecting the variable types, inject it in the following variable nodes
$this->addCollectedVariableTypes($node);
break;
case $node instanceof Class_:
$this->addCollectedPropertiesTypeToTheirDeclaration($node);
break;
}
parent::leaveNode($node);
}
private function leaveMethodCall(MethodCall $methodCall): void
{
try {
$varCollectionType = self::getTypeCollectionFromNode($methodCall->var);
/** @var Type $methodCallVarType */
foreach ($varCollectionType as $methodCallVarType) {
if (!$methodCallVarType->hasAst()) {
$this->addTypeToNode($methodCall, Type::constructUnknownFromNode($methodCall));
continue;
}
$returnTypeNode = $methodCallVarType->getAstMethod((string) $methodCall->name)->returnType;
if ($returnTypeNode === null) {
$this->addTypeToNode($methodCall, Type::constructVoid());
} else {
$classMethodReturnTypeCollection = self::getTypeCollectionFromNode($returnTypeNode);
$this->addTypeCollectionToNode($methodCall, $classMethodReturnTypeCollection);
}
}
} catch (TypeNotFoundInNodeException $e) {
StaticLoggerFacade::warning(
"Silently ignoring a TypeNotFoundInNodeException.\n"
. 'This is failing, at least, for nested method calls like'
. '`$invoice->transactions->first()->getServicePro();`.' . "\n"
. "This should be fixed in the type addition visitors, otherwise we might be missing events.\n"
. $e->getMessage(),
[__METHOD__]
);
} catch (MethodNotFoundInClassException $e) {
StaticLoggerFacade::warning(
"Silently ignoring a MethodNotFoundInClassException.\n"
. "This should be fixed, otherwise we might be missing events.\n"
. $e->getMessage(),
[__METHOD__]
);
}
}
}
<?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\UnknownPropertyException;
use Hgraca\PhpExtension\Type\TypeHelper;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Stmt\Property;
trait PropertyCollectorTrait
{
private $propertyTypeBuffer = [];
private function collectPropertyType(string $propertyName, TypeCollection $propertyType): void
{
$this->propertyTypeBuffer[$propertyName] = $propertyType;
}
private function hasCollectedPropertyType(string $propertyName): bool
{
return array_key_exists($propertyName, $this->propertyTypeBuffer);
}
protected function getCollectedPropertyType(string $propertyName): TypeCollection
{
if (!$this->hasCollectedPropertyType($propertyName)) {
throw new UnknownPropertyException($propertyName);
}
return $this->propertyTypeBuffer[$propertyName];
}
private function resetCollectedPropertyType(): void
{
$this->propertyTypeBuffer = [];
}
private function getPropertyName($property): string
{
switch (true) {
case $property instanceof Property:
return (string) $property->props[0]->name;
break;
case $property instanceof PropertyFetch:
return (string) $property->name;
break;
default:
throw new NotImplementedException(
'Can\'t get name from property of type ' . TypeHelper::getType($property)
);
}
}
}
<?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\Port\Logger\StaticLoggerFacade;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\TypeNotFoundInNodeException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\Exception\UnknownPropertyException;
use Hgraca\AppMapper\Infrastructure\Parser\NikicPhpParser\NodeTypeManagerTrait;
use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use PhpParser\NodeVisitorAbstract;
/**
* This visitor makes a swipe injecting the types from the property declarations into the property usages
* (PropertyFetch)
*/
final class PropertyFetchTypeInjectorVisitor extends NodeVisitorAbstract
{
use NodeTypeManagerTrait;
use PropertyCollectorTrait;
public function enterNode(Node $node): void
{
switch (true) {
case $node instanceof Property:
// Properties declared at the top of the class are added to buffer
try {
$this->collectPropertyType(
(string) $node->props[0]->name,
self::getTypeCollectionFromNode($node)
);
} catch (TypeNotFoundInNodeException $e) {
// If the property does not have the type, we ignore it
}
break;
case $node instanceof PropertyFetch:
// Properties used within the class are injected with type from buffer
StaticLoggerFacade::notice(
"We are only adding types to properties in the class itself.\n"
. "We should fix this by adding them also to the super classes and traits.\n",
[__METHOD__]
);
try {
$this->addTypeCollectionToNode($node, $this->getCollectedPropertyType((string) $node->name));
} catch (UnknownPropertyException $e) {
StaticLoggerFacade::warning(
"Silently ignoring a UnknownPropertyException in this visitor.\n"
. "The property is not in the buffer, so we can't add it to the PropertyFetch.\n"
. "This should be fixed in the type addition visitors.\n"
. $e->getMessage(),
[__METHOD__]
);
}
break;
}
}
public function leaveNode(Node $node): void
{
if ($node instanceof Class_) {
$this->resetCollectedPropertyType();
}
}
}
<?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/
*