<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/language.oop5.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'zh',
  ),
  'this' => 
  array (
    0 => 'language.oop5.basic.php',
    1 => '基本概念',
    2 => '基本概念',
  ),
  'up' => 
  array (
    0 => 'language.oop5.php',
    1 => '类与对象',
  ),
  'prev' => 
  array (
    0 => 'oop5.intro.php',
    1 => '简介',
  ),
  'next' => 
  array (
    0 => 'language.oop5.properties.php',
    1 => '属性',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'zh',
    'path' => 'language/oop5/basic.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="language.oop5.basic" class="sect1">
 <h2 class="title">基本概念</h2>

 <div class="sect2" id="language.oop5.basic.class">
  <h3 class="title">class</h3>
  <p class="para">
   每个类的定义都以关键字 <code class="literal">class</code>
   开头，后面跟着类名，后面跟着一对花括号，里面包含有类的属性与方法的定义。
  </p>
  <p class="para">
   类名可以是任何不是 PHP <a href="reserved.php" class="link">保留字</a>
   的有效标签。自 PHP 8.4.0 起，弃用使用单个下划线 <code class="literal">_</code>
   作为类名。有效类名以字母或下划线开头，后面跟着若干字母、数字或下划线。以正则表达式表示为
   <code class="code">^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$</code>。
  </p>
  <p class="para">
   一个类可以包含有属于自己的 <a href="language.oop5.constants.php" class="link">常量</a>，<a href="language.oop5.properties.php" class="link">变量</a>（称为“属性”）以及函数（称为“方法”）。
  </p>
  <div class="example" id="example-1">
   <p><strong>示例 #1 简单的类定义</strong></p>
   <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">class </span><span style="color: #0000BB">SimpleClass<br /></span><span style="color: #007700">{<br />    </span><span style="color: #FF8000">// 声明属性<br />    </span><span style="color: #007700">public </span><span style="color: #0000BB">$var </span><span style="color: #007700">= </span><span style="color: #DD0000">'a default value'</span><span style="color: #007700">;<br /><br />    </span><span style="color: #FF8000">// 声明方法<br />    </span><span style="color: #007700">public function </span><span style="color: #0000BB">displayVar</span><span style="color: #007700">() {<br />        echo </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">var</span><span style="color: #007700">;<br />    }<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
  <p class="para">
   当一个方法在类定义内部被调用时，有一个可用的伪变量
   <var class="varname">$this</var>。<var class="varname">$this</var>
   是一个到当前对象的引用。
  </p>
  <div class="warning"><strong class="warning">警告</strong>
      <p class="para">
          以静态方式去调用一个非静态方法，将会抛出一个
          <span class="classname"><a href="class.error.php" class="classname">Error</a></span>。
          在 PHP 8.0.0 之前版本中，将会产生一个废弃通知，同时
          <var class="varname">$this</var> 将会被声明为未定义。
      </p>
   <div class="example" id="language.oop5.basic.class.this">
    <p><strong>示例 #2 使用 <var class="varname">$this</var> 伪变量的示例</strong></p>
    <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">class </span><span style="color: #0000BB">A<br /></span><span style="color: #007700">{<br />    function </span><span style="color: #0000BB">foo</span><span style="color: #007700">()<br />    {<br />        if (isset(</span><span style="color: #0000BB">$this</span><span style="color: #007700">)) {<br />            echo </span><span style="color: #DD0000">'$this is defined ('</span><span style="color: #007700">;<br />            echo </span><span style="color: #0000BB">get_class</span><span style="color: #007700">(</span><span style="color: #0000BB">$this</span><span style="color: #007700">);<br />            echo </span><span style="color: #DD0000">")\n"</span><span style="color: #007700">;<br />        } else {<br />            echo </span><span style="color: #DD0000">"\$this is not defined.\n"</span><span style="color: #007700">;<br />        }<br />    }<br />}<br /><br />class </span><span style="color: #0000BB">B<br /></span><span style="color: #007700">{<br />    function </span><span style="color: #0000BB">bar</span><span style="color: #007700">()<br />    {<br />        </span><span style="color: #0000BB">A</span><span style="color: #007700">::</span><span style="color: #0000BB">foo</span><span style="color: #007700">();<br />    }<br />}<br /><br /></span><span style="color: #0000BB">$a </span><span style="color: #007700">= new </span><span style="color: #0000BB">A</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">$a</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">foo</span><span style="color: #007700">();<br /><br /></span><span style="color: #0000BB">A</span><span style="color: #007700">::</span><span style="color: #0000BB">foo</span><span style="color: #007700">();<br /><br /></span><span style="color: #0000BB">$b </span><span style="color: #007700">= new </span><span style="color: #0000BB">B</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">$b</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bar</span><span style="color: #007700">();<br /><br /></span><span style="color: #0000BB">B</span><span style="color: #007700">::</span><span style="color: #0000BB">bar</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例在 PHP 7 中的输出：</p></div>
     <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
$this is defined (A)

Deprecated: Non-static method A::foo() should not be called statically in %s  on line 27
$this is not defined.

Deprecated: Non-static method A::foo() should not be called statically in %s  on line 20
$this is not defined.

Deprecated: Non-static method B::bar() should not be called statically in %s  on line 32

Deprecated: Non-static method A::foo() should not be called statically in %s  on line 20
$this is not defined.
</pre></div>
     </div>
     <div class="example-contents"><p>以上示例在 PHP 8 中的输出：</p></div>
     <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
$this is defined (A)

Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27
Stack trace:
#0 {main}
  thrown in %s  on line 27
</pre></div>
     </div>
    </div>
   </div>

   <div class="sect3" id="language.oop5.basic.class.readonly">
    <h4 class="title">只读类</h4>
    <p class="para">
     自 PHP 8.2.0 起，可以使用 <span class="modifier">readonly</span> 修饰符来标记类。将类标记为 <span class="modifier">readonly</span>
     只会向每个声明的属性添加 <a href="language.oop5.properties.php#language.oop5.properties.readonly-properties" class="link"><span class="modifier">readonly</span>
     修饰符</a>并禁止创建<a href="language.oop5.properties.php#language.oop5.properties.dynamic-properties" class="link">动态属性</a>。此外，不能通过使用
     <span class="classname"><a href="class.allowdynamicproperties.php" class="classname">AllowDynamicProperties</a></span> 注解来添加对后者的支持。尝试这样做会触发编译错误。
    </p>
    <div class="informalexample">
     <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">#[</span><span style="color: #0000BB">\AllowDynamicProperties</span><span style="color: #007700">]<br />readonly class </span><span style="color: #0000BB">Foo </span><span style="color: #007700">{<br />}<br /><br /></span><span style="color: #FF8000">// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

    </div>

    <p class="para">
     由于无类型的属性和静态属性不能用 <code class="literal">readonly</code> 修饰符，所以 readonly 也不会对其声明：
    </p>
    <div class="informalexample">
     <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">readonly class </span><span style="color: #0000BB">Foo<br /></span><span style="color: #007700">{<br />    public </span><span style="color: #0000BB">$bar</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #FF8000">// Fatal error: Readonly property Foo::$bar must have type<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">readonly class </span><span style="color: #0000BB">Foo<br /></span><span style="color: #007700">{<br />    public static </span><span style="color: #0000BB">int $bar</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #FF8000">// Fatal error: Readonly class Foo cannot declare static properties<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

    </div>
    <p class="para">
     仅当子类也是 <span class="modifier">readonly</span> 类时，才可以<a href="language.oop5.basic.php#language.oop5.basic.extends" class="link">继承</a> <span class="modifier">readonly</span> 类。
    </p>
   </div>
  </div>

 <div class="sect2" id="language.oop5.basic.new">
  <h3 class="title">new</h3>
  <p class="para">
   要创建一个类的实例，必须使用 <code class="literal">new</code>
   关键字。当创建新对象时该对象总是被赋值，除非该对象定义了 <a href="language.oop5.decon.php" class="link">构造函数</a> 并且在出错时抛出了一个 <a href="language.exceptions.php" class="link">异常</a>。类应在被实例化之前定义（某些情况下则必须这样）。
  </p>
  <p class="para">
   如果一个变量包含一个类名的 <span class="type"><a href="language.types.string.php" class="type string">string</a></span> 和 <code class="literal">new</code> 时，将创建该类的一个新实例。
   如果该类属于一个命名空间，则必须使用其完整名称。
  </p>

   <blockquote class="note"><p><strong class="note">注意</strong>: 
    <p class="para">
     如果没有参数要传递给类的构造函数，类名后的括号则可以省略掉。
    </p>
   </p></blockquote>

  <div class="example" id="example-2">
   <p><strong>示例 #3 创建实例</strong></p>
   <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">class </span><span style="color: #0000BB">SimpleClass </span><span style="color: #007700">{<br />}<br /><br /></span><span style="color: #0000BB">$instance </span><span style="color: #007700">= new </span><span style="color: #0000BB">SimpleClass</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$instance</span><span style="color: #007700">);<br /><br /></span><span style="color: #FF8000">// 也可以这样做：<br /></span><span style="color: #0000BB">$className </span><span style="color: #007700">= </span><span style="color: #DD0000">'SimpleClass'</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$instance </span><span style="color: #007700">= new </span><span style="color: #0000BB">$className</span><span style="color: #007700">(); </span><span style="color: #FF8000">// new SimpleClass()<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$instance</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
  <p class="para">
    PHP 8.0.0 起，支持任意表达式中使用 <code class="literal">new</code>。如果表达式生成一个 
    <span class="type"><a href="language.types.string.php" class="type string">string</a></span>，这将允许更复杂的实例化。表达式必须使用括号括起来。
   </p>
   <div class="example" id="example-3">
    <p><strong>示例 #4 使用任意表达式创建实例</strong></p>
    <div class="example-contents"><p>
     在下列示例中，我们展示了多个生成类名的任意有效表达式的示例。展示了函数调用，string 连接和 <strong><code>::class</code></strong> 常量。
    </p></div>
    <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">ClassA </span><span style="color: #007700">extends </span><span style="color: #0000BB">\stdClass </span><span style="color: #007700">{}<br />class </span><span style="color: #0000BB">ClassB </span><span style="color: #007700">extends </span><span style="color: #0000BB">\stdClass </span><span style="color: #007700">{}<br />class </span><span style="color: #0000BB">ClassC </span><span style="color: #007700">extends </span><span style="color: #0000BB">ClassB </span><span style="color: #007700">{}<br />class </span><span style="color: #0000BB">ClassD </span><span style="color: #007700">extends </span><span style="color: #0000BB">ClassA </span><span style="color: #007700">{}<br /><br />function </span><span style="color: #0000BB">getSomeClass</span><span style="color: #007700">(): </span><span style="color: #0000BB">string<br /></span><span style="color: #007700">{<br />    return </span><span style="color: #DD0000">'ClassA'</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(new (</span><span style="color: #0000BB">getSomeClass</span><span style="color: #007700">()));<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(new (</span><span style="color: #DD0000">'Class' </span><span style="color: #007700">. </span><span style="color: #DD0000">'B'</span><span style="color: #007700">));<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(new (</span><span style="color: #DD0000">'Class' </span><span style="color: #007700">. </span><span style="color: #DD0000">'C'</span><span style="color: #007700">));<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(new (</span><span style="color: #0000BB">ClassD</span><span style="color: #007700">::class));<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
    </div>

    <div class="example-contents"><p>以上示例在 PHP 8 中的输出：</p></div>
    <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
object(ClassA)#1 (0) {
}
object(ClassB)#1 (0) {
}
object(ClassC)#1 (0) {
}
object(ClassD)#1 (0) {
}

