Location: PHPKode > projects > SolarPHP > solar-system-1.1.1/solar/source/solar/Solar/Php.php
<?php
/**
 * 
 * Lets you execute a Solar-based script in a separate PHP process, then get
 * back its exit code, last line, and output.
 * 
 * Intended use is for documentation and testing, where you don't want the
 * classes loaded in the main environment to interact with the classes in the
 * current environment.
 * 
 * An example to run `echo "hello world!"` in a separate process:
 * 
 * {{code: php
 *     require_once 'Solar.php';
 *     Solar::start();
 *     
 *     $ini = array(
 *         'include_path'    =>  '/path/to/lib',
 *         'error_reporting' =>  E_ALL | E_STRICT,
 *         'error_display'   =>  1,
 *         'html_errors'     =>  0,
 *     );
 *     
 *     $php = Solar::factory('Solar_Php');
 *     
 *     $php->setIniFile(false)
 *         ->setIniArray($ini)
 *         ->runSolarCode('echo "hello world!\n"');
 *     
 *     Solar::stop();
 * }}
 * 
 * @category Solar
 * 
 * @package Solar
 * 
 * @author Paul M. Jones <hide@address.com>
 * 
 * @license http://opensource.org/licenses/bsd-license.php BSD
 * 
 * @version $Id: Php.php 3988 2009-09-04 13:51:51Z pmjones $
 * 
 */
class Solar_Php extends Solar_Base
{
    /**
     * 
     * Default configuration values.
     * 
     * @config string php Command to invoke the PHP binary.
     * 
     * @config string ini_file Which php.ini file to use; if null, use the
     * default php.ini file.
     * 
     * @config array ini_set Override php.ini settings with these settings
     * instead. The element key is the .ini setting name, and the element 
     * value is the .ini value to use.
     * 
     * @config mixed solar_config When calling Solar::start(), use this as the
     * config value.
     * 
     * @config bool echo Whether or not to echo the process output as it goes.
     *
     * @var array
     * 
     */
    protected $_Solar_Php = array(
        'php'          => 'php',
        'ini_file'     => null,
        'ini_set'      => null,
        'solar_config' => null,
        'echo'         => null,
    );
    
    /**
     * 
     * Command to invoke the PHP binary.
     * 
     * @var string
     * 
     */
    protected $_php = 'php';
    
    /**
     * 
     * Which php.ini file to use.
     * 
     * Null means to use the default php.ini file, but false means to use *no*
     * php.ini file.
     * 
     * @var string
     * 
     */
    protected $_ini_file = null;
    
    /**
     * 
     * Override php.ini file settings with these settings.
     * 
     * Format is an array of key-value pairs, where the key is the setting
     * name and the value is the setting value.
     * 
     * @var array
     * 
     */
    protected $_ini_set = array();
    
    /**
     * 
     * When calling Solar::start() in the new process, use this as the $config
     * value.
     * 
     * @var mixed
     * 
     */
    protected $_solar_config = null;
    
    /**
     * 
     * Whether or not to echo the process output as it goes.
     * 
     * @var bool
     * 
     */
    protected $_echo = true;
    
    /**
     * 
     * After the code runs, each line of output (if any).
     * 
     * @var array
     * 
     */
    protected $_output;
    
    /**
     * 
     * After the code runs, the last line of output (if any).
     * 
     * @var array
     * 
     */
    protected $_last_line;
    
    /**
     * 
     * After the code runs, the exit status code (if any).
     * 
     * Note that null is *not* the same as zero; zero is normally an "OK"
     * exit code, whereas null means "no exit code given".
     * 
     * @var array
     * 
     */
    protected $_exit_code;
    
    /**
     * 
     * Command-line arguments to pass to the code.
     * 
     * @var array
     * 
     */
    protected $_argv = array();
    
    /**
     * 
     * Post-construction tasks to complete object construction.
     * 
     * @return void
     * 
     */
    protected function _postConstruct()
    {
        parent::_postConstruct();
        
        // populate each of these properties with its config value ...
        $list = array_keys($this->_Solar_Php);
        foreach ($list as $key) {
            // ... but only if not null.
            if ($this->_config[$key] !== null) {
                $var = "_$key";
                $this->$var = $this->_config[$key];
            }
        }
    }
    
