Perforce Chronicle 2012.2/486814
API Documentation

P4_Spec_PluralAbstract Class Reference

This class layers support for plural specs such as changes, jobs, users, etc. More...

Inheritance diagram for P4_Spec_PluralAbstract:
P4_SpecAbstract P4_ModelAbstract P4_ConnectedAbstract P4_ModelInterface P4_ConnectedInterface P4_Branch P4_Change P4_Client P4_Depot P4_Group P4_Job P4_Label P4_Stream P4_User

List of all members.

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

Detailed Description

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 '*', '...'

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_Spec_PluralAbstract::_deferPopulate ( reset = false) [protected]

Extended to preserve id when values are cleared.

Schedule populate to run when data is requested (lazy-load).

Parameters:
bool$resetoptionally 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.

Parameters:
array$listEntrya single spec entry from spec list output.
array$flagsthe flags that were used for this 'fetchAll' run.
P4_Connection_Interface$connectiona specific connection to use.
Returns:
P4_Spec_PluralAbstract a (partially) populated instance of this spec class.

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.

Returns:
string Perforce command to use for fetchAll

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.

Parameters:
array$optionsarray of options to augment fetch behavior. see fetchAll for documented options.
Returns:
array set of flags suitable for passing to spec list command.

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.

Returns:
string the name of this spec's id field.
Exceptions:
P4_Spec_Exceptionif 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.

Returns:
array $data the raw spec output from Perforce.

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.

Returns:
function A callback function with the signature function($entry)

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.

Parameters:
string$fieldthe name of the field to get the value of.
Returns:
mixed the value of the field.
Exceptions:
P4_Spec_Exceptionif 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]

Check if the given id is in a valid format for this spec type.

Parameters:
string$idthe id to check
Returns:
bool true if id is valid, false otherwise

Reimplemented in P4_Change, P4_Group, P4_Job, P4_Stream, and P4_User.

    {
        $validator = new P4_Validate_SpecName;
        return $validator->isValid($id);
    }
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.

Parameters:
string$fieldthe name of the field to set the value of.
mixed$valuethe value to set in the field.
Returns:
P4_SpecAbstract provides a fluent interface
Exceptions:
P4_Spec_Exceptionif 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.

Parameters:
array$paramsoptional - additional flags to pass to delete (e.g. some specs support -f to force delete).
Returns:
P4_Spec_PluralAbstract provides a fluent interface
Exceptions:
P4_Spec_Exceptionif 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.

Parameters:
string$idthe id to check for.
P4_Connection_Interface$connectionoptional - a specific connection to use.
Returns:
bool true if the given id matches an existing record.

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.

Parameters:
string$idthe id of the entry to fetch.
P4_Connection_Interface$connectionoptional - a specific connection to use.
Returns:
P4_Spec_PluralAbstract instace of the requested entry.
Exceptions:
InvalidArgumentExceptionif 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.

Parameters:
array$optionsoptional - 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.

Parameters:
P4_Connection_Interface$connectionoptional - a specific connection to use.
Returns:
P4_Model_Iterator all records of this type.

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.

Returns:
null|string the id of this 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.

Parameters:
array | null$valuesoptional - values to set on temp entry, can include ID
function | null$cleanupCallbackoptional - callback to use for cleanup. signature is: function($entry, $defaultCallback)
P4_Connection_Interface$connectionoptional - a specific connection to use.
Returns:
P4_Spec_PluralAbstract instace of the temp entry.
    {
        // 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.

Returns:
string an id suitable for use with temporary specs.
    {
        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.

Parameters:
null | string$idthe id of this entry - pass null to clear.
Returns:
P4_Spec_PluralAbstract provides a fluent interface
Exceptions:
InvalidArgumentExceptionif 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;
    }

Member Data Documentation

P4_Spec_PluralAbstract::$_idField = null [static, protected]

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