BetterReg.php

<?php

// namespace Taeluf;

foreach (scandir(__DIR__) as $file){
    if (is_dir(__DIR__.'/'.$file))continue;
    require_once(__DIR__.'/'.$file);
}

interface BetterRegInterface {
    // to actually call publicly
    public function parseReg($regex, $namespace='');
    public function matchReg($key,$string,...$args);

    // to set/get simple data 
    public function setRegData($data);
    public function setRegParser($parser);
    public function getRegParser();
    public function getRegData($data);

    //extensibility
    public function regFunctionFound($function, $namespace);
    public function regFunctionCall($function, $namespace);
    public function regMatchFound($key, $string, $func, $match,$allMatches);

}

trait BetterRegShorthandsTrait {
    public function parse($regex, $namespace=''){
        return $this->parseReg($regex,$namespace);
    }

    public function match($key, $string,...$args){
        return $this->matchReg($key,$string,...$args);
    }
}

trait BetterRegTrait {

    protected $parser;
    protected $regexData = [];

    public function __construct($dataSet){
        $this->setRegData($dataSet);
        $this->setRegParser(new \BetterReg\Parser);
    }

    public function regFunctionFound($function, $namespace){
        return $this->regFunctionCall($function,$namespace);
    }

    public function regFunctionCall($function, $namespace){
        $target = 'reg'.ucfirst($function->name);
        if (!method_exists($this,$target)){
            $func = $function->name;
            $class = get_class($this);
            throw new \Exception("You must implement '$target' on '$class' to handle regex function '$func'");   
        }
        return $this->$target($namespace,...$function->args);
    }

    public function regMatchFound($key, $string, $func, $match,$allMatches,$args=[]){
        //delegate to function
    
        $info['key']=$key;
        $info['string']=$string;
        $info['func']=$func;
        $info['match'] = $match;
        $info['allMatches'] = $allMatches;
        $captures = array_slice($match,1);
        $ret = $this->$func($info,...$args, ...$captures);

        return $ret;
    }

    public function matchReg($key, $string,...$args){
        $regex = $this->getRegData($key);
            // echo "\nName:$name\n\n";
        $func = preg_replace('/[^a-zA-Z]/','_',$key);
        $func = explode('_',$func);
        $func = array_map('ucfirst',$func);
        $func = implode('_',$func);
        $func = 'match'.$func;
        if (!method_exists($this, $func)){
            $class = get_class($this);
            throw new \Exception("You must implement '$func' on '$class' to match '$key'");
        }

        $didMatch = preg_match_all($regex, $string, $matches, PREG_SET_ORDER);
        if (!$didMatch){
            return false;
        }
        
        foreach ($matches as $key => $match){
            $ret[] = $this->regMatchFound($key, $string, $func, $match, $matches, $args);
        }

        return $ret;
    }

    public function parseReg($regex,$namespace=''){

        $parsed = $this->getRegParser()->parse($regex);
        // expecting
        // [ 'functions' => [list of functions to call],
        //   'sourceReg' => 'long regex string'
        // ]
        $dirtyReg = $parsed['sourceReg'];
        $cleanReg = $parsed['cleanReg'];
        foreach ($parsed['functions'] as $f){
            // expecting $f to be
            // [ 'name'=> 'funcName',
            //   'argString'=> 'unparsed, list, of args',
            //   'definition' => '::funcName(unparsed, list, of args)'
            //   'args' => ['unparsed', 'list', 'of args']
            // ]

            $replacement = $this->regFunctionFound($f, $namespace);
            // echo "Reg:${regex}\n";
            // echo "  FN:".$f->definition."\n";
            // echo "  Replacement: $replacement";
            // echo "\n";
            $dirtyReg = str_replace($f->definition, $replacement, $dirtyReg);
            $cleanReg = str_replace($f->definition, $replacement, $cleanReg);
            // echo "    RegOut: ${regex}\n";
        }

        return $cleanReg;
    }

    public function setRegData($data){
        $this->data = $data;
    }
    public function setRegParser($parser){
        $this->parser = $parser;
    }

    public function getRegData($key){
        return $this->regData[$key];
    }
    public function getRegParser(){
        return $this->parser;
    }
}

class BetterReg implements BetterRegInterface {
    use BetterRegTrait;
    use BetterRegShorthandsTrait;
}