it编程 > 编程语言 > Php

PHP关键字Self、Static和parent的区别详解

38人参与 2025-02-13 Php

简介

在使用php代码时,您可能经常会遇到parent::、static::和self::。但是当你第一次作为一个开发人员开始的时候,有时候你会很困惑,不知道它们是做什么的,以及它们之间的区别。

在我第一次作为开发人员开始工作后的很长一段时间里,我认为static::和self::是完全一样的。

parent::是什么?

假设我们有一个basetestcase类,它有一个setup方法:

class basetestcase
{
    public function setup(): void
    {
        echo 'run base test case set up here...';
    }
}
 
(new basetestcase())->setup();
 
// output is: "run base test case set up here...';

正如我们所看到的,当我们调用 setup 方法时,它按预期运行并输出文本。

现在,让我们假设我们想要创建一个新的featuretest类来继承basetestcase类。如果我们想运行featuretest类的setup方法,我们可以这样做:

class featuretest extends basetestcase
{
    //
}
 
(new featuretest())->setup();
 
// output is: "run base test case set up here...";

正如我们所看到的,我们没有在featuretest中定义setup方法,所以在basetestcase中定义的方法将被运行。

现在,假设我们想在运行featuretest中的setup方法时运行一些额外的逻辑。例如,如果这些类是作为phpunit测试的一部分使用的测试用例,那么我们可能需要在数据库中创建模型或设置测试值。

一开始,你可能(错误地)认为你可以在你的featuretest方法中定义setup方法,然后调用$this->setup()。老实说,当我第一次学习编程的时候,我总是陷入这个陷阱!

所以我们的代码可能看起来像这样:

class featuretest extends basetestcase
{
    public function setup(): void
    {
        $this->setup();
 
        echo 'run extra feature test set up here...';
    }
}
 
(new featuretest())->setup();

但是,您会发现,如果我们运行这段代码,我们最终会陷入一个循环,导致您的应用程序崩溃。这是因为我们递归地要求setup一遍又一遍地调用它自己。你可能会得到类似这样的输出:

fatal error: out of memory (allocated 31457280 bytes) (tried to allocate 262144 bytes) in /in/1mxtt on line 15
mmap() failed: [12] cannot allocate memory
mmap() failed: [12] cannot allocate memory
process exited with code 255.

因此,我们需要告诉php在basetestcase中使用setup方法,而不是使用$this->setup()。为了做到这一点,我们可以像这样用parent::setup()替换$this->setup()

class featuretest extends basetestcase
{
    public function setup(): void
    {
        parent::setup();
 
        echo 'run extra feature test set up here...';
    }
}
 
(new featuretest())->setup();
 
// output is: "run base test case set up here... run extra feature test set up here...";

现在,正如你所看到的,当我们在featuretest类中运行setup方法时,我们首先运行basetestcase中的代码,然后继续运行子类中定义的其余代码。

值得注意的是,您并不总是需要将parent::调用放在方法的顶部。实际上,您可以将其放置在方法中任何最适合代码目的的位置。例如,如果你想先在featuretest类中运行你的代码,然后在basetestcase类中运行,你可以像这样将parent::setup()调用移动到方法的底部:

self::是什么?

假设我们有一个model类,它有一个静态的connection属性和一个makeconnection方法。我们还可以想象我们有一个user类,它继承了model类并覆盖了connection属性。

这两个类可能看起来像这样:

class model
{
    public static string $connection = 'mysql';
 
    public function makeconnection(): void
    {
        echo 'making connection to: '.self::$connection;
    }
}
 
class user extends model
{
    public static string $connection = 'postgres';
}

现在让我们在两个类上运行makeconnection方法,看看我们会得到什么输出:

(new model())->makeconnection();
 
// output is: "making connection to mysql"
 
(new user())->makeconnection();
 
// output is: "making connection to mysql";

正如我们所看到的,这两个调用都导致了model类的connection属性被使用。这是因为self使用了在方法所在的类上定义的属性。在这两种情况下,makeconnection方法在model类上是打开的,因为user类上不存在一个方法。

为了进一步说明这一点,我们将向user类添加makeconnection方法,如下所示:

class model
{
    public static string $connection = 'mysql';
 
    public function makeconnection(): void
    {
        echo 'making connection to: '.self::$connection;
    }
}
 
class user extends model
{
    public static string $connection = 'postgres';
 
    public function makeconnection(): void
    {
        echo 'making connection to: '.self::$connection;
    }
}

现在,如果我们再次调用这两个方法,我们会得到以下输出:

(new model())->makeconnection();
 
// output is: "making connection to mysql"
 
(new user())->makeconnection();
 
// output is: "making connection to postgres";

正如您所看到的,对makeconnection的调用现在将使用user类上的connection字段,因为这是该方法存在的地方。

static::是什么?

现在我们已经知道了self::的作用,让我们来看看static::

为了更好地理解它的作用,让我们更新上面的代码示例,使用static::而不是self::,如下所示:

class model
{
    public static $connection = 'mysql';
 
    public function makeconnection()
    {
        echo 'making connection to: '.static::$connection;
    }
}
 
class user extends model
{
    public static $connection = 'postgres';
}

如果我们在两个类上运行makeconnection方法,我们会得到以下输出:

(new model())->makeconnection();
 
// output is: "making connection to mysql"
 
(new user())->makeconnection();
 
// output is: "making connection to postgres";

正如我们所看到的,这个输出与我们之前使用self::$connection时不同。对user类上的makeconnection方法的调用使用了user类上的connection属性,而不是model类(该方法实际所属的类)。这是由于php中一个名为“后期静态绑定”的特性。

根据php文档:这个特性被命名为“后期静态绑定”,从内部的角度考虑。“后期绑定”来自这样一个事实,即static::将不会使用定义方法的类来解析,而是使用运行时信息来计算。它也被称为“静态绑定”,因为它可以用于(但不限于)静态方法调用。"

因此,在我们的示例中,使用了user类上的connection属性,因为我们在同一个类上调用了makeconnection方法。

然而,值得注意的是,如果connection属性在user类上不存在,它将回退到使用model类上的属性。

什么时候使用self::或 static::?

现在我们对self::static::之间的区别有了一个大致的了解,让我们快速介绍一下如何决定在自己的代码中使用哪一个。

这一切都取决于您正在编写的代码的用例。

一般来说,我通常会使用static::而不是self::,因为我希望我的类是可扩展的

例如,假设我想写一个类,我完全打算由子类继承(例如上面示例中的basetestcase类)。除非我真的想防止子类重写属性或方法,否则我想使用static::

这意味着我可以有信心,如果我重写任何静态方法或字段,我的子类将使用我的重写。我无法告诉你有多少次我在代码中遇到了bug,当我在父类中使用self::时,然后无法弄清楚为什么我的子类没有使用我的重写!

另一方面,一些开发人员可能会争辩说,你应该坚持使用self::,因为你不应该真的从类继承。他们可能会建议你应该遵循“组合优于继承”的原则。我不会深入研究这个话题,因为这是未来的另一篇博客文章。但从广义上说,简单地说,这个原则指出,你应该避免通过将所有逻辑放在父类中来为类添加功能,而是通过用许多更小的类来构建类来添加功能。

这意味着如果你遵循这个原则,你就不需要使用static::,因为你永远不会扩展你的父类。如果你想确保类不能被扩展,你甚至可以更进一步,在定义类时使用final关键字。使用final关键字可以防止类被继承,所以它可以减少您对类可能意外扩展并引入任何潜在错误的担忧。

一般来说,最好在编写代码时根据具体情况决定应该使用static::还是self::

以上就是php关键字self、static和parent的区别详解的详细内容,更多关于php关键字self、static和parent的资料请关注代码网其它相关文章!

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

PHP实现用户认证与权限管理的实现

02-13

PHP执行php.exe -v命令报错的解决方案

02-13

在ThinkPHP中实现文件上传的实用示例

02-13

PHP使用PHPExcel读取excel数据并批量上传到数据库

02-13

ThinkPHP中的接口的安全防护措施小结

02-13

基于PHP实现一个简单的http服务器

02-13

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论