vendor\klio\klio-bundle\src\Symfony\RouteController.php line 127
<?php
namespace Klio\KlioBundle\Symfony;
use voku\helper\HtmlMin;
use App\Controller\Routes;
use Klio\KlioBundle\Form\Form;
use Klio\KlioBundle\Files\File;
use Klio\KlioBundle\Files\Image;
use Klio\KlioBundle\Security\Hash;
use Klio\KlioBundle\Security\Crypt;
use Klio\KlioBundle\Security\ClientIp;
use Klio\KlioBundle\Files\UploadedFile;
use Klio\KlioBundle\FileSystem\FsUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class RouteController extends AbstractController
{
private Request $request;
private HtmlSanitizerInterface $sanitizer;
protected Controller $controller;
/**
* les constantes
*/
/**
* On empèche de créer d'autres propriétées que celes d'origine
*
* @param [type] $name
* @param [type] $value
*/
public function __set($name, $value)
{
throw new \Exception("Cannot add or change property \$$name to instance of " . __CLASS__);
}
/**
* Configuration initiale et commune à toutes les routes, création des constantes
*
* @param string $configFile Fichier de config complémentaire optionnel
* @return void
*/
public function init(string $configFile = ""): void
{
$this->request = $this->container->get('request_stack')->getCurrentRequest();
$this->controller = new Controller($this);
global $SESSION;
$SESSION = $this->request->getSession();
// on defini une constante ROOT qui donne le path vers
define('__ROOT__', FsUtils::localPathSanitizer($this->getParameter('kernel.project_dir')));
define('__PRIVATE__', __ROOT__ . '/private');
// l'hôte du site
define('__HOST__', $_SERVER['SERVER_NAME']);
// mode DEV ?
define('__DEV__', $_ENV["APP_ENV"] === "dev");
// mode recette ?
define("__RECETTE__", @$_ENV['APP_MODE'] === 'recette');
//Import des variables d'environnement du fichier de conf du store
$store = $_ENV['STORE_FOLDER'];
if (substr($store, 0, 1) == "/" && substr($store, 0, 2) != "//" && substr($store, 0, 6) != "/Users") $store = __ROOT__ . $store;
define("__STORE_FOLDER__", $store);
define('__STORE__', $store);
unset($store);
// upload folder, dossier ou on stocke tous les fichiers téléchargés
define('__UPLOAD__', __STORE__ . '/upload');
define('__APP_USER__', $_ENV["APP_USER"]);
define('__PHILIPPE__', $_ENV['APP_USER'] === "philippe");
define('__SAMI__', $_ENV['APP_USER'] === "sami");
// temp folder
$temp = $_ENV['TEMP_FOLDER'];
if (substr($temp, 0, 1) == "/" && substr($temp, 0, 6) != "/Users") $temp = __ROOT__ . $temp;
define('__TEMP__', $temp);
unset($temp);
include_once(__STORE_FOLDER__ . '/Config.php');
// les éléments liés à l'IP /Klio/Scecurity/ClientIP
$IP = new ClientIp();
// l'IP du client
define("__IP__", $IP->getIp());
// est-ce une IP KLIO ?
define("__ISKLIO__", $IP->isKlio());
// est-ce une IP CAF ?
define("__ISCAF__", $IP->isCaf());
// est-ce une IP audit ?
define("__ISAUDIT__", $IP->isAudit());
// est-ce une IP de confiance ?
define("__SECUREIP__", $IP->isSecure()); //
// desctruction de l'objet une fois les constantes définies
unset($IP);
// clé de salage pour les fonctions de cryptographie
define('__SALT__', 'Phu42J;/Q7kBv+8n9zSMxZ+ekAR+EKQc7ZzRéYezz/8twk0F91jD/mq0J,DjC4!ON6%*5tïfZ7sçlg');
// l'URL appelée par le client, si elle se termine par un slash, on enève le slash final
$url = $this->request->getPathInfo();
define('__USER_URL__', $url); // on garde le slash final pour le mode compatibilité avec le FW7 et le FW4
if (substr($url, -1) === "/") $url = substr($url, 0, -1);
define('__URL__', $url);
// le chemin local, dans /private qui correspond à l'URL
define('__PATH__', FsUtils::routeSanitizer($this->request->getPathInfo()));
define('__PATHDIR__', FsUtils::dirSanitizer($this->request->getPathInfo()));
// public folder path
define('__PUBLIC__', getcwd());
// token de session
if (!$SESSION->get("TOKEN")) {
$SESSION->set("TOKEN", Hash::uuid62());
}
define('__TOKEN__', $SESSION->get("TOKEN"));
//
if (!$SESSION->get("CRYPTOKEY")) {
$SESSION->set("CRYPTOKEY", Crypt::newKey());
}
define('__CRYPTOKEY__', $SESSION->get("CRYPTOKEY"));
}
/**
* On traite toutes les variables pour ortenir une reponse soit par du code direct, soit via un twig
* Cette fonction sert de setter aux deux éléments essentiels : ->response et ->status
*
* @return void
*/
protected function routeResponse()
{
/**
* __URL__ = l'URL saisie
* les chemins de traitement d'image ou de fichiers
* on les traite avant les pages car ils peuvet avoir des caractères spéciaux dans l'URL ( Exemple : /pdf/newsletter/Newsletter%20Janvier.pdf)
*/
if (stripos(__URL__, '/i/') === 0) {
/*
fontion image
/i/folder/hash/format/nom_de_l_image.jpg ou /i/folder/hash/nom_de_l_image.jpg
format : 0x200 , c200x200, c0x200o0x0b100x200
c = crop
o = origin
b = box
dossier non sécure, on met l'image en cache
dossier secure, on utilise obligatoirement un php avec test d'accès
*/
// on peut avoir une fonction personnalisée dans un controller à la racine de private /private/_file/Controller.php
if (!file_exists(__PRIVATE__ . '/_config/_image/Controller.php')) {
Image::getImage(__URL__);
exit();
}
}
// téléchargement forcé (image ou pdf ne s'ouvre pas dans le navigateur mais en téléchargement)
if (stripos(__URL__, '/d/') === 0) {
// on peut avoir une fonction personnalisée dans un controller à la racine de private /private/_download/Controller.php
if (!file_exists(__PRIVATE__ . '/_config/routes/_d/Controller.php')) {
$file = new File(__URL__);
$file->download();
} else $this->controller->setController('/_config/routes/_d/Controller.php');
}
// chargement de fichier (image ou pdf s'ouvre dans le navigateur)
if (stripos(__URL__, '/f/') === 0) {
// on peut avoir une fonction personnalisée dans un controller à la racine de private /private/_file/Controller.php
if (!file_exists(__PRIVATE__ . '/_config/routes/_f/Controller.php')) {
$file = new File(__URL__);
$file->stream();
} else $this->controller->setController('/_config/routes/_f/Controller.php');
}
// suppression de fichier
if (stripos(__URL__, '/delf/') === 0) {
// on peut avoir une fonction personnalisée dans un controller à la racine de private /private/_file/Controller.php
if (file_exists(__PRIVATE__ . '/_config/routes/_delf/Controller.php')) {
$this->controller->setController('/_config/routes/_delf/Controller.php');
} else die("Error DELFILE");
}
// fichier PDF, éventuellement créé à la volée
if (stripos(__URL__, '/p/') === 0) {
/*
function pdf
/p/folder/hash/nom_de_fichier.pdf ou /p/folder/fonction_de_generation_de_pdf/id_source/nom_de_fichier.pdf
*/
}
// on teste l'url pour vérifier si elle est
if (!$this->checkPath()) {
$this->controller->routeCancel = true; // on marque la route comme trouvée pour ne pas chercher
$this->send404(); // on renvoie d'office un 404 notfound
}
/*
* si la route est propre, on inclut le code du controller
* soit celui par défaut (Controller.php dans le dossier /private qui correspond à __PATH__), soit celui qui a été indiqué via $this>setController()
* la fonction includePrivateController dira s'occupe de definir $this->controller->routeFound
*/ else $this->controller->includePrivateController();
}
/**
*
* dans FsUtils::routeSanitizer on définie une constante __PATH__ "propre". L'URL sans aucun caractère spéciaux (uniquement A-Za-z0-9_-)
* __PATH__ est censé donner le chemin dans /private
* URL et PATH doivent être identiques
* si l'URL n'est pas propre, on considère que c'est une tentative de hack, on bloque la demande
*
* @return void */
protected function checkPath(): bool
{
$path = __PATH__;
$url = __URL__;
// upload de fichier V4
if (strpos(__URL__, '/upload/files/') === 0) return true;
// si l'URL commence par /FW7/ ou /FW4/, on est en mode compatibilité, on a le droit d'appeller directement un fichier php uniquement.
if (
(strpos(__URL__, '/FW4/') === 0
or strpos(__URL__, '/FW7/') === 0
or strpos(__URL__, '/k4/') === 0
) and strtolower(substr(__URL__, -4)) === '.php'
) {
return true;
}
// les chemins spéciaux : /d/ /f/ /i/
if (
(stripos(__URL__, '/d/') === 0
or stripos(__URL__, '/i/') === 0
or stripos(__URL__, '/f/') === 0
or stripos(__URL__, '/p/') === 0
)
) return true;
// cas particulier pour la home
if ($path === '/' and $url == '') return true;
// sinon, on doit être égal
//dump($path);
// dd($url);
return ($path === $url);
}
protected function routeFound(): bool
{
return $this->controller->getRouteFound();
}
/**
* Défini le fichier php qui va être include dans l'objet Controller pour y ajouter une logique
*
* @param string $controllerFile Le path/name du .php
* @return string
*/
protected function setController(string $controllerFile): string
{
$this->controller->setController($controllerFile);
return $this->controller->getController();
}
protected function addController(string $controllerFile): array
{
$this->controller->addController($controllerFile);
return $this->controller->getControllers();
}
protected function getResponse(): string
{
$this->routeResponse();
$this->routeRender();
return $this->controller->getResponse();
}
protected function getStatus(): string
{
return $this->controller->getStatus();
}
private function encore()
{
// on init les deux tableaux pour les JS et les CSS
$encore = [];
$encore['encore_js'] = [];
$encore['encore_css'] = [];
$jsArray = $this->controller->getJs();
foreach ($jsArray as $idx => $js) {
if (!str_starts_with($js, '/')) $js = $this->controller->getControllerPath() . '/' . $js;
$js = str_replace('//', '/', $js);
$jsArray[$idx] = $js;
$encore['encore_js'][] = md5("./private" . $js);
}
$this->controller->setJs($jsArray);
$cssArray = $this->controller->getCss();
foreach ($cssArray as $idx => $css) {
if (!str_starts_with($css, '/')) $css = $this->controller->getControllerPath() . '/' . $css;
$css = str_replace('//', '/', $css);
$cssArray[$idx] = $css;
$encore['encore_css'][] = md5("./private" . $css);
}
$this->controller->setCss($cssArray);
$this->controller->addTwig($encore);
}
/**
*
* Faire le rendu du Twig
*
* par défaut le TWIG, c'est le fichier Template.twig dans le même dossier que le Controller.php
* mais on peut dans le controller choisir d'appeller un autre TWIG
* en renseigant $RESPONSE->template en donnant au choix
* un chemin absolu qui commence par
* Exemple /test/tmp.twig va chercher dans /private/test/tmp.twig ou /templates/test/tmp.twig
* Exemple /tmp.twig va chercher dans /private/tmp.twig ou /templates/tmp.twig
* un chemin relatif sans / : Autre.twig pour chercher dans le même dosssier que le Controller.php
* Exemple : Template2.twig va chercher dans le même dosssier que le controller le fichier Template2.twig
*
*/
public function routeRender()
{
// si on a déjà une réponse via getResponse, pas besoin d'aller chercher le twig
// le twig n'est là que pour remplir controller->response
if ($this->controller->getResponse()) return true;
// à ce stade, si la route n'est pas trouvée, on renvoi un 404
if (!$this->controller->getRouteFound()) $this->send404();
// le template
$template = $this->controller->getTemplate();
if (!$template) {
$path = $this->controller->getControllerPath();
if ($this->routeFound()) $template = ($path == '/' ? $path : $path . '/') . "Template.twig";
} else if (!str_starts_with($template, '/')) $template = $this->controller->getControllerPath() . "/" . $template;
$this->controller->setTemplate($template);
// les listes de JS et les CSS associés
$this->encore();
// le rendu du template pour obtenir la réponse
$this->controller->setResponse($this->renderView($this->controller->getTemplate(), $this->controller->getTwig()));
$this->outputBuffer();
//if (__DEV__) dump($this->controller);
}
/**
* mise à jour du code initial en le faisant passer à travers un buffer de sortie
*
* @param Controller &$CONTROLLER
* @param string $template
* @return void
*/
private function outputBuffer()
{
$response = $this->controller->getResponse();
//on charge SimpleDomHtml
//require_once __FW__ . '/php/libs/simplehtmldom_1_9_1/simple_html_dom.php';
//require_once __ROOT__ . '/vendor/simplehtmldom/simplehtmldom/simple_html_dom.php';
require_once $_ENV['PACKAGES_FOLDER'] . "/frameworks/simplehtmldom_1_9_1/simple_html_dom.php";
$DOM = str_get_html($response, $lowercase = true, $forceTagsClosed = true, $target_charset = DEFAULT_TARGET_CHARSET, $stripRN = false, $defaultBRText = DEFAULT_BR_TEXT, $defaultSpanText = DEFAULT_SPAN_TEXT);
// traitement du form
// on fait passer le code de sortie à travers cette fonction pour changer le code à l'intérieur des form
if ($DOM) {
foreach ($DOM->find('form') as $form) {
if (!$form->getAttribute('nobuffer')) new Form($form, $DOM);
}
// si l'objet response à sa propre function de buffer (pour un traitement spécifique du code de sortie)
// on appelle cette fonction
if ($this->controller->getBuffer() and is_callable('\App\Controller\OutputBuffers::' . $this->controller->getBuffer())) {
//call_user_func('\App\Controller\OutputBuffers::' . $this->controller->getBuffer(), $DOM, $this->controller);
}
/**
* on sauve le résultat modifié dans le code de sortie de l'objet response
*/
$response = $DOM->save();
$DOM->clear();
unset($DOM);
//$htmlMin = new HtmlMin();
//$response = $htmlMin->minify($response);
// le minifier de voku supprime les balises </body></html> ce qui est correct au sens du html5 mais enmpèche symfony de positionner la debug bar
// on remet donc les balises juste pour ce besoin
//if (__DEV__ and stripos($response, '<html>') !== false) $response .= '</body></html>';
}
$this->controller->setResponse($response);
}
/**
* Annule la route et envoie un 404
*
* @return bool
*/
public function send404(): void
{
$this->controller->setStatus(404);
$this->controller->setTemplate('/404.twig');
$this->controller->setTwig(["status" => "404", "message" => "Page introuvable."]);
if (is_callable('App\Controller\Routes::notFound')) Routes::notFound($this->controller);
}
}