Passed
Push — php7.3 ( 4ab8d4 )
by Akihito
10:50
created

Injector::getAdapter()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 11
rs 10
cc 3
nc 3
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Package;
6
7
use BEAR\AppMeta\Meta;
8
use BEAR\Package\Injector\FileUpdate;
9
use BEAR\Package\Module\ScriptinjectorModule;
10
use BEAR\Sunday\Extension\Application\AppInterface;
11
use Ray\Compiler\Annotation\Compile;
12
use Ray\Compiler\ScriptInjector;
13
use Ray\Di\AbstractModule;
14
use Ray\Di\Injector as RayInjector;
15
use Ray\Di\InjectorInterface;
16
use Symfony\Component\Cache\Adapter\AdapterInterface;
17
use Symfony\Component\Cache\Adapter\ApcuAdapter;
18
use Symfony\Component\Cache\Adapter\ChainAdapter;
19
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
20
use Symfony\Contracts\Cache\CacheInterface;
21
22
use function assert;
23
use function is_bool;
24
use function is_dir;
25
use function mkdir;
26
use function str_replace;
27
28
final class Injector
29
{
30
    /**
31
     * Serialized injector instances
32
     *
33
     * @var array<string, InjectorInterface>
34
     */
35
    private static $instances;
36
37
    /** @var array<string, AbstractModule> */
38
    private static $modules;
39
40
    /**
41
     * @codeCoverageIgnore
42
     */
43
    private function __construct()
44
    {
45
    }
46
47
    public static function getInstance(string $appName, string $context, string $appDir, ?CacheInterface $cache = null): InjectorInterface
48
    {
49
        $injectorId = str_replace('\\', '_', $appName) . $context;
50
        if (isset(self::$instances[$injectorId])) {
51
            return self::$instances[$injectorId];
52
        }
53
54
        $meta = new Meta($appName, $context, $appDir);
55
        $cache = self::getAdapter($cache, $injectorId, $meta);
56
        assert($cache instanceof AdapterInterface);
57
        /** @psalm-suppress all */
58
        [$injector, $fileUpdate] = $cache->getItem($injectorId)->get();
59
        $isCacheableInjector = $injector instanceof ScriptInjector || ($injector instanceof InjectorInterface && $fileUpdate instanceof FileUpdate && $fileUpdate->isNotUpdated($meta));
60
        if (! $isCacheableInjector) {
61
            $injector = self::factory($meta, $context);
62
            $cache->save($cache->getItem($injectorId)->set([$injector, new FileUpdate($meta)]));
63
        }
64
65
        self::$instances[$injectorId] = $injector;
66
67
        return $injector;
68
    }
69
70
    public static function getOverrideInstance(string $appName, string $context, string $appDir, AbstractModule $overrideModule): InjectorInterface
71
    {
72
        return self::factory(new Meta($appName, $context, $appDir), $context, $overrideModule);
73
    }
74
75
    private static function factory(Meta $meta, string $context, ?AbstractModule $overideModule = null): InjectorInterface
76
    {
77
        $scriptDir = $meta->tmpDir . '/di';
78
        ! is_dir($scriptDir) && ! @mkdir($scriptDir) && ! is_dir($scriptDir);
79
        $moduleId = $meta->appDir . $context;
80
        if (! isset(self::$modules[$moduleId])) {
81
            self::$modules[$moduleId] = (new Module())($meta, $context);
82
        }
83
84
        $module = self::$modules[$moduleId];
85
        if ($overideModule instanceof AbstractModule) {
86
            $module->override($overideModule);
87
        }
88
89
        $injector = new RayInjector($module, $scriptDir);
90
        $isProd = $injector->getInstance('', Compile::class);
91
        assert(is_bool($isProd));
92
        if ($isProd) {
93
            $injector = new ScriptInjector($scriptDir, static function () use ($scriptDir, $module) {
94
                return new ScriptinjectorModule($scriptDir, $module);
95
            });
96
        }
97
98
        $injector->getInstance(AppInterface::class);
99
100
        return $injector;
101
    }
102
103
    public static function getAdapter(?CacheInterface $cache, string $injectorId, Meta $meta): CacheInterface
104
    {
105
        if ($cache instanceof CacheInterface) {
106
            return $cache;
107
        }
108
        $filesystemAdapter = new FilesystemAdapter($injectorId, 0, $meta->tmpDir . '/injector');
109
        if (ApcuAdapter::isSupported()) {
110
            return new ChainAdapter([new ApcuAdapter($injectorId), $filesystemAdapter]);
111
        }
112
113
        return $filesystemAdapter;
114
    }
115
}
116