Skip to content

Commit d023bca

Browse files
committed
Update App.php
1 parent c684cf8 commit d023bca

1 file changed

Lines changed: 274 additions & 43 deletions

File tree

src/App.php

Lines changed: 274 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use Psr\Container\ContainerExceptionInterface;
3737
use Psr\Container\ContainerInterface;
3838
use Psr\Container\NotFoundExceptionInterface;
39+
use ReflectionClass;
3940
use ReflectionException;
4041
use ReflectionFunction;
4142
use ReflectionFunctionAbstract;
@@ -169,7 +170,49 @@ public function onWebsocketConnect(TcpConnection &$connection, Http\Request $req
169170
// Проверка на 404 и 405
170171
$routeInfo = Router::dispatch('GET', $request->path());
171172
switch ($routeInfo[0]) {
173+
case Dispatcher::FOUND:
174+
$routeInfo[0] = 'route';
175+
$callback = $routeInfo[1];
176+
$app = $controller = $action = '';
177+
$args = !empty($routeInfo[2]) ? $routeInfo[2] : null;
178+
$route = clone $routeInfo[3];
179+
180+
if ($args) {
181+
$route->setParams($args);
182+
}
183+
184+
if (is_array($callback)) {
185+
$controller = $callback[0];
186+
$plugin = static::getPluginByClass($controller);
187+
$app = static::getAppByController($controller);
188+
$action = static::getRealMethod($controller, $callback[1]) ?? '';
189+
} else {
190+
$plugin = static::getPluginByPath($path);
191+
}
192+
193+
$callback = static::getCallback($plugin, $app, $callback, $args);
194+
static::$callbacks[$path] = [$callback, $plugin, $app, $controller ?: '', $action, $route];
195+
if (count(static::$callbacks) >= 1024) {
196+
unset(static::$callbacks[key(static::$callbacks)]);
197+
}
198+
break;
172199
case Dispatcher::NOT_FOUND:
200+
// Парсим контроллер и действие из пути
201+
$controllerAndAction = static::parseControllerAction($path);
202+
$plugin = $controllerAndAction['plugin'] ?? static::getPluginByPath($path);
203+
204+
// Получаем приложение, контроллер и действие
205+
$app = $controllerAndAction['app'];
206+
$controller = $controllerAndAction['controller'];
207+
$action = $controllerAndAction['action'];
208+
209+
// Получаем обратный вызов
210+
$callback = static::getCallback($plugin, $app, [$controller, $action]);
211+
static::$callbacks[$path] = [$callback, $plugin, $app, $controller, $action, null];
212+
if (count(static::$callbacks) >= 1024) {
213+
unset(static::$callbacks[key(static::$callbacks)]);
214+
}
215+
173216
Server::log('Не найден URL: ' . $path . PHP_EOL);
174217
static::close_http($connection, 404);
175218
return;
@@ -221,49 +264,7 @@ public function onMessage(mixed $connection, mixed $request): void
221264
static::send($connection, $callback($request));
222265
}
223266

224-
$routeInfo = Router::dispatch('GET', $path);
225-
226-
switch ($routeInfo[0]) {
227-
case Dispatcher::FOUND:
228-
$routeInfo[0] = 'route';
229-
$callback = $routeInfo[1];
230-
$app = $controller = $action = '';
231-
$args = !empty($routeInfo[2]) ? $routeInfo[2] : null;
232-
$route = clone $routeInfo[3];
233-
234-
if ($args) {
235-
$route->setParams($args);
236-
}
237-
238-
if (is_array($callback)) {
239-
$controller = $callback[0];
240-
$plugin = static::getPluginByClass($controller);
241-
$app = static::getAppByController($controller);
242-
$action = static::getRealMethod($controller, $callback[1]) ?? '';
243-
} else {
244-
$plugin = static::getPluginByPath($path);
245-
}
246-
247-
$callback = static::getCallback($plugin, $app, $callback, $args);
248-
static::$callbacks[$path] = [$callback, $plugin, $app, $controller ?: '', $action, $route];
249-
if (count(static::$callbacks) >= 1024) {
250-
unset(static::$callbacks[key(static::$callbacks)]);
251-
}
252-
253-
[$callback,
254-
$request->plugin, $request->app,
255-
$request->controller, $request->action,
256-
$request->route] = static::$callbacks[$path];
257-
258-
static::send($connection, $callback($buffer));
259-
break;
260-
case Dispatcher::NOT_FOUND:
261-
static::close($connection, 404);
262-
break;
263-
case Dispatcher::METHOD_NOT_ALLOWED:
264-
static::close($connection, 405);
265-
break;
266-
}
267+
static::close($connection, 404);
267268
} catch (Throwable $e) {
268269
// Если возникло исключение, отправляем ответ на исключение
269270
static::send($connection, static::exceptionResponse($e, $request));
@@ -707,6 +708,236 @@ public static function getPluginByPath(string $path): string
707708
return $tmp[1] ?? '';
708709
}
709710

