Merge pull request 'BenBack' (#1) from BenBack into master
Reviewed-on: #1
This commit is contained in:
58
README.md
58
README.md
@@ -1 +1,57 @@
|
|||||||
# Les Recettes des Papis
|
# Les recettes des Papis
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
- Décompressez l'archive du site.
|
||||||
|
- Pointez votre domaine sur le dossier "public".
|
||||||
|
- Modifiez les fichiers de configuration dans "config".
|
||||||
|
|
||||||
|
## Informations pratiques sur le PHP.
|
||||||
|
|
||||||
|
- Ce site utilise le principe des routes. Afin de ne pas avoir x fichiers publiques identiques pour chaque page.
|
||||||
|
- Tout est géré par le fichier public/index.php.
|
||||||
|
- index.php va charger le Kernel (Noyau) de l'application.
|
||||||
|
- Le noyau fonctionne sur le principe d'élément unique. La méthode ``Kernel::getInstance()`` va permettre d'avoir l'unique instance de la classe Kernel.
|
||||||
|
- Une fois ``Kernel::getInstance()->init()`` déclenché, le noyau enregistre l'autoloading des fichiers, puis enregistre la configuration.
|
||||||
|
- La configuration sont des fichiers dans le dossier config/ qui vont renvoyer un tableau clé/valeur. Exemple
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Fichier config/test.php
|
||||||
|
<?php return [
|
||||||
|
'cle_1' => "Val1",
|
||||||
|
'cle_2' => "Val2",
|
||||||
|
// ...
|
||||||
|
] ?>
|
||||||
|
```
|
||||||
|
|
||||||
|
- Les fichiers de configuration sont chargés via la méthode ``ConfigFactory::loadConfigFile()`` qui va vérifier si le fichier de configuration existe et le charge en conséquence.
|
||||||
|
- On enchaine ensuite sur le Router.
|
||||||
|
- Le Router est une classe utilitaire qui va permettre de faire le lien entre la route saisie ``/test/...`` de l'utilisateur et les routes enregistrés par le site.
|
||||||
|
- Le Router va d'abord commencer par récupérer la route voulue par l'utilisateur.
|
||||||
|
- Puis, le router va rechercher tous les Controllers existants.
|
||||||
|
- Un Controller est une classe qui va permettre de définir les routes et de dire, qu'est-ce qui se passe quand je prends cette route. Les routes du Controller sont définis par la methode ``Controller::defineRoutes()``
|
||||||
|
- Pour définir les routes dans la méthode précédente, vous pouvez soit créer un objet ``Route`` en remplissant tous les champs, ou bien la méthode ``Controller::Route()`` qui va préremplir certains champs.
|
||||||
|
- Exemple de Controller
|
||||||
|
```php
|
||||||
|
class TestController extends Controller {
|
||||||
|
|
||||||
|
public static function defineRoutes(): array {
|
||||||
|
return [
|
||||||
|
self::Route( routeUrl: '/test', routeName: "Test", routeAction: "test" ),
|
||||||
|
new Route( routeUrl: "/test2", routeName: "Test2", routeController: "TestController", routeAction: "test2", routeMethods = [ 'GET' ] )
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test(){
|
||||||
|
echo "Je suis déclenché lorsque j'atteinds la route /test";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test2(){
|
||||||
|
echo "Je suis déclenché lorsque j'atteinds la route /test2 !!!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Retournons au routeur, pour chercher les controllers, il va scanner les fichiers du dossier src/Domain avec un Iterator Récursif. (Pour se simplifier un peu la tâche.)
|
||||||
|
- Il va traiter le nom du chemin absolu pour obtenir le nom de la classe, puis va vérifier si cette classe est une sous-classe de Controller.
|
||||||
|
- Après avoir trouvé tous les Controllers, il va récupérer toutes les routes de chaque controller.
|
||||||
|
- Enfin, il va vérifier si la route utilisateur correspond à une des routes dans sa liste, si ça correspond, le Router va charger la méthode ``new {RouteController}()->{RouteAction}()``
|
||||||
|
- Si par exemple, le router trouve la route ``/test`` voulu par l'utilisateur, il va déclencher la méthode ``new TestController()->test()``.
|
||||||
8
config/general.php
Normal file
8
config/general.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'website_url' => 'http://127.0.0.1:8080/',
|
||||||
|
'website_name' => 'Les recettes des Papis',
|
||||||
|
|
||||||
|
'website_path' => APP_ROOT,
|
||||||
|
];
|
||||||
6
config/route_arguments.php
Normal file
6
config/route_arguments.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'{int}' => '([0-9]+)',
|
||||||
|
'{chars}' => '([A-Za-z]+)',
|
||||||
|
'{string}' => '([0-9A-Za-z]+)',
|
||||||
|
];
|
||||||
4
config/views.php
Normal file
4
config/views.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'base_view_skeleton' => 'base'
|
||||||
|
];
|
||||||
13
public/index.php
Normal file
13
public/index.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Kernel;
|
||||||
|
|
||||||
|
if( !defined( 'APP_ROOT' ) )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Définit la racine de l'application en chemin absolue.
|
||||||
|
*/
|
||||||
|
define( 'APP_ROOT', realpath( __DIR__ . '/../') . '/' );
|
||||||
|
|
||||||
|
require_once '../src/Kernel.php';
|
||||||
|
Kernel::start();
|
||||||
37
src/Domain/Controller.php
Normal file
37
src/Domain/Controller.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domain;
|
||||||
|
|
||||||
|
use App\Http\Route;
|
||||||
|
|
||||||
|
abstract class Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Va permettre de créer une route en préremplissant certains arguments.
|
||||||
|
* Pour les paramètres, veuillez voir le constructeur d'une Route.
|
||||||
|
*
|
||||||
|
* @param mixed ...$args
|
||||||
|
* @return Route
|
||||||
|
*
|
||||||
|
* @see Route::__construct()
|
||||||
|
*/
|
||||||
|
public static function Route( ...$args ): Route {
|
||||||
|
$defaults = [
|
||||||
|
'routeController' => static::class,
|
||||||
|
'routeMethods' => [ 'GET' ],
|
||||||
|
];
|
||||||
|
|
||||||
|
$args = array_merge($defaults, $args);
|
||||||
|
return new Route( ...$args );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de définir les routes du controller sous le format d'une liste d'objets Route.
|
||||||
|
* Vous pouvez utiliser la method self::Route() pour préremplir des champs.
|
||||||
|
*
|
||||||
|
* @return Route[]
|
||||||
|
* @see self::Route()
|
||||||
|
*/
|
||||||
|
abstract public static function defineRoutes(): array;
|
||||||
|
|
||||||
|
}
|
||||||
35
src/Domain/Pages/PagesController.php
Normal file
35
src/Domain/Pages/PagesController.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domain\Pages;
|
||||||
|
|
||||||
|
use App\Domain\Controller;
|
||||||
|
use App\Infrastructure\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller pour les pages sans lien avec un contenu spécifique.
|
||||||
|
* Exemple : Page d'accueil.
|
||||||
|
*/
|
||||||
|
class PagesController extends Controller {
|
||||||
|
|
||||||
|
public static function defineRoutes(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::Route( routeUrl: '/', routeName: 'Homepage', routeAction: 'index' ),
|
||||||
|
self::Route( routeUrl: '/test/{string}/baba', routeName: 'test', routeAction: 'test' ),
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route de la page d'accueil.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function index(): View {
|
||||||
|
return new View( 'home', [ 'ok' => 'bla' ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test( string $id ): void {
|
||||||
|
echo "Coucou" . $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
28
src/Exceptions/ConfigFailedLoadingException.php
Normal file
28
src/Exceptions/ConfigFailedLoadingException.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception qui se déclenche lorsqu'un fichier de configuration n'a pas pu être chargé.
|
||||||
|
*
|
||||||
|
* - Peut se déclencher si un fichier de configuration non voulu.
|
||||||
|
*/
|
||||||
|
class ConfigFailedLoadingException extends \Exception {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chemin vers le fichier de configuration manquant.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public private(set) string $configFilePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructeur.
|
||||||
|
* @param string $configFilePath Le fichier de configuration manquant.
|
||||||
|
*/
|
||||||
|
public function __construct( string $configFilePath ) {
|
||||||
|
$this->configFilePath = $configFilePath;
|
||||||
|
$this->message = "Failed to load configuration file '{$configFilePath}'.";
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
src/Exceptions/InvalidRouteException.php
Normal file
15
src/Exceptions/InvalidRouteException.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
class InvalidRouteException extends \Exception {
|
||||||
|
|
||||||
|
private string $clientRoute;
|
||||||
|
|
||||||
|
public function __construct(string $clientRoute)
|
||||||
|
{
|
||||||
|
$this->clientRoute = $clientRoute;
|
||||||
|
$message = "{$clientRoute} doesn't exist";
|
||||||
|
parent::__construct($message, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
13
src/Exceptions/InvalidViewException.php
Normal file
13
src/Exceptions/InvalidViewException.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class InvalidViewException extends \Exception {
|
||||||
|
|
||||||
|
public function __construct( string $viewName )
|
||||||
|
{
|
||||||
|
parent::__construct( "View {$viewName} does not exist.", 500 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
38
src/Helpers/AutoLoader.php
Normal file
38
src/Helpers/AutoLoader.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classe utilitaire contenant des méthodes pour charger automatiquement les classes.
|
||||||
|
*/
|
||||||
|
class AutoLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace principal de l'application.
|
||||||
|
*/
|
||||||
|
public const string PRIMARY_NAMESPACE = 'App\\';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet d'enregistrer la méthode _autoload_ en tant que méthode pour charger des classes.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function register(): void {
|
||||||
|
spl_autoload_register( [ self::class, '_autoload_' ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de charger une classe.
|
||||||
|
*
|
||||||
|
* @param string $className
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function _autoload_( string $className ): void {
|
||||||
|
if( !str_starts_with( $className, self::PRIMARY_NAMESPACE ) ) // Ne s'occupe que des classes à charger de cette application.
|
||||||
|
return;
|
||||||
|
|
||||||
|
$className = str_replace( self::PRIMARY_NAMESPACE, '', $className );
|
||||||
|
$className = str_replace( '\\', '/', $className );
|
||||||
|
|
||||||
|
require APP_ROOT . 'src/' . $className . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
1
src/Helpers/CacheManager.php
Normal file
1
src/Helpers/CacheManager.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
42
src/Helpers/ConfigFactory.php
Normal file
42
src/Helpers/ConfigFactory.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
use App\Exceptions\ConfigFailedLoadingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classe outillage qui permet de charger les fichiers de configuration.
|
||||||
|
*/
|
||||||
|
class ConfigFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chemin relatif à partir de APP_ROOT du dossier contenant les fichiers de configuration.
|
||||||
|
*
|
||||||
|
* @see APP_ROOT
|
||||||
|
*/
|
||||||
|
const string CONFIG_RELATIVE_FOLDER = "config/";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de charger un fichier de configuration.
|
||||||
|
*
|
||||||
|
* @param string $fileName Le nom du fichier de configuration, avec ou sans l'extension .php.
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @throws ConfigFailedLoadingException
|
||||||
|
*/
|
||||||
|
public static function loadConfigFile( string $fileName ): array {
|
||||||
|
if( !str_ends_with( $fileName, ".php" ) ) {
|
||||||
|
$fileName .= ".php";
|
||||||
|
}
|
||||||
|
|
||||||
|
$filePath = APP_ROOT . self::CONFIG_RELATIVE_FOLDER . $fileName;
|
||||||
|
|
||||||
|
if( !file_exists( $filePath ) ){
|
||||||
|
throw new ConfigFailedLoadingException( "Config file '$fileName' does not exist" );
|
||||||
|
}
|
||||||
|
|
||||||
|
$configArray = require $filePath;
|
||||||
|
return $configArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
31
src/Http/Route.php
Normal file
31
src/Http/Route.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de définir une route précise.
|
||||||
|
*/
|
||||||
|
final class Route {
|
||||||
|
|
||||||
|
|
||||||
|
public private(set) string $routeUrl;
|
||||||
|
public private(set) string $routeName;
|
||||||
|
public private(set) string $routeController;
|
||||||
|
public private(set) string $routeAction;
|
||||||
|
public private(set) array $routeMethods;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $routeUrl,
|
||||||
|
string $routeName,
|
||||||
|
string $routeController,
|
||||||
|
string $routeAction,
|
||||||
|
array $routeMethods
|
||||||
|
){
|
||||||
|
$this->routeUrl = $routeUrl;
|
||||||
|
$this->routeName = $routeName;
|
||||||
|
$this->routeController = $routeController;
|
||||||
|
$this->routeAction = $routeAction;
|
||||||
|
$this->routeMethods = $routeMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
164
src/Http/Router.php
Normal file
164
src/Http/Router.php
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http;
|
||||||
|
|
||||||
|
use App\Domain\Controller;
|
||||||
|
use App\Exceptions\InvalidRouteException;
|
||||||
|
use App\Helpers\AutoLoader;
|
||||||
|
use App\Kernel;
|
||||||
|
use FilesystemIterator;
|
||||||
|
|
||||||
|
final class Router {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route voulue par le client.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public private(set) static string $clientRouteString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Informations de la route du client.
|
||||||
|
* @var Route
|
||||||
|
*/
|
||||||
|
public private(set) static Route $clientRoute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Informations sur les arguments passés à la route.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public private(set) static array $clientRouteArguments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste des controllers sorti du cache ou récupéré via fetchControllers.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static array $controllers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste des routes tirées des controllers. Rempli par la méthode fetchRoutes.
|
||||||
|
* @var Route[]
|
||||||
|
*/
|
||||||
|
private static array $routes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction principale qui va router le contenu vers la bonne méthode du bon Controller.
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @throws InvalidRouteException Si la route voulue n'existe pas ou n'est pas bien formaté.
|
||||||
|
*/
|
||||||
|
public static function routeTo(): void {
|
||||||
|
|
||||||
|
self::$clientRouteString = $_SERVER['REQUEST_URI'];
|
||||||
|
|
||||||
|
self::$controllers = self::fetchControllers();
|
||||||
|
self::$routes = self::fetchRoutes();
|
||||||
|
|
||||||
|
$clientRoute = self::clientRouteExist();
|
||||||
|
if( !$clientRoute ){
|
||||||
|
throw new InvalidRouteException( self::$clientRouteString );
|
||||||
|
}
|
||||||
|
self::$clientRoute = $clientRoute;
|
||||||
|
|
||||||
|
if( !in_array( $_SERVER['REQUEST_METHOD'], self::$clientRoute->routeMethods ) ){
|
||||||
|
throw new InvalidRouteException( self::$clientRouteString );
|
||||||
|
} else {
|
||||||
|
self::executeRouteAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de récupérer tous les controllers du dossier Domain.
|
||||||
|
* @return class-string[]
|
||||||
|
*/
|
||||||
|
private static function fetchControllers(): array {
|
||||||
|
|
||||||
|
$classes = [];
|
||||||
|
|
||||||
|
$iterator = new \RecursiveIteratorIterator(
|
||||||
|
new \RecursiveDirectoryIterator( APP_ROOT . 'src/Domain', FilesystemIterator::SKIP_DOTS ),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach( $iterator as $file ){
|
||||||
|
|
||||||
|
if( $file->isFile() && $file->getExtension() === 'php' ){
|
||||||
|
|
||||||
|
$fileName = $file->getPathname();
|
||||||
|
|
||||||
|
// Transformation du chemin du fichier vers le nom complet de la classe.
|
||||||
|
$fileName = str_replace( APP_ROOT . 'src/', AutoLoader::PRIMARY_NAMESPACE, $fileName );
|
||||||
|
$fileName = str_replace( '.php', '', $fileName );
|
||||||
|
$fileName = str_replace( '/', '\\', $fileName );
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Vérifie que l'on est bien sur un Controller.
|
||||||
|
* Ignore Controller car Controller n'est pas une sous-classe de Controller.
|
||||||
|
*/
|
||||||
|
if( is_subclass_of( $fileName, Controller::class ) ){
|
||||||
|
$classes[] = $fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $classes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupérer toutes les routes des controllers récupéré avant.
|
||||||
|
* @return Route[]
|
||||||
|
*/
|
||||||
|
private static function fetchRoutes(): array {
|
||||||
|
|
||||||
|
$routes = [];
|
||||||
|
|
||||||
|
foreach( self::$controllers as $controllerClassString ){
|
||||||
|
$routes = array_merge( $routes, $controllerClassString::defineRoutes() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de savoir si la route que le client veut existe.
|
||||||
|
* @return Route|bool Retourne la route de l'utilisateur ou false si inexistante.
|
||||||
|
*/
|
||||||
|
private static function clientRouteExist(): Route|bool {
|
||||||
|
|
||||||
|
$clientRouteName = trim( self::$clientRouteString, '/' );
|
||||||
|
foreach( self::$routes as $route ){
|
||||||
|
$routeName = self::getRegexRoute( $route );
|
||||||
|
if( preg_match( $routeName, $clientRouteName, $matches ) ){
|
||||||
|
array_shift( $matches );
|
||||||
|
self::$clientRouteArguments = $matches;
|
||||||
|
return $route;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getRegexRoute( Route $route ): string {
|
||||||
|
$routeUrl = trim( $route->routeUrl, '/' );
|
||||||
|
foreach ( Kernel::$configs['route_arguments'] as $key => $value ){
|
||||||
|
$routeUrl = str_replace( $key, $value, $routeUrl );
|
||||||
|
}
|
||||||
|
return "#^{$routeUrl}$#";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Va permettre d'exécuter la méthode du Controller→action() en parsant les arguments si existants.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function executeRouteAction(): void {
|
||||||
|
$controller = self::$clientRoute->routeController;
|
||||||
|
$method = self::$clientRoute->routeAction;
|
||||||
|
|
||||||
|
new $controller()->$method( ...self::$clientRouteArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
194
src/Infrastructure/View.php
Normal file
194
src/Infrastructure/View.php
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Infrastructure;
|
||||||
|
|
||||||
|
use App\Exceptions\InvalidViewException;
|
||||||
|
use App\Http\Router;
|
||||||
|
use App\Kernel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de rendre une vue / contenu de la page.
|
||||||
|
* /!\ : Une seule vue peut-être appellé par page, hormis celle en plus pour le squelette.
|
||||||
|
*/
|
||||||
|
final class View {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Répertoire où se trouvent les vues.
|
||||||
|
*/
|
||||||
|
const string VIEW_PATH = APP_ROOT . 'views/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Le nom du fichier de vue.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public private(set) string $viewName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Les arguments passés à la vue.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public private(set) array $viewArgs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* La base de la vue. (Pour ne pas répéter header/footer à chaque fois).
|
||||||
|
* null correspond à pas de squelette.
|
||||||
|
*
|
||||||
|
* @var string|mixed|null
|
||||||
|
*/
|
||||||
|
public private(set) ?string $skeleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilisé par les squelettes, permet d'injecter des contenus comme le contenu de son enfant.
|
||||||
|
* @var array<string,string>
|
||||||
|
*/
|
||||||
|
public private(set) array $integratedContent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Garde la dernière instance de vue.
|
||||||
|
* Si il y a un squelette, ce sera l'instance du squelette.
|
||||||
|
*
|
||||||
|
* @var View
|
||||||
|
*/
|
||||||
|
private static self $lastInstance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de construire les informations de la vue.
|
||||||
|
*
|
||||||
|
* @param string $viewName Le nom du fichier de vue.
|
||||||
|
* @param array $viewArgs Les arguments de la vue.
|
||||||
|
* @param bool $autoRender Si la méthode $this->render() est automatiquement exécuté.
|
||||||
|
* @param string|null $skeleton Le squelette du fichier, null correspond à aucun squelette, default correspond au
|
||||||
|
* squelette défini dans la configuration des vues.
|
||||||
|
* @param array<string,string> $integratedContent Le contenu intégré dans le nouveau squelette.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
string $viewName,
|
||||||
|
array $viewArgs = [],
|
||||||
|
bool $autoRender = true,
|
||||||
|
?string $skeleton = 'default',
|
||||||
|
array $integratedContent = []
|
||||||
|
) {
|
||||||
|
|
||||||
|
if( !str_ends_with( $viewName, '.php' ) ) {
|
||||||
|
$viewName .= '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->viewName = $viewName;
|
||||||
|
$this->viewArgs = $viewArgs;
|
||||||
|
|
||||||
|
$this->skeleton = $skeleton;
|
||||||
|
if( $this->skeleton === "default" ){
|
||||||
|
$this->skeleton = Kernel::$configs['views']['base_view_skeleton'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->integratedContent = $integratedContent;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!file_exists(self::VIEW_PATH . $this->viewName)) {
|
||||||
|
throw new InvalidViewException($this->viewName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($autoRender) {
|
||||||
|
$this->render();
|
||||||
|
}
|
||||||
|
} catch (InvalidViewException $e) {
|
||||||
|
die( $e->getMessage() );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de démarrer le rendu d'une vue.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function startView(): void {
|
||||||
|
ob_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de finir le rendu d'une vue.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function endView(): void {
|
||||||
|
echo ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de faire le rendu proprement de la vue.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function render(): void {
|
||||||
|
|
||||||
|
self::$lastInstance = $this;
|
||||||
|
|
||||||
|
if( $this->skeleton === null ){
|
||||||
|
|
||||||
|
// Si on a pas de squelette, on inclut la vue.
|
||||||
|
// pour accéder aux éléments de la vue, on utilise les méthodes statiques de cette classe.
|
||||||
|
$this->startView();
|
||||||
|
require self::VIEW_PATH . $this->viewName;
|
||||||
|
$this->endView();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Si on a un squelette, on charge tout le contenu de la vue enfante.
|
||||||
|
$content = file_get_contents( self::VIEW_PATH . $this->viewName );
|
||||||
|
|
||||||
|
// On démarre la vue du squelette.
|
||||||
|
$base = new View( $this->skeleton, $this->viewArgs, skeleton: null, integratedContent: [ 'content' => $content ] );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VIEW TOOLS
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet d'injecter un contenu sauvegardé dans $this->integratedContent.
|
||||||
|
*
|
||||||
|
* @param string $integratedContentName
|
||||||
|
* @return void N'affiche rien si le contenu n'existe pas.
|
||||||
|
*/
|
||||||
|
public static function inject( string $integratedContentName ): void {
|
||||||
|
if( isset( self::$lastInstance->integratedContent[ $integratedContentName ] ) )
|
||||||
|
echo self::$lastInstance->integratedContent[ $integratedContentName ];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de récupérer un argument passé à la vue.
|
||||||
|
*
|
||||||
|
* @param string $argumentName
|
||||||
|
* @return mixed null si l'argument n'existe pas.
|
||||||
|
*/
|
||||||
|
public static function arg( string $argumentName ): mixed {
|
||||||
|
if( isset( self::$lastInstance->viewArgs[ $argumentName ] ) )
|
||||||
|
return self::$lastInstance->viewArgs[ $argumentName ];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet d'intégrer un bloc à la vue.
|
||||||
|
*
|
||||||
|
* @param string $partialName
|
||||||
|
* @return void N'affiche rien si le bloc n'existe pas.
|
||||||
|
*/
|
||||||
|
public static function partial( string $partialName ): void {
|
||||||
|
|
||||||
|
if( !str_ends_with( $partialName, '.php' ) ) {
|
||||||
|
$partialName .= '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
if( file_exists( self::VIEW_PATH . 'partials/' . $partialName ) ) {
|
||||||
|
require self::VIEW_PATH . 'partials/' . $partialName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getHeadTitle(): string {
|
||||||
|
$siteUrl = Kernel::$configs['general']['website_name'];
|
||||||
|
return Router::$clientRoute->routeName . ' - ' . $siteUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
97
src/Kernel.php
Normal file
97
src/Kernel.php
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
use App\Exceptions\ConfigFailedLoadingException;
|
||||||
|
use App\Exceptions\InvalidRouteException;
|
||||||
|
use App\Helpers\AutoLoader;
|
||||||
|
use App\Helpers\ConfigFactory;
|
||||||
|
use App\Http\Router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classe primaire du site.
|
||||||
|
* Permet de lancer l'application.
|
||||||
|
*/
|
||||||
|
final class Kernel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste des fichiers de configuration sous le format suivant :
|
||||||
|
* [ 'nom_de_la_config' -> [ 'cle" → valeur ] ]
|
||||||
|
*
|
||||||
|
* @var array<string,array>
|
||||||
|
*/
|
||||||
|
public private(set) static array $configs = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance actuelle de l'application.
|
||||||
|
* @var Kernel|null
|
||||||
|
*/
|
||||||
|
private static ?self $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Méthode qui permet de démarrer le site.
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function start(): self {
|
||||||
|
self::$instance = new self();
|
||||||
|
self::$instance->init();
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet d'obtenir l'instance actuelle du site.
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function getInstance(): self {
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructeur.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de préparer le démarrage du site.
|
||||||
|
* Lancé automatiquement par start().
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @see self::start()
|
||||||
|
*/
|
||||||
|
public function init(): void {
|
||||||
|
$this->buildAutoloader();
|
||||||
|
$this->loadConfig();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Router::routeTo();
|
||||||
|
} catch ( InvalidRouteException $e ){
|
||||||
|
die( $e->getMessage() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de mettre en place l'Autoloader.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function buildAutoloader(): void {
|
||||||
|
require_once 'Helpers/AutoLoader.php';
|
||||||
|
AutoLoader::register();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de charger tous les fichiers de configuration du site.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function loadConfig(): void {
|
||||||
|
try {
|
||||||
|
|
||||||
|
self::$configs['general'] = ConfigFactory::loadConfigFile('general');
|
||||||
|
self::$configs['route_arguments'] = ConfigFactory::loadConfigFile('route_arguments');
|
||||||
|
self::$configs['views'] = ConfigFactory::loadConfigFile('views');
|
||||||
|
|
||||||
|
} catch( ConfigFailedLoadingException $e ){
|
||||||
|
die( $e->getMessage() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
7
views/base.php
Normal file
7
views/base.php
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?php use App\Infrastructure\View as V; ?>
|
||||||
|
|
||||||
|
<?php V::partial( 'header' ); ?>
|
||||||
|
|
||||||
|
<?php V::inject( 'content'); ?>
|
||||||
|
|
||||||
|
<?php V::partial( 'footer' ); ?>
|
||||||
1
views/home.php
Normal file
1
views/home.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<h1>Coucou</h1>
|
||||||
4
views/partials/footer.php
Normal file
4
views/partials/footer.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php use App\Infrastructure\View as V; ?>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
9
views/partials/header.php
Normal file
9
views/partials/header.php
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php use App\Infrastructure\View as V; ?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><?php echo V::getHeadTitle(); ?></title>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
Reference in New Issue
Block a user