Добавлю к ответам остальных - еще посоветовал бы почитать про depency injection.
Посмотрите, как приложение реализовано laravel.
Там есть хелпер app(), который всегда вернет экземпляр приложения из контейнера "прямого singleton" - там нет, за три года разработки ни разу не увидел необходимости прямого singlton, все можно сделать через контейнер управления зависимостями.
Если вы не используете фреймворк, или хотите написать сами, вот вам мой пример:
точка входа: index.php
-- Подключается автозагрузка классов, лучше composer - без этого сейчас писать моветон.
-- Подключается файл с хелперами и нужными функциями: app() и.т.д.
-- Создается обертка для request вида: $request = new request($_GET,$_POST,...) и.т.д.
-- Создается экземпляр приложения: $app = new App($config), где конфиг либо массив настроек, либо в моем случае класс.
-- $response = $app->handle($request), у меня просто echo $app->handle($request), присутствует немного магии
-- Как выглядит метод handle:
$this->instance(RequestContract::class, $request);
try {
$this->instance(RequestContract::class, $request);
$response = $this->getPipeLine()->send($request);
$this->afterHandle($request);
} catch (\Exception $e){
$response = app(Handler::class)->handle($e);
}
if(!$response instanceof ResponseContract){
$response = $this->prepareResponse($response);
}
return $response;
Объект request - регистрируется в контейнере и всегда будет отдаваться один и тот-же экземпляр.
$this->getPipeLine - можете упростить, у меня это посредники (laravel style), в этом месте возьмите сразу работу с диспетчером роутера.
Я использую обвязку для fastroute, так как это реальный проект, но написать на регулярках простой роутер несложно.
Если роут не найден, либо была ошибка в коде в глубину - выбрасывается определенный exception, который обрабатывается в специальном классе и также отдает response с 404, 501 и.т.д. ошибками.
Если роут найден, то он вызывается и возвращает данные.
Я использую контейнер laravel с autowiring, в моем случае, если контроллер и метод найдены, все выглядит так.
return $this->container->call(
[$this->container->make($handler[0], $routeInfo[2]), $handler[1]],
$routeInfo[2]
);
Автоваринг - достаточно удобная штука на рефлексии, сама подставляет зависимости из контейнера, и если не ошибаюсь, в laravel появилась раньше, чем в symfony, свой контейнер похожий на laravel вышел 130 строчек, можете поискать в английском интернете по фразе: "php autowiring"- там есть статьи на эту тему.
Сейчас меня заплюют, про то, что нужно взять готовый фреймворк и не писать костыли, но при 10млн просмотров нестатичных страниц в день, такое решение в разы обходит по скорости готовый фреймворк, так как лишнего ничего нет. Еще вам скинули ссылку на курс на ютубе Дмитрия Елисеева, может быть он и профессиональный разработчик, но видосики по 2 часа - это реальное издевательство, очень сильно разжевано, да, но очень долго, возможно отдельный редактор бы его спас. Я бы его лучше слушал вместо музыки. То, что ты именно не понимаешь - глазами не найти, лучше ищите статьи, желательно на английском, очень много разных заметок, смотрите исходные коды разных фреймворков - это даст вам понять, как все работает. Еще скажу о PSR - это не свод законов, а рекомендации, хотя первые - это реальное табу, нарушать нельзя, но все psr не поддерживает почти не один современный фреймворк. Так как стандартизация не всегда вяжется с удобством и скоростью, всегда приходится выбирать.
P.S Это только мой взгляд, у разных людей - разные мнения и разный опыт.