<?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 => 'zh',
  ),
  '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' => 'zh',
    '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 />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
  </div>


  <p class="para">
   由于有标量的条目回退（Backed）到一个更简单值，又叫回退条目（Backed Case）。
   包含所有回退条目的 Enum 又叫“回退 Enum”（Backed Enum）。
   回退 Enum 只能包含回退条目。
   纯粹 Enum 只能包含纯粹条目。
  </p>

  <p class="para">
   回退枚举仅能回退到 <code class="literal">int</code> 或 <code class="literal">string</code> 里的一种类型，
   且同时仅支持使用一种类型（就是说，不能联合 <code class="literal">int|string</code>）。
   如果枚举为标量形式，所有的条目必须明确定义唯一的标量值。
   无法自动生成标量（比如：连续的数字）。
   回退条目必须是唯一的；两个回退条目不能有相同的标量。
   然而，也可以用常量引用到条目，实际上是创建了个别名。
   参见 <a href="language.enumerations.constants.php" class="link">枚举常量</a>。
  </p>

  <p class="para">
   等值可以是常量标量表达式。PHP 8.2.0
   之前，必须是文字或文字表达式。这意味着不支持常量和常量表达式。也就是说，允许
   <code class="code">1 + 1</code>，但不允许 <code class="code">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"<br /></span><span style="color: #0000BB">?&gt;</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<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
  </div>


  <p class="para">
   回退枚举实现了内置的 <span class="interfacename"><a href="class.backedenum.php" class="interfacename">BackedEnum</a></span> interface，
   暴露了两个额外的方法：
  </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> 方法也遵循基本的严格/松散类型规则。
   系统在弱类型模式下接受传入 integer 和 string，并自动强制转换对应值。
   传入 float 也能运行，并且强制转换。
   在严格类型模式下，为 string 回退枚举的 <code class="literal">from()</code> 传入 integer 会导致
   <span class="classname"><a href="class.typeerror.php" class="classname">TypeError</a></span>，反之亦然；float 都会出现有问题。
   其他所有的参数类型，在以上所有模式中都会抛出 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">;<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
  </div>


  <p class="para">手动为回退枚举定义 <code class="literal">from()</code> 或 <code class="literal">tryFrom()</code> 方法会导致 fatal 错误。</p>

 </div><?php manual_footer($setup); ?>