</pre></div>
    </div>
   </div>
   <p class="para">
   在类定义内部，可以用 <code class="literal">new self</code> 和 <code class="literal">new parent</code> 创建新对象。
  </p>
  <p class="para">
   当把一个对象已经创建的实例赋给一个新变量时，新变量会访问同一个实例，就和用该对象赋值一样。此行为和给函数传递入实例时一样。可以用
   <a href="language.oop5.cloning.php" class="link">克隆</a> 给一个已创建的对象建立一个新实例。
  </p>
   <div class="example" id="example-4">
    <p><strong>示例 #5 对象赋值</strong></p>
    <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">class </span><span style="color: #0000BB">SimpleClass </span><span style="color: #007700">{<br />    public </span><span style="color: #0000BB">string $var</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">$instance </span><span style="color: #007700">= new </span><span style="color: #0000BB">SimpleClass</span><span style="color: #007700">();<br /><br /></span><span style="color: #0000BB">$assigned   </span><span style="color: #007700">=  </span><span style="color: #0000BB">$instance</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$reference  </span><span style="color: #007700">=&amp; </span><span style="color: #0000BB">$instance</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$instance</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">var </span><span style="color: #007700">= </span><span style="color: #DD0000">'$assigned will have this value'</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$instance </span><span style="color: #007700">= </span><span style="color: #0000BB">null</span><span style="color: #007700">; </span><span style="color: #FF8000">// $instance 和 $reference 变为 null<br /><br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$instance</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$reference</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$assigned</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
    </div>

    <div class="example-contents"><p>以上示例会输出：</p></div>
    <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
