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::

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Disruptor 有哪些典型的使用场景?

大家好,我是君哥。 Disruptor 是一款高性能的内存有界队列,它通过内存预分配、无锁并发、解决伪共享问题、使用 RingBuffer 取代阻塞队列等措施来大幅提升队列性能。 但开发者们往往对它的使用场景不太了解,到底应该在哪些场景使用呢&#…

[MySQL报错]关于发生net start mysql 服务无法启动,服务没有报告任何错误的五种解决方案。

咋直接进入主题。 我遇到的问题是net start mysql 服务无法启动,服务没有报告任何错误 其问题出在哪里呢 一.ini文件配置问题 在于你没有给你下载好的mysql文件中配置.ini文件。 该如何配置呢。那就是先在文件夹中创建一个文本文件,把下面内容复制进去…

HTML5新特性|01 音频视频

音频 1、Audio (音频) HTML5提供了播放音频文件的标准 2、control(控制器) control 属性供添加播放、暂停和音量控件 3、标签: <audio> 定义声音 <source> 规定多媒体资源,可以是多个<!DOCTYPE html> <html lang"en"> <head><…

goView二开低代码平台1.0

官网文档地址&#xff1a;GoView 说明文档 | 低代码数据可视化开发平台 简介&#xff1a;GoView 是一个拖拽式低代码数据可视化开发平台&#xff0c;通过拖拽创建数据大屏&#xff0c;使用Vue3框架&#xff0c;Ts语言和NaiveUI组件库创建的开源项目。安装步骤和地址文档里都有…

2024年中国新能源汽车用车发展怎么样 PaperGPT(一)

概述 在国家政策的强力扶持下&#xff0c;2024年中国新能源汽车市场迎来了新的发展机遇。本文将基于《中国新能源汽车用车报告&#xff08;2024年&#xff09;》的数据&#xff0c;对新能源汽车的市场发展和用车趋势概述。 新能源汽车市场发展 政策推动&#xff1a;国家和地…

数据表中列的完整性约束概述

文章目录 一、完整性约束概述二、设置表字段的主键约束三、设置表字段的外键约束四、设置表字段的非空约束五、设置表字段唯一约束六、设置表字段值自动增加七、设置表字段的默认值八、调整列的完整性约束 一、完整性约束概述 完整性约束条件是对字段进行限制&#xff0c;要求…

Unity网络通信相关

Socket 通信一张图搞定 谁提供服务谁绑定端口&#xff0c;建立Listener,写Host

ChatGPT 与 AGI:人工智能的当下与未来走向全解析

在人工智能的浩瀚星空中&#xff0c;AGI&#xff08;通用人工智能&#xff09;无疑是那颗最为璀璨且备受瞩目的星辰。OpenAI 对 AGI 的定义为“在最具经济价值的任务中超越人类的高度自治系统”&#xff0c;并勾勒出其发展的五个阶段&#xff0c;当下我们大多处于以 ChatGPT 为…

七次课掌握 Photoshop

mediaTEA 的《七次课掌握 Photoshop》系列文章以循序渐进的教学方式&#xff0c;帮助学员在短时间内高效掌握 Photoshop 的核心功能。 从基础知识到高级技巧&#xff0c;课程涵盖图像编辑、选区与抠图、形状与文字、绘画与修饰、调整与混合、样式与滤镜&#xff0c;以及自动化与…

【Goland】怎么执行 go mod download

1、终端的执行 go mod tidy 2、终端执行不行的话&#xff0c;就可以通过右击go.mod文件来执行&#xff1b; 3、也可以按住Ctrl点击这个包安装&#xff1b;

玩转OCR | 腾讯云智能结构化OCR初次体验

目录 一、什么是OCR&#xff08;需要了解&#xff09; 二、产品概述与核心优势 产品概述 智能结构化能做什么 举例说明&#xff08;选看&#xff09; 1、物流单据识别 2、常见证件识别 3、票据单据识别 4、行业材料识别 三、产品特性 高精度 泛化性 易用性 四、…

基于BiLSTM和随机森林回归模型的序列数据预测

本文以新冠疫情相关数据集为案例,进行新冠数量预测。(源码请留言或评论) 首先介绍相关理论概念: 序列数据特点 序列数据是人工智能和机器学习领域的重要研究对象,在多个应用领域展现出独特的特征。这种数据类型的核心特点是 元素之间的顺序至关重要 ,反映了数据内在的时…

安装、快速入门

安装 sudo docker run \-e RABBITMQ_DEFAULT_USERroot \-e RABBITMQ_DEFAULT_PASS123456 \-v rabbitmq-plugins:/plugins \--name rabbitmq \--hostname rabbitmq \-p 15672:15672 \-p 5672:5672 \-d \rabbitmq 1、防火墙开放两个端口 2、RabbitMQ 安装 Web 插件&#xff1a; …

JVM学习:CMS和G1收集器浅析

总框架 一、Java自动内存管理基础 1、运行时数据区 运行时数据区可分为线程隔离和线程共享两个维度&#xff0c;垃圾回收主要是针对堆内存进行回收 &#xff08;1&#xff09;线程隔离 程序计数器 虚拟机多线程是通过线程轮流切换、分配处理器执行时间来实现的。为了线程切换…

用uniapp写一个播放视频首页页面代码

效果如下图所示 首页有导航栏&#xff0c;搜索框&#xff0c;和视频列表&#xff0c; 导航栏如下图 搜索框如下图 视频列表如下图 文件目录 视频首页页面代码如下 <template> <view class"video-home"> <!-- 搜索栏 --> <view class…

uniapp 判断多选、选中取消选中的逻辑处理

一、效果展示 二、代码 1.父组件: :id=“this.id” : 给子组件传递参数【id】 @callParentMethod=“takeIndexFun” :给子组件传递方法,这样可以在子组件直接调用父组件的方法 <view @click="$refs.member.open()"

影刀进阶指令 | Kimi (对标ChatGPT)

文章目录 影刀进阶指令 | Kimi &#xff08;对标ChatGPT&#xff09;一. 需求二. 流程三. 实现3.1 流程概览3.2 流程步骤讲解1\. 确定问题2\. 填写问题并发送3\. 检测答案是否出完 四. 运维 影刀进阶指令 | Kimi &#xff08;对标ChatGPT&#xff09; 简单讲讲RPA调用kimi实现…

【面试系列】深入浅出 Spring Boot

熟悉SpringBoot&#xff0c;对常用注解、自动装配原理、Jar启动流程、自定义Starter有一定的理解&#xff1b; 面试题 Spring Boot 的核心注解是哪个&#xff1f;它主要由哪几个注解组成的&#xff1f;Spring Boot的自动配置原理是什么&#xff1f;你如何理解 Spring Boot 配置…

MySQL root用户密码忘记怎么办(Reset root account password)

在使用MySQL数据库的的过程中&#xff0c;不可避免的会出现忘记密码的现象。普通用户的密码如果忘记&#xff0c;可以用更高权限的用户&#xff08;例如root&#xff09;进行重置。但是如果root用户的密码忘记了&#xff0c;由于root用户本身就是最高权限&#xff0c;那这个方法…

Java之内部类*

将一个类定义在另一个类或者一个方法的内部&#xff0c;前者称为内部类&#xff0c;后者称为外部类 实例内部类&#xff1a;实力内部类所处的位置与外部类成员位置相同&#xff0c;因此也受public private等访问限定符的约束静态内部类&#xff08;static&#xff09;匿名内部…