ApplicationFactory::compileContainer()   B
last analyzed

Complexity

Conditions 5
Paths 8

Size

Total Lines 33
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 8.439
c 0
b 0
f 0
cc 5
eloc 20
nc 8
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the dotfiles project.
7
 *
8
 *     (c) Anthonius Munthi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Dotfiles\Core;
15
16
use Composer\Autoload\ClassLoader;
17
use Dotfiles\Core\Command\CompileCommand;
18
use Dotfiles\Core\Command\SubsplitCommand;
19
use Dotfiles\Core\Config\Config;
20
use Dotfiles\Core\Config\Definition;
21
use Dotfiles\Core\DI\Builder;
22
use Dotfiles\Core\Util\Toolkit;
23
use Symfony\Component\Config\Definition\ConfigurationInterface;
24
use Symfony\Component\DependencyInjection\Container;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Dotfiles\Core\Container. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
25
use Symfony\Component\Finder\Finder;
26
27
class ApplicationFactory
28
{
29
    /**
30
     * @var Builder
31
     */
32
    private $builder;
33
34
    /**
35
     * @var Config
36
     */
37
    private $config;
38
39
    /**
40
     * @var Container
41
     */
42
    private $container;
43
44
    /**
45
     * @var Plugin[]
46
     */
47
    private $plugins = array();
48
49
    /**
50
     * @return $this
51
     */
52
    public function boot(): self
53
    {
54
        $this->config = new Config();
55
        $this->builder = new Builder($this->config);
56
57
        $this->addAutoload();
58
        $this->loadPlugins();
59
        $this->compileContainer();
60
61
        return $this;
62
    }
63
64
    /**
65
     * @return Container
66
     */
67
    public function getContainer(): Container
68
    {
69
        return $this->container;
70
    }
71
72
    /**
73
     * @param string $name
74
     *
75
     * @return bool
76
     */
77
    public function hasPlugin(string $name): bool
78
    {
79
        return array_key_exists($name, $this->plugins);
80
    }
81
82
    private function addAutoload(): void
83
    {
84
        $baseDir = Toolkit::getBaseDir();
85
        $autoloadFile = $baseDir.'/vendor/autoload.php';
86
87
        // ignore if files is already loaded in phar file
88
        if (
89
            is_file($autoloadFile) &&
90
            (false === strpos($autoloadFile, 'phar:///'))
91
        ) {
92
            include_once $autoloadFile;
93
        }
94
    }
95
96
    private function compileContainer(): void
97
    {
98
        // begin loading configuration
99
        $config = $this->config;
100
        $phar = \Phar::running(false);
101
        if (is_file($phar) && is_dir($dir = dirname($phar).'/config')) {
102
            $config->addConfigDir($dir);
103
        }
104
        $config->addDefinition(new Definition());
105
        $config->loadConfiguration();
106
107
        // start build container
108
        $builder = $this->builder;
109
        $builder->setConfig($config);
110
        $builder->getContainerBuilder()->getParameterBag()->add($config->getAll(true));
111
112
        /* @var Plugin $plugin */
113
        foreach ($this->plugins as $plugin) {
114
            $plugin->load($config->getAll(true), $builder->getContainerBuilder());
115
        }
116
117
        $builder->compile();
118
        $container = $builder->getContainer();
119
        $container->set(Config::class, $config);
120
121
        if ('dev' === getenv('DOTFILES_ENV')) {
122
            $app = $container->get('dotfiles.app');
123
            $app->add(new SubsplitCommand());
124
            $app->add(new CompileCommand());
125
        }
126
127
        $this->container = $container;
128
        $this->ensureDirectories($config);
129
    }
130
131
    private function ensureDirectories(Config $config): void
132
    {
133
        Toolkit::ensureDir($config->get('dotfiles.temp_dir'));
134
        Toolkit::ensureDir($config->get('dotfiles.bin_dir'));
135
        Toolkit::ensureDir($config->get('dotfiles.cache_dir'));
136
        Toolkit::ensureDir($config->get('dotfiles.log_dir'));
137
    }
138
139
    /**
140
     * Load available plugins directory.
141
     *
142
     * @return array
143
     */
144
    private function loadDirectoryFromAutoloader()
145
    {
146
        $spl = spl_autoload_functions();
147
148
        $dirs = array();
149
        foreach ($spl as $item) {
150
            $object = $item[0];
151
            if (!$object instanceof ClassLoader) {
152
                continue;
153
            }
154
            $temp = array_merge($object->getPrefixes(), $object->getPrefixesPsr4());
155
            foreach ($temp as $name => $dir) {
156
                if (false === strpos($name, 'Dotfiles')) {
157
                    continue;
158
                }
159
                foreach ($dir as $num => $path) {
160
                    $path = realpath($path);
161
                    if ($path && is_dir($path) && !in_array($path, $dirs)) {
162
                        $dirs[] = $path;
163
                    }
164
                }
165
            }
166
        }
167
168
        return $dirs;
169
    }
170
171
    /**
172
     * Load available plugins.
173
     */
174
    private function loadPlugins(): void
175
    {
176
        $finder = Finder::create();
177
        $finder
178
            ->name('*Plugin.php')
179
        ;
180
        if (is_dir($dir = __DIR__.'/../Plugins')) {
181
            $finder->in(__DIR__.'/../Plugins');
182
        }
183
        $dirs = $this->loadDirectoryFromAutoloader();
184
        $finder->in($dirs);
185
        foreach ($finder->files() as $file) {
186
            $namespace = 'Dotfiles\\Plugins\\'.str_replace('Plugin.php', '', $file->getFileName());
187
            $className = $namespace.'\\'.str_replace('.php', '', $file->getFileName());
188
            if (class_exists($className)) {
189
                /* @var \Dotfiles\Core\Plugin $plugin */
190
                $plugin = new $className();
191
                $this->registerPlugin($plugin);
192
            }
193
        }
194
    }
195
196
    /**
197
     * Register plugin.
198
     *
199
     * @param Plugin $plugin
200
     */
201
    private function registerPlugin(Plugin $plugin): void
202
    {
203
        if ($this->hasPlugin($plugin->getName())) {
204
            return;
205
        }
206
207
        $this->plugins[$plugin->getName()] = $plugin;
208
        $config = $plugin->getConfiguration(array(), $this->builder->getContainerBuilder());
209
        if ($config instanceof ConfigurationInterface) {
210
            $this->config->addDefinition($config);
211
        }
212
    }
213
}
214