Completed
Push — compiler ( eb0a0d...d0e3d2 )
by Akihito
01:35
created

CompileAutoload::saveAutoloadFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.552
c 0
b 0
f 0
cc 2
nc 2
nop 2
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 strpos;
27
use function trait_exists;
28
29
class CompileAutoload
30
{
31
    /** @var string */
32
    private $appDir;
33
34
    /** @var string */
35
    private $context;
36
37
    /** @var Meta */
38
    private $appMeta;
39
40
    /** @var ArrayObject<int, string> */
41
    private $overwritten;
42
43
    /** @var ArrayObject<int, string> */
44
    private $classes;
45
46
    /** @var FilePutContents */
47
    private $filePutContents;
48
49
    /** @var FakeRun */
50
    private $fakeRun;
51
52
    /**
53
     * @param ArrayObject<int, string> $overwritten
54
     * @param ArrayObject<int, string> $classes
55
     */
56
    public function __construct(
57
        FakeRun $fakeRun,
58
        FilePutContents $filePutContents,
59
        Meta $appMeta,
60
        ArrayObject $overwritten,
61
        ArrayObject $classes,
62
        string $appDir,
63
        string $context
64
    ) {
65
        $this->appDir = $appDir;
66
        $this->context = $context;
67
        $this->appMeta = $appMeta;
68
        $this->overwritten = $overwritten;
69
        $this->classes = $classes;
70
        $this->filePutContents = $filePutContents;
71
        $this->fakeRun = $fakeRun;
72
    }
73
74
    public function getFileInfo(string $filename): string
75
    {
76
        if (in_array($filename, (array) $this->overwritten, true)) {
77
            return $filename . ' (overwritten)';
78
        }
79
80
        return $filename;
81
    }
82
83
    public function __invoke(): int
84
    {
85
        ($this->fakeRun)();
86
        /** @var list<string> $classes */
0 ignored issues
show
Documentation introduced by
The doc-type list<string> could not be parsed: Expected "|" or "end of type", but got "<" at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
87
        $classes = (array) $this->classes;
88
        $paths = $this->getPaths($classes);
89
        $autolaod = $this->saveAutoloadFile($this->appMeta->appDir, $paths);
90
        $start = $_SERVER['REQUEST_TIME_FLOAT'];
91
        assert(is_float($start));
92
        $time = number_format(microtime(true) - $start, 2);
93
        $memory = number_format(memory_get_peak_usage() / (1024 * 1024), 3);
94
        printf("Compilation (2/2) took %f seconds and used %fMB of memory\n", $time, $memory);
95
        printf("autoload.php: %s\n", $this->getFileInfo($autolaod));
96
97
        return 0;
98
    }
99
100
    /**
101
     * @param array<string> $classes
102
     *
103
     * @return array<string>
104
     */
105
    public function getPaths(array $classes): array
106
    {
107
        $paths = [];
108
        foreach ($classes as $class) {
109
            // could be phpdoc tag by annotation loader
110
            if ($this->isNotAutoloadble($class)) {
111
                continue;
112
            }
113
114
            /** @var class-string $class */
0 ignored issues
show
Documentation introduced by
The doc-type class-string could not be parsed: Unknown type name "class-string" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
115
            $filePath = (string) (new ReflectionClass($class))->getFileName();
116
            if (! $this->isNotCompileFile($filePath)) {
117
                continue; // @codeCoverageIgnore
118
            }
119
120
            $paths[] = $this->getRelativePath($this->appDir, $filePath);
121
        }
122
123
        return $paths;
124
    }
125
126
    /**
127
     * @param array<string> $paths
128
     */
129
    public function saveAutoloadFile(string $appDir, array $paths): string
130
    {
131
        $requiredFile = '';
132
        foreach ($paths as $path) {
133
            $requiredFile .= sprintf(
134
                "require %s;\n",
135
                $path
136
            );
137
        }
138
139
        $autoloadFile = sprintf("<?php
140
141
// %s autoload
142
143
%s
144
require __DIR__ . '/vendor/autoload.php';
145
", $this->context, $requiredFile);
146
        $fileName = realpath($appDir) . '/autoload.php';
147
148
        ($this->filePutContents)($fileName, $autoloadFile);
149
150
        return $fileName;
151
    }
152
153
    private function isNotAutoloadble(string $class): bool
154
    {
155
        return ! class_exists($class, false) && ! interface_exists($class, false) && ! trait_exists($class, false);
156
    }
157
158
    private function isNotCompileFile(string $filePath): bool
159
    {
160
        return file_exists($filePath) || is_int(strpos($filePath, 'phar'));
161
    }
162
163
    private function getRelativePath(string $rootDir, string $file): string
164
    {
165
        $dir = (string) realpath($rootDir);
166
        if (strpos($file, $dir) !== false) {
167
            return (string) preg_replace('#^' . preg_quote($dir, '#') . '#', "__DIR__ . '", $file) . "'";
168
        }
169
170
        return sprintf("'%s'", $file);
171
    }
172
}
173