<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/language.enumerations.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ru',
  ),
  'this' => 
  array (
    0 => 'language.enumerations.backed.php',
    1 => 'Типизированные перечисления',
    2 => 'Типизированные перечисления',
  ),
  'up' => 
  array (
    0 => 'language.enumerations.php',
    1 => 'Перечисления',
  ),
  'prev' => 
  array (
    0 => 'language.enumerations.basics.php',
    1 => 'Основы перечислений',
  ),
  'next' => 
  array (
    0 => 'language.enumerations.methods.php',
    1 => 'Методы перечислений',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ru',
    'path' => 'language/enumerations.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="language.enumerations.backed" class="sect1">
  <h2 class="title">Типизированные перечисления</h2>

  <p class="para">
   Варианты перечислений по умолчанию не содержат скалярного эквивалента
   и относятся к стандартным одноэлементным объектам,
   но варианты перечисления часто требуется сохранять и считывать из базы данных
   или аналогичного хранилища данных, поэтому полезно указывать для перечисления встроенный скалярный —
   и поэтому тривиально сериализуемый — эквивалент, который определили внутри.
  </p>

  <p class="para">Скалярный эквивалент перечислений определяют следующим синтаксисом:</p>

  <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">enum </span><span style="color: #0000BB">Suit</span><span style="color: #007700">: </span><span style="color: #0000BB">string<br /></span><span style="color: #007700">{<br />    case </span><span style="color: #0000BB">Hearts </span><span style="color: #007700">= </span><span style="color: #DD0000">'H'</span><span style="color: #007700">;<br />    case </span><span style="color: #0000BB">Diamonds </span><span style="color: #007700">= </span><span style="color: #DD0000">'D'</span><span style="color: #007700">;<br />    case </span><span style="color: #0000BB">Clubs </span><span style="color: #007700">= </span><span style="color: #DD0000">'C'</span><span style="color: #007700">;<br />    case </span><span style="color: #0000BB">Spades </span><span style="color: #007700">= </span><span style="color: #DD0000">'S'</span><span style="color: #007700">;<br />}</span></span></code></div>
  </div>


  <p class="para">
   Вариант со скалярным эквивалентом называется типизированным (англ. Backed Case), поскольку «поддерживается» упрощённым значением.
   Перечисление с типизированными вариантами называется «типизированным перечислением» (англ. Backed Enum).
   Типизированное перечисление допускает только типизированные варианты,
   а чистое перечисление — только чистые.
  </p>

  <p class="para">
   Перечисления допускают поддержку единственным типом — <code class="literal">int</code> или <code class="literal">string</code>,
   поэтому типы нельзя объединять: <code class="literal">int|string</code>.
   Для каждого варианта типизированного перечисления потребуется явно определить уникальный скалярный эквивалент.
   Перечисление не генерирует вариантам скалярные эквиваленты наподобие последовательных целых чисел автоматически.
   PHP проверит уникальность скалярных значений типизированных вариантов перечисления,
   поэтому двум вариантам типизированного перечисления нельзя присваивать один и тот же скалярный эквивалент.
   При этом константам разрешается ссылаться на варианты перечисления, что фактически создаёт псевдоним варианта.
   Смотрите главу «<a href="language.enumerations.constants.php" class="link">Константы перечислений</a>».
  </p>

  <p class="para">
   Эквивалентные значения для вариантов перечисления устанавливаются как константные скалярные выражения.
   До PHP 8.2.0 варианты перечислений поддерживали только литералы или литеральные выражения.
   Константы и константные выражения не поддерживались. Поэтому выражения наподобие <code class="literal">1 + 1</code> работали,
   а выражения <code class="literal">1 + SOME_CONST</code> вызывали фатальную ошибку.
  </p>

  <p class="para">
   У типизированных вариантов есть дополнительное доступное только для чтения свойство <code class="literal">value</code> —
   это значение, заданное в определении варианта.
  </p>

  <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">print </span><span style="color: #0000BB">Suit</span><span style="color: #007700">::</span><span style="color: #0000BB">Clubs</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">value</span><span style="color: #007700">;<br /></span><span style="color: #FF8000">// Конструкция выведет "C"</span></span></code></div>
   </div>


   <p class="para">
    Переменную нельзя назначать как ссылку на свойство <code class="literal">value</code>,
    чтобы свойство оставалось доступным только для чтения.
    Поэтому следующий код выдаст ошибку:
   </p>

   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$suit </span><span style="color: #007700">= </span><span style="color: #0000BB">Suit</span><span style="color: #007700">::</span><span style="color: #0000BB">Clubs</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$ref </span><span style="color: #007700">= &amp;</span><span style="color: #0000BB">$suit</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">value</span><span style="color: #007700">;<br /></span><span style="color: #FF8000">// Error: Cannot acquire reference to property Suit::$value</span></span></code></div>
   </div>


   <p class="para">
    Типизированные перечисления реализуют внутренний интерфейс <span class="interfacename"><a href="class.backedenum.php" class="interfacename">BackedEnum</a></span>,
    который даёт два дополнительных метода:
   </p>

   <ul class="simplelist">
    <li>
     <code class="literal">from(int|string): self</code> возьмёт скаляр и вернёт вариант перечисления, которому он принадлежит.
     Если вариант, который соответствует варианту перечисления, не найден, метод выбросит исключение <span class="classname"><a href="class.valueerror.php" class="classname">ValueError</a></span>.
     Это в основном полезно тогда, когда входной скаляр надёжен,
     а отсутствие значения перечисления надо рассматривать как ошибку, останавливающую приложение.
    </li>
    <li>
     <code class="literal">tryFrom(int|string): ?self</code> возьмёт скаляр и вернёт вариант перечисления, которому он принадлежит.
     Если вариант, который соответствует варианту перечисления, не найден, метод вернёт <code class="literal">null</code>.
     Это в основном полезно тогда, когда входной скаляр ненадёжен и вызывающая функция
     хочет реализовать свою обработку ошибок или логику значения по умолчанию.
    </li>
   </ul>

   <p class="para">
    Методы <code class="literal">from()</code> и <code class="literal">tryFrom()</code> следуют стандартным правилам
    слабой/строгой типизации. В режиме слабой типизации допустима передача целого числа или строки,
    система приведёт значение и найдёт вариант, который ему соответствует.
    Передача числа с плавающей точкой также будет работать и приводиться.
    В режиме строгой типизации передача целого числа в метод <code class="literal">from()</code> в перечислении
    со строковой типизацией (или наоборот) в любом случае приведёт к исключению <span class="classname"><a href="class.typeerror.php" class="classname">TypeError</a></span>,
    как и передача числа с плавающей точкой.
    Все остальные типы параметров выбросят исключение TypeError в обоих режимах.
   </p>

   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$record </span><span style="color: #007700">= </span><span style="color: #0000BB">get_stuff_from_database</span><span style="color: #007700">(</span><span style="color: #0000BB">$id</span><span style="color: #007700">);<br />print </span><span style="color: #0000BB">$record</span><span style="color: #007700">[</span><span style="color: #DD0000">'suit'</span><span style="color: #007700">];<br /><br /></span><span style="color: #0000BB">$suit </span><span style="color: #007700">=  </span><span style="color: #0000BB">Suit</span><span style="color: #007700">::</span><span style="color: #0000BB">from</span><span style="color: #007700">(</span><span style="color: #0000BB">$record</span><span style="color: #007700">[</span><span style="color: #DD0000">'suit'</span><span style="color: #007700">]);<br /></span><span style="color: #FF8000">// Недопустимые данные выбросят исключение ValueError: "X" is not a valid scalar value for enum "Suit"<br /></span><span style="color: #007700">print </span><span style="color: #0000BB">$suit</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">value</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$suit </span><span style="color: #007700">= </span><span style="color: #0000BB">Suit</span><span style="color: #007700">::</span><span style="color: #0000BB">tryFrom</span><span style="color: #007700">(</span><span style="color: #DD0000">'A'</span><span style="color: #007700">) ?? </span><span style="color: #0000BB">Suit</span><span style="color: #007700">::</span><span style="color: #0000BB">Spades</span><span style="color: #007700">;<br /></span><span style="color: #FF8000">// Недопустимые данные возвращают значение null, поэтому вместо этого будет использовано Suit::Spades.<br /></span><span style="color: #007700">print </span><span style="color: #0000BB">$suit</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">value</span><span style="color: #007700">;</span></span></code></div>
   </div>


   <p class="para">
    Ручное определение метода <code class="literal">from()</code> или <code class="literal">tryFrom()</code> в типизированных перечислениях
    приведёт к фатальной ошибке.
   </p>
 </div><?php manual_footer($setup); ?>