类与对象

PHP 类是一个包含代码的文件。类文件是一个实际存在于硬盘上的文件。如果你关掉电脑,然后再打开,类文件仍然存在。它包含的代码是在执行过程中创建对象的模板。类文件可以包含属性和行为。一旦类被实例化,属性就能在内存中保存数据。行为将由方法或函数处理。您可以使用访问器和突变器方法来更改类的属性值。

Dog.php类文件
<?php
namespace Animal;

class Dog
{
    public function returnSound(): string
    {
        return "Bark";
    }
}

前面的示例类是一个 PHP 文件,其中包含命名空间声明、类名和一个名为 returnSound() 的方法或函数,该方法或函数返回一个 "Bark" 字符串。

另一方面,对象是类文件的实例。该对象物理上存在于计算机的 RAM 中,这是易失性存储器。这意味着如果您关闭计算机或停止程序,您就会丢失该对象。仅当您运行程序时才会创建该对象。

PHP 中,当您执行程序时,PHP 将从硬盘加载类文件,以创建临时存在于 RAM 中的对象实例。从字面上看,类是 PHP 在程序运行时创建对象的模板。

我们将使用一个消费者类来利用或使用 Dog.php 类,并使用一个变量来保存类的实例,该实例是一个对象:

Display.php类文件
<?php
namespace Animal;

class Display
{
    public function outputSound()
    {
        $dog = new Dog();
        echo $dog->returnSound();
    }
}

Display 类是另一个类,可以将其视为消费者类或示例程序的起点。在这个类中,我们有一个 outputSound() 方法,用于 echo 对象的 returnSound() 方法的返回值。在 outputSound() 方法中,我们编写了 PHP 使用 new 关键字创建 Dog 类实例的指令:

image 2023 10 23 11 40 48 835
Figure 1. Figure 4.1 – Assigning the Dog object to the $dog variable

当 PHP 执行 outputSound() 方法时,它将根据存储在计算机驱动器中的 Dog.php 类文件创建一个对象,然后将该实例或对象暂时存储在计算机内存中。$dog 变量将与 Dog 类实例或对象进行映射。无论何时使用对象的方法或属性,基本上都是从电脑内存而非 Dog.php 类文件访问对象。要进一步理解这一点,我们需要讨论一下 PHP 中的引用(References),这将在下一个小节中介绍。

现在,既然我们已经创建了一个 Dog.php 类文件的新实例并将其赋值给 $dog 变量,我们就可以访问 Dog 对象的方法、函数或属性了,这取决于它们的可见性。我们将在本章的 "OOP 中的封装" 部分讨论可见性。在我们的示例中,Dog.php 类的 returnSound() 方法被定义为 public,因此我们可以通过 Display.php 类的 outputSound() 方法访问该方法:$dog->returnSound();

PHP 中的引用和对象

引用到底是什么?在 PHP 中,它是一个别名,或者说是一个或多个变量指向特定内容的一种方式。在前面的 Dog 对象示例中,我们创建了一个 Dog.php 类的实例,并将其赋值给 $dog 变量。$dog 变量本身并不包含 Dog 对象或实例的内存地址;它只是包含一个标识符,以便指向存储在内存中的 Dog 对象。也就是说,你可以让 $dog1$dog2 变量指向同一个对象。让我们修改 Dog.phpDisplay.php 类来演示这一概念。

我们将这样修改 Dog.php

<?php
namespace Animal;

class Dog
{
    private string $sound;

    public function __construct()
    {
        $this->setSound("Bark");
    }

    public function returnSound(): string
    {
        return $this->getSound();
    }

    /**
    * @return string
    */
    public function getSound(): string
    {
        return $this->sound;
    }

    /**
    * @param string $sound
    */
    public function setSound(string $sound): void
    {
        $this->sound = $sound;
    }
}

我们将修改 Display.php 类如下:

<?php
namespace Animal;

class Display
{
    public function outputSound()
    {
        $dog1 = new Dog();
        $dog2 = $dog1;

        $dog1->setSound("Barky Bark");

        // Will return "Barky Bark" which was set to $dog1.
        echo $dog2->returnSound();
    }
}

我们修改了 Dog.php 类,以便我们可以在实例化它后更改它返回的声音。在 Display.php 类中,您会注意到我们引入了一个新变量 $dog2,并为其分配了 $dog1。我们只有一个 Dog 对象实例,并且 $dog1$dog2 变量具有相同的标识符并且引用相同的事物。这是代表这个概念的图表:

image 2023 10 23 13 09 48 298
Figure 2. Figure 4.2 – What happens to $dog1’s property also happens to $dog2

因此,如果我们运行 $dog2->returnSound(),它将返回我们在 $dog1 中设置的更新后的字符串,即使我们在将 $dog1 分配给 $dog2 后更改了 $sound 属性。

那么,如果您不希望 $dog2 受到 $dog1 属性发生的影响,但仍想创建该对象的副本或副本,该怎么办? 您可以像这样使用 PHP 的 clone 关键字:

<?php

namespace Animal;

class Display
{
    public function outputSound()
    {
        $dog1 = new Dog();
        $dog2 = clone $dog1;

        $dog1->setSound("Barky Bark");

        // Will return "Bark".
        echo $dog2->returnSound();
    }
}

这次,$dog2 将返回由其构造函数分配给 $dog1$sound 属性的原始 Bark 字符串值。这是一个图表供您参考以理解这一点:

image 2023 10 23 13 12 06 933
Figure 3. Figure 4.3 – Clone object

由于 $dog2 在 actor 改变 $dog1$sound 属性之前就已被克隆,因此 $dog2 将保留旧值。无论 $dog1 发生什么,$dog2 都不会再自动发生,因为它们不再引用内存中的同一个对象:

image 2023 10 23 13 12 49 008
Figure 4. Figure 4.4 – Class versus object

总之,PHP 类是一个包含 PHP 能够创建对象的模板的文件。当使用 new 关键字并执行时,PHP 获取 class 文件并生成 class 文件的实例,并将其存储在计算机的内存中,这就是我们所说的对象。

现在我们已经阐明并解释了 PHP 中对象和类之间的区别,现在我们可以从抽象开始讨论 OOP 的四大支柱。