Роуты формируются примерно так (тупо пример, чтобы все варики затестить): PHP: return fn(Application $application) => $application->getRouter() ->addGetRoute('/', fn() => [new HomeController(), '__invoke']) ->addGetRoute('/test', [new HomeController(), '__invoke']) ->addGetRoute('/{id}', [HomeController::class, '__invoke'], [ 'id' => '\d+' ]) ->addGetRoute('/{name}', HomeController::class); Резольвер: PHP: public function resolve(Request $request): void { $result = $this->match($request); if (is_null($result)) { // 404 } else { $handler = $result['handler']; $arguments = $result['arguments']; if ($handler instanceof Closure) { call_user_func($handler(), $request, $arguments); } elseif (is_callable($handler)) { call_user_func($handler, $request, $arguments); } elseif (is_array($handler)) { [$controller, $action] = $handler; call_user_func([new $controller, $action], $request, $arguments); } else { $route = new $handler; $route($request, $arguments); } } } Как бы всё работает, всё нормально у меня (я по поводу проверок в резольвере)?
Тут такое дело, сколько бы ты вариантов указания экшн не реализовал, это и будут "все варики" твоего фреймворка. Хоть 1, хоть 10. Кстати, разве instanceof Closure не является частным случаем is_callable()? Помоему, одну проверку можно убрать, но я не проверял. --- Добавлено --- Из примера непонятно зачем само определение маршрутов оформлено в кложу.
Нет. Я так столкивался с проблемой одной. PHP: <?php class Foo { public function __invoke() { return 2; } } $o = new Foo; var_dump ( is_callable ( $o ), $o instanceof Closure ); bool(true) bool(false) --- Добавлено --- @Вероломство, ты компосером владеешь ?
Closure это анонимная функция и она является callable. И в call_user_func() её тоже можно вставлять как другие callable. Создам кложу для проверки четности, например. Код (Text): $ tinker Psy Shell v0.9.12 (PHP 7.4.25 — cli) by Justin Hileman >>> $closure = fn($x) => $x % 2 == 0; => Closure($x) {#4735 …2} >>> var_dump ( is_callable ( $closure ), $closure instanceof Closure ); bool(true) bool(true) => null >>> call_user_func($closure, 1) => false >>> call_user_func($closure, 2) => true >>> @Вероломство получается if ($handler instanceof Closure) можно убрать, потому что следующий if всё что надо сделает для варианта с Closure
@artoodetoo мне чтобы callable запустить, то нужно просто $handler прописать, но чтобы анонимку запустить (в которой содержится callable), то нужно получить доступ к её содержимому, а это $handler() поэтому и идёт проверка именно на Closure
Я нифига не понял, но дело хозяйское зачем такое может понадобиться, кроме как для абстрактного бессмысленного примера...
ну так зачем ты тогда предлагаешь бессмысленный пример? ты предложил не проверять на анонимку, НО вызывать, как callable в обоих случаях, в то время как анонимка - это не callable - это анонимка, в которую запакована callable ))) чтобы получить callable из анонимки, то её нужно вызывать со скобками, как функцию, дядя ))) PHP: call_user_func($handler() /** Closure */, $request, $arguments); call_user_func($handler /** callable */, $request, $arguments);
я вижу что твой пример это содержит. но не вижу смысла. ты молодец, поставил себе задачку и решил её. потом пошел спрашивать "хорошо ли я сделал", ну и получил ответ. обижаться не на что, мой юный падаван. ) вопрос зачем оно там запаковано? есть осмысленный пример анонимной функции в описании маршрута: то что из коробки показано в laravel. типа такого тут ничего не "запаковано". просто побыстрому создали эшен без ссылки на контроллер. PHP: Route::get('/', function () { return view('welcome'); }); видишь разницу со своей задумкой? вот про решение для такого примера я тебе написал "проверять на Closure специально не нужно. оно будет выполняться и без этого". --- Добавлено --- также непонятно зачем ты "запаковал" саму таблицу маршрутов в анонимную функцию. чтобы где-то придумать как его "распаковать"? )))
таблица маршрутов не запакована в анонимку, а сделано это для того, чтобы автокомплит шторма работал у $application СРАЗУ, потому что маршруты в инклудном файле находятся, а phpdoc не прикалывает писать для $application))) в анонимку запаковывается обработчик какого-то маршрута, если он callable и объект создаётся в момент вызова анонимки, в отличие от случая, когда обработчик уже callable и объект болтается уже готовый в таблице маршрутов PHP: ->addGetRoute('/', fn() => [new HomeController(), 'indexAction']) // объект будет создан при вызове анонимки ->addGetRoute('/test', [new HomeController(), 'indexAction']) // объект уже создан дядя Йода)))
Лол. Ты смешной. Вопрос был: зачем? А ты взялся рассказывать как работает функция. Зачем добавлять что-то ненужное и при этом делать невозможным экшен без класса контроллера?
Моё предложение — поддерживай такие варианты: [HomeController::class, 'getIndex'] 'HomeController@getIndex' // то же, что и предыдущий, только выглядит короче HomeController::class // подразумевыается что будет создан объект класса и у него вызовется __invoke() fn() => 'Hello world!' // на самом деле ЛЮБОЙ callable, а не только кложа. результат выполнения это непосредственно нужное действие (экшн), а не ссылка на что-то-там. --- Добавлено --- ты знаешь как всё это сделать. --- Добавлено --- мне кажется ты просто запутался в том что является callable и как используется магия __invoke.
__invoke тут не при чём - просто я чтобы не создавать ДЛЯ ПРИМЕРА 4 разных контроллера, то использовал один для всех вариантов, поэтому там __invoke отсвечивает, чтобы в свою очередь отработал И варик с магией ПРИ ДЕБАГЕ моём своего рода я с тобой соглашусь, что нужно убирать всю эту дичь, НО я вообще отказался от Closure и callable, оставил только [HomeController::class,'__invoke'] и просто HomeController::class (зачем мне запакованные или готовые объекты в роутах), я обработаю строковые значения и будет так: PHP: class Resolver { private Container $container; public function __construct(Container $container) { $this->container = $container; } public function resolve(string|array $handler): callable { if (is_string($handler)) { return $this->container->get($handler); } [$controller, $action] = $handler; $controller = $this->container->get($controller); return [$controller, $action]; } }