概述

类是一种类型,它可以包含零个或多个显式声明的成员,这些成员可以是任何组合的类常量;数据成员,称为属性;以及函数成员,称为方法。在运行时向实例添加属性的能力在动态成员部分中描述。类类型的一个对象(通常称为实例)是通过new 运算符创建的(即实例化)。

PHP 支持继承,这是一种方法,通过这种方法,派生类可以扩展和专门化一个基类(也称为父类)。PHP 中的类不是都从一个共同的祖先派生。一个抽象类是一种用于派生的基类型,但不能直接实例化。具体类是一个非抽象类。一个最终类是指其他类无法从其派生的类。

一个类可以实现一个或多个接口,每个接口都定义一个契约。接口可以有方法和常量,但没有属性。

一个类可以使用一个或多个特性,这允许一个类获得多重继承的一些好处。

一个构造函数是一种特殊方法,用于在实例创建后立即对其进行初始化。一个析构函数是一种特殊方法,用于在不再需要实例时释放资源。其他特殊方法也存在;它们在特殊方法部分中描述。

一个类的每个成员都有一个默认或显式声明的可见性,这决定了哪些源代码可以访问它们。具有private可见性的成员只能从其自身类内部访问。具有protected可见性的成员只能从其自身类内部以及继承链中高于和低于它的类内部访问。具有public可见性的成员不受限制。

一个方法的签名是该方法的类名、名称和参数列表的组合,包括参数类型声明和传递参数的方式(byRef),以及返回值是否通过byRef返回。

在基类中实现的方法和属性可以通过在派生类中重新声明它们(具有兼容签名)(见下文)来覆盖。如果覆盖方法没有兼容的签名,会发出一个非致命错误,但覆盖仍然允许。不建议对覆盖方法使用不兼容的签名。

当分配一个实例时,new返回一个指向该对象的句柄。因此,句柄的赋值不会复制对象本身。(有关浅复制和深复制的讨论,请参见克隆对象)。

虽然 PHP 支持匿名类类型,但这种类型不能使用类声明声明。相反,它必须在实例化时指定;即作为对象创建表达式的一部分。

类声明

语法

class-declaration:
   class-modifieropt   class   name   class-base-clauseopt   class-interface-clauseopt   {   class-member-declarationsopt   }

class-modifier:
   abstract
   final

class-base-clause:
   extends   qualified-name

class-interface-clause:
   implements   qualified-name
   class-interface-clause   ,   qualified-name

约束

name必须是有效的名称,并且不能是selfparent保留关键字

qualified-name必须是有效的qualified-name,并且其name元素不能是selfparent保留关键字

包含任何带有abstract修饰符的class-member-declarationsclass-declaration本身必须具有abstractclass-modifier

class-base-clause不能命名最终类。

class-base-clause中的qualified-name必须命名一个现有的类。

一个类不能直接或间接地从自身派生。

具体类必须实现class-interface-clause中指定的所有接口中的每个方法。

对于每个接口方法,相应的实现方法必须与接口方法兼容,包括以下内容

  • 如果接口方法被定义为通过byRef返回,则实现方法也应该通过byRef返回。
  • 如果接口方法是可变的,则实现方法也必须是可变的(另见下文)。
  • 实现方法的必需参数(即没有默认值)的数量不能超过接口方法的必需参数的数量(不允许添加非可选参数)。
  • 实现方法的参数总数应至少与接口方法的参数数量相同(不允许删除参数)。
  • 实现方法的每个参数必须与原型方法的对应参数兼容。
  • 如果接口方法定义了返回值类型,则实现方法必须具有相同的返回值类型。

兼容的参数定义如下

  • 参数名称无关紧要。
  • 如果参数在接口中是可选的(具有默认值),那么它应该在实现中是可选的。但是,实现可以提供不同的默认值。
  • byRef参数需要byRef实现,非byRef参数不能有byRef实现。
  • 对于没有参数类型的参数,只有没有类型的声明是兼容的。
  • 对于有类型参数,只有具有相同类型的参数是兼容的。
  • 对于可变参数,可变参数(最后一个)参数的定义应该根据上述内容兼容。实现可以在可变参数之前定义额外的可选参数,但这些参数应该与接口方法上的可变参数兼容。

class-interface-clause中的qualified-name必须命名一个接口类型。

语义

一个class-declaration通过name定义一个类类型。类名不区分大小写。

abstract修饰符声明一个类只能用作基类;该类不能直接实例化。抽象类可以包含一个或多个抽象成员,但这不是必需的。当一个具体类从一个抽象类派生时,具体类必须包含对它继承的每个抽象成员的实现。抽象方法的实现必须具有兼容的签名,不兼容的实现是不允许的。

final修饰符阻止将一个类用作基类。

可选的class-base-clause指定正在定义的类派生的一个基类。在这种情况下,派生类继承基类中的所有成员。

可选的class-interface-clause指定正在定义的类实现的一个或多个接口。

示例

abstract class Vehicle
{
  public abstract function getMaxSpeed();
  ...
}
abstract class Aircraft extends Vehicle
{
  public abstract function getMaxAltitude();
  ...
}
class PassengerJet extends Aircraft
{
  public function getMaxSpeed()
  {
    // implement method
  }
  public function getMaxAltitude()
  {
    // implement method
  }
  ...
}
$pj = new PassengerJet(...);
echo "\$pj's maximum speed: " . $pj->getMaxSpeed() . "\n";
echo "\$pj's maximum altitude: " . $pj->getMaxAltitude() . "\n";
// -----------------------------------------
final class MathLibrary
{
  private function MathLibrary() {} // disallows instantiation
  public static function sin() { ... }
  // ...
}
$v = MathLibrary::sin(2.34);
// -----------------------------------------
interface MyCollection
{
        function put($item);
        function get();
}
class MyList implements MyCollection
{
  public function put($item)
  {
    // implement method
  }
  public function get()
  {
    // implement method
  }
  ...
}

类成员

语法

class-member-declarations:
   class-member-declaration
   class-member-declarations   class-member-declaration

class-member-declaration:
   class-const-declaration
   property-declaration
   method-declaration
   constructor-declaration
   destructor-declaration
   trait-use-clause

语义

一个类的成员是其class-member-declarations指定的成员,以及从其基类继承的成员。

一个类可以包含以下成员

  • 常量 - 与类关联的常数值。
  • 属性 - 类的变量。
  • 方法 - 类可以执行的计算和操作。

一些方法具有特殊语义,例如

成员可以通过trait-use-clauses从一个或多个特性中导入。

