Обновление клиента (apps, 3rdparty, install)
This commit is contained in:
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2020, 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.
|
||||
+484
@@ -0,0 +1,484 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MabeEnum;
|
||||
|
||||
use ReflectionClass;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* Abstract base enumeration class.
|
||||
*
|
||||
* @copyright 2020, Marc Bennewitz
|
||||
* @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
|
||||
* @link http://github.com/marc-mabe/php-enum for the canonical source repository
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
abstract class Enum implements \Stringable
|
||||
{
|
||||
/**
|
||||
* The selected enumerator value
|
||||
*
|
||||
* @var null|bool|int|float|string|array<mixed>
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* The ordinal number of the enumerator
|
||||
*
|
||||
* @var null|int
|
||||
*/
|
||||
private $ordinal;
|
||||
|
||||
/**
|
||||
* A map of enumerator names and values by enumeration class
|
||||
*
|
||||
* @var array<class-string<Enum>, array<string, null|bool|int|float|string|array<mixed>>>
|
||||
*/
|
||||
private static $constants = [];
|
||||
|
||||
/**
|
||||
* A List of available enumerator names by enumeration class
|
||||
*
|
||||
* @var array<class-string<Enum>, string[]>
|
||||
*/
|
||||
private static $names = [];
|
||||
|
||||
/**
|
||||
* A map of enumerator names and instances by enumeration class
|
||||
*
|
||||
* @var array<class-string<Enum>, array<string, Enum>>
|
||||
*/
|
||||
private static $instances = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param null|bool|int|float|string|array<mixed> $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(): string
|
||||
{
|
||||
return $this->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws LogicException Enums are not cloneable
|
||||
* because instances are implemented as singletons
|
||||
*/
|
||||
final public function __clone()
|
||||
{
|
||||
throw new LogicException('Enums are not cloneable');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws LogicException Serialization is not supported by default in this pseudo-enum implementation
|
||||
*
|
||||
* @psalm-return never-return
|
||||
*/
|
||||
final public function __sleep()
|
||||
{
|
||||
throw new LogicException('Serialization is not supported by default in this pseudo-enum implementation');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws LogicException Serialization is not supported by default in this pseudo-enum implementation
|
||||
*
|
||||
* @psalm-return never-return
|
||||
*/
|
||||
final public function __wakeup()
|
||||
{
|
||||
throw new LogicException('Serialization is not supported by default in this pseudo-enum implementation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the enumerator
|
||||
*
|
||||
* @return null|bool|int|float|string|array<mixed>
|
||||
*/
|
||||
final public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the enumerator
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return string
|
||||
* @psalm-return non-empty-string
|
||||
*/
|
||||
final public function getName()
|
||||
{
|
||||
return self::$names[static::class][$this->ordinal ?? $this->getOrdinal()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ordinal number of the enumerator
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
final public function getOrdinal()
|
||||
{
|
||||
if ($this->ordinal === null) {
|
||||
$ordinal = 0;
|
||||
$value = $this->value;
|
||||
$constants = self::$constants[static::class] ?? static::getConstants();
|
||||
foreach ($constants 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 static|null|bool|int|float|string|array<mixed> $enumerator An enumerator object or value
|
||||
* @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|array<mixed> $enumerator An enumerator object or value
|
||||
* @return static
|
||||
* @throws InvalidArgumentException On an unknown or invalid value
|
||||
* @throws LogicException On ambiguous constant values
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function get($enumerator)
|
||||
{
|
||||
if ($enumerator instanceof static) {
|
||||
if (\get_class($enumerator) !== static::class) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid value of type %s for enumeration %s',
|
||||
\get_class($enumerator),
|
||||
static::class
|
||||
));
|
||||
}
|
||||
|
||||
return $enumerator;
|
||||
}
|
||||
|
||||
return static::byValue($enumerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an enumerator instance by the given value
|
||||
*
|
||||
* @param null|bool|int|float|string|array<mixed> $value Enumerator value
|
||||
* @return static
|
||||
* @throws InvalidArgumentException On an unknown or invalid value
|
||||
* @throws LogicException On ambiguous constant values
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function byValue($value)
|
||||
{
|
||||
/** @var mixed $value */
|
||||
|
||||
$constants = self::$constants[static::class] ?? static::getConstants();
|
||||
|
||||
$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
|
||||
));
|
||||
}
|
||||
|
||||
/** @var static $instance */
|
||||
$instance = self::$instances[static::class][$name]
|
||||
?? self::$instances[static::class][$name] = new static($constants[$name]);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function byName(string $name)
|
||||
{
|
||||
if (isset(self::$instances[static::class][$name])) {
|
||||
/** @var static $instance */
|
||||
$instance = self::$instances[static::class][$name];
|
||||
return $instance;
|
||||
}
|
||||
|
||||
$const = static::class . "::{$name}";
|
||||
if (!\defined($const)) {
|
||||
throw new InvalidArgumentException("{$const} not defined");
|
||||
}
|
||||
|
||||
assert(
|
||||
self::noAmbiguousValues(static::getConstants()),
|
||||
'Ambiguous enumerator values detected for ' . static::class
|
||||
);
|
||||
|
||||
/** @var array<int|string, mixed>|bool|float|int|string|null $value */
|
||||
$value = \constant($const);
|
||||
return self::$instances[static::class][$name] = new static($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an enumeration instance by the given ordinal number
|
||||
*
|
||||
* @param int $ordinal The ordinal number of the enumerator
|
||||
* @return static
|
||||
* @throws InvalidArgumentException On an invalid ordinal number
|
||||
* @throws LogicException On ambiguous values
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function byOrdinal(int $ordinal)
|
||||
{
|
||||
$constants = self::$constants[static::class] ?? static::getConstants();
|
||||
|
||||
if (!isset(self::$names[static::class][$ordinal])) {
|
||||
throw new InvalidArgumentException(\sprintf(
|
||||
'Invalid ordinal number %s, must between 0 and %s',
|
||||
$ordinal,
|
||||
\count(self::$names[static::class]) - 1
|
||||
));
|
||||
}
|
||||
|
||||
$name = self::$names[static::class][$ordinal];
|
||||
|
||||
/** @var static $instance */
|
||||
$instance = self::$instances[static::class][$name]
|
||||
?? self::$instances[static::class][$name] = new static($constants[$name], $ordinal);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of enumerator instances ordered by ordinal number
|
||||
*
|
||||
* @return static[]
|
||||
*
|
||||
* @phpstan-return array<int, static>
|
||||
* @psalm-return list<static>
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function getEnumerators()
|
||||
{
|
||||
if (!isset(self::$names[static::class])) {
|
||||
static::getConstants();
|
||||
}
|
||||
|
||||
/** @var callable $byNameFn */
|
||||
$byNameFn = [static::class, 'byName'];
|
||||
return \array_map($byNameFn, self::$names[static::class]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of enumerator values ordered by ordinal number
|
||||
*
|
||||
* @return (null|bool|int|float|string|array)[]
|
||||
*
|
||||
* @phpstan-return array<int, null|bool|int|float|string|array<int|string, mixed>>
|
||||
* @psalm-return list<null|bool|int|float|string|array>
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function getValues()
|
||||
{
|
||||
return \array_values(self::$constants[static::class] ?? static::getConstants());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of enumerator names ordered by ordinal number
|
||||
*
|
||||
* @return string[]
|
||||
*
|
||||
* @phpstan-return array<int, string>
|
||||
* @psalm-return list<non-empty-string>
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function getNames()
|
||||
{
|
||||
if (!isset(self::$names[static::class])) {
|
||||
static::getConstants();
|
||||
}
|
||||
return self::$names[static::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of enumerator ordinal numbers
|
||||
*
|
||||
* @return int[]
|
||||
*
|
||||
* @phpstan-return array<int, int>
|
||||
* @psalm-return list<int>
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function getOrdinals()
|
||||
{
|
||||
$count = \count(self::$constants[static::class] ?? static::getConstants());
|
||||
return $count ? \range(0, $count - 1) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available constants of the called class
|
||||
*
|
||||
* @return (null|bool|int|float|string|array)[]
|
||||
* @throws LogicException On ambiguous constant values
|
||||
*
|
||||
* @phpstan-return array<string, null|bool|int|float|string|array<int|string, mixed>>
|
||||
* @psalm-return array<non-empty-string, null|bool|int|float|string|array>
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function getConstants()
|
||||
{
|
||||
if (isset(self::$constants[static::class])) {
|
||||
return self::$constants[static::class];
|
||||
}
|
||||
|
||||
$reflection = new ReflectionClass(static::class);
|
||||
$constants = [];
|
||||
|
||||
do {
|
||||
$scopeConstants = [];
|
||||
// Enumerators must be defined as public class constants
|
||||
foreach ($reflection->getReflectionConstants() as $reflConstant) {
|
||||
if ($reflConstant->isPublic()) {
|
||||
$scopeConstants[ $reflConstant->getName() ] = $reflConstant->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
$constants = $scopeConstants + $constants;
|
||||
} while (($reflection = $reflection->getParentClass()) && $reflection->name !== __CLASS__);
|
||||
|
||||
/** @var array<string, null|bool|int|float|string|array<mixed>> $constants */
|
||||
|
||||
assert(
|
||||
self::noAmbiguousValues($constants),
|
||||
'Ambiguous enumerator values detected for ' . static::class
|
||||
);
|
||||
|
||||
self::$names[static::class] = \array_keys($constants);
|
||||
return self::$constants[static::class] = $constants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the given constants does not contain ambiguous values
|
||||
* @param array<string, null|bool|int|float|string|array<mixed>> $constants
|
||||
* @return bool
|
||||
*/
|
||||
private static function noAmbiguousValues($constants)
|
||||
{
|
||||
foreach ($constants as $value) {
|
||||
$names = \array_keys($constants, $value, true);
|
||||
if (\count($names) > 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the given enumerator is part of this enumeration
|
||||
*
|
||||
* @param static|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @return bool
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function has($enumerator)
|
||||
{
|
||||
if ($enumerator instanceof static) {
|
||||
return \get_class($enumerator) === static::class;
|
||||
}
|
||||
|
||||
return static::hasValue($enumerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the given enumerator value is part of this enumeration
|
||||
*
|
||||
* @param null|bool|int|float|string|array<mixed> $value
|
||||
* @return bool
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function hasValue($value)
|
||||
{
|
||||
return \in_array($value, self::$constants[static::class] ?? static::getConstants(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the given enumerator name is part of this enumeration
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function hasName(string $name)
|
||||
{
|
||||
return \defined("static::{$name}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 enumerator (called as method)
|
||||
* @param array<mixed> $args There should be no arguments
|
||||
* @return static
|
||||
* @throws InvalidArgumentException On an invalid or unknown name
|
||||
* @throws LogicException On ambiguous constant values
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
final public static function __callStatic(string $method, array $args)
|
||||
{
|
||||
return static::byName($method);
|
||||
}
|
||||
}
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MabeEnum;
|
||||
|
||||
use ArrayAccess;
|
||||
use Countable;
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
use IteratorAggregate;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* A map of enumerators and data values (EnumMap<T of Enum, mixed>).
|
||||
*
|
||||
* @template T of Enum
|
||||
* @implements ArrayAccess<T, mixed>
|
||||
* @implements IteratorAggregate<T, mixed>
|
||||
*
|
||||
* @copyright 2020, Marc Bennewitz
|
||||
* @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
|
||||
* @link http://github.com/marc-mabe/php-enum for the canonical source repository
|
||||
*/
|
||||
class EnumMap implements ArrayAccess, Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* The classname of the enumeration type
|
||||
* @var class-string<T>
|
||||
*/
|
||||
private $enumeration;
|
||||
|
||||
/**
|
||||
* Internal map of ordinal number and data value
|
||||
* @var array<int, mixed>
|
||||
*/
|
||||
private $map = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param class-string<T> $enumeration The classname of the enumeration type
|
||||
* @param null|iterable<T|null|bool|int|float|string|array<mixed>, mixed> $map Initialize map
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(string $enumeration, ?iterable $map = null)
|
||||
{
|
||||
if (!\is_subclass_of($enumeration, Enum::class)) {
|
||||
throw new InvalidArgumentException(\sprintf(
|
||||
'%s can handle subclasses of %s only',
|
||||
__CLASS__,
|
||||
Enum::class
|
||||
));
|
||||
}
|
||||
$this->enumeration = $enumeration;
|
||||
|
||||
if ($map) {
|
||||
$this->addIterable($map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add virtual private property "__pairs" with a list of key-value-pairs
|
||||
* to the result of var_dump.
|
||||
*
|
||||
* This helps debugging as internally the map is using the ordinal number.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function __debugInfo(): array {
|
||||
$dbg = (array)$this;
|
||||
$dbg["\0" . self::class . "\0__pairs"] = array_map(function ($k, $v) {
|
||||
return [$k, $v];
|
||||
}, $this->getKeys(), $this->getValues());
|
||||
return $dbg;
|
||||
}
|
||||
|
||||
/* write access (mutable) */
|
||||
|
||||
/**
|
||||
* Adds the given enumerator (object or value) mapping to the specified data value.
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @param mixed $value
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
* @see offsetSet()
|
||||
*/
|
||||
public function add($enumerator, $value): void
|
||||
{
|
||||
$ord = ($this->enumeration)::get($enumerator)->getOrdinal();
|
||||
$this->map[$ord] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given iterable, mapping enumerators (objects or values) to data values.
|
||||
* @param iterable<T|null|bool|int|float|string|array<mixed>, mixed> $map
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
*/
|
||||
public function addIterable(iterable $map): void
|
||||
{
|
||||
$innerMap = $this->map;
|
||||
foreach ($map as $enumerator => $value) {
|
||||
$ord = ($this->enumeration)::get($enumerator)->getOrdinal();
|
||||
$innerMap[$ord] = $value;
|
||||
}
|
||||
$this->map = $innerMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given enumerator (object or value) mapping.
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
* @see offsetUnset()
|
||||
*/
|
||||
public function remove($enumerator): void
|
||||
{
|
||||
$ord = ($this->enumeration)::get($enumerator)->getOrdinal();
|
||||
unset($this->map[$ord]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given iterable enumerator (object or value) mappings.
|
||||
* @param iterable<T|null|bool|int|float|string|array<mixed>> $enumerators
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
*/
|
||||
public function removeIterable(iterable $enumerators): void
|
||||
{
|
||||
$map = $this->map;
|
||||
foreach ($enumerators as $enumerator) {
|
||||
$ord = ($this->enumeration)::get($enumerator)->getOrdinal();
|
||||
unset($map[$ord]);
|
||||
}
|
||||
|
||||
$this->map = $map;
|
||||
}
|
||||
|
||||
/* write access (immutable) */
|
||||
|
||||
/**
|
||||
* Creates a new map with the given enumerator (object or value) mapping to the specified data value added.
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
*/
|
||||
public function with($enumerator, $value): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->add($enumerator, $value);
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new map with the given iterable mapping enumerators (objects or values) to data values added.
|
||||
* @param iterable<T|null|bool|int|float|string|array<mixed>, mixed> $map
|
||||
* @return static
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
*/
|
||||
public function withIterable(iterable $map): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->addIterable($map);
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new map with the given enumerator mapping removed.
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @return static
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
*/
|
||||
public function without($enumerator): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->remove($enumerator);
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new map with the given iterable enumerator (object or value) mappings removed.
|
||||
* @param iterable<T|null|bool|int|float|string|array<mixed>> $enumerators
|
||||
* @return static
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
*/
|
||||
public function withoutIterable(iterable $enumerators): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->removeIterable($enumerators);
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/* read access */
|
||||
|
||||
/**
|
||||
* Get the classname of the enumeration type.
|
||||
* @return class-string<T>
|
||||
*/
|
||||
public function getEnumeration(): string
|
||||
{
|
||||
return $this->enumeration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapped data value of the given enumerator (object or value).
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
* @throws UnexpectedValueException If the given enumerator does not exist in this map
|
||||
* @see offsetGet()
|
||||
*/
|
||||
public function get($enumerator)
|
||||
{
|
||||
$enumerator = ($this->enumeration)::get($enumerator);
|
||||
$ord = $enumerator->getOrdinal();
|
||||
if (!\array_key_exists($ord, $this->map)) {
|
||||
throw new UnexpectedValueException(sprintf(
|
||||
'Enumerator %s could not be found',
|
||||
\var_export($enumerator->getValue(), true)
|
||||
));
|
||||
}
|
||||
|
||||
return $this->map[$ord];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of enumerator keys.
|
||||
* @return T[]
|
||||
*
|
||||
* @phpstan-return array<int, T>
|
||||
* @psalm-return list<T>
|
||||
*/
|
||||
public function getKeys(): array
|
||||
{
|
||||
/** @var callable $byOrdinalFn */
|
||||
$byOrdinalFn = [$this->enumeration, 'byOrdinal'];
|
||||
|
||||
return \array_map($byOrdinalFn, \array_keys($this->map));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of mapped data values.
|
||||
* @return mixed[]
|
||||
*
|
||||
* @phpstan-return array<int, mixed>
|
||||
* @psalm-return list<mixed>
|
||||
*/
|
||||
public function getValues(): array
|
||||
{
|
||||
return \array_values($this->map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the given data value.
|
||||
* @param mixed $value
|
||||
* @param bool $strict Use strict type comparison
|
||||
* @return T|null The enumerator object of the first matching data value or NULL
|
||||
*/
|
||||
public function search($value, bool $strict = false)
|
||||
{
|
||||
/** @var false|int $ord */
|
||||
$ord = \array_search($value, $this->map, $strict);
|
||||
if ($ord !== false) {
|
||||
return ($this->enumeration)::byOrdinal($ord);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the given enumerator key (object or value) exists.
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @return bool
|
||||
* @see offsetExists()
|
||||
*/
|
||||
public function has($enumerator): bool
|
||||
{
|
||||
try {
|
||||
$ord = ($this->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 (object or value) exists.
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @return bool
|
||||
* @see offsetExists()
|
||||
* @see has()
|
||||
* @deprecated Will trigger deprecation warning in last 4.x and removed in 5.x
|
||||
*/
|
||||
public function contains($enumerator): bool
|
||||
{
|
||||
return $this->has($enumerator);
|
||||
}
|
||||
|
||||
/* ArrayAccess */
|
||||
|
||||
/**
|
||||
* Test if the given enumerator key (object or value) exists and is not NULL
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @return bool
|
||||
* @see contains()
|
||||
*/
|
||||
public function offsetExists($enumerator): bool
|
||||
{
|
||||
try {
|
||||
return isset($this->map[($this->enumeration)::get($enumerator)->getOrdinal()]);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
// An invalid enumerator can't be an offset of this map
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapped data value of the given enumerator (object or value).
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @return mixed The mapped date value of the given enumerator or NULL
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
* @see get()
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($enumerator)
|
||||
{
|
||||
try {
|
||||
return $this->get($enumerator);
|
||||
} catch (UnexpectedValueException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given enumerator (object or value) mapping to the specified data value.
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
* @see add()
|
||||
*/
|
||||
public function offsetSet($enumerator, $value = null): void
|
||||
{
|
||||
$this->add($enumerator, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given enumerator (object or value) mapping.
|
||||
* @param T|null|bool|int|float|string|array<mixed> $enumerator
|
||||
* @return void
|
||||
* @throws InvalidArgumentException On an invalid given enumerator
|
||||
* @see remove()
|
||||
*/
|
||||
public function offsetUnset($enumerator): void
|
||||
{
|
||||
$this->remove($enumerator);
|
||||
}
|
||||
|
||||
/* IteratorAggregate */
|
||||
|
||||
/**
|
||||
* Get a new Iterator.
|
||||
*
|
||||
* @return Iterator<T, mixed> Iterator<K extends Enum, V>
|
||||
*/
|
||||
public function getIterator(): Iterator
|
||||
{
|
||||
$map = $this->map;
|
||||
foreach ($map as $ordinal => $value) {
|
||||
yield ($this->enumeration)::byOrdinal($ordinal) => $value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Countable */
|
||||
|
||||
/**
|
||||
* Count the number of elements
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return \count($this->map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the map is empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->map);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MabeEnum;
|
||||
|
||||
use RuntimeException;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* Trait to make enumerations serializable
|
||||
*
|
||||
* This trait is a workaround to make enumerations serializable.
|
||||
*
|
||||
* Please note that this feature breaks singleton behaviour of your enumerations
|
||||
* if an enumeration will be unserialized after it was instantiated already.
|
||||
*
|
||||
* @copyright 2020, Marc Bennewitz
|
||||
* @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
|
||||
* @link http://github.com/marc-mabe/php-enum for the canonical source repository
|
||||
* @link https://github.com/marc-mabe/php-enum/issues/52 for further information about this feature
|
||||
*/
|
||||
trait EnumSerializableTrait
|
||||
{
|
||||
/**
|
||||
* The method will be defined by MabeEnum\Enum
|
||||
* @return null|bool|int|float|string|array<mixed>
|
||||
*/
|
||||
abstract public function getValue();
|
||||
|
||||
/**
|
||||
* Returns an array of data to be serialized.
|
||||
* This magic method will be called by serialize() in PHP >= 7.4
|
||||
*
|
||||
* @return array<string, null|bool|int|float|string|array<mixed>>
|
||||
*/
|
||||
public function __serialize(): array
|
||||
{
|
||||
return ['value' => $this->getValue()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives an array of data to be unserialized on a new instance without constructor.
|
||||
* This magic method will be called in PHP >= 7.4 is the data where serialized with PHP >= 7.4.
|
||||
*
|
||||
* @throws RuntimeException On missing, unknown or invalid value
|
||||
* @throws LogicException On calling this method on an already initialized enumerator
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
* @return void
|
||||
*/
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
if (!\array_key_exists('value', $data)) {
|
||||
throw new RuntimeException('Missing array key "value"');
|
||||
}
|
||||
|
||||
/** @var mixed $value */
|
||||
$value = $data['value'];
|
||||
$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 = static::class;
|
||||
$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->bindTo($this, Enum::class)();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the value of the enumeration
|
||||
* This will be called automatically on `serialize()` if the enumeration implements the `Serializable` interface
|
||||
*
|
||||
* @return string
|
||||
* @deprecated Since PHP 7.4
|
||||
*/
|
||||
public function serialize(): string
|
||||
{
|
||||
return \serialize($this->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return void
|
||||
* @throws RuntimeException On an unknown or invalid value
|
||||
* @throws LogicException On calling this method on an already initialized enumerator
|
||||
* @deprecated Since PHP 7.4
|
||||
*/
|
||||
public function unserialize($serialized): void
|
||||
{
|
||||
$this->__unserialize(['value' => \unserialize($serialized)]);
|
||||
}
|
||||
}
|
||||
+1193
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
interface Stringable
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user