Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better AoT compiler #3

Open
weierophinney opened this issue Dec 31, 2019 · 3 comments
Open

Better AoT compiler #3

weierophinney opened this issue Dec 31, 2019 · 3 comments

Comments

@weierophinney
Copy link
Member

In order to improve AoT compiler, on my project I've done some changes.

The problem is that with AoT, dependency factories are not created. To accomplish my goal I've made a simple DependencyScanner that try to fetch every dependency classes:

<?php

declare(strict_types=1);

namespace AppAoT\Scanner;

use Zend\Code\Reflection\ClassReflection;

class DependencyScanner
{
    /** @var string[] */
    private $classNamesToScan;

    /** @var string[] */
    private $classNames = [];

    /** @var bool */
    private $isScanned = false;

    /**
     * DependencyScanner constructor.
     *
     * @param string[] $classNamesToScan
     */
    public function __construct(array $classNamesToScan = [])
    {
        $this->classNamesToScan = $classNamesToScan;
    }

    protected function scan(): void
    {
        if ($this->isScanned) {
            return;
        }

        foreach ($this->classNamesToScan as $className)
        {
            $this->scanClass($className);
        }

        $this->isScanned = true;
    }

    protected function scanClass(string $name): void
    {
        if (! \class_exists($name)) {
            return;
        }

        if (\array_key_exists($name, $this->classNames)) {
            return;
        }

        $this->classNames[$name] = true;

        $classReflection = new ClassReflection($name);
        $constructor = $classReflection->getConstructor();

        if (null === $constructor) {
            return;
        }

        $parameters = $constructor->getParameters();

        foreach ($parameters as $parameter) {
            $class = $parameter->getClass();

            if (null === $class) {
                continue;
            }

            $this->scanClass($class->getName());
        }
    }

    /**
     * @return string[]
     */
    public function getClassNames(): array
    {
        $this->scan();
        
        return \array_keys($this->classNames);
    }
}

And changed my di-generate-aot.sh to use it, getting every psr-4 namespace directory (does not support dir arrays yet), and using the previous class to fetch every dependency class.

<?php

namespace AppAoT;

use AppAoT\Scanner\DependencyScanner;
use Psr\Container\ContainerInterface;
use Zend\Code\Scanner\DirectoryScanner;
use Zend\Di\CodeGenerator\InjectorGenerator;
use Zend\Di\ConfigInterface;
use Zend\Di\Definition\RuntimeDefinition;
use Zend\Di\Resolver\DependencyResolver;

require __DIR__ . '/../vendor/autoload.php';

$composerFile = __DIR__ . '/../composer.json';

if (! \is_readable($composerFile)) {
    throw new \RuntimeException('Unable to find composer.json');
}

$composer = \json_decode(\file_get_contents($composerFile), true);
$directories = \array_values($composer['autoload']['psr-4'] ?? []);

/** @var ContainerInterface $container */
$container = require __DIR__ . '/../config/container.php';
$config = $container->get(ConfigInterface::class);

$resolver = new DependencyResolver(new RuntimeDefinition(), $config);
$resolver->setContainer($container);

$scanner = new DirectoryScanner($directories);

$dependencyScanner = new DependencyScanner($scanner->getClassNames());
$classNames = $dependencyScanner->getClassNames();

$generator = new InjectorGenerator($config, $resolver, __NAMESPACE__ . '\Generated');
$generator->setOutputDirectory(__DIR__ . '/../src/AppAoT/gen');
$generator->generate($classNames);

It's just an example, but I think we can include something like that in zend-code and/or here, and improve the documentation in order to provide a better AoT compiler compared to others compiled containers.

P.S. It does not resolve aliases yet.


Originally posted by @thomasvargiu at zendframework/zend-di#42

@weierophinney
Copy link
Member Author

Thanks for your ideas. This is an interesting feature. I guess we could achieve this by utilizing the dependency resolver and a recursive iterator.


Originally posted by @tux-rampage at zendframework/zend-di#42 (comment)

@weierophinney
Copy link
Member Author

@tux-rampage I think we need get rid of zend-code scanner as these are going to be removed, see: zendframework/zend-code#124

I've noticed it works wrong with configuration files, like:

<?php

return [
    'factories' => [
        'service' => ClassName::class,
    ],
];

it doesn't break zend-di, but we get wrong class names (as reported in closed issue linked above).


Originally posted by @michalbundyra at zendframework/zend-di#42 (comment)

@weierophinney
Copy link
Member Author

@webimpress thanks for pointing this out. I removed the zend-code bits for 3.1 and I am not planning to re-introduce it. I thought about roave's better reflection instead since they offer support for composer.json autoloaders. If you have any other suggestions, they'll be welcome.


Originally posted by @tux-rampage at zendframework/zend-di#42 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant