`
dcj3sjt126com
  • 浏览: 1855459 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

深入PHP使用技巧之变量

    博客分类:
  • PHP
阅读更多

众所周知,PHP与其他脚本语言一样,属于弱变量类型的语言。同时PHP本身也是通过C语言来实现。本文主要介绍PHP内部是如何实现弱变量类型的,并且据此分析在PHP开发中需要注意的一些使用技术。其中会重点分析PHP中的copy on write机制和引用相关方面的话题。 本章节属于《深入PHP使用技巧》的第一部分。

 

如何实现弱变量

在了解PHP实现弱变量类型之前,可以先思考下:如何通过C/C++来实现弱变量类型的效果呢?

这个问题我在BIT培训课上基本上有两种答案:

方法1:采用C++的继承机制。首先定义一个基础类型

1
2
3
Class Var
{
}

然后基于Var,派生出不同的子类型IntVar/FloatVar/StringVar等等。
方法2:基于C语言的 Struct。其中一个字段用于标识类型,另外一个字段用于存储数据,由于数据要是各种类型,所以通常需要采用指针

比如:

1
2
3
4
struct var {
    Int type;
    Void *data;
};

两种思路本身并没有太大区别,也都基本上能够满足需求。在PHP中采用了第二种思路,并且做了比较多的优化。在PHP中,所有的变量都会对应同一种类型zval,其中zval也就是struct _zval_struct,具体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
} zvalue_value;
struct _zval_struct {
    /* Variable information */
    zvalue_value value;     /* value */
    zend_uint refcount;
    zend_uchar type;    /* active type */
    zend_uchar is_ref;
};

从zval可以看出,PHP在细节方面的确做了不少优化的功夫。

  1. zend_uchar type。采用uchar节省内存。
  2. zvalue_value value;     采用union来替换void *,这样同样能节省空间,并且比void *更能表义清晰。
  3. 在字符串类型中,默认保留了字符串的长度。这样很容易做到字符串的二进制安全,并且在计算字符串长度的时候不需要进行扫描。

观察PHP弱变量的实现,也会有以下疑惑:

  1. 为什么会没有int类型呢?其实在PHP中是有的,只是说默认int数据就保存在long中。
  2. 资源类型咋表现的呢?资源在PHP内部其实就是一数字。详细后续会介绍。
  3. refcount和is_ref是干嘛的呢?呵呵,这就是第二部分要介绍的了。

Reference counting & Copy-on-Write

PHP和其他语言类似,在其语法中有两种赋值方式:引用赋值和非引用赋值(普通的==赋值)。

1
2
3
4
5
<?php
    $a = 1;
    $b = $a;//非引用赋值
    $c = &$a;//引用赋值
?>

引用赋值和非引用赋值在PHP内部是如何实现的呢?一种通常的认识是:“引用赋值就是两个变量对应同一个Zval,非引用赋值则是直接产生一个新的zval,同时把对应的值直接copy过来。”也就是该代码的内存结构如下:

(该图是大多数人认为的PHP内存结构,是错误的)

这样的确能够满足大部分情况下的需求,但显然不是最佳的解决方案,尤其是在内存管理上,比如说以下代码就会显得非常的低效。

1
2
3
4
5
<?php
$arr = array(...);//定义一个非常大的PHP数组
myfunc($arr);//每一个函数调用都是一次隐性的非引用赋值
myfunc($arr);
?>

因为每次函数调用会进行一次内存dump,而大内存的内存dump是非常耗CPU的。在C语言中,一种解决方案是采用指针,所有函数调用尽量传递指针。的确很灵活高效,但也很难维护~指针可以说是C语言程序员心头的痛(当然也是福~^_^)。还有一种更高级更有效的方法是采用引用计数(Reference counting)。

在PHP中,也可以采用引用来解决这样的问题,但你见过采用在PHP中大量使用引用的吗?显然很少。

在PHP内核中,Zval的实现正是采用了引用计数的概念,说起引用计数就不得不谈到copy-on-write 机制。这样前面谈到的refcount和is_ref就有作用了。

  • refcount:引用次数。在zval初始创建的时候就为1。每增加一个引用,则refcount ++。
  • is_ref:用于表示一个zval是否是引用状态。zval初始化的情况下会是0,表示不是引用。

在Zend/Zend.h内部有一些关于ZVAL的宏定义,里面比较清晰的解析了引用计数的一些规则,其中重点关注以下几个宏定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define INIT_PZVAL(z)       \
    (z)->refcount = 1;       \
    (z)->is_ref = 0;
#define SEPARATE_ZVAL_IF_NOT_REF(ppzv)      \//非引用下的变量分离
    if (!PZVAL_IS_REF(*ppzv)) {             \
        SEPARATE_ZVAL(ppzv);                \
    }
#define SEPARATE_ZVAL_TO_MAKE_IS_REF(ppzv)  \//非引用下的变量分离,并且设置引用
    if (!PZVAL_IS_REF(*ppzv)) {             \
        SEPARATE_ZVAL(ppzv);                \
        (*(ppzv))->is_ref = 1;               \
    }
#define SEPARATE_ARG_IF_REF(varptr) \     //引用下的变量分离
    if (PZVAL_IS_REF(varptr)) { \
        zval *original_var = varptr; \
        ALLOC_ZVAL(varptr); \
        varptr->value = original_var->value; \
        varptr->type = original_var->type; \
        varptr->is_ref = 0; \
        varptr->refcount = 1; \
        zval_copy_ctor(varptr); \
    } else { \
        varptr->refcount++; \
    }

这里面谈到两个重要的概念:
1、非引用下的变量分离。
非引用下的变量分离,是指在一堆非引用变量中插入引用的情况下,在PHP内部进行的一种内存操作。以下面的列子来看:

1
2
3
$a = 1;
$b = $a;
$c = &$b;

在前两句执行之后,内存结构如下图
在第三句 $c = &$b;语句中则会执行“非引用下的变量分离。”,具体步骤是:

  1. 将b分离出来,同时把a对应的zval的refcount-1。
  2. copy 出一个新的zval,并把zval的is_ref设置成1.
  3. 把C指向这个新的zval,同时refcount ++

最终效果如下图:

2、引用下的变量分离。

引用下的变量分离,是指在一堆引用变量中进行一个非引用赋值操作,这个时候会直接执行copy内存的操作。

以下面的例子来说

1
2
3
$a = 1;
$b = &$a;
$c = $b;

在执行完前两行后,PHP中内存结构如下:

在第三句,则会执行“引用下的变量分离”也就是真正的copy,最终内存结构如下图

据此,基本上对PHP变量内部的一些原理比较清楚了,但还有一些需要注意点的:
1、PHP变量的引用计数特性,对于数组同样也存在。但注意,对于key则不生效。(具体在后面章节会分析到。)
2、PHP变量中的对象比较特殊,在PHP5之后,默认都是采用引用赋值的方式。具体实现可以参考Zend_objects.*系列代码。
3、对于分析PHP内部变量,推荐采用xdebug_debug_zval,而不要采用内置的debug_zval_dump。因为PHP内置的debug_zval_dump函数一方面无法处理is_ref,而且采用了引用的方式来处理,从而导致看到结果会有误解。

使用技巧结论

据此可以得出分析出不少结论:

1、在PHP开发中不推荐采用引用。因为PHP内部对内存优化本身做了不少工作,引用不会带来太多优化。(但注意推荐非强制)

2、在PHP中strlen是o(1)的。

本文首发xlq的博客(转载请保留)http://blog.xiuwz.com/2011/11/09/php-using-internal-zval/
分享到:
评论

相关推荐

    去来php教程之变量也运算符

    本教程将深入讲解PHP中的变量和运算符,帮助初学者更好地理解和运用这两个关键概念。 ### PHP变量 1. **声明**:PHP是一种弱类型的语言,这意味着变量在使用之前无需预定义类型。你可以直接赋值来创建变量,例如`$...

    深入理解PHP 高级技巧、面向对象与核心技术(原书第3版)源码

    《深入理解PHP 高级技巧、面向对象与核心技术(原书第3版)》是一本专为PHP开发者设计的深入学习书籍,旨在帮助读者掌握PHP的高级特性、面向对象编程以及核心概念。源码文件“phpvqp3_scripts”包含了本书中所讲解的...

    技术 深入理解PHP:高级技巧、面向对象与核心技术_技术文档_

    《深入理解PHP:高级技巧、面向对象与核心技术》是一本专为PHP开发者设计的技术文档,旨在帮助读者深入掌握PHP编程的精髓。这本书涵盖了PHP的高级技巧、面向对象编程(OOP)以及核心技术等多个方面,旨在提升开发者...

    深入理解PHP内核-php宝典

    PHP使用了一系列技巧来优化这一过程。 - **继承与多态**:继承允许子类继承父类的属性和方法,而多态则允许不同类的对象对相同的方法做出不同的响应。这两者是PHP面向对象编程的核心。 ##### 4. 内存管理 - **内存...

    Laravel开发-utilities 将 PHP 变量转换为 JavaScript 变量

    然后,按照库的文档配置和使用,将PHP变量转换并嵌入到HTML中。这个工具可能会简化上述过程,避免手动处理JSON编码和安全问题。 5. **注意事项**:在处理敏感数据时,一定要确保正确地进行了JSON编码和转义,防止跨...

    深入浅出PHP

    ### 深入浅出PHP:基础语法与实践 #### PHP简介 PHP是一种广泛使用的开源服务器端脚本语言,特别适用于Web开发,并可嵌入HTML中。PHP支持多种数据库,如MySQL、Oracle等,能够高效地处理动态内容,是创建交互式...

    Html语句中加变量的方法

    在你给出的例子中,使用的是ASP(VBScript)的语法,这是微软在上世纪90年代推出的一种服务器端脚本技术。 在ASP中,`&lt;% %&gt;`符号用于包裹VBScript代码,而`&lt;%= %&gt;`则是用来输出VBScript表达式的值到HTML中。现在让...

    深入php与jquery开发源码

    《深入PHP与jQuery开发源码》是一本专为IT专业人士准备的高级教程,旨在帮助读者深入理解PHP和jQuery这两种在Web开发中广泛使用的语言和技术。本书不仅提供了丰富的理论知识,更通过实例源码分析,让读者能够实际...

    phpcms 自动添加变量

    本文将深入探讨如何在 phpcms 系统中自动添加变量,以及这一过程涉及到的相关技术与步骤。 1. 变量系统理解: 在 phpcms 中,变量被用来存储和传递特定的信息,如网站标题、版权信息、联系方式等。这些变量可以在...

    深入PHP与jQuery开发

    《深入PHP与jQuery开发》是一本专为PHP和jQuery开发者设计的专业教程,旨在帮助读者深入理解和熟练运用这两种技术。PHP是一种广泛使用的服务器端脚本语言,尤其在网页开发领域具有重要地位;而jQuery则是一个强大的...

    php使用技巧,php使用技巧,php使用技巧,php使用技巧,

    本文将深入探讨一些实用的PHP使用技巧,帮助开发者提升工作效率和代码质量。 1. **变量与类型转换**:PHP是弱类型语言,允许在不声明类型的情况下创建变量。但为了代码的可读性和稳定性,推荐使用`var`或`let`...

    深入理解PHP内核.mobi

    《深入理解PHP内核》是一本专为PHP开发者和爱好者量身打造的开源书籍,它深入探讨了PHP的内部机制,旨在帮助读者更好地理解和优化PHP应用程序。这本书涵盖了从基础到高级的各种主题,包括PHP的环境配置、源码解读、...

    PHP程序设计-3期(KC016) 经验技巧2-3 全局变量.doc

    本文将深入探讨关于全局变量的一些经验和技巧,以及如何正确地在函数内部引用它们。 首先,我们要明白一个重要的概念:全局变量的生命周期。全局变量在整个脚本的运行期间都存在,从脚本开始执行到脚本结束。它们在...

    深入PHP面向对象模式与实践第2版

    《深入PHP面向对象模式与实践第2版》是一本专注于PHP面向对象编程的书籍,它不仅涉及到面向对象编程的基础知识,还深入讲解了面向对象设计模式以及如何在实际项目中应用这些模式。该书是对PHP开发者掌握面向对象技术...

    博客系统(《深入体验php项目开发》的源码)

    《深入体验PHP项目开发》一书的源码提供了一个完整的博客系统实现,旨在帮助读者深入理解PHP编程在实际项目中的应用。这个博客系统涵盖了从基本的网页设计到复杂的数据库交互等多个方面,是学习PHP开发的理想实践...

    PHP-内核解析-深入PHP

    《PHP-内核解析-深入PHP》这本书是针对PHP开发者深入理解PHP内核的重要参考资料,旨在帮助读者从底层原理的角度掌握PHP的运行机制。通过学习本书,你可以了解PHP的架构设计、内存管理、变量处理、函数调用、执行流程...

    深入PHP:面向对象、模式与实践(第三版)高清PDF和完整源码下载.rar

    《深入PHP:面向对象、模式与实践》是PHP开发领域的一本经典著作,尤其是第三版,它涵盖了PHP编程中的核心概念、高级技巧以及最佳实践。这本书深入探讨了PHP的面向对象编程(OOP),设计模式,以及实际项目中的应用...

    PHP7内核剖析,包括php基本框架,变量,Zend虚拟机,php基本语法实现,内存管理,线程安全,扩展开发,命名空间等

    《PHP7内核剖析》是一本深入探讨PHP7核心机制的专著,涵盖了从基本框架到高级特性的广泛内容。本书旨在帮助读者理解PHP7的内部运作原理,从而提升编程能力,优化性能,并为扩展开发提供坚实的理论基础。 首先,PHP7...

    深入体验PHP项目开发pdf02

    【标题】"深入体验PHP项目开发pdf02"所涵盖的知识点主要集中在PHP语言在实际项目中的应用,这通常涉及到一系列高级PHP编程技巧、最佳实践以及项目管理策略。该资源可能是一个系列教程或书籍的第二部分,旨在帮助...

Global site tag (gtag.js) - Google Analytics