Plik routingu w JSON

Data: 2018-10-04, autor: Michał Misztal

Przygód z MVC ciąg dalszy. Zacząłem przyglądać się CodeIgniter i Django. W CodeIgniter podoba mi się to, że faktycznie jest niewielki i nie nakłania do tworzenia templatek które później i tak cały framework musi przemielić. Czyli faktycznie powinien być szybki, posiada obsługę baz danych, plik routingu, firewall, cache. Czego chcieć więcej. Do Django trzeba się przyzwyczaić bo to przecież na Pythonie stoi i nie wiadomo jak w naszym hostingowym środowisku będzie działać.

Jakiś tydzień temu przypadkowo przełączyli nas na nowy panel w Home.pl. Wszystko ładnie się błyszczy ale nie mam dostępu do SVN. Trafiłem na infolinię i pierwszy konsultant nie wiedział co to ten SVN jest więc przełączył mnie to tego co wiedział, a ten co wiedział to nie wiedział kiedy w nowym panelu się pojawi i czy się pojawi więc przełączył mnie do tego który to wiedział a ten który to wiedział nie odbierał telefonu. No, gdyby grali "Radar love" to może i poczekałbym 20 minut, ale nie grali tam nic z tych rzeczy, więc się rozłączyłem. Widże, że przez ssh mam dostęp do koment git ale nie będę zakładał płatnego konta na GitHubie dla kilku stronek które rocznie nie wygenerują tyle żeby mi się to zwróciło. Pozostaje więc SVN, może wróci.

Póki SVN jest nieobecny (w sensie w panelu, a przez ssh nie widzę by dało się to uruchomić) dostarajm mój framework. Jest prosty i szybki jak CodeIgniter. Co prawda nie posiada większości jego funkcjonalności ale hej, coś jest. Dziś na warsztat wziąłem plik routingu. Myślałem nad YAML ale ze względu na to, że nie ma go w standardowej instalacji PHP to sobie go daruję i utworzę pliki routingu jako JSON. Powiedzmy dla prostej strony mamy takie coś



{
    "index" : {
        "path" : "/",
        "controller" : "IndexController::index"
    },
    "downloadVcard" : {
        "path" : "/download/vcard",
        "controller" : "DownloadController::returnVcardFromFile"
    },
    "google site verify" : {
        "path" : "/google96748556dc1ee8cb.html",
        "controller" : "GoogleController::siteVerify"
    },
    "aktualnosci" : {
        "path" : "/aktualnosci/page",
        "controller" : "AktualnosciController::index",
        "var" : {
            "page" : "\\d+"
        }
    },
    "artykul" : {
        "path" : "/artykul/alias",
        "controller" : "AktualnosciController::artykul",
        "var" : {
            "alias" : "\\w+"
        }
    }
}

Z drobnymi różnicami wygląda tak jak w Symfony. Plusem jest to, że mogę wskazać kontroler i metodę która przetwarza dany url. Parsowanie pliku routingu zajmuje się poniższy kod



namespace Framework {

    class Route {

        protected $routeFile;
        protected $route;
        protected $paths;
        protected $url = "/";

        function __construct() {

            $this->parseUrl();
            $this->parseRouteFile();
            
            if($this->findRoute() == true){

            }else{

                $this->route = [
                    "name" => "error_404",
                    "path" => "/error/404",
                    "controller" => "ErrorController::index",
                    "var" => [
                        "errNumber" => "404"
                    ]
                ];
            }
            
            $this->getPathsFromRouteFile();
            $this->execController();
            
            return true;
        }

        protected function parseUrl() {
            $url = filter_var($_GET['url'], FILTER_SANITIZE_STRING);
            $this->url = '/' . $url;
        }

        protected function parseRouteFile(): bool {
            $routesFileName = '.config/routes.json';

            if (file_exists($routesFileName)) {
                $this->routeFile = json_decode(file_get_contents($routesFileName), true);
                return true;
            }

            return false;
        }

        protected function findRoute() : bool {
            
            $ret = false;

            foreach ($this->routeFile as $k => $v) {

                $pattern = stripslashes($v['path']);

                if (isset($v['var'])) {
                    foreach ($v['var'] as $key => $value) {
                        $pattern = str_replace($key, $value, $pattern);
                    }
                }

                $pattern = preg_replace('/\//', '\\\/', $pattern);

                if (preg_match('/' . $pattern . '/', $this->url)) {
                    $this->route = [
                        "name" => $k,
                        "path" => $v['path'],
                        "controller" => $v['controller']
                    ];

                    $explUrl = preg_split('/(\/|\,)/', $this->url);
                    $explPath = preg_split('/(\/|\,)/', $this->route['path']);

                    for ($i = 0; $i < count($explPath); $i++) {
                        $var[$explPath[$i]] = $explUrl[$i];
                    }

                    $this->route['var'] = $var;
                }
            }
            
            if($this->url === $this->route['path']) $ret = true;
                        
            return $ret;
        }

        protected function execController() {
            $tmp = explode('::', $this->route["controller"]);

            $controllerName = $tmp[0];
            $initMethod = $tmp[1];
            
            $fileName = "controllers/{$controllerName}.php";
            $modelName = preg_replace('/Controller/', '', $controllerName);
            
            if (file_exists($fileName)) {
                $controller = new $controllerName;
                $controller->loadModel($modelName);
                $controller->$initMethod($this->route);
            }
        }

        protected function getPathsFromRouteFile(): bool {
            $var = null;

            foreach ($this->routeFile as $k => $v) {
                $var[$k] = $v['path'];
            }

            $this->route['paths'] = $var;
            return true;
        }

    }

}

Powyższy kod parsuje cały plik routingu. Jak widzicie nie ma tutaj jeszcze żadnych zabezpieczeń (no prawie) ale całość istnieje (jeszcze) tylko na localhoście. Dodatkowo parsują mi się linki. Do /download/vcard mogę się odwołać przez $this->args['paths']['downloadVcard']. Do takiego pliku można jeszcze dodać np tytuł strony, znaczniki słów kluczowych, opisu czy breadcrumbs. Można ale niekoniecznie trzeba. Ale można.

Skomentuj lub zgłoś błąd

© Michał Misztal 2018