NULL
NULL
object(SimpleClass)#1 (1) {
   [&quot;var&quot;]=&gt;
     string(30) &quot;$assigned will have this value&quot;
}
</pre></div>
    </div>
   </div>
  <p class="para">
      有几种方法可以创建一个对象的实例。
  </p>
  <div class="example" id="example-5">
   <p><strong>示例 #6 创建新对象</strong></p>
   <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Test<br /></span><span style="color: #007700">{<br />    public static function </span><span style="color: #0000BB">getNew</span><span style="color: #007700">()<br />    {<br />        return new static();<br />    }<br />}<br /><br />class </span><span style="color: #0000BB">Child </span><span style="color: #007700">extends </span><span style="color: #0000BB">Test </span><span style="color: #007700">{}<br /><br /></span><span style="color: #0000BB">$obj1 </span><span style="color: #007700">= new </span><span style="color: #0000BB">Test</span><span style="color: #007700">(); </span><span style="color: #FF8000">// 通过类名<br /></span><span style="color: #0000BB">$obj2 </span><span style="color: #007700">= new </span><span style="color: #0000BB">$obj1</span><span style="color: #007700">(); </span><span style="color: #FF8000">// 通过包含对象的变量<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$obj1 </span><span style="color: #007700">!== </span><span style="color: #0000BB">$obj2</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">$obj3 </span><span style="color: #007700">= </span><span style="color: #0000BB">Test</span><span style="color: #007700">::</span><span style="color: #0000BB">getNew</span><span style="color: #007700">(); </span><span style="color: #FF8000">// 通过类方法<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$obj3 </span><span style="color: #007700">instanceof </span><span style="color: #0000BB">Test</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">$obj4 </span><span style="color: #007700">= </span><span style="color: #0000BB">Child</span><span style="color: #007700">::</span><span style="color: #0000BB">getNew</span><span style="color: #007700">(); </span><span style="color: #FF8000">// 通过子类方法<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$obj4 </span><span style="color: #007700">instanceof </span><span style="color: #0000BB">Child</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
    </div>

    <div class="example-contents"><p>以上示例会输出：</p></div>
    <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
bool(true)
bool(true)
bool(true)
</pre></div>
    </div>
   </div>

   <p class="para">
    可以通过一个表达式来访问新创建对象的成员：
   </p>
   <div class="example" id="example-6">
    <p><strong>示例 #7 访问新创建对象的成员</strong></p>
    <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">echo (new </span><span style="color: #0000BB">DateTime</span><span style="color: #007700">())-&gt;</span><span style="color: #0000BB">format</span><span style="color: #007700">(</span><span style="color: #DD0000">'Y'</span><span style="color: #007700">), </span><span style="color: #0000BB">PHP_EOL</span><span style="color: #007700">;<br /><br /></span><span style="color: #FF8000">// 从 PHP 8.4.0 起，周围的括号是可选的<br /></span><span style="color: #007700">echo new </span><span style="color: #0000BB">DateTime</span><span style="color: #007700">()-&gt;</span><span style="color: #0000BB">format</span><span style="color: #007700">(</span><span style="color: #DD0000">'Y'</span><span style="color: #007700">), </span><span style="color: #0000BB">PHP_EOL</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
    </div>

    <div class="example-contents"><p>以上示例的输出类似于：</p></div>
    <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
2025
2025
</pre></div>
    </div>
   </div>

   <blockquote class="note"><p><strong class="note">注意</strong>: 
    <span class="simpara">
     在 PHP 7.1 之前，如果类没有定义构造函数，则不对参数进行执行。
    </span>
   </p></blockquote>
  </div>

  <div class="sect2" id="language.oop5.basic.properties-methods">
   <h3 class="title">属性和方法</h3>
   <p class="para">
    类的属性和方法存在于不同的“命名空间”中，这意味着同一个类的属性和方法可以使用同样的名字。
    在类中访问属性和调用方法使用同样的操作符，具体是访问一个属性还是调用一个方法，取决于你的上下文，即用法是变量访问还是函数调用。
   </p>
   <div class="example" id="example-7">
    <p><strong>示例 #8 访问类属性 vs. 调用类方法</strong></p>
    <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Foo<br /></span><span style="color: #007700">{<br />    public </span><span style="color: #0000BB">$bar </span><span style="color: #007700">= </span><span style="color: #DD0000">'property'</span><span style="color: #007700">;<br /><br />    public function </span><span style="color: #0000BB">bar</span><span style="color: #007700">() {<br />        return </span><span style="color: #DD0000">'method'</span><span style="color: #007700">;<br />    }<br />}<br /><br /></span><span style="color: #0000BB">$obj </span><span style="color: #007700">= new </span><span style="color: #0000BB">Foo</span><span style="color: #007700">();<br />echo </span><span style="color: #0000BB">$obj</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bar</span><span style="color: #007700">, </span><span style="color: #0000BB">PHP_EOL</span><span style="color: #007700">, </span><span style="color: #0000BB">$obj</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bar</span><span style="color: #007700">(), </span><span style="color: #0000BB">PHP_EOL</span><span style="color: #007700">;</span></span></code></div>
    </div>

    <div class="example-contents"><p>以上示例会输出：</p></div>
    <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
property
method
</pre></div>
    </div>
   </div>
   <p class="para">
    这意味着，如果你的类属性被分配给一个
    <a href="functions.anonymous.php" class="link">匿名函数</a>
    你将无法直接调用它。因为访问类属性的优先级要更高，在此场景下需要用括号包裹起来调用。
   </p>
   <div class="example" id="example-8">
    <p><strong>示例 #9 类属性被赋值为匿名函数时的调用示例</strong></p>
    <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Foo<br /></span><span style="color: #007700">{<br />    public </span><span style="color: #0000BB">$bar</span><span style="color: #007700">;<br /><br />    public function </span><span style="color: #0000BB">__construct</span><span style="color: #007700">() {<br />        </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bar </span><span style="color: #007700">= function() {<br />            return </span><span style="color: #0000BB">42</span><span style="color: #007700">;<br />        };<br />    }<br />}<br /><br /></span><span style="color: #0000BB">$obj </span><span style="color: #007700">= new </span><span style="color: #0000BB">Foo</span><span style="color: #007700">();<br /><br />echo (</span><span style="color: #0000BB">$obj</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bar</span><span style="color: #007700">)(), </span><span style="color: #0000BB">PHP_EOL</span><span style="color: #007700">;</span></span></code></div>
    </div>

    <div class="example-contents"><p>以上示例会输出：</p></div>
    <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
42
</pre></div>
    </div>
   </div>
  </div>

 <div class="sect2" id="language.oop5.basic.extends">
  
  
  <h3 class="title">extends</h3>
  <p class="para">
   一个类可以在声明中用 <code class="literal">extends</code>
   关键字继承另一个类的方法和属性。PHP 不支持多重继承，一个类只能继承一个基类。
  </p>
  <p class="para">
   被继承的方法和属性可以通过用同样的名字重新声明被覆盖。但是如果父类定义方法或者常量时使用了
   <a href="language.oop5.final.php" class="link">final</a>，则不可被覆盖。可以通过
   <a href="language.oop5.paamayim-nekudotayim.php" class="link">parent::</a>
   来访问被覆盖的方法或属性。
  </p>
  <blockquote class="note"><p><strong class="note">注意</strong>: 
    <span class="simpara">
     从 PHP 8.1.0 起，常量可以声明为 final。
    </span>
   </p></blockquote>
  <div class="example" id="example-9">
   <p><strong>示例 #10 简单的类继承</strong></p>
    <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">class </span><span style="color: #0000BB">SimpleClass<br /></span><span style="color: #007700">{<br />    function </span><span style="color: #0000BB">displayVar</span><span style="color: #007700">()<br />    {<br />        echo </span><span style="color: #DD0000">"Parent class\n"</span><span style="color: #007700">;<br />    }<br />}<br /><br />class </span><span style="color: #0000BB">ExtendClass </span><span style="color: #007700">extends </span><span style="color: #0000BB">SimpleClass<br /></span><span style="color: #007700">{<br />    </span><span style="color: #FF8000">// 同样名称的方法，将会覆盖父类的方法<br />    </span><span style="color: #007700">function </span><span style="color: #0000BB">displayVar</span><span style="color: #007700">()<br />    {<br />        echo </span><span style="color: #DD0000">"Extending class\n"</span><span style="color: #007700">;<br />        </span><span style="color: #0000BB">parent</span><span style="color: #007700">::</span><span style="color: #0000BB">displayVar</span><span style="color: #007700">();<br />    }<br />}<br /><br /></span><span style="color: #0000BB">$extended </span><span style="color: #007700">= new </span><span style="color: #0000BB">ExtendClass</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">$extended</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">displayVar</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
    </div>

    <div class="example-contents"><p>以上示例会输出：</p></div>
    <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
Extending class
Parent Class
</pre></div>
    </div>
   </div>

   <div class="sect3" id="language.oop.lsp">
    <h4 class="title">签名兼容性规则</h4>
    <p class="para">
     当覆盖（override）方法时，签名必须兼容父类方法。否则会导致 Fatal 错误，PHP 8.0.0 之前是 <strong><code><a href="errorfunc.constants.php#constant.e-warning">E_WARNING</a></code></strong> 级错误。
     兼容签名是指：遵守<a href="language.oop5.variance.php" class="link">协变与逆变</a>规则；强制参数可以改为可选参数；添加的新参数只能是可选；放宽可见性而不是继续限制。这就是著名的里氏替换原则（Liskov
     Substitution Principle），简称 LSP。不过<a href="language.oop5.decon.php#language.oop5.decon.constructor" class="link">构造方法</a>和私有（<code class="literal">private</code>）方法不需要遵循签名兼容规则，哪怕签名不匹配也不会导致 Fatal 错误。
    </p>
    <div class="example" id="example-10">
     <p><strong>示例 #11 兼容子类方法</strong></p>
     <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Base<br /></span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">int $a</span><span style="color: #007700">) {<br />        echo </span><span style="color: #DD0000">"Valid\n"</span><span style="color: #007700">;<br />    }<br />}<br /><br />class </span><span style="color: #0000BB">Extend1 </span><span style="color: #007700">extends </span><span style="color: #0000BB">Base<br /></span><span style="color: #007700">{<br />    function </span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">int $a </span><span style="color: #007700">= </span><span style="color: #0000BB">5</span><span style="color: #007700">)<br />    {<br />        </span><span style="color: #0000BB">parent</span><span style="color: #007700">::</span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">$a</span><span style="color: #007700">);<br />    }<br />}<br /><br />class </span><span style="color: #0000BB">Extend2 </span><span style="color: #007700">extends </span><span style="color: #0000BB">Base<br /></span><span style="color: #007700">{<br />    function </span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">int $a</span><span style="color: #007700">, </span><span style="color: #0000BB">$b </span><span style="color: #007700">= </span><span style="color: #0000BB">5</span><span style="color: #007700">)<br />    {<br />        </span><span style="color: #0000BB">parent</span><span style="color: #007700">::</span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">$a</span><span style="color: #007700">);<br />    }<br />}<br /><br /></span><span style="color: #0000BB">$extended1 </span><span style="color: #007700">= new </span><span style="color: #0000BB">Extend1</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">$extended1</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">foo</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">$extended2 </span><span style="color: #007700">= new </span><span style="color: #0000BB">Extend2</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">$extended2</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">1</span><span style="color: #007700">);</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例会输出：</p></div>
     <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
Valid
Valid
</pre></div>
     </div>
    </div>

    <p class="para">
     下面演示子类与父类方法不兼容的例子：通过移除参数、修改可选参数为必填参数。
    </p>
    <div class="example" id="example-11">
     <p><strong>示例 #12 子类方法移除参数后，导致 Fatal 错误</strong></p>
     <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Base<br /></span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">int $a </span><span style="color: #007700">= </span><span style="color: #0000BB">5</span><span style="color: #007700">) {<br />        echo </span><span style="color: #DD0000">"Valid\n"</span><span style="color: #007700">;<br />    }<br />}<br /><br />class </span><span style="color: #0000BB">Extend </span><span style="color: #007700">extends </span><span style="color: #0000BB">Base<br /></span><span style="color: #007700">{<br />    function </span><span style="color: #0000BB">foo</span><span style="color: #007700">()<br />    {<br />        </span><span style="color: #0000BB">parent</span><span style="color: #007700">::</span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">1</span><span style="color: #007700">);<br />    }<br />}</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例在 PHP 8 中的输出类似于：</p></div>
     <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13
