vendor\klio\klio-bundle\src\Symfony\RouteController.php line 131

  1. <?php
  2. namespace Klio\KlioBundle\Symfony;
  3. use voku\helper\HtmlMin;
  4. use App\Controller\Routes;
  5. use Klio\KlioBundle\Form\Form;
  6. use Klio\KlioBundle\Files\File;
  7. use Klio\KlioBundle\Files\Image;
  8. use Klio\KlioBundle\Security\Hash;
  9. use Klio\KlioBundle\Security\Crypt;
  10. use Klio\KlioBundle\Security\ClientIp;
  11. use Klio\KlioBundle\Files\UploadedFile;
  12. use Klio\KlioBundle\FileSystem\FsUtils;
  13. use Symfony\Component\HttpFoundation\Request;
  14. use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface;
  15. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  16. class RouteController extends AbstractController
  17. {
  18.     private Request $request;
  19.     private HtmlSanitizerInterface $sanitizer;
  20.     protected Controller $controller;
  21.     /**
  22.      * les constantes 
  23.      */
  24.     /**
  25.      * On empèche de créer d'autres propriétées que celes d'origine
  26.      *
  27.      * @param [type] $name
  28.      * @param [type] $value
  29.      */
  30.     public function __set($name$value)
  31.     {
  32.         throw new \Exception("Cannot add or change property \$$name to instance of " __CLASS__);
  33.     }
  34.     /**
  35.      * Configuration initiale et commune à toutes les routes, création des constantes
  36.      *
  37.      * @param string $configFile Fichier de config complémentaire optionnel
  38.      * @return void
  39.      */
  40.     public function init(string $configFile ""): void
  41.     {
  42.         $this->request $this->container->get('request_stack')->getCurrentRequest();
  43.         $this->controller = new Controller($this);
  44.         global $SESSION;
  45.         $SESSION $this->request->getSession();
  46.         // on defini une constante ROOT qui donne le path vers 
  47.         define('__ROOT__'FsUtils::localPathSanitizer($this->getParameter('kernel.project_dir')));
  48.         define('__PRIVATE__'__ROOT__ '/private');
  49.         // l'hôte du site
  50.         define('__HOST__'$_SERVER['SERVER_NAME']);
  51.         // mode DEV ?
  52.         define('__DEV__'$_ENV["APP_ENV"] === "dev");
  53.         // mode recette ?
  54.         define("__RECETTE__", @$_ENV['APP_MODE'] === 'recette');
  55.         //Import des variables d'environnement du fichier de conf du store 
  56.         $store $_ENV['STORE_FOLDER'];
  57.         if (substr($store01) == "/" && substr($store02) != "//" &&  substr($store06) != "/Users"$store __ROOT__ $store;
  58.         define("__STORE_FOLDER__",  $store);
  59.         define('__STORE__'$store);
  60.         unset($store);
  61.         // upload folder, dossier ou on stocke tous les fichiers téléchargés
  62.         define('__UPLOAD__'__STORE__ '/upload');
  63.         define('__APP_USER__'$_ENV["APP_USER"]);
  64.         define('__PHILIPPE__'$_ENV['APP_USER'] === "philippe");
  65.         define('__SAMI__'$_ENV['APP_USER'] === "sami");
  66.         // temp folder
  67.         $temp $_ENV['TEMP_FOLDER'];
  68.         if (substr($temp01) == "/" && substr($temp06) != "/Users"$temp __ROOT__ $temp;
  69.         define('__TEMP__'$temp);
  70.         unset($temp);
  71.         include_once(__STORE_FOLDER__ '/Config.php');
  72.         // les éléments liés à l'IP /Klio/Scecurity/ClientIP
  73.         $IP = new ClientIp();
  74.         // l'IP du client
  75.         define("__IP__"$IP->getIp());
  76.         // est-ce une IP KLIO ?
  77.         define("__ISKLIO__"$IP->isKlio());
  78.         // est-ce une IP CAF ?
  79.         define("__ISCAF__"$IP->isCaf());
  80.         // est-ce une IP audit ?
  81.         define("__ISAUDIT__"$IP->isAudit());
  82.         // est-ce une IP de confiance ?
  83.         define("__SECUREIP__"$IP->isSecure()); //
  84.         // desctruction de l'objet une fois les constantes définies
  85.         unset($IP);
  86.         // clé de salage pour les fonctions de cryptographie 
  87.         define('__SALT__''Phu42J;/Q7kBv+8n9zSMxZ+ekAR+EKQc7ZzRéYezz/8twk0F91jD/mq0J,DjC4!ON6%*5tïfZ7sçlg');
  88.         // l'URL appelée par le client, si elle se termine par un slash, on enève le slash final
  89.         $url $this->request->getPathInfo();
  90.         define('__USER_URL__'$url); // on garde le slash final pour le mode compatibilité avec le FW7 et le FW4 
  91.         if (substr($url, -1) === "/"$url substr($url0, -1);
  92.         define('__URL__'$url);
  93.         // le chemin local, dans /private qui correspond à l'URL
  94.         define('__PATH__'FsUtils::routeSanitizer($this->request->getPathInfo()));
  95.         define('__PATHDIR__'FsUtils::dirSanitizer($this->request->getPathInfo()));
  96.         // public folder path
  97.         define('__PUBLIC__'getcwd());
  98.         // token de session
  99.         if (!$SESSION->get("TOKEN")) {
  100.             $SESSION->set("TOKEN"Hash::uuid62());
  101.         }
  102.         define('__TOKEN__'$SESSION->get("TOKEN"));
  103.         //
  104.         if (!$SESSION->get("CRYPTOKEY")) {
  105.             $SESSION->set("CRYPTOKEY"Crypt::newKey());
  106.         }
  107.         define('__CRYPTOKEY__'$SESSION->get("CRYPTOKEY"));
  108.     }
  109.     /**
  110.      * On traite toutes les variables pour ortenir une reponse soit par du code direct, soit via un twig
  111.      * Cette fonction sert de setter aux deux éléments essentiels : ->response et ->status
  112.      *
  113.      * @return void
  114.      */
  115.     protected function routeResponse()
  116.     {
  117.         /**
  118.          * __URL__ = l'URL saisie
  119.          * les chemins de traitement d'image ou de fichiers
  120.          * on les traite avant les pages car ils peuvet  avoir des caractères spéciaux dans l'URL ( Exemple : /pdf/newsletter/Newsletter%20Janvier.pdf)
  121.          */
  122.         if (stripos(__URL__'/i/') === 0) {
  123.             /* 
  124.             fontion image
  125.             /i/folder/hash/format/nom_de_l_image.jpg ou /i/folder/hash/nom_de_l_image.jpg
  126.             format : 0x200 , c200x200, c0x200o0x0b100x200
  127.             c = crop
  128.             o = origin
  129.             b = box
  130.             dossier non sécure, on met l'image en cache
  131.             dossier secure, on utilise obligatoirement un php avec test d'accès
  132.             */
  133.             // on peut avoir une fonction personnalisée dans un controller à la racine de private /private/_file/Controller.php
  134.             if (!file_exists(__PRIVATE__ '/_config/_image/Controller.php')) {
  135.                 Image::getImage(__URL__);
  136.                 exit();
  137.             }
  138.         }
  139.         // téléchargement forcé (image ou pdf ne s'ouvre pas dans le navigateur mais en téléchargement)
  140.         if (stripos(__URL__'/d/') === 0) {
  141.             // on peut avoir une fonction personnalisée dans un controller à la racine de private /private/_download/Controller.php
  142.             if (!file_exists(__PRIVATE__ '/_config/routes/_d/Controller.php')) {
  143.                 $file = new File(__URL__);
  144.                 $file->download();
  145.             } else $this->controller->setController('/_config/routes/_d/Controller.php');
  146.         }
  147.         // chargement de fichier (image ou pdf s'ouvre dans le navigateur)
  148.         if (stripos(__URL__'/f/') === 0) {
  149.             // on peut avoir une fonction personnalisée dans un controller à la racine de private /private/_file/Controller.php
  150.             if (!file_exists(__PRIVATE__ '/_config/routes/_f/Controller.php')) {
  151.                 $file = new File(__URL__);
  152.                 $file->stream();
  153.             } else $this->controller->setController('/_config/routes/_f/Controller.php');
  154.         }
  155.         // suppression de fichier 
  156.         if (stripos(__URL__'/delf/') === 0) {
  157.             // on peut avoir une fonction personnalisée dans un controller à la racine de private /private/_file/Controller.php
  158.             if (file_exists(__PRIVATE__ '/_config/routes/_delf/Controller.php')) {
  159.                 $this->controller->setController('/_config/routes/_delf/Controller.php');
  160.             } else die("Error DELFILE");
  161.         }
  162.         // fichier PDF, éventuellement créé à la volée
  163.         if (stripos(__URL__'/p/') === 0) {
  164.             /* 
  165.             function pdf 
  166.             /p/folder/hash/nom_de_fichier.pdf ou /p/folder/fonction_de_generation_de_pdf/id_source/nom_de_fichier.pdf
  167.             */
  168.         }
  169.         // on teste l'url pour vérifier si elle est
  170.         if (!$this->checkPath()) {
  171.             $this->controller->routeCancel true// on marque la route comme trouvée pour ne pas chercher 
  172.             $this->send404(); // on renvoie d'office un 404 notfound 
  173.         }
  174.         /*
  175.          * si la route est propre, on inclut le code du controller
  176.          * soit celui par défaut (Controller.php dans le dossier /private qui correspond à __PATH__), soit celui qui a été indiqué via $this>setController()
  177.          * la fonction includePrivateController dira s'occupe de definir $this->controller->routeFound
  178.          */ else $this->controller->includePrivateController();
  179.     }
  180.     /** 
  181.      *  
  182.      * dans FsUtils::routeSanitizer on définie une constante __PATH__ "propre". L'URL sans aucun caractère spéciaux (uniquement A-Za-z0-9_-)
  183.      * __PATH__ est censé donner le chemin dans /private
  184.      * URL et PATH doivent être identiques
  185.      * si l'URL n'est pas propre, on considère que c'est une tentative de hack, on bloque la demande   
  186.      * 
  187.      * @return void  */
  188.     protected function checkPath(): bool
  189.     {
  190.         $path __PATH__;
  191.         $url __URL__;
  192.         // upload de fichier V4
  193.         if (strpos(__URL__'/upload/files/') === 0) return true;
  194.         // si l'URL commence par /FW7/ ou /FW4/, on est en mode compatibilité, on a le droit d'appeller directement un fichier php uniquement. 
  195.         if (
  196.             (strpos(__URL__'/FW4/') === 0
  197.                 or strpos(__URL__'/FW7/') === 0
  198.                 or strpos(__URL__'/k4/') === 0
  199.             ) and strtolower(substr(__URL__, -4)) === '.php'
  200.         ) {
  201.             return true;
  202.         }
  203.         // les chemins spéciaux : /d/ /f/ /i/
  204.         if (
  205.             (stripos(__URL__'/d/') === 0
  206.                 or stripos(__URL__'/i/') === 0
  207.                 or stripos(__URL__'/f/') === 0
  208.                 or stripos(__URL__'/p/') === 0
  209.             )
  210.         ) return true;
  211.         // cas particulier pour la home
  212.         if ($path === '/' and $url == '') return true;
  213.         // sinon, on doit être égal
  214.         //dump($path);
  215.         // dd($url);
  216.         return ($path === $url);
  217.     }
  218.     protected function routeFound(): bool
  219.     {
  220.         return $this->controller->getRouteFound();
  221.     }
  222.     /**
  223.      * Défini le fichier php qui va être include dans l'objet Controller pour y ajouter une logique
  224.      *
  225.      * @param string $controllerFile Le path/name du .php
  226.      * @return string
  227.      */
  228.     protected function setController(string $controllerFile): string
  229.     {
  230.         $this->controller->setController($controllerFile);
  231.         return $this->controller->getController();
  232.     }
  233.     protected function addController(string $controllerFile): array
  234.     {
  235.         $this->controller->addController($controllerFile);
  236.         return $this->controller->getControllers();
  237.     }
  238.     protected function getResponse(): string
  239.     {
  240.         $this->routeResponse();
  241.         $this->routeRender();
  242.         return $this->controller->getResponse();
  243.     }
  244.     protected function getStatus(): string
  245.     {
  246.         return $this->controller->getStatus();
  247.     }
  248.     private function encore()
  249.     {
  250.         // on init les deux tableaux pour les JS et les CSS
  251.         $encore = [];
  252.         $encore['encore_js'] = [];
  253.         $encore['encore_css'] = [];
  254.         $jsArray $this->controller->getJs();
  255.         foreach ($jsArray as $idx => $js) {
  256.             if (!str_starts_with($js'/')) $js $this->controller->getControllerPath() . '/' $js;
  257.             $js str_replace('//''/'$js);
  258.             $jsArray[$idx] = $js;
  259.             $encore['encore_js'][] = md5("./private" $js);
  260.         }
  261.         $this->controller->setJs($jsArray);
  262.         $cssArray $this->controller->getCss();
  263.         foreach ($cssArray as $idx => $css) {
  264.             if (!str_starts_with($css'/')) $css $this->controller->getControllerPath() . '/' $css;
  265.             $css str_replace('//''/'$css);
  266.             $cssArray[$idx] = $css;
  267.             $encore['encore_css'][] = md5("./private" $css);
  268.         }
  269.         $this->controller->setCss($cssArray);
  270.         $this->controller->addTwig($encore);
  271.     }
  272.     /**
  273.      * 
  274.      * Faire le rendu du Twig
  275.      * 
  276.      * par défaut le TWIG, c'est le fichier Template.twig dans le même dossier que le Controller.php
  277.      * mais on peut dans le controller choisir d'appeller un autre TWIG
  278.      * en renseigant $RESPONSE->template en donnant au choix
  279.      * un chemin absolu qui commence par 
  280.      * Exemple /test/tmp.twig  va chercher dans /private/test/tmp.twig ou /templates/test/tmp.twig
  281.      * Exemple /tmp.twig  va chercher dans /private/tmp.twig ou /templates/tmp.twig
  282.      * un chemin relatif sans / : Autre.twig pour chercher dans le même dosssier que le Controller.php
  283.      * Exemple : Template2.twig va chercher dans le même dosssier que le controller le fichier Template2.twig
  284.      * 
  285.      */
  286.     public function routeRender()
  287.     {
  288.         // si on a déjà une réponse via getResponse, pas besoin d'aller chercher le twig
  289.         // le twig n'est là que pour remplir controller->response
  290.         if ($this->controller->getResponse()) return true;
  291.         // à ce stade, si la route n'est pas trouvée, on renvoi un 404
  292.         if (!$this->controller->getRouteFound()) $this->send404();
  293.         // le template
  294.         $template $this->controller->getTemplate();
  295.         if (!$template) {
  296.             $path $this->controller->getControllerPath();
  297.             if ($this->routeFound()) $template = ($path == '/' $path $path '/') . "Template.twig";
  298.         } else if (!str_starts_with($template'/')) $template $this->controller->getControllerPath() . "/" $template;
  299.         $this->controller->setTemplate($template);
  300.         // les listes de JS et les CSS associés
  301.         $this->encore();
  302.         // le rendu du template pour obtenir la réponse
  303.         $this->controller->setResponse($this->renderView($this->controller->getTemplate(), $this->controller->getTwig()));
  304.         $this->outputBuffer();
  305.         //if (__DEV__) dump($this->controller);
  306.     }
  307.     /**
  308.      * mise à jour du code initial en le faisant passer à travers un buffer de sortie
  309.      *
  310.      * @param Controller &$CONTROLLER
  311.      * @param string $template
  312.      * @return void
  313.      */
  314.     private function outputBuffer()
  315.     {
  316.         $response $this->controller->getResponse();
  317.         //on charge SimpleDomHtml
  318.         //require_once __FW__ . '/php/libs/simplehtmldom_1_9_1/simple_html_dom.php';
  319.         //require_once __ROOT__ . '/vendor/simplehtmldom/simplehtmldom/simple_html_dom.php';
  320.         require_once $_ENV['PACKAGES_FOLDER'] . "/frameworks/simplehtmldom_1_9_1/simple_html_dom.php";
  321.         $DOM str_get_html($response$lowercase true$forceTagsClosed true$target_charset DEFAULT_TARGET_CHARSET$stripRN false$defaultBRText DEFAULT_BR_TEXT$defaultSpanText DEFAULT_SPAN_TEXT);
  322.         // traitement du form
  323.         // on fait passer le code de sortie à travers cette fonction pour changer le code à l'intérieur des form
  324.         if ($DOM) {
  325.             foreach ($DOM->find('form') as $form) {
  326.                 if (!$form->getAttribute('nobuffer')) new Form($form$DOM);
  327.             }
  328.             // si l'objet response à sa propre function de buffer (pour un traitement spécifique du code de sortie)
  329.             // on appelle cette fonction
  330.             if ($this->controller->getBuffer() and is_callable('\App\Controller\OutputBuffers::' $this->controller->getBuffer())) {
  331.                 //call_user_func('\App\Controller\OutputBuffers::' . $this->controller->getBuffer(), $DOM, $this->controller);
  332.             }
  333.             /**
  334.              * on sauve le résultat modifié dans le code de sortie de l'objet response
  335.              */
  336.             $response $DOM->save();
  337.             $DOM->clear();
  338.             unset($DOM);
  339.             //$htmlMin = new HtmlMin();
  340.             //$response = $htmlMin->minify($response);
  341.             // 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
  342.             // on remet donc les balises juste pour ce besoin
  343.             //if (__DEV__ and stripos($response, '<html>') !== false) $response .= '</body></html>';
  344.         }
  345.         $this->controller->setResponse($response);
  346.     }
  347.     /**
  348.      * Annule la route et envoie un 404
  349.      *
  350.      * @return bool
  351.      */
  352.     public function send404(): void
  353.     {
  354.         $this->controller->setStatus(404);
  355.         $this->controller->setTemplate('/404.twig');
  356.         $this->controller->setTwig(["status" => "404""message" => "Page introuvable."]);
  357.         if (is_callable('App\Controller\Routes::notFound')) Routes::notFound($this->controller);
  358.     }
  359. }