zoukankan      html  css  js  c++  java
  • PHP源码阅读Part2函数

      注:这篇文章的内容出自nikic的博客,这里只是翻译整理一下!

      上一部分我们讨论了从哪里获取源码,以及源码的主要结构。这部分我们主要讨论,如何找到一个PHP核心函数的C源码,以及它的实现。

      如何获得PHP核心函数的C源码

      这里我们以strpos函数为例,首先进入PHP5.4源码的根目录,在搜索框中输入strpos,你会发现搜索出了很多,找起来很不方便,我们使用一点小小的技巧,在搜索在搜索框中输入"PHP_FUNCTION strpos"(注意:要加引号),现在,得到了两条搜索结果: 

    /PHP_5_4/ext/standard/
        php_string.h 48   PHP_FUNCTION(strpos);
        string.c     1789 PHP_FUNCTION(strpos);
    

      显然,php_string.h文件中是函数的声明。string.c文件中是函数的实现。数组、字符串等核心函数都是在ext目录下。

      PHP函数

      打开.c文件,看一下strpos函数的实现代码,源码里使用了很多C语言的宏,看起来可能有点犯怵。对比一下其他函数的实现方式,你会发现每个函数的实现基本上都有一些相同的结构。首先是声明各种变量,接着调用zend_parse_parameters,接下来是主体实现部分,过程中如果出现问题就RETURN_**,然后调用php_error_docref。

      OK,首先我们看一下变量声明部分:

    zval *needle;
    char *haystack;
    char *found = NULL;
    char  needle_char[2];
    long  offset = 0;
    int   haystack_len;

       needle是指向zval结构体的一个指针,这是strpos函数的第二个参数,可以是任何类型,PHP的所有数据类型,映射到C语言中,都是用zval结构体实现的,这个我们下节中会讲到。

      haystack是一个字符指针,它将指向传进来的字符串的第一个字符,haystack+1将指向第二个,haystack+2指向第三个,以此类推。所以,通过指针的递增,可以读到整个字符串。

      这里有个问题,就是,我们必须要知道字符串的长度,否则指针一直会递增下去。为了解决这个问题,PHP定义了一个变量haystack_len,来存储字符串的长度。

      我们可能感兴趣的最后一个变量是offset,这是PHP strpos函数的第三个参数,用来记录搜索的起至位置。在C源码中它被声明为long类型。而在PHP strpos中它是整型。这里只要记住PHP的整型其实对应的是C语言的long类型就行了,详细的内容下节会介绍到。接下来:

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &haystack, &haystack_len, &needle, &offset) == FAILURE) {
        return;
    }

        zend_parse_parameters实现的功能主要是把上面声明的变量,经过处理,然后返回(变量的引用)。第一个参数是参数的个数,通过宏 ZEND_NUM_ARGS()得到,下一个参数是TSRMLS_CC宏,你会发现这个奇怪的宏遍布PHP源码,它主要涉及到TSRM(Thread Safe Resource Mananger),这里我们不做深入的研究,以上两个参数没有用逗号分隔,是因为逗号已经包含在宏里。接下来的参数是"sz|l",这串字符表示:

    •   s // 第一个参数是字符串(string)类型
    •   z // 第二个参数是zval类型(混合)
    •   | // 表示接下来的参数是可选的(这里只有一个)
    •   l // 第三个参数是long类型

      以上所描述的三个参数对应PHP核心函数strpos的三个参数,分别被定义为s、z、l类型,对应&haystack、&needle、&offset。另外b表示boolean类型,d表示浮点型(double),a表示数组(array)型,o表示对象(object),f表示回调函数(function)。

      经过zend_parse_parameters的处理,haystack变量将存着被查找的字符串,needle存着要查找的字符串,offset存着偏移值,haystak_len存着haystack字符串的长度。

      如果返回的是FAILURE(比如:调用strpos传入不合法的参数),将会直接return;对应到PHP中,将会返回一个NULL值。如果参数通过了验证,接下来将是函数功能实现的主要部分:

    if (offset < 0 || offset > haystack_len) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string");
        RETURN_FALSE;
    }

        判断offset是否超出了haystack的范围,如果超出了,就使用php_error_docref函数,返回false。这里的php_error_docref只是产生一个PHP手册的链接,对应着相应的错误,除非你在PHP配置文件中打开了这项功能,否则不会有什么效果。真正发出警告的是zend_error函数,这个函数在PHP引擎里很常见,当然扩展里也会用到。

    if (Z_TYPE_P(needle) == IS_STRING) {
        if (!Z_STRLEN_P(needle)) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");
            RETURN_FALSE;
        }
    
        found = php_memnstr(haystack + offset,
                            Z_STRVAL_P(needle),
                            Z_STRLEN_P(needle),
                            haystack + haystack_len);
    }

        这里判断needle是否为字符串,如果needle是字符串,但是为空,将会抛出错误,返回false。否则调用php_memnstr函数,你可以点击这个函数,查看它的具体实现。php_menstr返回了needle在haystack第一次出现的位置,是一个字符指针,赋给found。

      我们接着再看if的另一个分支:

    else {
        if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
            RETURN_FALSE;
        }
        needle_char[1] = 0;
    
        found = php_memnstr(haystack + offset,
                            needle_char,
                            1,
                            haystack + haystack_len);
    }

      如果needle不为字符串类型时,根据说明手册里,如果needle不为字符串,将会被转为整型,然后把这个整型对应为字符串来查找,也就是说strpos($str,'A')和strpos($str,65)是同样的效果。php_needle_char()就是用来实现这个功能,把转好的字符存放在needle_char[0]中,然后将needle_char[1]置0,这是c语言表示字符串的一种方法。接下来,和上面的一样。

      ZEND函数

      并不是所有的函数都定义在ext目录下,作为扩展,有很少一部分函数是由zend引擎实现的,比如strlen函数。你搜"PHP_FUNCTION strlen"是不会有结果的,这个函数的实现在Zend的目录下,所以你得搜"ZEND_FUNCTION strlen"。

    ZEND_FUNCTION(strlen)
    {
        char *s1;
        int s1_len;
    
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s1, &s1_len) == FAILURE) {
            return;
        }
    
        RETVAL_LONG(s1_len);
    }

       这个函数的实现很简单,这里就不再赘述了。

      关于下一节

      下一节我们将会讨论源码中zval数据结构,这个结构体对应这PHP中所有数据类型。





      
  • 相关阅读:
    原码、补码、反码
    处理器体系结构
    CSAPP学习笔记—虚拟内存
    Sequence Models
    Neural Networks and Deep Learning
    windows7_下Eclipse中部署tomcat7.0进行JSP+servlet开发
    used in key specification without a key length
    在jsp页面下, 让eclipse完全支持HTML/JS/CSS智能提示
    求知若饥,虚心若愚
    C指针右左法则
  • 原文地址:https://www.cnblogs.com/smilealgernon/p/3052676.html
Copyright © 2011-2022 走看看