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

简介

在使用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文档。https://www.php.net/manual/en/language.oop5.late-static-bindings.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::

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/945291.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

今日收获(C语言)

一.文件的打开 有这样一个结构体,它内部是文件信息区,文件信息区中的变化可以影响到硬盘中的数据。这个结构体的名字是FILE。我们如果想要写代码对文件进行各种操作,就需要一个指向文件信息区的指针,这个指针的类型是FILE*&#…

【C++】九九乘法表编程题详解与多角度对比分析

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯题目概述题目描述 💯老师的实现方法代码解析优点不足 💯我的实现方法代码解析优点不足 💯实现方法对比💯优化与扩展代码优化…

RK3568 bsp 9 - USB调试记录

文章目录 1、环境介绍2、RK3568 USB资源介绍3、配置目标4、dts配置4.1、USB3.0 OTG4.2、USB2.0 Host 2 和 USB2.0 Host 3 5、kernel配置5.1、USB PHY CONFIG5.2、USB Host CONFIG5.3、USB OTG CONFIG5.4、USB外设CONFIG5.4.1、Mass Storage Class CONFIG5.4.2、USB HID CONFIG …

图像描述/字幕开源模型与数据集全览

图像描述/字幕(Image Captioning)是用文字描述图像内容的任务,属于计算机视觉和自然语言处理的交叉领域。大多数图像描述系统采用编码器-解码器(encoder-decoder)框架,其中输入图像被编码为中间表示形式&am…

mongodb(6.0.15)安装注意事项,重装系统后数据恢复

window10系统 上周重装了系统,环境变量之类的都没有了。现在要恢复。 我电脑里之前的安装包没有删除(虽然之前也没在C盘安装,但是找不到了,所以需要重新下载安装),长下图这样。这个不是最新版本&#xff0…

Redis单线程快的原因

基于内存操作:Redis将数据存储在内存中,使得数据的读写速度极快,这是其性能优势的主要原因。单线程避免上下文切换:在多线程环境下,CPU核数有限,线程上下文切换会带来性能损耗。Redis采用单线程&#xff0c…

IOS safari 播放 mp4 遇到的坎儿

起因 事情的起因是调试 IOS 手机下播放服务器接口返回的 mp4 文件流失败。对于没调试过移动端和 Safari 的我来说着实费了些功夫,网上和AI也没有讲明白。好在最终大概理清楚了,在这里整理出来供有缘人参考。 问题 因为直接用 IOS 手机的浏览器打开页面…

import org.springframework.data.jpa.repository.JpaRepository<T, ID>;

org.springframework.data.jpa.repository.JpaRepository<T, ID> 接口中的 ID 类型参数。 理解 JpaRepository<T, ID> 中的 T 和 ID&#xff1a; T (Type): T 代表的是你想要操作的 实体类 的类型。例如&#xff0c;如果你有一个名为 User 的实体类&#xff0c;那…

Ubuntu网络配置(桥接模式, nat模式, host主机模式)

windows上安装了vmware虚拟机&#xff0c; vmware虚拟机上运行着ubuntu系统。windows与虚拟机可以通过三种方式进行通信。分别是桥接模式&#xff1b;nat模式&#xff1b;host模式 一、桥接模式 所谓桥接模式&#xff0c;也就是虚拟机与宿主机处于同一个网段&#xff0c; 宿主机…

RT-DETR融合[CVPR2020]EfficientDet中的BiFPN结构

RT-DETR使用教程&#xff1a; RT-DETR使用教程 RT-DETR改进汇总贴&#xff1a;RT-DETR更新汇总贴 《EfficientDet: Scalable and Efficient Object Detection》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/abs/1911.09070 代码链接&#xff1a;https://github.com/…

Unity 实现Canvas显示3D物体

新建一个UI相机&#xff0c;选择渲染层为UI 将主相机的渲染层去掉UI层 、 将Canvas的RenderMode设置为Screen Space - Camera,将RenderCamera设置为UI相机 新建3D物体的UI父物体&#xff0c;并将3D物体的层级设置为UI层 适当的放缩3DObjParent&#xff0c;让3D物体能显示出来…

RabbitMQ基础篇之Java客户端快速入门

文章目录 需求 项目设置与依赖管理 配置RabbitMQ的连接信息创建队列与消息发送创建消费者&#xff08;消息接收&#xff09;环境准备与操作 需求 利用控制台创建队列 simple.queue在 publisher 服务中&#xff0c;利用 SpringAMQP 直接向 simple.queue 发送消息在 consumer 服…

解决SecureCRT登录后无法用Tab键补齐命令

解决SecureCRT登录后无法用Tab键补齐命令 打开SecureCRT软件&#xff0c;选项—全局选项—常规—默认的会话设置—编辑默认的设置—连接—-右侧的协议选择为Telnet &#xff1b;在左侧&#xff0c;选择telnet&#xff0c;在右侧的高级框里“强制每次一个字符模式”勾上&#x…

音视频入门基础:MPEG2-TS专题(23)——通过FFprobe显示TS流每个packet的信息

音视频入门基础&#xff1a;MPEG2-TS专题系列文章&#xff1a; 音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;1&#xff09;——MPEG2-TS官方文档下载 音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;2&#xff09;——使用FFmpeg命令生成ts文件 音视频入门基础…

运行python程序报错 undefined symbol: ffi_type_uint32 的参考解决方法

文章目录 写在前面一、问题描述二、解决方法参考链接 写在前面 自己的测试环境&#xff1a; Ubuntu20.04 ROS-Noetic 一、问题描述 运行 python 程序出现如下问题&#xff1a; Traceback (most recent call last):File "<string>", line 1, in <module&…

go 模拟TCP粘包和拆包,及解决方法

1. 什么是 TCP 粘包与拆包&#xff1f; 粘包&#xff08;Sticky Packet&#xff09; 粘包是指在发送多个小的数据包时&#xff0c;接收端会将这些数据包合并成一个数据包接收。由于 TCP 是面向流的协议&#xff0c;它并不会在每次数据发送时附加边界信息。所以当多个数据包按顺…

Day10补代码随想录 理论基础|232.用栈实现队列|225.用队列实现栈|20.有效的括号|1047.删除字符串中的所有相邻重复项

栈和队列理论基础 抽象认识 栈是先进后出(FIFO)&#xff0c;队列是先进先出(LIFO) 队首(先进))队尾(后进)栈顶(后进)栈底(先进) 栈(Stack) 只在一端进行进出操作(只在一端进一端出)像个篮球框&#xff0c;取用篮球从一端进出。 /进栈 int a[1000];//足够大的栈空间 int top-1…

Gemma2 2B 模型的model.safetensors.index.json文件解析

Gemma2 2B 模型的 model.safetensors.index.json 文件解析 在使用 Gemma2 2B 模型或其他大型预训练模型时&#xff0c;model.safetensors.index.json 文件起到了索引的作用&#xff0c;它帮助我们了解模型的结构、参数存储方式以及如何加载模型的具体权重。本博客将深入解析该…

大模型系列——旋转位置编码和长度外推

绝对位置编码 旋转位置编码 论文中有个很直观的图片展示了旋转变换的过程&#xff1a; 对于“我”对应的d维向量&#xff0c; 拆分成d/2组以后&#xff0c;每组对应一个角度&#xff0c;若1对应的向量为(x1,x2)&#xff0c;应用旋转位置编码&#xff0c;相当于这个分量旋转了m…

网络安全威胁2024年中报告

下载地址&#xff1a; 网络安全威胁2024年中报告-奇安信