所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

PHP Self|Static|parent区别详解 别再混淆三个核心关键字用法

PHP Self|Static|parent区别详解 别再混淆三个核心关键字用法 一

文章目录CloseOpen

先搞懂三个关键字的基础定义:别再“张冠李戴”

要分清它们,得先把“绑定时机”“作用对象”这两个词吃透——简单说,“绑定时机”就是PHP什么时候确定要调用哪个类的成员,“作用对象”就是这个关键字针对的是哪个类。

Self:编译时就“锁死”的“当前类”

Self“当前类”的别名,注意“当前类”指的是Self的那个类,不管有没有继承,它都会在PHP编译代码的时候(也就是解析代码、还没执行的时候)就“锁死”——举个我朋友的例子:

class User {

public static $role = '普通用户';

public static function getRole() {

return Self::$role; // 这里的Self,永远指向User

}

}

class Admin extends User {

public static $role = '管理员'; // 子类覆盖了静态属性

}

echo Admin::getRole(); // 输出:普通用户(是不是和你想的不一样?)

为什么?因为getRole()User类里写的,Self在编译时就绑定了User,所以不管子类Admin怎么覆盖$role,用Self拿到的还是User$role。我后来帮他改成Static才解决问题——这个后面说。

再举个更日常的例子:你写了个BaseModel类,里面有个静态方法getTableName()返回'base_table',子类UserModel继承后想返回'user_table',如果用Self::getTableName(),结果还是'base_table'——这就是Self的“死性”,它只认“自己所在的类”。

parent:专门“找爸爸”的继承工具

parent就简单多了,它是“父类”的别名,专门用来在子类中调用父类被覆盖的成员(比如方法、属性),而且是在运行时确定父类——比如我之前写电商系统的“订单类”时,父类Order有个calculatePrice()方法计算基础价格,子类VIPOrder要加个折扣:

class Order {

protected function calculatePrice($amount) {

return $amount 10; // 基础价格:10元/件

}

}

class VIPOrder extends Order {

protected function calculatePrice($amount) {

$basePrice = parent::calculatePrice($amount); // 先调用父类的基础计算

return $basePrice 0.8; // VIP打8折

}

}

$vipOrder = new VIPOrder();

echo $vipOrder->calculatePrice(5); // 输出:40(5100.8)

这里parent::calculatePrice($amount)就是明确调用父类Order的方法——注意,parent必须用在有继承关系的子类里,而且父类的成员得是protectedpublic(如果是private,子类根本访问不到)。我之前踩过一个坑:父类的方法是private,结果用parent调用时报错“无法访问私有方法”,后来改成protected才解决——这个细节你得记牢。

Static:运行时才“选对象”的“动态选手”

Static是PHP5.3之后加的“延迟静态绑定”(Late Static Binding)特性,它的核心是“在运行时确定调用的类”——也就是根据“实际调用的那个子类”来决定要访问的成员,而不是写Static的类。再看我朋友的例子,把Self改成Static

class User {

public static $role = '普通用户';

public static function getRole() {

return Static::$role; // 这里的Static,看“谁调用它”

}

}

class Admin extends User {

public static $role = '管理员';

}

echo Admin::getRole(); // 输出:管理员(对了!)

为什么?因为Admin::getRole()Admin类在调用,Static会在运行时(代码执行的时候)“看”到实际调用的是Admin,所以指向Admin$role。这就是Static的“活”——它能处理继承中的“动态调用”。

我之前做过一个“支付方式”的需求:父类Payment有个getPaymentType()方法返回'默认支付',子类WechatPay返回'微信支付'Alipay返回'支付宝支付',用Static就能完美解决:

class Payment {

public static function getPaymentType() {

return Static::$type;

}

}

class WechatPay extends Payment {

public static $type = '微信支付';

}

class Alipay extends Payment {

public static $type = '支付宝支付';

}

echo WechatPay::getPaymentType(); // 微信支付

echo Alipay::getPaymentType(); // 支付宝支付

是不是比Self灵活多了?PHP官方文档里对Static的说明很准确:“用于在继承范围内引用静态调用的类”(参考链接:PHP手册-延迟静态绑定)——简单说,就是“继承里的动态静态调用,找Static准没错”。

一张表分清三者区别:再也不混淆

光讲定义可能还是有点懵?我把三者的核心区别做成了表格,你保存下来,写代码时对着看就行:

关键字 绑定时机 作用对象 典型场景
Self 编译时 写Self的当前类 调用当前类的静态成员/常量(不涉及继承动态调用)
parent 运行时 子类的父类 子类中调用父类被覆盖的方法/属性
Static 运行时 实际调用的类(子类) 继承中的动态静态调用(需要根据子类调整)

比如你要写一个“日志类”,父类BaseLog有个writeLog()方法写文件,子类DbLog要同时写数据库——用parent::writeLog()调用父类方法;如果父类有个静态属性$logPath,子类要覆盖成自己的路径——用Static::$logPath;如果只是当前类自己用的静态常量(比如const VERSION = '1.0')——用Self::VERSION就够了。

