loadClass($className)) { throw new Exception(sprintf('Class %s couldn\'t be loaded!', $className)); } } ); } public static function BuildCache(): void { self::BuildClassCache(); self::BuildRouteCache(); } public static function BuildClassCache(): void { $classesResult = self::scanForClasses(); $cacheContent = ''; foreach ($classesResult as $className => $path) { $cacheContent .= sprintf("\t\t'%s' => '%s',\n", $className, $path); } $cacheContent .= "\t]\n);"; self::buildCacheFile($cacheContent, 'classes'); } private function loadClass(string $className): bool { if (!isset(CLASSES[$className]) || !@include(CLASSES[$className])) { return false; } return true; } public static function BuildRouteCache(): void { $controllersResult = self::scanForControllers(); $controllerMethods = [ 'GET' => [], 'POST' => [], 'PUT' => [], 'DELETE' => [], ]; foreach ($controllersResult as $className => $path) { $file = fopen($path, 'r'); $content = fread($file, filesize($path)); fclose($file); preg_match_all('/(?<=private )\w+ \$\w+(?=;)/', $content, $matches); $params = []; foreach ($matches[0] as $match) { $parts = explode(' ', $match); $params[] = [ 'type' => $parts[0], 'name' => $parts[1], ]; } preg_match('/(?<=protected string \$route = \').*(?=\';)/', $content, $matches); $route = $matches[0]; preg_match('/[A-Z][a-z]+(?=Controller)/', $className, $matches); $method = strtoupper($matches[0]); $controllerMethods[$method][$route] = [ 'name' => $className, 'params' => $params, ]; } $cacheContent = ''; foreach ($controllerMethods as $method => $controllers) { $cacheContent .= self::createRoutesForMethod($method, $controllers); } $cacheContent .= "\t]\n);"; self::buildCacheFile($cacheContent, 'routes'); } private static function createRoutesForMethod(string $method, array $routes): string { krsort($routes); $stringRoutes = ''; foreach ($routes as $route => $params) { $stringRoutes .= sprintf( "'%s' => [ 'controller' => %s::class, 'params' => [ %s ], ], ", $route, $params['name'], self::createRouteParams($params['params']) ); } return sprintf( " '%s' => [ %s ], ", $method, $stringRoutes, ); } private static function createRouteParams(array $params): string { $string = ''; foreach ($params as $param) { $string .= sprintf( " '%s' => [ 'type' => '%s', ], ", str_replace('$', '', $param['name']), $param['type'] ); } return $string; } private static function reformatCacheFileContent(string $content): string { $depth = 0; $reformatted = ''; $replace = ''; // Removing indents foreach (explode("\n", $content) as $line) { $trim = trim($line); if ($trim !== '') { $replace .= $trim . "\n"; } } for ($i = 0; $i < strlen($replace); $i++) { if (in_array($replace[$i], [')', ']'])) { $depth--; } if ($replace[$i - 1] === "\n") { $reformatted .= str_repeat("\t", $depth); } $reformatted .= $replace[$i]; if (in_array($replace[$i], ['(', '['])) { $depth++; } } return $reformatted; } private static function buildCacheFile(string $content, string $cacheName): void { $cacheContent = sprintf( "format('Y-m-d H:i:s'), strtoupper($cacheName) ); $cacheContent .= $content; $file = fopen(getcwd() . '/' . self::PATH_CACHE . $cacheName . '.php', 'w'); fwrite($file, self::reformatCacheFileContent($cacheContent)); fclose($file); } private static function scanForFiles(string $folder): array { $folder = substr($folder, -1) === '/' ? substr($folder, 0, -1) : $folder; $files = []; $handler = opendir($folder); while ($file = readdir($handler)) { $path = $folder . '/' . $file; if (is_dir($path) && $file !== '.' && $file !== '..') { $files = array_merge($files, self::scanForFiles($path)); } elseif (is_file($path) && substr($path, -4) === '.php') { $className = substr($file, 0, -4); $files[$className] = $path; } } return $files; } private static function scanForClasses(): array { return self::scanForFiles(getcwd() . '/' . self::PATH_CLASSES); } private static function scanForControllers(): array { return self::scanForFiles(getcwd() . '/' . self::PATH_CONTROLLERS); } }