Bicycle/System/Classes/Route.php
Egor Isaev b65603b4d0 dev
2026-06-23 10:44:18 +03:00

132 lines
4.5 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* @package Bicycle
* @author Egor Isaev
* @description Route.php
* @copyright (c) 03/06/2026
*/
namespace System\Classes;
/**
* Автоматический роутинг без конфигурации: разбирает URI и находит
* контроллер на файловой системе в App/Controller (регистронезависимо).
*/
class Route
{
/**
* Разбирает URI и возвращает массив с ключами controller, action, params.
*
* Поддерживаемые форматы URL:
* / → Index::index
* /controller → Controller::index
* /controller/action → Controller::action
* /controller/action/p1/p2 → Controller::action + params
* /subdir/controller/action → Subdir/Controller::action (если subdir — папка в App/Controller)
*
* @param string $uri URI без ведущего/замыкающего слэша
* @return array{controller:string, action:string, params:array}
*/
public static function resolve(string $uri): array
{
if ($uri === '') {
return self::make('Index', 'index', []);
}
$parts = explode('/', $uri);
$controller = self::findController($parts[0], '');
if ($controller !== null || !isset($parts[1])) {
return self::make(
$controller ?? ucfirst($parts[0]),
$parts[1] ?? 'index',
array_slice($parts, 2)
);
}
$subdir_controller = self::findController($parts[1], $parts[0]);
if ($subdir_controller !== null) {
return self::make(
$subdir_controller,
$parts[2] ?? 'index',
array_slice($parts, 3)
);
}
// Ни плоский контроллер, ни subdir не найдены — возвращаем flat-роутинг
return self::make(
ucfirst($parts[0]),
$parts[1] ?? 'index',
array_slice($parts, 2)
);
}
/**
* Ищет файл {name}Controller.php в App/Classes[/subdir] без учёта регистра.
* Возвращает имя контроллера (или subdir/имя) без суффикса Controller, либо null.
*
* @param string $name Базовое имя контроллера из URI
* @param string $subdir Имя подкаталога или '' для плоского поиска
* @return string|null
*/
private static function findController(string $name, string $subdir): ?string
{
$base = DOCROOT . '/App/Controller/';
$dir = $base;
if ($subdir !== '') {
foreach (is_dir($base) ? scandir($base) : [] as $entry) {
if ($entry !== '.' && $entry !== '..'
&& is_dir($base . $entry)
&& strtolower($entry) === strtolower($subdir)
) {
$dir = $base . $entry . '/';
$subdir = $entry;
break;
}
}
if ($dir === $base) {
return null;
}
}
if (!is_dir($dir)) {
return null;
}
$needle = strtolower($name . 'Controller');
foreach (scandir($dir) as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) !== 'php') {
continue;
}
$basename = pathinfo($file, PATHINFO_FILENAME);
if (strtolower($basename) === $needle) {
$clean = substr($basename, 0, -10); // убираем 'Controller'
return $subdir !== '' ? $subdir . '/' . $clean : $clean;
}
}
return null;
}
/**
* Собирает результат роутинга в единый формат.
*
* @param string $controller Имя контроллера (возможно с subdir через /)
* @param string $action Имя действия без суффикса Action
* @param array $params Позиционные параметры из URI
* @return array{controller:string, action:string, params:array}
*/
private static function make(string $controller, string $action, array $params): array
{
return [
'controller' => $controller,
'action' => $action,
'params' => $params,
];
}
}