</pre></div>
     </div>
    </div>
    <div class="example" id="example-12">
     <p><strong>示例 #13 子类方法把可选参数改成强制参数，导致 Fatal 错误</strong></p>
     <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Base<br /></span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">int $a </span><span style="color: #007700">= </span><span style="color: #0000BB">5</span><span style="color: #007700">) {<br />        echo </span><span style="color: #DD0000">"Valid\n"</span><span style="color: #007700">;<br />    }<br />}<br /><br />class </span><span style="color: #0000BB">Extend </span><span style="color: #007700">extends </span><span style="color: #0000BB">Base<br /></span><span style="color: #007700">{<br />    function </span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">int $a</span><span style="color: #007700">)<br />    {<br />        </span><span style="color: #0000BB">parent</span><span style="color: #007700">::</span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">$a</span><span style="color: #007700">);<br />    }<br />}</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例在 PHP 8 中的输出类似于：</p></div>
     <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
</pre></div>
     </div>
    </div>

    <div class="warning"><strong class="warning">警告</strong>
     <p class="para">
      重命名子类方法的参数名称也是签名兼容的。
      然而我们不建议这样做，因为使用<a href="functions.arguments.php#functions.named-arguments" class="link">命名参数</a>时，
      这种做法会导致运行时的 <span class="classname"><a href="class.error.php" class="classname">Error</a></span>。
     </p>
     <div class="example" id="example-13">
      <p><strong>示例 #14 在子类中重命一个命名参数，导致 Error</strong></p>
      <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">A </span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">test</span><span style="color: #007700">(</span><span style="color: #0000BB">$foo</span><span style="color: #007700">, </span><span style="color: #0000BB">$bar</span><span style="color: #007700">) {}<br />}<br /><br />class </span><span style="color: #0000BB">B </span><span style="color: #007700">extends </span><span style="color: #0000BB">A </span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">test</span><span style="color: #007700">(</span><span style="color: #0000BB">$a</span><span style="color: #007700">, </span><span style="color: #0000BB">$b</span><span style="color: #007700">) {}<br />}<br /><br /></span><span style="color: #0000BB">$obj </span><span style="color: #007700">= new </span><span style="color: #0000BB">B</span><span style="color: #007700">;<br /><br /></span><span style="color: #FF8000">// 按 A::test() 的签名约定传入参数<br /></span><span style="color: #0000BB">$obj</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">test</span><span style="color: #007700">(</span><span style="color: #0000BB">foo</span><span style="color: #007700">: </span><span style="color: #DD0000">"foo"</span><span style="color: #007700">, </span><span style="color: #0000BB">bar</span><span style="color: #007700">: </span><span style="color: #DD0000">"bar"</span><span style="color: #007700">); </span><span style="color: #FF8000">// ERROR!</span></span></code></div>
      </div>

      <div class="example-contents"><p>以上示例的输出类似于：</p></div>
      <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14
Stack trace:
#0 {main}
  thrown in /in/XaaeN on line 14
</pre></div>
      </div>
     </div>
    </div>
   </div>
  </div>

  <div class="sect2" id="language.oop5.basic.class.class">
   <h3 class="title">::class</h3>

   <p class="para">
    关键词 <code class="literal">class</code> 也可用于类名的解析。使用 <code class="literal">ClassName::class</code>
    可以获取包含类 <code class="literal">ClassName</code> 的完全限定名称。这对使用了
    <a href="language.namespaces.php" class="link">命名空间</a> 的类尤其有用。
   </p>
   <p class="para">
    <div class="example" id="language.oop5.basic.class.class.name">
     <p><strong>示例 #15 类名的解析</strong></p>
     <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">namespace </span><span style="color: #0000BB">NS </span><span style="color: #007700">{<br />    class </span><span style="color: #0000BB">ClassName </span><span style="color: #007700">{<br />    }<br /><br />    echo </span><span style="color: #0000BB">ClassName</span><span style="color: #007700">::class;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例会输出：</p></div>
     <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
NS\ClassName
</pre></div>
     </div>
    </div>
   </p>
   <blockquote class="note"><p><strong class="note">注意</strong>: 
    <p class="para">使用 <code class="literal">::class</code>
     解析类名操作会在底层编译时进行。这意味着在执行该操作时，类还没有被加载。
     因此，即使要调用的类不存在，类名也会被展示。在此种场景下，并不会发生错误。
    </p>
    <div class="example" id="language.oop5.basic.class.class.fail">
     <p><strong>示例 #16 解析不存在的类名</strong></p>
     <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">print </span><span style="color: #0000BB">Does\Not\Exist</span><span style="color: #007700">::class;<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     <div class="example-contents"><p>以上示例会输出：</p></div>
     <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
