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中所有数据类型。





      
  • 相关阅读:
    Maven关于web.xml中Servlet和Servlet映射的问题
    intellij idea的Maven项目运行报程序包找不到的错误
    修改Maven项目默认JDK版本
    刷题15. 3Sum
    刷题11. Container With Most Water
    刷题10. Regular Expression Matching
    刷题5. Longest Palindromic Substring
    刷题4. Median of Two Sorted Arrays
    刷题3. Longest Substring Without Repeating Characters
    刷题2. Add Two Numbers
  • 原文地址:https://www.cnblogs.com/smilealgernon/p/3052676.html
Copyright © 2011-2022 走看看