• 设为首页
  • 收藏本站
  • 积分充值
  • VIP赞助
  • 手机版
  • 微博
  • 微信
    微信公众号 添加方式:
    1:搜索微信号(888888
    2:扫描左侧二维码
  • 快捷导航
    福建二哥 门户 查看主题

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

    发布者: 404号房间 | 发布时间: 2025-6-14 13:16| 查看数: 135| 评论数: 0|帖子模式

    简介

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

    parent::是什么?

    假设我们有一个BaseTestCase类,它有一个setUp方法:
    1. class BaseTestCase
    2. {
    3.     public function setUp(): void
    4.     {
    5.         echo 'Run base test case set up here...';
    6.     }
    7. }

    8. (new BaseTestCase())->setUp();

    9. // Output is: "Run base test case set up here...';
    复制代码
    正如我们所看到的,当我们调用 setUp 方法时,它按预期运行并输出文本。
    现在,让我们假设我们想要创建一个新的
    1. FeatureTest
    复制代码
    类来继承
    1. BaseTestCase
    复制代码
    类。如果我们想运行
    1. FeatureTest
    复制代码
    类的
    1. setUp
    复制代码
    方法,我们可以这样做:
    1. class FeatureTest extends BaseTestCase
    2. {
    3.     //
    4. }

    5. (new FeatureTest())->setUp();

    6. // Output is: "Run base test case set up here...";
    复制代码
    正如我们所看到的,我们没有在FeatureTest中定义setUp方法,所以在BaseTestCase中定义的方法将被运行。
    现在,假设我们想在运行FeatureTest中的setUp方法时运行一些额外的逻辑。例如,如果这些类是作为PhpUnit测试的一部分使用的测试用例,那么我们可能需要在数据库中创建模型或设置测试值。
    一开始,你可能(错误地)认为你可以在你的FeatureTest方法中定义setUp方法,然后调用$this->setUp()。老实说,当我第一次学习编程的时候,我总是陷入这个陷阱!
    所以我们的代码可能看起来像这样:
    1. class FeatureTest extends BaseTestCase
    2. {
    3.     public function setUp(): void
    4.     {
    5.         $this->setUp();

    6.         echo 'Run extra feature test set up here...';
    7.     }
    8. }

    9. (new FeatureTest())->setUp();
    复制代码
    但是,您会发现,如果我们运行这段代码,我们最终会陷入一个循环,导致您的应用程序崩溃。这是因为我们递归地要求
    1. setUp
    复制代码
    一遍又一遍地调用它自己。你可能会得到类似这样的输出:
    1. Fatal error: Out of memory (allocated 31457280 bytes) (tried to allocate 262144 bytes) in /in/1MXtt on line 15
    2. mmap() failed: [12] Cannot allocate memory
    3. mmap() failed: [12] Cannot allocate memory
    4. Process exited with code 255.
    复制代码
    因此,我们需要告诉PHP在
    1. BaseTestCase
    复制代码
    中使用
    1. setUp
    复制代码
    方法,而不是使用
    1. $this->setUp()
    复制代码
    。为了做到这一点,我们可以像这样用
    1. parent::setUp()
    复制代码
    替换
    1. $this->setUp()
    复制代码
    1. class FeatureTest extends BaseTestCase
    2. {
    3.     public function setUp(): void
    4.     {
    5.         parent::setUp();

    6.         echo 'Run extra feature test set up here...';
    7.     }
    8. }

    9. (new FeatureTest())->setUp();

    10. // 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::是什么?

    假设我们有一个
    1. Model
    复制代码
    类,它有一个静态的
    1. connection
    复制代码
    属性和一个
    1. makeConnection
    复制代码
    方法。我们还可以想象我们有一个
    1. User
    复制代码
    类,它继承了
    1. Model
    复制代码
    类并覆盖了
    1. connection
    复制代码
    属性。
    这两个类可能看起来像这样:
    1. class Model
    2. {
    3.     public static string $connection = 'mysql';

    4.     public function makeConnection(): void
    5.     {
    6.         echo 'Making connection to: '.self::$connection;
    7.     }
    8. }

    9. class User extends Model
    10. {
    11.     public static string $connection = 'postgres';
    12. }
    复制代码
    现在让我们在两个类上运行
    1. makeConnection
    复制代码
    方法,看看我们会得到什么输出:
    1. (new Model())->makeConnection();

    2. // Output is: "Making connection to mysql"

    3. (new User())->makeConnection();

    4. // Output is: "Making connection to mysql";
    复制代码
    正如我们所看到的,这两个调用都导致了Model类的connection属性被使用。这是因为self使用了在方法所在的类上定义的属性。在这两种情况下,makeConnection方法在Model类上是打开的,因为User类上不存在一个方法。
    为了进一步说明这一点,我们将向User类添加makeConnection方法,如下所示:
    1. class Model
    2. {
    3.     public static string $connection = 'mysql';

    4.     public function makeConnection(): void
    5.     {
    6.         echo 'Making connection to: '.self::$connection;
    7.     }
    8. }

    9. class User extends Model
    10. {
    11.     public static string $connection = 'postgres';

    12.     public function makeConnection(): void
    13.     {
    14.         echo 'Making connection to: '.self::$connection;
    15.     }
    16. }
    复制代码
    现在,如果我们再次调用这两个方法,我们会得到以下输出:
    1. (new Model())->makeConnection();

    2. // Output is: "Making connection to mysql"

    3. (new User())->makeConnection();

    4. // Output is: "Making connection to postgres";
    复制代码
    正如您所看到的,对
    1. makeConnection
    复制代码
    的调用现在将使用
    1. User
    复制代码
    类上的
    1. connection
    复制代码
    字段,因为这是该方法存在的地方。

    static::是什么?

    现在我们已经知道了
    1. self::
    复制代码
    的作用,让我们来看看
    1. static::
    复制代码

    为了更好地理解它的作用,让我们更新上面的代码示例,使用
    1. static::
    复制代码
    而不是
    1. self::
    复制代码
    ,如下所示:
    1. class Model
    2. {
    3.     public static $connection = 'mysql';

    4.     public function makeConnection()
    5.     {
    6.         echo 'Making connection to: '.static::$connection;
    7.     }
    8. }

    9. class User extends Model
    10. {
    11.     public static $connection = 'postgres';
    12. }
    复制代码
    如果我们在两个类上运行
    1. makeConnection
    复制代码
    方法,我们会得到以下输出:
    1. (new Model())->makeConnection();

    2. // Output is: "Making connection to mysql"

    3. (new User())->makeConnection();

    4. // Output is: "Making connection to postgres";
    复制代码
    正如我们所看到的,这个输出与我们之前使用
    1. self::$connection
    复制代码
    时不同。对
    1. User
    复制代码
    类上的
    1. makeConnection
    复制代码
    方法的调用使用了
    1. User
    复制代码
    类上的
    1. connection
    复制代码
    属性,而不是
    1. Model
    复制代码
    类(该方法实际所属的类)。这是由于PHP中一个名为“后期静态绑定”的特性。
    1. 根据PHP文档:这个特性被命名为“后期静态绑定”,从内部的角度考虑。“后期绑定”来自这样一个事实,即static::将不会使用定义方法的类来解析,而是使用运行时信息来计算。它也被称为“静态绑定”,因为它可以用于(但不限于)静态方法调用。"
    复制代码
    因此,在我们的示例中,使用了
    1. User
    复制代码
    类上的
    1. connection
    复制代码
    属性,因为我们在同一个类上调用了
    1. makeConnection
    复制代码
    方法。
    然而,值得注意的是,如果
    1. connection
    复制代码
    属性在
    1. User
    复制代码
    类上不存在,它将回退到使用
    1. Model
    复制代码
    类上的属性。

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

    现在我们对
    1. self::
    复制代码
    1. static::
    复制代码
    之间的区别有了一个大致的了解,让我们快速介绍一下如何决定在自己的代码中使用哪一个。
    这一切都取决于您正在编写的代码的用例。
    一般来说,我通常会使用
    1. static::
    复制代码
    而不是
    1. self::
    复制代码
    ,因为我希望我的类是可扩展的
    例如,假设我想写一个类,我完全打算由子类继承(例如上面示例中的
    1. BaseTestCase
    复制代码
    类)。除非我真的想防止子类重写属性或方法,否则我想使用
    1. static::
    复制代码

    这意味着我可以有信心,如果我重写任何静态方法或字段,我的子类将使用我的重写。我无法告诉你有多少次我在代码中遇到了bug,当我在父类中使用
    1. self::
    复制代码
    时,然后无法弄清楚为什么我的子类没有使用我的重写!
    另一方面,一些开发人员可能会争辩说,你应该坚持使用
    1. self::
    复制代码
    ,因为你不应该真的从类继承。他们可能会建议你应该遵循“组合优于继承”的原则。我不会深入研究这个话题,因为这是未来的另一篇博客文章。但从广义上说,简单地说,这个原则指出,你应该避免通过将所有逻辑放在父类中来为类添加功能,而是通过用许多更小的类来构建类来添加功能。
    这意味着如果你遵循这个原则,你就不需要使用
    1. static::
    复制代码
    ,因为你永远不会扩展你的父类。如果你想确保类不能被扩展,你甚至可以更进一步,在定义类时使用
    1. final
    复制代码
    关键字。使用
    1. final
    复制代码
    关键字可以防止类被继承,所以它可以减少您对类可能意外扩展并引入任何潜在错误的担忧。
    一般来说,最好在编写代码时根据具体情况决定应该使用
    1. static::
    复制代码
    还是
    1. self::
    复制代码

    以上就是PHP关键字Self、Static和parent的区别详解的详细内容,更多关于PHP关键字Self、Static和parent的资料请关注脚本之家其它相关文章!

    来源:https://www.jb51.net/program/333377jhd.htm
    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

    最新评论

    QQ Archiver 手机版 小黑屋 福建二哥 ( 闽ICP备2022004717号|闽公网安备35052402000345号 )

    Powered by Discuz! X3.5 © 2001-2023

    快速回复 返回顶部 返回列表