/ */ class P4_Connection_Extension extends P4_Connection_Abstract { protected $_instance; /** * Constructs a P4 connection object. * * @param string $port optional - the port to connect to. * @param string $user optional - the user to connect as. * @param string $client optional - the client spec to use. * @param string $password optional - the password to use. * @param string $ticket optional - a ticket to use. */ public function __construct( $port = null, $user = null, $client = null, $password = null, $ticket = null ) { // ensure that p4-php is installed. if (!extension_loaded('perforce')) { throw new P4_Exception( 'Cannot create P4 API extension instance. Perforce extension not loaded.'); } // create an instance of p4-php. $this->_instance = new P4; // disable automatic sequence expansion (call expandSequences on result object if desired) $this->_instance->expand_sequences = false; // prevent command exceptions from being thrown by P4. // we throw our own so that we can attach the result. $this->_instance->exception_level = 0; parent::__construct($port, $user, $client, $password, $ticket); } /** * Disconnect from the Perforce Server. * * @return P4_Connection_Interface provides fluent interface. */ public function disconnect() { // call parent to run disconnect callbacks. parent::disconnect(); if ($this->isConnected()) { $this->_instance->disconnect(); } return $this; } /** * Check connected state. * * @return bool true if connected, false otherwise. */ public function isConnected() { return $this->_instance->connected(); } /** * Extends parent to set our instance's password to the returned * ticket value if login succeeds. * * @return string|null the ticket issued by the server or null if * no ticket issued (ie. user has no password). * @throws P4_Connection_LoginException if login fails. */ public function login() { $ticket = parent::login(); if ($ticket) { $this->_instance->password = $ticket; } return $ticket; } /** * Extend set port to update p4-php. * * @param string $port the port to connect to. * @return P4_Connection_Interface provides fluent interface. */ public function setPort($port) { parent::setPort($port); $this->_instance->port = $this->getPort(); return $this; } /** * Extend set user to update p4-php. * * @param string $user the user to connect as. * @return P4_Connection_Interface provides fluent interface. */ public function setUser($user) { parent::setUser($user); $this->_instance->user = $this->getUser(); return $this; } /** * Extend set client to update p4-php. * * @param string $client the name of the client workspace to use. * @return P4_Connection_Interface provides fluent interface. */ public function setClient($client) { parent::setClient($client); // 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. $this->_instance->client = $this->getClient() ?: P4_Client::makeTempId(); return $this; } /** * Extend set password to update p4-php. * * @param string $password the password to use as authentication. * @return P4_Connection_Interface provides fluent interface. */ public function setPassword($password) { parent::setPassword($password); $this->_instance->password = $this->getPassword(); return $this; } /** * Extend set ticket to update p4-php. * Note: the ticket is stored in the password field in p4-php. * * @param string $ticket the ticket to use as authentication. * @return P4_Connection_Interface provides fluent interface. */ public function setTicket($ticket) { parent::setTicket($ticket); if ($ticket) { $this->_instance->password = $this->getTicket(); } return $this; } /** * Extended to set charset in p4-php. * Sets the character set to use for this perforce connection. * * You should only set a character set when connecting to a * 'unicode enabled' server, or when setting the special value * of 'none'. * * @param string $charset the charset to use (e.g. 'utf8'). * @return P4_Connection_Interface provides fluent interface. */ public function setCharset($charset) { $this->_instance->charset = $charset; return parent::setCharset($charset); } /** * Extended to set host name in p4-php. * Sets the client host name overriding the environment. * * @param string|null $host the host name to use. * @return P4_Connection_Interface provides fluent interface. */ public function setHost($host) { $this->_instance->host = $host; return parent::setHost($host); } /** * Extended to set app name in p4-php. * Set the name of the application that is using this connection. * * @param string|null $name the app name to report to the server. * @return P4_Connection_Interface provides fluent interface. */ public function setAppName($name) { $this->_instance->set_protocol('app', (string) $name); return parent::setAppName($name); } /** * Get the identity of this Connection implementation. * * Resulting array will contain: * - name * - platform * - version (p4-php version) * - build (p4-php build) * - apiversion (p4-api version) * - apibuild (p4-api build) * - date * - original (all text following 'Rev. ' from original response) * * @return array an array of client Connection information * @throws P4_Exception if the returned version string is invalid */ public function getConnectionIdentity() { // obtain the extension's identification $output = $this->_instance->identify(); // extract the version string and split into components preg_match('/\nRev. (.*)\.$/', $output, $matches); $parts = isset($matches[1]) ? preg_split('/\/| \(| API\) \(|\)/', $matches[1]) : null; if (count($parts) < 8) { $message = 'p4php 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[4], 'apibuild' => $parts[5], 'date' => $parts[6] . '/' . $parts[7] . '/' . $parts[8], 'original' => $matches[1] ); return $identity; } /** * Actually issues a command. Called by run() to perform the dirty work. * * @param string $command the command to run. * @param array $params optional - arguments. * @param array|string $input optional - input for the command - should be provided * in array form when writing perforce spec records. * @param boolean $tagged optional - true/false to enable/disable tagged output. * defaults to true. * @return P4_Result the perforce result object. */ protected function _run($command, $params = array(), $input = null, $tagged = true) { // push command to front of parameters array array_unshift($params, $command); // set input for the command. if ($input !== null) { $this->_instance->input = $input; } // toggle tagged output. $this->_instance->tagged = (bool) $tagged; // establish connection to perforce server. if (!$this->isConnected()) { $this->connect(); } // run command. $data = call_user_func_array(array($this->_instance, "run"), $params); // collect data in result object and ensure output is in array form. $result = new P4_Result($command, $data, $tagged); $result->setErrors($this->_instance->errors); $result->setWarnings($this->_instance->warnings); return $result; } /** * Prepare input for passing to the p4 extension. * Ensure input is either a string or an array of strings. * * @param string|array $input the input to prepare for p4. * @param string $command the command to prepare input for. * @return string|array the prepared input. */ protected function _prepareInput($input, $command) { // if input is not an array, cast to string and return. if (!is_array($input)) { return (string) $input; } // ensure each element of array is a string. $stringify = function(&$input) { $input = (string) $input; }; array_walk_recursive($input, $stringify); return $input; } /** * Does real work of establishing connection. Called by connect(). * * @throws P4_Connection_ConnectException if the connection fails. */ protected function _connect() { // temporarily enable exceptions to catch connection failure. $this->_instance->exception_level = 1; try { $this->_instance->connect(); $this->_instance->exception_level = 0; } catch (P4_Exception $e) { $this->_instance->exception_level = 0; throw new P4_Connection_ConnectException( "Connect failed: " . $e->getMessage() ); } } }