实操中最容易踩的3个坑,我替你避了

最后再讲几个我亲测的“避坑技巧”,都是踩过才记住的:

  • Self别用来做“继承动态调用”:如果你写的类会被继承,而且需要子类覆盖静态成员,绝对别用Self——比如前面的UserAdmin例子,用Self只会拿到父类的值,改成Static才对。
  • parent要确认父类有对应成员:我之前写Cache类时,子类RedisCache想调用父类的getCache(),结果父类的方法是private,直接报错——父类的成员得是protectedpublicparent才能访问。
  • Static只用于“静态成员”:别用Static调用非静态方法,虽然PHP不会报错,但逻辑上不对——非静态方法用$this更合适,Static是给静态成员准备的。
  • 比如我最近写的一个“权限管理系统”,角色类Role有个静态方法getPermissions(),子类AdminRole覆盖后返回更多权限,用Static::getPermissions()就能正确拿到子类的权限列表,上线后没出过错——这就是“分清关键字”的好处。

    如果你之前踩过这三个关键字的坑,或者按我讲的例子试了,欢迎在评论区告诉我结果——比如“用Static解决了子类静态属性的问题”,或者“之前parent调用错了父类方法”,咱们一起讨论。其实PHP的面向对象不难,难的是把这些“小关键字”的细节摸透——毕竟写对一行代码,比改半小时bug舒服多了~


    很多人以为parent只能调用父类的方法,其实不是——它连父类的属性也能调,只不过得满足个小条件:父类的那个成员得是protected或者public。要是父类把属性或方法设成private,子类用parent调用直接就会报错,我之前就踩过这坑。比如我写过一个商品分类的父类Goods,里面有个protected的静态属性$category = ‘日用品’,子类Electronics想在自己的getFullCategory方法里先拿到父类的分类,再加上子类的细分(比如“电子产品”),就用了parent::$category,顺利拿到“日用品”后,再拼接成“日用品-电子产品”,结果完全符合预期。但后来我误把父类的$category改成private,子类再调用直接报“无法访问私有属性”,改回protected才解决问题。

    再来说方法,我做电商订单系统时更常用parent调方法。比如父类Order有个public的calculateBasePrice方法,用来计算商品的基础总价(比如“商品数量×单价”),子类VIPOrder需要计算折扣后的价格,这时候就先用parent::calculateBasePrice拿到基础价,再乘以VIP专属的0.8折扣率。这里parent的作用很明确:子类要覆盖父类的方法,但还想复用父类的基础逻辑,不用重新写一遍计算基础价的代码。其实不管是属性还是方法,parent的核心就是“连接子类和父类的继承关系”——让子类能拿到父类里“允许被继承的东西”。但一定要记住,父类的成员不能是private,就像你想拿家里的东西,得是爸爸允许你碰的,要是他把东西锁在只有自己能开的柜子里,你肯定拿不到对吧?

    还有个小细节要注意,parent不仅能调用“被子类覆盖的成员”,就算子类没覆盖父类的成员,也能用parent调用。比如父类有个public的getOrderId方法,子类没覆盖它,那子类里用parent::getOrderId和直接用self::getOrderId结果一样,但要是子类覆盖了,parent就会明确指向父类的版本。简单说,parent就是“不管子类有没有改,我就要父类的那个版本”——比如你想喝爸爸泡的茶,不管你自己会不会泡茶,直接找爸爸要他泡的,就是这个意思。


    Self和Static的本质区别是什么?

    Self是编译时绑定,指向“写Self的当前类”(比如父类里的Self永远指向父类);Static是运行时绑定,指向“实际调用的类”(比如子类调用时指向子类)。简单说,Self“锁死”在代码所在的类,Static“跟随”实际使用的类。

    parent只能调用父类的方法吗?

    不是,parent可以调用父类的方法和属性,但要满足一个前提:父类的成员必须是protectedpublicprivate成员子类无法访问)。比如子类可以用parent::$role调用父类的protected静态属性,也能用parent::getName()调用父类的public方法。

    什么时候应该优先用Static而不是Self?

    当你的类会被继承,且需要子类覆盖静态成员(比如静态属性、静态方法)时,一定要用Static。比如父类有个静态属性$logPath,子类要改成自己的日志路径,用Static才能拿到子类的路径;用Self会永远拿到父类的路径,导致逻辑错误。

    用parent调用父类成员时,最容易踩什么坑?

    最常见的坑是父类成员权限错误——如果父类的方法/属性是private,子类用parent调用会直接报错。解决办法:把父类成员改成protected(允许子类访问)或public(公开访问)。

    Static可以用来调用非静态成员吗?

    不 Static是为静态成员设计的(静态方法、静态属性属于“类”),非静态成员属于“对象”(需要用$this调用)。虽然PHP不会强制报错,但逻辑上不符合面向对象的封装性,容易导致代码混乱。

    原文链接:https://www.mayiym.com/47490.html,转载请注明出处。
    0
    显示验证码
    没有账号?注册  忘记密码?

    社交账号快速登录

    微信扫一扫关注
    如已关注,请回复“登录”二字获取验证码