Cache.php

<?php

namespace Tlf\LexerTrait;

/**
 * Handles caching of asts & grammar lists
 */
trait Cache {

    /**
     * Static cache so different instances of Lexer don't use all the memory, since cacheMap files are fairly large.
     */
    static protected $_cacheMap;

    /**
     * Copy of .cache/map.json
     */
    protected $cacheMap = null;

    public function getCachedAst($filePath){
        if ($this->useCache===false)return false;
        $cacheDir = dirname(__DIR__, 2).'/.cache/';
        if (!is_file($cacheDir.'/map.json'))return false;
        $this->cacheMap = &static::$_cacheMap[$cacheDir.'/map.json'];
        if ($this->cacheMap == null){
            // $mem = memory_get_usage();
            // $max = ini_get('memory_limit');
            // $filesize = filesize($cacheDir.'/map.json');
            // ob_end_clean();
            //
            // echo "\n\n";
            // var_dump("Mem: $mem ");
            // var_dump("Max: $max");
            // var_dump("File: $filesize");
            // echo "\n\n";
            // 
            // string(12) "Mem: 645656 "
            // string(9) "Max: 128M"
            //string(12) "File: 139698"

            // exit;
            $content = file_get_contents($cacheDir.'/map.json');
            $this->cacheMap = json_decode($content,true);
        }
        if (!isset($this->cacheMap['files'][$filePath]))return false;
        $file = $this->cacheMap['files'][$filePath];

        if (sha1_file($filePath)!=$file['sha1'])return false;
        
        $grammarList = $this->getGrammarListForCache();
        if ($grammarList != $file['grammarList']) return false;

        $astTree = include($file['cachedAstPath']);
        if (!is_array($astTree))return false;

        $ast = new \Tlf\Lexer\Ast('file');
        $ast->setTree($astTree);
        
        return $ast;
    }

    public function cacheFileAst($filePath, $ast){
        if ($this->useCache===false)return;
        $tree = $ast->getTree();

        $cacheDir = dirname(__DIR__, 2).'/.cache/';
        if (!is_dir($cacheDir))mkdir($cacheDir);
        if (!is_dir($cacheDir.'/fileast/'))mkdir($cacheDir.'/fileast/');
        if (!is_file($cacheDir.'/map.json'))file_put_contents($cacheDir.'/map.json',json_encode([]));
        $map = $this->cacheMap ?? json_decode(file_get_contents($cacheDir.'/map.json'),true);

        if (isset($map['files'][$filePath]['cachedAstPath'])){
            if (is_file($cachedPath = $map['files'][$filePath]['cachedAstPath'])){
                unlink($cachedPath);
            }
        }

        $map['files'][$filePath]['sha1'] = sha1_file($filePath);
        $map['files'][$filePath]['grammarList'] = $this->getGrammarListForCache();
        if (!is_dir($cacheDir.'/fileast'))mkdir($cacheDir.'/fileast');
        $cachedAstPath = $cacheDir.'/fileast/'.uniqid().'.php';
        $map['files'][$filePath]['cachedAstPath'] = $cachedAstPath;

        file_put_contents($cachedAstPath,
            '<?php return '
                . var_export($tree,true)
            .';'
        );
        
        $this->cacheMap = $map;
        file_put_contents($cacheDir.'/map.json', json_encode($map, JSON_PRETTY_PRINT));
    }

    public function getGrammarListForCache(){
        if ($this->grammarListForCache!=null)return $this->grammarListForCache;
        $grammarList = [];
        foreach ($this->grammars as $g){
            $grammarList[get_class($g)] = filemtime((new \ReflectionClass($g))->getFileName());
        }
        ksort($grammarList);

        $this->grammarListForCache = $grammarList;
        return $grammarList;
    }
}