- <?php
- /**
- * Abstracts operations against Perforce labels.
- *
- * @copyright 2011 Perforce Software. All rights reserved.
- * @license Please see LICENSE.txt in top-level folder of this distribution.
- * @version <release>/<patch>
- */
-
- namespace P4\Spec;
-
- use P4\Validate;
- use P4\Connection\ConnectionInterface;
-
- class Label extends PluralAbstract
- {
- const SPEC_TYPE = 'label';
- const ID_FIELD = 'Label';
-
- const FETCH_BY_NAME = 'name';
- const FETCH_BY_OWNER = 'owner';
-
- protected $fields = array(
- 'Update' => array(
- 'accessor' => 'getUpdateDateTime'
- ),
- 'Access' => array(
- 'accessor' => 'getAccessDateTime'
- ),
- 'Owner' => array(
- 'accessor' => 'getOwner',
- 'mutator' => 'setOwner'
- ),
- 'Description' => array(
- 'accessor' => 'getDescription',
- 'mutator' => 'setDescription'
- ),
- 'Options' => array(
- 'accessor' => 'getOptions',
- 'mutator' => 'setOptions'
- ),
- 'Revision' => array(
- 'accessor' => 'getRevision',
- 'mutator' => 'setRevision'
- ),
- 'View' => array(
- 'accessor' => 'getView',
- 'mutator' => 'setView'
- )
- );
-
- /**
- * Get all Labels from Perforce. Adds filtering options.
- *
- * @param 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.
- * FETCH_BY_NAME - set to label name pattern (e.g. 'labe*').
- * FETCH_BY_OWNER - set to owner's username (e.g. 'jdoe').
- *
- * @param ConnectionInterface $connection optional - a specific connection to use.
- * @return \P4\Model\Fielded\Iterator all records of this type.
- */
- public static function fetchAll($options = array(), ConnectionInterface $connection = null)
- {
- // simply return parent - method exists to document options.
- return parent::fetchAll($options, $connection);
- }
-
- /**
- * Determine if the given label id exists.
- *
- * @param string $id the id to check for.
- * @param ConnectionInterface $connection optional - a specific connection to use.
- * @return bool true if the given id matches an existing label.
- */
- public static function exists($id, ConnectionInterface $connection = null)
- {
- // check id for valid format
- if (!static::isValidId($id)) {
- return false;
- }
-
- $labels = static::fetchAll(
- array(
- static::FETCH_BY_NAME => $id,
- static::FETCH_MAXIMUM => 1
- ),
- $connection
- );
-
- return (bool) count($labels);
- }
-
- /**
- * Get the last update time for this label spec.
- * This value is read only, no setUpdateTime function is provided.
- *
- * If this is a brand new spec, null will be returned in lieu of a time.
- *
- * @return string|null Date/Time of last update, formatted "2009/11/23 12:57:06" or null
- */
- public function getUpdateDateTime()
- {
- return $this->getRawValue('Update');
- }
-
- /**
- * Get the last access time for this label spec.
- * This value is read only, no setAccessTime function is provided.
- *
- * If this is a brand new spec, null will be returned in lieu of a time.
- *
- * @return string|null Date/Time of last access, formatted "2009/11/23 12:57:06" or null
- */
- public function getAccessDateTime()
- {
- return $this->getRawValue('Access');
- }
-
- /**
- * Get the owner of this label.
- *
- * @return string|null User who owns this record.
- */
- public function getOwner()
- {
- return $this->getRawValue('Owner');
- }
-
- /**
- * Set the owner of this label to passed value.
- *
- * @param string|User|null $owner A string containing username
- * @return Label provides a fluent interface.
- * @throws \InvalidArgumentException Owner is incorrect type.
- */
- public function setOwner($owner)
- {
- if ($owner instanceof User) {
- $owner = $owner->getId();
- }
-
- if (!is_string($owner) && !is_null($owner)) {
- throw new \InvalidArgumentException('Owner must be a string, P4\Spec\User or null.');
- }
-
- return $this->setRawValue('Owner', $owner);
- }
-
- /**
- * Get the description for this label.
- *
- * @return string|null description for this label.
- */
- public function getDescription()
- {
- return $this->getRawValue('Description');
- }
-
- /**
- * Set a description for this label.
- *
- * @param string|null $description description for this label.
- * @return Label provides a fluent interface.
- * @throws \InvalidArgumentException Description is incorrect type.
- */
- public function setDescription($description)
- {
- if (!is_string($description) && !is_null($description)) {
- throw new \InvalidArgumentException('Description must be a string or null.');
- }
-
- return $this->setRawValue('Description', $description);
- }
-
- /**
- * Get options for this label.
- *
- * @return string|null options which are set on this label ('locked' or 'unlocked').
- */
- public function getOptions()
- {
- return $this->getRawValue('Options');
- }
-
- /**
- * Set the options for this label. See getOptions for expected values.
- *
- * @param string|null $options options to set on this label.
- * @return Label provides a fluent interface.
- * @throws \InvalidArgumentException Options are incorrect type.
- */
- public function setOptions($options)
- {
- if (!is_string($options) && !is_null($options)) {
- throw new \InvalidArgumentException('Options must be a string or null.');
- }
-
- return $this->setRawValue('Options', $options);
- }
-
- /**
- * Get the revision setting for this label.
- *
- * @return string|null Revision setting for this label.
- */
- public function getRevision()
- {
- $revision = $this->getRawValue('Revision');
-
- // strip quotes if needed
- if (is_string($revision) &&
- substr($revision, 0, 1) === '"' &&
- substr($revision, -1) === '"') {
- return substr($revision, 1, -1);
- }
-
- return $revision;
- }
-
- /**
- * Set the revision setting for this label.
- *
- * @param string|null $revision Revision setting for this label.
- * @return Label provides a fluent interface.
- * @throws \InvalidArgumentException revision is incorrect type.
- */
- public function setRevision($revision)
- {
- if (!is_string($revision) && !is_null($revision)) {
- throw new \InvalidArgumentException('Revision must be a string or null.');
- }
-
- // quote string values; leaves null values alone
- if (is_string($revision)) {
- $revision = '"' . $revision . '"';
- }
-
- return $this->setRawValue('Revision', $revision);
- }
-
- /**
- * Get the view for this label.
- * View entries will be returned as an array of strings e.g.:
- * array (
- * 0 => '//depot/example/with space/...',
- * 1 => '//depot/alternate/example/*'
- * )
- * Labels view is fairly unique as each entry is only one depot path.
- *
- * @return array list view entries for this label, empty array if none.
- */
- public function getView()
- {
- return $this->getRawValue('View') ?: array();
- }
-
- /**
- * Set the view for this label. See getView for format details.
- *
- * @param array $view Array of view strings, empty array for none.
- * @return Label provides a fluent interface.
- * @throws \InvalidArgumentException View array, or a view entry, is incorrect type.
- */
- public function setView($view)
- {
- if (!is_array($view)) {
- throw new \InvalidArgumentException('View must be passed as array.');
- }
-
- foreach ($view as $entry) {
- if (!is_string($entry) || trim($entry) === "") {
- throw new \InvalidArgumentException(
- "Each view entry must be a non-empty string."
- );
- }
- }
-
- return $this->setRawValue('View', $view);
- }
-
- /**
- * Add a view entry to this Label.
- *
- * @param string $path the depot path to add.
- * @return Label provides a fluent interface.
- */
- public function addView($path)
- {
- $entries = $this->getView();
- $entries[] = $path;
-
- return $this->setView($entries);
- }
-
- /**
- * Adds the specified filespecs to this label. The update is completed
- * synchronously, no need to call save.
- *
- * @param array $filespecs The filespecs to add to this label, can include rev-specs
- * @return Label provides a fluent interface.
- */
- public function tag($filespecs)
- {
- if (!is_array($filespecs) || in_array(false, array_map('is_string', $filespecs))) {
- throw new \InvalidArgumentException(
- 'Tag requires an array of string values for input'
- );
- }
-
- // there is a potential to exceed the arg-max limit;
- // run tag command as few times as possible
- $connection = $this->getConnection();
- $batches = $connection->batchArgs($filespecs, array('-l', $this->getId()));
- foreach ($batches as $batch) {
- $connection->run('tag', $batch);
- }
-
- return $this;
- }
-
- /**
- * Check if the given id is in a valid format for this spec type.
- *
- * @param string $id the id to check
- * @return bool true if id is valid, false otherwise
- */
- protected static function isValidId($id)
- {
- $validator = new Validate\SpecName;
- $validator->allowRelative(true);
- $validator->allowPercent(false);
- $validator->allowCommas(false);
- $validator->allowSlashes(true);
- return $validator->isValid($id);
- }
-
- /**
- * Produce set of flags for the spec list command, given fetch all options array.
- * Extends parent to add support for filter option.
- *
- * @param array $options array of options to augment fetch behavior.
- * see fetchAll for documented options.
- * @return array set of flags suitable for passing to spec list command.
- */
- protected static function getFetchAllFlags($options)
- {
- $flags = parent::getFetchAllFlags($options);
-
- if (isset($options[static::FETCH_BY_NAME])) {
- $name = $options[static::FETCH_BY_NAME];
-
- if (!is_string($name) || trim($name) === '') {
- throw new \InvalidArgumentException(
- 'Filter by Name expects a non-empty string as input'
- );
- }
-
- $flags[] = '-e';
- $flags[] = $name;
- }
-
- if (isset($options[static::FETCH_BY_OWNER])) {
- $owner = $options[static::FETCH_BY_OWNER];
-
- // We allow empty values as this returns labels with no owner
- if (!is_string($owner) || trim($owner) === '') {
- throw new \InvalidArgumentException(
- 'Filter by Owner expects a non-empty string as input'
- );
- }
-
- $flags[] = '-u';
- $flags[] = $owner;
- }
-
- return $flags;
- }
- }