- <?php
- /**
- * Abstracts operations against Perforce counters.
- *
- * This class is somewhat unique as calling set will immediately write the new value
- * to perforce; no separate save step is required.
- * When reading values out we do attempt to use cached results, to ensure you read
- * out the value directly from perforce set $force to true when calling get.
- *
- * @copyright 2011 Perforce Software. All rights reserved.
- * @license Please see LICENSE.txt in top-level folder of this distribution.
- * @version <release>/<patch>
- */
-
- namespace P4\Counter;
-
- use P4\Connection\ConnectionInterface;
- use P4\Connection\Exception\CommandException;
- use P4\Counter\Exception\NotFoundException;
- use P4\Model\Connected;
- use P4\Exception;
- use P4\OutputHandler\Limit;
-
- abstract class AbstractCounter extends Connected\ConnectedAbstract
- {
- const FETCH_MAXIMUM = 'maximum';
- const FETCH_BY_NAME = 'name';
- const FETCH_AFTER = 'after';
-
- // flags can be specified by the implementer. they will be included for
- // all calls to 'p4 counter' or 'p4 counters' (e.g. -u to swap to keys)
- protected static $flags = array();
-
- protected $id = null;
- protected $value = null;
-
- /**
- * Get the id of this counter.
- *
- * @return null|string the id of this entry.
- */
- public function getId()
- {
- return $this->id;
- }
-
- /**
- * Set the id of this counter. Id must be in a valid format or null.
- *
- * @param null|string $id the id of this entry - pass null to clear.
- * @return Counter provides a fluent interface
- * @throws \InvalidArgumentException if id does not pass validation.
- */
- public function setId($id)
- {
- if ($id !== null && !static::isValidId($id)) {
- throw new \InvalidArgumentException("Cannot set id. Id is invalid.");
- }
-
- $this->id = $id;
- $this->value = null;
-
- return $this;
- }
-
- /**
- * Determine if the given counter 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 counter.
- */
- public static function exists($id, ConnectionInterface $connection = null)
- {
- // check id for valid format
- if (!static::isValidId($id)) {
- return false;
- }
-
- $counters = static::fetchAll(
- array(static::FETCH_BY_NAME => $id),
- $connection
- );
-
- return in_array($id, $counters->invoke('getId'));
- }
-
- /**
- * Get the requested counter from Perforce.
- *
- * @param string $id the id of the counter to fetch.
- * @param ConnectionInterface $connection optional - a specific connection to use.
- * @return Counter instace of the requested counter.
- * @throws \InvalidArgumentException if invalid id is given.
- * @throws NotFoundException if record cannot be located
- */
- public static function fetch($id, ConnectionInterface $connection = null)
- {
- // 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();
-
- $counters = static::fetchAll(
- array(static::FETCH_BY_NAME => $id),
- $connection
- );
-
- // be defensive; we only expect one result but ensure its the correct id
- foreach ($counters as $counter) {
- if ($counter->getId() == $id) {
- return $counter;
- }
- }
-
- // if we made it here we couldn't find the counter so throw
- throw new NotFoundException(
- "Cannot fetch entry. Id does not exist."
- );
- }
-
- /**
- * Get all Counters from Perforce.
- *
- * @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.
- * Note: Max limit is imposed client side on <2013.1.
- * FETCH_BY_NAME - set to string value to limit to counters
- * matching the given name/pattern.
- * FETCH_AFTER - set to an id _after_ which we start collecting
- * @param ConnectionInterface $connection optional - a specific connection to use.
- * @return Connected\Iterator all counters matching passed option(s).
- */
- public static function fetchAll($options = array(), ConnectionInterface $connection = null)
- {
- // if no connection given, use default.
- $connection = $connection ?: static::getDefaultConnection();
-
- // check if the server supports filtering counters (-e) or max (-m)
- $supportsFilter = $connection->isServerMinVersion('2010.1');
- $supportsMax = $connection->isServerMinVersion('2013.1');
-
- // normalize options and pull them out
- $options += array(static::FETCH_MAXIMUM => 0, static::FETCH_BY_NAME => null, static::FETCH_AFTER => null);
- $max = (int) $options[static::FETCH_MAXIMUM];
- $after = $options[static::FETCH_AFTER];
- $isAfter = false;
- $filter = $options[static::FETCH_BY_NAME];
- $pattern = false;
-
- // configure params starting with default flags for this model
- $params = static::$flags;
-
- // use server max limiting if we have no after/filters to interfere and its supported
- if ($max && !$after && (!$filter || $supportsFilter) && $supportsMax) {
- $params[] = '-m';
- $params[] = $max;
- }
-
- // user server side filtering if possible, fall back to defining regex
- if ($filter && $supportsFilter) {
- $params[] = '-e';
- $params[] = $options[static::FETCH_BY_NAME];
- } elseif ($filter) {
- $pattern = preg_quote($options[static::FETCH_BY_NAME]);
- $pattern = '/^' . str_replace('\*', '.*', $pattern) . '$/';
- }
-
- // configure a handler to enforce limit, after and filter
- $handler = new Limit;
- $handler->setMax($max);
- $handler->setFilterCallback(
- function ($data) use ($pattern, $after, &$isAfter) {
- // skip entries which fail our pattern
- if ($pattern && !preg_match($pattern, $data['counter'])) {
- return false;
- }
-
- // if we have an 'after' and haven't seen it check and skip
- if ($after && !$isAfter) {
- $isAfter = ($after == $data['counter']);
- return false;
- }
-
- // made it this far its a good entry don't filter it
- return true;
- }
- );
-
- // convert result data to counter objects.
- $result = $connection->runHandler($handler, 'counters', $params);
- $counters = new Connected\Iterator;
- foreach ($result->getData() as $data) {
- // populate a counter and add it to the iterator
- try {
- $counter = new static($connection);
- $counter->setId($data['counter']);
- $counter->value = $data['value'];
- } catch (\InvalidArgumentException $e) {
- // assume id was invalid - ignore.
- continue;
- }
-
- $counters[] = $counter;
- }
-
- return $counters;
- }
-
- /**
- * Get counter's value.
- *
- * If a cached value is available it will, by default, be used. If you pass
- * true as the $force param you can force the current value to always be
- * read out from the perforce server.
- *
- * @param bool $force optional - false (default) allow cached value
- * true ensure current value is read from p4d
- * @return mixed the value of the counter.
- */
- public function get($force = false)
- {
- // if we have a cached value and the caller allows it simply return
- if (!$force && $this->value !== null) {
- return $this->value;
- }
-
- $id = $this->getId();
- $connection = $this->getConnection();
-
- // if the ID is not set or the ID doesn't exist in perforce, return null
- if ($id === null || !static::exists($id, $connection)) {
- return null;
- }
-
- $params = static::$flags;
- $params[] = $id;
- $result = $connection->run('counter', $params);
- $data = $result->getData();
- $value = $data[0]['value'];
-
- // cache the value for later
- $this->value = $value;
-
- return $value;
- }
-
- /**
- * Increment counters value by 1. If the counter doesn't exist it will be
- * created and assigned the value 1.
- * The update is carried out atomically by the server.
- *
- * @return string The counters new value
- * @throws Exception If the current value is non-numeric
- */
- public function increment()
- {
- $id = $this->getId();
- if ($id === null) {
- throw new Exception("Cannot increment value. No id has been set.");
- }
-
- $params = static::$flags;
- $params[] = '-i';
- $params[] = $id;
- $result = $this->getConnection()->run('counter', $params);
- $data = $result->getData();
- $value = $data[0]['value'];
-
- // update our value cache
- $this->value = $value;
-
- return $value;
- }
-
- /**
- * Delete this counter entry. We intend implementor to provide an
- * actual 'delete' method at which point they can decide if they
- * wan't to expose 'force' or not.
- *
- *
- * @param bool $force optional - force delete the counter.
- * @return Counter provides a fluent interface
- * @throws Exception if no id has been set.
- */
- protected function doDelete($force = false)
- {
- $id = $this->getId();
- if ($id === null) {
- throw new Exception("Cannot delete. No id has been set.");
- }
-
- // setup counter command args.
- $params = static::$flags;
- if ($force) {
- $params[] = '-f';
- }
- $params[] = "-d";
- $params[] = $id;
-
- try {
- $this->getConnection()->run('counter', $params);
- } catch (CommandException $e) {
- if (strpos($e->getMessage(), 'No such counter')) {
- throw new NotFoundException(
- "Cannot delete entry. Id does not exist."
- );
- }
- throw $e;
- }
-
- // clear our cached value
- $this->value = null;
-
- return $this;
- }
-
- /**
- * Set counters value. The value will be immediately written to perforce.
- * We intend implementor to provide an actual 'set' method at which
- * point they can decide if they wan't to expose 'force' or not.
- *
- * @param mixed $value the value to set in the counter.
- * @param bool $force optional - force set the counter.
- * @return Counter provides a fluent interface
- * @throws Exception if no Id has been set
- */
- protected function doSet($value, $force = false)
- {
- $id = $this->getId();
- if ($id === null) {
- throw new Exception("Cannot set value. No id has been set.");
- }
-
- // setup counter command args.
- $params = static::$flags;
- if ($force) {
- $params[] = '-f';
- }
- $params[] = $id;
- $params[] = $value;
-
- $this->getConnection()->run('counter', $params);
-
- // update our value cache
- $this->value = $value;
-
- return $this;
- }
-
- /**
- * Check if the given id is in a valid format.
- *
- * @param string $id the id to check
- * @return bool true if id is valid, false otherwise
- */
- protected static function isValidId($id)
- {
- $validator = new \P4\Validate\CounterName;
- return $validator->isValid($id);
- }
- }
# |
Change |
User |
Description |
Committed |
|
#1
|
18334 |
Liz Lam |
initial add of jambox |
9 years ago
|
|