Leadtech/BootFramework

View on GitHub
src/Leadtech/Boot/Builder.php

Summary

Maintainability
B
4 hrs
Test Coverage
<?php

namespace Boot;

use Boot\Exception\BootstrapException;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ExpressionLanguageProvider;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Filesystem\Filesystem;

/**
 * Class ApplicationContextBuilder.
 *
 * @author  Daan Biesterbos <daan@leadtech.nl>
 */
class Builder
{
    /** @var string */
    protected $appName = 'UNKNOWN';

    /** @var string  */
    protected $appVersion = 'UNKNOWN';

    /** @var  string */
    protected $compiledClassDir = null;

    /** @var  string */
    protected $projectDir = null;

    /** @var array  */
    protected $configDirs = [];

    /** @var string  */
    protected $environment = Boot::PRODUCTION;

    /** @var array  */
    protected $parameters = [];

    /** @var CompilerPassInterface[] */
    protected $compilerPasses = [];

    /** @var array  */
    protected $expressionProviders = [];

    /** @var InitializerInterface[] */
    protected $initializers = [];

    /** @var EventDispatcher */
    protected $eventDispatcher = null;

    /** @var Filesystem */
    protected $fileSystem = null;

    /**
     * @param $projectDir
     */
    public function __construct($projectDir)
    {
        if (!is_dir($projectDir)) {
            throw new \InvalidArgumentException(
                "Invalid project directory. The directory `$projectDir` does not exist."
            );
        }

        $this->projectDir = realpath($projectDir);
    }

    /**
     * @return ContainerInterface
     */
    public function build()
    {
        // Initialize
        foreach ($this->getInitializers() as $initializer) {
            $initializer->initialize($this);
        }

        // Create the application context
        return $this->createApplicationContext()

            // Configurations
            ->setCompiledClassDir($this->getCompiledClassDir())
            ->setDirectories($this->getRealPaths())
            ->setEnvironment($this->getEnvironment())
            ->setCompilerPasses($this->getCompilerPasses())

            // Bootstrap the application
            ->bootstrap(
                $this->getParameters(),
                $this->isOptimized(),
                $this->getInitializers(),
                $this->getExpressionProviders()
            )
        ;
    }

    /**
     * Creates instance of the application context.
     *
     * @return ApplicationContext
     */
    protected function createApplicationContext()
    {
        return new ApplicationContext($this->getAppName());
    }

    /**
     * @param string $name
     *
     * @return $this
     */
    public function appName($name)
    {
        if (!ctype_alnum($name)) {
            throw new \InvalidArgumentException('The appname must be alphanumeric. Only letters and digits are allowed.');
        }
        $this->appName = $name;

        return $this;
    }

    /**
     * @param string $appVersion
     *
     * @return $this
     */
    public function appVersion($appVersion)
    {
        $this->appVersion = $appVersion;

        return $this;
    }

    /**
     * @return string
     */
    public function getAppVersion()
    {
        return $this->appVersion;
    }

    /**
     * @param InitializerInterface $initializer
     *
     * @return $this
     */
    public function initializer(InitializerInterface $initializer)
    {
        $this->initializers[] = $initializer;

        return $this;
    }

    /**
     * @param ExpressionLanguageProvider $provider
     *
     * @return $this
     */
    public function expr(ExpressionLanguageProvider $provider)
    {
        $this->expressionProviders[] = $provider;

        return $this;
    }

    /**
     * @param $env
     *
     * @return $this
     */
    public function environment($env)
    {
        $this->environment = $env;

        return $this;
    }

    /**
     * @param string $path
     *
     * @return $this
     */
    public function configDir($path)
    {
        $this->configDirs[] = $path;

        return $this;
    }

    /**
     * @param array $paths
     *
     * @return $this
     */
    public function configDirs(array $paths)
    {
        $this->configDirs = array_merge($paths, $this->configDirs);

        return $this;
    }

    /**
     * @param $name
     * @param $value
     *
     * @return $this
     */
    public function parameter($name, $value)
    {
        $this->parameters[$name] = $value;

        return $this;
    }

    /**
     * @param string $directory
     *
     * @return $this
     */
    public function optimize($directory)
    {
        // Determine if the given directory is relative or absolute.
        // If the path is relative do prepend the project dir.
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            if (!preg_match('/^[A-Z]:.*$/', $directory)) {
                $directory = $this->getProjectDir().DIRECTORY_SEPARATOR.$directory;
            }
        } elseif (substr($directory, 0, 1) != DIRECTORY_SEPARATOR) {
            $directory = $this->getProjectDir().DIRECTORY_SEPARATOR.$directory;
        }