711+
/**
712+
* Функция для разбора контроллера и действия из пути.
713+
*
714+
* @param string $path Путь.
715+
* @return array|false Возвращает массив с информацией о контроллере и действии, если они найдены, иначе возвращает false.
716+
* @throws ReflectionException
717+
*/
718+
protected static function parseControllerAction(string $path): false|array
719+
{
720+
// Удаляем дефисы из пути
721+
$path = str_replace(['-', '//'], ['', '/'], $path);
722+
723+
static $cache = [];
724+
if (isset($cache[$path])) {
725+
return $cache[$path];
726+
}
727+
728+
// Разбиваем путь на части
729+
$pathExplode = explode('/', trim($path, '/'));
730+
731+
// Проверяем, является ли путь плагином
732+
$isPlugin = isset($pathExplode[1]) && $pathExplode[0] === 'app';
733+
734+
// Получаем префиксы для конфигурации, пути и класса
735+
$configPrefix = $isPlugin ? "plugin.$pathExplode[1]." : '';
736+
$pathPrefix = $isPlugin ? "/app/$pathExplode[1]" : '';
737+
$classPrefix = $isPlugin ? "plugin\\$pathExplode[1]" : '';
738+
739+
// Получаем суффикс контроллера из конфигурации
740+
$suffix = Config::get("{$configPrefix}app.controller_suffix", '');
741+
742+
// Получаем относительный путь
743+
$relativePath = trim(substr($path, strlen($pathPrefix)), '/');
744+
$pathExplode = $relativePath ? explode('/', $relativePath) : [];
745+
746+
// По умолчанию действие - это 'index'
747+
$action = 'index';
748+
749+
// Пытаемся угадать контроллер и действие
750+
if (!$controllerAction = static::guessControllerAction($pathExplode, $action, $suffix, $classPrefix)) {
751+
// Если контроллер и действие не найдены и путь состоит из одной части, возвращаем false
752+
if (count($pathExplode) <= 1) {
753+
return false;
754+
}
755+
756+
$action = end($pathExplode);
757+
unset($pathExplode[count($pathExplode) - 1]);
758+
$controllerAction = static::guessControllerAction($pathExplode, $action, $suffix, $classPrefix);
759+
}
760+
761+
if ($controllerAction && !isset($path[256])) {
762+
$cache[$path] = $controllerAction;
763+
if (count($cache) > 1024) {
764+
unset($cache[key($cache)]);
765+
}
766+
}
767+
768+
return $controllerAction;
769+
}
770+
771+
772+
/**
773+
* Функция для предположения контроллера и действия.
774+
*
775+
* @param array $pathExplode Массив с разделенными частями пути.
776+
* @param string $action Название действия.
777+
* @param string $suffix Суффикс.
778+
* @param string $classPrefix Префикс класса.
779+
* @return array|false Возвращает массив с информацией о контроллере и действии, если они найдены, иначе возвращает false.
780+
* @throws ReflectionException
781+
*/
782+
protected static function guessControllerAction(array $pathExplode, string $action, string $suffix, string $classPrefix): false|array
783+
{
784+
// Создаем карту возможных путей к контроллеру
785+
$map[] = trim("$classPrefix\\app\\controller\\" . implode('\\', $pathExplode), '\\');
786+
foreach ($pathExplode as $index => $section) {
787+
$tmp = $pathExplode;
788+
array_splice($tmp, $index, 1, [$section, 'controller']);
789+
$map[] = trim("$classPrefix\\" . implode('\\', array_merge(['app'], $tmp)), '\\');
790+
}
791+
foreach ($map as $item) {
792+
$map[] = $item . '\\index';
793+
}
794+
795+
// Проверяем каждый возможный путь
796+
foreach ($map as $controllerClass) {
797+
// Удаляем xx\xx\controller
798+
if (str_ends_with($controllerClass, '\\controller')) {
799+
continue;
800+
}
801+
$controllerClass .= $suffix;
802+
// Если контроллер и действие найдены, возвращаем информацию о них
803+
if ($controllerAction = static::getControllerAction($controllerClass, $action)) {
804+
return $controllerAction;
805+
}
806+
}
807+
808+
// Если контроллер или действие не найдены, возвращаем false
809+
return false;
810+
}
811+
812+
813+
/**
814+
* Функция для получения контроллера и действия.
815+
*
816+
* @param string $controllerClass Имя класса контроллера.
817+
* @param string $action Название действия.
818+
* @return array|false Возвращает массив с информацией о контроллере и действии, если они найдены, иначе возвращает false.
819+
* @throws ReflectionException
820+
*/
821+
protected static function getControllerAction(string $controllerClass, string $action): false|array
822+
{
823+
// Отключаем вызов магических методов
824+
if (str_starts_with($action, '__')) {
825+
return false;
826+
}
827+
828+
// Если класс контроллера и действие найдены, возвращаем информацию о них
829+
if (($controllerClass = static::getController($controllerClass)) && ($action = static::getAction($controllerClass, $action))) {
830+
return [
831+
'plugin' => static::getPluginByClass($controllerClass),
832+
'app' => static::getAppByController($controllerClass),
833+
'controller' => $controllerClass,
834+
'action' => $action
835+
];
836+
}
837+
838+
// Если класс контроллера или действие не найдены, возвращаем false
839+
return false;
840+
}
841+
842+
/**
843+
* Функция для получения контроллера.
844+
*
845+
* @param string $controllerClass Имя класса контроллера.
846+
* @return string|false Возвращает имя класса контроллера, если он найден, иначе возвращает false.
847+
* @throws ReflectionException
848+
*/
849+
protected static function getController(string $controllerClass): false|string
850+
{
851+
// Если класс контроллера существует, возвращаем его имя
852+
if (class_exists($controllerClass)) {
853+
return (new ReflectionClass($controllerClass))->name;
854+
}
855+
856+
// Разбиваем полное имя класса на части
857+
$explodes = explode('\\', strtolower(ltrim($controllerClass, '\\')));
858+
$basePath = $explodes[0] === 'plugin' ? Path::basePath('plugin') : app_path();
859+
unset($explodes[0]);
860+
$fileName = array_pop($explodes) . '.php';
861+
$found = true;
862+
863+
// Ищем соответствующую директорию
864+
foreach ($explodes as $pathSection) {
865+
if (!$found) {
866+
break;
867+
}
868+
$dirs = scan_dir($basePath, false);
869+
$found = false;
870+
foreach ($dirs as $name) {
871+
$path = "$basePath/$name";
872+
if (is_dir($path) && strtolower($name) === $pathSection) {
873+
$basePath = $path;
874+
$found = true;
875+
break;
876+
}
877+
}
878+
}
879+
880+
// Если директория не найдена, возвращаем false
881+
if (!$found) {
882+
return false;
883+
}
884+
885+
// Ищем файл контроллера в директории
886+
foreach (scandir($basePath) ?: [] as $name) {
887+
if (strtolower($name) === $fileName) {
888+
require_once "$basePath/$name";
889+
if (class_exists($controllerClass, false)) {
890+
return (new ReflectionClass($controllerClass))->name;
891+
}
892+
}
893+
}
894+
895+
// Если файл контроллера не найден, возвращаем false
896+
return false;
897+
}
898+
899+
/**
900+
* Функция для получения действия контроллера.
901+
*
902+
* @param string $controllerClass Имя класса контроллера.
903+
* @param string $action Название действия.
904+
* @return string|false Возвращает название действия, если оно найдено, иначе возвращает false.
905+
*/
906+
protected static function getAction(string $controllerClass, string $action): false|string
907+
{
908+
// Получаем все методы класса контроллера
909+
$methods = get_class_methods($controllerClass);
910+
$lowerAction = strtolower($action);
911+
$found = false;
912+
913+
// Проверяем, есть ли метод, соответствующий действию
914+
foreach ($methods as $candidate) {
915+
if (strtolower($candidate) === $lowerAction) {
916+
$action = $candidate;
917+
$found = true;
918+
break;
919+
}
920+
}
921+
922+
// Если действие найдено, возвращаем его
923+
if ($found) {
924+
return $action;
925+
}
926+
927+
// Если действие не является публичным методом, возвращаем false
928+
if (method_exists($controllerClass, $action)) {
929+
return false;
930+
}
931+
932+
// Если в классе контроллера есть метод __call, возвращаем действие
933+
if (method_exists($controllerClass, '__call')) {
934+
return $action;
935+
}
936+
937+
// В противном случае возвращаем false
938+
return false;
939+
}
940+
710941
/**
711942
* @param mixed $data
712943
* @return string

0 commit comments

Comments
 (0)