Skip to content

Better AoT compiler #3

@weierophinney

Description

@weierophinney

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions