Perforce Chronicle 2012.2/486814
API Documentation

P4_Connection_CommandLine Class Reference

Perforce Command Line Client. More...

Inheritance diagram for P4_Connection_CommandLine:
P4_Connection_Abstract P4_Connection_Interface

List of all members.

Public Member Functions

 disconnect ()
 Pretend to disconnect - set the connected flag to false.
 getArgMax ()
 Get the maximum allowable length of all command arguments.
 getConnectionIdentity ()
 Get the identity of this Connection implementation.
 isConnected ()
 Check connected state.
 setP4Path ($path)
 Set the full path/filename to the p4 executable.

Static Public Member Functions

static escapeArg ($arg)
 Escape a string for use as a command argument.
static escapeShellCmd ($cmd)
 Provide our own escapeshellcmd to support platform-specific functionality.

Public Attributes

const CMD_CANNOT_EXEC = 126
const CMD_NOT_FOUND = 127
const E_EMPTY = 0
const E_FAILED = 3
const E_FATAL = 4
const E_INFO = 1
const E_WARN = 2
const P4_BINARY = 'p4'

Protected Member Functions

 _connect ()
 Does real work of establishing connection.
 _prepareInput ($input, $command)
 Prepare input for passing to the p4 via stdin.
 _run ($command, $params=array(), $input=null, $tagged=true)
 Actually issues a command.

Protected Attributes

 $_p4Path = null

Detailed Description

Perforce Command Line Client.

A PHP Wrapper for the Perforce Command-Line Client (P4).

Copyright:
2011-2012 Perforce Software. All rights reserved
License:
Please see LICENSE.txt in top-level folder of this distribution.
Version:
2012.2/486814

Member Function Documentation

P4_Connection_CommandLine::_connect ( ) [protected]

Does real work of establishing connection.

Called by connect().

The command-line wrapper does not maintain a persistent connection. But, it can use 'p4 info' to test the connection parameters.

Exceptions:
P4_Connection_ConnectExceptionif the connection fails.

Reimplemented from P4_Connection_Abstract.

    {
        // info will trigger a connect exception if connect to server fails.
        $this->_run('info');
        $this->_isConnected = true;
    }
P4_Connection_CommandLine::_prepareInput ( input,
command 
) [protected]

Prepare input for passing to the p4 via stdin.

If input is an array, serialize it to a string suitable for passing to the p4 client as marshalled PHP input. If the array is multi-dimensional, flatten it.

In the special case of the 'password' command, convert to a string by imploding with newlines rather than serializing.

Parameters:
string | array$inputthe input to prepare for p4.
string$commandthe command to prepare input for.
Returns:
string the serialized output string.

Reimplemented from P4_Connection_Abstract.

    {
        // if input is not an array, don't serialize it.
        if (!is_array($input)) {
            return $input;
        }

        // if command is 'password', convert to string via implode.
        if ($command == "password" || $command == "passwd") {
            return implode("\n", $input) . "\n";
        }

        // flatten input array and cast values to strings.
        $flatInput = array();
        foreach ($input as $key => $value) {
            if (is_array($value)) {
                foreach ($value as $subKey => $subValue) {
                    $flatInput[$key . $subKey] = (string) $subValue;
                }
            } else {
                $flatInput[$key] = (string) $value;
            }
        }

        // serialize and return.
        return serialize($flatInput);
    }
P4_Connection_CommandLine::_run ( command,
params = array(),
input = null,
tagged = true 
) [protected]

Actually issues a command.

Called by run() to perform the dirty work.

Parameters:
string$commandthe command to run.
array$paramsoptional - arguments.
array | string$inputoptional - input for the command - should be provided in array form when writing perforce spec records.
boolean$taggedoptional - true/false to enable/disable tagged output. defaults to true.
Returns:
P4_Result the perforce result object.