该类还可以具有动态成员,这些成员不是类定义的一部分。

方法和属性可以是静态成员或实例成员。静态成员使用static声明。实例成员是非静态的成员。静态或实例成员的名称永远不能单独使用;它必须始终用作范围解析运算符成员访问运算符的右操作数。

类的每个实例都包含它自己的、唯一的该类的实例属性集。实例成员通过->运算符访问。相反,静态属性为其类指定了一个确切的VSlot,该VSlot本身不属于任何实例。静态属性是否存在与该类是否存在任何实例无关。静态成员通过::运算符访问。

当任何实例方法对类的给定实例进行操作时,在该方法内部,该对象可以通过$this访问。由于静态方法不操作特定实例,因此它没有$this

示例

class Point
{
  private static $pointCount = 0;     // static property

  private $x;             // instance property
  private $y;             // instance property

  public static function getPointCount()    // static method
  {
    return self::$pointCount;     // access static property
  }
  public function move($x, $y)        // instance method
  {
    $this->x = $x;
    $this->y = $y;
  }
  public function __construct($x = 0, $y = 0) // instance method
  {
    $this->x = $x;          // access instance property
    $this->y = $y;          // access instance property
    ++self::$pointCount;    // access static property
  }
  public function __destruct()      // instance method
  {
    --self::$pointCount;        // access static property
    ...
  }
  ...
}
echo "Point count = " . Point::getPointCount() . "\n";
$cName = 'Point';
echo "Point count = " . $cName::getPointCount() . "\n";

动态成员

最初,实例只具有在其类定义中显式声明的属性。但是,可以在运行时向实例添加和删除属性。类的静态属性不能在运行时更改,尝试访问不存在的静态属性会导致致命错误。运行时创建的属性始终具有公共可见性。

该类还可以为动态成员(方法和属性)定义特殊方法。这种机制使用与访问普通成员相同的语法,但它不会访问实际的属性和方法,而是使用特殊方法来模拟访问。

在动态属性的情况下,如果一个类通过定义一系列特殊方法来提供这样做的功能,它可以通过将这些属性存储在另一个对象中或存储在数据库中(例如)来处理这些属性的存储分配和管理。对于动态方法,静态方法和非静态方法都可以通过特殊方法处理。

考虑以下涉及动态属性的场景

class Point { ... } // has no public property "color", but has made
                    // provision to support dynamic properties.
$p = new Point(10, 15);
$p->color = "red"; // create/set the dynamic property "color"
$v = $p->color;    // get the dynamic property "color"
isset($p->color);  // test if the dynamic property "color" exists
unset($p->color);  // remove the dynamic property "color"

当使用指定名称但当前不可见(因为它是隐藏的或不存在)的属性时,将调用动态属性处理。如果该属性在可修改的左值上下文中使用(如赋值为“red”),引擎将生成对实例方法 __set 的调用。该方法将该名称视为正在操作的实例的动态属性,并将它的值设置为“red”,如果需要,则创建该属性。类似地,在非左值上下文中(如将 color 赋值给 $v),引擎将生成对实例方法 __get 的调用,该方法将该名称视为正在操作的实例的动态属性,并获取它的值。在对内建 isset 的调用中,这将生成对实例方法 __isset 的调用,而使用 unset 语句 将生成对实例方法 __unset 的调用。通过定义这四个特殊方法,类的实现者可以控制动态属性的处理方式。

引擎仅在方法已定义时调用它们,如果它们未定义,则不会产生任何错误,并且将使用默认行为。

对于动态方法,如果对未定义的实例方法进行调用,并且实例具有 __call 方法,则将调用该方法。否则,根据默认设置,将产生一个致命错误。如果对未定义的类方法进行静态调用,并且类定义了 __callStatic 方法,则将调用该方法。否则,根据默认设置,将产生一个致命错误。在这两种情况下,调用的返回值都是被调用方法的返回值。

考虑以下代码片段,其中 Widget 类既没有名为 iMethod 的实例方法,也没有名为 sMethod 的静态方法,但该类已提供处理动态方法的机制。

$obj = new Widget;
$obj->iMethod(10, TRUE, "abc");
Widget::sMethod(NULL, 1.234);

对 iMethod 的调用将被视为

$obj->__call('iMethod', array(10, TRUE, "abc"))

而对 sMethod 的调用将被视为

Widget::__callStatic('sMethod', array(NULL, 1.234))

常量

语法

const-declaration:
   const   const-elements   ;

class-const-declaration:
   visibility-modifieropt   const   const-elements   ;

const-elements:
   const-element
   const-elements   ,   const-element

const-element:
   name   =   constant-expression

约束

const-declaration 只能出现在脚本的顶层,并且不能重新定义现有的 c-常量

class-const-declaration 必须位于 class-declarationinterface-declaration 内。

类常量不能具有 static 指定符。

语义

const-declaration 定义一个 c-常量。

如果省略了类常量的 visibility-modifier,则假定为 publicvisibility-modifier 适用于在 const-elements 列表中定义的所有常量。

所有常量都隐式地为 static

示例

const MIN_VAL = 20;
const LOWER = MIN_VAL;
// -----------------------------------------
class Automobile
{
  const DEFAULT_COLOR = "white";
  public DEFAULT_BRAND = 'benz';
  protected WHEEL_NUM = 4;
  private PRIVATE_CONST = 'const';
  ...
}
$col = Automobile::DEFAULT_COLOR;

属性

语法

property-declaration:
   property-modifier   property-elements   ;

property-modifier:
   var
   visibility-modifier   static-modifieropt
   static-modifier   visibility-modifieropt

visibility-modifier:
   public
   protected
   private

static-modifier:
   static

property-elements:
   property-element
   property-elements   property-element

property-element:
   variable-name   property-initializeropt   ;

property-initializer:
   =   constant-expression

语义

property-declaration 定义一个或多个实例或静态属性。

如果省略了 visibility-modifier,则假定为 publicvar 修饰符暗示公共可见性。static 修饰符将成员定义为 静态成员

实例属性的 property-initializer 在调用类的 构造函数 之前应用。

可见的实例属性可以被 unset,在这种情况下,该属性实际上将从该实例中删除。

示例

class Point
{
  private static $pointCount = 0; // static property with initializer

  private $x; // instance property
  private $y; // instance property
  ...

}

方法

语法

method-declaration:
   method-modifiersopt   function-definition
   method-modifiers   function-definition-header   ;

method-modifiers:
   method-modifier
   method-modifiers   method-modifier

method-modifier:
   visibility-modifier
   static-modifier
   class-modifier

