Compiler   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Importance

Changes 27
Bugs 0 Features 1
Metric Value
wmc 14
eloc 77
c 27
b 0
f 1
dl 0
loc 147
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A dumpAutoload() 0 3 1
A unregisterComposerLoader() 0 10 2
A hookNullObjectClass() 0 12 2
A registerLoader() 0 27 5
A compile() 0 27 1
A compileClassMetaInfo() 0 16 2
A __construct() 0 17 1
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 RuntimeException;
19
20
use function assert;
21
use function file_exists;
22
use function is_int;
23
use function memory_get_peak_usage;
24
use function microtime;
25
use function number_format;
26
use function printf;
27
use function realpath;
28
use function spl_autoload_functions;
29
use function spl_autoload_register;
30
use function spl_autoload_unregister;
31
use function strpos;
32
33
use const PHP_EOL;
34
35
/**
36
 * @psalm-import-type AppName from Types
37
 * @psalm-import-type Context from Types
38
 * @psalm-import-type AppDir from Types
39
 * @psalm-import-type ClassList from Types
40
 * @psalm-import-type OverwrittenFiles from Types
41
 */
42
43
final class Compiler
44
{
45
    /** @var ArrayObject<int, string> */
46
    private ArrayObject $classes;
47
    private Meta $appMeta;
48
    private CompileAutoload $dumpAutoload;
49
    private CompilePreload $compilePreload;
50
    private CompileObjectGraph $compilerObjectGraph;
51
52
    /**
53
     * @param AppName $appName application name "MyVendor|MyProject"
0 ignored issues
show
Bug introduced by
The type BEAR\Package\AppName 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...
54
     * @param Context $context application context "prod-app"
55
     * @param AppDir  $appDir  application path
0 ignored issues
show
Bug introduced by
The type BEAR\Package\AppDir 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...
56
     *
57
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
58
     */
59
    public function __construct(string $appName, private string $context, string $appDir, bool $prepend = true)
60
    {
61
        /** @var ArrayObject<int, string> $classes */
62
        $classes = new ArrayObject();
63
        $this->classes = $classes;
64
        $this->registerLoader($appDir, $prepend);
65
        $this->hookNullObjectClass($appDir);
66
        $this->appMeta = new Meta($appName, $context, $appDir);
67
        /** @psalm-suppress MixedAssignment (?) */
68
        $injector = Injector::getInstance($appName, $context, $appDir);
69
        /** @var ArrayObject<int, string> $overWritten */
70
        $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->dumpAutoload, $filePutContents, $classes, $context);
75
        $this->compilerObjectGraph = new CompileObjectGraph($filePutContents, $this->appMeta->logDir);
76
    }
77
78
    /**
79
     * Compile application
80
     *
81
     * @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...
82
     */
83
    public function compile(): int
84
    {
85
        $preload = ($this->compilePreload)($this->appMeta, $this->context);
86
        $module = (new Module())($this->appMeta, $this->context);
87
        $compiler = new \Ray\Compiler\Compiler();
88
        $appDirRealpath = realpath($this->appMeta->appDir);
89
        assert($appDirRealpath !== false);
90
        $scriptDir = $appDirRealpath . '/var/di/' . $this->context;
91
        $compiler->compile($module, $scriptDir);
92
93
        // Compile class meta info (annotations and named parameters)
94
        $compiled = $this->compileClassMetaInfo();
95
96
        echo PHP_EOL;
97
        $dot = ($this->compilerObjectGraph)($module);
98
        $start = $_SERVER['REQUEST_TIME_FLOAT'] ?? 0.0;
99
        $time = number_format(microtime(true) - $start, 2);
100
        $memory = number_format(memory_get_peak_usage() / (1024 * 1024), 3);
101
        echo PHP_EOL;
102
        printf("Compilation took %f seconds and used %fMB of memory\n", $time, $memory);
103
        printf("Compiled: %d resource classes\n", $compiled);
104
        printf("Preload compile: %s\n", $this->dumpAutoload->getFileInfo($preload));
105
        $dotRealpath = realpath($dot);
106
        assert($dotRealpath !== false);
107
        printf("Object graph diagram: %s\n", $dotRealpath);
108
109
        return 0;
110
    }
111
112
    public function dumpAutoload(): int
113
    {
114
        return ($this->dumpAutoload)();
115
    }
116
117
    private function compileClassMetaInfo(): int
118
    {
119
        $injector = Injector::getInstance($this->appMeta->name, $this->context, $this->appMeta->appDir);
120
        $namedParams = $injector->getInstance(NamedParameterInterface::class);
121
        assert($namedParams instanceof NamedParameterInterface);
122
123
        $compileClassMetaInfo = new CompileClassMetaInfo();
124
        $resources = $this->appMeta->getResourceListGenerator();
125
        $count = 0;
126
        foreach ($resources as $resource) {
127
            [$className] = $resource;
128
            $compileClassMetaInfo($namedParams, $className);
129
            $count++;
130
        }
131
132
        return $count;
133
    }
134
135
    /** @SuppressWarnings(PHPMD.BooleanArgumentFlag) */
136
    private function registerLoader(string $appDir, bool $prepend = true): void
137
    {
138
        $this->unregisterComposerLoader();
139
        $loaderFile = $appDir . '/vendor/autoload.php';
140
        if (! file_exists($loaderFile)) {
141
            throw new RuntimeException('no loader');
142
        }
143
144
        $loader = require $loaderFile;
145
        assert($loader instanceof ClassLoader);
146
        spl_autoload_register(
147
            /** @ class-string $class */
148
            function (string $class) use ($loader): void {
149
                $loader->loadClass($class);
150
                if (
151
                    $class === NullPage::class
152
                    || 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...
153
                    || is_int(strpos($class, NullPage::class))
154
                ) {
155
                    return;
156
                }
157
158
                /** @psalm-suppress NullArgument */
159
                $this->classes[] = $class;
160
            },
161
            true,
162
            $prepend,
163
        );
164
    }
165
166
    private function hookNullObjectClass(string $appDir): void
167
    {
168
        $appDirRealpath = realpath($appDir);
169
        assert($appDirRealpath !== false);
170
        $compileScript = $appDirRealpath . '/.compile.php';
171
        if (! file_exists($compileScript)) {
172
            // @codeCoverageIgnoreStart
173
            return;
174
            // @codeCoverageIgnoreEnd
175
        }
176
177
        require $compileScript;
178
    }
179
180
    private function unregisterComposerLoader(): void
181
    {
182
        $autoload = spl_autoload_functions();
183
        if (! isset($autoload[0])) {
184
            // @codeCoverageIgnoreStart
185
            return;
186
            // @codeCoverageIgnoreEnd
187
        }
188
189
        spl_autoload_unregister($autoload[0]);
190
    }
191
}
192