CompileApp::compile()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 16
c 2
b 0
f 0
dl 0
loc 23
rs 9.7333
cc 2
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Package\Compiler;
6
7
use BEAR\AppMeta\AbstractAppMeta;
8
use BEAR\AppMeta\Meta;
9
use BEAR\Package\Injector\PackageInjector;
10
use BEAR\Resource\Exception\ParameterException;
11
use BEAR\Resource\NamedParameterInterface;
12
use BEAR\Sunday\Extension\Application\AppInterface;
13
use Doctrine\Common\Annotations\Reader;
14
use Ray\Compiler\CompileInjector;
15
use Ray\PsrCacheModule\LocalCacheProvider;
16
use ReflectionClass;
17
18
use function assert;
19
use function in_array;
20
use function is_callable;
21
use function microtime;
22
use function sprintf;
23
use function str_starts_with;
24
25
final class CompileApp
26
{
27
    /** @var array{class: int, method: int, time: float} */
28
    private array $logs = [
29
        'class' => 0,
30
        'method' => 0,
31
        'time' => 0,
32
    ];
33
34
    /**
35
     * Compile application
36
     *
37
     * DI+AOP script file
38
     * Parameter meta information
39
     * (No annotation cached)
40
     *
41
     * @param list<string> $extraContexts
0 ignored issues
show
Bug introduced by
The type BEAR\Package\Compiler\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
42
     *
43
     * @return array{class: int, method: int, time: float}
44
     */
45
    public function compile(CompileInjector $injector, array $extraContexts = []): array
46
    {
47
        $start = microtime(true);
48
        $reader = $injector->getInstance(Reader::class);
49
        assert($reader instanceof Reader);
50
        $namedParams = $injector->getInstance(NamedParameterInterface::class);
51
        assert($namedParams instanceof NamedParameterInterface);
52
        // create DI factory class and AOP compiled class for all resources and save $app cache.
53
        $app = $injector->getInstance(AppInterface::class);
54
        assert($app instanceof AppInterface);
55
        $meta = $injector->getInstance(AbstractAppMeta::class);
56
        // check resource injection and create annotation cache
57
        $resources = $meta->getResourceListGenerator();
58
        foreach ($resources as $resource) {
59
            $this->logs['class']++;
60
            [$className] = $resource;
61
            $this->saveMeta($namedParams, new ReflectionClass($className));
62
        }
63
64
        $this->compileExtraContexts($extraContexts, $meta);
65
        $this->logs['time'] = (float) sprintf('%.3f', microtime(true) - $start);
66
67
        return $this->logs;
68
    }
69
70
    /**
71
     * Save annotation and method meta information
72
     *
73
     * @param ReflectionClass<object> $class
74
     */
75
    private function saveMeta(NamedParameterInterface $namedParams, ReflectionClass $class): void
76
    {
77
        $instance = $class->newInstanceWithoutConstructor();
78
79
        $methods = $class->getMethods();
80
        foreach ($methods as $method) {
81
            $methodName = $method->getName();
82
83
            if (! str_starts_with($methodName, 'on')) {
84
                continue;
85
            }
86
87
            $this->logs['method']++;
88
89
            $this->saveNamedParam($namedParams, $instance, $methodName);
90
        }
91
    }
92
93
    private function saveNamedParam(NamedParameterInterface $namedParameter, object $instance, string $method): void
94
    {
95
        // named parameter
96
        if (! in_array($method, ['onGet', 'onPost', 'onPut', 'onPatch', 'onDelete', 'onHead'], true)) {
97
            return;  // @codeCoverageIgnore
98
        }
99
100
        $callable = [$instance, $method];
101
        if (! is_callable($callable)) {
102
            return;  // @codeCoverageIgnore
103
        }
104
105
        try {
106
            $namedParameter->getParameters($callable, []);
107
            // @codeCoverageIgnoreStart
108
        } catch (ParameterException) {
109
            return; // It is OK to ignore exceptions. The objective is to obtain meta-information.
110
111
            // @codeCoverageIgnoreEnd
112
        }
113
    }
114
115
    /** @param list<string> $extraContexts */
116
    public function compileExtraContexts(array $extraContexts, AbstractAppMeta $meta): void
117
    {
118
        $cache = (new LocalCacheProvider())->get();
119
        foreach ($extraContexts as $context) {
120
            $contextualMeta = new Meta($meta->name, $context, $meta->appDir);
121
            PackageInjector::getInstance($contextualMeta, $context, $cache)->getInstance(AppInterface::class);
122
        }
123
    }
124
}
125