Passed
Push — check-cache ( 5bf960 )
by Akihito
11:11
created

PackageInjector::getInstance()   B

Complexity

Conditions 7
Paths 13

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 14
c 2
b 0
f 0
dl 0
loc 24
rs 8.8333
cc 7
nc 13
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Package\Injector;
6
7
use BEAR\AppMeta\AbstractAppMeta;
8
use BEAR\Package\Exception\LogicException;
9
use BEAR\Package\LazyModule;
10
use BEAR\Package\Module;
11
use BEAR\Sunday\Extension\Application\AppInterface;
12
use Ray\Compiler\Annotation\Compile;
13
use Ray\Compiler\CompileInjector;
14
use Ray\Compiler\ScriptInjector;
15
use Ray\Di\AbstractModule;
16
use Ray\Di\Injector as RayInjector;
17
use Ray\Di\InjectorInterface;
18
use Symfony\Component\Cache\Adapter\AdapterInterface;
19
use Symfony\Contracts\Cache\CacheInterface;
20
21
use function assert;
22
use function is_bool;
23
use function is_dir;
24
use function mkdir;
25
use function str_replace;
26
27
final class PackageInjector
28
{
29
    /**
30
     * Serialized injector instances
31
     *
32
     * @var array<string, InjectorInterface>
33
     */
34
    private static array $instances;
35
36
    /** @codeCoverageIgnore */
37
    private function __construct()
38
    {
39
    }
40
41
    public static function getInstance(AbstractAppMeta $meta, string $context, CacheInterface|null $cache): InjectorInterface
42
    {
43
        $injectorId = str_replace('\\', '_', $meta->name) . $context;
44
        if (isset(self::$instances[$injectorId])) {
45
            return self::$instances[$injectorId];
46
        }
47
48
        assert($cache instanceof AdapterInterface);
49
        /** @psalm-suppress all */
50
        [$injector, $fileUpdate] = $cache->getItem($injectorId)->get(); // @phpstan-ignore-line
51
        $isCacheableInjector = $injector instanceof ScriptInjector || ($injector instanceof InjectorInterface && $fileUpdate instanceof FileUpdate && $fileUpdate->isNotUpdated($meta));
52
        if (! $isCacheableInjector) {
53
            $injector = self::factory($meta, $context);
54
            $cache->save($cache->getItem($injectorId)->set([$injector, new FileUpdate($meta)]));
55
            // Check the cache
56
            $readCache = $cache->getItem($injectorId)->get();
57
            if ($readCache === null) {
58
                throw new LogicException('Failed to save the injector cache. Perhaps a non-serializable object is contained. Report the issue at https://github.com/bearsunday/BEAR.Package/issues');
59
            }
60
        }
61
62
        self::$instances[$injectorId] = $injector;
63
64
        return $injector;
65
    }
66
67
    public static function factory(AbstractAppMeta $meta, string $context, AbstractModule|null $overideModule = null): InjectorInterface
68
    {
69
        $scriptDir = $meta->tmpDir . '/di';
70
        ! is_dir($scriptDir) && ! @mkdir($scriptDir) && ! is_dir($scriptDir);
71
        $module = (new Module())($meta, $context);
72
73
        if ($overideModule instanceof AbstractModule) {
74
            $module->override($overideModule);
75
        }
76
77
        $injector = new RayInjector($module, $scriptDir);
78
        $isProd = $injector->getInstance('', Compile::class);
79
        assert(is_bool($isProd));
80
        if ($isProd) {
81
            $injector = new CompileInjector($scriptDir, new LazyModule($meta, $context, $scriptDir));
82
        }
83
84
        $injector->getInstance(AppInterface::class);
85
86
        return $injector;
87
    }
88
}
89