Issues (36)

src/Compiler/CompileAutoload.php (1 issue)

Labels
Severity
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 ReflectionClass;
10
11
use function assert;
12
use function class_exists;
13
use function file_exists;
14
use function in_array;
15
use function interface_exists;
16
use function is_float;
17
use function is_int;
18
use function memory_get_peak_usage;
19
use function microtime;
20
use function number_format;
21
use function preg_quote;
22
use function preg_replace;
23
use function printf;
24
use function realpath;
25
use function sprintf;
26
use function str_contains;
27
use function strpos;
28
use function trait_exists;
29
30
final class CompileAutoload
31
{
32
    /**
33
     * @param ArrayObject<int, string> $overwritten
34
     * @param ArrayObject<int, string> $classes
35
     */
36
    public function __construct(
37
        private FakeRun $fakeRun,
38
        private FilePutContents $filePutContents,
39
        private Meta $appMeta,
40
        private ArrayObject $overwritten,
41
        private ArrayObject $classes,
42
        private string $appDir,
43
        private string $context,
44
    ) {
45
    }
46
47
    public function getFileInfo(string $filename): string
48
    {
49
        if (in_array($filename, (array) $this->overwritten, true)) {
50
            return $filename . ' (overwritten)';
51
        }
52
53
        return $filename;
54
    }
55
56
    public function __invoke(): int
57
    {
58
        ($this->fakeRun)();
59
        /** @var list<string> $classes */
60
        $classes = (array) $this->classes;
61
        $paths = $this->getPaths($classes);
0 ignored issues
show
$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

61
        $paths = $this->getPaths(/** @scrutinizer ignore-type */ $classes);
Loading history...
62
        $autoload = $this->saveAutoloadFile($this->appMeta->appDir, $paths);
63
        $start = $_SERVER['REQUEST_TIME_FLOAT'] ?? 0;
64
        assert(is_float($start));
65
        $time = number_format(microtime(true) - $start, 2);
66
        $memory = number_format(memory_get_peak_usage() / (1024 * 1024), 3);
67
        printf("Compilation (2/2) took %f seconds and used %fMB of memory\n", $time, $memory);
68
        printf("autoload.php: %s\n", $this->getFileInfo($autoload));
69
70
        return 0;
71
    }
72
73
    /**
74
     * @param array<string> $classes
75
     *
76
     * @return array<string>
77
     */
78
    public function getPaths(array $classes): array
79
    {
80
        $paths = [];
81
        foreach ($classes as $class) {
82
            // could be phpdoc tag by annotation loader
83
            if ($this->isNotAutoloadble($class)) {
84
                continue;
85
            }
86
87
            /** @var class-string $class */
88
            $filePath = (string) (new ReflectionClass($class))->getFileName();
89
            if (! $this->isNotCompileFile($filePath)) {
90
                continue; // @codeCoverageIgnore
91
            }
92
93
            $paths[] = $this->getRelativePath($this->appDir, $filePath);
94
        }
95
96
        return $paths;
97
    }
98
99
    /** @param array<string> $paths */
100
    public function saveAutoloadFile(string $appDir, array $paths): string
101
    {
102
        $requiredFile = '';
103
        foreach ($paths as $path) {
104
            $requiredFile .= sprintf(
105
                "require %s;\n",
106
                $path,
107
            );
108
        }
109
110
        $autoloadFile = sprintf("<?php
111
112
// %s autoload
113
114
%s
115
require __DIR__ . '/vendor/autoload.php';
116
", $this->context, $requiredFile);
117
        $fileName = realpath($appDir) . '/autoload.php';
118
119
        ($this->filePutContents)($fileName, $autoloadFile);
120
121
        return $fileName;
122
    }
123
124
    private function isNotAutoloadble(string $class): bool
125
    {
126
        return ! class_exists($class, false) && ! interface_exists($class, false) && ! trait_exists($class, false);
127
    }
128
129
    private function isNotCompileFile(string $filePath): bool
130
    {
131
        return file_exists($filePath) || is_int(strpos($filePath, 'phar'));
132
    }
133
134
    private function getRelativePath(string $rootDir, string $file): string
135
    {
136
        $dir = (string) realpath($rootDir);
137
        if (str_contains($file, $dir)) {
138
            return (string) preg_replace('#^' . preg_quote($dir, '#') . '#', "__DIR__ . '", $file) . "'";
139
        }
140
141
        return sprintf("'%s'", $file);
142
    }
143
}
144