Perforce Chronicle 2012.2/486814
API Documentation
|
Perforce Command Line Client. More...
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 |
Perforce Command Line Client.
A PHP Wrapper for the Perforce Command-Line Client (P4).
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.
P4_Connection_ConnectException | if 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.
string | array | $input | the input to prepare for p4. |
string | $command | the command to prepare input for. |
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.
string | $command | the command to run. |
array | $params | optional - arguments. |
array | string | $input | optional - input for the command - should be provided in array form when writing perforce spec records. |
boolean | $tagged | optional - true/false to enable/disable tagged output. defaults to true. |
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.
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.
string | $arg | the string to escape |
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.
string | $cmd | The command to escape. |
{ // 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.
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:
P4_Exception | if 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.
Implements P4_Connection_Interface.
{
return $this->_isConnected;
}
P4_Connection_CommandLine::setP4Path | ( | $ | path | ) |
Set the full path/filename to the p4 executable.
string | null | $path | the 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; }
P4_Connection_CommandLine::$_p4Path = null [protected] |
const P4_Connection_CommandLine::CMD_CANNOT_EXEC = 126 |
const P4_Connection_CommandLine::CMD_NOT_FOUND = 127 |
const P4_Connection_CommandLine::E_EMPTY = 0 |
const P4_Connection_CommandLine::E_FAILED = 3 |
const P4_Connection_CommandLine::E_FATAL = 4 |
const P4_Connection_CommandLine::E_INFO = 1 |
const P4_Connection_CommandLine::E_WARN = 2 |
const P4_Connection_CommandLine::P4_BINARY = 'p4' |