Reimplemented from P4_Connection_Abstract.

    {
        // escape parameters for safe shell execution.
        for ($i = 0; $i < count($params); $i++) {
            $params[$i] = static::escapeArg($params[$i]);
        }

        // build up the full p4 command.
        $p4 = static::escapeShellCmd($this->_getP4Path());
        if ($this->getPort()) {
            $p4 .= ' -p ' . static::escapeArg($this->getPort());
        }
        if ($this->getUser()) {
            $p4 .= ' -u ' . static::escapeArg($this->getUser());
        }
        if ($this->getTicket()) {
            $p4 .= ' -P ' . static::escapeArg($this->getTicket());
        }
        if (!$this->getTicket() && $this->_password ) {
            $p4 .= ' -P ' . static::escapeArg($this->_password);
        }
        if ($this->getCharset()) {
            $p4 .= ' -C ' . static::escapeArg($this->getCharset());
        }
        if ($this->getHost()) {
            $p4 .= ' -H ' . static::escapeArg($this->getHost());
        }
        if ($this->getAppName()) {
            $p4 .= ' -Z ' . static::escapeArg('app=' . $this->getAppName());
        }
        if ($tagged) {
            $p4 .= ' -Ztag';
        }

        // if no client is specified, normally the host name is used.
        // this can collide with an existing depot or client name, so
        // we use a temp id to avoid errors.
        $client = $this->getClient() ?: P4_Client::makeTempId();
        $p4 .= ' -c ' . static::escapeArg($client);

        $p4 .= ' -Mp ';  // use serialized PHP input/output.
        $p4 .= ' ' . static::escapeArg($command) . ' ' . implode(' ', $params);

        // log the full p4 command
        $message = "P4 (" . spl_object_hash($this) . ") execute p4: " . $p4;
        P4_Log::log(
            substr($message, 0, static::LOG_MAX_STRING_LENGTH),
            P4_Log::DEBUG
        );

        // create a temporary file name to store stderr output.
        $stdErrFile = tempnam(sys_get_temp_dir(), "stderr");

        // define descriptors for proc_open communication.
        //  0 - stdin
        //  1 - stdout
        //  2 - stderr
        $descriptors = array(
            0 => array("pipe", "rw"),
            1 => array("pipe", "w"),
            2 => array("file", $stdErrFile, "w"));

        // launch the process.
        $pipes   = array();
        $process = proc_open(
            $p4,
            $descriptors,
            $pipes,
            NULL,
            NULL,
            array(
                'bypass_shell'  => P4_Environment::isWindows()
            )
        );

        // check for proc_open error.
        if (!is_resource($process)) {
            $message = "Unable to proc_open() p4 ('$p4').";
            throw new P4_Exception($message);
        }

        // if input provided, write it to stdin.
        if ($input !== null) {
            fwrite($pipes[0], $input);
            fwrite($pipes[0], "\n");
            fflush($pipes[0]);
        }

        // read out pipes and close them.
        @fclose($pipes[0]);
        $stdOut = stream_get_contents($pipes[1]);
        $stdErr = file_get_contents($stdErrFile);
        @fclose($pipes[1]);
        @fclose($pipes[2]);
        $status = proc_close($process);
        unlink($stdErrFile);

        // check for shell exec problems.
        if ($status === self::CMD_CANNOT_EXEC || $status === self::CMD_NOT_FOUND) {
            $message = "Unable to execute p4 ('" . trim($stdErr) . "').";
            throw new P4_Exception($message);
        }

        // check for usage error.
        if (stristr($stdErr, "invalid option")) {
            throw new P4_Exception("Usage error: " . $stdErr);
        }

        // check for connection error.
        if (stristr($stdErr, "connect to server failed")
            || preg_match("/TCP connect to .+ failed/", $stdErr)
        ) {
            $this->_isConnected = false;
            throw new P4_Connection_ConnectException("Connect failed: " . $stdErr);
        } else {
            $this->_isConnected = true;
        }

        // unserialize output into a perforce result object.
        $result = new P4_Result($command, null, $tagged);
        $output = $this->_unserializeOutput($stdOut);

        // ensure that output unserializes to an array.
        if (!is_array($output)) {
            $message = "Command failed. Output did not deserialize into an array.";
            $e = new P4_Connection_CommandException($message);
            $e->setConnection($this);
            $e->setResult($result);
            throw $e;
        }

        // put data into result set.
        // separate errors and warnings from data.
        foreach ($output as $data) {
            switch ($data['code']) {
                case 'error':
                    if ($data['severity'] > self::E_WARN) {
                        $result->addError($data['data']);
                    } else {
                        $result->addWarning($data['data']);
                    }
                    break;
                case 'text':
                case 'binary':
                case 'info':
                    $result->addData($data['data']);
                    break;
                default:
                    unset($data['code']);
                    $result->addData($data);
                    break;
            }
        }

        // check for output on stderr and add to result object.
        if ($stdErr && $status) {
            $result->addError($stdErr);
        }

        return $result;
    }
P4_Connection_CommandLine::disconnect ( )

Pretend to disconnect - set the connected flag to false.

