I’m new to OOP and MVC with PHP, and I’m currently learning by making my own custom MVC app for testing purposes. I’m not using Symfony yet, but I’ve integrated Twig and I have a problem with it when I call a 404 error outside a controller, in the two following cases :
- When requested page doesn’t exist on server (like http://localhost/test/)
- When requested URL doesn’t match with an existing controller and method (like http://localhost/test/task/)
I’ve got the following error :
Fatal error: Uncaught Error: Class 'TwigLoaderFilesystemLoader' not found in /Applications/XAMPP/xamppfiles/htdocs/librairies/Renderer.php:32 Stack trace: #0 /Applications/XAMPP/xamppfiles/htdocs/librairies/Renderer.php(21): Renderer::loadTwig() #1 /Applications/XAMPP/xamppfiles/htdocs/librairies/Http.php(26): Renderer::render('errors/404', Array) #2 /Applications/XAMPP/xamppfiles/htdocs/librairies/Application.php(35): Http::error404() #3 /Applications/XAMPP/xamppfiles/htdocs/index.php(9): Application::process() #4 {main} thrown in /Applications/XAMPP/xamppfiles/htdocs/librairies/Renderer.php on line 32
However, if I call the right controller with an existing method, everything works fine (like http://localhost/article/show/123). If I call 404 error inside a controller method (e.g. if the $_GET ID of an article does not exist in the DB), everything works fine too and my 404 error template is correctly rendered.
How I call a 404 error ?
I use a static method Http::error404()
that render my 404 error Twig template.
class Http { /** * Display a 404 error - page not found * * @return void */ public static function error404(): void { header('HTTP/1.1 404 Not Found'); $pageTitle = "Erreur 404 - Page non-trouvée"; Renderer::render('errors/404', compact('pageTitle')); exit; } }
Application class
My App use a mini-router named Application. It checks if the controller and the called method exists. If not, call 404 error with Http::error404()
, and it is in this case that the above fatal error appears.
class Application { /** * This is the app process, called in index.php file. * Use controllers and methods directly in URL. * URL are rewritten by .htaccess file at app root. * * Usage : https://example.com/controller/task/{optionnal_parameter} * Ex : https://example.com/article/show/145 * * @return void */ public static function process() { // By default, call homepage $controllerName = 'ArticleController'; $task = 'home'; if (!empty($_GET['controller'])) { $controllerName = ucfirst($_GET['controller'] . 'controller'); } if (!empty($_GET['task'])) { $task = $_GET['task']; } $controllerPath = "controllers\" . $controllerName; // Check if this controller & method exists if (!method_exists($controllerPath, $task)) { Http::error404(); // <-- Here, i'm not in a controller (line 35) } $controller = new $controllerPath(); $controller->$task(); } }
There is a strange thing here : if I define any controller before calling 404 error, everything works as I would like and my 404 error template is correctly rendered. But it’s not a clean and elegant solution…
// Check if this controller & method exists if (!method_exists($controllerPath, $task)) { new ControllersArticleController(); // <-- Not clean, but solve my fatal error Http::error404(); }
The same thing happens in my 404.php
file, called by the server and declared in the .htaccess
when a file is called and it doesn’t exist. My 404 PHP file just contains few lines :
require_once 'librairies/autoload.php'; Http::error404();
If I define any controller, it’s working perfectly and fatal error disappears.
require_once 'librairies/autoload.php'; new ControllersArticleController(); Http::error404();
Render Class
This is my rendering class. The fatal error appears in this file, as shown in line 32 below, when Http
class with error404 method render errors/404 Twig template.
require_once 'librairies/autoload.php'; use TwigEnvironment; use TwigLoaderFilesystemLoader; use TwigTwigFunction; class Renderer { /** * Print a HTML template with $var injection * * @param string $path * @param array $var */ public static function render(string $path, $var = []) { $template = self::loadTwig()->load("$path.html.twig"); echo $template->display($var); } /** * Load Twig * * @return Environment */ public static function loadTwig() { $loader = new FilesystemLoader('templates'); // <-- Line 32 is here ! $twig = new Environment($loader, [ 'auto_reload' => true, 'autoescape' => 'html' ]); $twig->addFunction( new TwigFunction('notificationDisplay', function() { return Notification::display(); }) ); return $twig; } }
I have only added the code that I think is related to my issue. Do not hesitate to ask if there are any points that need to be clarified.
I’ve been looking for a solution to this error for seven days and haven’t found a solution. Your help is my last resort. Thank you very much!
Advertisement
Answer
I removed all the many unnecessary require_once
autoloader in app files, and kept only the one in index.php
, as indicated by @NicoHaase.
From that point, Twig is no longer found and the same error message appears on all requests !
The solution was easy to find : in my index.php
file, I was using the wrong autoloader. I was calling my custom autoloader instead of the one from Composer…
I just change :
require_once 'librairies/autoload.php'; Application::process();
To :
require_once 'vendor/autoload.php'; Application::process();
And everything works fine now ! Thanks to @NicoHaase and @DarkBee for putting me on the right track !