        $dirExists = is_dir($directory);
        if (!$dirExists) {
            $this->getFileSystem()->mkdir($directory, 0777);
        }

        $this->compiledClassDir = $directory;

        return $this;
    }

    /**
     * @param CompilerPassInterface $compilerPass
     *
     * @return $this
     */
    public function afterRemoving(CompilerPassInterface $compilerPass)
    {
        $this->compilerPasses[] = [$compilerPass, PassConfig::TYPE_AFTER_REMOVING];

        return $this;
    }

    /**
     * @param CompilerPassInterface $compilerPass
     *
     * @return $this
     */
    public function beforeRemoving(CompilerPassInterface $compilerPass)
    {
        $this->compilerPasses[] = [$compilerPass, PassConfig::TYPE_BEFORE_REMOVING];

        return $this;
    }

    /**
     * @param CompilerPassInterface $compilerPass
     *
     * @return $this
     */
    public function beforeOptimization(CompilerPassInterface $compilerPass)
    {
        $this->compilerPasses[] = [$compilerPass, PassConfig::TYPE_BEFORE_OPTIMIZATION];

        return $this;
    }

    /**
     * @param CompilerPassInterface $compilerPass
     *
     * @return $this
     */
    public function onOptimization(CompilerPassInterface $compilerPass)
    {
        $this->compilerPasses[] = [$compilerPass, PassConfig::TYPE_OPTIMIZE];

        return $this;
    }

    /**
     * @param CompilerPassInterface $compilerPass
     *
     * @return $this
     */
    public function onRemoving(CompilerPassInterface $compilerPass)
    {
        $this->compilerPasses[] = [$compilerPass, PassConfig::TYPE_REMOVE];

        return $this;
    }

    /**
     * @return string[]
     */
    protected function getRealPaths()
    {
        $directories = [];
        $rootDir = $this->projectDir ?: getcwd();
        foreach ($this->getConfigDirs() as $path) {

            // Check if this path is absolute
            if (is_dir($path)) {
                $directories[] = $path;
                continue;
            }

            // Prepend relative dirs to project root directory
            if (substr($path, 0, 1) !== DIRECTORY_SEPARATOR) {

                // Create full path
                $realpath = realpath($rootDir.DIRECTORY_SEPARATOR.$path);

                // Check if the realpath is valid, if so use this path.
                if (!empty($realpath)) {
                    // Path is valid!
                    $directories[] = $realpath;
                    continue;
                }

                // Path not found
                throw new \InvalidArgumentException("Unable to resolve path to directory '{$path}'.");
            }
        }

        return $directories;
    }

    /**
     * @return string
     */
    public function getAppName()
    {
        return $this->appName;
    }

    /**
     * @return string
     */
    public function getCompiledClassDir()
    {
        return $this->compiledClassDir;
    }

    /**
     * @return string
     */
    public function getProjectDir()
    {
        return $this->projectDir;
    }

    /**
     * @return array
     */
    public function getConfigDirs()
    {
        return $this->configDirs;
    }

    /**
     * @return bool
     */
    public function isOptimized()
    {
        return  $this->environment === Boot::PRODUCTION && !empty($this->compiledClassDir);
    }

    /**
     * @return string
     */
    public function getEnvironment()
    {
        return $this->environment;
    }

    /**
     * @return array
     */
    public function getParameters()
    {
        return $this->parameters;
    }

    /**
     * @return array
     */
    public function getExpressionProviders()
    {
        return $this->expressionProviders;
    }

    /**
     * @return InitializerInterface[]
     */
    public function getInitializers()
    {
        return $this->initializers;
    }

    /**
     * Lazy load a event dispatcher if needed.
     *
     * @return EventDispatcherInterface
     */
    public function getEventDispatcher()
    {
        if (!$this->eventDispatcher instanceof EventDispatcher) {
            $this->eventDispatcher = new EventDispatcher();
        }

        return $this->eventDispatcher;
    }

    /**
     * @return CompilerPassInterface[]
     */
    public function getCompilerPasses()
    {
        return $this->compilerPasses;
    }

    /**
     * @return Filesystem
     */
    public function getFileSystem()
    {
        if (!$this->fileSystem instanceof Filesystem) {
            $this->fileSystem = new Filesystem();
        }

        return $this->fileSystem;
    }
}