Make the type storage consistent

Always as a Type class object.
parent 2d2add13
......@@ -85,7 +85,7 @@ dep-update:
./bin/docker-run.sh ${CONTAINER_NAME} composer update
.test:
- $(MAKE) cs-fix
- $(MAKE) .cs-fix
bin/phpunit
$(MAKE) .test-dep
......
This diff is collapsed.
parameters:
# All services in this list will be added to the container used in tests
# This is specially useful for the services provided by 3rd party bundles
test.container.service_list:
- 'Hgraca\ContextMapper\Core\Component\Main\Application\Service\ContextMapService'
- 'Hgraca\ContextMapper\Core\Port\Configuration\ConfigurationFactoryInterface'
<?php
declare(strict_types=1);
/*
* This file is part of the Context 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\ContextMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\PhpExtension\Exception\RuntimeException;
final class TypeNotFoundInNodeException extends RuntimeException
{
}
<?php
declare(strict_types=1);
/*
* This file is part of the Context 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\ContextMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\PhpExtension\Exception\RuntimeException;
final class UnknownFqcnException extends RuntimeException
{
}
<?php
declare(strict_types=1);
/*
* This file is part of the Context 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\ContextMapper\Infrastructure\Parser\NikicPhpParser\Exception;
use Hgraca\PhpExtension\Exception\RuntimeException;
final class UnknownPropertyException extends RuntimeException
{
public function __construct(string $propertyName)
{
parent::__construct("Unknown property $propertyName");
}
}
......@@ -21,7 +21,7 @@ use Hgraca\ContextMapper\Core\Port\Parser\Exception\ParserException;
use Hgraca\ContextMapper\Core\Port\Parser\Node\ClassInterface;
use Hgraca\ContextMapper\Core\Port\Parser\Node\MethodInterface;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Exception\MethodNotFoundInClassException;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AstConnectorVisitorInterface;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AbstractTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\Type;
use PhpParser\Node;
use PhpParser\Node\Expr\New_;
......@@ -50,19 +50,30 @@ final class ClassAdapter implements ClassInterface
*/
private $implementedList;
public function __construct(Class_ $class)
private function __construct()
{
$this->class = $class;
}
public static function constructFromClassNode(Class_ $class): self
{
$self = new self();
$self->class = $class;
return $self;
}
public static function constructFromNew(New_ $newExpression): self
{
return new self($newExpression->getAttribute(AstConnectorVisitorInterface::KEY_AST));
/** @var Class_ $class */
$class = AbstractTypeInjectorVisitor::getTypeFromNode($newExpression)->getAst();
return self::constructFromClassNode($class);
}
public function getFullyQualifiedType(): string
{
return '\\' . ltrim($this->class->namespacedName->toCodeString(), '\\');
return ltrim($this->class->namespacedName->toCodeString(), '\\');
}
public function getCanonicalType(): string
......@@ -130,7 +141,7 @@ final class ClassAdapter implements ClassInterface
/** @var Type $interfaceType */
$interfaceType = $interfaceNameNode->getAttribute(Type::getName());
$implementedList[] = [
$interfaceType->getFqcn() => $interfaceType->hasAst() ? $interfaceType->getAst() : null,
$interfaceType->toString() => $interfaceType->hasAst() ? $interfaceType->getAst() : null,
];
if ($interfaceType->hasAst()) {
$implementedList[] = $this->findAllParentsFullyQualifiedNameListRecursively(
......@@ -167,7 +178,7 @@ final class ClassAdapter implements ClassInterface
/** @var Type $parentType */
$parentType = $parentNameNode->getAttribute(Type::getName());
$parentList[] = [
$parentType->getFqcn() => $parentType->hasAst() ? $parentType->getAst() : null,
$parentType->toString() => $parentType->hasAst() ? $parentType->getAst() : null,
];
if (!$parentType->hasAst()) {
......
......@@ -32,7 +32,7 @@ final class FullyQualifiedTypeNode implements TypeNodeInterface
public function __construct(string $fqcn)
{
$this->fqcn = '\\' . ltrim($fqcn, '\\');
$this->fqcn = ltrim($fqcn, '\\');
}
public function getFullyQualifiedType(): string
......
......@@ -20,7 +20,7 @@ namespace Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Node;
use Hgraca\ContextMapper\Core\Port\Parser\Node\MethodInterface;
use Hgraca\ContextMapper\Core\Port\Parser\Node\MethodParameterInterface;
use Hgraca\ContextMapper\Core\Port\Parser\Node\TypeNodeInterface;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AstConnectorVisitorInterface;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AbstractTypeInjectorVisitor;
use PhpParser\Node\Stmt\ClassMethod;
final class MethodAdapter implements MethodInterface
......@@ -45,8 +45,8 @@ final class MethodAdapter implements MethodInterface
$returnType = $this->classMethod->getReturnType();
return NodeFactory::constructTypeNodeAdapter(
$returnType !== null && $returnType->hasAttribute(AstConnectorVisitorInterface::KEY_AST)
? $returnType->getAttribute(AstConnectorVisitorInterface::KEY_AST)
$returnType !== null
? AbstractTypeInjectorVisitor::getTypeFromNode($returnType)
: null
);
}
......
......@@ -20,11 +20,12 @@ namespace Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Node;
use Hgraca\ContextMapper\Core\Port\Parser\Node\MethodArgumentInterface;
use Hgraca\ContextMapper\Core\Port\Parser\Node\TypeNodeInterface;
use Hgraca\ContextMapper\Core\SharedKernel\Exception\NotImplementedException;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AstConnectorVisitorInterface;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AbstractTypeInjectorVisitor;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Class_;
final class MethodArgumentAdapter implements MethodArgumentInterface
{
......@@ -38,16 +39,16 @@ final class MethodArgumentAdapter implements MethodArgumentInterface
switch (true) {
case $argument instanceof New_:
$this->argument = NodeFactory::constructTypeNodeAdapter(
$argument->class->getAttribute(AstConnectorVisitorInterface::KEY_AST)
AbstractTypeInjectorVisitor::getTypeFromNode($argument)
);
break;
case $argument instanceof Variable:
$this->argument = NodeFactory::constructTypeNodeAdapter(
$argument->getAttribute(AstConnectorVisitorInterface::KEY_AST)
AbstractTypeInjectorVisitor::getTypeFromNode($argument)
);
break;
case $argument instanceof StaticCall:
$class = new ClassAdapter($argument->class->getAttribute(AstConnectorVisitorInterface::KEY_AST));
case $argument instanceof StaticCall && $this->staticCallClassAstIsKnown($argument):
$class = ClassAdapter::constructFromClassNode($this->getStaticCallClassAst($argument));
$method = $class->getMethod($argument->name->toString());
$this->argument = $method->getReturnTypeNode();
break;
......@@ -75,4 +76,17 @@ final class MethodArgumentAdapter implements MethodArgumentInterface
{
throw new NotImplementedException();
}
private function staticCallClassAstIsKnown(StaticCall $argument): bool
{
return AbstractTypeInjectorVisitor::getTypeFromNode($argument->class)->hasAst();
}
private function getStaticCallClassAst(StaticCall $argument): Class_
{
/** @var Class_ $class */
$class = AbstractTypeInjectorVisitor::getTypeFromNode($argument->class)->getAst();
return $class;
}
}
......@@ -101,7 +101,7 @@ final class MethodCallAdapter implements MethodCallInterface
$node = $node->getAttribute(ParentConnectorVisitor::PARENT_NODE);
} while (!$node instanceof Class_);
$this->enclosingClass = new ClassAdapter($node);
$this->enclosingClass = ClassAdapter::constructFromClassNode($node);
}
return $this->enclosingClass;
......
......@@ -19,12 +19,14 @@ namespace Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Node;
use Hgraca\ContextMapper\Core\Port\Parser\Node\AdapterNodeInterface;
use Hgraca\ContextMapper\Core\Port\Parser\Node\TypeNodeInterface;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AbstractTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\Type;
use Hgraca\PhpExtension\Type\TypeService;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use function is_string;
final class NodeFactory
{
......@@ -33,6 +35,7 @@ final class NodeFactory
*/
public static function constructNodeAdapter($parserNode): AdapterNodeInterface
{
$type = AbstractTypeInjectorVisitor::getTypeFromNode($parserNode);
switch (true) {
case $parserNode instanceof ClassMethod: // this needs to be above the `Expr`
return new MethodAdapter($parserNode);
......@@ -40,37 +43,28 @@ final class NodeFactory
return new MethodCallAdapter($parserNode);
case $parserNode instanceof Class_:
case $parserNode instanceof Expr: // MethodArgument
case self::isFullyQualifiedName($parserNode):
return self::constructTypeNodeAdapter($parserNode);
case TypeService::isValidFQCN((string) $type):
return self::constructTypeNodeAdapter($type);
default:
return new UnknownTypeNode($parserNode);
}
}
/**
* @param null|string|Node $parserNode
* @param null|string|Node $type
*/
public static function constructTypeNodeAdapter($parserNode): TypeNodeInterface
public static function constructTypeNodeAdapter(Type $type): TypeNodeInterface
{
$ast = $type->getAst();
switch (true) {
case $parserNode instanceof Class_:
return new ClassAdapter($parserNode);
case $parserNode instanceof Expr:
return new MethodArgumentAdapter($parserNode);
case self::isFullyQualifiedName($parserNode):
return new FullyQualifiedTypeNode($parserNode);
case $ast instanceof Class_:
return ClassAdapter::constructFromClassNode($ast);
case $ast instanceof Expr:
return new MethodArgumentAdapter($ast);
case TypeService::isValidFQCN((string) $type):
return new FullyQualifiedTypeNode((string) $type);
default:
return new UnknownTypeNode($parserNode);
return new UnknownTypeNode((string) $type);
}
}
private static function isFullyQualifiedName($string): bool
{
return is_string($string)
&& !in_array(
$string,
['boolean', 'integer', 'float', 'string', 'array', 'object', 'callable', 'iterable', 'resource', 'null'],
true
);
}
}
......@@ -41,7 +41,7 @@ final class TypeNameAdapter implements TypeNodeInterface
/** @var FullyQualified $resolvedName */
$resolvedName = $this->name->getAttribute('resolvedName');
return $resolvedName->toCodeString();
return ltrim($resolvedName->toCodeString(), '\\');
}
public function getCanonicalType(): string
......@@ -51,7 +51,7 @@ final class TypeNameAdapter implements TypeNodeInterface
public function getTypeNode(): Node
{
return $this->name->getAttribute('ast');
return $this->name->getAttribute('astCollection');
}
public function getAllFamilyFullyQualifiedNameList(): array
......
......@@ -19,15 +19,18 @@ namespace Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Exception\AstNodeNotFoundException;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Exception\UnitNotFoundInNamespaceException;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\ExtendedTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\ImplementedTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AssignmentFromNewTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AssignmentFromParameterTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\ClassFamilyTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\ClassTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\InstantiationTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\InterfaceFamilyTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\MethodParametersTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\MethodReturnTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\ParentConnectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\PropertyDeclarationTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\PropertyTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\PropertyFetchTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\StaticCallClassTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\VariableTypeInjectorVisitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\ThisTypeInjectorVisitor;
use Hgraca\PhpExtension\String\JsonEncoder;
use PhpParser\JsonDecoder;
use PhpParser\Node;
......@@ -131,29 +134,40 @@ final class NodeCollection
// Add all nodes into the collection
// Add visitors here if they don't need the final collection
$traverser = new NodeTraverser();
$traverser->addVisitor(new ParentConnectorVisitor());
$traverser->addVisitor(new NameResolver(null, ['preserveOriginalNames' => true, 'replaceNodes' => false]));
$traverser->addVisitor(new ParentConnectorVisitor());
$traverser->traverse($nodeList);
// First we need to set all nodes in the collection, then we can run the visitors that need the collection
// Run visitors that don't need any Types added before hand
$traverser = new NodeTraverser();
$traverser->addVisitor(new ExtendedTypeInjectorVisitor($this));
$traverser->addVisitor(new ImplementedTypeInjectorVisitor($this));
$traverser->addVisitor(new ClassTypeInjectorVisitor($this)); // TODO test
$traverser->addVisitor(new ClassFamilyTypeInjectorVisitor($this)); // TODO test
$traverser->addVisitor(new InterfaceFamilyTypeInjectorVisitor($this)); // TODO test
$traverser->addVisitor(new StaticCallClassTypeInjectorVisitor($this));
$traverser->addVisitor(new MethodReturnTypeInjectorVisitor($this));
$traverser->addVisitor(new ThisTypeInjectorVisitor($this));
$traverser->addVisitor(new MethodParametersTypeInjectorVisitor($this));
$traverser->addVisitor(new InstantiationTypeInjectorVisitor($this));
$traverser->addVisitor(new VariableTypeInjectorVisitor($this));
$traverser->traverse($nodeList);
$traverser = new NodeTraverser();
$traverser->addVisitor(new PropertyDeclarationTypeInjectorVisitor($this));
$traverser->addVisitor(new AssignmentFromNewTypeInjectorVisitor($this)); // TODO test
$traverser->addVisitor(new AssignmentFromParameterTypeInjectorVisitor($this)); // TODO test
$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 PropertyTypeInjectorVisitor($this));
$traverser->addVisitor(new PropertyFetchTypeInjectorVisitor($this));
$traverser->traverse($nodeList);
// $traverser = new NodeTraverser();
// TODO add AssignmentFromMethodCallTypeInjectorVisitor
// $traverser->traverse($nodeList);
$traverser = new NodeTraverser();
// TODO make a second pass with the PropertyFetchTypeInjectorVisitor
$traverser->traverse($nodeList);
$GLOBALS['nodes'] = $nodeList;
......
......@@ -17,8 +17,7 @@ declare(strict_types=1);
namespace Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Node\NodeFactory;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AstConnectorVisitorInterface;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor\AbstractTypeInjectorVisitor;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
......@@ -112,12 +111,11 @@ final class QueryBuilder
if (!$node instanceof MethodCall) {
return false;
}
$dispatcherFqcn = NodeFactory::constructTypeNodeAdapter(
$node->var->getAttribute(AstConnectorVisitorInterface::KEY_AST)
)->getFullyQualifiedType();
$dispatcherMethodName = $node->name->name;
$methodCall = $node;
$dispatcherFqcn = (string) AbstractTypeInjectorVisitor::getTypeFromNode($methodCall->var);
$dispatcherMethodName = (string) $methodCall->name;
return $node instanceof MethodCall
return $methodCall instanceof MethodCall
&& preg_match($eventDispatcherTypeRegex, $dispatcherFqcn)
&& preg_match($eventDispatcherMethodRegex, $dispatcherMethodName);
}
......
<?php
declare(strict_types=1);
/*
* This file is part of the Context 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\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor;
use Hgraca\ContextMapper\Core\SharedKernel\Exception\NotImplementedException;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Exception\AstNodeNotFoundException;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\NodeCollection;
use Hgraca\PhpExtension\Type\TypeService;
use PhpParser\Node;
use PhpParser\Node\Expr\New_;
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\NodeVisitorAbstract;
use function is_string;
abstract class AbstractTypeInjectorVisitor extends NodeVisitorAbstract implements AstConnectorVisitorInterface
{
/**
* @var NodeCollection
*/
protected $astCollection;
/**
* @var Type
*/
protected $self;
public function __construct(NodeCollection $astCollection)
{
/* @noinspection UnusedConstructorDependenciesInspection Used in trait */
$this->astCollection = $astCollection;
}
public function enterNode(Node $node): void
{
if ($node instanceof Class_) {
$this->self = $this->buildType($node->namespacedName);
}
}
// TODO create a new AssignmentFromMethodCallTypeInjectorVisitor and put this there
// TODO create a new AssignmentFromStaticCallTypeInjectorVisitor and put this there
// protected function handleAssignmentNode(Assign $assignment): void
// {
// $expression = $assignment->expr;
// $var = $assignment->var;
// switch (true) {
// case $expression instanceof MethodCall:
// // MethodCall on property
// $methodCall = $expression;
// $newType = $this->buildType($methodCall);
// $this->addTypeToNode($methodCall->class, $newType);
// switch (true) {
// case $var instanceof Variable:
// // Assignment of method return type to variable
// $this->addTypeToNode($var, $newType);
// $this->addVariableTypeToBuffer((string) $var->name, $newType);
// break;
// case $var instanceof PropertyFetch:
// // Assignment of method return type to property
// $property = $var;
// $this->addTypeToNode($property, $newType);
// $this->addPropertyTypeToBuffer((string) $property->name, $newType);
// break;
// }
// 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 new Type('NULL');
break;
default:
throw new NotImplementedException('Can\'t build Type from ' . TypeService::getType($node));
}
}
protected function buildTypeFromIdentifier(Identifier $identifier)
{
return new Type($identifier->name, null);
}
protected function buildTypeFromName(Name $name): Type
{
$fqcn = $this->buildFqcn($name);
if ($fqcn === 'self' || $fqcn === 'this') {
return $this->self;
}
try {
return new Type($fqcn, $this->astCollection->getAstNode($fqcn));
} catch (AstNodeNotFoundException $e) {
return new Type($fqcn);
}
}
protected function buildTypeFromNew(New_ $new)
{
return $this->buildType($new->class);
}
protected function buildTypeFromNullable(NullableType $nullableTypeNode): Type
{
return $this->buildType($nullableTypeNode->type);
}
private function buildTypeFromParam(Param $param)
{
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);
}
public function addTypeToNode(Node $node, Type $type): void
{
$node->setAttribute(Type::getName(), $type);
}
public static function getTypeFromNode(Node $node): Type
{
if (!$node->hasAttribute(Type::getName())) {
return new Type('Unknown (' . get_class($node) . ')');
// throw new TypeNotFoundInNodeException("Can't find type in node " . get_class($node));
}
return $node->getAttribute(Type::getName());
}
}
......@@ -17,85 +17,62 @@ declare(strict_types=1);
namespace Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\Visitor;
use Hgraca\ContextMapper\Infrastructure\Parser\NikicPhpParser\NodeCollection;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use PhpParser\NodeVisitorAbstract;
use function array_key_exists;
use PhpParser\Node\Stmt\ClassMethod;
class PropertyDeclarationTypeInjectorVisitor extends NodeVisitorAbstract implements AstConnectorVisitorInterface
final class AssignmentFromNewTypeInjectorVisitor extends AbstractTypeInjectorVisitor
{
/**
* @var NodeCollection
*/
private $ast;
private $propertyList = [];
public function __construct(NodeCollection $ast)
{
$this->ast = $ast;
}
use PropertyBufferTrait;
use VariableBufferTrait;
public function enterNode(Node $node): void
{
// TODO deal with properties initialized in the `parent::__construct(...)` call
// ie: NotifyServiceProAboutNewSavedSearchResultsLayoutGenerator::generate
parent::enterNode($node);
switch (true) {
case $node instanceof PropertyFetch
&& $node->getAttribute(ParentConnectorVisitor::PARENT_NODE) instanceof Assign:
// isPropertyAssignment
/** @var Assign $assignment */
$assignment = $node->getAttribute(ParentConnectorVisitor::PARENT_NODE);