Location: PHPKode > projects > SolarPHP > solar-system-1.1.1/solar/source/solar/Solar/Controller/Console.php
<?php
/**
 * 
 * The CLI equivalent of a front-controller to find and invoke a command
 * (technically a sub-command).
 * 
 * @category Solar
 * 
 * @package Solar_Controller
 * 
 * @author Paul M. Jones <hide@address.com>
 * 
 * @license http://opensource.org/licenses/bsd-license.php BSD
 * 
 * @version $Id: Console.php 4370 2010-02-11 15:41:19Z pmjones $
 * 
 */
class Solar_Controller_Console extends Solar_Base
{
    /**
     * 
     * Default configuration values.
     * 
     * @config array classes Base class names for commands.
     * 
     * @config array routing An array of commands to class names.
     * 
     * @config array disable An array of command names to disallow.
     * 
     * @config string default The default command to run.
     * 
     * @var array
     * 
     */
    protected $_Solar_Controller_Console = array(
        'classes' => array('Solar_Cli'),
        'routing' => array(),
        'disable' => array(),
        'default' => 'help',
    );
    
    /**
     * 
     * A list of command names that should be disallowed.
     * 
     * @var array
     * 
     */
    protected $_disable = array();
    
    /**
     * 
     * Explicit command-name to class-name mappings.
     * 
     * @var array
     * 
     */
    protected $_routing;
    
    /**
     * 
     * The list of commands this controller can invoke.
     * 
     * @var array
     * 
     */
    protected $_command_list = array();
    
    /**
     * 
     * Post-construction tasks to complete object construction.
     * 
     * @return void
     * 
     */
    protected function _postConstruct()
    {
        parent::_postConstruct();
        
        // get the current request environment
        $this->_request = Solar_Registry::get('request');
        
        // set convenience vars from config
        $this->_routing = $this->_config['routing'];
        $this->_default = $this->_config['default'];
        $this->_disable = (array)  $this->_config['disable'];
        
        // set up a class stack for finding commands
        $this->_stack = Solar::factory('Solar_Class_Stack');
        $this->_stack->add($this->_config['classes']);
        
        // extended setup
        $this->_setup();
    }
    
    /**
     * 
     * Finds and invokes a command.
     * 
     * @param array $argv The command-line arguments.
     * 
     * @return string The output of the page action.
     * 
     */
    public function exec($argv = null)
    {
        // get the command-line arguments
        if ($argv === null) {
            $argv = $this->_request->server['argv'];
            array_shift($argv);
        } else {
            $argv = (array) $argv;
        }
        
        // take the command name off the top of the path and
        $command = array_shift($argv);
        
        // is the command disallowed?
        if (in_array($command, $this->_disable)) {
            return $this->_notFound($command);
        }
        
        // try to get a controller class from it.
        $class = $this->_getCommandClass($command);
        
        // did we get a class from it?
        if (! $class) {
            // put the original segment back on top.
            array_unshift($argv, $command);
            // try to get a controller class from the default page name
            $class = $this->_getCommandClass($this->_default);
        }
        
        // last chance: do we have a class yet?
        if (! $class) {
            return $this->_notFound($command);
        }
        
        // instantiate and invoke the command
        $obj = Solar::factory($class);
        $obj->setConsoleController($this);
        return $obj->exec($argv);
    }
    
    /**
     * 
     * Returns a list of commands recognized by this console controller, and the
     * related classes for those commands.
     * 
     * @return array An associative array where the key is the command name, and
     * the value is the class for that command.
     * 
     */
    public function getCommandList()
    {
        if (! $this->_command_list) {
            $this->_setCommandList();
        }
        return $this->_command_list;
    }
    
    /**
     * 
     * Sets up the environment for all commands.
     * 
     * @return void
     * 
     */
    protected function _setup()
    {
    }
    
    /**
     * 
     * Populates the list of recognized commands.
     * 
     * @return void
     * 
     */
    protected function _setCommandList()
    {
        $list = array();
    
        // loop through class stack and add commands
        $stack = $this->_stack->get();
        foreach ($stack as $class) {
        
            $dir = Solar_Dir::exists(str_replace('_', DIRECTORY_SEPARATOR, $class));
            if (! $dir) {
                continue;
            }
        
            // loop through each file in the directory
            $files = scandir($dir);
            foreach ($files as $file) {
                // file must end in .php and start with an upper-case letter
                $char = $file[0];
                $keep = substr($file, -4) == '.php' &&
                        ctype_alpha($char) &&
                        strtoupper($char) == $char;
            
                if (! $keep) {
                    // doesn't look like a command class
                    continue;
                }
                
                // the list-value is the base class name, plus the file name,
                // minus the .php extension, to give us a class name
                $val = $class . substr($file, 0, -4);
            
                // the list-key is the command name; convert the file name to a
                // command name.  FooBar.php becomes "foo-bar".
                $key = substr($file, 0, -4);
                $key = preg_replace('/([a-z])([A-Z])/', '$1-$2', $key);
                $key = strtolower($key);
            
                // keep the command name and class name
                $list[$key] = $val;
            }
        }
    
        // override with explicit routings
        $this->_command_list = array_merge($list, $this->_routing);
        
        // sort, and done
        ksort($this->_command_list);
    }
    
    /**
     * 
     * Finds the command class from a command name.
     * 
     * @param string $command The command name.
     * 
     * @return string The related command class picked from
     * the routing, or from the list of available classes.  If not found,
     * returns false.
     * 
     */
    protected function _getCommandClass($command)
    {
        // skip on leading dashes, it can't be a command
        if (substr($command, 0, 1) == '-') {
            return;
        }
        
        // find the command class
        if (! empty($this->_routing[$command])) {
            // found an explicit route
            $class = $this->_routing[$command];
        } else {
            // no explicit route, try to find a matching class
            $command = str_replace('-',' ', $command);
            $command = str_replace(' ', '', ucwords(trim($command)));
            $class = $this->_stack->load($command, false);
        }
        
        // done!
        return $class;
    }
    
    /**
     * 
     * This runs when exec() cannot find a related command class.
     * 
     * The method throws an exception, which should be caught by the calling
     * script.
     * 
     * @param string $cmd The name of the command not found.
     * 
     * @throws Solar_Controller_Console_Exception
     * 
     * @return void
     * 
     */
    protected function _notFound($cmd)
    {
        $cmd = trim($cmd);
        if (empty($cmd)) {
            throw $this->_exception('ERR_NO_COMMAND');
        } else {
            throw $this->_exception('ERR_COMMAND_NOT_FOUND', array(
                'cmd' => $cmd,
                'classes' => $this->_config['classes'],
                'routing' => $this->_config['routing'],
            ));
        }
    }
}
Return current item: SolarPHP