From 948f7cc13b9b3de55c14e1f622eb3d09f4a0be1c Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sun, 19 Sep 2010 20:48:19 +0200 Subject: [PATCH] several cosmetic changes --- backend/.gitignore | 1 + backend/bin/doctrine.php | 6 +- backend/index.php | 9 +- .../lib/Interpreter/AggregatorInterpreter.php | 2 +- backend/lib/Interpreter/Interpreter.php | 2 +- backend/lib/Interpreter/MeterInterpreter.php | 2 +- backend/lib/Logger/Logger.php | 3 - backend/lib/Model/Data.php | 7 +- backend/lib/Model/Definition.php | 8 +- backend/lib/Model/Entity.php | 94 ++++++--- backend/lib/Model/Property.php | 49 +++-- backend/lib/Model/Token.php | 2 - backend/lib/Util/ClassLoader.php | 40 ++-- backend/lib/Util/Configuration.php | 6 +- backend/lib/Util/Debug.php | 3 +- backend/lib/Util/UUID.php | 192 ++++++++++++------ share/tests/ajax.php | 2 +- 17 files changed, 273 insertions(+), 155 deletions(-) create mode 100644 backend/.gitignore diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..bc3d175 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1 @@ +/database.sqlite diff --git a/backend/bin/doctrine.php b/backend/bin/doctrine.php index 90aef89..df24c7a 100644 --- a/backend/bin/doctrine.php +++ b/backend/bin/doctrine.php @@ -27,7 +27,9 @@ use Volkszaehler\Util; // TODO replace by state class -const VZ_BACKEND_DIR = '/home/steffen/workspace/volkszaehler.org/backend'; +define('VZ_VERSION', 0.2); +define('VZ_DIR', realpath(__DIR__ . '/../..')); +define('VZ_BACKEND_DIR', VZ_DIR . '/backend'); // class autoloading require VZ_BACKEND_DIR . '/lib/Util/ClassLoader.php'; @@ -44,7 +46,7 @@ foreach ($classLoaders as $loader) { // load configuration Util\Configuration::load(VZ_BACKEND_DIR . '/volkszaehler.conf'); -$em = Volkszaehler\Dispatcher::createEntityManager(); +$em = Volkszaehler\Router::createEntityManager(); $helperSet = new \Symfony\Component\Console\Helper\HelperSet(array( 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), diff --git a/backend/index.php b/backend/index.php index 0cf7d98..581b3bb 100644 --- a/backend/index.php +++ b/backend/index.php @@ -30,13 +30,12 @@ use Volkszaehler\Util; use Volkszaehler\Controller; // enable strict error reporting -error_reporting(E_ALL); +error_reporting(E_ALL | E_STRICT); // TODO replace by state class define('VZ_VERSION', 0.2); define('VZ_DIR', realpath(__DIR__ . '/..')); define('VZ_BACKEND_DIR', VZ_DIR . '/backend'); -define('DEV_ENV', TRUE); // class autoloading require VZ_BACKEND_DIR . '/lib/Util/ClassLoader.php'; @@ -44,7 +43,6 @@ require VZ_BACKEND_DIR . '/lib/Util/ClassLoader.php'; $classLoaders = array(); $classLoaders[] = new Util\ClassLoader('Volkszaehler', VZ_BACKEND_DIR . '/lib'); $classLoaders[] = new Util\ClassLoader('Doctrine', VZ_BACKEND_DIR . '/lib/vendor/Doctrine'); -//$classLoaders[] = new Util\ClassLoader('Symfony', VZ_BACKEND_DIR . '/lib/vendor/Symfony'); // only required for the cli foreach ($classLoaders as $loader) { $loader->register(); // register on SPL autoload stack @@ -52,7 +50,8 @@ foreach ($classLoaders as $loader) { Util\Configuration::load(VZ_BACKEND_DIR . '/volkszaehler.conf'); -$fc = new Dispatcher; // spawn frontcontroller / dispatcher -$fc->run(); // execute controller and sends output +$r = new Router(); +$r->run(); +$r->view->send(); ?> diff --git a/backend/lib/Interpreter/AggregatorInterpreter.php b/backend/lib/Interpreter/AggregatorInterpreter.php index 911c5c1..267e45d 100644 --- a/backend/lib/Interpreter/AggregatorInterpreter.php +++ b/backend/lib/Interpreter/AggregatorInterpreter.php @@ -56,7 +56,7 @@ class AggregatorInterpreter { foreach ($aggregator->getChannels() as $channels) { if (isset($indicator) && $indicator != $channel->getIndicator()) { - throw new \Exception('we only can aggregate channels of the same indicator'); + throw new \Exception('Can\'t aggregate entities of mixed types!'); } else { $indicator = $channel->getIndicator(); diff --git a/backend/lib/Interpreter/Interpreter.php b/backend/lib/Interpreter/Interpreter.php index 448d83c..9e36054 100644 --- a/backend/lib/Interpreter/Interpreter.php +++ b/backend/lib/Interpreter/Interpreter.php @@ -115,7 +115,7 @@ abstract class Interpreter implements InterpreterInterface { return new Iterator\DataAggregationIterator($stmt, $rowCount, $tuples); } else { - throw new \Exception('invalid groupBy parameter'); + throw new \Exception('Invalid parameter: "groupBy"'); } } diff --git a/backend/lib/Interpreter/MeterInterpreter.php b/backend/lib/Interpreter/MeterInterpreter.php index 735b478..7790f29 100644 --- a/backend/lib/Interpreter/MeterInterpreter.php +++ b/backend/lib/Interpreter/MeterInterpreter.php @@ -130,7 +130,7 @@ class MeterInterpreter extends Interpreter { $delta = $next[0] - $last[0]; return array( - (int) ($next[0] - $delta / 2), // timestamp + ($next[0] - $delta / 2), // timestamp $next[1] * (3600000 / (($this->channel->getProperty('resolution')->getValue() / 1000) * $delta)), // value (isset($next[2])) ? $next[2] : 1 ); diff --git a/backend/lib/Logger/Logger.php b/backend/lib/Logger/Logger.php index 07a8cb2..d3ada4d 100644 --- a/backend/lib/Logger/Logger.php +++ b/backend/lib/Logger/Logger.php @@ -76,9 +76,6 @@ abstract class Logger implements LoggerInterface { $data = array($data); } - foreach ($data as $reading) { - $this->em->persist($reading); - } $this->em->flush(); } } diff --git a/backend/lib/Model/Data.php b/backend/lib/Model/Data.php index 438493b..d93e711 100644 --- a/backend/lib/Model/Data.php +++ b/backend/lib/Model/Data.php @@ -34,7 +34,12 @@ use Volkszaehler\Model; * @todo rename? Bsp: DataSample, Sample, Reading * * @Entity - * @Table(name="data") + * @Table( + * name="data", + * uniqueConstraints={ + * @UniqueConstraint(name="unique_timestamp", columns={"timestamp", "channel_id"}) + * } + * ) */ class Data { /** diff --git a/backend/lib/Model/Definition.php b/backend/lib/Model/Definition.php index 737b79b..ed6a6de 100644 --- a/backend/lib/Model/Definition.php +++ b/backend/lib/Model/Definition.php @@ -50,7 +50,7 @@ abstract class Definition { $this->$name = $value; } else { - throw new \Exception('unknown definition syntax: ' . $name); + throw new \Exception('Unknown definition syntax: ' . $name); } } } @@ -63,8 +63,12 @@ abstract class Definition { * @return Util\Definition */ public static function get($name) { + if (is_null(static::$definitions)) { + static::load(); + } + if (!static::exists($name)) { - throw new \Exception('unknown definition'); + throw new \Exception('Unknown definition'); } return static::$definitions[$name]; diff --git a/backend/lib/Model/Entity.php b/backend/lib/Model/Entity.php index dc8a182..da209bd 100644 --- a/backend/lib/Model/Entity.php +++ b/backend/lib/Model/Entity.php @@ -23,6 +23,8 @@ namespace Volkszaehler\Model; +use Doctrine\ORM; + use Doctrine\Common\Collections; use Volkszaehler\Util; @@ -63,7 +65,7 @@ abstract class Entity { /** * @OneToMany(targetEntity="Property", mappedBy="entity", cascade={"remove", "persist"}) - * @OrderBy({"name" = "ASC"}) + * @OrderBy({"key" = "ASC"}) */ protected $properties = NULL; @@ -71,77 +73,103 @@ abstract class Entity { * Constructor * * @param string $type - * @param array $properties of Model\Property */ public function __construct($type) { if (!EntityDefinition::exists($type)) { - throw new \Exception('unknown entity type'); + throw new \Exception('Unknown entity type'); } $this->type = $type; - $this->uuid = Util\UUID::mint(); + $this->uuid = (string) Util\UUID::mint(); $this->tokens = new Collections\ArrayCollection(); $this->properties = new Collections\ArrayCollection(); } + /** - * Checks for optional and required properties according to share/entities.json - * - * Throws an exception if something is incorrect + * Checks for required and invalid properties * * @PrePersist - * @PreUpdate - * @PostLoad - * @todo to be implemented */ - protected function validate() { + public function checkProperties() { + $missingProperties = array_diff($this->getDefinition()->getRequiredProperties(), array_keys($this->getProperties())); + $invalidProperties = array_diff(array_keys($this->getProperties()), $this->getDefinition()->getValidProperties()); + if (count($missingProperties) > 0) { + throw new \Exception('Entity "' . $this->getType() . '" requires propert' . ((count($missingProperties) == 1) ? 'y' : 'ies') . ': "' . implode(', ', $missingProperties) . '"'); + } + + if (count($invalidProperties) > 0) { + throw new \Exception('Propert' . ((count($invalidProperties) == 1) ? 'y' : 'ies') . ' "' . implode(', ', $unallowedProperties) . '" ' . ((count($unallowedProperties) == 1) ? 'is' : 'are') . ' not allowed for entity "' . $this->getType() . '"'); + } } /** * Get a property by name * - * @param string $name - * @return Model\Property + * @param string $key + * @return mixed */ - public function getProperty($name) { - return $this->properties->filter(function($property) use ($name) { - return $property->getName() == $name; - })->first(); + public function getProperty($key) { + return $this->findProperty($key)->getValue(); } /** * Get all properties or properties by prefix * * @param string $prefix + * @return array */ public function getProperties($prefix = NULL) { - if (is_null($prefix)) { - return $this->properties; + $properties = array(); + foreach ($this->properties as $property) { + if (substr($property->getKey(), 0, strlen($prefix)) == $prefix) { + $properties[$property->getKey()] = $property->getValue(); + } } - else { - return $this->properties->filter(function($property) use ($prefix) { - return substr($property->getName(), 0, strlen($prefix) + 1) == $prefix . ':'; - }); + return $properties; + } + + /** + * + * @param string $key + * @return Model\Property + */ + protected function findProperty($key) { + foreach ($this->properties as $property) { + if ($property->getKey() == $key) { + return $property; + } } } /** - * @param string $name of the property - * @param string|integer|float $value of the property - * @todo check if already set for this entity + * Set property + * + * @param string $key name of the property + * @param mixed $value of the property */ - public function setProperty(Property $property) { - $this->properties->add($property); + public function setProperty($key, $value) { + if ($property = $this->findProperty($key)) { // property already exists; just change value + $property->setValue($value); + } + else { // create new property + $property = new Property($this, $key, $value); + $this->properties->add($property); + } } /** + * Unset property + * * @param string $name of the property - * @todo to be implemented + * @param Doctrine\EntityManager $em */ - public function unsetProperty($name) { - + public function unsetProperty($key, ORM\EntityManager $em) { + $property = $this->findProperty($key); + $em->remove($property); + $this->properties->remove($index); } /* @@ -161,8 +189,8 @@ abstract class Entity { * @return Interpreter */ public function getInterpreter(\Doctrine\ORM\EntityManager $em, $from, $to) { - $interpreterClassName = 'Volkszaehler\Interpreter\\' . $this->getDefinition()->getInterpreter(); - return new $interpreterClassName($this, $em, $from, $to); + $class = 'Volkszaehler\Interpreter\\' . $this->getDefinition()->getInterpreter(); + return new $class($this, $em, $from, $to); } } diff --git a/backend/lib/Model/Property.php b/backend/lib/Model/Property.php index 48461e3..8fdb9ad 100644 --- a/backend/lib/Model/Property.php +++ b/backend/lib/Model/Property.php @@ -36,7 +36,7 @@ use Volkszaehler\Model; * @Table( * name="properties", * uniqueConstraints={ - * @UniqueConstraint(name="unique_properties", columns={"id", "name"}) + * @UniqueConstraint(name="unique_keys", columns={"entity_id", "`key` "}) * } * ) * @HasLifecycleCallbacks @@ -51,8 +51,8 @@ class Property { */ protected $id; - /** @Column(type="string", nullable=false) */ - protected $name; + /** @Column(name="`key`", type="string", nullable=false) */ + protected $key; /** @Column(type="string", nullable=false) */ protected $value; @@ -66,11 +66,11 @@ class Property { * @param string $key * @param string $value */ - public function __construct(Model\Entity $entity, $name, $value) { - $this->setName($name); - $this->setValue($value); - + public function __construct(Model\Entity $entity, $key, $value) { $this->entity = $entity; + + $this->key = $key; + $this->value = $value; } /** @@ -78,14 +78,14 @@ class Property { * * @PostLoad */ - public function castValue() { + public function cast() { if ($this->getDefinition()->getType() != 'multiple') { settype($this->value, $this->getDefinition()->getType()); } } /** - * Validate property name & value + * Validate property key & value * * Throws an exception if something is incorrect * @@ -93,24 +93,43 @@ class Property { * @PreUpdate */ public function validate() { - if (!PropertyDefinition::exists($this->name)) { - throw new \Exception('invalid property name: ' . $this->name); + if (!PropertyDefinition::exists($this->key)) { + throw new \Exception('Invalid property key: ' . $this->key); } + $this->cast(); // TODO not safe + if (!$this->getDefinition()->validateValue($this->value)) { - throw new \Exception('invalid property value: ' . $this->value); + throw new \Exception('Invalid property value: ' . $this->value); + } + } + + /** + * @PreRemove + */ + public function checkRemove() { + if (in_array($this->key, $this->entity->getDefinition()->getRequiredProperties())) { + throw new \Exception('"' . $this->key . '" is a required property for the "' . $this->entity->getType() . '" entity'); + } + } + + /** + * @PrePersist + */ + public function checkPersist() { + if (!in_array($this->key, $this->entity->getDefinition()->getValidProperties())) { + throw new \Exception('"' . $this->key . '" is not a valid property for the "' . $this->entity->getType() . '" entity'); } } /* * Setter & Getter */ - public function getName() { return $this->name; } + public function getKey() { return $this->key; } public function getValue() { return $this->value; } - public function getDefinition() { return PropertyDefinition::get($this->name); } + public function getDefinition() { return PropertyDefinition::get($this->key); } public function setValue($value) { $this->value = $value; } - protected function setName($name) { $this->name = $name; } } ?> diff --git a/backend/lib/Model/Token.php b/backend/lib/Model/Token.php index 7fc973f..4865ad6 100644 --- a/backend/lib/Model/Token.php +++ b/backend/lib/Model/Token.php @@ -25,8 +25,6 @@ namespace Volkszaehler\Model; use Volkszaehler\Util; -use Volkszaehler\Model; - /** * Token entity * diff --git a/backend/lib/Util/ClassLoader.php b/backend/lib/Util/ClassLoader.php index e705e49..b86ce07 100644 --- a/backend/lib/Util/ClassLoader.php +++ b/backend/lib/Util/ClassLoader.php @@ -108,15 +108,15 @@ class ClassLoader { /** * Loads the given class or interface. * - * @param string $classname The name of the class to load. + * @param string $class The name of the class to load. * @return boolean TRUE if the class has been successfully loaded, FALSE otherwise. */ - public function loadClass($className) { - if ($this->namespace !== NULL && strpos($className, $this->namespace . $this->namespaceSeparator) !== 0) { + public function loadClass($class) { + if ($this->namespace !== NULL && strpos($class, $this->namespace . $this->namespaceSeparator) !== 0) { return FALSE; } - $subNamespace = substr($className, strlen($this->namespace)); + $subNamespace = substr($class, strlen($this->namespace)); $parts = explode($this->namespaceSeparator, $subNamespace); $path = implode(DIRECTORY_SEPARATOR, $parts); @@ -128,15 +128,15 @@ class ClassLoader { * Asks this ClassLoader whether it can potentially load the class (file) with * the given name. * - * @param string $className The fully-qualified name of the class. + * @param string $class The fully-qualified name of the class. * @return boolean TRUE if this ClassLoader can load the class, FALSE otherwise. */ - public function canLoadClass($className) { - if ($this->namespace !== NULL && strpos($className, $this->namespace . $this->namespaceSeparator) !== 0) { + public function canLoadClass($class) { + if ($this->namespace !== NULL && strpos($class, $this->namespace . $this->namespaceSeparator) !== 0) { return FALSE; } - $subNamespace = substr($className, strlen($this->namespace)); + $subNamespace = substr($class, strlen($this->namespace)); $parts = explode($this->namespaceSeparator, $subNamespace); $path = implode(DIRECTORY_SEPARATOR, $parts); @@ -161,11 +161,11 @@ class ClassLoader { * for its existence. This is not the case with a ClassLoader, who separates * these responsibilities. * - * @param string $className The fully-qualified name of the class. + * @param string $class The fully-qualified name of the class. * @return boolean TRUE if the class exists as per the definition given above, FALSE otherwise. */ - public static function classExists($className) { - if (class_exists($className, FALSE)) { + public static function classExists($class) { + if (class_exists($class, FALSE)) { return TRUE; } @@ -173,20 +173,20 @@ class ClassLoader { if (is_array($loader)) { // array(???, ???) if (is_object($loader[0])) { if ($loader[0] instanceof ClassLoader) { // array($obj, 'methodName') - if ($loader[0]->canLoadClass($className)) { + if ($loader[0]->canLoadClass($class)) { return TRUE; } - } else if ($loader[0]->{$loader[1]}($className)) { + } else if ($loader[0]->{$loader[1]}($class)) { return TRUE; } - } else if ($loader[0]::$loader[1]($className)) { // array('ClassName', 'methodName') + } else if ($loader[0]::$loader[1]($class)) { // array('className', 'methodName') return TRUE; } - } else if ($loader instanceof \Closure) { // function($className) {..} - if ($loader($className)) { + } else if ($loader instanceof \Closure) { // function($class) {..} + if ($loader($class)) { return TRUE; } - } else if (is_string($loader) && $loader($className)) { // "MyClass::loadClass" + } else if (is_string($loader) && $loader($class)) { // "MyClass::loadClass" return TRUE; } } @@ -198,13 +198,13 @@ class ClassLoader { * Gets the ClassLoader from the SPL autoload stack that is responsible * for (and is able to load) the class with the given name. * - * @param string $className The name of the class. + * @param string $class The name of the class. * @return The ClassLoader for the class or NULL if no such ClassLoader exists. */ - public static function getClassLoader($className) { + public static function getClassLoader($class) { foreach (spl_autoload_functions() as $loader) { if (is_array($loader) && $loader[0] instanceof ClassLoader && - $loader[0]->canLoadClass($className)) { + $loader[0]->canLoadClass($class)) { return $loader[0]; } } diff --git a/backend/lib/Util/Configuration.php b/backend/lib/Util/Configuration.php index 5db1cf1..85a329f 100644 --- a/backend/lib/Util/Configuration.php +++ b/backend/lib/Util/Configuration.php @@ -38,7 +38,7 @@ class Configuration { */ static public function write($var, $value) { if (!is_scalar($value) && !is_array($value)) { - throw new \Exception('sry we can\'t store this datatype in the configuration'); + throw new \Exception('Can\'t store this datatype in the configuration'); } $values =& self::$values; @@ -94,13 +94,13 @@ class Configuration { $filename .= '.php'; if (!file_exists($filename)) { - throw new \Exception('configuration file not found: ' . $filename); + throw new \Exception('Configuration file not found: ' . $filename); } include $filename; if (!isset($config)) { - throw new \Exception('no variable $config found in ' . $filename); + throw new \Exception('No variable $config found in ' . $filename); } self::$values = $config; diff --git a/backend/lib/Util/Debug.php b/backend/lib/Util/Debug.php index e39c8f4..0dcc55d 100644 --- a/backend/lib/Util/Debug.php +++ b/backend/lib/Util/Debug.php @@ -24,7 +24,6 @@ namespace Volkszaehler\Util; use Doctrine\ORM; - use Doctrine\DBAL\Logging; /** @@ -62,7 +61,7 @@ class Debug { $em->getConnection()->getConfiguration()->setSQLLogger($this->sqlLogger); if (isset(self::$instance)) { - throw new \Exception('debugging has already been started. please use the static functions!'); + throw new \Exception('Debugging has already been started. please use the static functions!'); } self::$instance = $this; diff --git a/backend/lib/Util/UUID.php b/backend/lib/Util/UUID.php index 1f59e67..7ed154f 100644 --- a/backend/lib/Util/UUID.php +++ b/backend/lib/Util/UUID.php @@ -94,7 +94,7 @@ class UUID { return new self(self::mintTime($node)); case 2: // Version 2 is not supported - throw new \Exception("Version 2 is unsupported."); + throw new \Exception('Version 2 is unsupported.'); case 3: return new self(self::mintName(self::MD5, $node, $ns)); case 4: @@ -102,19 +102,42 @@ class UUID { case 5: return new self(self::mintName(self::SHA1, $node, $ns)); default: - throw new \Exception("Selected version is invalid or unsupported."); + throw new \Exception('Selected version is invalid or unsupported.'); } } /** * Import an existing UUID * - * @param unkown_type $uuid + * @param unknown_type $uuid */ public static function import($uuid) { return new self(self::makeBin($uuid, 16)); } + /** + * Performant validation of UUID's + * + * Replaces preg_match('/[a-f0-9\-]{36}/', $uuid); + * + * @param string $uuid + * @param boolen $short whether to allow abbreviated form of UUID's or not + */ + public static function validate($uuid, $short = FALSE) { + $len = strlen($uuid); + + for ($i = 0; $i < $len; $i++) { + $char = $uuid[$i]; + $ord = ord($char); + + if (($ord > 57 || $ord < 48) && ($ord > 70 || $ord < 65) && ($ord > 102 || $ord < 97) && $ord != 45) { + return FALSE; // char not allowed + } + } + + return ($short) ? $len <= 36 : $len == 36; // check for strlen + } + /** * Compares the binary representations of two UUIDs. * The comparison will return TRUE if they are bit-exact, @@ -133,43 +156,42 @@ class UUID { public function __get($var) { switch($var) { - case "bytes": + case 'bytes': return $this->bytes; - case "hex": + case 'hex': return bin2hex($this->bytes); - case "string": + case 'string': return $this->__toString(); - case "urn": - return "urn:uuid:".$this->__toString(); - case "version": + case 'urn': + return 'urn:uuid:' . $this->__toString(); + case 'version': return ord($this->bytes[6]) >> 4; - case "variant": + case 'variant': $byte = ord($this->bytes[8]); - if ($byte >= self::varRes) - return 3; - if ($byte >= self::varMS) - return 2; - if ($byte >= self::varRFC) - return 1; - else - return 0; - case "node": - if (ord($this->bytes[6])>>4==1) - return bin2hex(substr($this->bytes,10)); - else - return NULL; - case "time": + if ($byte >= self::varRes) return 3; + if ($byte >= self::varMS) return 2; + if ($byte >= self::varRFC) return 1; + else return 0; + case 'node': + if (ord($this->bytes[6])>>4==1) { + return bin2hex(substr($this->bytes,10)); + } + else { + return NULL; + } + case 'time': if (ord($this->bytes[6])>>4==1) { // Restore contiguous big-endian byte order $time = bin2hex($this->bytes[6].$this->bytes[7].$this->bytes[4].$this->bytes[5].$this->bytes[0].$this->bytes[1].$this->bytes[2].$this->bytes[3]); // Clear version flag - $time[0] = "0"; + $time[0] = '0'; // Do some reverse arithmetic to get a Unix timestamp $time = (hexdec($time) - self::interval) / 10000000; return $time; } - else - return NULL; + else { + return NULL; + } default: return NULL; } @@ -177,76 +199,104 @@ class UUID { protected function __construct($uuid) { if (strlen($uuid) != 16) { - throw new \Exception("Input must be a 128-bit integer."); + throw new \Exception('Input must be a 128-bit integer.'); } $this->bytes = $uuid; // Optimize the most common use $this->string = - bin2hex(substr($uuid,0,4))."-". - bin2hex(substr($uuid,4,2))."-". - bin2hex(substr($uuid,6,2))."-". - bin2hex(substr($uuid,8,2))."-". + bin2hex(substr($uuid,0,4)).'-'. + bin2hex(substr($uuid,4,2)).'-'. + bin2hex(substr($uuid,6,2)).'-'. + bin2hex(substr($uuid,8,2)).'-'. bin2hex(substr($uuid,10,6)); } + /** + * Generates a Version 1 UUID. + * These are derived from the time at which they were generated. + * + * @param $node + */ protected static function mintTime($node = NULL) { - /* Generates a Version 1 UUID. - These are derived from the time at which they were generated. */ // Get time since Gregorian calendar reform in 100ns intervals // This is exceedingly difficult because of PHP's (and pack()'s) // integer size limits. // Note that this will never be more accurate than to the microsecond. - $time = microtime(1) * 10000000 + self::interval; + $time = microtime(TRUE) * 10000000 + self::interval; + // Convert to a string representation - $time = sprintf("%F", $time); - preg_match("/^\d+/", $time, $time); //strip decimal point + $time = sprintf('%F', $time); + preg_match('/^\d+/', $time, $time); //strip decimal point + // And now to a 64-bit binary representation $time = base_convert($time[0], 10, 16); - $time = pack("H*", str_pad($time, 16, "0", STR_PAD_LEFT)); + $time = pack('H*', str_pad($time, 16, '0', STR_PAD_LEFT)); + // Reorder bytes to their proper locations in the UUID $uuid = $time[4].$time[5].$time[6].$time[7].$time[2].$time[3].$time[0].$time[1]; + // Generate a random clock sequence $uuid .= Random::getBytes(2); + // set variant $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC); + // set version $uuid[6] = chr(ord($uuid[6]) & self::clearVer | self::version1); + // Set the final 'node' parameter, a MAC address - if ($node) - $node = self::makeBin($node, 6); - if (!$node) { + if ($node) { + $node = self::makeBin($node, 6); + } + else { // If no node was provided or if the node was invalid, // generate a random MAC address and set the multicast bit $node = Random::getBytes(6); - $node[0] = pack("C", ord($node[0]) | 1); + $node[0] = pack('C', ord($node[0]) | 1); } + $uuid .= $node; return $uuid; } + /** + * Generate a Version 4 UUID. + * These are derived soly from random numbers. + */ protected static function mintRand() { - /* Generate a Version 4 UUID. - These are derived soly from random numbers. */ // generate random fields $uuid = Random::getBytes(16); + // set variant $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC); + // set version $uuid[6] = chr(ord($uuid[6]) & self::clearVer | self::version4); + return $uuid; } + /** + * Generates a Version 3 or Version 5 UUID. + * These are derived from a hash of a name and its namespace, in binary form. + * + * @param integer $ver the version (MD5 or SHA1) + * @param string $node the name string + * $param string $ns the namespace + */ protected static function mintName($ver, $node, $ns) { - /* Generates a Version 3 or Version 5 UUID. - These are derived from a hash of a name and its namespace, in binary form. */ - if (!$node) - throw new \Exception("A name-string is required for Version 3 or 5 UUIDs."); + if (!$node) { + throw new \Exception('A name-string is required for Version 3 or 5 UUIDs.'); + } + // if the namespace UUID isn't binary, make it so $ns = self::makeBin($ns, 16); - if (!$ns) - throw new \Exception("A binary namespace is required for Version 3 or 5 UUIDs."); + if (!$ns) { + throw new \Exception('A binary namespace is required for Version 3 or 5 UUIDs.'); + } + switch($ver) { case self::MD5: $version = self::version3; @@ -257,27 +307,43 @@ class UUID { $uuid = substr(sha1($ns.$node,1),0, 16); break; } + // set variant $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC); + // set version $uuid[6] = chr(ord($uuid[6]) & self::clearVer | $version); + return ($uuid); } + /** + * Insure that an input string is either binary or hexadecimal. + * Returns binary representation, or FALSE on failure. + * + * @param unkown_type $str + * @param integer $len + */ protected static function makeBin($str, $len) { - /* Insure that an input string is either binary or hexadecimal. - Returns binary representation, or FALSE on failure. */ - if ($str instanceof self) - return $str->bytes; - if (strlen($str)==$len) - return $str; - else - $str = preg_replace("/^urn:uuid:/is", "", $str); // strip URN scheme and namespace - $str = preg_replace("/[^a-f0-9]/is", "", $str); // strip non-hex characters - if (strlen($str) != ($len * 2)) - return FALSE; - else - return pack("H*", $str); + if ($str instanceof self) { + return $str->bytes; + } + + if (strlen($str) == $len) { + return $str; + } + else { + $str = preg_replace('/^urn:uuid:/is', '', $str); // strip URN scheme and namespace + } + + $str = preg_replace('/[^a-f0-9]/is', '', $str); // strip non-hex characters + + if (strlen($str) != ($len * 2)) { + return FALSE; + } + else { + return pack('H*', $str); + } } } diff --git a/share/tests/ajax.php b/share/tests/ajax.php index 884cad7..0eb796e 100644 --- a/share/tests/ajax.php +++ b/share/tests/ajax.php @@ -36,7 +36,7 @@