    /**
     * 
     * Sets the PHP command to call at the command line.
     * 
     * @param string $php The PHP command; e.g., "/usr/local/php".
     * 
     * @return Solar_Php
     * 
     */
    public function setPhp($php)
    {
        $this->_php = $php;
        return $this;
    }
    
    /**
     * 
     * Sets the location of the php.ini file to use.
     * 
     * If null, uses the default php.ini file location.
     * 
     * If false, uses *no* php.ini file (the `--no-php-ini` switch).
     * 
     * @param string $file The php.ini file location.
     * 
     * @return Solar_Php
     * 
     */
    public function setIniFile($file)
    {
        if ($file !== null && $file !== false) {
            $file = (string) $file;
        }
        $this->_ini_file = $file;
        return $this;
    }
    
    /**
     * 
     * Sets one php.ini value, overriding the php.ini file.
     * 
     * @param string $key The php.ini setting name.
     * 
     * @param string $val The php.ini setting value.
     * 
     * @return Solar_Php
     * 
     */
    public function setIniVal($key, $val)
    {
        $this->_ini_set[$key] = $val;
        return $this;
    }
    
    /**
     * 
     * Sets an array of php.ini values, overriding the php.ini file.
     * 
     * Each key in the array is a php.ini setting name, and each value is the
     * corresponding php.ini value.
     * 
     * @param string $list The array of php.ini key-value pairs.
     * 
     * @return Solar_Php
     * 
     */
    public function setIniArray($list)
    {
        foreach ($list as $key => $val) {
            $this->_ini_set[$key] = $val;
        }
        return $this;
    }
    
    /**
     * 
     * Add a command-line argument for the code.
     * 
     * @param mixed $val The command-line argument value.
     * 
     * @return Solar_Php
     * 
     */
    public function addArgv($val)
    {
        $this->_argv[] = $val;
        return $this;
    }
    
    /**
     * 
     * Set all command-line arguments for the code at one time; clears all
     * previous argument values.
     * 
     * @param array $array A sequential array of command-line arguments.
     * 
     * @return Solar_Php
     * 
     */
    public function setArgv($array)
    {
        $this->_argv = (array) $array;
        return $this;
    }
    
    /**
     * 
     * When calling Solar::start() in the new process, use this as the $config
     * value.
     * 
     * @param mixed $solar_config The value to use for Solar::start().
     * 
     * @return Solar_Php
     * 
     */
    public function setSolarConfig($solar_config)
    {
        $this->_solar_config = $solar_config;
        return $this;
    }
    
    /**
     * 
     * Turns execution process output on and off.
     * 
     * @param bool $echo True to echo the process as it runs, or false to
     * suppress output.
     * 
     * @return Solar_Php
     * 
     */
    public function setEcho($echo)
    {
        $this->_echo = (bool) $echo;
        return $this;
    }
    
    /**
     * 
     * Runs a file as a Solar script.
     * 
     * @param string $file The file to load as a Solar script.
     * 
     * @return Solar_Php
     * 
     */
    public function runSolar($file)
    {
        $code = file_get_contents($file);
        return $this->runSolarCode($code);
    }
    
    /**
     * 
     * Runs a code string as a Solar script.
     * 
     * @param string $code The code to run as a Solar script.
     * 
     * @return Solar_Php
     * 
     */
    public function runSolarCode($code)
    {
        $code = $this->_buildSolarCode($code);
        return $this->_run($code);
    }
    
    /**
     * 
     * Runs the named file as the PHP code for the process.
     * 
     * @param string $file The script file name.
     * 
     * @return Solar_Php
     * 
     */
    public function run($file)
    {
        $code = file_get_contents($file);
        return $this->runCode($code);
    }
    
    /**
     * 
     * Runs the given string as the PHP code for the process.
     * 
     * @param string $code The script code.
     * 
     * @return Solar_Php
     * 
     */
    public function runCode($code)
    {
        $code = $this->_buildCode($code);
        return $this->_run($code);
    }
    
