Я знаю, что в PHP пока нет собственных перечислений. Но я привык к ним из мира Java. Я хотел бы использовать перечисления как способ дать предопределенные значения, которые могут понять функции автозаполнения IDE.
Константы делают свое дело, но есть проблема столкновения пространств имен и (или фактически потому что) они глобальные. У массивов нет проблемы с пространством имен, но они слишком расплывчаты, они могут быть перезаписаны во время выполнения, а IDE редко знают, как автоматически заполнять свои ключи без дополнительных аннотаций или атрибутов статического анализа.
Есть ли какие-либо решения / обходные пути, которые вы обычно используете? Кто-нибудь помнит, были ли у PHP-ребят какие-нибудь мысли или решения по поводу перечислений?
Я создал обходную функцию, которая перечисляет константы как побитовые, так и нет. Не заметил, что вы спрашивали об этом раньше, но у меня есть лучшее решение, чем здесь переменные класса: stackoverflow.com/questions/3836385/…






Самым распространенным решением, которое я видел для перечисления в PHP, было создание универсального класса перечисления, а затем его расширение. Вы можете взглянуть на это.
ОБНОВИТЬ: В качестве альтернативы я нашел это на phpclasses.org.
Хотя реализация проста и, вероятно, сработает, обратная сторона этого заключается в том, что IDE, вероятно, не знают, как автоматически заполнять перечисления. Я не мог проверить тот, что был на phpclasses.org, потому что он хотел, чтобы я зарегистрировался.
Я использовал классы с константами:
class Enum {
const NAME = 'aaaa';
const SOME_VALUE = 'bbbb';
}
print Enum::NAME;
А как насчет констант класса?
<?php
class YourClass
{
const SOME_CONSTANT = 1;
public function echoConstant()
{
echo self::SOME_CONSTANT;
}
}
echo YourClass::SOME_CONSTANT;
$c = new YourClass;
$c->echoConstant();
Я предпочитаю этот простой подход
echoConstant можно заменить на __toString. А потом просто echo $cВ зависимости от варианта использования я обычно использую что-то просто, например следующее:
abstract class DaysOfWeek
{
const Sunday = 0;
const Monday = 1;
// etc.
}
$today = DaysOfWeek::Sunday;
Однако в других случаях может потребоваться дополнительная проверка констант и значений. Основываясь на приведенных ниже комментариях об отражении и несколько других заметок, вот расширенный пример, который может лучше обслуживать гораздо более широкий круг случаев:
abstract class BasicEnum {
private static $constCacheArray = NULL;
private static function getConstants() {
if (self::$constCacheArray == NULL) {
self::$constCacheArray = [];
}
$calledClass = get_called_class();
if (!array_key_exists($calledClass, self::$constCacheArray)) {
$reflect = new ReflectionClass($calledClass);
self::$constCacheArray[$calledClass] = $reflect->getConstants();
}
return self::$constCacheArray[$calledClass];
}
public static function isValidName($name, $strict = false) {
$constants = self::getConstants();
if ($strict) {
return array_key_exists($name, $constants);
}
$keys = array_map('strtolower', array_keys($constants));
return in_array(strtolower($name), $keys);
}
public static function isValidValue($value, $strict = true) {
$values = array_values(self::getConstants());
return in_array($value, $values, $strict);
}
}
Создав простой класс перечисления, расширяющий BasicEnum, теперь у вас есть возможность использовать методы таким образом для простой проверки ввода:
abstract class DaysOfWeek extends BasicEnum {
const Sunday = 0;
const Monday = 1;
const Tuesday = 2;
const Wednesday = 3;
const Thursday = 4;
const Friday = 5;
const Saturday = 6;
}
DaysOfWeek::isValidName('Humpday'); // false
DaysOfWeek::isValidName('Monday'); // true
DaysOfWeek::isValidName('monday'); // true
DaysOfWeek::isValidName('monday', $strict = true); // false
DaysOfWeek::isValidName(0); // false
DaysOfWeek::isValidValue(0); // true
DaysOfWeek::isValidValue(5); // true
DaysOfWeek::isValidValue(7); // false
DaysOfWeek::isValidValue('Friday'); // false
В качестве примечания: каждый раз, когда я использую отражение хотя бы один раз в статическом / константном классе, где данные не изменятся (например, в перечислении), я кэширую результаты этих вызовов отражения, поскольку каждый раз использование новых объектов отражения в конечном итоге будет иметь заметное влияние на производительность (хранится в ассоциативном массив для нескольких перечислений).
Теперь, когда большинство людей обновили наконец как минимум до 5.3 и доступен SplEnum, это, безусловно, также жизнеспособный вариант - если вы не возражаете против традиционно неинтуитивной идеи наличия фактического enum экземпляры во всей кодовой базе. В приведенном выше примере экземпляры BasicEnum и DaysOfWeek не могут быть созданы, да и не должны.
Я тоже этим пользуюсь. Вы также можете подумать о создании классов abstract и final, чтобы их нельзя было создать или расширить.
Вы можете сделать класс и abstract, и final? Я знаю, что в Java это запрещено. Вы можете сделать это на php?
@ryeguy Кажется, у вас не получится сделать и то и другоеabstract и final. В таком случае я бы выбрал абстрактное.
Могли бы вы сделать это с пространствами имен вместо классов в PHP 5.3?
Да, теперь, когда доступны пространства имен, вы можете в качестве альтернативы разместить воскресенье / понедельник / и т. д. константы в пространстве имен DaysOfWeek, называя их DaysOfWeek\Sunday и т. д. Выбор между двумя методами в конечном итоге будет зависеть от личных предпочтений в дизайне и обозначениях, но оба работают одинаково хорошо в версии 5.3 и более поздних версиях.
Об аннотации или выпуске; Я делаю их окончательными и даю им пустой частный конструктор
@BrianCline, является ли один из стилей более семантически правильным, чем другой?
@TylerCrompton: Нет, я так не думаю. Я все еще использую последний класс, но в то же время мы еще не приняли в значительной степени пространства имен. Я бы сказал, что выбирайте стиль, который вы предпочитаете больше всего, который стилистически наиболее соответствует вашей среде и направлению, в котором она будет развиваться в будущем.
Это намного лучше, чем использование констант в существующем классе и загрязнение его пространства имен.
Улучшение, просто расширяя class DaysOfWeek extends SplEnum => То же, что и в исходной публикации, но с добавленной функциональностью без дополнительных усилий.
Я думаю, что интерфейсы намного лучше, чем классы, как в ответе @Andi T
@ greg0ire, потому что вы не могли создать экземпляр перечисления на других языках, и вы не могли создать экземпляр самого интерфейса, поэтому это более естественно вписывается в дизайн перечисления
@MaximilianRuta, но как интерфейс он имеет определенное полиморфное значение, то есть другие классы все еще могут его реализовать, чего вы не можете / не хотите делать с перечислениями. Так что лично меня это вводит в заблуждение. Я думаю, что решение Лекса лучше всего.
Будьте осторожны с использованием 0, чтобы не столкнуться с какими-либо непредвиденными проблемами ложного сравнения, например эквивалентно null и друзьям в заявлении switch. Был там.
Чтобы перебрать имена и значения для проверки во время выполнения: $r = new ReflectionClass('DaysOfWeek'); foreach ($r->getConstants() as $day => $code) { ... }
@Искра; Да, я полагаю, ты мог бы. Но это не решило бы эту проблему.
В этом замечательном коде есть очень небольшая ошибка, которая может привести к некоторым странным результатам, если BasicEnum будет расширен более чем одним классом в одном приложении. Я внесу исправление в ответ ниже.
Я собираюсь сделать из этого класса пакет Composer. Я знаю, что он крошечный, но мне не нравится копировать одно и то же снова и снова в проектах. @BrianCline: разве вы не хотите делать это сами?
@ greg0ire: Спасибо за упаковку, однако, пожалуйста, сделайте лицензию нет GPL на код, который я предоставил в качестве своего ответа. В нижнем колонтитуле SO указано, что пользовательские вклады лицензированы CC BY-SA 3.0, что требует лицензирования распространяемых / совместно используемых копий и производных работ по той же лицензии; никакие версии GPL не указаны CC как совместимые с BY-SA 3.0. Подробности см. В сводка CC BY-SA 3.0 или полный текст лицензии.
Опубликовал это на Reddit и отметил вас в README
Спасибо, сэр - рад, что вы нашли в этом какую-то пользу. Я не уверен, что люди в ветке Reddit заметили, что этот ответ был написан почти 5 лет назад (при этом код был немного старше ответа). Я вырос на C и C++, и это было примерно лучшее, что PHP мог делать в то время, поскольку тонкости SPL были еще очень молоды (читайте: молодой во времена PHP). Также, если я не ошибаюсь, SplEnum по-прежнему требует отдельного расширения SPL_Types, и документы по-прежнему помечают его как экспериментальное после всего этого.
Как упоминал @NeilTownsend, в этом коде есть серьезная ошибка. Вот демо. codepad.viper-7.com/boTjXT только в php, когда ответ с абсолютно серьезной ошибкой получает 700+ голосов lol.
Согласитесь с Нилом и Козлом. Мое решение состояло в том, чтобы использовать static :: вместо self :: в методе getConstants (), а затем повторно объявить $ constCache во всех дочерних перечислениях. Обратите внимание, что поздняя статическая привязка - это php5.3 +
Я попытался заменить значения consts на array()s (потому что это возможно в последних версиях PHP), чтобы избежать ложных cases в switches при использовании ints или других классов эмуляции enum. Я надеялся, что это создаст уникальный «указатель», который можно будет сравнивать так же быстро, как int, но, к сожалению, array - это по стоимости. Что за урод, PHP
Если вам нужно использовать глобально уникальные перечисления (т. Е. Даже при сравнении элементов между разными перечислениями) и просты в использовании, не стесняйтесь использовать следующий код. Я также добавил несколько методов, которые считаю полезными. Вы найдете примеры в комментариях вверху кода.
<?php
/**
* Class Enum
*
* @author Christopher Fox <[email protected]>
*
* @version 1.0
*
* This class provides the function of an enumeration.
* The values of Enum elements are unique (even between different Enums)
* as you would expect them to be.
*
* Constructing a new Enum:
* ========================
*
* In the following example we construct an enum called "UserState"
* with the elements "inactive", "active", "banned" and "deleted".
*
* <code>
* Enum::Create('UserState', 'inactive', 'active', 'banned', 'deleted');
* </code>
*
* Using Enums:
* ============
*
* The following example demonstrates how to compare two Enum elements
*
* <code>
* var_dump(UserState::inactive == UserState::banned); // result: false
* var_dump(UserState::active == UserState::active); // result: true
* </code>
*
* Special Enum methods:
* =====================
*
* Get the number of elements in an Enum:
*
* <code>
* echo UserState::CountEntries(); // result: 4
* </code>
*
* Get a list with all elements of the Enum:
*
* <code>
* $allUserStates = UserState::GetEntries();
* </code>
*
* Get a name of an element:
*
* <code>
* echo UserState::GetName(UserState::deleted); // result: deleted
* </code>
*
* Get an integer ID for an element (e.g. to store as a value in a database table):
* This is simply the index of the element (beginning with 1).
* Note that this ID is only unique for this Enum but now between different Enums.
*
* <code>
* echo UserState::GetDatabaseID(UserState::active); // result: 2
* </code>
*/
class Enum
{
/**
* @var Enum $instance The only instance of Enum (Singleton)
*/
private static $instance;
/**
* @var array $enums An array of all enums with Enum names as keys
* and arrays of element names as values
*/
private $enums;
/**
* Constructs (the only) Enum instance
*/
private function __construct()
{
$this->enums = array();
}
/**
* Constructs a new enum
*
* @param string $name The class name for the enum
* @param mixed $_ A list of strings to use as names for enum entries
*/
public static function Create($name, $_)
{
// Create (the only) Enum instance if this hasn't happened yet
if (self::$instance===null)
{
self::$instance = new Enum();
}
// Fetch the arguments of the function
$args = func_get_args();
// Exclude the "name" argument from the array of function arguments,
// so only the enum element names remain in the array
array_shift($args);
self::$instance->add($name, $args);
}
/**
* Creates an enumeration if this hasn't happened yet
*
* @param string $name The class name for the enum
* @param array $fields The names of the enum elements
*/
private function add($name, $fields)
{
if (!array_key_exists($name, $this->enums))
{
$this->enums[$name] = array();
// Generate the code of the class for this enumeration
$classDeclaration = "class " . $name . " {\n"
. "private static \$name = '" . $name . "';\n"
. $this->getClassConstants($name, $fields)
. $this->getFunctionGetEntries($name)
. $this->getFunctionCountEntries($name)
. $this->getFunctionGetDatabaseID()
. $this->getFunctionGetName()
. "}";
// Create the class for this enumeration
eval($classDeclaration);
}
}
/**
* Returns the code of the class constants
* for an enumeration. These are the representations
* of the elements.
*
* @param string $name The class name for the enum
* @param array $fields The names of the enum elements
*
* @return string The code of the class constants
*/
private function getClassConstants($name, $fields)
{
$constants = '';
foreach ($fields as $field)
{
// Create a unique ID for the Enum element
// This ID is unique because class and variables
// names can't contain a semicolon. Therefore we
// can use the semicolon as a separator here.
$uniqueID = $name . ";" . $field;
$constants .= "const " . $field . " = '". $uniqueID . "';\n";
// Store the unique ID
array_push($this->enums[$name], $uniqueID);
}
return $constants;
}
/**
* Returns the code of the function "GetEntries()"
* for an enumeration
*
* @param string $name The class name for the enum
*
* @return string The code of the function "GetEntries()"
*/
private function getFunctionGetEntries($name)
{
$entryList = '';
// Put the unique element IDs in single quotes and
// separate them with commas
foreach ($this->enums[$name] as $key => $entry)
{
if ($key > 0) $entryList .= ',';
$entryList .= "'" . $entry . "'";
}
return "public static function GetEntries() { \n"
. " return array(" . $entryList . ");\n"
. "}\n";
}
/**
* Returns the code of the function "CountEntries()"
* for an enumeration
*
* @param string $name The class name for the enum
*
* @return string The code of the function "CountEntries()"
*/
private function getFunctionCountEntries($name)
{
// This function will simply return a constant number (e.g. return 5;)
return "public static function CountEntries() { \n"
. " return " . count($this->enums[$name]) . ";\n"
. "}\n";
}
/**
* Returns the code of the function "GetDatabaseID()"
* for an enumeration
*
* @return string The code of the function "GetDatabaseID()"
*/
private function getFunctionGetDatabaseID()
{
// Check for the index of this element inside of the array
// of elements and add +1
return "public static function GetDatabaseID(\$entry) { \n"
. "\$key = array_search(\$entry, self::GetEntries());\n"
. " return \$key + 1;\n"
. "}\n";
}
/**
* Returns the code of the function "GetName()"
* for an enumeration
*
* @return string The code of the function "GetName()"
*/
private function getFunctionGetName()
{
// Remove the class name from the unique ID
// and return this value (which is the element name)
return "public static function GetName(\$entry) { \n"
. "return substr(\$entry, strlen(self::\$name) + 1 , strlen(\$entry));\n"
. "}\n";
}
}
?>
Мне это очень нравится. Однако одна из основных претензий - это способность IDE подбирать значения для автозаполнения. Я не уверен, что это было бы возможно без специального дополнения для IDE. Не то чтобы это невозможно было сделать, просто нужно немного поработать.
Используете eval() только для того, чтобы объявить новую среду выполнения Enums? Eek. Я этого не чувствую. Как предотвратить создание неправильного класса Enum другими классами до того, как вы определите правильный? Разве Enums не были известны до времени выполнения? И, как подразумевает @corsiKa, автозаполнение IDE отсутствует. Единственное преимущество, которое я вижу, - это ленивое кодирование.
Вот библиотека github для обработки типобезопасных перечислений в php:
Эта библиотека обрабатывает создание классов, кэширование классов и реализует шаблон проектирования Type Safe Enumeration с несколькими вспомогательными методами для работы с перечислениями, такими как получение порядкового номера для сортировки перечислений или получение двоичного значения для комбинаций перечислений.
Сгенерированный код использует простой старый файл шаблона php, который также можно настраивать, поэтому вы можете предоставить свой собственный шаблон.
Это полный тест, покрытый phpunit.
php-enums на github (не стесняйтесь форкнуть)
<?php
//require the library
require_once __DIR__ . '/src/Enum.func.php';
//if you don't have a cache directory, create one
@mkdir(__DIR__ . '/cache');
EnumGenerator::setDefaultCachedClassesDir(__DIR__ . '/cache');
//Class definition is evaluated on the fly:
Enum('FruitsEnum', array('apple' , 'orange' , 'rasberry' , 'bannana'));
//Class definition is cached in the cache directory for later usage:
Enum('CachedFruitsEnum', array('apple' , 'orange' , 'rasberry' , 'bannana'), '\my\company\name\space', true);
echo 'FruitsEnum::APPLE() == FruitsEnum::APPLE(): ';
var_dump(FruitsEnum::APPLE() == FruitsEnum::APPLE()) . "\n";
echo 'FruitsEnum::APPLE() == FruitsEnum::ORANGE(): ';
var_dump(FruitsEnum::APPLE() == FruitsEnum::ORANGE()) . "\n";
echo 'FruitsEnum::APPLE() instanceof Enum: ';
var_dump(FruitsEnum::APPLE() instanceof Enum) . "\n";
echo 'FruitsEnum::APPLE() instanceof FruitsEnum: ';
var_dump(FruitsEnum::APPLE() instanceof FruitsEnum) . "\n";
echo "->getName()\n";
foreach (FruitsEnum::iterator() as $enum)
{
echo " " . $enum->getName() . "\n";
}
echo "->getValue()\n";
foreach (FruitsEnum::iterator() as $enum)
{
echo " " . $enum->getValue() . "\n";
}
echo "->getOrdinal()\n";
foreach (CachedFruitsEnum::iterator() as $enum)
{
echo " " . $enum->getOrdinal() . "\n";
}
echo "->getBinary()\n";
foreach (CachedFruitsEnum::iterator() as $enum)
{
echo " " . $enum->getBinary() . "\n";
}
FruitsEnum::APPLE() == FruitsEnum::APPLE(): bool(true)
FruitsEnum::APPLE() == FruitsEnum::ORANGE(): bool(false)
FruitsEnum::APPLE() instanceof Enum: bool(true)
FruitsEnum::APPLE() instanceof FruitsEnum: bool(true)
->getName()
APPLE
ORANGE
RASBERRY
BANNANA
->getValue()
apple
orange
rasberry
bannana
->getValue() when values have been specified
pig
dog
cat
bird
->getOrdinal()
1
2
3
4
->getBinary()
1
2
4
8
Ну, для простого java, такого как enum в php, я использую:
class SomeTypeName {
private static $enum = array(1 => "Read", 2 => "Write");
public function toOrdinal($name) {
return array_search($name, self::$enum);
}
public function toString($ordinal) {
return self::$enum[$ordinal];
}
}
И назвать это:
SomeTypeName::toOrdinal("Read");
SomeTypeName::toString(1);
Но я новичок в PHP и борюсь с синтаксисом, поэтому, возможно, это не лучший способ. Я экспериментировал с константами класса, используя Reflection, чтобы получить имя константы из ее значения, может быть лучше.
Хороший ответ, большинство других ответов используют классы. Однако у вас не может быть вложенных классов.
Это дает возможность перебирать значения с помощью foreach. И вред в том, что незаконное значение не улавливается.
Однако в среде IDE нет автодополнения, так что это будет стимулировать работу наугад. Константы позволили бы автозавершение, звучит лучше.
Вчера написал этот класс в моем блоге. Я думаю, что это может быть легко использовать в сценариях php:
final class EnumException extends Exception{}
abstract class Enum
{
/**
* @var array ReflectionClass
*/
protected static $reflectorInstances = array();
/**
* Массив конфигурированного объекта-константы enum
* @var array
*/
protected static $enumInstances = array();
/**
* Массив соответствий значение->ключ используется для проверки -
* если ли константа с таким значением
* @var array
*/
protected static $foundNameValueLink = array();
protected $constName;
protected $constValue;
/**
* Реализует паттерн "Одиночка"
* Возвращает объект константы, но но как объект его использовать не стоит,
* т.к. для него реализован "волшебный метод" __toString()
* Это должно использоваться только для типизачии его как параметра
* @paradm Node
*/
final public static function get($value)
{
// Это остается здесь для увеличения производительности (по замерам ~10%)
$name = self::getName($value);
if ($name === false)
throw new EnumException("Неизвестая константа");
$className = get_called_class();
if (!isset(self::$enumInstances[$className][$name]))
{
$value = constant($className.'::'.$name);
self::$enumInstances[$className][$name] = new $className($name, $value);
}
return self::$enumInstances[$className][$name];
}
/**
* Возвращает массив констант пар ключ-значение всего перечисления
* @return array
*/
final public static function toArray()
{
$classConstantsArray = self::getReflectorInstance()->getConstants();
foreach ($classConstantsArray as $k => $v)
$classConstantsArray[$k] = (string)$v;
return $classConstantsArray;
}
/**
* Для последующего использования в toArray для получения массива констант ключ->значение
* @return ReflectionClass
*/
final private static function getReflectorInstance()
{
$className = get_called_class();
if (!isset(self::$reflectorInstances[$className]))
{
self::$reflectorInstances[$className] = new ReflectionClass($className);
}
return self::$reflectorInstances[$className];
}
/**
* Получает имя константы по её значению
* @param string $value
*/
final public static function getName($value)
{
$className = (string)get_called_class();
$value = (string)$value;
if (!isset(self::$foundNameValueLink[$className][$value]))
{
$constantName = array_search($value, self::toArray(), true);
self::$foundNameValueLink[$className][$value] = $constantName;
}
return self::$foundNameValueLink[$className][$value];
}
/**
* Используется ли такое имя константы в перечислении
* @param string $name
*/
final public static function isExistName($name)
{
$constArray = self::toArray();
return isset($constArray[$name]);
}
/**
* Используется ли такое значение константы в перечислении
* @param string $value
*/
final public static function isExistValue($value)
{
return self::getName($value) === false ? false : true;
}
final private function __clone(){}
final private function __construct($name, $value)
{
$this->constName = $name;
$this->constValue = $value;
}
final public function __toString()
{
return (string)$this->constValue;
}
}
Использование:
class enumWorkType extends Enum
{
const FULL = 0;
const SHORT = 1;
}
Но это хороший класс и имя функции является родным. А также translate.google.ru может помочь.
Используйте chrome, ребята, и переводите, если вы программисты, вы читаете код!
В общем, всегда лучше включать код в ответ, а не ссылаться на внешний ресурс, который может быть или не появиться в течение 'n' месяцев / лет и т. д.
Мой класс такой большой, и я думаю, что читать этот пост будет неудобно.
Я думаю, здесь есть две плохие вещи: он на русском (каждый программист должен знать английский и использовать его, даже в комментариях), и его здесь нет. См. Справку о том, как включить огромный код.
@Arturgspb: Код идеально подходит для сайта, дайте нам знать, если чего-то не хватает.
Есть и собственное расширение. SplEnum
SplEnum gives the ability to emulate and create enumeration objects natively in PHP.
http://www.php.net/manual/en/class.splenum.php
Внимание:
https://www.php.net/manual/en/spl-types.installation.php
The PECL extension is not bundled with PHP.
A DLL for this PECL extension is currently unavailable.
Вот пример со спленумом: dreamincode.net/forums/topic/201638-enum-in-php
Откатился, мне больше нравится когда вижу ссылку. Это дает мне контекстную информацию.
SplEnum дает возможность эмулировать и создавать объекты перечисления непосредственно в PHP.
Что мне не нравится в этом, так это целочисленные константы, если вы сравните значение int с "enum", оно вернет true ... что плохо, imo.
Я откатился снова. Я не хочу, чтобы вы, ребята, редактировали ссылку.
Но вам все равно нужно определить значение для каждого элемента Enum.
Установка SplType на Linux PHP - это одно, но как, черт возьми, кто-то заставил его работать (без инструментов сборки) на Windows PHP ?? Наши рабочие станции разработчиков (где мы пишем код, тестируемый на локальном веб-сервере перед развертыванием) используют стек XAMPP. Как без ошибок установить и активировать php_spl_types.dll?
Будьте осторожны, используя это. Типы SPL экспериментальные: «Это расширение является ЭКСПЕРИМЕНТАЛЬНЫМ. Поведение этого расширения, включая названия его функций и любую другую документацию, относящуюся к этому расширению, может измениться без уведомления в будущих выпусках PHP. Это расширение следует использовать на свой страх и риск».
Просто используйте пакет myclabs/php-enum .. просто. В основном как расширение SPL, но не расширение. Делай то, что хочешь ?
Все SPL_Types ушли с php.net: php.net/manual/en/book.spl-types.php, а также SplEnum. И пакет pecl.php.net/package/SPL_Types больше не поддерживается. РВАТЬ. Типы SPL.
Мне тоже нравятся перечисления из java, и по этой причине я пишу свои перечисления таким образом, я думаю, что это наиболее похожее поведение, как в перечислениях Java, конечно, если кто-то хочет использовать больше методов из java, следует написать его здесь или в абстрактный класс, но основная идея встроена в код ниже
class FruitsEnum {
static $APPLE = null;
static $ORANGE = null;
private $value = null;
public static $map;
public function __construct($value) {
$this->value = $value;
}
public static function init () {
self::$APPLE = new FruitsEnum("Apple");
self::$ORANGE = new FruitsEnum("Orange");
//static map to get object by name - example Enum::get("INIT") - returns Enum::$INIT object;
self::$map = array (
"Apple" => self::$APPLE,
"Orange" => self::$ORANGE
);
}
public static function get($element) {
if ($element == null)
return null;
return self::$map[$element];
}
public function getValue() {
return $this->value;
}
public function equals(FruitsEnum $element) {
return $element->getValue() == $this->getValue();
}
public function __toString () {
return $this->value;
}
}
FruitsEnum::init();
var_dump(FruitsEnum::$APPLE->equals(FruitsEnum::$APPLE)); //true
var_dump(FruitsEnum::$APPLE->equals(FruitsEnum::$ORANGE)); //false
var_dump(FruitsEnum::$APPLE instanceof FruitsEnum); //true
var_dump(FruitsEnum::get("Apple")->equals(FruitsEnum::$APPLE)); //true - enum from string
var_dump(FruitsEnum::get("Apple")->equals(FruitsEnum::get("Orange"))); //false
Я делаю почти то же самое, но с двумя небольшими дополнениями: я спрятал статические значения за статическими геттерами. Одна из причин заключается в том, что я визуально предпочитаю FruitsEnum::Apple(), а не FruitsEnum::$Apple, но более важная причина состоит в том, чтобы не дать кому-либо еще установить $APPLE, тем самым нарушая перечисление для всего приложения. Другой - простой частный статический флаг $initialized, который гарантирует, что вызов init() станет бездействующим после его первого вызова (так что никто не может вмешиваться и с этим).
Мартин мне действительно нравился. .init() странный, и я не возражаю против геттерного подхода.
Это мой подход к «динамическому» перечислению ... чтобы я мог вызывать его с помощью переменных, например. из формы.
посмотрите обновленную версию под этим кодовым блоком ...
$value = "concert";
$Enumvalue = EnumCategory::enum($value);
//$EnumValue = 1
class EnumCategory{
const concert = 1;
const festival = 2;
const sport = 3;
const nightlife = 4;
const theatre = 5;
const musical = 6;
const cinema = 7;
const charity = 8;
const museum = 9;
const other = 10;
public function enum($string){
return constant('EnumCategory::'.$string);
}
}
ОБНОВЛЕНИЕ: лучший способ сделать это ...
class EnumCategory {
static $concert = 1;
static $festival = 2;
static $sport = 3;
static $nightlife = 4;
static $theatre = 5;
static $musical = 6;
static $cinema = 7;
static $charity = 8;
static $museum = 9;
static $other = 10;
}
Звоните с
EnumCategory::${$category};
Проблема с этим существом; EnumCategory::$sport = 9;. Добро пожаловать в музей спорта. constявляется лучший способ сделать это.
Я использую interface вместо class:
interface DaysOfWeek
{
const Sunday = 0;
const Monday = 1;
// etc.
}
var $today = DaysOfWeek::Sunday;
class Foo implements DaysOfWeek { }, а затем Foo::Sunday ... что?
Автор вопроса просит решить две проблемы: пространство имен и автозаполнение IDE. Как следует из наиболее рейтингового ответа, самый простой способ - использовать class (или interface, что является лишь вопросом предпочтений).
интерфейсы используются для обеспечения целостности реализации класса, это выходит за рамки интерфейса
@ user3886650 Интерфейсы могут и использовались / используются в Java для сохранения постоянных значений. Таким образом, вы не обязаны создавать экземпляр класса только для получения постоянных значений, а любая IDE предлагает завершение кода для них. Кроме того, если вы создадите класс, реализующий этот интерфейс, он унаследует все эти константы - иногда это очень удобно.
@ user3886650 Верно, но в PHP интерфейсы могут иметь константы. Кроме того, эти константы интерфейса не могут быть переопределены путем реализации классов или их дочерних элементов. По сути, это лучший ответ с точки зрения PHP, потому что все, что можно переопределить, на самом деле не работает как константа. Константа должна означать константу, а не иногда (хотя полиморфизм может быть полезен иногда).
Однако я бы не получил доступ к константам интерфейса, как показано здесь.
Я знаю, что это старый поток, однако ни одно из обходных решений, которые я видел, действительно не выглядело как перечисления, поскольку почти все обходные пути требуют, чтобы вы вручную присваивали значения элементам перечисления, или это требует, чтобы вы передавали массив ключей перечисления в функция. Поэтому я создал для этого собственное решение.
Чтобы создать класс перечисления с использованием моего решения, можно просто расширить этот класс перечисления ниже, создать кучу статических переменных (не нужно их инициализировать) и выполнить вызов yourEnumClass :: init () чуть ниже определения вашего класса перечисления .
edit: это работает только в php> = 5.3, но, вероятно, его можно изменить для работы и в более старых версиях
/**
* A base class for enums.
*
* This class can be used as a base class for enums.
* It can be used to create regular enums (incremental indices), but it can also be used to create binary flag values.
* To create an enum class you can simply extend this class, and make a call to <yourEnumClass>::init() before you use the enum.
* Preferably this call is made directly after the class declaration.
* Example usages:
* DaysOfTheWeek.class.php
* abstract class DaysOfTheWeek extends Enum{
* static $MONDAY = 1;
* static $TUESDAY;
* static $WEDNESDAY;
* static $THURSDAY;
* static $FRIDAY;
* static $SATURDAY;
* static $SUNDAY;
* }
* DaysOfTheWeek::init();
*
* example.php
* require_once("DaysOfTheWeek.class.php");
* $today = date('N');
* if ($today == DaysOfTheWeek::$SUNDAY || $today == DaysOfTheWeek::$SATURDAY)
* echo "It's weekend!";
*
* Flags.class.php
* abstract class Flags extends Enum{
* static $FLAG_1;
* static $FLAG_2;
* static $FLAG_3;
* }
* Flags::init(Enum::$BINARY_FLAG);
*
* example2.php
* require_once("Flags.class.php");
* $flags = Flags::$FLAG_1 | Flags::$FLAG_2;
* if ($flags & Flags::$FLAG_1)
* echo "Flag_1 is set";
*
* @author Tiddo Langerak
*/
abstract class Enum{
static $BINARY_FLAG = 1;
/**
* This function must be called to initialize the enumeration!
*
* @param bool $flags If the USE_BINARY flag is provided, the enum values will be binary flag values. Default: no flags set.
*/
public static function init($flags = 0){
//First, we want to get a list of all static properties of the enum class. We'll use the ReflectionClass for this.
$enum = get_called_class();
$ref = new ReflectionClass($enum);
$items = $ref->getStaticProperties();
//Now we can start assigning values to the items.
if ($flags & self::$BINARY_FLAG){
//If we want binary flag values, our first value should be 1.
$value = 1;
//Now we can set the values for all items.
foreach ($items as $key=>$item){
if (!isset($item)){
//If no value is set manually, we should set it.
$enum::$$key = $value;
//And we need to calculate the new value
$value *= 2;
} else {
//If there was already a value set, we will continue starting from that value, but only if that was a valid binary flag value.
//Otherwise, we will just skip this item.
if ($key != 0 && ($key & ($key - 1) == 0))
$value = 2 * $item;
}
}
} else {
//If we want to use regular indices, we'll start with index 0.
$value = 0;
//Now we can set the values for all items.
foreach ($items as $key=>$item){
if (!isset($item)){
//If no value is set manually, we should set it, and increment the value for the next item.
$enum::$$key = $value;
$value++;
} else {
//If a value was already set, we'll continue from that value.
$value = $item+1;
}
}
}
}
}
Для простых перечислений я использую такую конструкцию. Обычно вы можете использовать их для операторов переключения.
<?php
define("OPTION_1", "1");
define("OPTION_2", OPTION_1 + 1);
define("OPTION_3", OPTION_2 + 1);
// Some function...
switch($Val){
case OPTION_1:{ Perform_1();}break;
case OPTION_2:{ Perform_2();}break;
...
}
?>
Это не так удобно, как нативное перечисление, как в C++, но похоже, что оно работает и требует меньше обслуживания, если позже вы захотите добавить промежуточную опцию.
Вы полностью упускаете суть. Перечисления Java являются частью их ООП. Вопрос заключался в том, есть ли в PHP альтернатива, кроме констант, и ваше решение не использует ООП и не избегает констант.
Моя попытка создать перечисление с помощью PHP ... оно чрезвычайно ограничено, поскольку не поддерживает объекты в качестве значений перечисления, но все же несколько полезно ...
class ProtocolsEnum {
const HTTP = '1';
const HTTPS = '2';
const FTP = '3';
/**
* Retrieve an enum value
* @param string $name
* @return string
*/
public static function getValueByName($name) {
return constant('self::'. $name);
}
/**
* Retrieve an enum key name
* @param string $code
* @return string
*/
public static function getNameByValue($code) {
foreach(get_class_constants() as $key => $val) {
if ($val == $code) {
return $key;
}
}
}
/**
* Retrieve associate array of all constants (used for creating droplist options)
* @return multitype:
*/
public static function toArray() {
return array_flip(self::get_class_constants());
}
private static function get_class_constants()
{
$reflect = new ReflectionClass(__CLASS__);
return $reflect->getConstants();
}
}
он ограничен во многих направлениях, и существующие ответы предлагают гораздо больше. Я бы сказал, что это не добавляет ничего полезного.
Принятый ответ - это то, что нужно, и на самом деле это то, что я делаю для простоты. Предлагается большинство преимуществ перечисления (читабельность, скорость и т. д.). Однако отсутствует одна концепция: безопасность типов. В большинстве языков перечисления также используются для ограничения допустимых значений. Ниже приведен пример того, как безопасность типов также может быть достигнута с помощью частных конструкторов, статических методов создания экземпляров и проверки типов:
class DaysOfWeek{
const Sunday = 0;
const Monday = 1;
// etc.
private $intVal;
private function __construct($intVal){
$this->intVal = $intVal;
}
//static instantiation methods
public static function MONDAY(){
return new self(self::Monday);
}
//etc.
}
//function using type checking
function printDayOfWeek(DaysOfWeek $d){ //compiler can now use type checking
// to something with $d...
}
//calling the function is safe!
printDayOfWeek(DaysOfWeek::MONDAY());
Мы могли бы пойти даже дальше: использование констант в классе DaysOfWeek может привести к неправильному использованию: например, его можно ошибочно использовать так:
printDayOfWeek(DaysOfWeek::Monday); //triggers a compiler error.
что неверно (вызывает целочисленную константу). Мы можем предотвратить это, используя частные статические переменные вместо констант:
class DaysOfWeeks{
private static $monday = 1;
//etc.
private $intVal;
//private constructor
private function __construct($intVal){
$this->intVal = $intVal;
}
//public instantiation methods
public static function MONDAY(){
return new self(self::$monday);
}
//etc.
//convert an instance to its integer value
public function intVal(){
return $this->intVal;
}
}
Конечно, невозможно получить доступ к целочисленным константам (на самом деле это и было целью). Метод intVal позволяет преобразовать объект DaysOfWeek в его целочисленное представление.
Обратите внимание, что мы могли бы даже пойти дальше, реализовав механизм кэширования в методах создания экземпляров для экономии памяти в случае, если перечисления широко используются ...
Надеюсь, это поможет
Я использовал описанный ниже подход, поскольку он дает мне возможность обеспечить безопасность типов для параметров функций, автозаполнение в NetBeans и хорошую производительность. Единственное, что мне не очень нравится, это то, что вам нужно вызывать [extended class name]::enumerate(); после определения класса.
abstract class Enum {
private $_value;
protected function __construct($value) {
$this->_value = $value;
}
public function __toString() {
return (string) $this->_value;
}
public static function enumerate() {
$class = get_called_class();
$ref = new ReflectionClass($class);
$statics = $ref->getStaticProperties();
foreach ($statics as $name => $value) {
$ref->setStaticPropertyValue($name, new $class($value));
}
}
}
class DaysOfWeek extends Enum {
public static $MONDAY = 0;
public static $SUNDAY = 1;
// etc.
}
DaysOfWeek::enumerate();
function isMonday(DaysOfWeek $d) {
if ($d == DaysOfWeek::$MONDAY) {
return true;
} else {
return false;
}
}
$day = DaysOfWeek::$MONDAY;
echo (isMonday($day) ? "bummer it's monday" : "Yay! it's not monday");
Ничто не мешает вам переопределить значения перечисления: DaysOfWeek::$MONDAY = 3;
@BrianFisher, я знаю, что сейчас уже поздно, но, если вам не нравится вызывать [extended class name]::enumerate(); после определения, почему вы не делаете этого в конструкции?
Указанное решение работает хорошо. Чисто и гладко.
Однако, если вам нужны строго типизированные перечисления, вы можете использовать это:
class TestEnum extends Enum
{
public static $TEST1;
public static $TEST2;
}
TestEnum::init(); // Automatically initializes enum values
С классом Enum, который выглядит так:
class Enum
{
public static function parse($enum)
{
$class = get_called_class();
$vars = get_class_vars($class);
if (array_key_exists($enum, $vars)) {
return $vars[$enum];
}
return null;
}
public static function init()
{
$className = get_called_class();
$consts = get_class_vars($className);
foreach ($consts as $constant => $value) {
if (is_null($className::$$constant)) {
$constantValue = $constant;
$constantValueName = $className . '::' . $constant . '_VALUE';
if (defined($constantValueName)) {
$constantValue = constant($constantValueName);
}
$className::$$constant = new $className($constantValue);
}
}
}
public function __construct($value)
{
$this->value = $value;
}
}
Таким образом, значения перечисления строго типизированы и
TestEnum::$TEST1 === TestEnum::parse('TEST1') // true statement
Здесь есть несколько хороших решений!
Вот моя версия.
Я думаю, что основным недостатком является то, что члены перечисления должны быть отдельно объявлены и созданы из-за описаний и неспособности PHP создавать объекты во время объявления статических членов. Я предполагаю, что можно было бы использовать отражение с проанализированными комментариями документа.
Абстрактное перечисление выглядит так:
<?php
abstract class AbstractEnum
{
/** @var array cache of all enum instances by class name and integer value */
private static $allEnumMembers = array();
/** @var mixed */
private $code;
/** @var string */
private $description;
/**
* Return an enum instance of the concrete type on which this static method is called, assuming an instance
* exists for the passed in value. Otherwise an exception is thrown.
*
* @param $code
* @return AbstractEnum
* @throws Exception
*/
public static function getByCode($code)
{
$concreteMembers = &self::getConcreteMembers();
if (array_key_exists($code, $concreteMembers)) {
return $concreteMembers[$code];
}
throw new Exception("Value '$code' does not exist for enum '".get_called_class()."'");
}
public static function getAllMembers()
{
return self::getConcreteMembers();
}
/**
* Create, cache and return an instance of the concrete enum type for the supplied primitive value.
*
* @param mixed $code code to uniquely identify this enum
* @param string $description
* @throws Exception
* @return AbstractEnum
*/
protected static function enum($code, $description)
{
$concreteMembers = &self::getConcreteMembers();
if (array_key_exists($code, $concreteMembers)) {
throw new Exception("Value '$code' has already been added to enum '".get_called_class()."'");
}
$concreteMembers[$code] = $concreteEnumInstance = new static($code, $description);
return $concreteEnumInstance;
}
/**
* @return AbstractEnum[]
*/
private static function &getConcreteMembers() {
$thisClassName = get_called_class();
if (!array_key_exists($thisClassName, self::$allEnumMembers)) {
$concreteMembers = array();
self::$allEnumMembers[$thisClassName] = $concreteMembers;
}
return self::$allEnumMembers[$thisClassName];
}
private function __construct($code, $description)
{
$this->code = $code;
$this->description = $description;
}
public function getCode()
{
return $this->code;
}
public function getDescription()
{
return $this->description;
}
}
Вот пример конкретного перечисления:
<?php
require('AbstractEnum.php');
class EMyEnum extends AbstractEnum
{
/** @var EMyEnum */
public static $MY_FIRST_VALUE;
/** @var EMyEnum */
public static $MY_SECOND_VALUE;
/** @var EMyEnum */
public static $MY_THIRD_VALUE;
public static function _init()
{
self::$MY_FIRST_VALUE = self::enum(1, 'My first value');
self::$MY_SECOND_VALUE = self::enum(2, 'My second value');
self::$MY_THIRD_VALUE = self::enum(3, 'My third value');
}
}
EMyEnum::_init();
Что можно использовать так:
<?php
require('EMyEnum.php');
echo EMyEnum::$MY_FIRST_VALUE->getCode().' : '.EMyEnum::$MY_FIRST_VALUE->getDescription().PHP_EOL.PHP_EOL;
var_dump(EMyEnum::getAllMembers());
echo PHP_EOL.EMyEnum::getByCode(2)->getDescription().PHP_EOL;
И производит этот вывод:
1 : My first value
array(3) { [1]=> object(EMyEnum)#1 (2) { ["code":"AbstractEnum":private]=> int(1) ["description":"AbstractEnum":private]=> string(14) "My first value" } [2]=> object(EMyEnum)#2 (2) { ["code":"AbstractEnum":private]=> int(2) ["description":"AbstractEnum":private]=> string(15) "My second value" } [3]=> object(EMyEnum)#3 (2) { ["code":"AbstractEnum":private]=> int(3) ["description":"AbstractEnum":private]=> string(14) "My third value" } }My second value
abstract class Enumeration
{
public static function enum()
{
$reflect = new ReflectionClass( get_called_class() );
return $reflect->getConstants();
}
}
class Test extends Enumeration
{
const A = 'a';
const B = 'b';
}
foreach (Test::enum() as $key => $value) {
echo "$key -> $value<br>";
}
Я прокомментировал некоторые другие ответы здесь, поэтому я подумал, что тоже буду взвешивать. В конце концов, поскольку PHP не поддерживает типизированные перечисления, вы можете пойти одним из двух способов: отказаться от типизированных перечислений или смириться с тем фактом, что их чрезвычайно сложно эффективно взломать.
Я предпочитаю жить с этим фактом и вместо этого использую метод const, который так или иначе использовали другие ответы здесь:
abstract class Enum
{
const NONE = null;
final private function __construct()
{
throw new NotSupportedException(); //
}
final private function __clone()
{
throw new NotSupportedException();
}
final public static function toArray()
{
return (new ReflectionClass(static::class))->getConstants();
}
final public static function isValid($value)
{
return in_array($value, static::toArray());
}
}
Пример перечисления:
final class ResponseStatusCode extends Enum
{
const OK = 200;
const CREATED = 201;
const ACCEPTED = 202;
// ...
const SERVICE_UNAVAILABLE = 503;
const GATEWAY_TIME_OUT = 504;
const HTTP_VERSION_NOT_SUPPORTED = 505;
}
Использование Enum в качестве базового класса, от которого расширяются все другие перечисления, позволяет использовать вспомогательные методы, такие как toArray, isValid и т. д. Для меня типизированные перечисления (и управление их экземплярами) оказываются слишком беспорядочными.
Если, существовал магический метод __getStatic (и желательно магический метод __equals тоже), большая часть этого могла быть смягчена своего рода многотонным шаблоном.
(Следующее является гипотетическим; это не будет работает, хотя, возможно, однажды это будет)
final class TestEnum
{
private static $_values = [
'FOO' => 1,
'BAR' => 2,
'QUX' => 3,
];
private static $_instances = [];
public static function __getStatic($name)
{
if (isset(static::$_values[$name]))
{
if (empty(static::$_instances[$name]))
{
static::$_instances[$name] = new static($name);
}
return static::$_instances[$name];
}
throw new Exception(sprintf('Invalid enumeration value, "%s"', $name));
}
private $_value;
public function __construct($name)
{
$this->_value = static::$_values[$name];
}
public function __equals($object)
{
if ($object instanceof static)
{
return $object->_value === $this->_value;
}
return $object === $this->_value;
}
}
$foo = TestEnum::$FOO; // object(TestEnum)#1 (1) {
// ["_value":"TestEnum":private]=>
// int(1)
// }
$zap = TestEnum::$ZAP; // Uncaught exception 'Exception' with message
// 'Invalid enumeration member, "ZAP"'
$qux = TestEnum::$QUX;
TestEnum::$QUX == $qux; // true
'hello world!' == $qux; // false
Мне очень нравится простота этого ответа. Это то, к чему вы можете вернуться позже и быстро понять, как это работает, не создавая впечатление, будто вы использовали какой-то взломанный подход. Жаль, что у него не набралось больше голосов.
Я нашел эта библиотека на github, и я думаю, что он предоставляет очень достойную альтернативу ответам здесь.
function setAction(Action $action) {format, parse,…)final, чтобы предотвратить это)<?php
use MyCLabs\Enum\Enum;
/**
* Action enum
*/
class Action extends Enum
{
const VIEW = 'view';
const EDIT = 'edit';
}
<?php
$action = new Action(Action::VIEW);
// or
$action = Action::VIEW();
<?php
function setAction(Action $action) {
// ...
}
Это правильный ответ (пока, пока enum не будет добавлен в PHP 7.x), потому что он позволяет подсказывать типы.
Это не только позволяет указывать типы, но и благодаря магии __toString(), это позволяет вам делать с перечислениями то, что вы обычно действительно хотите - использовать их в операторе switch или if, сравнивая непосредственно со значениями констант. Лучший подход, если не считать поддержки нативного перечисления, IMO.
Верхний ответ выше фантастический. Однако, если вы используете extend двумя разными способами, то какое бы расширение ни было выполнено первым, вызовет функцию, которая создаст кеш. Этот кеш затем будет использоваться всеми последующими вызовами, независимо от того, какое расширение инициирует вызовы ...
Чтобы решить эту проблему, замените переменную и первую функцию на:
private static $constCacheArray = null;
private static function getConstants() {
if (self::$constCacheArray === null) self::$constCacheArray = array();
$calledClass = get_called_class();
if (!array_key_exists($calledClass, self::$constCacheArray)) {
$reflect = new \ReflectionClass($calledClass);
self::$constCacheArray[$calledClass] = $reflect->getConstants();
}
return self::$constCacheArray[$calledClass];
}
Была эта проблема. Брайан или кто-то с правами редактирования должен коснуться этого в принятом ответе. Я решил это в своем коде, используя метод static :: вместо self :: в функции getConstants () и повторно объявив $ constCache в дочерних перечислениях.
Это может быть некрасиво, но использование константы интерфейса может быть лучшим способом использовать PHP.
Четыре года спустя я снова столкнулся с этим. Мой текущий подход таков, поскольку он позволяет выполнять завершение кода в среде IDE, а также обеспечивать безопасность типов:
Базовый класс:
abstract class TypedEnum
{
private static $_instancedValues;
private $_value;
private $_name;
private function __construct($value, $name)
{
$this->_value = $value;
$this->_name = $name;
}
private static function _fromGetter($getter, $value)
{
$reflectionClass = new ReflectionClass(get_called_class());
$methods = $reflectionClass->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC);
$className = get_called_class();
foreach($methods as $method)
{
if ($method->class === $className)
{
$enumItem = $method->invoke(null);
if ($enumItem instanceof $className && $enumItem->$getter() === $value)
{
return $enumItem;
}
}
}
throw new OutOfRangeException();
}
protected static function _create($value)
{
if (self::$_instancedValues === null)
{
self::$_instancedValues = array();
}
$className = get_called_class();
if (!isset(self::$_instancedValues[$className]))
{
self::$_instancedValues[$className] = array();
}
if (!isset(self::$_instancedValues[$className][$value]))
{
$debugTrace = debug_backtrace();
$lastCaller = array_shift($debugTrace);
while ($lastCaller['class'] !== $className && count($debugTrace) > 0)
{
$lastCaller = array_shift($debugTrace);
}
self::$_instancedValues[$className][$value] = new static($value, $lastCaller['function']);
}
return self::$_instancedValues[$className][$value];
}
public static function fromValue($value)
{
return self::_fromGetter('getValue', $value);
}
public static function fromName($value)
{
return self::_fromGetter('getName', $value);
}
public function getValue()
{
return $this->_value;
}
public function getName()
{
return $this->_name;
}
}
Пример Enum:
final class DaysOfWeek extends TypedEnum
{
public static function Sunday() { return self::_create(0); }
public static function Monday() { return self::_create(1); }
public static function Tuesday() { return self::_create(2); }
public static function Wednesday() { return self::_create(3); }
public static function Thursday() { return self::_create(4); }
public static function Friday() { return self::_create(5); }
public static function Saturday() { return self::_create(6); }
}
Пример использования:
function saveEvent(DaysOfWeek $weekDay, $comment)
{
// store week day numeric value and comment:
$myDatabase->save('myeventtable',
array('weekday_id' => $weekDay->getValue()),
array('comment' => $comment));
}
// call the function, note: DaysOfWeek::Monday() returns an object of type DaysOfWeek
saveEvent(DaysOfWeek::Monday(), 'some comment');
Обратите внимание, что все экземпляры одной и той же записи перечисления одинаковы:
$monday1 = DaysOfWeek::Monday();
$monday2 = DaysOfWeek::Monday();
$monday1 === $monday2; // true
Вы также можете использовать его внутри оператора switch:
function getGermanWeekDayName(DaysOfWeek $weekDay)
{
switch ($weekDay)
{
case DaysOfWeek::Monday(): return 'Montag';
case DaysOfWeek::Tuesday(): return 'Dienstag';
// ...
}
Вы также можете создать запись перечисления по имени или значению:
$monday = DaysOfWeek::fromValue(2);
$tuesday = DaysOfWeek::fromName('Tuesday');
Или вы можете просто получить имя (то есть имя функции) из существующей записи перечисления:
$wednesday = DaysOfWeek::Wednesday()
echo $wednesDay->getName(); // Wednesday
+1 для частного конструктора. Я бы не стал делать вспомогательный абстрактный класс, просто класс, частный конструктор и кое-что из const Monday = DaysOfWeek('Monday');.
Я вот кое о чем задаюсь вопросом. В mysql 0 для перечисления считается пустым. Допустимые значения всегда начинаются с 1. Вызывает ли когда-либо расширенный класс с первым значением / int как 0 проблемы? Потому что я знаю, что mySql / Maria будет хранить значения int, но значения строки столбца всегда будут пустыми. ('') если вы передадите 0; mariadb.com/kb/en/enumdev.mysql.com/doc/refman/8.0/en/enum.html
// My Enumeration Class
class Enum
{
protected $m_actions = array();
public function __construct($actions)
{
$this->init($actions);
}
public function init($actions)
{
$this->m_actions = array();
for($i = 0; $i < count($actions); ++$i)
{
$this->m_actions[$actions[$i]] = ($i + 1);
define($actions[$i], ($i + 1));
}
}
public function toString($index)
{
$keys = array_keys($this->m_actions);
for($i = 0; $i < count($keys); ++$i)
{
if ($this->m_actions[$keys[$i]] == $index)
{
return $keys[$i];
}
}
return "undefined";
}
public function fromString($str)
{
return $this->m_actions[$str];
}
}
// Enumeration creation
$actions = new Enum(array("CREATE", "READ", "UPDATE", "DELETE"));
// Examples
print($action_objects->toString(DELETE));
print($action_objects->fromString("DELETE"));
if ($action_objects->fromString($_POST["myAction"]) == CREATE)
{
print("CREATE");
}
Мое определение класса Enum ниже - Строго типизированный, и очень естественный для использования и определения.
Определение:
class Fruit extends Enum {
static public $APPLE = 1;
static public $ORANGE = 2;
}
Fruit::initialize(); //Can also be called in autoloader
Переключиться на Enum
$myFruit = Fruit::$APPLE;
switch ($myFruit) {
case Fruit::$APPLE : echo "I like apples\n"; break;
case Fruit::$ORANGE : echo "I hate oranges\n"; break;
}
>> I like apples
Передайте Enum как параметр (строго типизированный)
/** Function only accepts Fruit enums as input**/
function echoFruit(Fruit $fruit) {
echo $fruit->getName().": ".$fruit->getValue()."\n";
}
/** Call function with each Enum value that Fruit has */
foreach (Fruit::getList() as $fruit) {
echoFruit($fruit);
}
//Call function with Apple enum
echoFruit(Fruit::$APPLE)
//Will produce an error. This solution is strongly typed
echoFruit(2);
>> APPLE: 1
>> ORANGE: 2
>> APPLE: 1
>> Argument 1 passed to echoFruit() must be an instance of Fruit, integer given
Echo Enum как строка
echo "I have an $myFruit\n";
>> I have an APPLE
Получить Enum по целому числу
$myFruit = Fruit::getByValue(2);
echo "Now I have an $myFruit\n";
>> Now I have an ORANGE
Получить перечисление по имени
$myFruit = Fruit::getByName("APPLE");
echo "But I definitely prefer an $myFruit\n\n";
>> But I definitely prefer an APPLE
Класс Enum:
/**
* @author Torge Kummerow
*/
class Enum {
/**
* Holds the values for each type of Enum
*/
static private $list = array();
/**
* Initializes the enum values by replacing the number with an instance of itself
* using reflection
*/
static public function initialize() {
$className = get_called_class();
$class = new ReflectionClass($className);
$staticProperties = $class->getStaticProperties();
self::$list[$className] = array();
foreach ($staticProperties as $propertyName => &$value) {
if ($propertyName == 'list')
continue;
$enum = new $className($propertyName, $value);
$class->setStaticPropertyValue($propertyName, $enum);
self::$list[$className][$propertyName] = $enum;
} unset($value);
}
/**
* Gets the enum for the given value
*
* @param integer $value
* @throws Exception
*
* @return Enum
*/
static public function getByValue($value) {
$className = get_called_class();
foreach (self::$list[$className] as $propertyName=>&$enum) {
/* @var $enum Enum */
if ($enum->value == $value)
return $enum;
} unset($enum);
throw new Exception("No such enum with value=$value of type ".get_called_class());
}
/**
* Gets the enum for the given name
*
* @param string $name
* @throws Exception
*
* @return Enum
*/
static public function getByName($name) {
$className = get_called_class();
if (array_key_exists($name, static::$list[$className]))
return self::$list[$className][$name];
throw new Exception("No such enum ".get_called_class()."::\$$name");
}
/**
* Returns the list of all enum variants
* @return Array of Enum
*/
static public function getList() {
$className = get_called_class();
return self::$list[$className];
}
private $name;
private $value;
public function __construct($name, $value) {
$this->name = $name;
$this->value = $value;
}
public function __toString() {
return $this->name;
}
public function getValue() {
return $this->value;
}
public function getName() {
return $this->name;
}
}
Добавление
Конечно, вы также можете добавлять комментарии для IDE
class Fruit extends Enum {
/**
* This comment is for autocomplete support in common IDEs
* @var Fruit A yummy apple
*/
static public $APPLE = 1;
/**
* This comment is for autocomplete support in common IDEs
* @var Fruit A sour orange
*/
static public $ORANGE = 2;
}
//This can also go to the autoloader if available.
Fruit::initialize();
class DayOfWeek {
static $values = array(
self::MONDAY,
self::TUESDAY,
// ...
);
const MONDAY = 0;
const TUESDAY = 1;
// ...
}
$today = DayOfWeek::MONDAY;
// If you want to check if a value is valid
assert( in_array( $today, DayOfWeek::$values ) );
Не используйте отражение. Это чрезвычайно затрудняет рассуждение о вашем коде и отслеживание того, где что-то используется, и имеет тенденцию ломать инструменты статического анализа (например, то, что встроено в вашу среду IDE).
Один из аспектов, отсутствующих в некоторых других ответах, - это способ использования перечислений с подсказкой типа.
Если вы определяете свое перечисление как набор констант в абстрактном классе, например
abstract class ShirtSize {
public const SMALL = 1;
public const MEDIUM = 2;
public const LARGE = 3;
}
тогда вы не можете ввести подсказку в параметре функции - например, потому что он не может быть создан, но также потому, что типом ShirtSize::SMALL является int, а не ShirtSize.
Вот почему нативные перечисления в PHP были бы намного лучше, чем все, что мы могли бы придумать. Однако мы можем аппроксимировать перечисление, сохраняя частное свойство, которое представляет значение перечисления, а затем ограничивая инициализацию этого свойства нашими предопределенными константами. Чтобы предотвратить создание экземпляра перечисления произвольно (без накладных расходов на проверку типов в белом списке), мы делаем конструктор закрытым.
class ShirtSize {
private $size;
private function __construct ($size) {
$this->size = $size;
}
public function equals (ShirtSize $s) {
return $this->size === $s->size;
}
public static function SMALL () { return new self(1); }
public static function MEDIUM () { return new self(2); }
public static function LARGE () { return new self(3); }
}
Тогда мы можем использовать ShirtSize вот так:
function sizeIsAvailable ($productId, ShirtSize $size) {
// business magic
}
if (sizeIsAvailable($_GET["id"], ShirtSize::LARGE())) {
echo "Available";
} else {
echo "Out of stock.";
}
$s2 = ShirtSize::SMALL();
$s3 = ShirtSize::MEDIUM();
echo $s2->equals($s3) ? "SMALL == MEDIUM" : "SMALL != MEDIUM";
Таким образом, самое большое отличие с точки зрения пользователя состоит в том, что вам нужно добавить () к имени константы.
Однако одним из недостатков является то, что === (который сравнивает равенство объектов) вернет false, когда == вернет true. По этой причине лучше всего предоставить метод equals, чтобы пользователям не приходилось помнить об использовании ==, а не === для сравнения двух значений перечисления.
Обновлено: несколько существующих ответов очень похожи, в частности: https://stackoverflow.com/a/25526473/2407870.
Я понимаю, что это очень-очень-очень старая ветка, но я думал об этом и хотел знать, что думают люди.
Примечания: Я поигрался с этим и понял, что, если я просто изменю функцию __call(), вы можете стать еще ближе к настоящему enums. Функция __call() обрабатывает все вызовы неизвестных функций. Допустим, вы хотите сделать три enums RED_LIGHT, YELLOW_LIGHT и GREEN_LIGHT. Вы можете сделать это сейчас, просто выполнив следующие действия:
$c->RED_LIGHT();
$c->YELLOW_LIGHT();
$c->GREEN_LIGHT();
После определения все, что вам нужно сделать, это снова вызвать их, чтобы получить значения:
echo $c->RED_LIGHT();
echo $c->YELLOW_LIGHT();
echo $c->GREEN_LIGHT();
и вы должны получить 0, 1 и 2. Удачи! Это также теперь на GitHub.
Обновление: я сделал так, что теперь используются функции __get() и __set(). Это позволяет вам не вызывать функцию, если вы этого не хотите. Вместо этого теперь вы можете просто сказать:
$c->RED_LIGHT;
$c->YELLOW_LIGHT;
$c->GREEN_LIGHT;
Как для создания, так и для получения ценностей. Поскольку переменные не были определены изначально, вызывается функция __get() (поскольку не указано значение), которая видит, что запись в массиве не была сделана. Таким образом, он делает запись, присваивает ей последнее заданное значение плюс один (+1), увеличивает последнее значение переменной и возвращает TRUE. Если вы установите значение:
$c->RED_LIGHT = 85;
Затем вызывается функция __set(), и последнее значение устанавливается равным новому значению плюс один (+1). Итак, теперь у нас есть довольно хороший способ делать перечисления, и их можно создавать на лету.
<?php
################################################################################
# Class ENUMS
#
# Original code by Mark Manning.
# Copyrighted (c) 2015 by Mark Manning.
# All rights reserved.
#
# This set of code is hereby placed into the free software universe
# via the GNU greater license thus placing it under the Copyleft
# rules and regulations with the following modifications:
#
# 1. You may use this work in any other work. Commercial or otherwise.
# 2. You may make as much money as you can with it.
# 3. You owe me nothing except to give me a small blurb somewhere in
# your program or maybe have pity on me and donate a dollar to
# [email protected]. :-)
#
# Blurb:
#
# PHP Class Enums by Mark Manning (markem-AT-sim1-DOT-us).
# Used with permission.
#
# Notes:
#
# VIM formatting. Set tabs to four(4) spaces.
#
################################################################################
class enums
{
private $enums;
private $clear_flag;
private $last_value;
################################################################################
# __construct(). Construction function. Optionally pass in your enums.
################################################################################
function __construct()
{
$this->enums = array();
$this->clear_flag = false;
$this->last_value = 0;
if ( func_num_args() > 0 ){
return $this->put( func_get_args() );
}
return true;
}
################################################################################
# put(). Insert one or more enums.
################################################################################
function put()
{
$args = func_get_args();
#
# Did they send us an array of enums?
# Ex: $c->put( array( "a"=>0, "b"=>1,...) );
# OR $c->put( array( "a", "b", "c",... ) );
#
if ( is_array($args[0]) ){
#
# Add them all in
#
foreach( $args[0] as $k=>$v ){
#
# Don't let them change it once it is set.
# Remove the IF statement if you want to be able to modify the enums.
#
if ( !isset($this->enums[$k]) ){
#
# If they sent an array of enums like this: "a","b","c",... then we have to
# change that to be "A"=>#. Where "#" is the current count of the enums.
#
if ( is_numeric($k) ){
$this->enums[$v] = $this->last_value++;
}
#
# Else - they sent "a"=>"A", "b"=>"B", "c"=>"C"...
#
else {
$this->last_value = $v + 1;
$this->enums[$k] = $v;
}
}
}
}
#
# Nope! Did they just sent us one enum?
#
else {
#
# Is this just a default declaration?
# Ex: $c->put( "a" );
#
if ( count($args) < 2 ){
#
# Again - remove the IF statement if you want to be able to change the enums.
#
if ( !isset($this->enums[$args[0]]) ){
$this->enums[$args[0]] = $this->last_value++;
}
#
# No - they sent us a regular enum
# Ex: $c->put( "a", "This is the first enum" );
#
else {
#
# Again - remove the IF statement if you want to be able to change the enums.
#
if ( !isset($this->enums[$args[0]]) ){
$this->last_value = $args[1] + 1;
$this->enums[$args[0]] = $args[1];
}
}
}
}
return true;
}
################################################################################
# get(). Get one or more enums.
################################################################################
function get()
{
$num = func_num_args();
$args = func_get_args();
#
# Is this an array of enums request? (ie: $c->get(array("a","b","c"...)) )
#
if ( is_array($args[0]) ){
$ary = array();
foreach( $args[0] as $k=>$v ){
$ary[$v] = $this->enums[$v];
}
return $ary;
}
#
# Is it just ONE enum they want? (ie: $c->get("a") )
#
else if ( ($num > 0) && ($num < 2) ){
return $this->enums[$args[0]];
}
#
# Is it a list of enums they want? (ie: $c->get( "a", "b", "c"...) )
#
else if ( $num > 1 ){
$ary = array();
foreach( $args as $k=>$v ){
$ary[$v] = $this->enums[$v];
}
return $ary;
}
#
# They either sent something funky or nothing at all.
#
return false;
}
################################################################################
# clear(). Clear out the enum array.
# Optional. Set the flag in the __construct function.
# After all, ENUMS are supposed to be constant.
################################################################################
function clear()
{
if ( $clear_flag ){
unset( $this->enums );
$this->enums = array();
}
return true;
}
################################################################################
# __call(). In case someone tries to blow up the class.
################################################################################
function __call( $name, $arguments )
{
if ( isset($this->enums[$name]) ){ return $this->enums[$name]; }
else if ( !isset($this->enums[$name]) && (count($arguments) > 0) ){
$this->last_value = $arguments[0] + 1;
$this->enums[$name] = $arguments[0];
return true;
}
else if ( !isset($this->enums[$name]) && (count($arguments) < 1) ){
$this->enums[$name] = $this->last_value++;
return true;
}
return false;
}
################################################################################
# __get(). Gets the value.
################################################################################
function __get($name)
{
if ( isset($this->enums[$name]) ){ return $this->enums[$name]; }
else if ( !isset($this->enums[$name]) ){
$this->enums[$name] = $this->last_value++;
return true;
}
return false;
}
################################################################################
# __set(). Sets the value.
################################################################################
function __set( $name, $value=null )
{
if ( isset($this->enums[$name]) ){ return false; }
else if ( !isset($this->enums[$name]) && !is_null($value) ){
$this->last_value = $value + 1;
$this->enums[$name] = $value;
return true;
}
else if ( !isset($this->enums[$name]) && is_null($value) ){
$this->enums[$name] = $this->last_value++;
return true;
}
return false;
}
################################################################################
# __destruct(). Deconstruct the class. Remove the list of enums.
################################################################################
function __destruct()
{
unset( $this->enums );
$this->enums = null;
return true;
}
}
#
# Test code
#
# $c = new enums();
# $c->RED_LIGHT(85);
# $c->YELLOW_LIGHT = 23;
# $c->GREEN_LIGHT;
#
# echo $c->RED_LIGHT . "\n";
# echo $c->YELLOW_LIGHT . "\n";
# echo $c->GREEN_LIGHT . "\n";
?>
Это может быть так просто, как
enum DaysOfWeek {
Sunday,
Monday,
// ...
}
в будущем.
К вашему сведению, начиная с версии 7.1, все еще не здесь
Наступив на ответ @Brian Cline, я подумал, что могу отдать свои 5 центов
<?php
/**
* A class that simulates Enums behaviour
* <code>
* class Season extends Enum{
* const Spring = 0;
* const Summer = 1;
* const Autumn = 2;
* const Winter = 3;
* }
*
* $currentSeason = new Season(Season::Spring);
* $nextYearSeason = new Season(Season::Spring);
* $winter = new Season(Season::Winter);
* $whatever = new Season(-1); // Throws InvalidArgumentException
* echo $currentSeason.is(Season::Spring); // True
* echo $currentSeason.getName(); // 'Spring'
* echo $currentSeason.is($nextYearSeason); // True
* echo $currentSeason.is(Season::Winter); // False
* echo $currentSeason.is(Season::Spring); // True
* echo $currentSeason.is($winter); // False
* </code>
*
* Class Enum
*
* PHP Version 5.5
*/
abstract class Enum
{
/**
* Will contain all the constants of every enum that gets created to
* avoid expensive ReflectionClass usage
* @var array
*/
private static $_constCacheArray = [];
/**
* The value that separates this instance from the rest of the same class
* @var mixed
*/
private $_value;
/**
* The label of the Enum instance. Will take the string name of the
* constant provided, used for logging and human readable messages
* @var string
*/
private $_name;
/**
* Creates an enum instance, while makes sure that the value given to the
* enum is a valid one
*
* @param mixed $value The value of the current
*
* @throws \InvalidArgumentException
*/
public final function __construct($value)
{
$constants = self::_getConstants();
if (count($constants) !== count(array_unique($constants))) {
throw new \InvalidArgumentException('Enums cannot contain duplicate constant values');
}
if ($name = array_search($value, $constants)) {
$this->_value = $value;
$this->_name = $name;
} else {
throw new \InvalidArgumentException('Invalid enum value provided');
}
}
/**
* Returns the constant name of the current enum instance
*
* @return string
*/
public function getName()
{
return $this->_name;
}
/**
* Returns the value of the current enum instance
*
* @return mixed
*/
public function getValue()
{
return $this->_value;
}
/**
* Checks whether this enum instance matches with the provided one.
* This function should be used to compare Enums at all times instead
* of an identity comparison
* <code>
* // Assuming EnumObject and EnumObject2 both extend the Enum class
* // and constants with such values are defined
* $var = new EnumObject('test');
* $var2 = new EnumObject('test');
* $var3 = new EnumObject2('test');
* $var4 = new EnumObject2('test2');
* echo $var->is($var2); // true
* echo $var->is('test'); // true
* echo $var->is($var3); // false
* echo $var3->is($var4); // false
* </code>
*
* @param mixed|Enum $enum The value we are comparing this enum object against
* If the value is instance of the Enum class makes
* sure they are instances of the same class as well,
* otherwise just ensures they have the same value
*
* @return bool
*/
public final function is($enum)
{
// If we are comparing enums, just make
// sure they have the same toString value
if (is_subclass_of($enum, __CLASS__)) {
return get_class($this) === get_class($enum)
&& $this->getValue() === $enum->getValue();
} else {
// Otherwise assume $enum is the value we are comparing against
// and do an exact comparison
return $this->getValue() === $enum;
}
}
/**
* Returns the constants that are set for the current Enum instance
*
* @return array
*/
private static function _getConstants()
{
if (self::$_constCacheArray == null) {
self::$_constCacheArray = [];
}
$calledClass = get_called_class();
if (!array_key_exists($calledClass, self::$_constCacheArray)) {
$reflect = new \ReflectionClass($calledClass);
self::$_constCacheArray[$calledClass] = $reflect->getConstants();
}
return self::$_constCacheArray[$calledClass];
}
}
почему-то не могу назвать это функциями. Это говорит мне, что такие функции не объявлены. Что я делаю не так? [базовый класс Enum находится в другом файле, я использую include('enums.php');]. По какой-то причине он не видит функций, объявленных в Enum для дочерних классов ...
Также ... как установить его из строки? что-то вроде $currentSeason.set("Spring");
Недавно я разработал простую библиотеку для PHP Enums: https://github.com/dnl-blkv/simple-php-enum
На момент написания этого ответа он все еще находится на стадии предварительного выпуска, но уже полностью функционален, хорошо документирован и опубликован на Packagist.
Это может быть удобным вариантом, если вы ищете простые в реализации перечисления, аналогичные перечислениям C / C++.
Более простая и легкая версия, не использующая отражение:
abstract class enum {
private function __construct() {}
static function has($const) {
$name = get_called_class();
return defined("$name::$const");
}
static function value($const) {
$name = get_called_class();
return defined("$name::$const")? constant("$name::$const") : false;
}
}
Использование:
class requestFormat extends enum { const HTML = 1; const JSON = 2; const XML = 3; const FORM = 4; }
echo requestFormat::value('JSON'); // 2
echo requestFormat::has('JSON'); // true
Это дает преимущество констант, а также позволяет проверять их достоверность, но в нем отсутствуют другие причудливые функции, предоставляемые более сложными решениями, учитывая этот вопрос, тем более очевидна невозможность проверки обратного значения (в приведенном выше примере вы не могу проверить, является ли «2» допустимым значением)
Если вам нужна безопасность типов и набор констант, соответствующих этому типу, один из способов - создать абстрактный класс для вашего перечисления, а затем расширить этот класс с помощью заблокированного конструктора, например:
abstract class DaysOfWeekEnum{
public function __construct(string $value){
$this->value = $value;
}
public function __toString(){
return $this->value;
}
}
class Monday extends DaysOfWeekEnum{
public function __construct(){
parent::__construct("Monday");
}
}
class Tuesday extends DaysOfWeekEnum{
public function __construct(){
parent::__construct("Tuesday");
}
}
Затем вы можете заставить свои методы взять экземпляр DaysOfWeek и передать ему экземпляр понедельника, вторника и т. д. Единственным недостатком является необходимость «обновлять» экземпляр каждый раз, когда вы хотите использовать свое перечисление, но я нахожу это того стоит.
function printWeekDay(DaysOfWeek $day){
echo "Today is $day.";
}
printWeekDay(new Monday());
Теперь вы можете использовать класс SplEnum для его создания изначально. Согласно официальной документации.
SplEnum gives the ability to emulate and create enumeration objects natively in PHP.
<?php
class Month extends SplEnum {
const __default = self::January;
const January = 1;
const February = 2;
const March = 3;
const April = 4;
const May = 5;
const June = 6;
const July = 7;
const August = 8;
const September = 9;
const October = 10;
const November = 11;
const December = 12;
}
echo new Month(Month::June) . PHP_EOL;
try {
new Month(13);
} catch (UnexpectedValueException $uve) {
echo $uve->getMessage() . PHP_EOL;
}
?>
Обратите внимание, что это расширение необходимо установить, но оно недоступно по умолчанию. Это относится к Особые типы, описанному на самом веб-сайте php. Приведенный выше пример взят с сайта PHP.
Наконец, ответ PHP 7.1+ с константами, которые нельзя изменить.
/**
* An interface that groups HTTP Accept: header Media Types in one place.
*/
interface MediaTypes
{
/**
* Now, if you have to use these same constants with another class, you can
* without creating funky inheritance / is-a relationships.
* Also, this gets around the single inheritance limitation.
*/
public const HTML = 'text/html';
public const JSON = 'application/json';
public const XML = 'application/xml';
public const TEXT = 'text/plain';
}
/**
* An generic request class.
*/
abstract class Request
{
// Why not put the constants here?
// 1) The logical reuse issue.
// 2) Single Inheritance.
// 3) Overriding is possible.
// Why put class constants here?
// 1) The constant value will not be necessary in other class families.
}
/**
* An incoming / server-side HTTP request class.
*/
class HttpRequest extends Request implements MediaTypes
{
// This class can implement groups of constants as necessary.
}
Если вы используете пространства имен, автозавершение кода должно работать.
Однако при этом вы теряете возможность скрывать константы внутри семейства классов (protected) или одного класса (private). По определению, все в Interface - это public.
Руководство по PHP: интерфейсы
Это не Java. Это работает в тех случаях, когда шаблон полиморфизма / стратегии не требуется для переопределения констант в родительском классе.
на основе: это суть
базовый класс для всех перечислений:
abstract class Enum {
protected $val;
protected function __construct($arg) {
$this->val = $arg;
}
public function __toString() {
return $this->val;
}
public function __set($arg1, $arg2) {
throw new Exception("enum does not have property");
}
public function __get($arg1) {
throw new Exception("enum does not have property");
}
// not really needed
public function __call($arg1, $arg2) {
throw new Exception("enum does not have method");
}
// not really needed
static public function __callStatic($arg1, $arg2) {
throw new Exception("enum does not have static method");
}
}
ваше перечисление:
final class MyEnum extends Enum {
static public function val1() {
return new self("val1");
}
static public function val2() {
return new self("val2");
}
static public function val3() {
return new self("val3");
}
}
Попробуй это:
$a = MyEnum::val1();
echo "1.the enum value is '$a'\n";
function consumeMyEnum(MyEnum $arg) {
return "2.the return value is '$arg'\n";
}
echo consumeMyEnum($a);
$version = explode(".", PHP_VERSION);
if ($version[0] >= 7) {
try {
echo consumeMyEnum("val1");
} catch (TypeError $e) {
echo "3.passing argument error happens (PHP 7.0 and above)\n";
}
}
echo ($a == MyEnum::val1()) ? "4.same\n" : "4.different\n";
echo ($a == MyEnum::val2()) ? "5.same\n" : "5.different\n";
$b = MyEnum::val1();
echo ($a == $b) ? "6.same\n" : "6.different\n";
echo ($a === $b) ? "7.same\n" : "7.different\n";
$c = MyEnum::val2();
echo ($a == $c) ? "8.same\n" : "8.different\n";
echo ($a === $c) ? "9.same\n" : "9.different\n";
switch ($c) {
case MyEnum::val1(): echo "10.case of 1st\n"; break;
case MyEnum::val2(): echo "11.case of 2nd\n"; break;
case MyEnum::val3(): echo "12.case of 3rd\n"; break;
}
try {
$a->prop = 10;
} catch (Exception $e) {
echo "13.set property error happens\n";
}
try {
echo $a->prop;
} catch (Exception $e) {
echo "14.get property error happens\n";
}
try {
echo $a->meth();
} catch (Exception $e) {
echo "15.method call error happens\n";
}
try {
echo MyEnum::meth();
} catch (Exception $e) {
echo "16.static method call error happens\n";
}
class Ordinary {}
echo $a instanceof MyEnum ? "17.MyEnum instance\n" : "17.not MyEnum instance\n";
echo $a instanceof Enum ? "18.Enum instance\n" : "18.not Enum instance\n";
echo $a instanceof Ordinary ? "19.Ordinary instance\n" : "19.not Ordinary instance\n";
попробуйте онлайн: Sanbox
На самом базовом уровне они будут работать следующим образом:
enum TransportMode {
case Bicycle;
case Car;
case Ship;
case Plane;
case Feet;
}
function travelCost(Vehicle $vehicle, int $distance): int
{ /* implementation */ }
$mode = TransportMode::Boat;
$bikeCost = travelCost(TransportMode::Bicycle, 90);
$boatCost = travelCost($mode, 90);
// this one would fail: (Enums are singletons, not scalars)
$failCost = travelCost('Car', 90);
По умолчанию перечисления не поддерживаются никакими скалярами. Таким образом, TransportMode::Bicycle не является 0, и вы не можете сравнивать использование > или < между перечислениями.
Но сработает следующее:
$foo = TransportMode::Car;
$bar = TransportMode::Car;
$foo === $bar; // true
$foo instanceof TransportMode; // true
$foo > $bar || $foo < $bar; // false either way
Вы также можете иметь «подкрепленные» перечисления, где каждый случай перечисления «подкреплен» либо int, либо string.
enum Metal: int {
case Gold = 1932;
case Silver = 1049;
case Lead = 1134;
case Uranium = 1905;
case Copper = 894;
}
value: Metal::Gold->value.Наконец, поддерживаемые перечисления реализуют внутренний интерфейс BackedEnum, который предоставляет два метода:
from(int|string): selftryFrom(int|string): ?selfОни почти эквивалентны, но первый вызовет исключение, если значение не найдено, а второй просто вернет null.
Enumeratin может иметь методы и, таким образом, реализовывать интерфейсы.
interface TravelCapable
{
public function travelCost(int $distance): int;
public function requiresFuel(): bool;
}
enum TransportMode: int implements TravelCapable{
case Bicycle = 10;
case Car = 1000 ;
case Ship = 800 ;
case Plane = 2000;
case Feet = 5;
public function travelCost(int $distance): int
{
return $this->value * $distance;
}
public function requiresFuel(): bool {
return match($this) {
TransportMode::Car, TransportMode::Ship, TransportMode::Plane => true,
TransportMode::Bicycle, TransportMode::Feet => false
}
}
}
И Pure Enums, и Backed Enums внутренне реализуют интерфейс UnitEnum, который включает (статический) метод UnitEnum::cases() и позволяет получить массив случаев, определенных в перечислении:
$modes = TransportMode::cases();
А теперь $modes - это:
[
TransportMode::Bicycle,
TransportMode::Car,
TransportMode::Ship,
TransportMode::Plane
TransportMode::Feet
]
Перечисления могут реализовывать свои собственные методы static, которые обычно используются для специализированных конструкторов.
Это охватывает основные. Чтобы получить все это, перейдите к соответствующий RFC, пока функция не будет выпущена и опубликована в документации PHP.