Compiler::hookNullObjectClass()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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