Router.php

<?php

namespace Tiny;

class Router implements \Tiny\IFace\Router {

    
    protected $dir;
    protected $base;
    protected $fileMap;
    
    public function __construct($pubDir,$baseUrl='/'){
        $this->dir = $pubDir;
        $this->base = $baseUrl;
        $this->fileMap = $this->fileMap($pubDir);
    }

    public function filePath($url='/'){
        $base = $this->base;
        $relativeUrl = $this->relativePath($url);
        if (!$relativeUrl)return FALSE;
        
        // var_dump("base:".$this->base);
        $fileKey = $this->getFileKey($url);
        $action = $this->getAction($url,$fileKey);
        $slug = $this->getSlug($url,$fileKey,$action);
        $file = $this->getFile($fileKey,$action,$slug);
        $path = str_replace(['////','///','//'],'/',$this->dir.'/'.$file);

        // var_dump("fkey:".$fileKey);
        // var_dump("action:".$action);
        
        if ($file==null
        ||!is_file($path)){
            return false;
        }
        
        return $path;

    }
    public function url($view='',$action='',$slug=''){
        $base = $base ?? $this->base;
        $url = $this->base.'/'.$view.'/'.$slug.'/'.$action.'/';
        $url = $this->cleanPath($url);
        return $url;
    }
    public function isRequested($url){
        $path = $this->filePath($url,$this->base);
        
        if ($path===null||$path===false)return false;
        return true;
    }
    
    public function disablePublic($fileKey,$action='*',$slug='*'){
        // return;
        if ($action!='*'&&$slug!='*'){
            throw new \Exception("Currently only fileKey (first paramater) is accepted on Router->disablePublic(...); \$action & \$slug must be wildcard (*)");
        }
        $fileKey = $this->cleanPath($fileKey);
        // var_dump($fileKey);
        if (isset($this->fileMap[$fileKey])){
            unset($this->fileMap[$fileKey]);
            return;
        } 
        if (substr($fileKey,-2)!='*/'){
            throw new \Exception("File Key '{$fileKey}' does not exist or has already been disabled.");
        }
        $fileKey = $this->cleanPath(substr($fileKey,0,-2));
        $matches = array_filter($this->fileMap,
            function($key) use ($fileKey){
                if (substr($key,0,strlen($fileKey))==$fileKey){
                    return true;
                }
            },
            ARRAY_FILTER_USE_KEY);
            
        foreach ($matches as $key=>$value){
            unset($this->fileMap[$key]);
        }
        
    }

    protected function relativePath($url){
        $url = parse_url($url,PHP_URL_PATH);
        $base = $this->base;
        $url = $this->cleanPath($url);
        if (substr($url,0,strlen($base))!=$base){   
            return false;
        }
        
        $url = substr($url,strlen($base));
        if (substr($url,0,1)!='/')$url = '/'.$url;
        return $url;
        
    }

    public function cleanUrl($url){
        return $this->cleanPath($url);
    }
    protected function cleanPath($url){
        $url = str_replace(['////','///','//'],'/','/'.$url);
        if (substr($url,-1)==='/'){
            $url = substr($url,0,-1);
        }
        $ext = pathinfo($url,PATHINFO_EXTENSION);
        if ($ext==null){
            $url .= '/';
        }
        return $url;
    }



    protected function getSlug($url,$fileKey,$action){
        $url = $this->relativePath($url);

        $url = substr($url,strlen($fileKey));
        while (substr($url,-1)=='/')$url = substr($url,0,-1);
        $actionLength = strlen($action);
        if ($actionLength!=0){
            $url = substr($url,0,-$actionLength);
        }
        return $url;
    }
    protected function getAction($url,$fileKey){
        $url = $this->relativePath($url);

        $url = substr($url,strlen($fileKey));
        if (substr($url,-1)==='/')$url = substr($url,0,-1);
        $parts = explode('/',$url);

        $actionWord = array_pop($parts);
        $views = $this->fileMap;
        if (isset($views[$fileKey][$actionWord])){
            return $actionWord;
        } else {
            return '';
        }
    }
    protected function getFileKey($url){
        $map = $this->fileMap;
        $url = parse_url($url,PHP_URL_PATH);
        $url = $this->relativePath($url);
        $parts = explode('/',$url);
        $fileKey = str_replace('//','/','/'.implode('/',$parts)  );

        $ext = pathinfo($fileKey,PATHINFO_EXTENSION);
        if ($ext==''||$ext==NULL)$fileKey .= '/';
        $url = str_replace(['///','//'],'/','/'.$url);

        if (isset($map[$url])){
            return $url;
        }
        while(!isset($map[$fileKey])
            &&strlen($fileKey)>2){
            array_pop($parts);
            $fileKey = str_replace('//','/','/'.implode('/',$parts).'/');
        }
        if (isset($map[$fileKey])){
            return $fileKey;
        } else {
            return null;
        }
    }

    public function getFile($fileKey,$action,$slug){
        
        $file = @$this->fileMap[$fileKey][$action][strlen($slug)>0 ? '--slug--' : '--no_slug--'];

        if ($file==null||strlen($file)<=0)return false;
        return $file;
    }
    public function getUrlParts($url = NULL){
        $url = $_SERVER['REQUEST_URI'] ?? $url;
        $fileKey = $this->getFileKey($url);
        $action = $this->getAction($url,$fileKey);
        $slug = $this->getSlug($url,$fileKey,$action);
        $parts['fileKey'] = $fileKey;
        $parts['action'] = $action;
        $parts['slug'] = $slug;
        return $parts;
    }
    

    protected function fileMap(){
        $pubDir = $this->dir;

        $paths = $this->allChildren($pubDir);

        $map = [];
        foreach ($paths as $path){
            $ext = pathinfo($path,PATHINFO_EXTENSION);
            
            if ($ext=='php'){
                $baseName = basename($path);
                $fileName = substr($baseName,0,strpos($baseName,'.'));
                if ($fileName=='index')$fileName = '';
                $fileKey = $this->cleanPath(dirname($path).'/'.$fileName.'/');
                $action = $this->parseAction($baseName);
                $isSlugString = $this->parseSlug($baseName);
                $map[$fileKey][$action][$isSlugString] = $path;
            } else {
                $map[$this->cleanPath($path)]['']['--no_slug--'] = $path;
            }
        }
        return $map;
    }

    
    public function parseSlug($baseName){
        $parts = explode('.',$baseName);
        $parts = array_reverse($parts);
        if (strtolower($parts[0])!=='php'){
            return '--no_slug--';
            throw new \Exception ("basename {$baseName} does not have a php extension");
        }
        if (count($parts)===2)return '--no_slug--';
        if (count($parts)===3
            &&$parts[1]=='item'){
                return '--slug--';
            } 
        if (count($parts)>3
            &&$parts[2]=='item'){
                return '--slug--';
            }
        return '--no_slug--';
    }

    public function parseAction($baseName){
        $parts = explode('.',$baseName);
        $parts = array_reverse($parts);
        if (strtolower($parts[0])!=='php'){
            return '';
            throw new \Exception ("basename {$baseName} does not have a php extension");
        }
        if (count($parts)==2
            ||$parts[1]=='item'){
                return '';
            }
        return $parts[1];
    }

    public function allChildren($dir, $relPath = '/'){
        $paths = [];

        $handle = opendir($dir);
        while ($file=readdir($handle)){
            if ($file=='.'||$file=='..')continue;
            if (is_dir($dir.'/'.$file)){
                $paths = array_merge($paths,$this->allChildren($dir.'/'.$file,$relPath.$file.'/'));
            } else {
                $paths[] = $relPath.$file;
            }
        }
        return $paths;
    }

}


?>