约束

function-definition 之前的方法修饰符 method-modifiers 不能包含 abstract 修饰符。

function-definition-header 之前的方法修饰符 method-modifiers 必须包含 abstract 修饰符。

方法不能多次指定相同的修饰符。方法不能具有多个 visibility-modifier。方法不能同时具有 abstractprivate 修饰符,也不能同时具有 abstractfinal 修饰符。

语义

method-declaration 定义一个实例或静态方法。方法是在类内部定义的函数。但是,abstract 的存在表示抽象方法,在这种情况下,不提供实现。abstract 的缺失表示具体方法,在这种情况下,将提供实现。

方法名称不区分大小写。

final 的存在表示该方法不能在派生类中被重写。

如果省略了 visibility-modifier,则假定为 public

示例

有关实例和静态方法的示例,请参见 类成员。有关抽象方法及其后续定义的示例,请参见 类声明

构造函数

语法

constructor-declaration:
   method-modifiers   function   &opt   __construct   (   parameter-declaration-listopt   )   compound-statement

约束

派生类中的重写构造函数必须具有与基类中的构造函数相同或更不严格的 可见性

method-modifiers 不能包含 static

语义

构造函数是一个特殊命名的 实例方法,用于在实例创建后立即初始化该实例。任何没有初始化器且未由构造函数显式初始化的实例属性都将取值为 NULL。构造函数可以通过值或通过引用返回结果。构造函数不能是抽象的或静态的。

类不必定义构造函数。

如果省略了 visibility-modifier,则假定为 publicprivate 构造函数阻止创建该类类型的实例,除非通过相同类的 方法。

可以通过重新声明构造函数来在派生类中重写构造函数。但是,重写构造函数不必具有与基类中定义的构造函数相同或兼容的签名。

构造函数由 object-creation-expression 以及从其他(派生类)构造函数内部调用。

如果派生类层次结构中的类具有构造函数,则每个级别的构造函数有责任使用表示法 parent::__construct(...) 显式地调用其基类中的构造函数。如果构造函数调用其基类构造函数,则建议将其作为 compound-statement 中的第一个语句执行,以便从下到上构建对象层次结构。构造函数不应多次调用其基类构造函数。对基类构造函数的调用将搜索类层次结构中最近的构造函数。并非层次结构的每个级别都需要构造函数。

示例

class Point
{
  private static $pointCount = 0;
  private $x;
  private $y;
  public function __construct($x = 0, $y = 0)
  {
    $this->x = $x;
    $this->y = $y;
    ++self::$pointCount;
  }
  public function __destruct()
  {
    --self::$pointCount;
    ...
  }
  ...
}
// -----------------------------------------
class MyRangeException extends Exception
{
  public function __construct($message, ...)
  {
    parent::__construct($message);
    ...
  }
  ...
}

析构函数

语法

destructor-declaration:
   method-modifiers   function   &opt   __destruct   (   )   compound-statement

约束

method-modifiers 不能包含 static

语义

析构函数是一个特殊命名的 实例方法,用于在不再需要实例时释放资源。所有类的实例的析构函数将在没有指向这些实例的句柄时自动调用,或在程序关闭期间以某种未指定顺序自动调用。与任何方法一样,析构函数可以通过值或通过引用返回结果。析构函数不能是静态的。

析构函数由引擎或从其他(派生类)析构函数内部调用。

如果派生类层次结构中的类具有析构函数,则每个级别的析构函数有责任使用表示法 parent::__destruct() 显式地调用其基类中的析构函数。如果析构函数调用其基类析构函数,则建议将其作为 compound-statement 中的最后一个语句执行,以便从上到下销毁对象层次结构。析构函数不应多次调用其基类析构函数。对基类析构函数的调用将搜索类层次结构中最近的析构函数。并非层次结构的每个级别都需要析构函数。private 析构函数阻止从派生类调用析构函数。

示例

有关构造函数和析构函数的示例,请参见 构造函数部分

继承

当一个类 extends 另一个类时,它可以通过声明具有相同名称的成员来重写父类的成员。只有属性和方法可以被重写。

重写成员的可见性不能更严格,只能更宽松(从 privateprotectedpublic)。

当重写私有成员时,定义类的 方法仍然可以访问原始私有成员,但是非静态的公共和受保护的成员在整个继承链中共享。

当重写方法时,重写方法的签名应该与原始方法的签名兼容,规则与原始方法属于接口而重写方法属于实现时相同。如果用不兼容的方法重写已实现的方法,将发出非致命错误,但引擎仍将接受重写。不建议使用不兼容的重写。

具有特殊语义的方法

概述

如果类包含对具有以下名称之一的方法的定义,则该方法必须具有规定的可见性、签名和语义。

方法名称描述
__call在实例方法调用的上下文中调用动态方法。
__callStatic在静态方法调用的上下文中调用动态方法。
__clone通常用于对对象进行 深复制
__construct构造函数。
__debugInfo为对象生成调试信息。
__destruct析构函数。
__get检索给定动态属性的值。
__invoke当对象作为函数调用时调用(例如 $a())。
__isset报告给定动态属性是否存在。
__set设置给定动态属性的值。
__set_state由导出函数 var_export 用于恢复对象的 状态。
__sleep在对该类的实例进行 序列化 之前执行。
__toString返回它被调用的实例的字符串表示形式。
__unset删除给定的动态属性。
__wakeup在对该类的实例进行 反序列化 之后执行。

一般来说,以 __ 开头的 方法名称保留用于特殊 方法。代码不应定义以 __ 开头的名称的 方法,除非它是此处描述的特殊 方法之一。

请注意,虽然下面的语法定义在 方法定义中使用 非抽象语法,但特殊 方法(与任何 方法一样)可以被声明为 abstract。在这种情况下,该定义实际上并不定义一个特殊 方法,而是定义一个重写具体类必须声明一个。然而,这些定义仍然必须遵循对特殊 方法的约束。

方法 __call

语法

  method-modifiers function  __call  (  $name  ,  $arguments  )  return-typeopt  compound-statement

约束

该方法不能是静态的,并且必须具有公共可见性。

传递给该方法的参数不能通过引用传递。

语义

此实例方法被调用以使用由数组元素指定的参数(由 $arguments 指定的数组)来调用由 $name 指定的 动态方法。它可以返回任何认为合适的返回值。

通常,当使用 -> 运算符 调用不可见的实例方法时,会隐式调用 __call

