DependencyBuilder.php

<?php


class DependencyBuilder {

    protected $dirs = [];
    protected $excludeDirs = [];

    protected $baseDir='';

    public function __construct($baseDir=''){
        $this->setBaseDir($baseDir);
    }
    public function setBaseDir($baseDir){
        $this->baseDir = $baseDir;
    }

    public function addExcludeDir($dir){
        $this->excludeDirs[] = $this->baseDir.'/'.$dir;
    }

    /**
     * Add a directory inside the base dir 
     */
    public function addScanDir($dir){
        $dir = $this->baseDir.'/'.$dir;
        if (!is_dir($dir)){
            trigger_error("Path '{$dir}' is not a directory.");
            return;
        }
        $this->dirs[] = $dir;
    }

    public function build(){

        $excludes = ['class'=>[],'file'=>[]];
        $classmap = dirname(__DIR__).'/.cache/classmap.php';
        $list = $this->getAllFiles();
        // var_dump($list);
        // exit;
        $classes = $this->getAllClasses($list);
        $classes = $this->sortClasses($classes);
        $export = var_export($classes,true);
        $php = "<?php return \n{$export}\n ?>";
        file_put_contents($classmap,$php); 

    }
    protected function sortClasses($classes){
        uasort($classes,
            function($a,$b){
                $c1 = count($a);
                $c2 = count($b);
                return ($c2>$c1) ?? ($c2<$c1);
            }
        );
        return $classes;
    }
    protected function getAllClasses($allFiles){
        $lens = [];
        $final = [];
        foreach ($allFiles as $file){
            if (strpos($file,'old')!==false)continue;
            $class = $this->classFromFile($file);
            if ($class=='')continue;
            if (isset($excludes['class'][$class])
                ||isset($excludes['file'][$file]))continue;
            $l = $lens[$class] ?? 0;
            if ($l==false)$final[$class] = [];
            if (($newL=strlen($file))<$l||$l==0){
                $final[$class] = [];
                array_unshift($final[$class],$file);
                $lens[$class] = $newL;
            } else {
                // $final[$class][] = $file;
            }
        }
        return $final;
    }
    protected function getAllFiles(){

        // var_dump($this->dirs);exit;
        $list=[];
        foreach ($this->dirs as $resourceDir){
            if (is_dir($resourceDir)){
            $rii = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($resourceDir));
            $files = array(); 
            foreach ($rii as $file) {
                // if ($file->isDir()){ 
                //     continue;
                // }
                if ($file->getExtension()=='php'){
                    $list[] = $file->getPathName();
                }
            }
        }
        }
        return $list;
    }

    protected function classFromFile($file){
        $expect = pathinfo($file,PATHINFO_FILENAME);
        $fp = fopen($file, 'r');
        $class = $namespace = $buffer = '';
        $i = 0;
        while (!$class||$expect!=$class) {
            if (feof($fp)) break;

            $buffer .= fread($fp, 512);
            ob_start();
            //suppress the token-message, since I'm not reading the full file 
            $tokens = @token_get_all($buffer);
            $err = ob_get_clean();
            // if (true&&strlen($err)>0){
                // echo $buffer;
                // echo $err."\n";
                // echo $class."\n";
                // echo "\n".$file;
                // print_r($tokens);
            // }
            if (strpos($buffer, '{') === false) continue;

            for (;$i<count($tokens);$i++) {
                if ($tokens[$i][0] === T_NAMESPACE) {
                    for ($j=$i+1;$j<count($tokens); $j++) {
                        if ($tokens[$j][0] === T_STRING) {
                            $namespace .= '\\'.$tokens[$j][1];
                        } else if ($tokens[$j] === '{' || $tokens[$j] === ';') {
                            break;
                        }
                    }
                }

                if ($tokens[$i][0] === T_CLASS 
                    || $tokens[$i][0] === T_TRAIT
                    || $tokens[$i][0] === T_INTERFACE
                    ) {
                    for ($j=$i+1;$j<count($tokens);$j++) {
                        if ($tokens[$j] === '{') {
                            $class = @$tokens[$i+2][1];
                        }
                    }
                }
            }
        }
        if ($class=='')return '';
        return $namespace.'\\'.$class;    
    }

}