1 | <?php |
||
22 | final class Compiler |
||
23 | { |
||
24 | /** |
||
25 | * @var string[] |
||
26 | */ |
||
27 | private $classes = []; |
||
28 | |||
29 | /** |
||
30 | * @var string |
||
31 | */ |
||
32 | private $ns; |
||
33 | |||
34 | /** |
||
35 | * Compile application |
||
36 | * |
||
37 | * @param string $appName application name "MyVendor|MyProject" |
||
38 | * @param string $context application context "prod-app" |
||
39 | * @param string $appDir application path |
||
40 | */ |
||
41 | public function __invoke(string $appName, string $context, string $appDir) : string |
||
42 | { |
||
43 | $this->ns = (string) filemtime(realpath($appDir) . '/src'); |
||
44 | $this->registerLoader($appDir); |
||
45 | $autoload = $this->compileAutoload($appName, $context, $appDir); |
||
46 | $preload = $this->compilePreload($appName, $context, $appDir); |
||
47 | $log = $this->compileDiScripts($appName, $context, $appDir); |
||
48 | |||
49 | return sprintf("Compile Log: %s\nautoload.php: %s\npreload.php: %s", $log, $autoload, $preload); |
||
50 | } |
||
51 | |||
52 | public function registerLoader(string $appDir) : void |
||
53 | { |
||
54 | $loaderFile = $appDir . '/vendor/autoload.php'; |
||
55 | if (! file_exists($loaderFile)) { |
||
56 | throw new \RuntimeException('no loader'); |
||
57 | } |
||
58 | $loaderFile = require $loaderFile; |
||
59 | spl_autoload_register( |
||
60 | function ($class) use ($loaderFile) : void { |
||
61 | $loaderFile->loadClass($class); |
||
62 | if ($class !== NullPage::class) { |
||
63 | $this->classes[] = $class; |
||
64 | } |
||
65 | }, |
||
66 | false, |
||
67 | true |
||
68 | ); |
||
69 | } |
||
70 | |||
71 | public function compileDiScripts(string $appName, string $context, string $appDir) : string |
||
72 | { |
||
73 | $appMeta = new Meta($appName, $context, $appDir); |
||
74 | $injector = new AppInjector($appName, $context, $appMeta, $this->ns); |
||
75 | $cache = $injector->getInstance(Cache::class); |
||
76 | $reader = $injector->getInstance(AnnotationReader::class); |
||
77 | /* @var $reader \Doctrine\Common\Annotations\Reader */ |
||
78 | $namedParams = $injector->getInstance(NamedParameterInterface::class); |
||
79 | /* @var $namedParams NamedParameterInterface */ |
||
80 | |||
81 | // create DI factory class and AOP compiled class for all resources and save $app cache. |
||
82 | (new Bootstrap)->newApp($appMeta, $context, $cache); |
||
83 | |||
84 | // check resource injection and create annotation cache |
||
85 | foreach ($appMeta->getResourceListGenerator() as [$className]) { |
||
86 | $this->scanClass($injector, $reader, $namedParams, (string) $className); |
||
|
|||
87 | } |
||
88 | $logFile = realpath($appMeta->logDir) . '/compile.log'; |
||
89 | $this->saveCompileLog($appMeta, $context, $logFile); |
||
90 | |||
91 | return $logFile; |
||
92 | } |
||
93 | |||
94 | private function compileAutoload(string $appName, string $context, string $appDir) : string |
||
95 | { |
||
96 | $this->invokeTypicalRequest($appName, $context); |
||
97 | $paths = $this->getPaths($this->classes, $appDir); |
||
98 | |||
99 | return $this->dumpAutoload($appDir, $paths); |
||
100 | } |
||
101 | |||
102 | private function dumpAutoload(string $appDir, array $paths) : string |
||
103 | { |
||
104 | $autoloadFile = '<?php' . PHP_EOL; |
||
105 | foreach ($paths as $path) { |
||
106 | $autoloadFile .= sprintf( |
||
107 | "require %s';\n", |
||
108 | $this->getRelativePath($appDir, $path) |
||
109 | ); |
||
110 | } |
||
111 | $autoloadFile .= "require __DIR__ . '/vendor/autoload.php';" . PHP_EOL; |
||
112 | $loaderFile = realpath($appDir) . '/autoload.php'; |
||
113 | file_put_contents($loaderFile, $autoloadFile); |
||
114 | |||
115 | return $loaderFile; |
||
116 | } |
||
117 | |||
118 | private function compilePreload(string $appName, string $context, string $appDir) : string |
||
119 | { |
||
120 | $this->loadResources($appName, $context, $appDir); |
||
121 | $paths = $this->getPaths($this->classes, $appDir); |
||
122 | $output = '<?php' . PHP_EOL; |
||
123 | $output .= "require __DIR__ . '/vendor/autoload.php';" . PHP_EOL; |
||
124 | foreach ($paths as $path) { |
||
125 | $output .= sprintf( |
||
126 | "require %s';\n", |
||
127 | $this->getRelativePath($appDir, $path) |
||
128 | ); |
||
129 | } |
||
130 | $preloadFile = realpath($appDir) . '/preload.php'; |
||
131 | file_put_contents($preloadFile, $output); |
||
132 | |||
133 | return $preloadFile; |
||
134 | } |
||
135 | |||
136 | private function getRelativePath(string $rootDir, string $file) : string |
||
137 | { |
||
138 | $dir = (string) realpath($rootDir); |
||
139 | if (strpos($file, $dir) !== false) { |
||
140 | return (string) preg_replace('#^' . preg_quote($dir, '#') . '#', "__DIR__ . '", $file); |
||
141 | } |
||
142 | |||
143 | return $file; |
||
144 | } |
||
145 | |||
146 | private function invokeTypicalRequest(string $appName, string $context) : void |
||
147 | { |
||
148 | $app = (new Bootstrap)->getApp($appName, $context); |
||
149 | $ro = new NullPage; |
||
150 | $ro->uri = new Uri('app://self/'); |
||
151 | $app->resource->get->object($ro)(); |
||
152 | } |
||
153 | |||
154 | private function scanClass(InjectorInterface $injector, Reader $reader, NamedParameterInterface $namedParams, string $className) : void |
||
177 | |||
178 | private function isMagicMethod(string $method) : bool |
||
182 | |||
183 | private function saveNamedParam(NamedParameterInterface $namedParameter, object $instance, string $method) : void |
||
199 | |||
200 | private function saveCompileLog(AbstractAppMeta $appMeta, string $context, string $logFile) : void |
||
210 | |||
211 | private function getPaths(array $classes, string $appDir) : array |
||
229 | |||
230 | private function loadResources(string $appName, string $context, string $appDir) : void |
||
231 | { |
||
232 | $meta = new Meta($appName, $context, $appDir); |
||
233 | $injector = new AppInjector($appName, $context, $meta, $this->ns); |
||
234 | foreach ($meta->getGenerator('*') as $resMeta) { |
||
235 | $injector->getInstance($resMeta->class); |
||
236 | } |
||
237 | } |
||
238 | } |
||
239 |
This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.