虽然可以显式调用 __call,但两种情况不一定产生相同的结果。考虑表达式 p->m(...),其中 p 是一个实例,m 是一个实例方法名。如果 m 是一个可见方法的名称,p->m(...) 不会导致调用 __call。而是使用可见方法。另一方面,表达式 p->__call('m',array(...)) 始终调用名为动态方法,忽略了可能存在具有相同名称的可见方法这一事实。如果 m 不是可见方法的名称,则这两个表达式等效;也就是说;在处理 p->m(...) 时,如果找不到名为该名称的可见方法,则假定为动态方法,并调用 __call

虽然 name 源代码标记具有规定的语法,但对 $name 指定的动态方法名称的内容没有限制。这里允许使用任何源字符。

示例

class Widget
{
  public function __call($name, $arguments)
  {
    // using the method name and argument list, redirect/process
    // the method call, as desired.
  }
  ...
}
$obj = new Widget;
$obj->iMethod(10, TRUE, "abc"); // $obj->__call('iMethod', array(...))

方法 __callStatic

语法

  method-modifiers  function  __callStatic  (  $name  ,  $arguments  )  return-typeopt  compound-statement

约束

方法修饰符 必须包含 static 并必须定义公有可见性。

传递给该方法的参数不能通过引用传递。

语义

这个静态方法用于调用 动态方法,该方法由 $name 指定,使用 $arguments 指定的数组元素指定的参数。它可以返回任何认为合适的值。

通常,当使用 :: 运算符 调用不可见的静态方法时,会隐式调用 __callStatic

虽然可以显式调用 __callStatic,但两种情况不一定产生相同的结果。考虑表达式 C::m(...),其中 C 是一个类,m 是一个静态方法名。如果 m 是一个可见方法的名称,C::m(...) 不会导致调用 __callStatic。而是使用可见方法。另一方面,表达式 C::__callStatic('m',array(...)) 始终调用名为动态方法,忽略了可能存在具有相同名称的静态可见方法这一事实。如果 m 不是可见方法的名称,则这两个表达式等效;也就是说;在处理 C::m(...) 时,如果找不到名为该名称的可见方法,则假定为动态方法,并调用 __callStatic

虽然 name 源代码标记具有规定的语法,但对 $name 指定的动态方法名称的拼写没有限制。这里允许使用任何源字符。

示例

class Widget
{
    public static function __callStatic($name, $arguments)
    {
      // using the method name and argument list, redirect/process\
      // the method call, as desired.
    }
    ...
}

Widget::sMethod(NULL, 1.234); // Widget::__callStatic('sMethod', array(...))

方法 __clone

语法

  method-modifiers  function  __clone  (  )  compound-statement

约束

方法修饰符 不得包含 static 并必须定义公有可见性。

语义

此实例方法由 clone 运算符 调用,通常用于创建 深拷贝 在其上调用的实例。程序不能直接调用方法 __clone

考虑一个类 Employee,从中派生一个类 Manager。让我们假设这两个类都包含作为对象的属性。要复制 Manager 对象,会调用其 __clone 方法来执行复制 Manager 类属性所需的操作。该方法应该反过来调用其父类 Employee__clone 方法,以便也可以复制该类的属性(依此类推,向上溯及派生类层次结构)。

要克隆一个对象,clone 运算符会对在其上调用的对象进行 浅拷贝。然后,如果要克隆的实例的类包含名为 __clone 的方法,则会调用该方法来进行深拷贝。方法 __clone 不能从类外部直接调用;它只能从派生类内部按名称调用,使用符号 parent::__clone()。此方法可以返回值;但是,如果这样做并且控制通过 clone 运算符直接返回到调用点,则将忽略该值。但是,可以检索返回到 parent::__clone() 调用的值。

虽然克隆会创建一个新对象,但它是在不使用构造函数的情况下执行的,在这种情况下,可能需要在 __clone 方法中添加代码来模拟在相应的构造函数中发生的事情。(参见下面的 Point 示例)。

__clone 的实现应该考虑实例具有 动态属性 的可能性。

示例

class Employee
{
  ...
  public function __clone()
  {
    // do what it takes here to make a copy of Employee object properties
  }
}
class Manager extends Employee
{
  ...
  public function __clone()
  {
    parent::__clone(); // request cloning of the Employee properties

    // do what it takes here to make a copy of Manager object properties
  }
  ...
}
// -----------------------------------------
class Point
{
  private static $pointCount = 0;
  public function __construct($x = 0, $y = 0)
  {
    ...
    ++self::$pointCount;
  }
  public function __clone()
  {
    ++self::$pointCount; // emulate the constructor
  }
  ...
}
$p1 = new Point;  // created using the constructor
$p2 = clone $p1;  // created by cloning

方法 __debugInfo

语法

  method-modifiers function  __debugInfo  (  )   compound-statement

约束

方法修饰符 不得包含 static 并必须定义公有可见性。

该函数应返回数组。

语义

此方法允许类为对象提供调试信息,这些信息可以用作 var_dump() 的信息源。

示例

class File {
  // "Resource(stream)" isn't all that useful
  private $fp;

  // But all the stream meta data is
  public function __debugInfo() {
    return $this->fp ? stream_get_meta_data($fp) : [];
  }

  public function open($filename, $mode = 'r'){
    $this->fp = fopen($filename, $mode);
  }
}

$f = new File;
var_dump($f); // object(File)#1 { }
$f->open('https://php.net');
var_dump($f);
/*
object(File)#1 {
  ["wrapper_type"]=>
  string(4) "http"
  ["stream_type"]=>
  string(10) "tcp_socket"
  etc...
*/

方法 __get

语法

  method-modifiers function  &opt  __get  (  $name  )  return-typeopt  compound-statement

约束

方法修饰符 不得包含 static 并必须定义公有可见性。

语义

此实例方法获取由 $name 指定的 动态属性 的值。由实现者定义返回值。

通常,当使用 -> 运算符 在非左值上下文中使用,并且指定的属性不可见时,会隐式调用 __get

虽然可以显式调用 __get,但两种情况不一定产生相同的结果。考虑表达式 $v = $p->m,其中 p 是一个实例,m 是一个属性名。如果 m 是一个可见属性的名称,p->m 不会导致调用 __get。而是使用可见属性。另一方面,表达式 p->__get('m') 始终获取名为动态属性的值,忽略了可能存在具有相同名称的可见属性这一事实。如果 m 不是可见属性的名称,则这两个表达式等效;也就是说;在处理 p->m 在非左值上下文中,如果找不到名为该名称的可见属性,则假定为动态属性,并调用 __get

考虑表达式 $v = $p->m = 5,其中 m 是一个动态属性。虽然 __set 被调用以将值 5 赋给该属性,但不会调用 __get 来检索该赋值完成后结果。

如果实现希望调用者能够修改返回值的内容(例如,返回一个可以由调用者修改的数组,并且修改反映在动态属性中),则 __get 应该通过引用返回。

示例

class Point
{
    private $dynamicProperties = array();
    private $x;
    private $y;
    public function __get($name)
    {
        if (array_key_exists($name, $this->dynamicProperties))
        {
            return $this->dynamicProperties[$name];
        }

        // no-such-property error handling goes here
        return NULL;
    }
  ...
}

实现说明

考虑以下类,该类包含名为 prop 的属性

class C
{
  public function __get($name)
  {
    return $this->$name;    // must not recurse
  }
  ...
}
$c = new C;
$x = $c->prop;

由于类中不存在名为 prop 的属性(动态或其他),并且定义了 __get 方法,因此这看起来像一个递归情况。但是,实现不能允许这样做。这同样适用于 __set__isset__unset 的看似自我引用的实现。每属性只执行一次动态解析迭代,并且特殊方法每属性名只调用一次。

虽然 name 源代码标记具有规定的语法,但对 $name 指定的动态属性名称的拼写没有限制。这里允许使用任何源字符。

方法 __invoke

语法

  method-modifiers  function  __invoke  ( parameter-declaration-listopt  )  return-typeopt  compound-statement

约束

方法修饰符 不得包含 static 并必须定义公有可见性。

语义

此实例方法允许使用函数调用符号使用实例。如果类的实例提供了此方法,那么在传递给 is_callable 时,它也会返回 TRUE

当实例作为函数被调用时,可用的参数列表将被提供给 __invoke,其返回值将成为初始函数调用的返回值。

示例

class C
{
  public function __invoke($p)
  {
    ...
    return ...;
  }
  ...
}
$c = new C;
is_callable($c) // returns TRUE
$r = $c(123);   // becomes $r = $c->__invoke(123);

方法 __isset

语法

  method-modifiers  function  __isset  (  $name  )  return-typeopt  compound-statement

约束

方法修饰符 不得包含 static 并必须定义公有可见性。

语义

如果由 $name 指定的 动态属性 存在,则此实例方法返回 TRUE;否则,返回 FALSE。动态属性存在方式的具体细节留给方法的实现者决定。

通常,当使用固有 isset 或固有 empty 调用带有指定不可见属性的参数时,会隐式调用 __isset

虽然可以显式调用 __isset,但两种情况不一定产生相同的结果。考虑表达式 isset($p->m),其中 p 是一个实例,m 是一个属性名。如果 m 是一个可见属性的名称,则不会调用 __isset。而是使用可见属性。另一方面,表达式 p->__isset('m') 始终测试名为动态属性,忽略了可能存在具有相同名称的可见属性这一事实。如果 m 不是可见属性的名称,则这两个表达式等效;也就是说;在处理 p->m 在非左值上下文中,如果找不到名为该名称的可见属性,则假定为动态属性。

虽然 name 源代码标记具有规定的语法,但对 $name 指定的动态属性名称的拼写没有限制。这里允许使用任何源字符。

示例

class Point
{
    private $dynamicProperties = array();
    private $x;
    private $y;
    public function __isset($name)
    {
        return isset($this->dynamicProperties[$name]);
    }
  ...
}

实现说明

参见 __get 的实现说明。

方法 __set

语法

  method-modifiers  function  __set  (  $name  ,  $value  )  return-typeopt  compound-statement

约束

方法修饰符 不得包含 static 并必须定义公有可见性。

语义

此实例方法将由 $name 指定的 动态属性 的值设置为 $value。预计不会返回值。

通常,当使用 -> 运算符 在可修改的左值上下文中使用,并且指定的属性不可见时,会隐式调用 __set

虽然可以显式调用 __set,但两种情况不一定产生相同的结果。考虑表达式 p->m = 5,其中 p 是一个实例,m 是一个属性名。如果 m 是一个可见属性的名称,p->m 不会导致调用 __set。而是使用可见属性。另一方面,表达式 p->__set('m',5) 始终设置名为动态属性的值,忽略了可能存在具有相同名称的可见属性这一事实。如果 m 不是可见属性的名称,则这两个表达式等效;也就是说;在处理 p->m 时,如果找不到名为该名称的可见属性,则假定为动态属性,并调用 __set

虽然 name 源代码标记具有规定的语法,但对 $name 指定的动态属性名称的拼写没有限制。这里允许使用任何源字符。

示例

class Point
{
    private $dynamicProperties = array();
    private $x;
    private $y;
    public function __set($name, $value)
    {
        $this->dynamicProperties[$name] = $value;
    }
  ...
}
// -----------------------------------------
class X
{
    public function __destruct() { ... }
}
$p = new Point(5, 9);
$p->thing = new X;  // set dynamic property "thing" to instance with destructor
...
// at the end of the program, p->thing's destructor is called

实现说明

参见 __get 的实现说明。

方法 __set_state

语法

  method-modifiers  function  __set_state  ( array  $properties  )  return-typeopt  compound-statement

约束

方法修饰符 必须包含 static 并必须定义公有可见性。

语义

此函数支持库函数 var_export,当它使用此类类型的实例时。var_export 获取一个变量,并生成该变量的字符串表示形式,作为适合与固有 eval 一起使用的有效 PHP 代码。

对于一个对象,var_export 返回的字符串具有以下通用格式

classname::__set_state(array('prop1' => value, ..., 'propN' => value , ))

其中属性名 prop1propN 不包含前导美元符号 ($)。此字符串包含对 __set_state 方法的调用,即使此类或其任何基类中都没有定义此方法,在这种情况下,随后使用此字符串调用 eval 将产生致命错误。要允许将字符串与 eval 一起使用,必须定义方法 __set_state,并且它必须创建该类类型的新实例,使用 $properties 中的键/值对初始化其实例属性,并且它必须返回该新对象。

使用 __set_state 方法扩展类时,应该覆盖该方法,否则对它的调用将在基类层次结构中查找此类方法,并且该方法将返回关联的基类实例,而不是调用它的类的实例。使用 static 允许 后期静态绑定 生成适当类的实例。

如果派生类定义了 __set_state 方法,但任何基类都具有该方法中不可见的实例属性,则该方法也必须调用父类的 __set_state,但这可能需要基类的支持。参见下面的第二个示例。

示例

class Point
{
  private $x;
  private $y;
  static public function __set_state(array $properties)
  {
    $p = new Point;
    $p->x = $properties['x'];
    $p->y = $properties['y'];
    return $p;
  }
  ...
}
$p = new Point(3, 5);
$v = var_export($p, TRUE);  // returns string representation of $p

生成的字符串看起来像这样

"Point::__set_state(array(
   'x' => 3,
   'y' => 5,
))"
eval('$z = ' . $v . ";"); // execute the string putting the result in $z
echo "Point \$z is $z\n"; // Point $z is (3,5)
// -----------------------------------------
class B // base class of D
{
  private $bprop;
  public function __construct($p)
  {
    $this->bprop = $p;
  }
  static public function __set_state(array $properties)
  {
    $b = new static($properties['bprop']);  // note the static
    return $b;
    // Because of the "new static", the return statement
    //   returns a B when called in a B context, and
    //   returns a D when called in a D context
  }
}
class D extends B
{
  private $dprop = 123;
  public function __construct($bp, $dp = NULL)
  {
    $this->dprop = $dp;
    parent::__construct($bp);
  }
  static public function __set_state(array $properties)
  {
    $d = parent::__set_state($properties); // expects back a D, NOT a B
    $d->dprop = $properties['dprop'];
    return $d;
  }
}
$b = new B(10);
$v = var_export($b, TRUE);
eval('$z = ' . $v . ";");
var_dump($z);
$d = new D(20, 30);
$v = var_export($d, TRUE);
eval('$z = ' . $v . ";");
var_dump($z);

方法 __sleep

语法

  method-modifiers  function  __sleep  ( )  return-typeopt  compound-statement

约束

方法修饰符 不得包含 static 并必须定义公有可见性。

语义

实例方法 __sleep__wakeup 支持 序列化

如果一个类具有__sleep方法,则库函数serialize会调用该方法来查找应该序列化哪些可见的实例属性。(在没有__sleepserialize方法的情况下,所有实例属性都会被序列化,包括在运行时定义的属性)。此信息由__sleep作为包含零个或多个元素的数组返回,其中每个元素的值都是唯一的,并且是可见实例属性的名称。这些属性的值按照元素在数组中插入的顺序进行序列化。如果__sleep没有显式返回一个值,则返回NULL,并且该值会被序列化。

除了创建属性名称数组之外,__sleep还可以执行在序列化发生之前可能需要的其他任何操作。

使用__sleep__wakeup的另一种方法是实现Serializable接口

请注意,如果定义了__sleep__wakeup的类被扩展,并且派生类没有覆盖这些方法,则序列化和反序列化将像处理基类实例一样进行,例如,可能不会序列化或恢复附加属性。

示例

考虑一个Point类,它不仅包含 x 和 y 坐标,还具有一个id属性;也就是说,在程序执行期间创建的每个不同的Point都具有唯一的数字 id。但是,在序列化Point时,无需包含此信息。它可以在该Point被反序列化时简单地重新创建。此信息是临时的,不需要在程序执行之间保留。(对于其他临时属性也是如此,例如包含临时结果或运行时缓存的属性)。

class Point
{
  private static $nextId = 1;
  private $x;
  private $y;
  private $id;
  public function __construct($x = 0, $y = 0)
  {
    $this->x = $x;
    $this->y = $y;
    $this->id = self::$nextId++;  // assign the next available id
  }
  public function __sleep()
  {
    return array('y', 'x'); // serialize only $y and $x, in that order
  }
  public function __wakeup()
  {
    $this->id = self::$nextId++;  // assign a new id
  }
  ...
}
$p = new Point(-1, 0);
$s = serialize($p);   // serialize Point(-1,0)
$v = unserialize($s); // unserialize Point(-1,0)

方法__toString

语法

  method-modifiers  function  __toString  ( )  return-typeopt  compound-statement

约束

方法修饰符 不得包含 static 并必须定义公有可见性。

此函数必须返回一个字符串。

此函数不能抛出任何异常。

语义

此实例方法旨在创建调用它的实例的字符串表示形式。

当需要将对象转换为字符串时,__toString会被许多语言和库设施(包括echo)调用。__toString也可以直接调用。

__toString的实现应考虑实例可能具有动态属性的可能性。

示例

class Point
{
  private $x;
  private $y;
  public function __construct($x = 0, $y = 0)
  {
    $this->x = $x;
    $this->y = $y;
  }
  public function __toString()
  {
    return '(' . $this->x . ',' . $this->y . ')';
  }
  ...
}
$p1 = new Point(20, 30);
echo $p1 . "\n";  // implicit call to __toString() returns "(20,30)"
// -----------------------------------------
class MyRangeException extends Exception
{
  public function __toString()
  {
    return parent::__toString()
      . string-representation-of-MyRangeException
  }
  ...
}

方法__unset

语法

  method-modifiers  function  __unset  (  $name  )  return-typeopt  compound-statement

约束

方法修饰符 不得包含 static 并必须定义公有可见性。

语义

如果由$name指定的动态属性存在,则此实例方法会将其删除;否则,调用将没有效果。不希望返回任何值。

通常,当unset语句被调用,并且其参数指定了一个不可见的属性时,__unset会被隐式调用。

虽然__unset可以显式调用,但这两种情况不一定产生相同的结果。考虑表达式unset($p->m),其中p是一个实例,m是一个属性名称。如果m是可见属性的名称,则不会调用__unset。而是使用可见属性。另一方面,表达式p->__unset('m'))始终删除名为动态属性,而不考虑是否存在具有相同名称的可见属性。如果m不是可见属性的名称,则这两个表达式等效;也就是说;当在非左值上下文中处理p->m时,如果找不到具有该名称的可见属性,则假定为动态属性。

虽然 name 源代码标记具有规定的语法,但对 $name 指定的动态属性名称的拼写没有限制。这里允许使用任何源字符。

示例

class Point
{
    private $dynamicProperties = array();
    private $x;
    private $y;
    public function __unset($name)
    {
        unset($this->dynamicProperties[$name]);
    }
  ...
}

实现说明

参见 __get 的实现说明。

方法__wakeup

语法

  method-modifiers  function  __wakeup  ( )  return-typeopt  compound-statement

约束

方法修饰符 不得包含 static 并必须定义公有可见性。

语义

实例方法__sleep__wakeup支持序列化

当库函数unserialize被调用用于反序列化对象的字符串表示形式(由库函数serialize创建)时,unserialize会创建该对象类型的实例而不调用构造函数,然后调用该类的__wakeup方法(如果有)来初始化该实例。在没有__wakeup方法的情况下,唯一执行的操作是还原序列化字符串中编码的实例属性的值。

__wakeup不希望返回任何值。

考虑一个Point类,它不仅包含 x 和 y 坐标,还具有一个id属性;也就是说,在程序执行期间创建的每个不同的Point都具有唯一的数字 id。但是,在序列化Point时,无需包含此信息。它可以简单地由__wakeup在该Point被反序列化时重新创建。这意味着__wakeup必须像构造函数一样进行模拟,具体取决于情况。

示例

参见__sleep

序列化

在 PHP 中,变量可以转换为适合用于文件存储或程序间通信的某种外部形式。转换为这种形式的过程称为序列化,而转换回原始形式的过程称为反序列化。这些功能由库函数serializeunserialize分别提供。

对于作为对象的变量,这两个函数本身会序列化和反序列化所有实例属性,这对于某些应用程序可能已经足够了。但是,如果程序员想要自定义这些过程,他们可以通过两种互斥的方式来实现。第一种方法是定义名为__sleep__wakeup的方法,并让它们分别在序列化之前和序列化之后获取控制权。有关此方法的信息,请参见__sleep__wakeup。第二种方法涉及通过定义两个方法serializeunserialize来实现接口Serializable

考虑一个Point类,它不仅包含 x 和 y 坐标,还具有一个id属性;也就是说,在程序执行期间创建的每个不同的Point都具有唯一的数字 id。但是,在序列化Point时,无需包含此信息。它可以在该Point被反序列化时简单地重新创建。此信息是临时的,不需要在程序执行之间保留。(对于其他临时属性也是如此,例如包含临时结果或运行时缓存的属性)。此外,考虑一个类ColoredPoint,它通过添加一个color属性来扩展Point。以下代码显示了为了使PointsColoredPoints都能够被序列化和反序列化而需要如何定义这些类

class Point implements Serializable // note the interface
{
  private static $nextId = 1;
  private $x;
  private $y;
  private $id;  // transient property; not serialized
  public function __construct($x = 0, $y = 0)
  {
    $this->x = $x;
    $this->y = $y;
    $this->id = self::$nextId++;
  }
  public function __toString()
  {
    return 'ID:' . $this->id . '(' . $this->x . ',' . $this->y . ')';
  }
  public function serialize()
  {
    return serialize(array('y' => $this->y, 'x' => $this->x));
  }

自定义方法serialize调用库函数serialize来创建数组的字符串版本,该数组的键是将被序列化的实例属性的名称。数组的插入顺序是属性在结果字符串中序列化的顺序。数组被返回。

  public function unserialize($data)
  {
    $data = unserialize($data);
    $this->x = $data['x'];
    $this->y = $data['y'];
    $this->id = self::$nextId++;
  }
}

自定义方法unserialize将传递给它的序列化字符串转换回数组。由于正在创建新对象,但没有调用任何构造函数,因此unserialize方法必须执行构造函数通常执行的任务。在本例中,这涉及为新对象分配唯一的 id。

$p = new Point(2, 5);
$s = serialize($p);

调用库函数serialize会调用自定义serialize方法。之后,变量$s包含Point(2,5)的序列化版本,可以将其存储在数据库中或传输到合作程序。读取或接收该序列化字符串的程序可以将其内容转换回相应的变量,如下所示

$v = unserialize($s);

调用库函数unserialize会调用自定义unserialize方法。之后,变量$s包含一个新的Point(2,5)

class ColoredPoint extends Point implements Serializable
{
  const RED = 1;
  const BLUE = 2;

  private $color; // an instance property

  public function __construct($x = 0, $y = 0, $color = RED)
  {
    parent::__construct($x, $y);
    $this->color = $color;
  }

  public function __toString()
  {
    return parent::__toString() . $this->color;
  }

  public function serialize()
  {
    return serialize(array(
      'color' => $this->color,
      'baseData' => parent::serialize()
    ));
  }

与类Point一样,此自定义方法返回一个包含将被序列化的实例属性的数组。但是,在第二个元素的情况下,使用了任意键名,其值为当前ColoredPoint对象中基类的Point的序列化版本。元素的顺序由程序员决定。

    public function unserialize($data)
    {
    $data = unserialize($data);
    $this->color = $data['color'];
    parent::unserialize($data['baseData']);
    }
}

由于ColoredPoint有一个基类,它会在调用基类的自定义方法之前反序列化自己的实例属性,以便它可以反序列化Point属性。

$cp = new ColoredPoint(9, 8, ColoredPoint::BLUE);
$s = serialize($cp);
...
$v = unserialize($s);

函数unserialize接受一个可选的第二个参数,该参数指定一个包含可信类名的数组,以字符串形式表示。在数据流中找到的类型名称不在此可信名称列表中的对象将被转换为类型为__PHP_Incomplete_Class的对象。

尝试序列化具有匿名类类型的对象会导致抛出一个类型为Exception的实例。

预定义类

Closure

预定义类Closure用于表示匿名函数。除了引擎描述的方式外,它不能被实例化。闭包对象是不可变的,并且不能允许创建或修改属性。

闭包可以是绑定的、未绑定的静态的。如果闭包被称为绑定,那么它有一个对象,$this在调用时将绑定到该对象。如果闭包是未绑定的,那么它没有$this将绑定的对象。如果闭包是静态的,那么它不能被绑定。

闭包可以是有范围的无范围的。如果闭包被称为有范围的,它具有一个类范围,它确定类对象的私有和受保护成员的可见性,包括但不限于$this上的此类成员。如果闭包被称为无范围的,则它没有设置类范围。

闭包有一个不变性,即有范围的闭包必须是绑定或静态的,而未绑定的闭包必须是无范围的。

class Closure
{
  public static bind(Closure $closure, $newthis [, $newscope = "static" ]);
  public bindTo($newthis [, $newscope = "static" ]);
  public call($newthis [, ...$parameters ]);
}

类成员定义如下

名称目的
bind使用特定的绑定对象$newthis和类范围$newscope复制闭包$closure。如果$newthisNULL,则如果未指定范围,闭包将被解绑,或者如果指定了范围,则为静态。$newscope是闭包要赋予的范围(一个包含类名的字符串,或者一个将使用其类的对象),或者"static"以保留当前范围。返回一个新的Closure对象或在失败时返回FALSE。此函数不得违反闭包必须既有范围又绑定或静态,或者否则既无范围又未绑定的不变性。此函数必须防止在$closure为静态的情况下将对象绑定到新闭包,但是新闭包可能具有不同的范围。
bindTo使用新的绑定对象和类范围复制由当前实例指定的闭包。此方法是bind的实例版本。
call使用绑定到$newthis$this$newthis类的类范围和$parameters指定的参数调用闭包(当前实例)。如果$newthis是NULL,或者如果闭包是静态的,则此函数必须失败。