    /**
     * 
     * Support method to actually run the code and retain information about
     * the run.
     * 
     * @param string $code The code to execute.
     * 
     * @return Solar_Php
     * 
     */
    protected function _run($code)
    {
        // clean up from last run
        $this->_output    = null;
        $this->_last_line = null;
        $this->_exit_code = null;
        
        // build the full command with PHP code
        $cmd = $this->_buildCommand($code);
        
        // open a process handle and send the command
        $handle = popen($cmd, 'rb');
        
        // read from the handle
        while (! feof($handle)) {
            $read = fread($handle, 4096);
            if ($this->_echo) {
                echo $read;
            }
            $this->_output .= $read;
        }
        
        // close the handle and retain the exit code
        $this->_exit_code = pclose($handle);
        
        // get the last line of output.
        $tmp = $this->_output;
        $len = strlen(PHP_EOL) * -1;
        if (substr($tmp, $len) == PHP_EOL) {
            $tmp = substr($tmp, 0, $len);
        }
        $tmp = explode(PHP_EOL, $tmp);
        $this->_last_line = end($tmp);
        
        // done!
        return $this;
    }
    
    /**
     * 
     * Gets the exit code from the separate process.
     * 
     * @return int
     * 
     */
    public function getExitCode()
    {
        return $this->_exit_code;
    }
    
    /**
     * 
     * Gets all lines of output from the separate process.
     * 
     * @return array
     * 
     */
    public function getOutput()
    {
        return $this->_output;
    }
    
    /**
     * 
     * Gets the last line of output from the separate process.
     * 
     * @return string
     * 
     */
    public function getLastLine()
    {
        return $this->_last_line;
    }
    
    /**
     * 
     * Builds a code string appropriate for the PHP command-line call.
     * 
     * @param string $code The code to run.
     * 
     * @return string The "fixed" code.
     * 
     */
    protected function _buildCode($code)
    {
        // trim leading and trailing space so as not to mess up the removal of
        // opening and closing tags.
        $code = trim($code);
        
        // strip long opening tag
        if (substr($code, 0, 5) == '<?php') {
            $code = substr($code, 5);
        }
        
        // strip short opening tag
        if (substr($code, 0, 2) == '<?') {
            $code = substr($code, 2);
        }
        
        // strip closing tag
        if (substr($code, -2) == '?>') {
            $code = substr($code, 0, -2);
        }
        
        return $code;
    }
    
    /**
     * 
     * Wraps the given code string in extra code to load, start, and stop
     * Solar.
     * 
     * @param string $code The code to run in the separate process.
     * 
     * @return string
     * 
     */
    protected function _buildSolarCode($code)
    {
        // the core code
        $code = $this->_buildCode($code);
        
        // get the solar config as a variable
        $solar_config = var_export($this->_solar_config, true);
        
        // wrap the code in Solar::start() and Solar::stop()
        $code = "require 'Solar.php'; "
              . "Solar::start($solar_config); "
              . "$code; "
              . "Solar::stop();";
        
        // done!
        return $code;
    }
    
    /**
     * 
     * Builds the command-line invocation of PHP.
     * 
     * @param string $code The code to execute.
     * 
     * @return string The PHP command with the necessary switches.
     * 
     */
    protected function _buildCommand($code)
    {
        // the PHP binary
        $cmd = $this->_php;
        
        // using a php.ini file?
        if ($this->_ini_file) {
            // non-default file or path
            $cmd .= " --php-ini " . escapeshellarg($this->_ini_file);
        } elseif ($this->_ini_file === false) {
            // explicitly *no* file to be used
            $cmd .= " --no-php-ini";
        }
        
        // override php.ini values
        foreach ((array) $this->_ini_set as $key => $val) {
            $key = escapeshellarg($key);
            $val = escapeshellarg($val);
            $cmd .= " --define $key=$val";
        }
        
        // add the code
        $cmd .= " --run " . escapeshellarg($code);
        
        foreach ($this->_argv as $val) {
            $cmd .= " $val";
        }
        
        // done
        return $cmd;
    }
}
Return current item: SolarPHP