ViewRoot.php

<?php

namespace Fresh;

class ViewRoot {

    public $file;
    protected $compFile;
    public $name;
    protected $passthru;

    protected $setupCode = [];

    protected $queries = [];
    protected $compileHandlers = [];
    protected $runtimeHandlers = [];
    protected $findHandler = null;

    public function __construct($viewFile,$viewName,$passthru=[]){
        $this->file = $viewFile;
        $this->name = $viewName;
        $this->passthru = $passthru;

        $this->compFile = dirname($this->file).'/compiled/'.$viewName.'.php';

        $this->initialize();
    }

    public function setPassthru($passthru){
        $this->passthru = $passthru;
    }

    public function addFormatter($name,$callback){
        $this->addRuntimeHandler('format_'.$name,$callback);
    }
    public function addQuery($xpath,$callable){
        $this->queries[] = ['xpath'=>$xpath,'callback'=>$callable];
    }
    public function addCompileHandler($name,$callable){
        $this->compileHandlers[$name][] = $callable;
    }
    protected function executeCompileHandlers($handlerName, ...$args){
        $handlers = $this->compileHandlers[$handlerName] ?? null;
        if ($handlers===null)return;
        foreach ($handlers as $handler){
            $handler(...$args);//$this,$rbAttrs,$prop,$attribute->value);
        }
    }
    public function addRuntimeHandler($name,$callable){
        // $this->runtimeHandlers[$name][] = $callable;
        $this->runtimeHandlers[$name] = $callable;
    }

    public function __call($method,$args){
        if (!isset($this->runtimeHandlers[$method]))throw new \BadMethodCallException("Method '{$method}' does not exist.");
        $handler = $this->runtimeHandlers[$method];
        // print_r($handler);
        // exit;   
        return $handler(...$args);
    }

    public function compile($forceRecompile=false){
        $file = $this->file;
        $compFile = $this->compFile;
        if (!$forceRecompile&&
            true&&
        file_exists($compFile)&&filemtime($file)<filemtime($compFile)){
            return $compFile;
        }

        $viewContent = file_get_contents($file);
        // echo $viewContent."\n\n";
        $compiler = new \RBCompiler();
        $cleanSource = $compiler->cleanSource($viewContent);
        $preDocHandlers = $this->compileHandlers['pre-doc'];
        foreach ($preDocHandlers as $handler){
            $cleanSource = $handler($cleanSource,$viewContent,$compiler,$this);
        }

        $doc = new \RBDoc($cleanSource);
        $compiler->appendCode($doc);


        foreach ($this->queries as $queryInfo){
            //TODO perform all queries before calling any handlers
            //TODO pass doc to the handler (in case the node gets removed from the doc)
            $xpath = $queryInfo['xpath'];
            $callback = $queryInfo['callback'];

            $xp = new \DOMXpath($doc);
            $nodeList = $xp->query($xpath);
            foreach ($nodeList as $node){
                $callback($doc,$this,$compiler,$node);
            }
        }
        $output = $compiler->output();
        // echo "output:\n\n";
        // echo $output;
        // echo "\n\n\n----done";
        // exit;

        $compiler->writeTo($compFile);
        $this->writeSetupCode();
        return $compFile;
    }

    protected function output(){
        $compiledFile = $this->compile();
        // var_dump($this->passthru);
        // exit;
        extract($this->passthru);

        ob_start();
        require($compiledFile);
        return ob_get_clean();
    }

    public function __toString(){
        $output = $this->output();
        return $output;
    }

    public function writeSetupCode(){
        $compDir = dirname($this->compFile).'/setup/';
        $setupFile = $compDir.basename($this->compFile);
        if (!is_dir($compDir))mkdir($compDir);
        file_put_contents($setupFile,implode('',$this->setupCode));
    }

    public function appendSetupCode($code){
        $this->setupCode[] = $code;
    }

    public function setup($passthru=[]){
        $compDir = dirname($this->compFile).'/setup/';
        $setupFile = $compDir.basename($this->compFile);
        extract($passthru);
        if (file_exists($setupFile))require($setupFile);
    }

}