ホームページ  >  記事  >  バックエンド開発  >  [翻訳] PHP 内部関数の定義を理解する (PHP 開発者のための PHP ソースコード - パート 2)、開発者ソースコード_PHP チュートリアル

[翻訳] PHP 内部関数の定義を理解する (PHP 開発者のための PHP ソースコード - パート 2)、開発者ソースコード_PHP チュートリアル

WBOY
WBOYオリジナル
2016-07-12 08:59:04791ブラウズ

[翻訳] PHP 内部関数の定義を理解する (PHP 開発者のための PHP ソース コード - パート 2)、開発者ソース コード

<p>文章来自:http://www.aintnot.com/2016/02/10/understanding-phps-internal-function-definitions-ch</p>
<p>原文:https://nikic.github.io/2012/03/16/Understanding-PHPs-internal-function-definitions.html</p>

「PHP 開発者のための PHP ソースコード」シリーズの第 2 部へようこそ。

前回の記事では、ircmaxell が PHP のソース コードの場所とその基本的なディレクトリ構造を説明し、C 言語を簡単に紹介しました (PHP は C 言語で書かれているため)。その記事を見逃した場合は、この記事を読み始める前に読んでおくとよいでしょう。

この記事では、PHP 内部関数の定義を見つけてその原則を理解することについて話します。

関数の定義を見つける方法

まず、strpos 関数の定義を調べてみましょう。

最初のステップは、PHP 5.4 ルート ディレクトリに移動し、ページ上部の検索ボックスに「strpos」と入力することです。検索結果は、PHP ソース コード内で strpos が出現する場所を示す大きなリストです。

この結果はあまり役に立たないので、ちょっとしたトリックを使用します。strpos の代わりに、「PHP_FUNCTION strpos」 (二重引用符を見逃さないでください。重要です) を検索します。

これで 2 つのエントリ リンクが得られます:

<span class="hljs-regexp">/PHP_5_4/ext/standard/</span> <span class="hljs-regexp">  php_string.h <span class="hljs-number">48 PHP_FUNCTION(strpos);</span></span> <span class="hljs-regexp"><span class="hljs-number">  string.c <span class="hljs-number">1789 PHP_FUNCTION(strpos) </span></span></span>最初に注意すべきことは、両方の場所が

フォルダー内にあるということです。 strpos 関数 (ほとんどの文字列、配列、ファイル関数と同様) は標準拡張機能の一部であるため、これが期待されるものです。 ext/standard

次に、新しいタブで両方のリンクを開いて、その背後にどのようなコードが隠されているかを確認してください。

最初のリンクから php_string.h ファイルに移動し、次のコードが含まれていることがわかります:

リーリー <code>   

これは、典型的なヘッダー ファイル (拡張子 .h で終わるファイル) は次のようになります: 関数の単純なリスト、他の場所で定義された関数。実際、私たちは何を探しているのかすでにわかっているので、これらには興味がありません。

2 番目のリンクはさらに興味深いもので、関数の実際のソース コードを含む

ファイルに移動します。 string.c

この機能を段階的に説明する前に、この機能を自分で理解してみることをお勧めします。これは非常に単純な関数であり、実際の詳細は分からなくても、コードの大部分は明確に見えます。

PHP関数のスケルトン

すべての PHP 関数は同じ基本構造を使用します。各変数は関数の先頭で定義され、その後

の呼び出しが呼び出されます。 zend_parse_parameters函数,然后到了主要的逻辑,当中有RETURN_***php_error_docref

それでは、関数の定義から始めましょう:

リーリー

<span class="hljs-title"><br></span>最初の行はポインタ

を定義します。 zval は、PHP 内の任意の PHP 変数を表す定義です。実際にどのようなものになるかについては、次の記事で説明します。 zval的指针needle

2 行目は 1 つの文字へのポインタを定義し、

3 行目へのポインタを定義します。したがって、ポインタを 1 つずつインクリメントすることで、文字列全体を読み取ることができます。 haystack。这时候,你需要记住,在C语言里面,数组代表指向它们第一个元素的指针。比如说,haystack变量会指向你所传递的$haystack字符串变量的第一个字符。haystack + 1会指向第二个字符,haystack + 2

次に問題が発生します。PHP は文字列がどこで終わるかを知る必要があります。それ以外の場合は、停止せずにポインタをインクリメントし続けます。この問題を解決するために、PHP は明示的な長さ (

変数) も保存します。 haystack_len

上記の定義では、関数の 3 番目のパラメーター、つまり検索を開始するオフセットを保存するために使用されるオフセット変数に興味があります。これは、int と同様に整数データ型である long を使用して定義されます。この 2 つの違いは重要ではありませんが、知っておく必要があるのは、PHP では整数値は長さとして保存され、文字列の長さは int として保存されるということです。

次の 3 行を見てください:

リーリー

<span class="hljs-keyword"><br></span>これらの 3 行のコードは、関数に渡されるパラメーターを取得し、上で宣言した変数にそれらを格納します。

関数に渡される最初のパラメータは、渡されるパラメータの数です。この番号は

マクロを介して提供されます。 ZEND_NUM_ARGS()

下一个函数是TSRMLS_CC宏,这是PHP的一种特性。你会发现这个奇怪的宏分散在PHP代码库的很多地方。是线程安全资源管理器(TSRM)的一部分,它保证PHP不会在多线程之间混乱变量。这对我们来说不是很重要,当你在代码中看到TSRMLS_CC(或者TSRMLS_DC)的时候,忽略它就行。(有一个奇怪的地方你需要注意的是,在"argument"之前没有逗号。这是因为不管你是否使用线程安全创建函数,该宏会被解释为空或者, trsm_ls。因此,逗号是宏的一部分。)

现在,我们来到重要的东西:"sz\|l"字符串标记了函数接收的参数。:

s  <span>//</span><span> 第一个参数是字符串</span>
z  <span>//</span><span> 第二个参数是一个zval结构体,任意的变量</span>
|  <span>//</span><span> 标识接下来的参数是可选的</span>
l  <span>//</span><span> 第三个参数是long类型(整型)</span>

除了s,z,l之外,还有更多的标识类型,但是大部分都能从字符中清楚其意思。例如b是boolean,d是double(浮点型数字),a是array,f是回调(function),o是object。

接下来的参数&haystack&haystack_len&needle&offset指定了需要赋值的参数的变量。你可以看到,它们都是使用引用(&)传递的,意味着它们传递的不是变量本身,而是指向它们的指针。

这个函数调用之后,haystack会包含haystack字符串,haystack_len是字符串的长度,needle是needle的值,offset是开始的偏移量。

而且,这个函数使用FAILURE(当你尝试传递无效参数到函数时会发生,比如传递一个数组赋值到字符串)来检查。这种情况下zend_parse_parameters函数会抛出警告,而此函数马上返回(会返回null给PHP的用户层代码)。

在参数解析完毕以后,主函数体开始:

<span>if</span> (offset < <span>0</span> || offset ><span> haystack_len) {
    php_error_docref(NULL TSRMLS_CC, E_WARNING, </span><span>"</span><span>Offset not contained in string</span><span>"</span><span>);
    RETURN_FALSE;
}</span>
<span class="hljs-keyword"><br></span>

这段代码做的事情很明显,如果offset超出了边界,一个E_WARNING级别的错误会通过php_error_docref函数抛出,然后函数使用RETURN_FALSE宏返回false。

php_error_docref是一个错误函数,你可以在扩展目录找到它(比如,ext文件夹)。它的名字根据它在错误页面中返回文档参考(就是那些不会正常工作的函数)定义。还有一个zend_error函数,它主要被Zend Engine使用,但也经常出现在扩展代码中。

两个函数都使用sprintf函数,比如格式化信息,因此错误信息可以包含占位符,那些占位符会被后面的参数填充。下面有一个例子:

php_error_docref(NULL TSRMLS_CC, E_WARNING, <span>"</span><span>Failed to write %d bytes to %s</span><span>"</span><span>, Z_STRLEN_PP(tmp), filename);
</span><span>//</span><span> %d is filled with Z_STRLEN_PP(tmp)
</span><span>//</span><span> %s is filled with filename</span>

让我们继续解析代码:

<span>if</span> (Z_TYPE_P(needle) ==<span> IS_STRING) {
    </span><span>if</span> (!<span>Z_STRLEN_P(needle)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, </span><span>"</span><span>Empty delimiter</span><span>"</span><span>);
        RETURN_FALSE;
    }

    found </span>= php_memnstr(haystack +<span> offset,
                        Z_STRVAL_P(needle),
                        Z_STRLEN_P(needle),
                        haystack </span>+<span> haystack_len);
}</span>
<span class="hljs-keyword"><br></span>

前面的5行非常清晰:这个分支只会在needle为字符串的情况下执行,而且如果它是空的话会抛出错误。然后到了比较有趣的一部分:php_memnstr被调用了,这个函数做了主要的工作。跟往常一样,你可以点击该函数名然后查看它的源码。

php_memnstr返回指向needle在haystack第一次出现的位置的指针(这就是为什么found变量要定义为char *,例如,指向字符的指针)。从这里可以知道,偏移量(offset)可以通过减法被简单地计算,可以在函数的最后看到:

RETURN_LONG(found - haystack);

最后,让我们来看看当needle作为非字符串的时候的分支:

<span>else</span><span> {
    </span><span>if</span> (php_needle_char(needle, needle_char TSRMLS_CC) !=<span> SUCCESS) {
        RETURN_FALSE;
    }
    needle_char[</span><span>1</span>] = <span>0</span><span>;

    found </span>= php_memnstr(haystack +<span> offset,
                        needle_char,
                        </span><span>1</span><span>,
                        haystack </span>+<span> haystack_len);
}</span>
<span class="hljs-keyword"><br></span>

我只引用在手册上写的"如果 needle 不是一个字符串,那么它将被转换为整型并被视为字符顺序值。"这基本上说明,除了写strpos($str, 'A'),你还可以写strpos($str, 65),因为A字符的编码是65。

如果你再查看变量定义,你可以看到needle_char被定义为char needle_char[2],即有两个字符的字符串,php_needle_char会将真正的字符(在这里是'A')到needle_char[0]。然后strpos函数会设置needle_char[1]为0。这背后的原因是因为,在C里面,字符串是使用'\0'结尾,就是说,最后一个字符被设置为NUL(编码为0的字符)。在PHP的语法环境里,这样的情况不存在,因为PHP存储了所有字符串的长度(因此它不需要0来帮助找到字符串的结尾),但是为了保证与C函数的兼容性,还是在PHP的内部实现了。

Zend functions

我对strpos这个函数感觉好累,让我们找另一个函数吧:strlen。我们使用之前的方法:

从PHP5.4源码根目录开始搜索strlen。

你会看到一堆无关的函数的使用,因此,搜索“PHP_FUNCTION strlen”。当你这么搜索的时候,你会发现一些奇怪的事情发生了:没有任何的结果。

原因是,strlen是少数通过Zend Engine而不是PHP扩展定义的函数。这种情况下,函数不是使用PHP_FUNCTION(strlen)定义,而是ZEND_FUNCTION(strlen)。因此,我们也要搜索“ZEND_FUNCTION strlen”。

我们都知道,我们需要点击没有分号结尾的链接跳到源码的定义。这个链接带我们到下面的函数定义:

<span>ZEND_FUNCTION(strlen)
{
    </span><span>char</span> *<span>s1;
    </span><span>int</span><span> s1_len;

    </span><span>if</span> (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, <span>"</span><span>s</span><span>"</span>, &s1, &s1_len) ==<span> FAILURE) {
        </span><span>return</span><span>;
    }

    RETVAL_LONG(s1_len);
}</span>

这个函数实现太简单了,我不觉得我还需要进一步的解释。

方法

我们会谈论类和对象如何工作的更多细节在其他文章里,但作为一个小小的剧透:你可以通过在搜索框搜索ClassName::methodName来搜索对象方法。例如,尝试搜索SplFixedArray::getSize

下一部分

下一部分会再次发表在。会谈论到zval是什么,它们是怎么工作的,以及它们是怎么在源码中被使用的(所有的Z_*宏)。

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/1099830.htmlTechArticle[译] 理解PHP内部函数的定义(给PHP开发者的PHP源码-第二部分),开发者源码 文章来自:http://www.aintnot.com/2016/02/10/understanding-phps-internal-fu...
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。