Perforce Chronicle 2012.2/486814
API Documentation
|
This class layers support for plural specs such as changes, jobs, users, etc. More...
Public Member Functions | |
delete (array $params=null) | |
Delete this spec entry. | |
getId () | |
Get the id of this spec entry. | |
setId ($id) | |
Set the id of this spec entry. | |
Static Public Member Functions | |
static | exists ($id, P4_Connection_Interface $connection=null) |
Determine if a spec record with the given id exists. | |
static | fetch ($id, P4_Connection_Interface $connection=null) |
Get the requested spec entry from Perforce. | |
static | fetchAll ($options=array(), P4_Connection_Interface $connection=null) |
Get all entries of this type from Perforce. | |
static | makeTemp (array $values=null, $cleanupCallback=null, P4_Connection_Interface $connection=null) |
Create a temporary entry. | |
static | makeTempId () |
Generate a temporary id by combining the id prefix with the current time, pid and a random uniqid(): | |
Public Attributes | |
const | FETCH_MAXIMUM = 'maximum' |
const | TEMP_ID_DELIMITER = "." |
const | TEMP_ID_PREFIX = '~tmp' |
Protected Member Functions | |
_deferPopulate ($reset=false) | |
Extended to preserve id when values are cleared. | |
_getSpecData () | |
Get raw spec data direct from Perforce. | |
_getValue ($field) | |
Get a field's raw value. | |
_populate () | |
Extend parent populate to exit early if id is null. | |
_setValue ($field, $value) | |
Set a field's raw value. | |
Static Protected Member Functions | |
static | _fromSpecListEntry ($listEntry, $flags, P4_Connection_Interface $connection) |
Given a spec entry from spec list output (e.g. | |
static | _getFetchAllCommand () |
Get the fetch all command, generally a plural version of the spec type. | |
static | _getFetchAllFlags ($options) |
Produce set of flags for the spec list command, given fetch all options array. | |
static | _getIdField () |
Get the name of the id field for this spec. | |
static | _getTempCleanupCallback () |
Provide a callback function to be used during cleanup of temp entries. | |
static | _isValidId ($id) |
Check if the given id is in a valid format for this spec type. | |
Static Protected Attributes | |
static | $_idField = null |
This class layers support for plural specs such as changes, jobs, users, etc.
on top of the singular spec support already present in P4_SpecAbstract.
------------------------------------------------------------------------------------------------ Note: there are great inconsistencies between the data produced by the commands that return a single spec entry (e.g. 'p4 group', 'p4 change') and the commands that return a list of spec entries (e.g. 'p4 groups', 'p4 changes'). These inconsistencies are (roughly):
MOST SPECS: = Case-inconsistency in field names. = Fewer fields.
SOME SPECS: = More fields
CHANGE SPEC: = desc -> Description (truncated w.out -l) = time -> Date (format change)
CLIENT + LABEL + USER SPECS: = update/access format
DEPOT SPECS: = name -> Depot = time -> Date (format) = extra -> Address
GROUP SPECS: = max (-m) broken = one entry per user per group. = user field kindof maps to Users field = might be best to use untagged output.
JOB SPECS: = Description truncated without -l.
------------------------------------------------------------------------------------------------ Note: most commands will provide results when fetching by an invalid ID. Details are provided below on how to determine if you have an existing entry or a blank template:
CHANGE Exception thrown on invalid ID, no action required.
CLIENT / LABEL / USER 'always' fields access/updated are not present if new entry. For a more reliable check run:
'clients -e ID' 'labels -e ID' 'users ID'
And check for an empty result.
DEPOT No way to tell from single output and no way to filter. Run depots and see if its listed.
GROUP No way to tell from single output. Run 'groups -v ID' item isn't present if result is empty. It is notable 'groups -v' without ID produces extensive output, not the expected usage error.
JOB ReportedDate not present on new entries but the jobspec is rather malleable. Safest check is running:
'jobs -e job=ID'
And check for an empty result.
------------------------------------------------------------------------------------------------ Note: It was originally assumed Plural Spec IDs could not include revision specifiers or file Wildcards. This would limit: '*', '...', '%1'-'%9' The following additional, common restrictions were found: all digits, starts with '-'
On a per class basis, the forbidden items are:
CHANGE Pure digits are allowed, all else forbidden
CLIENT '*', '...', '%', pure digits, leading '-'
LABEL '*', '...', '%', pure digits, leading '-'
USER '*', '...', pure digits, leading '-'
DEPOT '*', '...', '%', pure digits, leading '-'
GROUP '*', '...', pure digits, leading '-'
JOB '*', '...'
P4_Spec_PluralAbstract::_deferPopulate | ( | $ | reset = false | ) | [protected] |
Extended to preserve id when values are cleared.
Schedule populate to run when data is requested (lazy-load).
bool | $reset | optionally clear instance values. |
Reimplemented from P4_SpecAbstract.
{ if ($reset) { $id = $this->getId(); } parent::_deferPopulate($reset); if ($reset) { $this->setId($id); } }
static P4_Spec_PluralAbstract::_fromSpecListEntry | ( | $ | listEntry, |
$ | flags, | ||
P4_Connection_Interface $ | connection | ||
) | [static, protected] |
Given a spec entry from spec list output (e.g.
'p4 jobs'), produce an instance of this spec with field values set where possible.
array | $listEntry | a single spec entry from spec list output. |
array | $flags | the flags that were used for this 'fetchAll' run. |
P4_Connection_Interface | $connection | a specific connection to use. |
Reimplemented in P4_Branch, P4_Change, P4_Client, P4_Depot, P4_Group, P4_Job, P4_Label, P4_Stream, and P4_User.
{ // most spec list entries have leading lower-case field // names which is inconsistent with defined field names. // make all field names lead with an upper-case letter. $keys = array_map('ucfirst', array_keys($listEntry)); $listEntry = array_combine($keys, $listEntry); // instantiate new spec object and set raw field values. $spec = new static($connection); $spec->_setValues($listEntry); return $spec; }
static P4_Spec_PluralAbstract::_getFetchAllCommand | ( | ) | [static, protected] |
Get the fetch all command, generally a plural version of the spec type.
Reimplemented in P4_Branch.
{ // derive list command from spec type by adding 's' // this works for most of the known plural specs return static::_getSpecType() . "s"; }
static P4_Spec_PluralAbstract::_getFetchAllFlags | ( | $ | options | ) | [static, protected] |
Produce set of flags for the spec list command, given fetch all options array.
array | $options | array of options to augment fetch behavior. see fetchAll for documented options. |
Reimplemented in P4_Branch, P4_Change, P4_Client, P4_Depot, P4_Group, P4_Job, P4_Label, P4_Stream, and P4_User.
{ $flags = array(); if (isset($options[self::FETCH_MAXIMUM])) { $flags[] = "-m"; $flags[] = (int) $options[self::FETCH_MAXIMUM]; } return $flags; }
static P4_Spec_PluralAbstract::_getIdField | ( | ) | [static, protected] |
Get the name of the id field for this spec.
P4_Spec_Exception | if the spec's id field is unset. |
{ // if spec id field not defined, throw. if (!is_string(static::$_idField) || !trim(static::$_idField)) { throw new P4_Spec_Exception('No id field is defined for this specification.'); } return static::$_idField; }
P4_Spec_PluralAbstract::_getSpecData | ( | ) | [protected] |
Get raw spec data direct from Perforce.
No caching involved. Extends parent to supply an id to the spec -o command.
Reimplemented from P4_SpecAbstract.
Reimplemented in P4_Change.
{ $result = $this->getConnection()->run( static::_getSpecType(), array("-o", $this->getId()) ); return $result->expandSequences()->getData(0); }
static P4_Spec_PluralAbstract::_getTempCleanupCallback | ( | ) | [static, protected] |
Provide a callback function to be used during cleanup of temp entries.
The callback should expect a single parameter, the entry being removed.
Reimplemented in P4_Client.
{ return function($entry) { // remove the temp entry we are responsible for $entry->delete(); }; }
P4_Spec_PluralAbstract::_getValue | ( | $ | field | ) | [protected] |
Get a field's raw value.
Extend parent to use getId() for id field.
string | $field | the name of the field to get the value of. |
P4_Spec_Exception | if the field does not exist. |
Reimplemented from P4_SpecAbstract.
{ if ($field === static::_getIdField()) { return $this->getId(); } // call-through. return parent::_getValue($field); }
static P4_Spec_PluralAbstract::_isValidId | ( | $ | id | ) | [static, protected] |
P4_Spec_PluralAbstract::_populate | ( | ) | [protected] |
Extend parent populate to exit early if id is null.
Reimplemented from P4_SpecAbstract.
{ // early exit if populate not needed. if (!$this->_needsPopulate) { return; } // don't attempt populate if id null. if ($this->getId() === null) { return; } parent::_populate(); }
P4_Spec_PluralAbstract::_setValue | ( | $ | field, |
$ | value | ||
) | [protected] |
Set a field's raw value.
Extend parent to use setId() for id field.
string | $field | the name of the field to set the value of. |
mixed | $value | the value to set in the field. |
P4_Spec_Exception | if the field does not exist. |
Reimplemented from P4_SpecAbstract.
{ if ($field === static::_getIdField()) { return $this->setId($value); } // call-through. return parent::_setValue($field, $value); }
P4_Spec_PluralAbstract::delete | ( | array $ | params = null | ) |
Delete this spec entry.
array | $params | optional - additional flags to pass to delete (e.g. some specs support -f to force delete). |
P4_Spec_Exception | if no id has been set. |
{ $id = $this->getId(); if ($id === null) { throw new P4_Spec_Exception("Cannot delete. No id has been set."); } // ensure id exists. $connection = $this->getConnection(); if (!static::exists($id, $connection)) { throw new P4_Spec_NotFoundException( "Cannot delete " . static::_getSpecType() . " $id. Record does not exist." ); } $params = array_merge((array) $params, array("-d", $id)); $result = $connection->run(static::_getSpecType(), $params); // should re-populate. $this->_deferPopulate(true); return $this; }
static P4_Spec_PluralAbstract::exists | ( | $ | id, |
P4_Connection_Interface $ | connection = null |
||
) | [static, abstract] |
Determine if a spec record with the given id exists.
Must be implemented by sub-classes because this test is impractical to generalize.
string | $id | the id to check for. |
P4_Connection_Interface | $connection | optional - a specific connection to use. |
Reimplemented in P4_Branch, P4_Change, P4_Client, P4_Depot, P4_Group, P4_Job, P4_Label, P4_Stream, and P4_User.
static P4_Spec_PluralAbstract::fetch | ( | $ | id, |
P4_Connection_Interface $ | connection = null |
||
) | [static] |
Get the requested spec entry from Perforce.
string | $id | the id of the entry to fetch. |
P4_Connection_Interface | $connection | optional - a specific connection to use. |
InvalidArgumentException | if no id is given. |
{ // ensure a valid id is provided. if (!static::_isValidId($id)) { throw new InvalidArgumentException("Must supply a valid id to fetch."); } // if no connection given, use default. $connection = $connection ?: static::getDefaultConnection(); // ensure id exists. if (!static::exists($id, $connection)) { throw new P4_Spec_NotFoundException( "Cannot fetch " . static::_getSpecType() . " $id. Record does not exist." ); } // construct spec instance. $spec = new static($connection); $spec->setId($id) ->_deferPopulate(); return $spec; }
static P4_Spec_PluralAbstract::fetchAll | ( | $ | options = array() , |
P4_Connection_Interface $ | connection = null |
||
) | [static] |
Get all entries of this type from Perforce.
array | $options | optional - array of options to augment fetch behavior. supported options are: |
FETCH_MAXIMUM - set to integer value to limit to the first 'max' number of entries.
P4_Connection_Interface | $connection | optional - a specific connection to use. |
Reimplemented in P4_Branch, P4_Change, P4_Client, P4_Group, P4_Job, P4_Label, P4_Stream, and P4_User.
{ // if no connection given, use default. $connection = $connection ?: static::getDefaultConnection(); // get command to use $command = static::_getFetchAllCommand(); // get command flags for given fetch options. $flags = static::_getFetchAllFlags($options); // fetch all specs. $result = $connection->run($command, $flags); // expand any sequences present $result->expandSequences(); // convert result data to spec objects. $specs = new P4_Model_Iterator; foreach ($result->getData() as $data) { $spec = static::_fromSpecListEntry($data, $flags, $connection); $spec->_deferPopulate(); $specs[] = $spec; } return $specs; }
P4_Spec_PluralAbstract::getId | ( | ) |
Get the id of this spec entry.
Reimplemented in P4_Change.
{ if (array_key_exists(static::_getIdField(), $this->_values)) { return $this->_values[static::_getIdField()]; } else { return null; } }
static P4_Spec_PluralAbstract::makeTemp | ( | array $ | values = null , |
$ | cleanupCallback = null , |
||
P4_Connection_Interface $ | connection = null |
||
) | [static] |
Create a temporary entry.
The passed values can, optionally, specify the id of the temp entry. If no id is passed in values, one will be generated following the conventions described in makeTempId().
Temp entries are deleted when the connection is closed.
array | null | $values | optional - values to set on temp entry, can include ID |
function | null | $cleanupCallback | optional - callback to use for cleanup. signature is: function($entry, $defaultCallback) |
P4_Connection_Interface | $connection | optional - a specific connection to use. |
{ // normalize to array $values = $values ?: array(); // generate an id if no value for our id field is present $idField = static::_getIdField(); if (!isset($values[$idField])) { $values[$idField] = static::makeTempId(); } // create the temporary instance. $temp = new static($connection); $temp->setValues($values)->save(); // remove the temp entry when the connection terminates. $defaultCallback = static::_getTempCleanupCallback(); $temp->getConnection()->addDisconnectCallback( function($connection) use ($temp, $cleanupCallback, $defaultCallback) { try { // use the passed callback if valid, fallback to the default callback if (is_callable($cleanupCallback)) { $cleanupCallback($temp, $defaultCallback); } else { $defaultCallback($temp); } } catch (Exception $e) { P4_Log::logException("Failed to delete temporary entry.", $e); } } ); return $temp; }
static P4_Spec_PluralAbstract::makeTempId | ( | ) | [static] |
Generate a temporary id by combining the id prefix with the current time, pid and a random uniqid():
~tmp.<unixtime>.<pid>.<uniqid>
The leading tilde ('~') places the temporary id at the end of the list. The unixtime ensures that the oldest ids will appear first (among temp ids), while the pid and uniqid provide reasonable assurance that no two ids will collide.
{ return implode( static::TEMP_ID_DELIMITER, array( static::TEMP_ID_PREFIX, time(), getmypid(), uniqid("", true) ) ); }
P4_Spec_PluralAbstract::setId | ( | $ | id | ) |
Set the id of this spec entry.
Id must be in a valid format or null.
null | string | $id | the id of this entry - pass null to clear. |
InvalidArgumentException | if id does not pass validation. |
Reimplemented in P4_Change.
{ if ($id !== null && !static::_isValidId($id)) { throw new InvalidArgumentException("Cannot set id. Id is invalid."); } // if populate was deferred, caller expects it // to have been populated already. $this->_populate(); $this->_values[static::_getIdField()] = $id; return $this; }
P4_Spec_PluralAbstract::$_idField = null [static, protected] |
const P4_Spec_PluralAbstract::FETCH_MAXIMUM = 'maximum' |
const P4_Spec_PluralAbstract::TEMP_ID_DELIMITER = "." |
const P4_Spec_PluralAbstract::TEMP_ID_PREFIX = '~tmp' |