匿名函数创建运算符被求值时,结果是引擎创建的类型为Closure(或从该类型派生的某些未指定类)的对象。此对象在此处称为“闭包对象”。此实例封装了在相应的匿名函数创建表达式中定义的匿名函数。

闭包对象的 内容取决于创建匿名函数的上下文。考虑以下场景

class C
{
  public function compute()
  {
    $count = 0;
    $values = array("red" => 3, 10);
    $callback = function ($p1, $p2) use (&$count, $values)
    {
      ...
    };
    ...
  }
}

一个Closure对象可能包含以下可选动态属性,按顺序排列:staticthisparameter

如果一个匿名函数创建表达式包含一个匿名函数使用子句,那么就会存在一个名为static的动态属性。这与闭包是否被认为是静态的无关。此属性是一个数组,其中包含使用变量名称列表中每个变量名称的元素,按照它们在使用子句中出现的词法顺序插入。每个元素的键是相应的变量名称,每个元素的值是在创建Closure对象时(而不是在使用它来调用封装的函数时)该变量的值。在上面的场景中,这将导致以下内容,显示为伪代码

$this->static = array(["count"]=>&0,["values"]=>array(["red"]=>3,[0]=>10));

如果一个匿名函数创建表达式用在实例方法中,那么就会存在一个名为this的动态属性。此属性是一个指向当前实例的句柄。在上面的场景中,这将导致以下内容,显示为伪代码

$this->this = $this;

如果一个匿名函数创建表达式包含一个参数声明列表,那么就会存在一个名为parameter的动态属性。此属性是一个包含一个或多个元素的数组,每个元素对应一个参数。这些元素按照其声明的词法顺序插入到该数组中。每个元素的键是相应的参数名称,每个元素的值是某个未指定的值。(这些值会被调用匿名函数时使用的参数值覆盖)。在上面的场景中,这将导致以下内容,显示为伪代码

$property = array("$p1" => ???, "$p2" => ???)

所有三个动态属性都可能不存在,在这种情况下,Closure对象为空。

闭包对象不能被序列化或反序列化。

Generator

此类支持yield运算符。此类不能直接实例化。它被定义如下

class Generator implements Iterator
{
  public function current();
  public function getReturn();
  public function key();
  public function next();
  public function rewind();
  public function send($value) ;
  public function throw(Exception $exception) ;
  public function valid();
}

类成员定义如下

名称目的
current实例方法Iterator::current的实现。
getReturn返回来自生成器的最终表达式,该表达式由return语句而不是yield生成。此函数只能在生成器完成生成值后有意义地调用一次;否则,将抛出Exception实例。
key实例方法Iterator::key的实现。
next实例方法Iterator::next的实现。
rewind实例方法Iterator::rewind的实现。
send此实例方法将$value指定的值发送到生成器作为当前yield表达式的结果,并恢复生成器的执行。$value是生成器当前所在的yield表达式的返回值。如果在调用此方法时生成器不在yield表达式中,它将首先被允许前进到第一个yield表达式,然后再发送该值。此方法返回生成的值。
throw此实例方法将异常抛出到生成器中,并恢复生成器的执行。其行为就好像当前的yield表达式被替换为抛出$exception一样。如果在调用此方法时生成器已经关闭,异常将在调用者的上下文中抛出。此方法返回生成的值。
valid实例方法Iterator::valid的实现。

生成器对象不能被序列化或反序列化。

__PHP_Incomplete_Class

在某些情况下,程序可以生成此类的实例,该实例本身不包含任何成员。其中之一涉及尝试反序列化一个编码了类实例的字符串,而该类没有定义,或者如果对象的类型被unserialize的过滤器参数声明为不受信任。

考虑以下代码

class Point
{
  private $x;
  private $y;
  ...
}
$p = new Point(2, 5);
$s = serialize($p); // properties $x and $y are serialized, in that order

假设序列化字符串存储在数据库中,并从那里被另一个程序检索。该程序包含以下代码,但没有包含类Point的定义

$v = unserialize($s);

而不是返回一个点Point(2, 5),__PHP_Incomplete_Class的实例会产生,并具有以下内容

__PHP_Incomplete_Class
{
   __PHP_Incomplete_Class_Name => "Point"
  x:Point:private => 2
  y:Point:private => 5
}

此类的对象可以被序列化,但是,任何尝试调用其方法或访问其属性以进行除序列化以外的任何其他操作都将导致致命错误。

stdClass

此类不包含任何成员。它可以被实例化并用作基类。当非对象被转换为对象,或成员选择运算符应用于NULLFALSE或空字符串时,将自动创建此类型的实例。

预定义错误类

PHP 有许多用于错误报告的预定义类。所有这些类都扩展了基类 Error。

Error

此类是所有内部 PHP 错误异常的基类。它被定义如下

class Error implements Throwable
{
  protected $message = '';
  protected $code = 0;
  protected $file;
  protected $line;

  public function __construct($message = "", $code = 0,
               Throwable $previous = NULL);

  final private function __clone();
}

有关基接口的信息,请参见Throwable。请注意,来自 Throwable 的方法在 Error 类中被实现为final,这意味着扩展类不能覆盖它们。

ArithmeticError

当某些数学运算期间发生错误时,将抛出此类的实例。它被定义如下

class ArithmeticError extends Error
{
}

AssertionError

当通过内在assert进行的断言失败时,将抛出此类的实例。类类型被定义如下

class AssertionError extends Error
{
}

DivisionByZeroError

当尝试将数字除以零时,将抛出此类的实例,例如使用余数运算符 (%%=) 时。请注意,这仅发生在整数运算中,常规浮点数除法 (/) 反而会产生非致命错误。类类型被定义如下

class DivisionByZeroError extends Error
{
}

ParseError

当解析 PHP 代码时发生错误(例如调用内在 eval 时),将抛出此类的实例。它被定义如下

class ParseError extends Error
{
}

TypeError

当发生以下任何情况时,将抛出此类的实例

  • 传递给函数的参数类型与它对应参数的声明类型不匹配。
  • 从函数返回的值类型与函数的声明返回值类型不匹配。
  • 严格模式下,传递给库函数的参数数量无效。

该类被定义如下

class TypeError extends Error
{
}

另请参见类 Exception