当前位置: 首页 > news >正文

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

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

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

parent::是什么?

假设我们有一个BaseTestCase类,它有一个setUp方法:

1

2

3

4

5

6

7

8

9

10

11

classBaseTestCase

{

publicfunctionsetUp(): void

{

echo'Run base test case set up here...';

}

}

(newBaseTestCase())->setUp();

// Output is: "Run base test case set up here...';

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

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

1

2

3

4

5

6

7

8

classFeatureTestextendsBaseTestCase

{

//

}

(newFeatureTest())->setUp();

// Output is: "Run base test case set up here...";

正如我们所看到的,我们没有在FeatureTest中定义setUp方法,所以在BaseTestCase中定义的方法将被运行。

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

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

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

1

2

3

4

5

6

7

8

9

10

11

classFeatureTestextendsBaseTestCase

{

publicfunctionsetUp(): void

{

$this->setUp();

echo'Run extra feature test set up here...';

}

}

(newFeatureTest())->setUp();

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

1

2

3

4

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()

1

2

3

4

5

6

7

8

9

10

11

12

13

classFeatureTestextendsBaseTestCase

{

publicfunctionsetUp(): void

{

parent::setUp();

echo'Run extra feature test set up here...';

}

}

(newFeatureTest())->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属性。

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

classModel

{

publicstaticstring$connection='mysql';

publicfunctionmakeConnection(): void

{

echo'Making connection to: '.self::$connection;

}

}

classUserextendsModel

{

publicstaticstring$connection='postgres';

}

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

1

2

3

4

5

6

7

(newModel())->makeConnection();

// Output is: "Making connection to mysql"

(newUser())->makeConnection();

// Output is: "Making connection to mysql";

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

classModel

{

publicstaticstring$connection='mysql';

publicfunctionmakeConnection(): void

{

echo'Making connection to: '.self::$connection;

}

}

classUserextendsModel

{

publicstaticstring$connection='postgres';

publicfunctionmakeConnection(): void

{

echo'Making connection to: '.self::$connection;

}

}

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

1

2

3

4

5

6

7

(newModel())->makeConnection();

// Output is: "Making connection to mysql"

(newUser())->makeConnection();

// Output is: "Making connection to postgres";

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

static::是什么?

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

classModel

{

publicstatic$connection='mysql';

publicfunctionmakeConnection()

{

echo'Making connection to: '.static::$connection;

}

}

classUserextendsModel

{

publicstatic$connection='postgres';

}

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

1

2

3

4

5

6

7

(newModel())->makeConnection();

// Output is: "Making connection to mysql"

(newUser())->makeConnection();

// Output is: "Making connection to postgres";

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

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

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

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

http://www.jsqmd.com/news/590475/

相关文章:

  • OpenClaw多账户管理:千问3.5-27B处理跨平台身份切换
  • Stable-Diffusion-v1-5-archive部署故障排查:端口/服务/日志三步定位法
  • Qwen1.5-1.8B-Chat-GPTQ-Int4效果展示:合同条款风险点识别与通俗化改写示例
  • SUNFLOWER MATCH LAB模型原理浅析:从操作系统视角看资源调度
  • Linux新手必看:5分钟掌握文件与目录的创建删除技巧(附常用命令清单)
  • NEURAL MASK 社区贡献指南:如何向开源项目提交代码与模型
  • 3个关键步骤掌握MTKClient:联发科设备底层调试与救砖全攻略
  • Speech Seaco Paraformer ASR实测:5分钟快速部署,中文语音识别准确率超预期
  • 力科LeCroy PCIe协议分析仪软件:从下载到实战抓包全指南
  • 阿里CosyVoice镜像部署教程:GPU加速,实时生成高清语音
  • 告别仿真器:手把手教你用树莓派4B+SOEM库驱动真实EtherCAT伺服电机
  • 基于springboot+vue大学生实习平台hx1235FLCE
  • PHP使用PHPExcel读取excel数据并批量上传到数据库
  • 突破JetBrains IDE限制:试用期重置工具完全指南
  • S2-Pro前端集成示例:在Vue项目中构建实时AI聊天界面
  • Qwen3-0.6B-FP8助力自动化软件测试:生成测试用例与执行报告分析
  • 用ESP32和SSD1680驱动墨水屏,手把手教你做个低功耗电子价签原型
  • C语言开发者入门AI:通过Qwen3-0.6B-FP8理解模型API调用原理
  • Mac用户福利:用Open-AutoGLM和MLX框架,免费运行手机AI助理
  • RWKV7-1.5B-G1A入门指南:10分钟完成Dify平台插件集成
  • Fish Speech 1.5语音克隆安全边界:防滥用机制与伦理使用建议
  • Node.js后端集成Phi-4-mini-reasoning:环境配置与高性能API服务搭建
  • 轻量模型Phi-4-mini-reasoning在嵌入式场景的应用:STM32开发日志分析与代码生成
  • Qwen2.5-VL模型服务API设计:REST与gRPC对比
  • 手把手教你用Qwen3.5-9B:从部署到对话,完整流程解析
  • 当CANopen遇上EtherCAT:用倍福EL6751网关连接伺服驱动器的实战心得
  • DeepSeek-OCR实战案例:教育行业讲义扫描件智能排版与导出
  • SecGPT-14B多场景落地:支撑等保2.0差距分析、整改建议生成
  • mPLUG工具优化技巧:如何提升图片问答响应速度
  • AutoGen Studio入门指南:小白也能轻松玩转AI多代理协作,从部署到实战