symplify/astral

Toolking for smart daily work with AST

Maintainers

Details

github.com/symplify/astral

Source

Installs: 1 418 398

Dependents: 18

Suggesters: 0

Security: 0

Stars: 23

Watchers: 2

Forks: 0

Type:phpstan-extension

10.0.0-beta16 2021-12-02 10:51 UTC

README

Downloads total

Install

composer require symplify/astral

Add to Symfony Project

Register package in config/config.php:

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\Astral\ValueObject\AstralConfig::FILE_PATH;

return static function (ContainerConfigurator $containerConfigurator): void {
    $containerConfigurator->import(AstralConfig::FILE_PATH);
};

Add to PHPStan Rules

Include in your phpstan.neon:

includes:
    - vendor/symplify/astral/config/services.neon

Usage

1. Resolve Name of Any Node

How can you get the name of a specific node?

$someObject->someMethodCall();
// "someObject"
// "someMethodCall"

Require SimpleNameResolver in any service:

use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use Symplify\Astral\Naming\SimpleNameResolver;

final class SomeRule
{
    public function __construct(
        // PHP 8.0 promoted property syntax
        private SimpleNameResolver $simpleNameResolver
    ) {
    }

    public function process(Node $node)
    {
        if ($node instanceof MethodCall) {
            $callerName = $this->simpleNameResolver->getName($node->var);
            $methodName = $this->simpleNameResolver->getName($node->name);
        }
    }
}

For dynamic names that are not possible to resolve, the null will be returned:

$variable->${someMethod}();

2. Resolve Value of Node

$value = 1000;

How can we get the 1000 value?

use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Scalar\LNumber;
use PHPStan\Analyser\Scope;
use Symplify\Astral\NodeValue\NodeValueResolver;

final class SomeRule
{
    public function __construct(
        // PHP 8.0 promoted property syntax
        private NodeValueResolver $nodeValueResolver
    ) {
    }

    public function process(Node $node, Scope $scope)
    {
        if ($node instanceof Assign && $node->expr instanceof LNumber) {
            $resolvedValue = $this->nodeValueResolver->resolve($node->expr, $scope->getFile());
        }
    }
}

Work for static expressions like these:

$value = 'Hey';
// "Hey"

SomeClass::class;
// "SomeClass"

class SomeClass
{
    public const VALUE = 'different';
}

SomeClass::VALUE;
// "different"

__DIR__;
// realpath of the __DIR__ in its place

3. Unique *Builder Classes

Native PhpParser node class and builder class share the same short class name.

use PhpParser\Builder\Class_;
use PhpParser\Node\Stmt\Class_;

$class = new Class_('ClassName');
$class = $class->getNode();

This confuses IDE and lead to wrong classes being used as type hints. To avoid that, this package provides *Builder names:

use Symplify\Astral\ValueObject\NodeBuilder\ClassBuilder;

$classBuilder = new ClassBuilder('some_class');
$class = $classBuilder->getNode();

4. Traverse Nodes with Simple Callback

Working with nodes is based on traversing each one of them. You can use native NodeVisitor and NodeTraverses. But that requires to create at least 2 objects, to connect them and call them.

What if we need just a small traverse right in this method? Service SimpleCallableNodeTraverser to the rescue:

use PhpParser\Node;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;

/** @var ClassMethod $classMethod */
$classMethod = '...';

$simpleCallableNodeTraverser = new SimpleCallableNodeTraverser();
$simpleCallableNodeTraverser->traverseNodesWithCallable($classMethod, function (Node $node) {
    if (! $node instanceof String_) {
        return null;
    }

    $node->value = 'changed name';
    return $node;
});

Report Issues

In case you are experiencing a bug or want to request a new feature head over to the Symplify monorepo issue tracker


Contribute

The sources of this package are contained in the Symplify monorepo. We welcome contributions for this package on symplify/symplify.