Cli.php

<?php

namespace Tlf;

class Cli {

    /**
     * The current working directory
     */
    public $pwd;

    /** map of command name to callable
     */
    public $command_map = [];

    /**
     * the array of commands (for environments that have sub-commands)
     */
    // public $commands;
    
    /**
     * The command being executed
     */
    public $command = 'main';
    /**
     * The args to pass to your command's callable (key=>value array)
     */
    public $args = [];

    public function __construct($pwd = null, $argv = null){
        $this->pwd = $pwd ?? $_SERVER['PWD'];
        $this->argv = $argv ?? $_SERVER['argv'];
    }

    /**
     * load standard inputs from cli
     * @param $stdin (optional) array of arguments like `['filename', 'subcommand', '-option', 'option_value', '--flag', 'etc']`
     * @default $stdin to $_SERVER['argv']
     */
    public function load_stdin($stdin=null){
        $stdin = $stdin ?? $this->argv;
        $this->script = basename($stdin[0]);
        if (!isset($stdin[1])||substr($stdin[1],0,1)=='-'){
            $this->command = $this->command;
            $slice = 1;
        } else {
            $this->command = $stdin[1];
            $slice = 2;
        }
        $args = $this->parse_args(array_slice($stdin,$slice), $this->args);
        $this->args = array_merge($this->args, $args);
    }

    public function parse_args($stdin_args, $args=[]){
        $last_key = null;
        foreach ($stdin_args as $arg){
            if ($arg[0]=='-'){
                if ($arg[1]=='-'){
                    $last_key= substr($arg,2);
                    $args[$last_key] = true;
                    continue;
                }
                $last_key = substr($arg,1);
                if (!isset($args[$last_key])){
                    $args[$last_key] = null;
                } 
            } else if ($last_key==null){
                $args['--'][] = $arg;
            } else {
                if ($arg==='true')$arg = true;
                else if ($arg === 'false')$arg = false;
                if (is_array($args[$last_key])){
                    $args[$last_key][] = $arg;
                } else {
                    $args[$last_key] = $arg;
                }
                $last_key = null;
            }
        }

        return $args;
    }

    public function load_command($command, $callable){
        $this->command_map[$command] = $callable;
    }

    public function load_inputs($args){
        $this->args = array_merge($this->args, $args);
    }

    /**
     * @param $command the name of the command to execute
     * @param $args args to execute
     */
    public function call_command(string $command, array $args=[]){
        if (!isset($this->command_map[$command])){
            echo 'Command "'.$command.'" does not exist.'."\n";
            return;
        }
        $call = $this->command_map[$command];
        $ret = $call($this, $args);
        echo "\n";
        return $ret;
    }

    /**
     * Run the cli based on loaded inputs
     */
    public function execute(){
        return $this->call_command($this->command, $this->args);
    }
}