Completed
Push — 1.x ( 49d1e1...5b3d82 )
by Akihito
15s
created

Compiler::compileClassMetaInfo()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 4.916

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 13
c 2
b 0
f 0
dl 0
loc 18
ccs 1
cts 10
cp 0.1
rs 9.8333
cc 2
nc 2
nop 0
crap 4.916
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Package;
6
7
use ArrayObject;
8
use BEAR\AppMeta\Meta;
9
use BEAR\Package\Compiler\CompileAutoload;
10
use BEAR\Package\Compiler\CompileClassMetaInfo;
11
use BEAR\Package\Compiler\CompileObjectGraph;
12
use BEAR\Package\Compiler\CompilePreload;
13
use BEAR\Package\Compiler\FakeRun;
14
use BEAR\Package\Compiler\FilePutContents;
15
use BEAR\Package\Provide\Error\NullPage;
16
use BEAR\Resource\NamedParameterInterface;
17
use Composer\Autoload\ClassLoader;
18
use Doctrine\Common\Annotations\Reader;
19
use RuntimeException;
20
21
use function assert;
22
use function file_exists;
23
use function is_int;
24
use function memory_get_peak_usage;
25
use function microtime;
26
use function number_format;
27
use function printf;
28
use function realpath;
29
use function spl_autoload_functions;
30
use function spl_autoload_register;
31
use function spl_autoload_unregister;
32
use function strpos;
33 1
34
use const PHP_EOL;
35 1
36 1
final class Compiler
37
{
38 1
    /** @var ArrayObject<int, string> */
39
    private ArrayObject $classes;
40
    private Meta $appMeta;
41 1
    private CompileAutoload $dumpAutoload;
42
    private CompilePreload $compilePreload;
43 1
    private CompileObjectGraph $compilerObjectGraph;
44 1
45 1
    /**
46 1
     * @param string $appName application name "MyVendor|MyProject"
47 1
     * @param string $context application context "prod-app"
48 1
     * @param string $appDir  application path
49
     *
50 1
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
51
     */
52
    public function __construct(string $appName, private string $context, string $appDir, bool $prepend = true)
53
    {
54 1
        /** @var ArrayObject<int, string> $classes */
55
        $classes = new ArrayObject();
56
        $this->classes = $classes;
57 1
        $this->registerLoader($appDir, $prepend);
58 1
        $this->hookNullObjectClass($appDir);
59
        $this->appMeta = new Meta($appName, $context, $appDir);
60 1
        /** @psalm-suppress MixedAssignment (?) */
61 1
        $injector = Injector::getInstance($appName, $context, $appDir);
62
        /** @var ArrayObject<int, string> $overWritten */
63 1
        $overWritten = new ArrayObject();
64
        $filePutContents = new FilePutContents($overWritten);
65
        $fakeRun = new FakeRun($injector, $context, $this->appMeta);
66 1
        $this->dumpAutoload = new CompileAutoload($fakeRun, $filePutContents, $this->appMeta, $overWritten, $this->classes, $appDir, $context);
67
        $this->compilePreload = new CompilePreload($fakeRun, $this->dumpAutoload, $filePutContents, $classes, $context);
68 1
        $this->compilerObjectGraph = new CompileObjectGraph($filePutContents, $this->appMeta->logDir);
69 1
    }
70 1
71
    /**
72
     * Compile application
73
     *
74
     * @return 0|1 exit code
0 ignored issues
show
Documentation Bug introduced by
The doc comment 0|1 at position 0 could not be parsed: Unknown type name '0' at position 0 in 0|1.
Loading history...
75
     */
76
    public function compile(): int
77
    {
78
        $preload = ($this->compilePreload)($this->appMeta, $this->context);
79
        $module = (new Module())($this->appMeta, $this->context);
80
        $compiler = new \Ray\Compiler\Compiler();
81
        $scriptDir = realpath($this->appMeta->appDir) . '/var/di/' . $this->context;
82
        $compiler->compile($module, $scriptDir);
83
84
        // Compile class meta info (annotations and named parameters)
85
        $compiled = $this->compileClassMetaInfo();
86
87
        echo PHP_EOL;
88
        $dot = ($this->compilerObjectGraph)($module);
89
        $start = $_SERVER['REQUEST_TIME_FLOAT'] ?? 0;
90
        $time = number_format(microtime(true) - $start, 2);
91
        $memory = number_format(memory_get_peak_usage() / (1024 * 1024), 3);
92
        echo PHP_EOL;
93
        printf("Compilation took %f seconds and used %fMB of memory\n", $time, $memory);
94
        printf("Compiled: %d resource classes\n", $compiled);
95
        printf("Preload compile: %s\n", $this->dumpAutoload->getFileInfo($preload));
96
        printf("Object graph diagram: %s\n", realpath($dot));
97
98
        return 0;
99
    }
100
101
    public function dumpAutoload(): int
102
    {
103
        return ($this->dumpAutoload)();
104
    }
105
106
    private function compileClassMetaInfo(): int
107
    {
108
        $injector = Injector::getInstance($this->appMeta->name, $this->context, $this->appMeta->appDir);
109
        $reader = $injector->getInstance(Reader::class);
110
        assert($reader instanceof Reader);
111
        $namedParams = $injector->getInstance(NamedParameterInterface::class);
112
        assert($namedParams instanceof NamedParameterInterface);
113
114
        $compileClassMetaInfo = new CompileClassMetaInfo();
115
        $resources = $this->appMeta->getResourceListGenerator();
116
        $count = 0;
117
        foreach ($resources as $resource) {
118
            [$className] = $resource;
119
            $compileClassMetaInfo($reader, $namedParams, $className);
120
            $count++;
121 1
        }
122
123
        return $count;
124 1
    }
125
126
    /** @SuppressWarnings(PHPMD.BooleanArgumentFlag) */
127
    private function registerLoader(string $appDir, bool $prepend = true): void
128
    {
129
        $this->unregisterComposerLoader();
130 1
        $loaderFile = $appDir . '/vendor/autoload.php';
131 1
        if (! file_exists($loaderFile)) {
132 1
            throw new RuntimeException('no loader');
133 1
        }
134 1
135 1
        $loader = require $loaderFile;
136 1
        assert($loader instanceof ClassLoader);
137
        spl_autoload_register(
138 1
            /** @ class-string $class */
139
            function (string $class) use ($loader): void {
140 1
                $loader->loadClass($class);
141
                if (
142 1
                    $class === NullPage::class
143
                    || is_int(strpos($class, Compiler::class))
0 ignored issues
show
introduced by
The condition is_int(strpos($class, BE...ckage\Compiler::class)) is always true.
Loading history...
144 1
                    || is_int(strpos($class, NullPage::class))
145
                ) {
146 1
                    return;
147
                }
148
149 1
                /** @psalm-suppress NullArgument */
150
                $this->classes[] = $class;
151
            },
152 1
            true,
153 1
            $prepend,
154
        );
155
    }
156 1
157 1
    private function hookNullObjectClass(string $appDir): void
158 1
    {
159
        $compileScript = realpath($appDir) . '/.compile.php';
160 1
        if (! file_exists($compileScript)) {
161
            // @codeCoverageIgnoreStart
162 1
            return;
163
            // @codeCoverageIgnoreEnd
164 1
        }
165
166 1
        require $compileScript;
167 1
    }
168 1
169
    private function unregisterComposerLoader(): void
170 1
    {
171 1
        $autoload = spl_autoload_functions();
172
        if (! isset($autoload[0])) {
173
            // @codeCoverageIgnoreStart
174
            return;
175
            // @codeCoverageIgnoreEnd
176
        }
177
178
        spl_autoload_unregister($autoload[0]);
179
    }
180
}
181