420 lines
10 KiB
PHP
420 lines
10 KiB
PHP
<?php
|
|
|
|
/**
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; under version 2
|
|
* of the License (non-upgradable).
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Copyright (c) 2016 (original work) Open Assessment Technologies SA ;
|
|
*/
|
|
|
|
/**
|
|
* @author Jean-Sébastien Conan <jean-sebastien.conan@vesperiagroup.com>
|
|
*/
|
|
|
|
namespace oat\taoTests\models\runner\time;
|
|
|
|
/**
|
|
* Class TimePoint
|
|
*
|
|
* Describes a temporal point by storing a timestamp with microseconds and some flags.
|
|
* A TimePoint can describe a START or a END temporal point used to define a time range.
|
|
* Each TimePoint can be related to a target (CLIENT or SERVER).
|
|
* A list of tags can be attached to a TimePoint to explain its role or its context.
|
|
*
|
|
* @package oat\taoTests\models\runner\time
|
|
*/
|
|
class TimePoint implements ArraySerializable, \Serializable, \JsonSerializable
|
|
{
|
|
/**
|
|
* Type of TimePoint: start of range
|
|
*/
|
|
const TYPE_START = 1;
|
|
|
|
/**
|
|
* Type of TimePoint: end of range
|
|
*/
|
|
const TYPE_END = 2;
|
|
|
|
/**
|
|
* Represents all types of TimePoint
|
|
*/
|
|
const TYPE_ALL = 3;
|
|
|
|
/**
|
|
* Type of TimePoint target: client side
|
|
*/
|
|
const TARGET_CLIENT = 1;
|
|
|
|
/**
|
|
* Type of TimePoint target: server side
|
|
*/
|
|
const TARGET_SERVER = 2;
|
|
|
|
/**
|
|
* Represents all types of TimePoint targets
|
|
*/
|
|
const TARGET_ALL = 3;
|
|
|
|
/**
|
|
* The decimal precision used to compare timestamps
|
|
*/
|
|
const PRECISION = 10000;
|
|
|
|
/**
|
|
* The timestamp representing the TimePoint
|
|
* @var float
|
|
*/
|
|
protected $timestamp = 0.0;
|
|
|
|
/**
|
|
* A collection of tags attached to the TimePoint
|
|
* @var array
|
|
*/
|
|
protected $tags = [];
|
|
|
|
/**
|
|
* The type of TimePoint. Must be a value from TYPE_START or TYPE_END constants.
|
|
* @var int
|
|
*/
|
|
protected $type = 0;
|
|
|
|
/**
|
|
* The type of target. Must be a value from TARGET_CLIENT or TARGET_SERVER constants.
|
|
* @var int
|
|
*/
|
|
protected $target = 0;
|
|
|
|
/**
|
|
* The unique reference to name the TimePoint
|
|
* @var string
|
|
*/
|
|
protected $ref;
|
|
|
|
/**
|
|
* QtiTimePoint constructor.
|
|
* @param string|array $tags
|
|
* @param float $timestamp
|
|
* @param int $type
|
|
* @param int $target
|
|
*/
|
|
public function __construct($tags = null, $timestamp = null, $type = null, $target = null)
|
|
{
|
|
if (isset($tags)) {
|
|
$this->setTags($tags);
|
|
}
|
|
|
|
if (isset($timestamp)) {
|
|
$this->setTimestamp($timestamp);
|
|
}
|
|
|
|
if (isset($type)) {
|
|
$this->setType($type);
|
|
}
|
|
|
|
if (isset($target)) {
|
|
$this->setTarget($target);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Exports the internal state to an array
|
|
* @return array
|
|
*/
|
|
public function toArray()
|
|
{
|
|
return [
|
|
'ts' => $this->getTimestamp(),
|
|
'type' => $this->getType(),
|
|
'target' => $this->getTarget(),
|
|
'tags' => $this->getTags(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Imports the internal state from an array
|
|
* @param array $data
|
|
*/
|
|
public function fromArray($data)
|
|
{
|
|
if ($data) {
|
|
if (isset($data['tags'])) {
|
|
$this->setTags($data['tags']);
|
|
}
|
|
|
|
if (isset($data['ts'])) {
|
|
$this->setTimestamp($data['ts']);
|
|
}
|
|
|
|
if (isset($data['type'])) {
|
|
$this->setType($data['type']);
|
|
}
|
|
|
|
if (isset($data['target'])) {
|
|
$this->setTarget($data['target']);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Specify data which should be serialized to JSON
|
|
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
|
* @return mixed data which can be serialized by <b>json_encode</b>,
|
|
* which is a value of any type other than a resource.
|
|
* @since 5.4.0
|
|
*/
|
|
public function jsonSerialize()
|
|
{
|
|
return $this->toArray();
|
|
}
|
|
|
|
/**
|
|
* String representation of object
|
|
* @link http://php.net/manual/en/serializable.serialize.php
|
|
* @return string the string representation of the object or null
|
|
* @since 5.1.0
|
|
*/
|
|
public function serialize()
|
|
{
|
|
return serialize($this->toArray());
|
|
}
|
|
|
|
/**
|
|
* Constructs the object
|
|
* @link http://php.net/manual/en/serializable.unserialize.php
|
|
* @param string $serialized <p>
|
|
* The string representation of the object.
|
|
* </p>
|
|
* @return void
|
|
* @since 5.1.0
|
|
*/
|
|
public function unserialize($serialized)
|
|
{
|
|
$this->fromArray(unserialize($serialized));
|
|
}
|
|
|
|
/**
|
|
* Sets the timestamp of the TimePoint
|
|
* @param float $timestamp
|
|
* @return TimePoint
|
|
*/
|
|
public function setTimestamp($timestamp)
|
|
{
|
|
$this->timestamp = floatval($timestamp);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Gets the timestamp of the TimePoint
|
|
* @return float
|
|
*/
|
|
public function getTimestamp()
|
|
{
|
|
return $this->timestamp;
|
|
}
|
|
|
|
/**
|
|
* Gets the normalized value of the timestamp. This value is the result of:
|
|
* `normalized_timestamp = timestamp_with_microseconds * precision`
|
|
* @return int
|
|
*/
|
|
public function getNormalizedTimestamp()
|
|
{
|
|
return round($this->getTimestamp() * self::PRECISION);
|
|
}
|
|
|
|
/**
|
|
* Sets the type of TimePoint
|
|
* @param int $type Must be a value from TYPE_START or TYPE_END constants.
|
|
* @return TimePoint
|
|
*/
|
|
public function setType($type)
|
|
{
|
|
$this->type = intval($type);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Gets the type of TimePoint
|
|
* @return int
|
|
*/
|
|
public function getType()
|
|
{
|
|
return $this->type;
|
|
}
|
|
|
|
/**
|
|
* Sets the target type of the TimePoint
|
|
* @param int $target Must be a value from TARGET_CLIENT or TARGET_SERVER constants.
|
|
* @return TimePoint
|
|
*/
|
|
public function setTarget($target)
|
|
{
|
|
$this->target = intval($target);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Gets the target type of the TimePoint
|
|
* @return int
|
|
*/
|
|
public function getTarget()
|
|
{
|
|
return $this->target;
|
|
}
|
|
|
|
/**
|
|
* Adds another tag to the TimePoint
|
|
* @param string $tag
|
|
* @return TimePoint
|
|
*/
|
|
public function addTag($tag)
|
|
{
|
|
$this->tags[] = (string)$tag;
|
|
$this->ref = null;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Removes a tag from the TimePoint
|
|
* @param string $tag
|
|
* @return TimePoint
|
|
*/
|
|
public function removeTag($tag)
|
|
{
|
|
$index = array_search($tag, $this->tags);
|
|
|
|
if ($index !== false) {
|
|
array_splice($this->tags, $index, 1);
|
|
$this->ref = null;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Gets a tag from the TimePoint. By default, it will return the first tag.
|
|
* @param int $index
|
|
* @return string
|
|
*/
|
|
public function getTag($index = 0)
|
|
{
|
|
$index = min(max(0, $index), count($this->tags));
|
|
return $this->tags[$index];
|
|
}
|
|
|
|
/**
|
|
* Sets the tags of the TimePoint
|
|
* @param string|array $tags
|
|
* @return TimePoint
|
|
*/
|
|
public function setTags($tags)
|
|
{
|
|
$this->tags = [];
|
|
$this->ref = null;
|
|
|
|
if (is_array($tags)) {
|
|
foreach ($tags as $tag) {
|
|
$this->addTag($tag);
|
|
}
|
|
} else {
|
|
$this->addTag($tags);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Gets all tags from the TimePoint
|
|
* @return array
|
|
*/
|
|
public function getTags()
|
|
{
|
|
return $this->tags;
|
|
}
|
|
|
|
/**
|
|
* Gets a unique reference to name the TimePoint
|
|
* @return string
|
|
*/
|
|
public function getRef()
|
|
{
|
|
if (is_null($this->ref)) {
|
|
$tags = $this->tags;
|
|
sort($tags);
|
|
$this->ref = md5(implode('-', $tags));
|
|
}
|
|
return $this->ref;
|
|
}
|
|
|
|
/**
|
|
* Checks if a TimePoint matches the criteria
|
|
* @param array $tags
|
|
* @param int $target
|
|
* @param int $type
|
|
* @return bool
|
|
*/
|
|
public function match(array $tags = null, $target = self::TARGET_ALL, $type = self::TYPE_ALL)
|
|
{
|
|
$match = ($this->getType() & $type) && ($this->getTarget() & $target);
|
|
|
|
if ($match && isset($tags)) {
|
|
$match = (count(array_intersect($tags, $this->getTags())) == count($tags));
|
|
}
|
|
|
|
return $match;
|
|
}
|
|
|
|
/**
|
|
* Compares the TimePoint with another instance.
|
|
* The comparison is made in this order:
|
|
* - reference
|
|
* - target
|
|
* - timestamp
|
|
* - type
|
|
*
|
|
* CAUTION!: The result order is not based on chronological order.
|
|
* Its goal is to gather TimePoint by reference and target, then sort by type and timestamp.
|
|
*
|
|
* @param TimePoint $point
|
|
* @return int
|
|
*/
|
|
public function compare(TimePoint $point)
|
|
{
|
|
$diff = strcmp($this->getRef(), $point->getRef());
|
|
if ($diff == 0) {
|
|
$diff = $this->getTarget() - $point->getTarget();
|
|
if ($diff == 0) {
|
|
$diff = $this->getNormalizedTimestamp() - $point->getNormalizedTimestamp();
|
|
if ($diff == 0) {
|
|
$diff = $this->getType() - $point->getType();
|
|
}
|
|
}
|
|
}
|
|
return $diff;
|
|
}
|
|
|
|
/**
|
|
* Sorts a range of TimePoint
|
|
* @param array $range
|
|
* @return array
|
|
*/
|
|
public static function sort(array &$range)
|
|
{
|
|
usort($range, function (TimePoint $a, TimePoint $b) {
|
|
return $a->compare($b);
|
|
});
|
|
return $range;
|
|
}
|
|
}
|