Does\Not\Exist
</pre></div>
     </div>
    </div>
   </p></blockquote>
   <p class="para">
    自 PHP 8.0.0 起，<code class="literal">::class</code> 也可用于对象。
    与上述情况不同，此时解析将会在运行时进行。此操作的运行结果和在对象上调用
    <span class="function"><a href="function.get-class.php" class="function">get_class()</a></span> 相同。
   </p>
   <div class="example" id="language.oop5.basic.class.class.object">
    <p><strong>示例 #17 类名解析</strong></p>
    <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">namespace </span><span style="color: #0000BB">NS </span><span style="color: #007700">{<br />    class </span><span style="color: #0000BB">ClassName </span><span style="color: #007700">{<br />    }<br /><br />    </span><span style="color: #0000BB">$c </span><span style="color: #007700">= new </span><span style="color: #0000BB">ClassName</span><span style="color: #007700">();<br />    print </span><span style="color: #0000BB">$c</span><span style="color: #007700">::class;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
    </div>

    <div class="example-contents"><p>以上示例会输出：</p></div>
    <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
NS\ClassName
</pre></div>
    </div>
   </div>
  </div>
 <div class="sect2" id="language.oop5.basic.nullsafe">
  <h3 class="title">Nullsafe 方法和属性</h3>
  <p class="para">
   自 PHP 8.0.0 起，类属性和方法可以通过 &quot;nullsafe&quot; 操作符访问：
   <code class="literal">?-&gt;</code>。
   除了一处不同，nullsafe 操作符和以上原来的属性、方法访问是一致的：
   对象引用解析（dereference）为 <strong><code><a href="reserved.constants.php#constant.null">null</a></code></strong> 时不抛出异常，而是返回 <strong><code><a href="reserved.constants.php#constant.null">null</a></code></strong>。
   并且如果是链式调用中的一部分，剩余链条会直接跳过。
  </p>
  <p class="para">
   此操作的结果，类似于在每次访问前使用 <span class="function"><a href="function.is-null.php" class="function">is_null()</a></span>
   函数判断方法和属性是否存在，但更加简洁。
  </p>
  <p class="para">
   <div class="example" id="example-14">
    <p><strong>示例 #18 Nullsafe 操作符</strong></p>
    <div class="example-contents">
<div class="annotation-non-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #FF8000">// 自 PHP 8.0.0 起可用<br /></span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">$repository</span><span style="color: #007700">?-&gt;</span><span style="color: #0000BB">getUser</span><span style="color: #007700">(</span><span style="color: #0000BB">5</span><span style="color: #007700">)?-&gt;</span><span style="color: #0000BB">name</span><span style="color: #007700">;<br /><br /></span><span style="color: #FF8000">// 上边那行代码等价于以下代码<br /></span><span style="color: #007700">if (</span><span style="color: #0000BB">is_null</span><span style="color: #007700">(</span><span style="color: #0000BB">$repository</span><span style="color: #007700">)) {<br />    </span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">null</span><span style="color: #007700">;<br />} else {<br />    </span><span style="color: #0000BB">$user </span><span style="color: #007700">= </span><span style="color: #0000BB">$repository</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getUser</span><span style="color: #007700">(</span><span style="color: #0000BB">5</span><span style="color: #007700">);<br />    if (</span><span style="color: #0000BB">is_null</span><span style="color: #007700">(</span><span style="color: #0000BB">$user</span><span style="color: #007700">)) {<br />        </span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">null</span><span style="color: #007700">;<br />    } else {<br />        </span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">$user</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">name</span><span style="color: #007700">;<br />    }<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
    </div>

   </div>
  </p>
  <blockquote class="note"><p><strong class="note">注意</strong>: 
   <p class="para">
    仅当 null 被认为是属性或方法返回的有效和预期的可能值时，才推荐使用 nullsafe
    操作符。如果业务中需要明确指示错误，抛出异常会是更好的处理方式。
   </p>
  </p></blockquote>
 </div>
</div><?php manual_footer($setup); ?>