# PHP 中的面向对象编程 II > 原文: [https://zetcode.com/lang/php/oopii/](https://zetcode.com/lang/php/oopii/) 在 PHP 教程的这一章中,我们将继续描述 PHP 中的 OOP。 ## PHP `static`关键字 我们可以将类属性和方法声明为`static`。 `static`属性和方法不属于该类的实例。 他们属于阶级本身。 可通过范围解析运算符`::`访问它们。 `staticmethod.php` ```php id = $id; $this->size = $size; $this->color = $color; } } class Color { public $red; public $green; public $blue; function __construct($red, $green, $blue) { $this->red = $red; $this->green = $green; $this->blue = $blue; } } $color = new Color(23, 42, 223); $object1 = new Object(23, "small", $color); $object2 = clone $object1; $object2->id++; $object2->color->red = 255; $object2->size = "big"; print_r($object1); print_r($object2); ``` 在上面的 PHP 脚本中,我们定义了两个自定义对象:`Object`和`Color`。 `Object`对象将具有对`Color`对象的引用。 ```php $color = new Color(23, 42, 223); ``` 我们创建`Color`对象的实例。 ```php $object1 = new Object(23, "small", $color); ``` 创建对象对象的实例。 它将`Color`对象的实例传递给其构造函数。 ```php $object2 = clone $object1; ``` 我们执行`Object`对象的浅表副本。 ```php $object2->id++; $object2->color->red = 255; $object2->size = "big"; ``` 在这里,我们修改克隆对象的成员字段。 我们增加 id,更改颜色对象的红色部分,并将大小更改为“大”。 ```php print_r($object1); print_r($object2); ``` 我们使用`print_r()`功能比较结果。 ```php $ php shallowcopy.php Object Object ( [id] => 23 [size] => small [color] => Color Object ( [red] => 255 [green] => 42 [blue] => 223 ) ) Object Object ( [id] => 24 [size] => big [color] => Color Object ( [red] => 255 [green] => 42 [blue] => 223 ) ) ``` 我们可以看到 ID 不同:23 与 24。大小不同:“小”与“大”。 但是,这两个实例的颜色对象的红色部分相同:255。更改克隆对象的成员值不会影响原始对象。 更改引用对象的成员也影响了原始对象。 换句话说,两个对象都引用内存中的同一颜色对象。 要更改此行为,我们接下来将做一个深层复制。 `deepcopy.php` ```php id = $id; $this->size = $size; $this->color = $color; } function __clone() { $red = $this->color->red; $green = $this->color->green; $blue = $this->color->blue; $this->color = new Color($red, $green, $blue); } } class Color { public $red; public $green; public $blue; function __construct($red, $green, $blue) { $this->red = $red; $this->green = $green; $this->blue = $blue; } } $color = new Color(23, 42, 223); $object1 = new Object(23, "small", $color); $object2 = clone $object1; $object2->id++; $object2->color->red = 255; $object2->size = "big"; print_r($object1); print_r($object2); ``` 在此 PHP 脚本中,我们实现了`__clone()`方法。 ```php function __clone() { $red = $this->color->red; $green = $this->color->green; $blue = $this->color->blue; $this->color = new Color($red, $green, $blue); } ``` 在`__clone()`方法内部,我们复制红色,绿色和蓝色成员字段并创建一个新的`Color`对象。 现在,`$color`字段指向另一个`Color`对象。 ```php $ php deepcopy.php Object Object ( [id] => 23 [size] => small [color] => Color Object ( [red] => 23 [green] => 42 [blue] => 223 ) ) Object Object ( [id] => 24 [size] => big [color] => Color Object ( [red] => 255 [green] => 42 [blue] => 223 ) ) ``` 现在,引用的`Color`对象的红色部分不相同。 原始对象保留了其先前的 23 值。 ## PHP 异常 异常是为处理异常的发生而设计的,这些特殊情况会改变程序执行的正常流程。 引发或引发异常并引发异常。 在执行应用期间,许多事情可能出错。 磁盘可能已满,我们无法保存文件。 互联网连接可能断开,我们的应用尝试连接到站点。 所有这些都可能导致我们的应用崩溃。 为避免发生这种情况,我们必须应对可能发生的所有可能的错误。 为此,我们可以使用异常处理。 从 PHP 5 开始,可以使用例外。大多数 PHP 错误仍然使用旧的错误报告,而不是例外。 使用`set_error_handler()`,我们可以解决此问题。 `zerodiv.php` ```php getMessage(), "\n"; } ``` 在上面的 PHP 脚本中,我们有意将数字除以零。 这会导致错误。 该错误也不例外,并且不会被`catch`关键字捕获。 ```php set_error_handler("error_handler"); ``` `set_error_handler()`功能设置用户定义的错误处理器功能。 ```php function error_handler($errno, $errstring, $errfile, $line, $trace) { throw new ErrorException($errstring, $errno, 0, $errfile, $line); } ``` 在`set_error_handler()`函数内部,我们抛出了`ErrorException`。 稍后,此异常由`catch`关键字捕获。 ```php try { $a = 0; $b = 32; $c = $b / $a; } ``` 我们正在检查错误的代码放在`try`关键字之后的块中。 ```php } catch(Exception $e) { echo $e->getMessage(); } ``` `catch`关键字用于捕获异常。 要了解更多信息,我们在异常对象上调用`getMessage()`方法。 ```php $ php zerodiv.php Error occurred Division by zero ``` 这是我们的 PHP 脚本的输出。 `Exception`是所有异常的基类。 我们可以创建派生自该基类的异常。 `myexception.php` ```php LIMIT) { throw new BigValueException("Exceeded the maximum value allowed\n"); } } catch (BigValueException $e) { echo $e->getMessage(); } ``` 假设我们处于无法处理大量数字的情况。 ```php define("LIMIT", 333); ``` 大于此常量的数字在我们的 PHP 脚本中被视为“大”。 ```php class BigValueException extends Exception { ``` 我们有一个`BigValueException`类。 此类通过`extends`关键字从`Exception`类派生。 ```php public function __construct($message) { parent::__construct($message); } ``` 在构造函数内部,我们称为父级的构造函数。 ```php if ($a > LIMIT) { throw new BigValueException("Exceeded the maximum value allowed\n"); } ``` 如果该值大于限制,则抛出自定义异常。 我们给异常消息“超出了允许的最大值”。 ```php } catch (BigValueException $e) { echo $e->getMessage(); } ``` 我们捕获到异常并将其消息打印到控制台。 ## PHP 构造函数重载 PHP 不支持直接的构造函数重载。 换句话说,每个类只能定义一个构造函数。 许多已经知道 Java 或 C# 语言的程序员都在寻找 PHP 中的类似功能。 我们有两种方法可以处理此问题。 第一种解决方案基于`func_get_args()`功能。 第二种解决方案使用工厂模式。 `constructors.php` ```php $item = array_shift($args); } } public function __toString() { return "Author: $this->author\nTitle: $this->title\nPublished: $this->year\n\n"; } } $book1 = new Book("Stephen Prata", "C Primer Plus"); echo $book1; $book2 = new Book("Joshua Bloch", "Effective Java", 2008); echo $book2; ``` 在上面的脚本中,我们有一个`Book`类。 我们使用 2 和 3 参数实例化该类。 ```php private $author = "not specified"; private $title = "not specified"; private $year = "not specified"; ``` 我们定义了三个成员字段。 它们的初始值为“未指定”。 ```php $args = func_get_args(); ``` `func_get_args()`函数返回一个包含函数的参数列表的数组。 这样的想法是:构造函数中的代码是动态的; 它取决于传递给它的参数。 ```php foreach(["title", "author", "year"] as $item) { ``` 我们使用`foreach`关键字浏览所有成员字段。 ```php $this->$item = array_shift($args); ``` 构造函数中最基本的任务之一是初始化类的成员字段。 这是通过上面的代码行完成的。 `array_shift()`函数从数组中删除第一项并返回它。 ```php $book1 = new Book("Stephen Prata", "C Primer Plus"); ... $book2 = new Book("Joshua Bloch", "Effective Java", 2008); ``` 我们有两个不同的构造函数。 第一个带有 2 个参数,第二个带有 3 个参数。 ```php $ php constructors.php Author: C Primer Plus Title: Stephen Prata Published: not specified Author: Effective Java Title: Joshua Bloch Published: 2008 ``` 这是脚本的结果。 下一个代码示例使用工厂模式模拟构造函数重载。 它是 OOP 中的创建模式之一。 该模式创建对象时未指定要创建的对象的确切类。 更一般地,术语工厂方法通常用于指代主要目的是创建对象的任何方法。 `factory.php` ```php name = $name; return $cat; } public static function withAge($age) { $cat = new Cat(); $cat->age = $age; return $cat; } public static function fullCat($name, $age) { $cat = new Cat(); $cat->name = $name; $cat->age = $age; return $cat; } public function __toString() { return "Name: $this->name, Age: $this->age\n"; } } $cici = Cat::withName("Cici"); echo $cici; $missy = Cat::withAge(6); echo $missy; $lucky = Cat::fullCat("Lucky", 4); echo $lucky; ``` 上面的 PHP 脚本中有一个`Cat`工厂类。 它具有三种不同的静态功能。 它们每个都返回一个特定的`Cat`对象。 ```php private $name = "unspecified"; private $age = "unspecified"; ``` 我们有两个成员字段。 它们的初始值为“未指定”。 ```php public static function withName($name) { $cat = new Cat(); $cat->name = $name; return $cat; } ``` 这是静态的`withName()`函数。 此函数创建`Cat`类的实例。 它设置`name`成员字段并返回对象。 ```php $cici = Cat::withName("Cici"); echo $cici; ``` 我们使用其中一种工厂方法创建猫的实例。 我们回荡对象。 即调用该类的`__toString()`方法。 ```php $ php factory.php Name: Cici, Age: unspecified Name: unspecified, Age: 6 Name: Lucky, Age: 4 ``` 脚本的输出。 在 PHP 教程的这一部分中,我们继续讨论 PHP 中的面向对象编程。