ringfinger/backend/classes/core/Autoloader.php

246 lines
6.6 KiB
PHP

<?php
declare(strict_types=1);
class Autoloader
{
private const PATH_CLASSES = 'backend/classes';
private const PATH_CONTROLLERS = self::PATH_CLASSES . '/controller';
private const PATH_CACHE = 'backend/cache/';
public function __construct(string $cachePath = self::PATH_CACHE)
{
if ($cachePath !== self::PATH_CACHE) {
$cachePath = substr($cachePath, -1) === '/' ? $cachePath : $cachePath . '/';
}
$routesFound = @include($cachePath . 'routes.php');
$classesFound = @include($cachePath . 'classes.php');
if (!$routesFound || !$classesFound) {
throw new Exception(
sprintf(
'Autoloader cache not found! Please generate it with %s::BuildCache() at first!',
self::class
)
);
}
spl_autoload_register(
function (string $className) {
if (!$this->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' => [],
];
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(
"<?php\n\n/*\n * This file was auto generated on %s\n */\n\ndefine(\n\t'%s',\n\t[\n",
(new DateTime())->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);
}
}