pax_global_header00006660000000000000000000000064131776221650014524gustar00rootroot0000000000000052 comment=e756ea3f8e938f9197a756ad32d797b5455d316d php-enum-3.0.0/000077500000000000000000000000001317762216500132555ustar00rootroot00000000000000php-enum-3.0.0/.gitignore000066400000000000000000000001451317762216500152450ustar00rootroot00000000000000#eclipse .project #phpstorm .idea #composer composer.lock composer.phar vendor #phpbench _storage php-enum-3.0.0/.travis.yml000066400000000000000000000042311317762216500153660ustar00rootroot00000000000000sudo: false language: php cache: directories: - $HOME/.composer/cache - $HOME/.local - $HOME/ocular.phar - $HOME/phpDocumentor.phar env: global: - CODE_COVERAGE="0" - PHPDOC="0" matrix: fast_finish: true include: - php: 5.6 env: - CODE_COVERAGE="1" - php: 7.0 env: - CODE_COVERAGE="1" - PHPDOC="1" - php: 7.1 env: - CODE_COVERAGE="1" - php: 7.2 env: - CODE_COVERAGE="1" - php: nightly # HHVM is no longer supported on Ubuntu Precise. Please consider using Trusty with `dist: trusty`. - php: hhvm dist: trusty allow_failures: - php: nightly install: - if [ "${CODE_COVERAGE}" == "0" ]; then phpenv config-rm xdebug.ini || return 0; fi - if [ "${CODE_COVERAGE}" == "1" ]; then wget -q -N -t 3 --retry-connrefused 'https://scrutinizer-ci.com/ocular.phar' || return 0; fi - if [ "${PHPDOC}" == "1" ]; then wget -q -N -t 3 --retry-connrefused 'https://github.com/phpDocumentor/phpDocumentor2/releases/download/v2.9.0/phpDocumentor.phar' || return 0; composer require --no-update --dev "evert/phpdoc-md:~0.2.0" || return 0; fi - composer install -n script: - if [ "$CODE_COVERAGE" == "1" ]; then php -d 'zend.assertions=1' vendor/bin/phpunit --verbose --coverage-text --coverage-clover=coverage.clover; else php -d 'zend.assertions=1' vendor/bin/phpunit --verbose; fi after_script: - if [ "${CODE_COVERAGE}" == "1" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi - if [ "${PHPDOC}" == "1" ]; then git clone "https://${CI_USER_TOKEN}@github.com/marc-mabe/php-enum.wiki.git" && php phpDocumentor.phar -d src -t docs/ --template="xml" && php vendor/bin/phpdocmd --lt '%c' --index 'Home.md' docs/structure.xml php-enum.wiki/ && cp php-enum.wiki/Home.md php-enum.wiki/_Sidebar.md && cd php-enum.wiki/ && git add . && git commit -m "auto generated PHP doc" && git push origin master:master; fi notifications: email: false php-enum-3.0.0/LICENSE.txt000066400000000000000000000027701317762216500151060ustar00rootroot00000000000000Copyright (c) 2015, Marc Bennewitz All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the organisation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. php-enum-3.0.0/README.md000066400000000000000000000311441317762216500145370ustar00rootroot00000000000000# php-enum [![Build Status](https://secure.travis-ci.org/marc-mabe/php-enum.png?branch=master)](http://travis-ci.org/marc-mabe/php-enum) [![Quality Score](https://scrutinizer-ci.com/g/marc-mabe/php-enum/badges/quality-score.png?s=7dfddb19a12314ecc5f05eeb2b297bdde3ad2623)](https://scrutinizer-ci.com/g/marc-mabe/php-enum/) [![Code Coverage](https://scrutinizer-ci.com/g/marc-mabe/php-enum/badges/coverage.png?s=8442d532fad964fd3d8afe493ac2d0d65162306a)](https://scrutinizer-ci.com/g/marc-mabe/php-enum/) [![Total Downloads](https://poser.pugx.org/marc-mabe/php-enum/downloads.png)](https://packagist.org/packages/marc-mabe/php-enum) [![Latest Stable](https://poser.pugx.org/marc-mabe/php-enum/v/stable.png)](https://packagist.org/packages/marc-mabe/php-enum) [![Dependency Status](https://www.versioneye.com/php/marc-mabe:php-enum/dev-master/badge.png)](https://www.versioneye.com/php/marc-mabe:php-enum/dev-master) This is a native PHP implementation to add enumeration support to PHP >= 5.3. It's an abstract class that needs to be extended to use it. # What is an Enumeration? [Wikipedia](http://wikipedia.org/wiki/Enumerated_type) > In computer programming, an enumerated type (also called enumeration or enum) > is a data type consisting of a set of named values called elements, members > or enumerators of the type. The enumerator names are usually identifiers that > behave as constants in the language. A variable that has been declared as > having an enumerated type can be assigned any of the enumerators as a value. > In other words, an enumerated type has values that are different from each > other, and that can be compared and assigned, but which do not have any > particular concrete representation in the computer's memory; compilers and > interpreters can represent them arbitrarily. # Usage ## PHPDoc You can find auto-generated PHP documentation in the [wiki](https://github.com/marc-mabe/php-enum/wiki). ## Basics ```php use MabeEnum\Enum; // define an own enumeration class class UserStatus extends Enum { const INACTIVE = 'i'; const ACTIVE = 'a'; const DELETED = 'd'; // all scalar data types and arrays are supported as enumerator values const NIL = null; const BOOLEAN = true; const INT = 1234; const STR = 'string'; const FLOAT = 0.123; const ARR = ['this', 'is', ['an', 'array']]; // Enumerators will be generated from public constants only public const PUBLIC_CONST = 'public constant'; // this will be an enumerator protected const PROTECTED_CONST = 'protected constant'; // this will NOT be an enumerator private const PRIVATE_CONST = 'private constant'; // this will NOT be an enumerator // works since PHP-7.0 - see https://wiki.php.net/rfc/context_sensitive_lexer const TRUE = 'true'; const FALSE = 'false'; const NULL = 'null'; const PUBLIC = 'public'; const PRIVATE = 'private'; const PROTECTED = 'protected'; const FUNCTION = 'function'; const TRAIT = 'trait'; const INTERFACE = 'interface'; // Doesn't work - see https://wiki.php.net/rfc/class_name_scalars // const CLASS = 'class'; } // ways to instantiate an enumerator $status = UserStatus::get(UserStatus::ACTIVE); // by value or instance $status = UserStatus::ACTIVE(); // by name as callable $status = UserStatus::byValue('a'); // by value $status = UserStatus::byName('ACTIVE'); // by name $status = UserStatus::byOrdinal(1); // by ordinal number // basic methods of an instantiated enumerator $status->getValue(); // returns the selected constant value $status->getName(); // returns the selected constant name $status->getOrdinal(); // returns the ordinal number of the selected constant // basic methods to list defined enumerators UserStatus::getEnumerators(); // returns a list of enumerator instances UserStatus::getValues(); // returns a list of enumerator values UserStatus::getNames(); // returns a list of enumerator names UserStatus::getOrdinals(); // returns a list of ordinal numbers UserStatus::getConstants(); // returns an associative array of enumerator names to enumerator values // same enumerators (of the same enumeration class) holds the same instance UserStatus::get(UserStatus::ACTIVE) === UserStatus::ACTIVE() UserStatus::get(UserStatus::DELETED) != UserStatus::INACTIVE() // simplified way to compare two enumerators $status = UserStatus::ACTIVE(); $status->is(UserStatus::ACTIVE); // true $status->is(UserStatus::ACTIVE()); // true $status->is(UserStatus::DELETED); // false $status->is(UserStatus::DELETED()); // false ``` ## Type-Hint ```php use MabeEnum\Enum; class User { protected $status; public function setStatus(UserStatus $status) { $this->status = $status; } public function getStatus() { if (!$this->status) { // initialize default $this->status = UserStatus::INACTIVE(); } return $this->status; } } ``` ### Type-Hint issue Because in normal OOP the above example allows `UserStatus` and types inherited from it. Please think about the following example: ```php class ExtendedUserStatus extends UserStatus { const EXTENDED = 'extended'; } $user = new User(); $user->setStatus(ExtendedUserStatus::EXTENDED()); ``` Now the setter receives a status it doesn't know about but allows it. #### Solution 1: Finilize the numeration ```php final class UserStatus extends Enum { // ... } class User { protected $status; public function setStatus(UserStatus $status) { $this->status = $status; } } ```` * Nice and obvious solution * Resulting behaviour matches native enumeration implementation of most other languages (like Java) But as this library emulates enumerations it has a view downsides: * Enumerator values can not be used directly * `$user->setStatus(UserStatus::ACTIVE)` fails * `$user->setStatus(UserStatus::ACTIVE())` works * Does not help if the enumeration was defined in an external library #### Solution 2: Using `Enum::get()` ```php class User { public function setStatus($status) { $this->status = UserStatus::get($status); } } ``` * Makes sure the resulting enumerator exactly matches an enumeration. (Inherited enumerators as not allowed). * Allows enumerator values directly * `$user->setStatus(UserStatus::ACTIVE)` works * `$user->setStatus(UserStatus::ACTIVE())` works * Also works for enumerations defined in external libraries But of course this solution has downsides, too: * Looses declarative type-hint * A bit slower ## EnumSet An `EnumSet` groups enumerators of the same enumeration type together. It implements `Iterator` and `Countable` so elements can be iterated and counted like a normal array using `foreach` and `count()`. Internally it's based on a bitset. Integer bitset or binary bitset depending on how many enumerators are defined for the given enumeration. Enumerators attached to an `EnumSet` are unique and ordered based on it's ordinal number by design. ```php use MabeEnum\EnumSet; // create a new EnumSet $enumSet = new EnumSet('UserStatus'); // attach enumerators (by value or by instance) $enumSet->attach(UserStatus::INACTIVE); $enumSet->attach(UserStatus::ACTIVE()); $enumSet->attach(UserStatus::DELETED()); // detach enumerators (by value or by instance) $enumSet->detach(UserStatus::INACTIVE); $enumSet->detach(UserStatus::DELETED()); // contains enumerators (by value or by instance) $enumSet->contains(UserStatus::INACTIVE); // bool // count number of attached enumerations $enumSet->count(); count($enumSet); // convert to array $enumSet->getValues(); // List of enumerator values $enumSet->getEnumerators(); // List of enumerator instances $enumSet->getNames(); // List of enumerator names $enumSet->getOrdinals(); // List of ordinal numbers // iterating over the set foreach ($enumSet as $ordinal => $enum) { gettype($ordinal); // int (the ordinal number of the enumerator) get_class($enum); // UserStatus (enumerator object) } // compare two EnumSets $enumSet->isEqual($other); // Check if the EnumSet is the same as other $enumSet->isSubset($other); // Check if the EnumSet is a subset of other $enumSet->isSuperset($other); // Check if the EnumSet is a superset of other $enumSet->union($other); // Produce a new set with enumerators from both this and other (this | other) $enumSet->intersect($other); // Produce a new set with enumerators common to both this and other (this & other) $enumSet->diff($other); // Produce a new set with enumerators in this but not in other (this - other) $enumSet->symDiff($other); // Produce a new set with enumerators in either this and other but not in both (this ^ other) ``` ## EnumMap An `EnumMap` maps enumerators of the same type to data assigned to. It implements `ArrayAccess`, `Countable` and `SeekableIterator` so elements can be accessed, iterated and counted like a normal array using `$enumMap[$key]`, `foreach` and `count()`. ```php use MabeEnum\EnumMap; // create a new EnumMap $enumMap = new EnumMap('UserStatus'); // read and write key-value-pairs like an array $enumMap[UserStatus::INACTIVE] = 'inaktiv'; $enumMap[UserStatus::ACTIVE] = 'aktiv'; $enumMap[UserStatus::DELETED] = 'gelöscht'; $enumMap[UserStatus::INACTIVE]; // 'inaktiv'; $enumMap[UserStatus::ACTIVE]; // 'aktiv'; $enumMap[UserStatus::DELETED]; // 'gelöscht'; isset($enumMap[UserStatus::DELETED]); // true unset($enumMap[UserStatus::DELETED]); isset($enumMap[UserStatus::DELETED]); // false // ... no matter if you use enumerator values or enumerator objects $enumMap[UserStatus::INACTIVE()] = 'inaktiv'; $enumMap[UserStatus::ACTIVE()] = 'aktiv'; $enumMap[UserStatus::DELETED()] = 'gelöscht'; $enumMap[UserStatus::INACTIVE()]; // 'inaktiv'; $enumMap[UserStatus::ACTIVE()]; // 'aktiv'; $enumMap[UserStatus::DELETED()]; // 'gelöscht'; isset($enumMap[UserStatus::DELETED()]); // true unset($enumMap[UserStatus::DELETED()]); isset($enumMap[UserStatus::DELETED()]); // false // count number of attached elements $enumMap->count(); count($enumMap); // support for null aware exists check $enumMap[UserStatus::NULL] = null; isset($enumMap[UserStatus::NULL]); // false $enumMap->contains(UserStatus::NULL); // true // iterating over the map foreach ($enumMap as $enum => $value) { get_class($enum); // UserStatus (enumerator object) gettype($value); // string (the value the enumerators maps to) } // get a list of keys (= a list of enumerator objects) $enumMap->getKeys(); // get a list of values (= a list of values the enumerator maps to) $enumMap->getValues(); ``` ## Serializing Because this enumeration implementation is based on a singleton pattern and in PHP it's currently impossible to unserialize a singleton without creating a new instance this feature isn't supported without any additional work. As of it's an often requested feature there is a trait that can be added to your enumeration definition. The trait adds serialization functionallity and injects the unserialized enumeration instance in case it's the first one. This reduces singleton behavior breakage but still it beaks if it's not the first instance and you could result in two different instance of the same enumeration. **Use it with caution!** PS: `EnumSet` and `EnumMap` are serializable by default as long as you don't set other non-serializable values. ### Example of using EnumSerializableTrait ```php use MabeEnum\Enum; use MabeEnum\EnumSerializableTrait; use Serializable; class CardinalDirection extends Enum implements Serializable { use EnumSerializableTrait; const NORTH = 'n'; const EAST = 'e'; const WEST = 'w'; const SOUTH = 's'; } $north1 = CardinalDirection::NORTH(); $north2 = unserialize(serialize($north1)); var_dump($north1 === $north2); // returns FALSE as described above var_dump($north1->is($north2)); // returns TRUE - this way the two instances are treated equal var_dump($north2->is($north1)); // returns TRUE - equality works in both directions ``` # Why not `SplEnum` * `SplEnum` is not build-in into PHP and requires pecl extension installed. * Instances of the same value of an `SplEnum` are not the same instance. * No support for `EnumMap` or `EnumSet`. # Install ## Composer Add `marc-mabe/php-enum` to the project's composer.json dependencies and run `php composer.phar install` ## GIT `git clone git://github.com/marc-mabe/php-enum.git` ## ZIP / TAR Download the last version from [Github](https://github.com/marc-mabe/php-enum/tags) and extract it. # New BSD License The files in this archive are released under the New BSD License. You can find a copy of this license in LICENSE.txt file. php-enum-3.0.0/bench/000077500000000000000000000000001317762216500143345ustar00rootroot00000000000000php-enum-3.0.0/bench/EnumBench.php000066400000000000000000000075571317762216500167270ustar00rootroot00000000000000enumPropsRefl = $enumRefl->getProperties(ReflectionProperty::IS_STATIC); foreach ($this->enumPropsRefl as $enumPropRefl) { $enumPropRefl->setAccessible(true); } $this->names = Enum66::getNames(); $this->values = Enum66::getValues(); $this->ordinals = Enum66::getOrdinals(); $this->enumerators = Enum66::getEnumerators(); } private function resetStaticEnumProps() { foreach ($this->enumPropsRefl as $enumPropRefl) { $enumPropRefl->setValue([]); } } public function benchGetName() { foreach ($this->enumerators as $enumerator) { $enumerator->getName(); } } public function benchGetValue() { foreach ($this->enumerators as $enumerator) { $enumerator->getValue(); } } public function benchGetOrdinal() { foreach ($this->enumerators as $enumerator) { $enumerator->getOrdinal(); } } public function benchIsByEnumerator() { foreach ($this->enumerators as $enumerator) { $enumerator->is($enumerator); } } public function benchIsByValue() { foreach ($this->enumerators as $enumerator) { $enumerator->is($enumerator->getValue()); } } public function benchDetectConstants() { $this->resetStaticEnumProps(); Enum66::getConstants(); } public function benchGetValues() { Enum66::getValues(); } public function benchGetNames() { Enum66::getNames(); } public function benchGetOrdinals() { Enum66::getOrdinals(); } public function benchGetEnumerators() { Enum66::getEnumerators(); } public function benchByValue() { foreach ($this->values as $value) { Enum66::byValue($value); } } public function benchByName() { foreach ($this->names as $name) { Enum66::byName($name); } } public function benchByOrdinal() { foreach ($this->ordinals as $ord) { Enum66::byOrdinal($ord); } } public function benchGetByValues() { foreach ($this->values as $value) { Enum66::get($value); } } public function benchGetByEnumerator() { foreach ($this->enumerators as $enumerator) { Enum66::get($enumerator); } } public function benchGetByCallStatic() { foreach ($this->names as $name) { Enum66::$name(); } } public function benchHasByEnumerator() { foreach ($this->enumerators as $enumerator) { Enum66::has($enumerator); } } public function benchHasByValue() { foreach ($this->values as $value) { Enum66::has($value); } } } php-enum-3.0.0/bench/EnumMapBench.php000066400000000000000000000067161317762216500173610ustar00rootroot00000000000000values = Enum66::getValues(); $this->enumerators = Enum66::getEnumerators(); $this->emptyMap = new EnumMap(Enum66::class); $this->fullMap = new EnumMap(Enum66::class); foreach ($this->enumerators as $i => $enumerator) { $this->fullMap->offsetSet($enumerator, $i); } } public function benchGetKeysEmpty() { $this->emptyMap->getKeys(); } public function benchGetKeysFull() { $this->fullMap->getKeys(); } public function benchGetValuesEmpty() { $this->emptyMap->getValues(); } public function benchGetValuesFull() { $this->fullMap->getValues(); } public function benchSearchTypeJuggling() { $this->fullMap->search('31'); } public function benchSearchStrict() { $this->fullMap->search(31, true); } public function benchOffsetSetEnumerator() { foreach ($this->enumerators as $enumerator) { $this->emptyMap->offsetSet($enumerator); } } public function benchOffsetSetValue() { foreach ($this->values as $value) { $this->emptyMap->offsetSet($value); } } public function benchOffsetUnsetEnumerator() { foreach ($this->enumerators as $enumerator) { $this->fullMap->offsetUnset($enumerator); } } public function benchOffsetUnsetValue() { foreach ($this->values as $value) { $this->fullMap->offsetUnset($value); } } public function benchOffsetExistsEnumerator() { foreach ($this->enumerators as $enumerator) { $this->fullMap->offsetExists($enumerator); } } public function benchOffsetExistsValue() { foreach ($this->values as $value) { $this->fullMap->offsetExists($value); } } public function benchContainsEnumerator() { foreach ($this->enumerators as $enumerator) { $this->fullMap->contains($enumerator); } } public function benchContainsValue() { foreach ($this->values as $value) { $this->fullMap->contains($value); } } public function benchIterateFull() { foreach ($this->fullMap as $enumerator => $_) { $enumerator->getValue(); } } public function benchIterateEmpty() { foreach ($this->emptyMap as $enumerator => $_) { $enumerator->getValue(); } } public function benchCountFull() { $this->fullMap->count(); } public function benchCountEmpty() { $this->emptyMap->count(); } } php-enum-3.0.0/bench/EnumSet32Bench.php000066400000000000000000000100601317762216500175270ustar00rootroot00000000000000values = Enum32::getValues(); $this->enumerators = Enum32::getEnumerators(); $this->emptySet = new EnumSet(Enum32::class); $this->fullSet = new EnumSet(Enum32::class); foreach ($this->enumerators as $enumerator) { $this->fullSet->attach($enumerator); } } public function benchAttachEnumerator() { foreach ($this->enumerators as $enumerator) { $this->emptySet->attach($enumerator); } } public function benchAttachValue() { foreach ($this->values as $value) { $this->emptySet->attach($value); } } public function benchDetachEnumerator() { foreach ($this->enumerators as $enumerator) { $this->fullSet->detach($enumerator); } } public function benchDetachValue() { foreach ($this->values as $value) { $this->fullSet->detach($value); } } public function benchContainsEnumeratorTrue() { foreach ($this->enumerators as $enumerator) { $this->fullSet->contains($enumerator); } } public function benchContainsEnumeratorFalse() { foreach ($this->enumerators as $enumerator) { $this->fullSet->contains($enumerator); } } public function benchContainsValueTrue() { foreach ($this->values as $value) { $this->fullSet->contains($value); } } public function benchContainsValueFalse() { foreach ($this->values as $value) { $this->fullSet->contains($value); } } public function benchIterateFull() { foreach ($this->fullSet as $enumerator) { $enumerator->getValue(); } } public function benchIterateEmpty() { foreach ($this->emptySet as $enumerator) { $enumerator->getValue(); } } public function benchCountFull() { $this->fullSet->count(); } public function benchCountEmpty() { $this->emptySet->count(); } public function benchIsEqual() { $this->fullSet->isEqual($this->fullSet); } public function benchIsSubset() { $this->fullSet->isEqual($this->fullSet); } public function benchIsSuperset() { $this->fullSet->isSuperset($this->fullSet); } public function benchUnion() { $this->fullSet->union($this->emptySet); } public function benchIntersect() { $this->fullSet->intersect($this->emptySet); } public function benchDiff() { $this->fullSet->diff($this->emptySet); } public function benchSymDiff() { $this->fullSet->symDiff($this->emptySet); } public function benchGetOrdinalsFull() { $this->fullSet->getOrdinals(); } public function benchGetOrdinalsEmpty() { $this->emptySet->getOrdinals(); } public function benchGetValues() { $this->fullSet->getValues(); } public function benchGetNames() { $this->fullSet->getNames(); } public function benchGetEnumerators() { $this->fullSet->getEnumerators(); } } php-enum-3.0.0/bench/EnumSet66Bench.php000066400000000000000000000113241317762216500175420ustar00rootroot00000000000000values = Enum66::getValues(); $this->enumerators = Enum66::getEnumerators(); $this->emptySet = new EnumSet(Enum66::class); $this->fullSet = new EnumSet(Enum66::class); foreach ($this->enumerators as $enumerator) { $this->fullSet->attach($enumerator); } } public function benchAttachEnumeratorOnEmpty() { foreach ($this->enumerators as $enumerator) { $this->emptySet->attach($enumerator); } } public function benchAttachValueOnEmpty() { foreach ($this->values as $value) { $this->emptySet->attach($value); } } public function benchAttachEnumeratorOnFull() { foreach ($this->enumerators as $enumerator) { $this->fullSet->attach($enumerator); } } public function benchAttachValueOnFull() { foreach ($this->values as $value) { $this->fullSet->attach($value); } } public function benchDetachEnumeratorOnEmpty() { foreach ($this->enumerators as $enumerator) { $this->emptySet->detach($enumerator); } } public function benchDetachValueOnEmpty() { foreach ($this->values as $value) { $this->emptySet->detach($value); } } public function benchDetachEnumeratorOnFull() { foreach ($this->enumerators as $enumerator) { $this->fullSet->detach($enumerator); } } public function benchDetachValueOnFull() { foreach ($this->values as $value) { $this->fullSet->detach($value); } } public function benchContainsEnumeratorTrue() { foreach ($this->enumerators as $enumerator) { $this->fullSet->contains($enumerator); } } public function benchContainsValueTrue() { foreach ($this->values as $value) { $this->fullSet->contains($value); } } public function benchContainsEnumeratorFalse() { foreach ($this->enumerators as $enumerator) { $this->fullSet->contains($enumerator); } } public function benchContainsValueFalse() { foreach ($this->values as $value) { $this->fullSet->contains($value); } } public function benchIterateFull() { foreach ($this->fullSet as $enumerator) { $enumerator->getValue(); } } public function benchIterateEmpty() { foreach ($this->emptySet as $enumerator) { $enumerator->getValue(); } } public function benchCountFull() { $this->fullSet->count(); } public function benchCountEmpty() { $this->emptySet->count(); } public function benchIsEqual() { $this->fullSet->isEqual($this->fullSet); } public function benchIsSubset() { $this->fullSet->isEqual($this->fullSet); } public function benchIsSuperset() { $this->fullSet->isSuperset($this->fullSet); } public function benchUnion() { $this->fullSet->union($this->emptySet); } public function benchIntersect() { $this->fullSet->intersect($this->emptySet); } public function benchDiff() { $this->fullSet->diff($this->emptySet); } public function benchSymDiff() { $this->fullSet->symDiff($this->emptySet); } public function benchGetOrdinalsFull() { $this->fullSet->getOrdinals(); } public function benchGetOrdinalsEmpty() { $this->emptySet->getOrdinals(); } public function benchGetValues() { $this->fullSet->getValues(); } public function benchGetNames() { $this->fullSet->getNames(); } public function benchGetEnumerators() { $this->fullSet->getEnumerators(); } } php-enum-3.0.0/bench/bootstrap.php000066400000000000000000000007421317762216500170650ustar00rootroot00000000000000= 70000 && $zendassertions != -1) { echo 'Please disable zend.assertions in php.ini (zend.assertions = -1)' . PHP_EOL . "Current ini setting: zend.assertions = {$zendassertions}]" . PHP_EOL; exit(1); } assert_options(ASSERT_ACTIVE, 0); assert_options(ASSERT_WARNING, 0); assert_options(ASSERT_BAIL, 0); assert_options(ASSERT_QUIET_EVAL, 0); require_once __DIR__ . '/../vendor/autoload.php'; php-enum-3.0.0/composer.json000066400000000000000000000025261317762216500160040ustar00rootroot00000000000000{ "name": "marc-mabe/php-enum", "description": "Simple and fast implementation of enumerations with native PHP>=5.6", "type": "library", "keywords": [ "enum", "enumeration", "enumerator", "enumset", "enum-set", "set", "enummap", "enum-map", "map", "type", "typehint", "type-hint" ], "homepage": "https://github.com/marc-mabe/php-enum", "authors": [{ "name": "Marc Bennewitz", "email": "dev@mabe.berlin", "homepage": "http://mabe.berlin/", "role": "Lead" }], "license": "BSD-3-Clause", "require": { "php": ">=5.6", "ext-reflection": "*" }, "require-dev": { "phpunit/phpunit": "^5.7 || ^6.0", "phpbench/phpbench": "@dev", "lstrojny/functional-php": "HHVM: variadic params with type constraints are not supported in non-Hack", "lstrojny/functional-php": "1.2.* || 1.0.*" }, "autoload": { "psr-4": { "MabeEnum\\": "src/" } }, "autoload-dev": { "psr-4": { "MabeEnumTest\\": "tests/MabeEnumTest/", "MabeEnumBench\\": "bench/" } }, "extra": { "branch-alias": { "dev-master": "3.0-dev", "dev-2.x": "2.3-dev", "dev-1.x": "1.3-dev" } } } php-enum-3.0.0/phpbench.json000066400000000000000000000001251317762216500157350ustar00rootroot00000000000000{ "bootstrap": "bench/bootstrap.php", "path": "bench/", "retry_threshold": 5 } php-enum-3.0.0/phpunit.xml.dist000066400000000000000000000016051317762216500164320ustar00rootroot00000000000000 ./tests ./src php-enum-3.0.0/src/000077500000000000000000000000001317762216500140445ustar00rootroot00000000000000php-enum-3.0.0/src/Enum.php000066400000000000000000000273561317762216500154760ustar00rootroot00000000000000 ["$name" => $value, ...], ...] */ private static $constants = []; /** * A List of available enumerator names by enumeration class * * @var array ["$class" => ["$name0", ...], ...] */ private static $names = []; /** * Already instantiated enumerators * * @var array ["$class" => ["$name" => $instance, ...], ...] */ private static $instances = []; /** * Constructor * * @param null|bool|int|float|string $value The value of the enumerator * @param int|null $ordinal The ordinal number of the enumerator */ final private function __construct($value, $ordinal = null) { $this->value = $value; $this->ordinal = $ordinal; } /** * Get the name of the enumerator * * @return string * @see getName() */ public function __toString() { return $this->getName(); } /** * @throws LogicException Enums are not cloneable * because instances are implemented as singletons */ final private function __clone() { throw new LogicException('Enums are not cloneable'); } /** * @throws LogicException Enums are not serializable * because instances are implemented as singletons */ final public function __sleep() { throw new LogicException('Enums are not serializable'); } /** * @throws LogicException Enums are not serializable * because instances are implemented as singletons */ final public function __wakeup() { throw new LogicException('Enums are not serializable'); } /** * Get the value of the enumerator * * @return null|bool|int|float|string */ final public function getValue() { return $this->value; } /** * Get the name of the enumerator * * @return string */ final public function getName() { $ordinal = $this->ordinal !== null ? $this->ordinal : $this->getOrdinal(); return self::$names[static::class][$ordinal]; } /** * Get the ordinal number of the enumerator * * @return int */ final public function getOrdinal() { if ($this->ordinal === null) { $ordinal = 0; $value = $this->value; foreach (self::detectConstants(static::class) as $constValue) { if ($value === $constValue) { break; } ++$ordinal; } $this->ordinal = $ordinal; } return $this->ordinal; } /** * Compare this enumerator against another and check if it's the same. * * @param mixed $enumerator * @return bool */ final public function is($enumerator) { return $this === $enumerator || $this->value === $enumerator // The following additional conditions are required only because of the issue of serializable singletons || ($enumerator instanceof static && \get_class($enumerator) === static::class && $enumerator->value === $this->value ); } /** * Get an enumerator instance of the given enumerator value or instance * * @param static|null|bool|int|float|string $enumerator * @return static * @throws InvalidArgumentException On an unknwon or invalid value * @throws LogicException On ambiguous constant values */ final public static function get($enumerator) { if ($enumerator instanceof static && \get_class($enumerator) === static::class) { return $enumerator; } return static::byValue($enumerator); } /** * Get an enumerator instance by the given value * * @param mixed $value * @return static * @throws InvalidArgumentException On an unknwon or invalid value * @throws LogicException On ambiguous constant values */ final public static function byValue($value) { $constants = self::detectConstants(static::class); $name = \array_search($value, $constants, true); if ($name === false) { throw new InvalidArgumentException(sprintf( 'Unknown value %s for enumeration %s', \is_scalar($value) ? \var_export($value, true) : 'of type ' . (\is_object($value) ? \get_class($value) : \gettype($value)), static::class )); } if (!isset(self::$instances[static::class][$name])) { self::$instances[static::class][$name] = new static($constants[$name]); } return self::$instances[static::class][$name]; } /** * Get an enumerator instance by the given name * * @param string $name The name of the enumerator * @return static * @throws InvalidArgumentException On an invalid or unknown name * @throws LogicException On ambiguous values */ final public static function byName($name) { $name = (string) $name; if (isset(self::$instances[static::class][$name])) { return self::$instances[static::class][$name]; } $const = static::class . '::' . $name; if (!\defined($const)) { throw new InvalidArgumentException($const . ' not defined'); } return self::$instances[static::class][$name] = new static(\constant($const)); } /** * Get an enumeration instance by the given ordinal number * * @param int $ordinal The ordinal number or the enumerator * @return static * @throws InvalidArgumentException On an invalid ordinal number * @throws LogicException On ambiguous values */ final public static function byOrdinal($ordinal) { $ordinal = (int) $ordinal; if (!isset(self::$names[static::class])) { self::detectConstants(static::class); } if (!isset(self::$names[static::class][$ordinal])) { throw new InvalidArgumentException(\sprintf( 'Invalid ordinal number, must between 0 and %s', \count(self::$names[static::class]) - 1 )); } $name = self::$names[static::class][$ordinal]; if (isset(self::$instances[static::class][$name])) { return self::$instances[static::class][$name]; } $const = static::class . '::' . $name; return self::$instances[static::class][$name] = new static(\constant($const), $ordinal); } /** * Get a list of enumerator instances ordered by ordinal number * * @return static[] */ final public static function getEnumerators() { if (!isset(self::$names[static::class])) { self::detectConstants(static::class); } return \array_map([static::class, 'byName'], self::$names[static::class]); } /** * Get a list of enumerator values ordered by ordinal number * * @return mixed[] */ final public static function getValues() { return \array_values(self::detectConstants(static::class)); } /** * Get a list of enumerator names ordered by ordinal number * * @return string[] */ final public static function getNames() { if (!isset(self::$names[static::class])) { self::detectConstants(static::class); } return self::$names[static::class]; } /* * Get a list of enumerator ordinal numbers * * @return int[] */ final public static function getOrdinals() { $count = \count(self::detectConstants(static::class)); return $count === 0 ? [] : \range(0, $count - 1); } /** * Get all available constants of the called class * * @return array * @throws LogicException On ambiguous constant values */ final public static function getConstants() { return self::detectConstants(static::class); } /** * Is the given enumerator part of this enumeration * * @param static|null|bool|int|float|string $value * @return bool */ final public static function has($value) { if ($value instanceof static && \get_class($value) === static::class) { return true; } $constants = self::detectConstants(static::class); return \in_array($value, $constants, true); } /** * Detect all public available constants of given enumeration class * * @param string $class * @return array */ private static function detectConstants($class) { if (!isset(self::$constants[$class])) { $reflection = new ReflectionClass($class); $publicConstants = []; do { $scopeConstants = []; if (\PHP_VERSION_ID >= 70100) { // Since PHP-7.1 visibility modifiers are allowed for class constants // for enumerations we are only interested in public once. foreach ($reflection->getReflectionConstants() as $reflConstant) { if ($reflConstant->isPublic()) { $scopeConstants[ $reflConstant->getName() ] = $reflConstant->getValue(); } } } else { // In PHP < 7.1 all class constants were public by definition $scopeConstants = $reflection->getConstants(); } $publicConstants = $scopeConstants + $publicConstants; } while (($reflection = $reflection->getParentClass()) && $reflection->name !== __CLASS__); assert(self::noAmbiguousValues($publicConstants)); self::$constants[$class] = $publicConstants; self::$names[$class] = \array_keys($publicConstants); } return self::$constants[$class]; } /** * Assert that the given enumeration class doesn't define ambiguous enumerator values * @param array $constants * @return bool */ private static function noAmbiguousValues(array $constants) { foreach ($constants as $value) { $names = \array_keys($constants, $value, true); if (\count($names) > 1) { return false; } } return true; } /** * Get an enumerator instance by the given name. * * This will be called automatically on calling a method * with the same name of a defined enumerator. * * @param string $method The name of the enumeraotr (called as method) * @param array $args There should be no arguments * @return static * @throws InvalidArgumentException On an invalid or unknown name * @throws LogicException On ambiguous constant values */ final public static function __callStatic($method, array $args) { return self::byName($method); } } php-enum-3.0.0/src/EnumMap.php000066400000000000000000000154101317762216500161200ustar00rootroot00000000000000) and mixed values. * * @link http://github.com/marc-mabe/php-enum for the canonical source repository * @copyright Copyright (c) 2017 Marc Bennewitz * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License */ class EnumMap implements ArrayAccess, Countable, SeekableIterator { /** * The classname of the enumeration type * @var string */ private $enumeration; /** * Internal map of ordinal number and value * @var array */ private $map = []; /** * List of ordinal numbers * @var int[] */ private $ordinals = []; /** * Current iterator position * @var int */ private $pos = 0; /** * Constructor * @param string $enumeration The classname of the enumeration type * @throws InvalidArgumentException */ public function __construct($enumeration) { if (!\is_subclass_of($enumeration, Enum::class)) { throw new InvalidArgumentException(\sprintf( "This EnumMap can handle subclasses of '%s' only", Enum::class )); } $this->enumeration = $enumeration; } /** * Get the classname of the enumeration * @return string */ public function getEnumeration() { return $this->enumeration; } /** * Get a list of map keys * @return Enum[] */ public function getKeys() { return \array_map([$this->enumeration, 'byOrdinal'], $this->ordinals); } /** * Get a list of map values * @return mixed[] */ public function getValues() { return \array_values($this->map); } /** * Search for the given value * @param mixed $value * @param bool $strict Use strict type comparison * @return Enum|null The found key or NULL */ public function search($value, $strict = false) { $ord = \array_search($value, $this->map, $strict); if ($ord !== false) { $enumeration = $this->enumeration; return $enumeration::byOrdinal($ord); } return null; } /** * Test if the given enumerator exists * @param Enum|null|boolean|int|float|string $enumerator * @return boolean * @see offsetExists */ public function contains($enumerator) { try { $enumeration = $this->enumeration; $ord = $enumeration::get($enumerator)->getOrdinal(); return array_key_exists($ord, $this->map); } catch (InvalidArgumentException $e) { // An invalid enumerator can't be contained in this map return false; } } /** * Test if the given enumerator key exists and is not NULL * @param Enum|null|boolean|int|float|string $enumerator * @return boolean * @see contains */ public function offsetExists($enumerator) { try { $enumeration = $this->enumeration; $ord = $enumeration::get($enumerator)->getOrdinal(); return isset($this->map[$ord]); } catch (InvalidArgumentException $e) { // An invalid enumerator can't be an offset of this map return false; } } /** * Get mapped data for the given enumerator * @param Enum|null|boolean|int|float|string $enumerator * @return mixed * @throws InvalidArgumentException On an invalid given enumerator */ public function offsetGet($enumerator) { $enumeration = $this->enumeration; $ord = $enumeration::get($enumerator)->getOrdinal(); if (!isset($this->map[$ord]) && !array_key_exists($ord, $this->map)) { throw new UnexpectedValueException(\sprintf( "Enumerator '%s' could not be found", \is_object($enumerator) ? $enumerator->getValue() : $enumerator )); } return $this->map[$ord]; } /** * Attach a new enumerator or overwrite an existing one * @param Enum|null|boolean|int|float|string $enumerator * @param mixed $value * @return void * @throws InvalidArgumentException On an invalid given enumerator * @see attach() */ public function offsetSet($enumerator, $value = null) { $enumeration = $this->enumeration; $ord = $enumeration::get($enumerator)->getOrdinal(); if (!array_key_exists($ord, $this->map)) { $this->ordinals[] = $ord; } $this->map[$ord] = $value; } /** * Detach an existing enumerator * @param Enum|null|boolean|int|float|string $enumerator * @return void * @throws InvalidArgumentException On an invalid given enumerator * @see detach() */ public function offsetUnset($enumerator) { $enumeration = $this->enumeration; $ord = $enumeration::get($enumerator)->getOrdinal(); if (($idx = \array_search($ord, $this->ordinals, true)) !== false) { unset($this->map[$ord], $this->ordinals[$idx]); $this->ordinals = \array_values($this->ordinals); } } /** * Seeks to the given iterator position. * @param int $pos */ public function seek($pos) { $pos = (int)$pos; if (!isset($this->ordinals[$pos])) { throw new OutOfBoundsException("Position {$pos} not found"); } $this->pos = $pos; } /** * Get the current value * @return mixed */ public function current() { if (!isset($this->ordinals[$this->pos])) { return null; } return $this->map[$this->ordinals[$this->pos]]; } /** * Get the current key * @return Enum|null */ public function key() { if (!isset($this->ordinals[$this->pos])) { return null; } $enumeration = $this->enumeration; return $enumeration::byOrdinal($this->ordinals[$this->pos]); } /** * Reset the iterator position to zero. * @return void */ public function rewind() { $this->pos = 0; } /** * Increment the iterator position by one. * @return void */ public function next() { ++$this->pos; } /** * Test if the iterator is in a valid state * @return boolean */ public function valid() { return isset($this->ordinals[$this->pos]); } /** * Count the number of elements * * @return int */ public function count() { return \count($this->ordinals); } } php-enum-3.0.0/src/EnumSerializableTrait.php000066400000000000000000000051401317762216500210140ustar00rootroot00000000000000getValue()); } /** * Unserializes a given serialized value and push it into the current instance * This will be called automatically on `unserialize()` if the enumeration implements the `Serializable` interface * @param string $serialized * @throws RuntimeException On an unknown or invalid value * @throws LogicException On changing numeration value by calling this directly */ public function unserialize($serialized) { $value = \unserialize($serialized); $constants = self::getConstants(); $name = \array_search($value, $constants, true); if ($name === false) { $message = \is_scalar($value) ? 'Unknown value ' . \var_export($value, true) : 'Invalid value of type ' . (\is_object($value) ? \get_class($value) : \gettype($value)); throw new RuntimeException($message); } $class = \get_class($this); $enumerator = $this; $closure = function () use ($class, $name, $value, $enumerator) { if ($value !== null && $this->value !== null) { throw new LogicException('Do not call this directly - please use unserialize($enum) instead'); } $this->value = $value; if (!isset(self::$instances[$class][$name])) { self::$instances[$class][$name] = $enumerator; } }; $closure = $closure->bindTo($this, Enum::class); $closure(); } } php-enum-3.0.0/src/EnumSet.php000066400000000000000000000550531317762216500161450ustar00rootroot00000000000000) * based on an integer or binary bitset depending of given enumeration size. * * @link http://github.com/marc-mabe/php-enum for the canonical source repository * @copyright Copyright (c) 2017 Marc Bennewitz * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License */ class EnumSet implements Iterator, Countable { /** * The classname of the Enumeration * @var string */ private $enumeration; /** * Ordinal number of current iterator position * @var int */ private $ordinal = 0; /** * Highest possible ordinal number * @var int */ private $ordinalMax; /** * Integer or binary (little endian) bitset * @var int|string */ private $bitset = 0; /**#@+ * Defines private method names to be called depended of how the bitset type was set too. * ... Integer or binary bitset. * ... *Int or *Bin method * * @var string */ private $fnDoRewind = 'doRewindInt'; private $fnDoCount = 'doCountInt'; private $fnDoGetOrdinals = 'doGetOrdinalsInt'; private $fnDoGetBit = 'doGetBitInt'; private $fnDoSetBit = 'doSetBitInt'; private $fnDoUnsetBit = 'doUnsetBitInt'; private $fnDoGetBinaryBitsetLe = 'doGetBinaryBitsetLeInt'; private $fnDoSetBinaryBitsetLe = 'doSetBinaryBitsetLeInt'; /**#@-*/ /** * Constructor * * @param string $enumeration The classname of the enumeration * @throws InvalidArgumentException */ public function __construct($enumeration) { if (!\is_subclass_of($enumeration, Enum::class)) { throw new InvalidArgumentException(\sprintf( "%s can handle subclasses of '%s' only", static::class, Enum::class )); } $this->enumeration = $enumeration; $this->ordinalMax = count($enumeration::getConstants()); // By default the bitset is initialized as integer bitset // in case the enumeraton has more enumerators then integer bits // we will switch this into a binary bitset if ($this->ordinalMax > \PHP_INT_SIZE * 8) { // init binary bitset with zeros $this->bitset = \str_repeat("\0", (int)\ceil($this->ordinalMax / 8)); // switch internal binary bitset functions $this->fnDoRewind = 'doRewindBin'; $this->fnDoCount = 'doCountBin'; $this->fnDoGetOrdinals = 'doGetOrdinalsBin'; $this->fnDoGetBit = 'doGetBitBin'; $this->fnDoSetBit = 'doSetBitBin'; $this->fnDoUnsetBit = 'doUnsetBitBin'; $this->fnDoGetBinaryBitsetLe = 'doGetBinaryBitsetLeBin'; $this->fnDoSetBinaryBitsetLe = 'doSetBinaryBitsetLeBin'; } } /** * Get the classname of the enumeration * @return string */ public function getEnumeration() { return $this->enumeration; } /** * Attach a new enumerator or overwrite an existing one * @param Enum|null|boolean|int|float|string $enumerator * @return void * @throws InvalidArgumentException On an invalid given enumerator */ public function attach($enumerator) { $enumeration = $this->enumeration; $this->{$this->fnDoSetBit}($enumeration::get($enumerator)->getOrdinal()); } /** * Detach the given enumerator * @param Enum|null|boolean|int|float|string $enumerator * @return void * @throws InvalidArgumentException On an invalid given enumerator */ public function detach($enumerator) { $enumeration = $this->enumeration; $this->{$this->fnDoUnsetBit}($enumeration::get($enumerator)->getOrdinal()); } /** * Test if the given enumerator was attached * @param Enum|null|boolean|int|float|string $enumerator * @return boolean */ public function contains($enumerator) { $enumeration = $this->enumeration; return $this->{$this->fnDoGetBit}($enumeration::get($enumerator)->getOrdinal()); } /* Iterator */ /** * Get the current enumerator * @return Enum|null Returns the current enumerator or NULL on an invalid iterator position */ public function current() { if ($this->valid()) { $enumeration = $this->enumeration; return $enumeration::byOrdinal($this->ordinal); } return null; } /** * Get the ordinal number of the current iterator position * @return int */ public function key() { return $this->ordinal; } /** * Go to the next valid iterator position. * If no valid iterator position is found the iterator position will be the last possible + 1. * @return void */ public function next() { do { if (++$this->ordinal >= $this->ordinalMax) { $this->ordinal = $this->ordinalMax; return; } } while (!$this->{$this->fnDoGetBit}($this->ordinal)); } /** * Go to the first valid iterator position. * If no valid iterator position was found the iterator position will be 0. * @return void * @see doRewindBin * @see doRewindInt */ public function rewind() { $this->{$this->fnDoRewind}(); } /** * Go to the first valid iterator position. * If no valid iterator position was found the iterator position will be 0. * * This is the binary bitset implementation. * * @return void * @see rewind * @see doRewindInt */ private function doRewindBin() { if (\ltrim($this->bitset, "\0") !== '') { $this->ordinal = -1; $this->next(); } else { $this->ordinal = 0; } } /** * Go to the first valid iterator position. * If no valid iterator position was found the iterator position will be 0. * * This is the binary bitset implementation. * * @return void * @see rewind * @see doRewindBin */ private function doRewindInt() { if ($this->bitset) { $this->ordinal = -1; $this->next(); } else { $this->ordinal = 0; } } /** * Test if the iterator is in a valid state * @return boolean */ public function valid() { return $this->ordinal !== $this->ordinalMax && $this->{$this->fnDoGetBit}($this->ordinal); } /* Countable */ /** * Count the number of elements * * @return int * @see doCountBin * @see doCountInt */ public function count() { return $this->{$this->fnDoCount}(); } /** * Count the number of elements. * * This is the binary bitset implementation. * * @return int * @see count * @see doCountInt */ private function doCountBin() { $count = 0; $bitset = $this->bitset; $byteLen = \strlen($bitset); for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) { if ($bitset[$bytePos] === "\0") { // fast skip null byte continue; } $ord = \ord($bitset[$bytePos]); if ($ord & 0b00000001) ++$count; if ($ord & 0b00000010) ++$count; if ($ord & 0b00000100) ++$count; if ($ord & 0b00001000) ++$count; if ($ord & 0b00010000) ++$count; if ($ord & 0b00100000) ++$count; if ($ord & 0b01000000) ++$count; if ($ord & 0b10000000) ++$count; } return $count; } /** * Count the number of elements. * * This is the integer bitset implementation. * * @return int * @see count * @see doCountBin */ private function doCountInt() { $count = 0; $bitset = $this->bitset; // PHP does not support right shift unsigned if ($bitset < 0) { $count = 1; $bitset = $bitset & \PHP_INT_MAX; } // iterate byte by byte and count set bits $phpIntBitSize = \PHP_INT_SIZE * 8; for ($bitPos = 0; $bitPos < $phpIntBitSize; $bitPos += 8) { $bitChk = 0xff << $bitPos; $byte = $bitset & $bitChk; if ($byte) { $byte = $byte >> $bitPos; if ($byte & 0b00000001) ++$count; if ($byte & 0b00000010) ++$count; if ($byte & 0b00000100) ++$count; if ($byte & 0b00001000) ++$count; if ($byte & 0b00010000) ++$count; if ($byte & 0b00100000) ++$count; if ($byte & 0b01000000) ++$count; if ($byte & 0b10000000) ++$count; } if ($bitset <= $bitChk) { break; } } return $count; } /** * Check if this EnumSet is the same as other * @param EnumSet $other * @return bool */ public function isEqual(EnumSet $other) { return $this->enumeration === $other->enumeration && $this->bitset === $other->bitset; } /** * Check if this EnumSet is a subset of other * @param EnumSet $other * @return bool */ public function isSubset(EnumSet $other) { if ($this->enumeration !== $other->enumeration) { return false; } return ($this->bitset & $other->bitset) === $this->bitset; } /** * Check if this EnumSet is a superset of other * @param EnumSet $other * @return bool */ public function isSuperset(EnumSet $other) { if ($this->enumeration !== $other->enumeration) { return false; } return ($this->bitset | $other->bitset) === $this->bitset; } /** * Produce a new set with enumerators from both this and other (this | other) * * @param EnumSet $other EnumSet of the same enumeration to produce the union * @return EnumSet */ public function union(EnumSet $other) { if ($this->enumeration !== $other->enumeration) { throw new InvalidArgumentException(\sprintf( 'Other should be of the same enumeration as this %s', $this->enumeration )); } $clone = clone $this; $clone->bitset = $this->bitset | $other->bitset; return $clone; } /** * Produce a new set with enumerators common to both this and other (this & other) * * @param EnumSet $other EnumSet of the same enumeration to produce the intersect * @return EnumSet */ public function intersect(EnumSet $other) { if ($this->enumeration !== $other->enumeration) { throw new InvalidArgumentException(\sprintf( 'Other should be of the same enumeration as this %s', $this->enumeration )); } $clone = clone $this; $clone->bitset = $this->bitset & $other->bitset; return $clone; } /** * Produce a new set with enumerators in this but not in other (this - other) * * @param EnumSet $other EnumSet of the same enumeration to produce the diff * @return EnumSet */ public function diff(EnumSet $other) { if ($this->enumeration !== $other->enumeration) { throw new InvalidArgumentException(\sprintf( 'Other should be of the same enumeration as this %s', $this->enumeration )); } $clone = clone $this; $clone->bitset = $this->bitset & ~$other->bitset; return $clone; } /** * Produce a new set with enumerators in either this and other but not in both (this ^ other) * * @param EnumSet $other EnumSet of the same enumeration to produce the symmetric difference * @return EnumSet */ public function symDiff(EnumSet $other) { if ($this->enumeration !== $other->enumeration) { throw new InvalidArgumentException(\sprintf( 'Other should be of the same enumeration as this %s', $this->enumeration )); } $clone = clone $this; $clone->bitset = $this->bitset ^ $other->bitset; return $clone; } /** * Get ordinal numbers of the defined enumerators as array * @return int[] */ public function getOrdinals() { return $this->{$this->fnDoGetOrdinals}(); } /** * Get ordinal numbers of the defined enumerators as array. * * This is the binary bitset implementation. * * @return int[] * @see getOrdinals * @see goGetOrdinalsInt */ private function doGetOrdinalsBin() { $ordinals = []; $bitset = $this->bitset; $byteLen = \strlen($bitset); for ($bytePos = 0; $bytePos < $byteLen; ++$bytePos) { if ($bitset[$bytePos] === "\0") { // fast skip null byte continue; } $ord = \ord($bitset[$bytePos]); for ($bitPos = 0; $bitPos < 8; ++$bitPos) { if ($ord & (1 << $bitPos)) { $ordinals[] = $bytePos * 8 + $bitPos; } } } return $ordinals; } /** * Get ordinal numbers of the defined enumerators as array. * * This is the integer bitset implementation. * * @return int[] * @see getOrdinals * @see doGetOrdinalsBin */ private function doGetOrdinalsInt() { $ordinals = []; $ordinalMax = $this->ordinalMax; $bitset = $this->bitset; for ($ord = 0; $ord < $ordinalMax; ++$ord) { if ($bitset & (1 << $ord)) { $ordinals[] = $ord; } } return $ordinals; } /** * Get values of the defined enumerators as array * @return null[]|bool[]|int[]|float[]|string[] */ public function getValues() { $enumeration = $this->enumeration; $values = []; foreach ($this->getOrdinals() as $ord) { $values[] = $enumeration::byOrdinal($ord)->getValue(); } return $values; } /** * Get names of the defined enumerators as array * @return string[] */ public function getNames() { $enumeration = $this->enumeration; $names = []; foreach ($this->getOrdinals() as $ord) { $names[] = $enumeration::byOrdinal($ord)->getName(); } return $names; } /** * Get the defined enumerators as array * @return Enum[] */ public function getEnumerators() { $enumeration = $this->enumeration; $enumerators = []; foreach ($this->getOrdinals() as $ord) { $enumerators[] = $enumeration::byOrdinal($ord); } return $enumerators; } /** * Get binary bitset in little-endian order * * @return string */ public function getBinaryBitsetLe() { return $this->{$this->fnDoGetBinaryBitsetLe}(); } /** * Get binary bitset in little-endian order. * * This is the binary bitset implementation. * * @return string */ private function doGetBinaryBitsetLeBin() { return $this->bitset; } /** * Get binary bitset in little-endian order. * * This is the integer bitset implementation. * * @return string */ private function doGetBinaryBitsetLeInt() { $bin = \pack(\PHP_INT_SIZE === 8 ? 'P' : 'V', $this->bitset); return \substr($bin, 0, (int)\ceil($this->ordinalMax / 8)); } /** * Set binary bitset in little-endian order * * NOTE: It resets the current position of the iterator * * @param string $bitset * @return void * @throws InvalidArgumentException On a non string is given as Parameter */ public function setBinaryBitsetLe($bitset) { if (!\is_string($bitset)) { throw new InvalidArgumentException('Bitset must be a string'); } $this->{$this->fnDoSetBinaryBitsetLe}($bitset); // reset the iterator position $this->rewind(); } /** * Set binary bitset in little-endian order * * NOTE: It resets the current position of the iterator * * @param string $bitset * @return void * @throws InvalidArgumentException On a non string is given as Parameter */ private function doSetBinaryBitsetLeBin($bitset) { $size = \strlen($this->bitset); $sizeIn = \strlen($bitset); if ($sizeIn < $size) { // add "\0" if the given bitset is not long enough $bitset .= \str_repeat("\0", $size - $sizeIn); } elseif ($sizeIn > $size) { if (\ltrim(\substr($bitset, $size), "\0") !== '') { throw new InvalidArgumentException('Out-Of-Range bits detected'); } $bitset = \substr($bitset, 0, $size); } // truncate out-of-range bits of last byte $lastByteMaxOrd = $this->ordinalMax % 8; if ($lastByteMaxOrd !== 0) { $lastByte = $bitset[$size - 1]; $lastByteExpected = \chr((1 << $lastByteMaxOrd) - 1) & $lastByte; if ($lastByte !== $lastByteExpected) { throw new InvalidArgumentException('Out-Of-Range bits detected'); } $this->bitset = \substr($bitset, 0, -1) . $lastByteExpected; } $this->bitset = $bitset; } /** * Set binary bitset in little-endian order * * NOTE: It resets the current position of the iterator * * @param string $bitset * @return void * @throws InvalidArgumentException On a non string is given as Parameter */ private function doSetBinaryBitsetLeInt($bitset) { $len = \strlen($bitset); $int = 0; for ($i = 0; $i < $len; ++$i) { $ord = \ord($bitset[$i]); if ($ord && $i > \PHP_INT_SIZE - 1) { throw new InvalidArgumentException('Out-Of-Range bits detected'); } $int |= $ord << (8 * $i); } if ($int & (~0 << $this->ordinalMax)) { throw new InvalidArgumentException('Out-Of-Range bits detected'); } $this->bitset = $int; } /** * Get binary bitset in big-endian order * * @return string */ public function getBinaryBitsetBe() { return \strrev($this->bitset); } /** * Set binary bitset in big-endian order * * NOTE: It resets the current position of the iterator * * @param string $bitset * @return void * @throws InvalidArgumentException On a non string is given as Parameter */ public function setBinaryBitsetBe($bitset) { if (!\is_string($bitset)) { throw new InvalidArgumentException('Bitset must be a string'); } $this->setBinaryBitsetLe(\strrev($bitset)); } /** * Get a bit at the given ordinal number * * @param int $ordinal Ordinal number of bit to get * @return boolean */ public function getBit($ordinal) { if ($ordinal < 0 || $ordinal > $this->ordinalMax) { throw new InvalidArgumentException("Ordinal number must be between 0 and {$this->ordinalMax}"); } return $this->{$this->fnDoGetBit}($ordinal); } /** * Get a bit at the given ordinal number. * * This is the binary bitset implementation. * * @param int $ordinal Ordinal number of bit to get * @return boolean * @see getBit * @see doGetBitInt */ private function doGetBitBin($ordinal) { return (\ord($this->bitset[(int) ($ordinal / 8)]) & 1 << ($ordinal % 8)) !== 0; } /** * Get a bit at the given ordinal number. * * This is the integer bitset implementation. * * @param int $ordinal Ordinal number of bit to get * @return boolean * @see getBit * @see doGetBitBin */ private function doGetBitInt($ordinal) { return (bool)($this->bitset & (1 << $ordinal)); } /** * Set a bit at the given ordinal number * * @param int $ordinal Ordnal number of bit to set * @param bool $bit The bit to set * @return void * @see doSetBitBin * @see doSetBitInt * @see doUnsetBin * @see doUnsetInt */ public function setBit($ordinal, $bit) { if ($ordinal < 0 || $ordinal > $this->ordinalMax) { throw new InvalidArgumentException("Ordinal number must be between 0 and {$this->ordinalMax}"); } if ($bit) { $this->{$this->fnDoSetBit}($ordinal); } else { $this->{$this->fnDoUnsetBit}($ordinal); } } /** * Set a bit at the given ordinal number. * * This is the binary bitset implementation. * * @param int $ordinal Ordnal number of bit to set * @return void * @see setBit * @see doSetBitInt */ private function doSetBitBin($ordinal) { $byte = (int) ($ordinal / 8); $this->bitset[$byte] = $this->bitset[$byte] | \chr(1 << ($ordinal % 8)); } /** * Set a bit at the given ordinal number. * * This is the binary bitset implementation. * * @param int $ordinal Ordnal number of bit to set * @return void * @see setBit * @see doSetBitBin */ private function doSetBitInt($ordinal) { $this->bitset = $this->bitset | (1 << $ordinal); } /** * Unset a bit at the given ordinal number. * * This is the binary bitset implementation. * * @param int $ordinal Ordinal number of bit to unset * @return void * @see setBit * @see doUnsetBitInt */ private function doUnsetBitBin($ordinal) { $byte = (int) ($ordinal / 8); $this->bitset[$byte] = $this->bitset[$byte] & \chr(~(1 << ($ordinal % 8))); } /** * Unset a bit at the given ordinal number. * * This is the integer bitset implementation. * * @param int $ordinal Ordinal number of bit to unset * @return void * @see setBit * @see doUnsetBitBin */ private function doUnsetBitInt($ordinal) { $this->bitset = $this->bitset & ~(1 << $ordinal); } } php-enum-3.0.0/tests/000077500000000000000000000000001317762216500144175ustar00rootroot00000000000000php-enum-3.0.0/tests/MabeEnumTest/000077500000000000000000000000001317762216500167505ustar00rootroot00000000000000php-enum-3.0.0/tests/MabeEnumTest/EnumMapTest.php000066400000000000000000000276021317762216500216720ustar00rootroot00000000000000assertSame(EnumBasic::class, $enumMap->getEnumeration()); $enum1 = EnumBasic::TWO(); $value1 = 'value1'; $enum2 = EnumBasic::ONE(); $value2 = 'value2'; $this->assertFalse($enumMap->contains($enum1)); $this->assertFalse($enumMap->contains($enum2)); $this->assertSame([], $enumMap->getKeys()); $this->assertSame([], $enumMap->getValues()); $this->assertNull($enumMap->offsetSet($enum1, $value1)); $this->assertTrue($enumMap->contains($enum1)); $this->assertSame($value1, $enumMap[$enum1]); $this->assertFalse($enumMap->contains($enum2)); $this->assertSame([$enum1], $enumMap->getKeys()); $this->assertSame([$value1], $enumMap->getValues()); $this->assertNull($enumMap->offsetSet($enum2, $value2)); $this->assertTrue($enumMap->contains($enum2)); $this->assertSame($value2, $enumMap[$enum2]); $this->assertSame([$enum1, $enum2], $enumMap->getKeys()); $this->assertSame([$value1, $value2], $enumMap->getValues()); $this->assertNull($enumMap->offsetUnset($enum1)); $this->assertFalse($enumMap->contains($enum1)); $this->assertSame([$enum2], $enumMap->getKeys()); $this->assertSame([$value2], $enumMap->getValues()); $this->assertNull($enumMap->offsetUnset($enum2)); $this->assertFalse($enumMap->contains($enum2)); $this->assertSame([], $enumMap->getKeys()); $this->assertSame([], $enumMap->getValues()); } public function testBasicWithEnumeratorValues() { $enumMap = new EnumMap(EnumBasic::class); $enum1 = EnumBasic::ONE; $value1 = 'value1'; $enum2 = EnumBasic::TWO; $value2 = 'value2'; $this->assertFalse($enumMap->contains($enum1)); $this->assertFalse($enumMap->contains($enum2)); $this->assertSame([], $enumMap->getKeys()); $this->assertSame([], $enumMap->getValues()); $this->assertNull($enumMap->offsetSet($enum1, $value1)); $this->assertTrue($enumMap->contains($enum1)); $this->assertSame($value1, $enumMap[$enum1]); $this->assertFalse($enumMap->contains($enum2)); $this->assertSame([EnumBasic::byValue($enum1)], $enumMap->getKeys()); $this->assertSame([$value1], $enumMap->getValues()); $this->assertNull($enumMap->offsetSet($enum2, $value2)); $this->assertTrue($enumMap->contains($enum2)); $this->assertSame($value2, $enumMap[$enum2]); $this->assertSame([EnumBasic::byValue($enum1), EnumBasic::byValue($enum2)], $enumMap->getKeys()); $this->assertSame([$value1, $value2], $enumMap->getValues()); $this->assertNull($enumMap->offsetUnset($enum1)); $this->assertFalse($enumMap->contains($enum1)); $this->assertSame([EnumBasic::byValue($enum2)], $enumMap->getKeys()); $this->assertSame([$value2], $enumMap->getValues()); $this->assertNull($enumMap->offsetUnset($enum2)); $this->assertFalse($enumMap->contains($enum2)); $this->assertSame([], $enumMap->getKeys()); $this->assertSame([], $enumMap->getValues()); } public function testOffsetGetMissingKey() { $enumMap = new EnumMap(EnumBasic::class); $this->expectException(UnexpectedValueException::class); $enumMap->offsetGet(EnumBasic::ONE); } public function testIterate() { $enumMap = new EnumMap(EnumBasic::class); $enum1 = EnumBasic::ONE(); $value1 = 'value1'; $enum2 = EnumBasic::TWO(); $value2 = 'value2'; // an empty enum map needs to be invalid, starting by 0 $enumMap->rewind(); $this->assertSame(0, $enumMap->count()); $this->assertFalse($enumMap->valid()); // attach $enumMap->offsetSet($enum1, $value1); $enumMap->offsetSet($enum2, $value2); // a not empty enum map should be valid, starting by 0 (if not iterated) $enumMap->rewind(); $this->assertSame(2, $enumMap->count()); $this->assertTrue($enumMap->valid()); $this->assertSame($enum1, $enumMap->key()); $this->assertSame($value1, $enumMap->current()); // go to the next element (last) $this->assertNull($enumMap->next()); $this->assertTrue($enumMap->valid()); $this->assertSame($enum2, $enumMap->key()); $this->assertSame($value2, $enumMap->current()); // go to the next element (out of range) $this->assertNull($enumMap->next()); $this->assertNull($enumMap->current()); $this->assertFalse($enumMap->valid()); $this->assertSame(null, $enumMap->key()); // rewind will set the iterator position back to 0 $enumMap->rewind(); $this->assertTrue($enumMap->valid()); $this->assertSame($enum1, $enumMap->key()); $this->assertSame($value1, $enumMap->current()); } public function testArrayAccessWithObjects() { $enumMap = new EnumMap(EnumBasic::class); $enumMap[EnumBasic::ONE()] = 'first'; $enumMap[EnumBasic::TWO()] = 'second'; $this->assertTrue(isset($enumMap[EnumBasic::ONE()])); $this->assertTrue(isset($enumMap[EnumBasic::TWO()])); $this->assertSame('first', $enumMap[EnumBasic::ONE()]); $this->assertSame('second', $enumMap[EnumBasic::TWO()]); unset($enumMap[EnumBasic::ONE()], $enumMap[EnumBasic::TWO()]); $this->assertFalse(isset($enumMap[EnumBasic::ONE()])); $this->assertFalse(isset($enumMap[EnumBasic::TWO()])); } public function testArrayAccessWithValues() { $enumMap = new EnumMap(EnumBasic::class); $enumMap[EnumBasic::ONE] = 'first'; $enumMap[EnumBasic::TWO] = 'second'; $this->assertTrue(isset($enumMap[EnumBasic::ONE])); $this->assertTrue(isset($enumMap[EnumBasic::TWO])); $this->assertSame('first', $enumMap[EnumBasic::ONE]); $this->assertSame('second', $enumMap[EnumBasic::TWO]); unset($enumMap[EnumBasic::ONE], $enumMap[EnumBasic::TWO]); $this->assertFalse(isset($enumMap[EnumBasic::ONE])); $this->assertFalse(isset($enumMap[EnumBasic::TWO])); } public function testConstructThrowsInvalidArgumentExceptionIfEnumClassDoesNotExtendBaseEnum() { $this->expectException(InvalidArgumentException::class); new EnumMap('stdClass'); } public function testInitEnumThrowsInvalidArgumentExceptionOnInvalidEnumGiven() { $enumMap = new EnumMap(EnumBasic::class); $this->expectException(InvalidArgumentException::class); $enumMap->offsetSet(EnumInheritance::INHERITANCE(), 'test'); } public function testContainsAndOffsetExistsReturnsFalseOnInvalidEnum() { $enumMap = new EnumMap(EnumBasic::class); $this->assertFalse($enumMap->contains(EnumInheritance::INHERITANCE())); $this->assertFalse($enumMap->contains(EnumInheritance::INHERITANCE)); $this->assertFalse(isset($enumMap[EnumInheritance::INHERITANCE()])); $this->assertFalse(isset($enumMap[EnumInheritance::INHERITANCE])); } public function testSearch() { $enumMap = new EnumMap(EnumBasic::class); $enumMap[EnumBasic::TWO()] = '2'; $enumMap[EnumBasic::THREE()] = '3'; $this->assertSame(EnumBasic::TWO(), $enumMap->search('2')); $this->assertSame(EnumBasic::TWO(), $enumMap->search(2)); $this->assertSame(EnumBasic::THREE(), $enumMap->search('3')); $this->assertSame(EnumBasic::THREE(), $enumMap->search(3)); $this->assertNull($enumMap->search('4')); $this->assertNull($enumMap->search(4)); $this->assertNull($enumMap->search('unknown')); } public function testSearchStrict() { $enumMap = new EnumMap(EnumBasic::class); $enumMap[EnumBasic::TWO()] = '2'; $enumMap[EnumBasic::THREE()] = '3'; $this->assertSame(EnumBasic::TWO(), $enumMap->search('2', true)); $this->assertNull($enumMap->search(2, true)); $this->assertSame(EnumBasic::THREE(), $enumMap->search('3', true)); $this->assertNull($enumMap->search(3, true)); $this->assertNull($enumMap->search('4', true)); $this->assertNull($enumMap->search(4, true)); $this->assertNull($enumMap->search('unknown', true)); } public function testNullValue() { $enumMap = new EnumMap(EnumBasic::class); $enumMap[EnumBasic::ONE()] = null; $this->assertSame(1, $enumMap->count()); $this->assertNull($enumMap[EnumBasic::ONE]); $this->assertNull($enumMap->offsetGet(EnumBasic::ONE)); $this->assertSame([EnumBasic::ONE()], $enumMap->getKeys()); $enumMap->rewind(); $this->assertSame(1, $enumMap->count()); $this->assertTrue($enumMap->valid()); $this->assertSame(EnumBasic::ONE(), $enumMap->key()); $this->assertNull($enumMap->current()); $this->assertFalse(isset($enumMap[EnumBasic::ONE])); $this->assertFalse(isset($enumMap[EnumBasic::ONE()])); $this->assertFalse($enumMap->offsetExists(EnumBasic::ONE)); $this->assertFalse($enumMap->offsetExists(EnumBasic::ONE())); $this->assertTrue($enumMap->contains(EnumBasic::ONE)); $this->assertTrue($enumMap->contains(EnumBasic::ONE())); // add the same enumeration a second time should do nothing $enumMap->offsetSet(EnumBasic::ONE(), null); $this->assertSame(1, $enumMap->count()); $this->assertSame([EnumBasic::ONE()], $enumMap->getKeys()); // overwrite by non null value $enumMap->offsetSet(EnumBasic::ONE(), false); $this->assertSame(1, $enumMap->count()); $this->assertSame([EnumBasic::ONE()], $enumMap->getKeys()); $this->assertSame(1, $enumMap->count()); $this->assertTrue($enumMap->valid()); $this->assertSame(EnumBasic::ONE(), $enumMap->key()); $this->assertFalse($enumMap->current()); $this->assertTrue(isset($enumMap[EnumBasic::ONE])); $this->assertTrue(isset($enumMap[EnumBasic::ONE()])); $this->assertTrue($enumMap->offsetExists(EnumBasic::ONE)); $this->assertTrue($enumMap->offsetExists(EnumBasic::ONE())); $this->assertTrue($enumMap->contains(EnumBasic::ONE)); $this->assertTrue($enumMap->contains(EnumBasic::ONE())); } public function testSeek() { $enumMap = new EnumMap(EnumBasic::class); $enumMap[EnumBasic::ONE()] = 'one'; $enumMap[EnumBasic::TWO()] = 'two'; $this->assertSame(EnumBasic::ONE(), $enumMap->key()); $enumMap->seek(1); $this->assertSame(EnumBasic::TWO(), $enumMap->key()); $enumMap->seek(0); $this->assertSame(EnumBasic::ONE(), $enumMap->key()); $this->expectException(OutOfBoundsException::class); $enumMap->seek(2); } public function testSerializable() { $enumMap = new EnumMap(EnumBasic::class); $enumMap[EnumBasic::ONE()] = 'one'; $enumMapCopy = unserialize(serialize($enumMap)); $this->assertTrue($enumMapCopy->offsetExists(EnumBasic::ONE)); $this->assertFalse($enumMapCopy->offsetExists(EnumBasic::TWO)); // unserialized instance should be the same $this->assertSame(EnumBasic::ONE(), $enumMapCopy->key()); } } php-enum-3.0.0/tests/MabeEnumTest/EnumSerializableTraitTest.php000066400000000000000000000060111317762216500245560ustar00rootroot00000000000000assertInternalType('string', $serialized); $unserialized = unserialize($serialized); $this->assertInstanceOf(SerializableEnum::class, $unserialized); } public function testUnserializeFirstWillHoldTheSameInstance() { $serialized = serialize(SerializableEnum::get(SerializableEnum::STR)); $this->assertInternalType('string', $serialized); // clear all instantiated instances so we can virtual test unserializing first $this->clearEnumeration(SerializableEnum::class); // First unserialize $unserialized = unserialize($serialized); $this->assertInstanceOf(SerializableEnum::class, $unserialized); // second instantiate $enum = SerializableEnum::get($unserialized->getValue()); // check if it's the same instance $this->assertSame($enum, $unserialized); } public function testUnserializeThrowsRuntimeExceptionOnUnknownValue() { $this->expectException(RuntimeException::class); unserialize('C:' . strlen(SerializableEnum::class) . ':"' . SerializableEnum::class . '":11:{s:4:"test";}'); } public function testUnserializeThrowsRuntimeExceptionOnInvalidValue() { $this->expectException(RuntimeException::class); unserialize('C:' . strlen(SerializableEnum::class) . ':"' . SerializableEnum::class . '":19:{O:8:"stdClass":0:{}}'); } public function testUnserializeThrowsLogicExceptionOnChangingValue() { $this->expectException(LogicException::class); $enum = SerializableEnum::get(SerializableEnum::INT); $enum->unserialize(serialize(SerializableEnum::STR)); } /** * Clears all instantiated enumerations and detected constants of the given enumerator * @param string $enumeration */ private function clearEnumeration($enumeration) { $reflClass = new ReflectionClass($enumeration); while ($reflClass->getName() !== Enum::class) { $reflClass = $reflClass->getParentClass(); } $reflPropInstances = $reflClass->getProperty('instances'); $reflPropInstances->setAccessible(true); $reflPropInstances->setValue(null, array()); $reflPropConstants = $reflClass->getProperty('constants'); $reflPropConstants->setAccessible(true); $reflPropConstants->setValue(null, array()); } } php-enum-3.0.0/tests/MabeEnumTest/EnumSetTest.php000066400000000000000000000705241317762216500217110ustar00rootroot00000000000000assertSame(EnumBasic::class, $enumSet->getEnumeration()); $enum1 = EnumBasic::ONE(); $enum2 = EnumBasic::TWO(); $this->assertFalse($enumSet->contains($enum1)); $this->assertNull($enumSet->attach($enum1)); $this->assertTrue($enumSet->contains($enum1)); $this->assertFalse($enumSet->contains($enum2)); $this->assertNull($enumSet->attach($enum2)); $this->assertTrue($enumSet->contains($enum2)); $this->assertNull($enumSet->detach($enum1)); $this->assertFalse($enumSet->contains($enum1)); $this->assertNull($enumSet->detach($enum2)); $this->assertFalse($enumSet->contains($enum2)); } public function testBasicWithConstantValuesAsEnums() { $enumSet = new EnumSet(EnumBasic::class); $enum1 = EnumBasic::ONE; $enum2 = EnumBasic::TWO; $this->assertFalse($enumSet->contains($enum1)); $this->assertNull($enumSet->attach($enum1)); $this->assertTrue($enumSet->contains($enum1)); $this->assertFalse($enumSet->contains($enum2)); $this->assertNull($enumSet->attach($enum2)); $this->assertTrue($enumSet->contains($enum2)); $this->assertNull($enumSet->detach($enum1)); $this->assertFalse($enumSet->contains($enum1)); $this->assertNull($enumSet->detach($enum2)); $this->assertFalse($enumSet->contains($enum2)); } public function testUnique() { $enumSet = new EnumSet(EnumBasic::class); $enumSet->attach(EnumBasic::ONE()); $enumSet->attach(EnumBasic::ONE); $enumSet->attach(EnumBasic::TWO()); $enumSet->attach(EnumBasic::TWO); $this->assertSame(2, $enumSet->count()); } public function testIterateOrdered() { $enumSet = new EnumSet(EnumBasic::class); // an empty enum set needs to be invalid, starting by 0 $this->assertSame(0, $enumSet->count()); $this->assertFalse($enumSet->valid()); $this->assertNull($enumSet->current()); // attach $enum1 = EnumBasic::ONE(); $enum2 = EnumBasic::TWO(); $enumSet->attach($enum1); $enumSet->attach($enum2); // a not empty enum set should be valid, starting by 0 (if not iterated) $enumSet->rewind(); $this->assertSame(2, $enumSet->count()); $this->assertTrue($enumSet->valid()); $this->assertSame($enum1->getOrdinal(), $enumSet->key()); $this->assertSame($enum1, $enumSet->current()); // go to the next element (last) $this->assertNull($enumSet->next()); $this->assertTrue($enumSet->valid()); $this->assertSame($enum2->getOrdinal(), $enumSet->key()); $this->assertSame($enum2, $enumSet->current()); // go to the next element (out of range) $this->assertNull($enumSet->next()); $this->assertFalse($enumSet->valid()); $this->assertNull($enumSet->current()); // rewind will set the iterator position back to 0 $enumSet->rewind(); $this->assertTrue($enumSet->valid()); $this->assertSame(0, $enumSet->key()); $this->assertSame($enum1, $enumSet->current()); } public function testIterateAndDetach() { $enumSet = new EnumSet(EnumInheritance::class); $enum1 = EnumInheritance::ONE(); $enum2 = EnumInheritance::TWO(); $enum3 = EnumInheritance::INHERITANCE(); // attach $enumSet->attach($enum1); $enumSet->attach($enum2); $enumSet->attach($enum3); // go to the next entry $enumSet->next(); $this->assertSame($enum2, $enumSet->current()); // detach current entry $enumSet->detach($enumSet->current()); $this->assertFalse($enumSet->valid()); $this->assertNull($enumSet->current()); $this->assertSame($enum2->getOrdinal(), $enumSet->key()); // go to the next entry should be the last entry $enumSet->next(); $this->assertSame($enum3, $enumSet->current()); // detech the last entry $enumSet->detach($enumSet->current()); $this->assertFalse($enumSet->valid()); $this->assertNull($enumSet->current()); $this->assertSame($enum3->getOrdinal(), $enumSet->key()); } public function testConstructThrowsInvalidArgumentExceptionIfEnumClassDoesNotExtendBaseEnum() { $this->expectException(InvalidArgumentException::class); new EnumSet(self::class); } public function testInitEnumThrowsInvalidArgumentExceptionOnInvalidEnum() { $enumSet = new EnumSet(EnumBasic::class); $this->expectException(InvalidArgumentException::class); $this->assertFalse($enumSet->contains(EnumInheritance::INHERITANCE())); } public function testIterateOutOfRangeIfLastOrdinalEnumIsSet() { $enumSet = new EnumSet(EnumBasic::class); $enum = EnumBasic::byOrdinal(count(EnumBasic::getConstants()) - 1); $enumSet->attach($enum); $enumSet->rewind(); $this->assertSame($enum, $enumSet->current()); // go to the next entry results in out of range $enumSet->next(); $this->assertFalse($enumSet->valid()); $this->assertSame($enum->getOrdinal() + 1, $enumSet->key()); // go more over doesn't change iterator position $enumSet->next(); $this->assertFalse($enumSet->valid()); $this->assertSame($enum->getOrdinal() + 1, $enumSet->key()); } public function testRewindIntFirstOnEmptySet() { $enumSet = new EnumSet(EnumBasic::class); $enumSet->attach(EnumBasic::TWO); $enumSet->rewind(); $this->assertGreaterThan(0, $enumSet->key()); $enumSet->detach(EnumBasic::TWO); $enumSet->rewind(); $this->assertSame(0, $enumSet->key()); } public function testRewindBinFirstOnEmptySet() { $enumSet = new EnumSet(Enum66::class); $enumSet->attach(Enum66::TWO); $enumSet->rewind(); $this->assertGreaterThan(0, $enumSet->key()); $enumSet->detach(Enum66::TWO); $enumSet->rewind(); $this->assertSame(0, $enumSet->key()); } public function test32EnumerationsSet() { $enumSet = new EnumSet(Enum32::class); foreach (Enum32::getConstants() as $name => $value) { $this->assertFalse($enumSet->contains($value)); $enumSet->attach($value); $this->assertTrue($enumSet->contains($value)); } $this->assertSame(32, $enumSet->count()); $expectedOrdinal = 0; foreach ($enumSet as $ordinal => $enum) { $this->assertSame($expectedOrdinal, $ordinal); $this->assertSame($expectedOrdinal, $enum->getOrdinal()); $expectedOrdinal++; } } public function test64EnumerationsSet() { $enumSet = new EnumSet(Enum64::class); foreach (Enum64::getConstants() as $name => $value) { $this->assertFalse($enumSet->contains($value)); $enumSet->attach($value); $this->assertTrue($enumSet->contains($value)); } $this->assertSame(64, $enumSet->count()); $expectedOrdinal = 0; foreach ($enumSet as $ordinal => $enum) { $this->assertSame($expectedOrdinal, $ordinal); $this->assertSame($expectedOrdinal, $enum->getOrdinal()); $expectedOrdinal++; } } public function test65EnumerationsSet() { $enum = new EnumSet(Enum65::class); $this->assertNull($enum->attach(Enum65::byOrdinal(64))); $enum->next(); $this->assertTrue($enum->valid()); } public function testGetBit() { $enumSet = new EnumSet(EnumBasic::class); $enumSet->attach(EnumBasic::TWO); $this->assertFalse($enumSet->getBit(EnumBasic::ONE()->getOrdinal())); $this->assertTrue($enumSet->getBit(EnumBasic::TWO()->getOrdinal())); } public function testGetBitOutOfRangeOrdinal() { $enumSet = new EnumSet(EnumBasic::class); $this->expectException(InvalidArgumentException::class); $enumSet->getBit(100); } public function testSetBit() { $enumSet = new EnumSet(EnumBasic::class); $enumSet->setBit(EnumBasic::TWO()->getOrdinal(), true); $this->assertTrue($enumSet->getBit(EnumBasic::TWO()->getOrdinal())); $enumSet->setBit(EnumBasic::TWO()->getOrdinal(), false); $this->assertFalse($enumSet->getBit(EnumBasic::TWO()->getOrdinal())); } public function testSetBitOutOfRangeOrdinal() { $enumSet = new EnumSet(EnumBasic::class); $this->expectException(InvalidArgumentException::class); $enumSet->setBit(100, true); } public function testGetBinaryBitsetLe() { $enumSet = new EnumSet(Enum65::class); $enum1 = Enum65::ONE; $enum2 = Enum65::TWO; $enum3 = Enum65::SIXTYFIVE; $enum4 = Enum65::SIXTYFOUR; $this->assertNull($enumSet->attach($enum1)); $this->assertSame("\x01\x00\x00\x00\x00\x00\x00\x00\x00", $enumSet->getBinaryBitsetLe()); $this->assertTrue($enumSet->contains($enum1)); $this->assertNull($enumSet->attach($enum2)); $this->assertSame("\x03\x00\x00\x00\x00\x00\x00\x00\x00", $enumSet->getBinaryBitsetLe()); $this->assertTrue($enumSet->contains($enum2)); $this->assertNull($enumSet->attach($enum3)); $this->assertSame("\x03\x00\x00\x00\x00\x00\x00\x00\x01", $enumSet->getBinaryBitsetLe()); $this->assertTrue($enumSet->contains($enum3)); $this->assertNull($enumSet->attach($enum4)); $this->assertSame("\x03\x00\x00\x00\x00\x00\x00\x80\x01", $enumSet->getBinaryBitsetLe()); $this->assertTrue($enumSet->contains($enum4)); $this->assertSame(4, $enumSet->count()); $this->assertNull($enumSet->detach($enum2)); $this->assertSame("\x01\x00\x00\x00\x00\x00\x00\x80\x01", $enumSet->getBinaryBitsetLe()); $this->assertFalse($enumSet->contains($enum2)); $this->assertSame(3, $enumSet->count()); } public function testGetBinaryBitsetBe() { $enumSet = new EnumSet(Enum65::class); $enum1 = Enum65::ONE; $enum2 = Enum65::TWO; $enum3 = Enum65::SIXTYFIVE; $enum4 = Enum65::SIXTYFOUR; $this->assertNull($enumSet->attach($enum1)); $this->assertSame("\x00\x00\x00\x00\x00\x00\x00\x00\x01", $enumSet->getBinaryBitsetBe()); $this->assertTrue($enumSet->contains($enum1)); $this->assertNull($enumSet->attach($enum2)); $this->assertSame("\x00\x00\x00\x00\x00\x00\x00\x00\x03", $enumSet->getBinaryBitsetBe()); $this->assertTrue($enumSet->contains($enum2)); $this->assertNull($enumSet->attach($enum3)); $this->assertSame("\x01\x00\x00\x00\x00\x00\x00\x00\x03", $enumSet->getBinaryBitsetBe()); $this->assertTrue($enumSet->contains($enum3)); $this->assertNull($enumSet->attach($enum4)); $this->assertSame("\x01\x80\x00\x00\x00\x00\x00\x00\x03", $enumSet->getBinaryBitsetBe()); $this->assertTrue($enumSet->contains($enum4)); $this->assertSame(4, $enumSet->count()); $this->assertNull($enumSet->detach($enum2)); $this->assertSame("\x01\x80\x00\x00\x00\x00\x00\x00\x01", $enumSet->getBinaryBitsetBe()); $this->assertFalse($enumSet->contains($enum2)); $this->assertSame(3, $enumSet->count()); } public function testSetBinaryBitsetLeBin() { $enumSet = new EnumSet(Enum65::class); $enumSet->setBinaryBitsetLe("\x01\x00\x00\x00\x00\x00\x00\x80\x01"); $this->assertContains(Enum65::ONE(), $enumSet); $this->assertNotContains(Enum65::TWO(), $enumSet); $this->assertContains(Enum65::SIXTYFIVE(), $enumSet); $this->assertContains(Enum65::SIXTYFOUR(), $enumSet); $this->assertSame(3, $enumSet->count()); } public function testSetBinaryBitsetLeBinShort() { $enumSet = new EnumSet(Enum65::class); $enumSet->setBinaryBitsetLe("\x0A"); $this->assertSame("\x0A\x00\x00\x00\x00\x00\x00\x00\x00", $enumSet->getBinaryBitsetLe()); } public function testSetBinaryBitsetLeBinLong() { $enumSet = new EnumSet(Enum65::class); $bitset = "\x0A\xFF\x00\x00\x00\x00\x00\x00\x00"; $enumSet->setBinaryBitsetLe($bitset . "\x00\x00\x00\x00\x00\x00\x00"); $this->assertSame($bitset, $enumSet->getBinaryBitsetLe()); } public function testSetBinaryBitsetLeBinOutOfRangeBitsOfExtendedBytes1() { $enumSet = new EnumSet(Enum65::class); $this->expectException(InvalidArgumentException::class, 'Out-Of-Range'); $enumSet->setBinaryBitsetLe("\xff\xff\xff\xff\xff\xff\xff\xff\x00\x02"); } public function testSetBinaryBitsetLeBinOutOfRangeBitsOfExtendedBytes2() { $enumSet = new EnumSet(Enum65::class); $this->expectException(InvalidArgumentException::class, 'Out-Of-Range'); $enumSet->setBinaryBitsetLe("\xff\xff\xff\xff\xff\xff\xff\xff\x02"); } public function testSetBinaryBitsetLeBinOutOfRangeBitsOfLastValidByte() { // using Enum65 to detect Out-Of-Range bits of last valid byte // Enum65 has max. ordinal number of 2 of the last byte. -> 0001 $enumSet = new EnumSet(Enum65::class); $bitset = $enumSet->getBinaryBitsetLe(); $newBitset = substr($bitset, 0, -1) . "\x02"; $this->expectException(InvalidArgumentException::class, 'Out-Of-Range'); $enumSet->setBinaryBitsetLe($newBitset); } public function testSetBinaryBitsetLeBinArgumentExceptionIfNotString() { $this->expectException(InvalidArgumentException::class); $enum = new EnumSet(Enum65::class); $enum->setBinaryBitsetLe(0); } public function testSetBinaryBitsetLeInt() { $enumSet = new EnumSet(Enum32::class); $enumSet->setBinaryBitsetLe("\x01\x00\x80\x01"); $this->assertContains(Enum32::ONE(), $enumSet); $this->assertNotContains(Enum32::TWO(), $enumSet); $this->assertContains(Enum32::TWENTYFOUR(), $enumSet); $this->assertContains(Enum32::TWENTYFIVE(), $enumSet); $this->assertSame(3, $enumSet->count()); } public function testSetBinaryBitsetLeIntShort() { $enumSet = new EnumSet(Enum32::class); $enumSet->setBinaryBitsetLe("\x0A"); $this->assertSame("\x0A\x00\x00\x00", $enumSet->getBinaryBitsetLe()); } public function testSetBinaryBitsetLeIntOutOfRangeBitsOfExtendedBytes1() { $enumSet = new EnumSet(EnumBasic::class); $this->expectException(InvalidArgumentException::class, 'Out-Of-Range'); $enumSet->setBinaryBitsetLe("\x0A\xFF\x01"); } public function testSetBinaryBitsetLeIntOutOfRangeBitsOfExtendedBytes2() { $enumSet = new EnumSet(EnumBasic::class); $this->expectException(InvalidArgumentException::class, 'Out-Of-Range'); $enumSet->setBinaryBitsetLe("\x01\x01\x01\x01\x01\x01\x01\x01\x01"); } public function testSetBinaryBitsetLeIntOutOfRangeBitsOfLastValidByte() { // using Enum65 to detect Out-Of-Range bits of last valid byte // Enum65 has max. ordinal number of 2 of the last byte. -> 0001 $enumSet = new EnumSet(Enum31::class); $bitset = $enumSet->getBinaryBitsetLe(); $newBitset = substr($bitset, 0, -1) . "\xFF"; $this->expectException(InvalidArgumentException::class, 'Out-Of-Range'); $enumSet->setBinaryBitsetLe($newBitset); } public function testSetBinaryBitsetBe() { $enumSet = new EnumSet(Enum65::class); $enumSet->setBinaryBitsetBe("\x01\x80\x00\x00\x00\x00\x00\x00\x01"); $this->assertTrue($enumSet->contains(Enum65::ONE)); $this->assertFalse($enumSet->contains(Enum65::TWO)); $this->assertTrue($enumSet->contains(Enum65::SIXTYFIVE)); $this->assertTrue($enumSet->contains(Enum65::SIXTYFOUR)); $this->assertTrue($enumSet->count() == 3); } public function testSetBinaryBitsetBeArgumentExceptionIfNotString() { $this->expectException(InvalidArgumentException::class); $enum = new EnumSet(Enum65::class); $enum->setBinaryBitsetBe(0); } public function testCountingEmptyEnumEmptySet() { $set = new EnumSet(EmptyEnum::class); $this->assertSame(0, $set->count()); } public function testCountSingleBit32() { foreach (Enum32::getEnumerators() as $enum) { $enumSet = new EnumSet(Enum32::class); $enumSet->attach($enum); $this->assertSame(1, $enumSet->count()); } } public function testCountSingleBit64() { foreach (Enum64::getEnumerators() as $enum) { $enumSet = new EnumSet(Enum64::class); $enumSet->attach($enum); $this->assertSame(1, $enumSet->count()); } } public function testCountSingleBit66() { foreach (Enum66::getEnumerators() as $enum) { $enumSet = new EnumSet(Enum66::class); $enumSet->attach($enum); $this->assertSame(1, $enumSet->count()); } } public function testIsEqual() { $set1 = new EnumSet(EnumBasic::class); $set2 = new EnumSet(EnumBasic::class); $this->assertTrue($set1->isEqual($set2)); foreach (EnumBasic::getEnumerators() as $enumerator) { $set1->attach($enumerator); $this->assertFalse($set1->isEqual($set2)); $set2->attach($enumerator); $this->assertTrue($set1->isEqual($set2)); } } public function testIsEqualWrongInstance() { $set1 = new EnumSet(EnumBasic::class); $set2 = new EnumSet(EnumInheritance::class); $this->assertFalse($set1->isEqual($set2)); foreach (EnumBasic::getEnumerators() as $enumerator) { $set1->attach($enumerator); $this->assertFalse($set1->isEqual($set2)); $set2->attach($enumerator->getValue()); $this->assertFalse($set1->isEqual($set2)); } } /** * if $A->isEqual($B) is true then $A->isSubsetOf($B) is also true */ public function testIsSubsetIsEqual() { $set1 = new EnumSet(Enum32::class); $set2 = new EnumSet(Enum32::class); $this->assertTrue($set1->isSubset($set2)); foreach (Enum32::getEnumerators() as $enumerator) { $set1->attach($enumerator); $set2->attach($enumerator); $this->assertTrue($set1->isSubset($set2)); } } public function testIsSubsetFull() { $set1 = new EnumSet(Enum32::class); $set2 = new EnumSet(Enum32::class); foreach (Enum32::getEnumerators() as $enumerator) { $set2->attach($enumerator); $this->assertTrue($set1->isSubset($set2)); } } public function testIsSubsetFalse() { $set1 = new EnumSet(Enum32::class); $set2 = new EnumSet(Enum32::class); foreach (Enum32::getEnumerators() as $enumerator) { $set1->attach($enumerator); $this->assertFalse($set1->isSubset($set2)); } } public function testIsSubsetWrongInstance() { $set1 = new EnumSet(EnumBasic::class); $set2 = new EnumSet(EnumInheritance::class); $this->assertFalse($set1->isSubset($set2)); foreach (EnumBasic::getEnumerators() as $enumerator) { $set1->attach($enumerator); $this->assertFalse($set1->isSubset($set2)); $set2->attach($enumerator->getValue()); $this->assertFalse($set1->isSubset($set2)); } } /** * if $A->isEqual($B) is true then $A->isSuperset($B) is also true */ public function testIsSupersetIsEqual() { $set1 = new EnumSet(Enum32::class); $set2 = new EnumSet(Enum32::class); $this->assertTrue($set1->isEqual($set2)); $this->assertTrue($set1->isSuperset($set2)); foreach (Enum32::getEnumerators() as $enumerator) { $set1->attach($enumerator); $set2->attach($enumerator); $this->assertTrue($set1->isEqual($set2)); $this->assertTrue($set1->isSuperset($set2)); } } public function testIsSupersetFull() { $set1 = new EnumSet(Enum32::class); $set2 = new EnumSet(Enum32::class); foreach (Enum32::getEnumerators() as $enumerator) { $set1->attach($enumerator); $this->assertTrue($set1->isSuperset($set2)); } } public function testIsSupersetFalse() { $set1 = new EnumSet(Enum32::class); $set2 = new EnumSet(Enum32::class); foreach (Enum32::getEnumerators() as $enumerator) { $set2->attach($enumerator); $this->assertFalse($set1->isSuperset($set2)); } } public function testIsSupersetWrongInstance() { $set1 = new EnumSet(EnumBasic::class); $set2 = new EnumSet(EnumInheritance::class); $this->assertFalse($set1->isSuperset($set2)); foreach (EnumBasic::getEnumerators() as $enumerator) { $set1->attach($enumerator); $this->assertFalse($set1->isSuperset($set2)); $set2->attach($enumerator->getValue()); $this->assertFalse($set1->isSuperset($set2)); } } public function testGetOrdinalsInt() { $set = new EnumSet(EnumBasic::class); $this->assertSame([], $set->getOrdinals()); foreach (EnumBasic::getConstants() as $value) { $set->attach($value); } $this->assertSame(range(0, count(EnumBasic::getConstants()) - 1), $set->getOrdinals()); } public function testGetOrdinalsBin() { $set = new EnumSet(Enum66::class); $this->assertSame([], $set->getOrdinals()); foreach (Enum66::getConstants() as $value) { $set->attach($value); } $this->assertSame(range(0, count(Enum66::getConstants()) - 1), $set->getOrdinals()); } public function testGetOrdinalsIntDoesNotEffectIteratorPosition() { $set = new EnumSet(EnumBasic::class); $set->attach(EnumBasic::ONE); $set->attach(EnumBasic::TWO); $set->next(); $set->getOrdinals(); $this->assertSame(EnumBasic::TWO, $set->current()->getValue()); } public function testGetOrdinalsBinDoesNotEffectIteratorPosition() { $set = new EnumSet(Enum66::class); $set->attach(Enum66::ONE); $set->attach(Enum66::TWO); $set->next(); $set->getOrdinals(); $this->assertSame(Enum66::TWO, $set->current()->getValue()); } public function testGetEnumerators() { $set = new EnumSet(EnumBasic::class); $this->assertSame([], $set->getEnumerators()); foreach (EnumBasic::getConstants() as $value) { $set->attach($value); } $this->assertSame(EnumBasic::getEnumerators(), $set->getEnumerators()); } public function testGetEnumeratorsDoesNotEffectIteratorPosition() { $set = new EnumSet(EnumBasic::class); $set->attach(EnumBasic::ONE); $set->attach(EnumBasic::TWO); $set->next(); $set->getEnumerators(); $this->assertSame(EnumBasic::TWO, $set->current()->getValue()); } public function testGetValues() { $set = new EnumSet(EnumBasic::class); $this->assertSame([], $set->getValues()); foreach (EnumBasic::getConstants() as $value) { $set->attach($value); } $this->assertSame(array_values(EnumBasic::getConstants()), $set->getValues()); } public function testGetValuesDoesNotEffectIteratorPosition() { $set = new EnumSet(EnumBasic::class); $set->attach(EnumBasic::ONE); $set->attach(EnumBasic::TWO); $set->next(); $set->getValues(); $this->assertSame(EnumBasic::TWO, $set->current()->getValue()); } public function testGetNames() { $set = new EnumSet(EnumBasic::class); $this->assertSame([], $set->getNames()); foreach (EnumBasic::getConstants() as $value) { $set->attach($value); } $this->assertSame(array_keys(EnumBasic::getConstants()), $set->getNames()); } public function testGetNamesDoesNotEffectIteratorPosition() { $set = new EnumSet(EnumBasic::class); $set->attach(EnumBasic::ONE); $set->attach(EnumBasic::TWO); $set->next(); $set->getNames(); $this->assertSame(EnumBasic::TWO, $set->current()->getValue()); } public function testUnion() { $set1 = new EnumSet(EnumBasic::class); $set1->attach(EnumBasic::ONE); $set1->attach(EnumBasic::TWO); $set2 = new EnumSet(EnumBasic::class); $set2->attach(EnumBasic::TWO); $set2->attach(EnumBasic::THREE); $set2->attach(EnumBasic::FOUR); $rs = $set1->union($set2); $this->assertSame([ EnumBasic::ONE, EnumBasic::TWO, EnumBasic::THREE, EnumBasic::FOUR, ], $rs->getValues()); } public function testUnionThrowsInvalidArgumentException() { $set1 = new EnumSet(EnumBasic::class); $set2 = new EnumSet(Enum32::class); $this->expectException(InvalidArgumentException::class); $set1->union($set2); } public function testIntersect() { $set1 = new EnumSet(EnumBasic::class); $set1->attach(EnumBasic::ONE); $set1->attach(EnumBasic::TWO); $set1->attach(EnumBasic::THREE); $set2 = new EnumSet(EnumBasic::class); $set2->attach(EnumBasic::TWO); $set2->attach(EnumBasic::THREE); $set2->attach(EnumBasic::FOUR); $rs = $set1->intersect($set2); $this->assertSame([EnumBasic::TWO, EnumBasic::THREE], $rs->getValues()); } public function testIntersectThrowsInvalidArgumentException() { $set1 = new EnumSet(EnumBasic::class); $set2 = new EnumSet(Enum32::class); $this->expectException(InvalidArgumentException::class); $set1->intersect($set2); } public function testDiff() { $set1 = new EnumSet(EnumBasic::class); $set1->attach(EnumBasic::ONE); $set1->attach(EnumBasic::TWO); $set1->attach(EnumBasic::THREE); $set2 = new EnumSet(EnumBasic::class); $set2->attach(EnumBasic::TWO); $set2->attach(EnumBasic::THREE); $set2->attach(EnumBasic::FOUR); $rs = $set1->diff($set2); $this->assertSame([EnumBasic::ONE], $rs->getValues()); } public function testDiffThrowsInvalidArgumentException() { $set1 = new EnumSet(EnumBasic::class); $set2 = new EnumSet(Enum32::class); $this->expectException(InvalidArgumentException::class); $set1->diff($set2); } public function testSymDiff() { $set1 = new EnumSet(EnumBasic::class); $set1->attach(EnumBasic::ONE); $set1->attach(EnumBasic::TWO); $set1->attach(EnumBasic::THREE); $set2 = new EnumSet(EnumBasic::class); $set2->attach(EnumBasic::TWO); $set2->attach(EnumBasic::THREE); $set2->attach(EnumBasic::FOUR); $rs = $set1->symDiff($set2); $this->assertSame([ EnumBasic::ONE, EnumBasic::FOUR, ], $rs->getValues()); } public function testSymDiffThrowsInvalidArgumentException() { $set1 = new EnumSet(EnumBasic::class); $set2 = new EnumSet(Enum32::class); $this->expectException(InvalidArgumentException::class); $set1->symDiff($set2); } } php-enum-3.0.0/tests/MabeEnumTest/EnumTest.php000066400000000000000000000336451317762216500212400ustar00rootroot00000000000000resetStaticEnumProps(); } public function tearDown() { assert_options(ASSERT_ACTIVE, 1); } /** * Un-initialize all known enumerations */ private function resetStaticEnumProps() { $enumRefl = new ReflectionClass(Enum::class); $enumPropsRefl = $enumRefl->getProperties(ReflectionProperty::IS_STATIC); foreach ($enumPropsRefl as $enumPropRefl) { $enumPropRefl->setAccessible(true); $enumPropRefl->setValue([]); } } /** * Test that Enumeration getters works fine after Enum::byName() * as Enum::byName() does not initialize the enumeration directly */ public function testByNameEnumGettersWorks() { $this->resetStaticEnumProps(); $this->assertSame(EnumBasic::ONE, EnumBasic::byName('ONE')->getValue()); $this->resetStaticEnumProps(); $this->assertSame('ONE', EnumBasic::byName('ONE')->getName()); $this->resetStaticEnumProps(); $this->assertSame(0, EnumBasic::byName('ONE')->getOrdinal()); } public function testGetNameReturnsConstantNameOfCurrentValue() { $enum = EnumBasic::get(EnumBasic::ONE); $this->assertSame('ONE', $enum->getName()); } public function testToStringMagicMethodReturnsName() { $enum = EnumBasic::get(EnumBasic::ONE); $this->assertSame('ONE', $enum->__toString()); } public function testEnumInheritance() { $this->assertSame(array( 'ONE' => 1, 'TWO' => 2, 'THREE' => 3, 'FOUR' => 4, 'FIVE' => 5, 'SIX' => 6, 'SEVEN' => 7, 'EIGHT' => 8, 'NINE' => 9, 'ZERO' => 0, 'FLOAT' => 0.123, 'STR' => 'str', 'STR_EMPTY' => '', 'NIL' => null, 'BOOLEAN_TRUE' => true, 'BOOLEAN_FALSE' => false, 'INHERITANCE' => 'Inheritance', ), EnumInheritance::getConstants()); $enum = EnumInheritance::get(EnumInheritance::ONE); $this->assertSame(EnumInheritance::ONE, $enum->getValue()); $this->assertSame(0, $enum->getOrdinal()); $enum = EnumInheritance::get(EnumInheritance::INHERITANCE); $this->assertSame(EnumInheritance::INHERITANCE, $enum->getValue()); $this->assertSame(16, $enum->getOrdinal()); } public function testGetWithStrictValue() { $enum = EnumBasic::get(EnumBasic::ONE); $this->assertSame(1, $enum->getValue()); $this->assertSame(0, $enum->getOrdinal()); } public function testGetWithNonStrictValueThrowsInvalidArgumentException() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage("Unknown value '2' for enumeration MabeEnumTest\\TestAsset\\EnumBasic"); EnumBasic::get((string)EnumBasic::TWO); } public function testGetWithInvalidValueThrowsInvalidArgumentException() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage("Unknown value 'unknown' for enumeration MabeEnumTest\\TestAsset\\EnumBasic"); EnumBasic::get('unknown'); } public function testGetWithInvalidArrayValueThrowsInvalidArgumentException() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage("Unknown value of type array for enumeration MabeEnumTest\\TestAsset\\EnumBasic"); EnumBasic::get(array()); } public function testGetWithInvalidTypeOfValueThrowsInvalidArgumentException() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage( "Unknown value of type " . get_class($this) . " for enumeration MabeEnumTest\\TestAsset\\EnumBasic" ); EnumBasic::get($this); } public function testGetByInstance() { $enum1 = EnumBasic::get(EnumBasic::ONE); $enum2 = EnumBasic::get($enum1); $this->assertSame($enum1, $enum2); } public function testGetByExtendedInstanceOfKnownValue() { $enum = EnumInheritance::get(EnumInheritance::ONE); $this->expectException(InvalidArgumentException::class); EnumBasic::get($enum); } public function testGetEnumeratorsConstansAlreadyDetected() { $constants = EnumInheritance::getConstants(); $enumerators = EnumInheritance::getEnumerators(); $count = count($enumerators); $this->assertSame(count($constants), $count); for ($i = 0; $i < $count; ++$i) { $this->assertArrayHasKey($i, $enumerators); $this->assertInstanceOf(EnumInheritance::class, $enumerators[$i]); $enumerator = $enumerators[$i]; $this->assertArrayHasKey($enumerator->getName(), $constants); $this->assertSame($constants[$enumerator->getName()], $enumerator->getValue()); } } public function testGetEnumeratorsConstansNotDetected() { $enumerators = EnumInheritance::getEnumerators(); $constants = EnumInheritance::getConstants(); $count = count($enumerators); $this->assertSame(count($constants), $count); for ($i = 0; $i < $count; ++$i) { $this->assertArrayHasKey($i, $enumerators); $this->assertInstanceOf(EnumInheritance::class, $enumerators[$i]); $enumerator = $enumerators[$i]; $this->assertArrayHasKey($enumerator->getName(), $constants); $this->assertSame($constants[$enumerator->getName()], $enumerator->getValue()); } } public function testGetValues() { $expectedValues = array_values(EnumInheritance::getConstants()); $values = EnumInheritance::getValues(); $count = count($values); $this->assertSame(count($expectedValues), $count); for ($i = 0; $i < $count; ++$i) { $this->assertArrayHasKey($i, $values); $this->assertSame($expectedValues[$i], $values[$i]); } } public function testGetNamesConstantsAlreadyDetected() { $expectedNames = array_keys(EnumInheritance::getConstants()); $names = EnumInheritance::getNames(); $count = count($names); $this->assertSame(count($expectedNames), $count); for ($i = 0; $i < $count; ++$i) { $this->assertArrayHasKey($i, $names); $this->assertSame($expectedNames[$i], $names[$i]); } } public function testGetNamesConstantsNotDetected() { $names = EnumInheritance::getNames(); $expectedNames = array_keys(EnumInheritance::getConstants()); $count = count($names); $this->assertSame(count($expectedNames), $count); for ($i = 0; $i < $count; ++$i) { $this->assertArrayHasKey($i, $names); $this->assertSame($expectedNames[$i], $names[$i]); } } public function testGetOrdinals() { $constants = EnumInheritance::getConstants(); $ordinals = EnumInheritance::getOrdinals(); $count = count($ordinals); $this->assertSame(count($constants), $count); for ($i = 0; $i < $count; ++$i) { $this->assertArrayHasKey($i, $ordinals); $this->assertSame($i, $ordinals[$i]); } } public function testGetAllValues() { $constants = EnumBasic::getConstants(); foreach ($constants as $name => $value) { $enum = EnumBasic::get($value); $this->assertSame($value, $enum->getValue()); $this->assertSame($name, $enum->getName()); } } public function testIsBasic() { $enum = EnumBasic::ONE(); // by value $this->assertTrue($enum->is(EnumBasic::ONE)); // same $this->assertFalse($enum->is('1')); // wrong value by strict comparison // by instance $this->assertTrue($enum->is(EnumBasic::ONE())); // same $this->assertFalse($enum->is(EnumBasic::TWO())); // different enumerators $this->assertFalse($enum->is(EnumInheritance::ONE())); // different enumeration type } public function testCallingGetOrdinalTwoTimesWillResultTheSameValue() { $enum = EnumBasic::get(EnumBasic::TWO); $this->assertSame(1, $enum->getOrdinal()); $this->assertSame(1, $enum->getOrdinal()); } public function testInstantiateUsingOrdinalNumber() { $enum = EnumInheritance::byOrdinal(16); $this->assertSame(16, $enum->getOrdinal()); $this->assertSame('INHERITANCE', $enum->getName()); } public function testInstantiateUsingInvalidOrdinalNumberThrowsInvalidArgumentException() { $this->expectException(InvalidArgumentException::class); EnumInheritance::byOrdinal(17); } public function testInstantiateByName() { $enum = EnumInheritance::byName('ONE'); $this->assertInstanceOf(EnumInheritance::class, $enum); $this->assertSame(EnumInheritance::ONE, $enum->getValue()); } public function testInstantiateByUnknownNameThrowsInvalidArgumentException() { $this->expectException(InvalidArgumentException::class); EnumInheritance::byName('UNKNOWN'); } public function testInstantiateUsingMagicMethod() { $enum = EnumInheritance::ONE(); $this->assertInstanceOf(EnumInheritance::class, $enum); $this->assertSame(EnumInheritance::ONE, $enum->getValue()); } public function testEnabledAssertAmbiguousEnumeratorValues() { $this->expectException(AssertionError::class); EnumAmbiguous::get('unknown'); } public function testDisabledAssertAmbiguousEnumeratorValues() { assert_options(ASSERT_ACTIVE, 0); $this->expectException(InvalidArgumentException::class); EnumAmbiguous::get('unknown'); } public function testExtendedEnabledAssertAmbiguousEnumeratorValues() { $this->expectException(AssertionError::class); EnumExtendedAmbiguous::get('unknown'); } public function testExtendedDisabledAssertAmbiguousEnumeratorValues() { assert_options(ASSERT_ACTIVE, 0); $this->expectException(InvalidArgumentException::class); EnumExtendedAmbiguous::get('unknown'); } public function testSingleton() { $enum1 = EnumBasic::get(EnumBasic::ONE); $enum2 = EnumBasic::ONE(); $this->assertSame($enum1, $enum2); } public function testCloneNotCallableAndThrowsLogicException() { $enum = EnumBasic::ONE(); $reflectionClass = new ReflectionClass($enum); $reflectionMethod = $reflectionClass->getMethod('__clone'); $this->assertTrue($reflectionMethod->isPrivate(), 'The method __clone must be private'); $this->assertTrue($reflectionMethod->isFinal(), 'The method __clone must be final'); $reflectionMethod->setAccessible(true); $this->expectException(LogicException::class); $reflectionMethod->invoke($enum); } public function testNotSerializable() { $enum = EnumBasic::ONE(); $this->expectException(LogicException::class); serialize($enum); } public function testNotUnserializable() { $this->expectException(LogicException::class); unserialize('O:' . strlen(EnumBasic::class) . ':"' . EnumBasic::class . '":0:{}'); } public function testHas() { $enum = EnumBasic::ONE(); $this->assertFalse($enum->has('invalid')); $this->assertFalse($enum->has(EnumInheritance::ONE())); $this->assertTrue($enum->has(EnumBasic::ONE())); $this->assertTrue($enum->has(EnumBasic::ONE)); } public function testConstVisibility() { if (PHP_VERSION_ID < 70100) { $this->markTestSkipped('This test is for PHP-7.1 and upper only'); } $constants = ConstVisibilityEnum::getConstants(); $this->assertSame(array( 'IPUB' => ConstVisibilityEnum::IPUB, 'PUB' => ConstVisibilityEnum::PUB, ), $constants); } public function testConstVisibilityExtended() { if (PHP_VERSION_ID < 70100) { $this->markTestSkipped('This test is for PHP-7.1 and upper only'); } $constants = ConstVisibilityEnumExtended::getConstants(); $this->assertSame(array( 'IPUB' => ConstVisibilityEnumExtended::IPUB, 'PUB' => ConstVisibilityEnumExtended::PUB, 'IPUB2' => ConstVisibilityEnumExtended::IPUB2, 'PUB2' => ConstVisibilityEnumExtended::PUB2, ), $constants); } public function testIsSerializableIssue() { $enum1 = SerializableEnum::INT(); $enum2 = unserialize(serialize($enum1)); $this->assertFalse($enum1 === $enum2, 'Wrong test implementation'); $this->assertTrue($enum1->is($enum2), 'Two different instances of exact the same enumerator should be equal'); } } php-enum-3.0.0/tests/MabeEnumTest/TestAsset/000077500000000000000000000000001317762216500206675ustar00rootroot00000000000000php-enum-3.0.0/tests/MabeEnumTest/TestAsset/ConstVisibilityEnum.php000066400000000000000000000010351317762216500253620ustar00rootroot00000000000000 64 bit bitset) * * @link http://github.com/marc-mabe/php-enum for the canonical source repository * @copyright Copyright (c) 2017 Marc Bennewitz * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License */ class Enum65 extends Enum64 { const SIXTYFIVE = 65; } php-enum-3.0.0/tests/MabeEnumTest/TestAsset/Enum66.php000066400000000000000000000006311317762216500224600ustar00rootroot00000000000000 64 bit bitset) * * @link http://github.com/marc-mabe/php-enum for the canonical source repository * @copyright Copyright (c) 2017 Marc Bennewitz * @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License */ class Enum66 extends Enum65 { const SIXTYSIX = 66; } php-enum-3.0.0/tests/MabeEnumTest/TestAsset/EnumAmbiguous.php000066400000000000000000000013541317762216500241630ustar00rootroot00000000000000= 70000 && $zendassertions == -1) { echo 'Please enable zend.assertions in php.ini (zend.assertions = 1)' . PHP_EOL . "Current ini setting: zend.assertions = {$zendassertions}]" . PHP_EOL; exit(1); } // activate assertions assert_options(ASSERT_ACTIVE, 1); assert_options(ASSERT_WARNING, 0); assert_options(ASSERT_BAIL, 0); assert_options(ASSERT_QUIET_EVAL, 0); if (!class_exists('AssertionError')) { // AssertionError has been added in PHP-7.0 class AssertionError extends Exception {}; } assert_options(ASSERT_CALLBACK, function($file, $line, $code) { throw new AssertionError("assert(): Assertion '{$code}' failed in {$file} on line {$line}"); }); // installed itself if (file_exists(__DIR__ . '/../vendor/autoload.php')) { require_once __DIR__ . '/../vendor/autoload.php'; // installed as dependency } elseif (file_exists(__DIR__ . '/../../../autoload.php')) { require_once __DIR__ . '/../../../autoload.php'; // not installed } else { echo "php-enum not installed - please run 'composer install'" . PHP_EOL; exit(1); } // autload test files spl_autoload_register(function ($class) { $file = __DIR__ . '/' . str_replace('\\', '/', $class) . '.php'; if (file_exists($file)) { require $file; } });