DebugTrait.php

<?php

namespace LiaisonTrait;

trait Debug {

/** 
 * - Add `$lia->enableDebugging()` if I think it's a good idea.
 * - make `debug` a read-only public property
 * - consider just returning when debug is disabled, instead of throwing.
 * - Document buffering the stack
 * - Document examples of debugging 
 * - Document using the debugger inside an extensions, with examples
 * - Review the documentation for accuracy
 * 
 * @export(TODO.Debug)
 */

    protected $debug = false;
    protected $stack = [
        'FUNCTIONS'=>[],
        'MEDIATORS'=>[],
        "STACK"=>[],
        'PREFIX'=>[],
        'ACTIONS'=>[],
    ];
    
    protected $buffers = [];
    protected $isBuffering = false;
    public function debugEnabled(){
        return $this->debug;
    }
    /**
     * Substacks can help make a debug entry easier to understand. This needs better documentation
     * 
     * @export(Usage.Debug.subStack)
     * 
     */
    public function debug_buffer_start(){
        if (!$this->debug){
            throw new \Exception("Enable debugging to use debug_buffer_start() function. pass ['debug'=>true] to Liaison constructor.");
        }
        $this->buffers[] = 0;
        $this->isBuffering = true;
    }
    public function debug_buffer_end(){
        if (!$this->debug){
            throw new \Exception("Enable debugging to use debug_buffer_end() function. pass ['debug'=>true] to Liaison constructor.");
        }
        $lastBufferCount = array_pop($this->buffers);
        if (count($this->buffers)==0){
            $this->isBuffering = false;
        }
        $bufferedItems = [];
        while ($lastBufferCount--){
            $bufferedItems[] = array_pop($this->stack[static::STACK]);
        }
        $bufferedItems = array_reverse($bufferedItems);
        return $bufferedItems;
    }
    protected function debug_buffer_increment(){
        $topBuffer = array_pop($this->buffers);
        $topBuffer++;
        $this->buffers[] = $topBuffer;
    }

    /**
     * To enable debugging: `$lia = new \Liaison(['debug'=>true]);`
     * Call `debug($debugGroup)` to get the stack of items in the debug group. Call `inspect(\Liaison::STACK)` to get a list of debugable groups currently in the stack.
     * 
     * So far, it's \Liaison::FUNCTIONS & \Liaison::MEDIATORS, but this could change. The code will be consistent, these docs may not be.
     * 
     * @export(Usage.Debug.debug)
     * 
     */
    public function debug($group){
        if (!$this->debug){
            throw new \Exception("Enable debugging to use debug() function. pass ['debug'=>true] to Liaison constructor.");
        }
        if (!isset($this->stack[$group])){
            throw new \Exception("Debug group '{$group}' does not exist. Liaison would need to be edited.");
        }
        $stack = $this->stack[static::STACK];
        if ($group===static::STACK)return $stack;

        $filtered = array_filter($stack,
            function($item) use ($group){
                if ($item['group']===$group)return true;
                return false;
            }
        );
        return $filtered;
    }
    /**
     * To add a debug item to the stack, if `$lia->debug` is true, call `$lia->addDebug(string $debugGroup, string $action, array $extraArgs)`.  
     * Example:  
     * ```php
     * if ($this->debug){
     *     $this->addDebug(\Liaison::FUNCTIONS, 'register', ['function'=>$functionName]);
     * }
     * ```
     * 
     * @export(Usage.Debug.add)
     * 
     */
    public function addDebug($group, $action, $args=[]){
        //@TODO silently exit if debugging is disabled (no exceptions). It takes the burden off of those bits that need to be debugged.
        if (!$this->debug){
            return;
        }
        if (!isset($this->stack[$group])||$group===static::STACK){
            throw new \Lia\Exception\Base("Debug group '{$group}' does not exist. Liaison would need to be edited.");
        }
        if ($this->isBuffering){
            $this->debug_buffer_increment();
        }
        $trace = debug_backtrace(0,2);
        $this->stack[static::STACK][] = 
            [
                "group" => $group,
                "action"=>$action,
                "args" => $args,
                "trace" => $trace[1]
            ];
    }

    /**
     * To find out what all has been registered to liaison, call `$lia->inspect()`. With no paramaters (or an invalid paramater), you'll receive a list of items that can be inspect.  
     * Then call `$lia->inspect(\Liaison::THE_ITEM)` to inspect the specific thing you're interested in, such as FUNCTIONS, MEDIATORS, CONFIG, etc.   
     * You can also pass THE_ITEM as a string.
     * 
     * Returned is an array w/ 'message' (string), and 'items' (array)
     * 
     * @export(Usage.Debug.inspect)
     */
    public function inspect($what=''){
        switch ($what){
            case static::FUNCTIONS:
                $items = array_keys($this->functions);
                $message = "These functions are currently registered.";
                return ['message'=>$message, 'items'=>$items];
            case static::MEDIATORS:
                $items = array_keys($this->mediators);
                $message = "These mediators are currently registered.";
                return ['message'=>$message, 'items'=>$items];
            case static::STACK:
                $groups = [];
                foreach ($this->stack[\Liaison::STACK] as $item){
                    $groups[$item['group']] = $item['group'];
                }
                sort($groups);
                $message = "Use debug() to inspect the stack. These groups are currently in the stack.";
                return ['message'=>$message, 'items'=>$groups];
            default:
                $items = (new \ReflectionClass(\Liaison::class))->getConstants();
                sort($items);
                return 
                [
                    'message'=>"You can pass the following Liaison class constants (except STACK) to be inspected:",
                    "items"=>$items
                ];
            break;
        }
    }
}