0

0

PHP保留类及特殊类

藏色散人

藏色散人

发布时间:2019-06-21 15:53:15

|

3685人浏览过

|

来源于kancloud

转载

PHP保留类及特殊类

在面向对象语言中,都会内置一些语言内置提供的基本功能类,比如javascript中的array,number等类,php中也有很多这种类,比如directory,stdclass,exception等类,同时一些标准扩展比如pdo等扩展中也会定义一些类,php中类是不允许重复定义的,所以在编写代码时不允许定义已经存在的类。

同时PHP中有一些特殊的类:selfstaticparent,相信读者对这self和parent都比较熟悉了,而static特殊类是PHP5.3才引入的。

PHP中的static关键字非常多义:

● 在函数体内的修饰变量的static关键字用于定义静态局部变量。

● 用于修饰类成员函数和成员变量时用于声明静态成员。

立即学习PHP免费学习笔记(深入)”;

● (PHP5.3)在作用域解析符(::)前又表示静态延迟绑定的特殊类。

这个关键字修饰的意义都表示"静态",在PHP手册中提到self,parent和static这几个关键字,但实际上除了static是关键字以外,其他两个均不是关键字,在手册的关键字列表中也没有这两个关键字,要验证这一点很简单:

 string(4) "self"

上面的代码并没有报错,如果你把error_reporting(E_ALL)打开,就能看到实际是什么情况了:运行这段代码会出现“ Notice: Use of undefined constant self - assumed 'self'“,也就是说PHP把self当成一个普通常量了,尝试未定义的常量会把产量本身当成一个字符串,例如上例的”self",不过同时会出一个NOTICE,这就是说self这个标示符并没有什么特殊的。

不同语言中的关键字的意义会有些区别,Wikipedia上的解释是: 具有特殊含义的标示符或者单词,从这个意义上说$this也算是一个关键字,但在PHP的关键字列表中并没有。 PHP的关键字和C/C++一样属于保留字(关键字),关键字用于表示特定的语法形式,例如函数定义,流程控制等结构。 这些关键字有他们的特定的使用场景,而上面提到的self和parent并没有这样的限制。

self,parent,static类

点创DOidea网上书店
点创DOidea网上书店

