Comment trouver la définition d'une fonction
Pour commencer, essayons de trouver la définition de la fonction strpos.
La première étape à essayer est d'aller dans le répertoire racine de PHP 5.4 et de saisir strpos dans le champ de recherche en haut de la page. Le résultat de la recherche est une grande liste indiquant où strpos apparaît dans le code source PHP.
Comme ce résultat ne nous est pas très utile, nous utilisons une petite astuce : nous recherchons "PHP_FUNCTION strpos" (ne manquez pas les guillemets doubles, ils sont importants), au lieu de strpos.
Nous obtenons maintenant deux liens d'entrée :
/PHP_5_4/ext/standard/
php_string.h 48 PHP_FUNCTION(strpos);
string.c 1789 PHP_FUNCTION( strpos)
La première chose à noter est que les deux emplacements se trouvent dans le dossier ext/standard. C'est ce que nous nous attendons à trouver, car la fonction strpos (comme la plupart des fonctions de chaîne, de tableau et de fichier) fait partie de l'extension standard.
Maintenant, ouvrez les deux liens dans un nouvel onglet et voyez quel code est caché derrière eux.
Vous verrez que le premier lien vous amène au fichier php_string.h, qui contient le code suivant :
// ... PHP_FUNCTION(strpos); PHP_FUNCTION(stripos); PHP_FUNCTION(strrpos); PHP_FUNCTION(strripos); PHP_FUNCTION(strrchr); PHP_FUNCTION(substr); // ...
Il s'agit d'un fichier d'en-tête typique (avec un suffixe .h) fichier à la fin) ressemble à : une simple liste de fonctions, les fonctions sont définies ailleurs. En fait, tout cela ne nous intéresse pas car nous savons déjà ce que nous recherchons.
Le deuxième lien est plus intéressant : il nous amène au fichier string.c, qui contient le code source réel de la fonction.
Avant de vous présenter cette fonction étape par étape, je vous recommande d'essayer de comprendre cette fonction vous-même. C'est une fonction très simple, et même si vous ne connaissez pas les vrais détails, la plupart du code semble clair.
Le squelette de la fonction PHP
Toutes les fonctions PHP utilisent la même structure de base. Chaque variable est définie en haut de la fonction, puis la fonction zend_parse_parameters est appelée, puis la logique principale arrive, y compris les appels à RETURN_*** et php_error_docref.
Alors, commençons par la définition de la fonction :
zval *needle;
char *haystack;
char *found = NULL;
char Needle_char[2];
long offset = 0;
int haystack_len;
La première ligne définit une aiguille de pointeur pointant vers zval. zval est la définition qui représente n'importe quelle variable PHP dans PHP. Ce à quoi cela ressemble réellement sera discuté dans le prochain article.
La deuxième ligne définit la botte de foin, un pointeur vers un seul caractère. À ce stade, vous devez vous rappeler qu’en langage C, les tableaux représentent des pointeurs vers leur premier élément. Par exemple, la variable haystack pointera vers le premier caractère de la variable $haystackstringque vous avez transmise. haystack + 1 pointera vers le deuxième caractère, haystack + 2 pointera vers le troisième caractère, et ainsi de suite. Ainsi, en incrémentant le pointeur un par un, la chaîne entière peut être lue.
Ensuite, le problème survient, PHP a besoin de savoir où se termine la chaîne. Sinon, il continuera à incrémenter le pointeur sans s'arrêter. Pour résoudre ce problème, PHP enregistre également une longueur explicite, qui est la variable haystack_len.
Maintenant, dans la définition ci-dessus, nous nous intéressons à la variable offset, qui sert à sauvegarder le troisième paramètre de la fonction : l'offset pour lancer la recherche. Il est défini en utilisant long, qui, comme int, est également un typeinteger data. Maintenant, la différence entre les deux n'est pas importante, mais ce que vous devez savoir, c'est qu'en PHP, les valeurs entières sont stockées aussi longtemps et la longueur des chaînes est stockée sous la forme int.
Jetez maintenant un œil aux trois lignes suivantes :
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &haystack, &haystack_len, &needle, &offset) == FAILURE) { return; }
Ce que font ces trois lignes de code, c'est de transmettre les paramètres à lafonction, puis de les stocker les dans la variable déclarée ci-dessus.
Le premier paramètre passé à la fonction est le nombre de paramètres passés. Ce numéro est fourni via la macro ZEND_NUM_ARGS().
La fonction suivante est la macro TSRMLS_CC, qui est une fonctionnalité de PHP. Vous trouverez cette étrange macro dispersée à de nombreux endroits dans la base de code PHP. Fait partie du Thread-Safe Resource Manager (TSRM), qui garantit que PHP ne mélange pas les variables sur plusieurs threads. Ce n'est pas très important pour nous, ignorez-le simplement lorsque vous voyez TSRMLS_CC (ou TSRMLS_DC) dans votre code. (Une chose étrange que vous devez noter est qu'il n'y a pas de virgule avant "argument". En effet, que vous créiez ou non la fonction en utilisant la sécurité des threads, la macro sera interprétée comme vide ou, trsm_ls. Par conséquent, la virgule fait partie de la macro . )
Maintenant, arrivons aux choses importantes : la chaîne "sz|l" marque les paramètres reçus par la fonction. :
s // 第一个参数是字符串 z // 第二个参数是一个zval结构体,任意的变量 | // 标识接下来的参数是可选的 l // 第三个参数是long类型(整型)
En plus des s, z, l, il existe d'autres types de logos, mais la plupart d'entre eux peuvent clairement comprendre leur signification à partir des caractères. Par exemple, b est un booléen, d est un double (nombre à virgule flottante), a est un tableau, f est un rappel (fonction) et o est un objet.
接下来的参数&haystack;,&haystack;_len,&needle;,&offset;指定了需要赋值的参数的变量。你可以看到,它们都是使用引用(&)传递的,意味着它们传递的不是变量本身,而是指向它们的指针。
这个函数调用之后,haystack会包含haystack字符串,haystack_len是字符串的长度,needle是needle的值,offset是开始的偏移量。
而且,这个函数使用FAILURE(当你尝试传递无效参数到函数时会发生,比如传递一个数组赋值到字符串)来检查。这种情况下zend_parse_parameters函数会抛出警告,而此函数马上返回(会返回null给PHP的用户层代码)。
在参数解析完毕以后,主函数体开始:
if (offset < 0 || offset > haystack_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string"); RETURN_FALSE; }
这段代码做的事情很明显,如果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, "Failed to write %d bytes to %s", Z_STRLEN_PP(tmp), filename); // %d is filled with Z_STRLEN_PP(tmp) // %s is filled with filename
让我们继续解析代码:
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); }
前面的5行非常清晰:这个分支只会在needle为字符串的情况下执行,而且如果它是空的话会抛出错误。然后到了比较有趣的一部分:php_memnstr被调用了,这个函数做了主要的工作。跟往常一样,你可以点击该函数名然后查看它的源码。
php_memnstr返回指向needle在haystack第一次出现的位置的指针(这就是为什么found变量要定义为char *,例如,指向字符的指针)。从这里可以知道,偏移量(offset)可以通过减法被简单地计算,可以在函数的最后看到:
RETURN_LONG(found - haystack);
最后,让我们来看看当needle作为非字符串的时候的分支:
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 不是一个字符串,那么它将被转换为整型并被视为字符顺序值。”这基本上说明,除了写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里面,字符串是使用’’结尾,就是说,最后一个字符被设置为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”。
我们都知道,我们需要点击没有分号结尾的链接跳到源码的定义。这个链接带我们到下面的函数定义:
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); }
这个函数实现太简单了,我不觉得我还需要进一步的解释。
方法
我们会谈论类和对象如何工作的更多细节在其他文章里,但作为一个小小的剧透:你可以通过在搜索框搜索ClassName::methodName来搜索对象方法。例如,尝试搜索SplFixedArray::getSize。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!