Handler.php

<?php

namespace Fresh;

/**
 * Handler is the source for extensability. You can set a handler either on the component & it will propagate, or set it directly on a view.
 * 
 * @export(Class.Handler)
 */
class Handler {

    protected $children = [];

    protected $handlers = [];


    public function __construct(){
    }
    public function addChild(object $childObj){
        $this->children[] = $childObj;
        foreach ($this->handlers as $domain => $handlers){
            foreach ($handlers as $name=>$callbackList){
                if (isset($callbackList['set'])){
                    $childObj->handler->setHandler($domain,$name,$callbackList['set']);
                } else {
                    foreach ($callbackList as $index => $callback){
                        $childObj->handler->addHandler($domain,$name,$callback);
                    }
                }
            }
        }
    }

    public function setHandler($domain,$name,$callback){
        if (isset($this->handlers[$domain][$name]['set'])){
            throw new \Exception("Cannot override a previous set handler for '{$domain}::{$name}'.");
        }
        $this->handlers[$domain][$name]['set'] = $callback;
        foreach ($this->children as $child){
            $handler = $child->handler;
            $handler->setHandler($domain,$name,$callback);
        }
    }
    public function addHandler($domain,$name,$data){
        $existing = $this->handlers[$domain][$name] ?? null;
        if ($existing!==null&&isset($existing['set'])){
            $exDomain = var_export($domain,true);
            $exName = var_export($name,true);
            $class = get_class();
            throw new \Exception("Cannot override handler previously set by calling {$class}::setHandler({$exDomain},{$exName},\$data) \n");
        }
        $this->handlers[$domain][$name][] = $data;
        foreach ($this->children as $child){
            $handler = $child->handler;
            $handler->addHandler($domain,$name,$data);
        }
    }

    /**
     * Doing $handlerObject->compile will get an array of handlers in the 'compmile' domain, via the `__get` magic method.  
     * Then: `return (object)$arrayOfHandlers`... SO you have, simply, a cast object. 
     * 
     * TODO Create a HandlerDomain object that has magic methods to make calling more dev-friendly  
     * For each handler, if the handler was set with setHandler()... you can `$handler->compile->$HandlerName()`... and get a return value  
     * If the handler was put with addHandler()... Then $handler->compile->$HandlerName returns an array of handlers, which can each then be called.
     * 
     * 
     * @export(Internals.Handler)
     */
    public function __get($domain){
        $handlers = $this->handlers[$domain] ?? null;
        if ($handlers===null){
            return null;
            // print_r($this->handlers);
            // throw new \Exception("No handlers exist for domain '{$domain}'.");
        }
        $ret = [];
        foreach ($handlers as $name=>$list){
            if (isset($list['set'])){
                $ret[$name] = $list['set'];
            } else {
                $ret[$name] = $list;
            }
        }
        return (object)$ret;
    }

    public function callMethod($domain,$method,$args){
        $handler = $this->runtime->$method ?? null;
        // echo "\n".$method;
        
        if ($handler===null
            ||is_array($handler)&&count($handler)==0
            // ||is_callable($handler)
            // &&is_array($handler)
            // &&!method_exists($handler[0],$handler[1])
            ){
                // var_dump($this->handlers);
            throw new \BadMethodCallException("Method '{$method}' does not exist ");
        }
        if (!is_callable($handler)&&is_array($handler)){
            $ret = [];
            foreach ($handler as $h){
                $ret[] = $h(...$args);
            }

            return $ret;
        } else {
            return $handler(...$args);
        }
    }
    
}