Completed
Push — 1.x ( c86fd3...4b8bd5 )
by Akihito
14s queued 12s
created

Compiler::compile()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 23
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 14.0742

Importance

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