PackageInjector::getInstance()   A
last analyzed

Complexity

Conditions 6
Paths 9

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 10
c 3
b 0
f 0
dl 0
loc 18
rs 9.2222
cc 6
nc 9
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\LazyModule;
9
use BEAR\Package\Module;
10
use BEAR\Sunday\Extension\Application\AppInterface;
11
use Ray\Compiler\Annotation\Compile;
12
use Ray\Compiler\CompileInjector;
13
use Ray\Compiler\ScriptInjector;
14
use Ray\Di\AbstractModule;
15
use Ray\Di\Injector as RayInjector;
16
use Ray\Di\InjectorInterface;
17
use Symfony\Component\Cache\Adapter\AdapterInterface;
18
use Symfony\Contracts\Cache\CacheInterface;
19
20
use function assert;
21
use function is_bool;
22
use function is_dir;
23
use function mkdir;
24
use function str_replace;
25
use function trigger_error;
26
27
use const E_USER_WARNING;
28
29
final class PackageInjector
30
{
31
    /**
32
     * Serialized injector instances
33
     *
34
     * @var array<string, InjectorInterface>
35
     */
36
    private static array $instances;
37
38
    /** @codeCoverageIgnore */
39
    private function __construct()
40
    {
41
    }
42
43
    /**
44
     * Returns an instance of InjectorInterface based on the given parameters
45
     *
46
     * - Injector instances are cached in memory and in the cache adapter.
47
     * - The injector is re-used in subsequent calls in the same context in the unit test.
48
     */
49
    public static function getInstance(AbstractAppMeta $meta, string $context, CacheInterface|null $cache): InjectorInterface
50
    {
51
        $injectorId = str_replace('\\', '_', $meta->name) . $context;
52
        if (isset(self::$instances[$injectorId])) {
53
            return self::$instances[$injectorId];
54
        }
55
56
        assert($cache instanceof AdapterInterface);
57
        /** @psalm-suppress all */
58
        [$injector, $fileUpdate] = $cache->getItem($injectorId)->get(); // @phpstan-ignore-line
59
        $isCacheableInjector = $injector instanceof ScriptInjector || ($injector instanceof InjectorInterface && $fileUpdate instanceof FileUpdate && $fileUpdate->isNotUpdated($meta));
60
        if (! $isCacheableInjector) {
61
            $injector = self::getInjector($meta, $context, $cache, $injectorId);
62
        }
63
64
        self::$instances[$injectorId] = $injector;
65
66
        return $injector;
67
    }
68
69
    /**
70
     * Return an injector instance with the given override module
71
     *
72
     * This is useful for testing purposes, where you want to override a module with a mock or stub
73
     */
74
    public static function factory(AbstractAppMeta $meta, string $context, AbstractModule|null $overrideModule = null): InjectorInterface
75
    {
76
        $scriptDir = $meta->tmpDir . '/di';
77
        ! is_dir($scriptDir) && ! @mkdir($scriptDir) && ! is_dir($scriptDir);
78
        $module = (new Module())($meta, $context);
79
80
        if ($overrideModule instanceof AbstractModule) {
81
            $module->override($overrideModule);
82
        }
83
84
        $injector = new RayInjector($module, $scriptDir);
85
        $isProd = $injector->getInstance('', Compile::class);
86
        assert(is_bool($isProd));
87
        if ($isProd) {
88
            $injector = new CompileInjector($scriptDir, new LazyModule($meta, $context, $scriptDir));
89
        }
90
91
        $injector->getInstance(AppInterface::class);
92
93
        return $injector;
94
    }
95
96
    private static function getInjector(AbstractAppMeta $meta, string $context, AdapterInterface $cache, string $injectorId): InjectorInterface
97
    {
98
        $injector = self::factory($meta, $context);
99
        $cache->save($cache->getItem($injectorId)->set([$injector, new FileUpdate($meta)]));
100
        // Check the cache
101
        if ($cache->getItem($injectorId)->get() === null) {
102
            trigger_error('Failed to verify the injector cache. See https://github.com/bearsunday/BEAR.Package/issues/418', E_USER_WARNING);
103
        }
104
105
        return $injector;
106
    }
107
}
108