一套专业的网上书店程序,可以作为新华书店及大中型书店网上销售的首选,满足在线支付及汇款确认机制。功能简介:图书分类、查询、排行、最新、特价、关注排行、销售排行,新闻系统、汇款确认机制、求购书籍、在线咨询、热门图书定义、全站广告后台管理、后台采用WEBEDIT编辑器、集成"支付宝"在线支付等...v3.5版特殊功能说明(前台):1.自带5种风格主题。2.友好的页面提示(对网站全部

下载

前面已经说过self的特殊性。self是一个特殊类,它指向当前类,但只有在类定义内部才有效,但也并不一定指向类本身这个特殊类,比如前面的代码,如果放在类方法体内运行,echo self; 还是会输出常量self的值,而不是当前类,它不止要求在类的定义内部,还要求在类的上下文环境,比如 new self()的时候,这时self就指向当前类,或者self::$static_varible,self::CONSTANT类似的作用域解析符号(::),这时的self才会作为指向本身的类而存在。

同理parent也和self类似。下面先看看在在类的环境下的编译吧$PHP_SRC/Zend/zend_language_parser.y:

class_name_reference:
        class_name                      { zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
    |   dynamic_class_name_reference    { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
;

在需要获取类名时会执行zend_do_fetch_class()函数:

void zend_do_fetch_class(znode *result, znode *class_name TSRMLS_DC)
{
    // ...
    opline->opcode = ZEND_FETCH_CLASS;
    if (class_name->op_type == IS_CONST) {
        int fetch_type;
 
        fetch_type = zend_get_class_fetch_type(class_name->u.constant.value.str.val, class_name->u.constant.value.str.len);
        switch (fetch_type) {
            case ZEND_FETCH_CLASS_SELF:
            case ZEND_FETCH_CLASS_PARENT:
            case ZEND_FETCH_CLASS_STATIC:
                SET_UNUSED(opline->op2);
                opline->extended_value = fetch_type;
                zval_dtor(&class_name->u.constant);
                break;
            default:
                zend_resolve_class_name(class_name, &opline->extended_value, 0 TSRMLS_CC);
                opline->op2 = *class_name;
                break;
        }
    } else {
        opline->op2 = *class_name;
    }
    // ...
}

上面省略了一些无关的代码,重点关注fetch_type变量。这是通过zend_get_class_fetch_type()函数获取到的。

int zend_get_class_fetch_type(const char *class_name, uint class_name_len)
{
    if ((class_name_len == sizeof("self")-1) &&
        !memcmp(class_name, "self", sizeof("self")-1)) {
        return ZEND_FETCH_CLASS_SELF;
    } else if ((class_name_len == sizeof("parent")-1) &&
        !memcmp(class_name, "parent", sizeof("parent")-1)) {
        return ZEND_FETCH_CLASS_PARENT;
    } else if ((class_name_len == sizeof("static")-1) &&
        !memcmp(class_name, "static", sizeof("static")-1)) {
        return ZEND_FETCH_CLASS_STATIC;
    } else {
        return ZEND_FETCH_CLASS_DEFAULT;
    }
}

前面的代码是Zend引擎编译类相关操作的代码,下面就到执行阶段了,self,parent等类的指向会在执行时进行获取,找到执行opcode为ZEND_FETCH_CLASS的执行函数:

zend_class_entry *zend_fetch_class(const char *class_name, uint class_name_len, int fetch_type TSRMLS_DC)
{
    zend_class_entry **pce;
    int use_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) == 0;
    int silent       = (fetch_type & ZEND_FETCH_CLASS_SILENT) != 0;
 
    fetch_type &= ZEND_FETCH_CLASS_MASK;
 
check_fetch_type:
    switch (fetch_type) {
        case ZEND_FETCH_CLASS_SELF:
            if (!EG(scope)) {
                zend_error(E_ERROR, "Cannot access self:: when no class scope is active");
            }
            return EG(scope);
        case ZEND_FETCH_CLASS_PARENT:
            if (!EG(scope)) {
                zend_error(E_ERROR, "Cannot access parent:: when no class scope is active");
            }
            if (!EG(scope)->parent) {
                zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent");
            }
            return EG(scope)->parent;
        case ZEND_FETCH_CLASS_STATIC:
            if (!EG(called_scope)) {
                zend_error(E_ERROR, "Cannot access static:: when no class scope is active");
            }
            return EG(called_scope);
        case ZEND_FETCH_CLASS_AUTO: {
                fetch_type = zend_get_class_fetch_type(class_name, class_name_len);
                if (fetch_type!=ZEND_FETCH_CLASS_DEFAULT) {
                    goto check_fetch_type;
                }
            }
            break;
    }
 
    if (zend_lookup_class_ex(class_name, class_name_len, use_autoload, &pce TSRMLS_CC) == FAILURE) {
        if (use_autoload) {
            if (!silent && !EG(exception)) {
                if (fetch_type == ZEND_FETCH_CLASS_INTERFACE) {
                    zend_error(E_ERROR, "Interface '%s' not found", class_name);
                } else {
                    zend_error(E_ERROR, "Class '%s' not found", class_name);
                }
                }
            }
        }
        return NULL;
    }
    return *pce;
}

从这个函数就能看出端倪了,当需要获取self类的时候,则将EG(scope)类返回,而EG(scope)指向的正是当前类。如果时parent类的话则从去EG(scope)->parent也就是当前类的父类,而static获取的时EG(called_scope),分别说说EG宏的这几个字段,前面已经介绍过EG宏,它可以展开为如下这个结构体:

struct _zend_executor_globals {
    // ...
    zend_class_entry *scope;
    zend_class_entry *called_scope; /* Scope of the calling class */
    // ...
}
 
struct _zend_class_entry {
    char type;
    char *name;
    zend_uint name_length;
    struct _zend_class_entry *parent;
}
#define struct _zend_class_entry zend_class_entry

其中的zend_class_entry就是PHP中类的内部结构表示,zend_class_entry有一个parent字段,也就是该类的父类。在EG结构体中的中called_scope会在执行过程中将当前执行的类赋值给called_scope,例如如下代码:

代码B::funcA()执行的时候,实际执行的是B的父类A中定义的funcA函数,A::funcA()执行时当前的类(scope)指向的是类A,而这个方法是从B类开始调用的,called_scope指向的是类B,static特殊类指向的正是called_scope,也就是当前类(触发方法调用的类),这也是延迟绑定的原理。

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
云朵浏览器入口合集
云朵浏览器入口合集

本专题整合了云朵浏览器入口合集,阅读专题下面的文章了解更多详细地址。

0

2026.01.20

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

20

2026.01.20

PS使用蒙版相关教程
PS使用蒙版相关教程

本专题整合了ps使用蒙版相关教程,阅读专题下面的文章了解更多详细内容。

62

2026.01.19

java用途介绍
java用途介绍

本专题整合了java用途功能相关介绍,阅读专题下面的文章了解更多详细内容。

87

2026.01.19

java输出数组相关教程
java输出数组相关教程

本专题整合了java输出数组相关教程,阅读专题下面的文章了解更多详细内容。

39

2026.01.19

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

10

2026.01.19

xml格式相关教程
xml格式相关教程

本专题整合了xml格式相关教程汇总,阅读专题下面的文章了解更多详细内容。

13

2026.01.19

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

19

2026.01.19

微信聊天记录删除恢复导出教程汇总
微信聊天记录删除恢复导出教程汇总

本专题整合了微信聊天记录相关教程大全,阅读专题下面的文章了解更多详细内容。

160

2026.01.18

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号