Perforce Chronicle 2012.2/486814
API Documentation
|
Abstracts operations against Perforce files. More...
Public Member Functions | |
add ($change=null, $fileType=null) | |
Open the file for add. | |
clearAnnotateCache () | |
Clear the annotated content cache. | |
clearAttribute ($attribute, $force=false) | |
Clear the given attribute on the file. | |
clearAttributes ($attributes, $force=false) | |
Clear the specified attributes on this file. | |
clearContentCache () | |
Clear the depot file cache. | |
clearStatusCache () | |
Clear the file status cache. | |
delete ($change=null, $force=true) | |
Open this file for delete. | |
deleteLocalFile () | |
Delete the local file from the workspace. | |
edit ($change=null, $fileType=null, $force=true) | |
Open the file for edit. | |
flush () | |
Flush the file - tells the server we have the file. | |
getAnnotateContent () | |
Get the annotated contents of the file in Perforce. | |
getAttribute ($attribute) | |
Get the named attribute from the set of submitted attributes on this file. | |
getAttributes ($open=false) | |
Get all submitted attributes of this file. | |
getBasename ($suffix=null) | |
Get the basename of the file. | |
getChange () | |
Convenience function to return the change object associated with the file at its current revspec. | |
getChanges (array $options=null) | |
Convienence function to return all changes associated with this file. | |
getDepotContents () | |
Get the contents of the file in Perforce. | |
getDepotFilename () | |
Get the path to the file in depot file syntax. | |
getDepotPath () | |
Get the depot path to the file. | |
getFields () | |
Return array with all model fields. | |
getFileSize () | |
Get the file's size in the depot. | |
getFilespec ($stripRevspec=false) | |
Get the filespec used to identify this file. | |
getFilespecWithRevision () | |
Get the filespec used to identify this file including a revision specification if one is known. | |
getLocalContents () | |
Get the contents of the local file in the client workspace. | |
getLocalFilename () | |
Get the path to the file in local file syntax. | |
getLocalFileSize () | |
Get the size of the local client file. | |
getLocalPath () | |
Get the local path to the file. | |
getOpenAttribute ($attribute) | |
Get the named attribute from the set of pending attributes on this file. | |
getOpenAttributes () | |
Get all pending attributes for this file. | |
getStatus ($field=null) | |
Get file status (run fstat on file). | |
getValue ($field) | |
Return value of given field of the model. | |
hasAttribute ($attribute) | |
Check if the file has the named attribute. | |
hasField ($field) | |
Check if given field is valid model field. | |
hasOpenAttribute ($attribute) | |
Check if the file has the named open attribute. | |
hasStatusField ($field) | |
Determine if this file has the named status field. | |
isDeleted () | |
Test if a file is deleted in the depot. | |
isHead () | |
Checks if this file is at the head revision or not. | |
isOpened () | |
Check if the file is opened in Perforce by the current client. | |
lock () | |
Lock this file in the depot. | |
needsResolve () | |
Used to check if the file requires resolve or not. | |
open ($change=null, $fileType=null, $force=true) | |
Open file for add or edit as appropriate. | |
reopen ($change=null, $type=null) | |
Open the file in another change and/or as a different filetype. | |
resolve ($options) | |
Resolves the file based on the passed option(s). | |
revert ($options=null) | |
Revert the file. | |
setAttribute ($key, $value, $propagate=true, $force=false) | |
Set the given attribute/value on the file. | |
setAttributes ($attributes, $propagate=true, $force=false) | |
Set attributes on this file. | |
setContentCache ($content) | |
Prime the depot file cache with the given value. | |
setFilespec ($filespec) | |
Set the filespec identifier for the file/revision. | |
setLocalContents ($content) | |
Write contents to the local client file. | |
setStatusCache ($status) | |
Set the file status cache to the given array of fields/values. | |
submit ($description, $options=null) | |
Submit the file to perforce. | |
sync ($force=false, $flush=false) | |
Sync the file from the depot. | |
touchLocalFile () | |
Touch the local client file. | |
unlock () | |
Unlock this file in the depot. | |
where () | |
Determine how this file maps through the client view. | |
Static Public Member Functions | |
static | count ($query, P4_Connection_Interface $connection=null) |
Count files matching the given query. | |
static | exists ($filespec, P4_Connection_Interface $connection=null, $excludeDeleted=false) |
Check if the given filespec is known to Perforce. | |
static | fetch ($filespec, P4_Connection_Interface $connection=null) |
Fetch a model of the given filespec. | |
static | fetchAll ($query, P4_Connection_Interface $connection=null) |
Fetch all files matching the given query. | |
static | hasRevspec ($filespec) |
Check if the given filespec has a revision specifier. | |
static | stripRevspec ($filespec) |
Strip the revision specifier from a file specification. | |
Public Attributes | |
const | ALL_FILES = '//...' |
const | REVERT_UNCHANGED = 'unchanged' |
Protected Member Functions | |
_isOpenForAction ($action) | |
Checks if the file is open for the given action. | |
_openForAction ($action, $change=null, $fileType=null, $force=true) | |
Open the file for the specified action. | |
Static Protected Member Functions | |
static | _parsePrintOutput ($data) |
Parses print output for one or more files into the below format: | |
Protected Attributes | |
$_cache = array() | |
$_filespec = null |
Abstracts operations against Perforce files.
THEORY OF OPERATION
Unlike a typical database, all changes to Perforce files must be pended to the current client workspace before they can be commited.
The file model provides access to two copies of file data: the submitted depot copy and the client workspace copy. When you are accessing file data (be it file contents or file attributes), you must consider which of these sources you want to get the data from.
For example, if you call getDepotContents() you will get the submitted depot copy of the file; whereas, if you call getLocalContents() you will get the contents of the client workspace file.
The class attempts to faithfully represent the behavior of Perforce. There is, however, some simplification at work. In particular, the open() method will automatically add or edit a file as appropriate. It will also sync the file to the client if necessary.
Similarly, if a file is open for delete, the add, edit and open methods will revert the file and reopen it. Conversely, if delete() is called on a file that is opened (but not for delete), the file will be reverted and then deleted(). To suppress this behavior, pass false as the force option.
COMMON USAGE
To fetch a file from Perforce, call fetch() and pass the filespec of the file you wish to retrieve. For example:
$file = P4_File::fetch('//depot/file');
To fetch several files, call fetchAll() and pass a file query object representing the fstat options that you wish to use. For example:
$files = P4_File::fetchAll( new P4_File_Query(array('filespecs' => '//depot/path/...')) );
The query class also has options to filter, sort and limit files. See the P4_File_Query class for additional details.
To submit a file:
$file = new P4_File; $file->setFilespec('//depot/file'); $file->open(); $file->setLocalContents('new file content'); $file->submit('Description of change');
To delete a file:
$file->delete(); $file->submit('Description of change');
P4_File::_isOpenForAction | ( | $ | action | ) | [protected] |
Checks if the file is open for the given action.
Applies a bit of fuzzy logic to consider move/add to be open for edit since a file must be opened for edit before it can be moved.
string | $action | the action to check for |
{ // if not opened at all, nothing more to check if (!$this->isOpened()) { return false; } $openAction = $this->getStatus('action'); if ($openAction == $action) { return true; } // consider move/add to also be open for edit - a file must be opened // for edit before it can be moved; therefore, a move/add file is open // for edit - without this, calling edit() on the target of a move // would incur a revert unless force is explicitly set to false. if ($openAction == 'move/add' && $action == 'edit') { return true; } return false; }
P4_File::_openForAction | ( | $ | action, |
$ | change = null , |
||
$ | fileType = null , |
||
$ | force = true |
||
) | [protected] |
Open the file for the specified action.
string | $action | the action to open the file for ('add', 'edit' or 'delete'). |
int | $change | optional - a numbered pending change to open the file in. |
string | $fileType | optional - the file-type to open the file as. |
bool | $force | optional - defaults to true - set to false to avoid reopening. |
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); // action must be one of: add, edit or delete. if (!in_array($action, array('add', 'edit', 'delete'))) { throw new P4_File_Exception("Cannot open file. Invalid open 'action' specified."); } // if already opened for specified action, verify change and type, then return. if ($this->_isOpenForAction($action)) { if (($change && $this->getStatus('change') !== $change) || ($fileType && $this->getStatus('type') !== $fileType) ) { $this->reopen($change, $fileType); } return $this; } $p4 = $this->getConnection(); $file = $this->getFilespec(true); // if force is true, revert files opened for the wrong action // unless it's open for integrate and we are trying to edit // or it's open for branch and we are trying to add (to keep // the integration credit). if ($force && $this->isOpened() && !$this->_isOpenForAction($action) && !($action == 'edit' && $this->_isOpenForAction('integrate')) && !($action == 'add' && $this->_isOpenForAction('branch')) ) { $result = $p4->run('revert', $file); } // setup command flags. $flags = array(); if ($change) { $flags[] = '-c'; $flags[] = $change; } if ($fileType) { $flags[] = '-t'; $flags[] = $fileType; } // allows delete to work without having to sync file. if ($action === 'delete') { $flags[] = '-v'; } $flags[] = $file; // throw for edit or delete of a deleted file (these are dead ends!) // use the -n flag to see what would happen without actually opening file. if (in_array($action, array('edit', 'delete'))) { $result = $p4->run($action, array_merge(array('-n'), $flags)); foreach ($result->getData() as $data) { if (is_string($data) && preg_match("/warning: $action of deleted file/", $data) ) { throw new P4_File_Exception( "Failed to open file for $action: " . $data ); } } } // open file for specified action. $result = $p4->run($action, $flags); // check for warnings. if ($result->hasWarnings()) { throw new P4_File_Exception( "Failed to open file for $action: " . implode(", ", $result->getWarnings()) ); } // status has changed - clear the status cache. $this->clearStatusCache(); // verify file was opened for specified action. if (!$this->hasStatusField('action') || $this->getStatus('action') !== $action) { throw new P4_File_Exception( "Failed to open file for $action: " . $result->getData(0) ); } return $this; }
static P4_File::_parsePrintOutput | ( | $ | data | ) | [static, protected] |
Parses print output for one or more files into the below format:
Array ( [//depot/path/to/file.ext] => Array ( [depotFile] => //depot/path/to/file.ext [rev] => 6 [change] => 222450 [action] => edit [type] => text [time] => 1257743394 [content] => Full content of file ) )
array | $data | Data from p4 print result |
{ $files = array(); // print output consists of the following elements, repeated for each file: // - file info // - file content (repeated for every 4k of file content, or when server feels like it) foreach ($data as $block) { // If we are at a meta-data block, store the file name and meta-data then continue if (is_array($block) && isset($block['depotFile'])) { $name = $block['depotFile']; $files[$name] = $block; // prime content entry as string $files[$name]['content'] = ''; continue; } // Be defensive, clear file name if we hit an unrecognized block if (is_array($block)) { $name = null; } // If we made it this far, and we have a file name, it's a content block; append it if (isset($name)) { $files[$name]['content'] .= $block; } } return $files; }
P4_File::add | ( | $ | change = null , |
$ | fileType = null |
||
) |
Open the file for add.
int | $change | optional - a numbered pending change to open the file in. |
string | $fileType | optional - the file-type to open the file as. |
{ return $this->_openForAction('add', $change, $fileType, false); }
P4_File::clearAnnotateCache | ( | ) |
Clear the annotated content cache.
{ $this->_cache['annotatedContent'] = null; return $this; }
P4_File::clearAttribute | ( | $ | attribute, |
$ | force = false |
||
) |
Clear the given attribute on the file.
By default the cleared attribute will be pended. To clear attributes in the depot directly, set the force flag to true.
string | $attribute | the name of the attribute to clear. |
bool | $force | optional - defaults to false - clear the attribute in the depot directly. |
{ return $this->clearAttributes(array($attribute), $force); }
P4_File::clearAttributes | ( | $ | attributes, |
$ | force = false |
||
) |
Clear the specified attributes on this file.
array | $attributes | the set of attributes to clear. |
bool | $force | optional - clear the attributes in the depot directly by default attributes are pended to the client workspace. |
{ if (!is_array($attributes)) { throw new InvalidArgumentException( "Can't clear attributes. Attributes must be an array." ); } // if no attributes given, nothing to clear. if (empty($attributes)) { return $this; } // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $filespec = $force ? $this->getFilespecWithRevision() : $this->getFilespec(true); // make -n/attr-name argument pairs. $params = array(); foreach ($attributes as $attribute) { $params[] = "-n"; $params[] = $attribute; } // there is a potential to exceed the arg-max/option-limit; // run attribute command as few times as possible $connection = $this->getConnection(); $prefixParams = $force ? array('-f') : array(); foreach ($connection->batchArgs($params, $prefixParams, array($filespec), 2) as $batch) { $connection->run('attribute', $batch); } // status has changed - clear the status cache. $this->clearStatusCache(); return $this; }
P4_File::clearContentCache | ( | ) |
Clear the depot file cache.
{ unset($this->_cache['content']); return $this; }
P4_File::clearStatusCache | ( | ) |
Clear the file status cache.
{ $this->_cache['status'] = null; return $this; }
static P4_File::count | ( | $ | query, |
P4_Connection_Interface $ | connection = null |
||
) | [static] |
Count files matching the given query.
This is a faster alternative to counting the result of fetchAll().
P4_File_Query | array | $query | A query object or array expressing fstat options. |
P4_Connection_Interface | $connection | optional - a specific connection to use. |
{ if (!$query instanceof P4_File_Query && !is_array($query)) { throw new InvalidArgumentException( 'Query must be a P4_File_Query or array.' ); } // normalize array input to a query if (is_array($query)) { $query = new P4_File_Query($query); } // ensure caller provided a filespec. if (!count($query->getFilespecs())) { throw new InvalidArgumentException( 'Cannot count files. No filespecs provided in query.' ); } // if no connection given, use default. $connection = $connection ?: static::getDefaultConnection(); // remove options that cause unnecessary work for the server $query = clone $query; $query->setSortBy(null)->setReverseOrder(false); // only fetch a single field for performance. $query->setLimitFields('depotFile'); // get fstat flags for given query and run fstat command. $flags = array_merge($query->getFstatFlags(), $query->getFilespecs()); $result = $connection->run('fstat', $flags); $count = count($result->getData()); // if fetching by change, the last block of data contains // the change description - remove it (unless we're fetching // from the default changelist) if ($query->getLimitToChangelist() !== null && $query->getLimitToChangelist() !== P4_Change::DEFAULT_CHANGE ) { $count--; } return $count; }
P4_File::delete | ( | $ | change = null , |
$ | force = true |
||
) |
Open this file for delete.
If the file is open, but not for delete, the file will be reverted and then deleted unless the force flag has been set to false.
int | $change | optional - a numbered pending change to open the file in. |
bool | $force | optional - defaults to true - reverts files that are open then deletes them. if false, files that are open (not for delete) will result in an exception being thrown. |
{ return $this->_openForAction('delete', $change, null, $force); }
P4_File::deleteLocalFile | ( | ) |
Delete the local file from the workspace.
P4_File_Exception | if the local file cannot be deleted. |
{ $localFile = $this->getLocalFilename(); if (!file_exists($localFile)) { throw new P4_File_Exception("Cannot delete local file. File does not exist."); } chmod($localFile, 0777); if (unlink($localFile) === false) { throw new P4_File_Exception("Failed to delete local file."); } return $this; }
P4_File::edit | ( | $ | change = null , |
$ | fileType = null , |
||
$ | force = true |
||
) |
Open the file for edit.
If the file is opened for delete, the file will be reverted and then edited unless the force flag has been set to false.
int | $change | optional - a numbered pending change to open the file in. |
string | $fileType | optional - the file-type to open the file as. |
bool | $force | optional - defaults to true - set to false to avoid reopening. |
{ // If our 'have' rev and our 'head' revision aren't the // same value throw an exception (caller needs to sync). if (!$this->hasStatusField('haveRev') || $this->getStatus('headRev') != $this->getStatus('haveRev') ) { throw new P4_File_Exception( 'Workspace file is not at specified revision; unable to edit' ); } return $this->_openForAction('edit', $change, $fileType, $force); }
static P4_File::exists | ( | $ | filespec, |
P4_Connection_Interface $ | connection = null , |
||
$ | excludeDeleted = false |
||
) | [static] |
Check if the given filespec is known to Perforce.
string | $filespec | a filespec with no wildcards. |
P4_Connection_Interface | $connection | optional - a specific connection to use. |
bool | $excludeDeleted | optional - exclude deleted files (defaults to false). |
{ static::_validateFilespec($filespec); // if no connection given, use default. $connection = $connection ?: static::getDefaultConnection(); // run files to see if file exists. $result = $connection->run('files', $filespec); if ($result->hasWarnings()) { return false; } elseif ($excludeDeleted && strstr($result->getData(0, 'action'), 'delete') !== false) { return false; } else { $rev = $result->getData(0, 'rev'); // this really shouldn't happen; just being defensive if ($rev === false) { throw new P4_File_Exception('Failed to capture revision during existance test'); } return $rev; } }
static P4_File::fetch | ( | $ | filespec, |
P4_Connection_Interface $ | connection = null |
||
) | [static] |
Fetch a model of the given filespec.
string | $filespec | a filespec with no wildcards - the filespec may be in any one of depot, client or local file syntax. |
P4_Connection_Interface | $connection | optional - a specific connection to use. |
{ // if no connection given, use default. $connection = $connection ?: static::getDefaultConnection(); // determine whether the file exists. $rev = self::exists($filespec, $connection); if ($rev === false) { throw new P4_File_NotFoundException( "Cannot fetch file '$filespec'. File does not exist." ); } // create new file instance and set the key. $file = new static($connection); $file->setFilespec($filespec); $file->_cache['revision'] = $rev; return $file; }
static P4_File::fetchAll | ( | $ | query, |
P4_Connection_Interface $ | connection = null |
||
) | [static] |
Fetch all files matching the given query.
P4_File_Query | array | $query | A query object or array expressing fstat options. |
P4_Connection_Interface | $connection | optional - a specific connection to use. |
InvalidArgumentException | if no filespec is given. |
{ if (!$query instanceof P4_File_Query && !is_array($query)) { throw new InvalidArgumentException( 'Query must be a P4_File_Query or array.' ); } // normalize array input to a query if (is_array($query)) { $query = new P4_File_Query($query); } // ensure caller provided a filespec. if (!count($query->getFilespecs())) { throw new InvalidArgumentException( 'Cannot fetch files. No filespecs provided in query.' ); } // if no connection given, use default. $connection = $connection ?: static::getDefaultConnection(); // get fstat flags for given query options and run fstat command. $flags = array_merge($query->getFstatFlags(), $query->getFilespecs()); // try/catch parent to deal with the exception we get on non-existend depots try { $result = $connection->run('fstat', $flags); } catch (P4_Connection_CommandException $e) { // if the 'depot' has been interpreted as an invalid client, just return no matches if (preg_match("/Command failed: .+ - must refer to client/", $e->getMessage())) { return new P4_Model_Iterator; } // unexpected error; rethrow it throw $e; } // if fetching by change, the last block of data contains // the change description - remove it (unless we're fetching // from the default changelist) $dataBlocks = $result->getData(); if ($query->getLimitToChangelist() !== null && $query->getLimitToChangelist() !== P4_Change::DEFAULT_CHANGE) { array_pop($dataBlocks); } // generate file models from fstat output. $files = new P4_Model_Iterator; foreach ($dataBlocks as $data) { $file = new static($connection); $file->setFilespec($data['depotFile']); $file->setStatusCache($data); $files[] = $file; } return $files; }
P4_File::flush | ( | ) |
Flush the file - tells the server we have the file.
P4_File_Exception | if the flush fails. |
{ return $this->sync(false, true); }
P4_File::getAnnotateContent | ( | ) |
Get the annotated contents of the file in Perforce.
Annotated file content is fetched once and then cached in the instance. The cache can be primed via setAnnotateCache(). It can be cleared via clearAnnotateCache().
array( 'upper' => <upper version="" number>="">, 'lower' => <lower version="" number>="">, 'data' => <text data="" for="" the="" current="" line>="">, )
{ if ( !array_key_exists('annotatedContent', $this->_cache) || !isset($this->_cache['annotatedContent']) ) { // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $result = $this->getConnection()->run('annotate', $this->getFilespec(true)); $annotate = $result->getData(); // remove the command's metadata array_shift($annotate); $this->_cache['annotatedContent'] = $annotate; } return $this->_cache['annotatedContent']; }
P4_File::getAttribute | ( | $ | attribute | ) |
Get the named attribute from the set of submitted attributes on this file.
Submitted attributes are attributes that have been committed to the depot.
string | $attribute | the name of the attribute to get the value of. |
{ return $this->getStatus('attr-' . $attribute); }
P4_File::getAttributes | ( | $ | open = false | ) |
Get all submitted attributes of this file.
Submitted attributes are attributes that have been committed to the depot.
bool | $open | optional - get open attributes - defaults to false. |
{ $attributes = array(); foreach ($this->getStatus() as $field => $value) { if (!$open && substr($field, 0, 5) == 'attr-') { $attributes[substr($field, 5)] = $value; } else if ($open && substr($field, 0, 9) == 'openattr-') { $attributes[substr($field, 9)] = $value; } } return $attributes; }
P4_File::getBasename | ( | $ | suffix = null | ) |
Get the basename of the file.
string | $suffix | if filename ends in this suffix it will be cut off. |
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); return basename($this->getFilespec(true), $suffix); }
P4_File::getChange | ( | ) |
Convenience function to return the change object associated with the file at its current revspec.
{ return P4_Change::fetch($this->getStatus('headChange'), $this->getConnection()); }
P4_File::getChanges | ( | array $ | options = null | ) |
Convienence function to return all changes associated with this file.
array | $options | optional - array of options to augment fetch behavior. supported options are the same as P4_Change, except for the use of FETCH_BY_FILESPEC which is not permitted here. |
{ $this->_validateHasFilespec(); $options = array_merge( (array) $options, array(P4_Change::FETCH_BY_FILESPEC => $this->getFilespec(true)) ); return P4_Change::fetchAll($options, $this->getConnection()); }
P4_File::getDepotContents | ( | ) |
Get the contents of the file in Perforce.
File content is fetched once and then cached in the instance. The cache can be primed via setContentCache(). It can be cleared via clearContentCache().
P4_File_Exception | if the print command fails. |
{ if (!array_key_exists('content', $this->_cache)) { // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $result = $this->getConnection()->run('print', $this->getFilespecWithRevision()); // check for warnings. if ($result->hasWarnings()) { throw new P4_File_Exception( "Failed to get depot contents: " . implode(", ", $result->getWarnings()) ); } $print = static::_parsePrintOutput($result->getData()); // get first element $print = reset($print); $this->_cache['content'] = $print['content']; $this->_cache['revision'] = $print['rev']; } return $this->_cache['content']; }
P4_File::getDepotFilename | ( | ) |
Get the path to the file in depot file syntax.
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $filespec = $this->getFilespec(true); // if filespec is in depot-file syntax, return it. // note, we must verify that it doesn't start with the client name. $clientPrefix = "//" . $this->getConnection()->getClient() . "/"; if (strlen($filespec) >=2 && substr($filespec, 0, 2) == '//' && substr($filespec, 0, strlen($clientPrefix)) != $clientPrefix) { return $filespec; } // otherwise, get depot file from p4 where. $where = $this->where(); return $where[0]; }
P4_File::getDepotPath | ( | ) |
Get the depot path to the file.
{ return dirname($this->getDepotFilename()); }
P4_File::getFields | ( | ) |
Return array with all model fields.
Implements P4_ModelInterface.
{ return array_keys($this->getStatus()); }
P4_File::getFileSize | ( | ) |
Get the file's size in the depot.
{ if (!$this->hasStatusField('fileSize')) { throw new P4_File_Exception('The file does not have a fileSize attribute.'); } return (int) $this->getStatus('fileSize'); }
P4_File::getFilespec | ( | $ | stripRevspec = false | ) |
Get the filespec used to identify this file.
If a revision specifier was passed to setFilespec or fetch, it will be returned here; otherwise, no revision specifier will be present.
bool | $stripRevspec | optional - revspecs will be removed, if present, when true |
{ return $stripRevspec ? static::stripRevspec($this->_filespec) : $this->_filespec; }
P4_File::getFilespecWithRevision | ( | ) |
Get the filespec used to identify this file including a revision specification if one is known.
If getFilespec includes a revspec, this value is used. Otherwise, if we have fetched file contents or status the corresponding numeric revision is used.
{ $filespec = $this->getFilespec(); if ($filespec === null || static::hasRevspec($filespec)) { return $filespec; } $revision = ''; if (isset($this->_cache['revision'])) { $revision = '#' . $this->_cache['revision']; } return $this->_filespec . $revision; }
P4_File::getLocalContents | ( | ) |
Get the contents of the local file in the client workspace.
{ if (!file_exists($this->getLocalFilename())) { throw new P4_File_Exception( 'Cannot get local file contents. Local file does not exist.' ); } return file_get_contents($this->getLocalFilename()); }
P4_File::getLocalFilename | ( | ) |
Get the path to the file in local file syntax.
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $filespec = $this->getFilespec(true); // if filespec is in local-file syntax return it. if (strlen($filespec) >=2 && substr($filespec, 0, 2) != '//') { return $filespec; } // otherwise, get local filename from p4 where. $where = $this->where(); return $where[2]; }
P4_File::getLocalFileSize | ( | ) |
Get the size of the local client file.
{ if (!file_exists($this->getLocalFilename())) { throw new P4_File_Exception('The local file does not exist.'); } return (int) filesize($this->getLocalFilename()); }
P4_File::getLocalPath | ( | ) |
Get the local path to the file.
{ return dirname($this->getLocalFilename()); }
P4_File::getOpenAttribute | ( | $ | attribute | ) |
Get the named attribute from the set of pending attributes on this file.
Pending attributes are attributes that have been written to the client but are not yet submitted to the depot.
string | $attribute | the name of the open attribute to get the value of. |
{ return $this->getStatus('openattr-' . $attribute); }
P4_File::getOpenAttributes | ( | ) |
Get all pending attributes for this file.
Pending attributes are attributes that have been written to the client but are not yet submitted to the depot.
{ return $this->getAttributes(true); }
P4_File::getStatus | ( | $ | field = null | ) |
Get file status (run fstat on file).
File status is fetched once and then cached in the instance. The cache can be primed via setStatusCache(). It can be cleared via clearStatusCache().
Attributes are fetched along with the status.
string | $field | optional - a specific status field to get. by default all fields are returned. |
P4_File_Exception | if the requested status field does not exist. |
{ // if cache is not primed, run fstat. if (!array_key_exists('status', $this->_cache) || !isset($this->_cache['status'])) { // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $result = $this->getConnection()->run( 'fstat', array('-Oal', $this->getFilespecWithRevision()) ); if ($result->hasWarnings()) { throw new P4_File_Exception( "Cannot get status: " . implode(", ", $result->getWarnings()) ); } if (is_array($result->getData(0))) { $this->setStatusCache($result->getData(0)); } else { $this->setStatusCache(array()); } } // return a specific field or all fields as appropriate. if ($field) { if (!array_key_exists($field, $this->_cache['status'])) { throw new P4_File_Exception("Can't fetch status. The requested field ('" . $field . "') does not exist."); } else { return $this->_cache['status'][$field]; } } else { return $this->_cache['status']; } }
P4_File::getValue | ( | $ | field | ) |
Return value of given field of the model.
string | $field | model field to retrieve |
Implements P4_ModelInterface.
{ return $this->getStatus($field); }
P4_File::hasAttribute | ( | $ | attribute | ) |
Check if the file has the named attribute.
string | $attribute | the name of the attribute to check for. |
{ return array_key_exists($attribute, $this->getAttributes()); }
P4_File::hasField | ( | $ | field | ) |
Check if given field is valid model field.
string | $field | model field to check |
Implements P4_ModelInterface.
{ return $this->hasStatusField($field); }
P4_File::hasOpenAttribute | ( | $ | attribute | ) |
Check if the file has the named open attribute.
string | $attribute | the name of the open attribute to check for. |
{ return array_key_exists($attribute, $this->getOpenAttributes()); }
static P4_File::hasRevspec | ( | $ | filespec | ) | [static] |
Check if the given filespec has a revision specifier.
string | $filespec | the filespec to check for a revspec. |
{ if (strpos($filespec, "#") !== false || strpos($filespec, "@") !== false) { return true; } return false; }
P4_File::hasStatusField | ( | $ | field | ) |
Determine if this file has the named status field.
string | $field | the name of the field to check for. |
{ try { $this->getStatus($field); return true; } catch (P4_File_Exception $e) { return false; } }
P4_File::isDeleted | ( | ) |
Test if a file is deleted in the depot.
Note: this method reports the deleted status based on the filespec, which could be a non-head revision.
{ $headAction = $this->getStatus('headAction'); if (preg_match('/delete/', $headAction)) { return true; } return false; }
P4_File::isHead | ( | ) |
Checks if this file is at the head revision or not.
{ $head = static::exists($this->getFilespec(true), $this->getConnection()); if ($head === $this->getStatus('headRev')) { return true; } return false; }
P4_File::isOpened | ( | ) |
Check if the file is opened in Perforce by the current client.
{ if ($this->hasStatusField('action')) { return true; } else { return false; } }
P4_File::lock | ( | ) |
Lock this file in the depot.
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $this->getConnection()->run('lock', $this->getFilespec(true)); // status has changed - clear the status cache. $this->clearStatusCache(); return $this; }
P4_File::needsResolve | ( | ) |
Used to check if the file requires resolve or not.
This function will return true only when a resolve is scheduled. It doesn't attempt to look at the current state and estimate if calling 'submit' would result in an unresolved exception.
{ $this->_validateHasFilespec(); $result = $this->getConnection()->run( 'resolve', '-n', $this->getFilespecWithRevision() ); return (bool) $result->hasData(); }
P4_File::open | ( | $ | change = null , |
$ | fileType = null , |
||
$ | force = true |
||
) |
Open file for add or edit as appropriate.
If the file is open for delete, revert and edit unless force=false. Will sync the file before opening it for edit.
int | $change | optional - a numbered pending change to open the file in. |
string | $fileType | optional - the file-type to open the file as. |
bool | $force | optional - defaults to true - reverts files that are open for delete then reopens them. if false, files that are open for delete will result in an exception being thrown. |
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); // add the file if it doesn't exist or is deleted at head - otherwise edit. if (!static::exists($this->getFilespecWithRevision(), $this->getConnection()) || $this->getStatus('headAction') == 'delete') { $this->add($change, $fileType); } else { $this->sync(true); $this->edit($change, $fileType, $force); } return $this; }
P4_File::reopen | ( | $ | change = null , |
$ | type = null |
||
) |
Open the file in another change and/or as a different filetype.
string | $change | the change list to open the file in. |
string | $type | the filetype to open the file as. |
InvalidArgumentException | if neither a change nor a type are given. |
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); // ensure user has specified a change and/or a type if (!$change && !$type) { throw new InvalidArgumentException( 'Cannot reopen file. You must provide a change and/or a filetype.' ); } $params = array(); if ($change) { $params[] = '-c'; $params[] = $change; } if ($type) { $params[] = '-t'; $params[] = $type; } $params[] = $this->getFilespec(true); $this->getConnection()->run('reopen', $params); // status has changed - clear the status cache. $this->clearStatusCache(); return $this; }
P4_File::resolve | ( | $ | options | ) |
Resolves the file based on the passed option(s).
You must specify one of the below: RESOLVE_ACCEPT_MERGED Automatically accept the Perforce-recom mended file revision: if theirs is identical to base, accept yours; if yours is identical to base, accept theirs; if yours and theirs are different from base, and there are no conflicts between yours and theirs; accept merge; other wise, there are conflicts between yours and theirs, so skip this file. RESOLVE_ACCEPT_YOURS Accept Yours, ignore theirs. RESOLVE_ACCEPT_THEIRS Accept Theirs. Use this flag with caution! RESOLVE_ACCEPT_SAFE Safe Accept. If either yours or theirs is different from base, (and the changes are in common) accept that revision. If both are different from base, skip this file. RESOLVE_ACCEPT_FORCE Force Accept. Accept the merge file no matter what. If the merge file has conflict markers, they will be left in, and you'll need to remove them by editing the file.
Additionally, one of the following whitespace options can, optionally, be passed: IGNORE_WHITESPACE_CHANGES Ignore whitespace-only changes (for instance, a tab replaced by eight spaces) IGNORE_WHITESPACE Ignore whitespace altogether (for instance, deletion of tabs or other whitespace) IGNORE_LINE_ENDINGS Ignore differences in line-ending convention
array | string | $options | Resolve option(s); must include a RESOLVE_* preference. |
{ if (is_string($options)) { $options = array($options); } if (!is_array($options)) { throw new InvalidArgumentException('Expected a string or array of options.'); } // limit the resolve to just our file and let change do the work $options[P4_Change::RESOLVE_FILE] = $this->getFilespec(true); $this->getChange()->resolve($options); return $this; }
P4_File::revert | ( | $ | options = null | ) |
Revert the file.
string | array | null | $options | options to influence the operation: REVERT_UNCHANGED - only revert if unchanged |
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); // if the unchanged option is given, add -a flag. $params = array(); $unchanged = in_array(static::REVERT_UNCHANGED, (array) $options); if ($unchanged) { $params[] = "-a"; } $params[] = $this->getFilespec(true); $this->getConnection()->run('revert', $params); // status has changed - clear the status cache. $this->clearStatusCache(); return $this; }
P4_File::setAttribute | ( | $ | key, |
$ | value, | ||
$ | propagate = true , |
||
$ | force = false |
||
) |
Set the given attribute/value on the file.
By default attributes will propagate to new revisions of the file To disable this, set the propagate argument to false.
By default attributes will be pended. To write attributes to the depot directly, set the force flag to true.
string | $key | the name of the attribute to write. |
string | null | $value | the value to write. |
bool | $propagate | optional - defaults to true - propagate the attribute to new revisions. |
bool | $force | optional - defaults to false - write the attribute to the depot directly. |
{ // ensure attribute key name is valid. // we do this prior to forming the array as an // invalid key (e.g. an array) would cause an error. $validator = new P4_Validate_AttributeName; if (!$validator->isValid($key)) { throw new InvalidArgumentException("Cannot set attribute. Attribute name is invalid."); } return $this->setAttributes(array($key => $value), $propagate, $force); }
P4_File::setAttributes | ( | $ | attributes, |
$ | propagate = true , |
||
$ | force = false |
||
) |
Set attributes on this file.
Does not clear existing attributes.
array | $attributes | the set of key/value pairs to set on the file. |
bool | $propagate | optional - defaults to true - automatically propagate the attributes to new revisions. |
bool | $force | optional - write the attributes to the depot directly by default attributes are pended to the client workspace. |
{ if (!is_array($attributes)) { throw new InvalidArgumentException( "Can't set attributes. Attributes must be an array." ); } // if no attributes to set, nothing to do. if (empty($attributes)) { return $this; } // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $params = array(); foreach ($attributes as $key => $value) { $value = is_null($value) ? '' : $value; // ensure value is a string. if (!is_string($value)) { throw new InvalidArgumentException("Cannot set attribute. Value must be a string."); } // ensure attribute key name is valid. $validator = new P4_Validate_AttributeName; if (!$validator->isValid($key)) { throw new InvalidArgumentException("Cannot set attribute. Attribute name is invalid."); } // add params for attribute name/value. $params[] = '-n'; $params[] = $key; $params[] = '-v'; $params[] = bin2hex($value); } // setup shared inital parameters $prefixParams = array(); if ($propagate) { $prefixParams[] = '-p'; } if ($force) { $prefixParams[] = '-f'; } // write value in binhex to avoid problems with binary data. $prefixParams[] = '-e'; // permit revspec only if force writing attribute. $filespec = $force ? $this->getFilespecWithRevision() : $this->getFilespec(true); // see if we can set multiple attributes at once (for performance) // if we're unable (e.g. a value exceeds arg-max), set individually via input. $batches = array(); $connection = $this->getConnection(); try { $batches = $connection->batchArgs($params, $prefixParams, array($filespec), 4); } catch (P4_Exception $e) { $prefixParams[] = '-i'; foreach ($attributes as $key => $value) { $value = is_null($value) ? '' : $value; $result = $this->getConnection()->run( 'attribute', array_merge($prefixParams, array('-n', $key, $filespec)), bin2hex($value) ); // stop processing if we encounter warnings. if ($result->hasWarnings()) { break; } } } // if we were able to batch the arguments, process them now. foreach ($batches as $batch) { $result = $this->getConnection()->run('attribute', $batch); // stop processing if we encounter warnings. if ($result->hasWarnings()) { break; } } if ($result->hasWarnings()) { throw new P4_File_Exception( "Failed to set attribute(s) on file: " . implode(", ", $result->getWarnings()) ); } // status has changed - clear the status cache. $this->clearStatusCache(); return $this; }
P4_File::setContentCache | ( | $ | content | ) |
Prime the depot file cache with the given value.
string | $content | the contents of the file in the depot. |
{ $this->_cache['content'] = $content; return $this; }
P4_File::setFilespec | ( | $ | filespec | ) |
Set the filespec identifier for the file/revision.
Filespec may be given in depot, client or local file-system syntax. The filename may be followed by a revision specifier. Wildcards are not permitted in the filespec.
For more information on filespecs visit: http://perforce.com/perforce/doc.current/manuals/cmdref/o.fspecs.html
Note: The instance cache is cleared when the filespec changes.
string | $filespec | the filespec of the file. |
{ static::_validateFilespec($filespec); $this->_filespec = $filespec; // identity has changed - clear all of the instance caches. $this->_cache = array(); return $this; }
P4_File::setLocalContents | ( | $ | content | ) |
Write contents to the local client file.
If the file does not exist, it will be created.
string | $content | the content to write to the file |
P4_File_Exception | if the file cannot be written. |
{ $this->touchLocalFile(); if (!is_writable($this->getLocalFilename())) { if (!chmod($this->getLocalFilename(), 0644)) { $message = "Failed to make local file writable."; throw new P4_File_Exception($message); } } if (file_put_contents($this->getLocalFilename(), $content) === false) { $message = "Failed to write local file."; throw new P4_File_Exception($message); } return $this; }
P4_File::setStatusCache | ( | $ | status | ) |
Set the file status cache to the given array of fields/values.
array | $status | an array of field/value pairs. |
InvalidArgumentException | if the given value is not an array. |
{ if (!is_array($status)) { throw new InvalidArgumentException('Cannot set status cache. Status must be an array.'); } $this->_cache['status'] = $status; if (isset($status['headRev'])) { $this->_cache['revision'] = $status['headRev']; } return $this; }
static P4_File::stripRevspec | ( | $ | filespec | ) | [static] |
Strip the revision specifier from a file specification.
This removes the #rev or @change component from a filespec.
string | $filespec | the filespec to strip the revspec from. |
{ $revPos = strpos($filespec, "#"); if ($revPos !== false) { $filespec = substr($filespec, 0, $revPos); } $revPos = strpos($filespec, "@"); if ($revPos !== false) { $filespec = substr($filespec, 0, $revPos); } return $filespec; }
P4_File::submit | ( | $ | description, |
$ | options = null |
||
) |
Submit the file to perforce.
If the optional resolve flags are passed, an attempt will be made to automatically resolve/resubmit should a conflict occur.
string | $description | the change description. |
null | string | array | $options | optional resolve flags, to be used if conflict occurs. See resolve() for details. |
InvalidArgumentException | if no description is given. |
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); // ensure that we have a description. if (!is_string($description) || !strlen($description)) { throw new InvalidArgumentException( 'Cannot submit. Description must be a non-empty string.' ); } // ensure the file is in the default pending change. // this is required to avoid inadvertently affecting // a numbered pending change description and its files. if ($this->hasStatusField('change') && $this->getStatus('change') != 'default') { $this->reopen('default'); } // setup the submit options $params = array(); $params[] = '-d'; $params[] = $description; $params[] = $this->getFilespec(true); try { $this->getConnection()->run('submit', $params); } catch (P4_Connection_ConflictException $e) { // if there are no resolve options; re-throw the resolve exception if (empty($options)) { throw $e; } // re-do submit via our change as this will // attempt to do the resolve. note change presently // does a wasted try prior to resolve but hopefully // the use is seldom enough we don't take a notable // performance hit on it. $e->getChange()->submit(null, $options); } // file has changed - clear all of the instance caches. $this->_cache = array(); // if we had a rev-spec previously, take it off $this->setFilespec($this->getFilespec(true)); return $this; }
P4_File::sync | ( | $ | force = false , |
$ | flush = false |
||
) |
Sync the file from the depot.
Note when the P4_File is fetched, or if made via new the first time it is accessed and has a valid filespec, the revision is pinned at that point in time. Sync will always use the pinned revision which is not necessarily head.
bool | $force | optional - defaults to false - force sync the file. |
bool | $flush | optional - defaults to false - don't transfer the file. |
P4_File_Exception | if sync fails. |
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $params = array(); if ($force) { $params[] = '-f'; } if ($flush) { $params[] = '-k'; } $params[] = $this->getFilespecWithRevision(); $result = $this->getConnection()->run('sync', $params); // status has changed - clear the status cache. $this->clearStatusCache(); // verify sync was successful. if ($result->hasWarnings()) { // if we had warnings throw if the haveRev doesn't equal the headRev // unless it is a deleted file in which case we expect a warning $haveRev = $this->hasStatusField('haveRev') ? $this->getStatus('haveRev') : -1; $headRev = $this->hasStatusField('headRev') ? $this->getStatus('headRev') : 0; if (!$this->isDeleted() && $headRev !== $haveRev) { throw new P4_File_Exception( "Failed to sync file: " . implode(", ", $result->getWarnings()) ); } } return $this; }
P4_File::touchLocalFile | ( | ) |
Touch the local client file.
If the file does not exist, it will be created.
P4_File_Exception | if the file cannot be touched. |
{ if (!is_dir($this->getLocalPath())) { $this->_createLocalPath(); } if (!is_file($this->getLocalFilename())) { if (!touch($this->getLocalFilename())) { $message = "Failed to touch local file."; throw new P4_File_Exception($message); } } return $this; }
P4_File::unlock | ( | ) |
Unlock this file in the depot.
{ // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $this->getConnection()->run('unlock', $this->getFilespec(true)); // status has changed - clear the status cache. $this->clearStatusCache(); return $this; }
P4_File::where | ( | ) |
Determine how this file maps through the client view.
Produces an array with three variations on the filespec. Depot-syntax, client-syntax and local file-system syntax (in that order).
Caches the result so that subsequent lookups do not incur the 'p4 where' command overhead.
P4_File_Exception | if the file is not mapped by the client. |
{ if (!array_key_exists('where', $this->_cache) || !isset($this->_cache['where'])) { // verify we have a filespec set; throws if invalid/missing $this->_validateHasFilespec(); $result = $this->getConnection()->run('where', $this->getFilespec(true)); if ($result->hasWarnings()) { throw new P4_File_Exception("Where failed. File is not mapped."); } $this->_cache['where'] = array_values($result->getData(0)); } return $this->_cache['where']; }
P4_File::$_cache = array() [protected] |
P4_File::$_filespec = null [protected] |
const P4_File::ALL_FILES = '//...' |
const P4_File::REVERT_UNCHANGED = 'unchanged' |