Returns:
P4_Connection_Interface provides fluent interface.

Reimplemented from P4_Connection_Abstract.

    {
        // call parent to run disconnect callbacks.
        parent::disconnect();

        $this->_isConnected = false;

        return $this;
    }
static P4_Connection_CommandLine::escapeArg ( arg) [static]

Escape a string for use as a command argument.

Replacement for the default escapeshellarg() function. In Windows, we use the bypass_shell option for proc_open, which changes the rules for escaping command line arguments.

Parameters:
string$argthe string to escape
Returns:
string the escaped string

Reimplemented from P4_Connection_Abstract.

    {
        // if not windows, exit early with normal escapeshellarg
        if (!P4_Environment::isWindows()) {
            return escapeshellarg($arg);
        }

        // As per MS spec: http://msdn.microsoft.com/en-us/library/a1y7w461.aspx
        // escape quotes and backslashes in command line arguments.

        // step 1: escape backslashes immediately preceeding double quotes
        $arg = preg_replace('/(\\\\+)"/', '\\1\\1"', $arg);
        // step 2: escape backslashes at the end of string (protects our added quotes)
        $arg = preg_replace('/(\\\\+)$/', '\\1\\1',  $arg);
        // step 3: escape double quotes
        $arg = preg_replace('/"/',        '\\"',     $arg);
        // step 4: wrap the result in double quotes
        $arg = '"' . $arg . '"';

        return $arg;
    }
static P4_Connection_CommandLine::escapeShellCmd ( cmd) [static]

Provide our own escapeshellcmd to support platform-specific functionality.

Parameters:
string$cmdThe command to escape.
Returns:
string The escaped command.
    {
        // if not windows, exit early with normal escapeshellcmd
        if (!P4_Environment::isWindows()) {
            return escapeshellcmd($cmd);
        }

        return static::escapeArg($cmd);
    }
P4_Connection_CommandLine::getArgMax ( )

Get the maximum allowable length of all command arguments.

Returns:
int the max length of combined arguments - zero for no limit

Reimplemented from P4_Connection_Abstract.

    {
        // return the system arg-max less a Kilobyte for our arguments
        return P4_Environment::getArgMax() - 1024;
    }
P4_Connection_CommandLine::getConnectionIdentity ( )

Get the identity of this Connection implementation.

Resulting array will contain:

  • name
  • platform
  • version
  • build
  • apiversion (same value as version, included for consistency)
  • apibuild (same value as build, included for consistency)
  • date
  • original (all text following 'Rev. ' from original response)
Returns:
array an array of client Connection information
Exceptions:
P4_Exceptionif the returned version string is invalid

Implements P4_Connection_Interface.

    {
        // obtain version output from p4 command
        exec($this->_getP4Path() .' -V 2>&1', $output, $returnVar);
        if ($returnVar != 0) {
            $message = "Unable to exec() the 'p4' command ("
                     . "return: " . $returnVar . ").";
            throw new P4_Exception($message);
        }

        // extract the composed version string and split it into components
        preg_match('/Rev. (.*)\.$/', array_pop($output), $matches);
        $parts = isset($matches[1]) ? preg_split('/\/| \(|\)/', $matches[1]) : null;
        if (count($parts) < 6) {
            $message = 'p4 returned an invalid version string';
            throw new P4_Exception($message);
        }

        // build identity array of version components, including original string
        $identity = array(
            'name'       => $parts[0],
            'platform'   => $parts[1],
            'version'    => $parts[2],
            'build'      => $parts[3],
            'apiversion' => $parts[2],
            'apibuild'   => $parts[3],
            'date'       => $parts[4] . '/' . $parts[5] . '/' . $parts[6],
            'original'   => $matches[1]
        );
        return $identity;
    }
P4_Connection_CommandLine::isConnected ( )

Check connected state.

Returns:
bool true if connected, false otherwise.

Implements P4_Connection_Interface.

    {
        return $this->_isConnected;
    }
P4_Connection_CommandLine::setP4Path ( path)

Set the full path/filename to the p4 executable.

Parameters:
string | null$paththe full path/filename to p4.
    {
        if (!is_string($path) && !is_null($path)) {
            throw new InvalidArgumentException(
                "Cannot set p4 path. Path must be a string or null"
            );
        }

        $this->_p4Path = $path;
    }

Member Data Documentation

P4_Connection_CommandLine::$_p4Path = null [protected]

The documentation for this class was generated from the following file: