CompileAutoload::__invoke()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 15
rs 9.9
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Package\Compiler;
6
7
use ArrayObject;
8
use BEAR\AppMeta\Meta;
9
use BEAR\Package\Types;
10
use ReflectionClass;
11
12
use function assert;
13
use function class_exists;
14
use function file_exists;
15
use function in_array;
16
use function interface_exists;
17
use function is_float;
18
use function is_int;
19
use function memory_get_peak_usage;
20
use function microtime;
21
use function number_format;
22
use function preg_quote;
23
use function preg_replace;
24
use function printf;
25
use function realpath;
26
use function sprintf;
27
use function str_contains;
28
use function strpos;
29
use function trait_exists;
30
31
/**
32
 * @psalm-import-type ClassList from Types
33
 * @psalm-import-type OverwrittenFiles from Types
34
 * @psalm-import-type ClassPaths from Types
35
 * @psalm-import-type AppDir from Types
36
 * @psalm-import-type Context from Types
37
 */
38
final class CompileAutoload
39
{
40
    /**
41
     * @param OverwrittenFiles $overwritten
0 ignored issues
show
Bug introduced by
The type BEAR\Package\Compiler\OverwrittenFiles 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
     * @param ClassList        $classes
0 ignored issues
show
Bug introduced by
The type BEAR\Package\Compiler\ClassList 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...
43
     * @param AppDir           $appDir
0 ignored issues
show
Bug introduced by
The type BEAR\Package\Compiler\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...
44
     * @param Context          $context
0 ignored issues
show
Bug introduced by
The type BEAR\Package\Compiler\Context 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...
45
     */
46
    public function __construct(
47
        private FakeRun $fakeRun,
48
        private FilePutContents $filePutContents,
49
        private Meta $appMeta,
50
        private ArrayObject $overwritten,
51
        private ArrayObject $classes,
52
        private string $appDir,
53
        private string $context,
54
    ) {
55
    }
56
57
    public function getFileInfo(string $filename): string
58
    {
59
        if (in_array($filename, (array) $this->overwritten, true)) {
60
            return $filename . ' (overwritten)';
61
        }
62
63
        return $filename;
64
    }
65
66
    public function __invoke(): int
67
    {
68
        ($this->fakeRun)();
69
        /** @var list<string> $classes */
70
        $classes = (array) $this->classes;
71
        $paths = $this->getPaths($classes);
0 ignored issues
show
Bug introduced by
$classes of type BEAR\Package\Compiler\list is incompatible with the type array expected by parameter $classes of BEAR\Package\Compiler\CompileAutoload::getPaths(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

71
        $paths = $this->getPaths(/** @scrutinizer ignore-type */ $classes);
Loading history...
72
        $autoload = $this->saveAutoloadFile($this->appMeta->appDir, $paths);
73
        $start = $_SERVER['REQUEST_TIME_FLOAT'] ?? 0;
74
        assert(is_float($start));
75
        $time = number_format(microtime(true) - $start, 2);
76
        $memory = number_format(memory_get_peak_usage() / (1024 * 1024), 3);
77
        printf("Compilation (2/2) took %f seconds and used %fMB of memory\n", $time, $memory);
78
        printf("autoload.php: %s\n", $this->getFileInfo($autoload));
79
80
        return 0;
81
    }
82
83
    /**
84
     * @param list<string> $classes
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...
85
     *
86
     * @return ClassPaths
0 ignored issues
show
Bug introduced by
The type BEAR\Package\Compiler\ClassPaths 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...
87
     */
88
    public function getPaths(array $classes): array
89
    {
90
        $paths = [];
91
        foreach ($classes as $class) {
92
            // could be phpdoc tag by annotation loader
93
            if ($this->isNotAutoloadble($class)) {
94
                continue;
95
            }
96
97
            /** @var class-string $class */
98
            $filePath = (string) (new ReflectionClass($class))->getFileName();
99
            if (! $this->isNotCompileFile($filePath)) {
100
                continue; // @codeCoverageIgnore
101
            }
102
103
            $paths[] = $this->getRelativePath($this->appDir, $filePath);
104
        }
105
106
        return $paths;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $paths returns the type array|string[] which is incompatible with the documented return type BEAR\Package\Compiler\ClassPaths.
Loading history...
107
    }
108
109
    /**
110
     * @param AppDir     $appDir
111
     * @param ClassPaths $paths
112
     */
113
    public function saveAutoloadFile(string $appDir, array $paths): string
114
    {
115
        $requiredFile = '';
116
        foreach ($paths as $path) {
117
            $requiredFile .= sprintf(
118
                "require %s;\n",
119
                $path,
120
            );
121
        }
122
123
        $autoloadFile = sprintf("<?php
124
125
// %s autoload
126
127
%s
128
require __DIR__ . '/vendor/autoload.php';
129
", $this->context, $requiredFile);
130
        $appDirRealpath = realpath($appDir);
131
        assert($appDirRealpath !== false);
132
        $fileName = $appDirRealpath . '/autoload.php';
133
134
        ($this->filePutContents)($fileName, $autoloadFile);
135
136
        return $fileName;
137
    }
138
139
    private function isNotAutoloadble(string $class): bool
140
    {
141
        return ! class_exists($class, false) && ! interface_exists($class, false) && ! trait_exists($class, false);
142
    }
143
144
    private function isNotCompileFile(string $filePath): bool
145
    {
146
        return file_exists($filePath) || is_int(strpos($filePath, 'phar'));
147
    }
148
149
    private function getRelativePath(string $rootDir, string $file): string
150
    {
151
        $dir = (string) realpath($rootDir);
152
        if (str_contains($file, $dir)) {
153
            return (string) preg_replace('#^' . preg_quote($dir, '#') . '#', "__DIR__ . '", $file) . "'";
154
        }
155
156
        return sprintf("'%s'", $file);
157
    }
158
}
159