[译] 理解PHP内部函数的定义(给PHP开发者的PHP源码-第二部分),开发者源码
<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源码"系列的第二部分。
在上一篇中,ircmaxell说明了你可以在哪里找到PHP的源码,它的基本目录结构以及简单地介绍了一些C语言(因为PHP是用C语言来写的)。如果你错过了那篇文章,在你开始读这篇文章之前也许你应该读一下它。
在这篇文章中,我们谈论的是定位PHP内部函数的定义,以及理解它们的原理。
如何找到函数的定义
作为开始,让我们尝试找出strpos函数的定义。
尝试的第一步,就是去PHP 5.4根目录然后在页面顶部的搜索框输入strpos。搜索的结果是一个很大的列表,展示了strpos
在PHP源码中出现的位置。
因为这个结果对我们并没有太大的帮助,我们使用一个小技巧:我们搜索"PHP_FUNCTION strpos"(不要漏了双引号,它们很重要),而不是strpos
.
现在我们得到两个入口链接:
<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>
第一个要注意的事情是,两个位置都是在ext/standard
文件夹。这就是我们希望找到的,因为strpos函数(跟大部分string,array和文件函数一样)是standard扩展的一部分。
现在,在新标签页打开两个链接,然后看看它们背后藏了什么代码。
你会看到第一个链接带你到了php_string.h文件,它包含了下面的代码:
<code>
<span>//</span><span> ...</span> <span> PHP_FUNCTION(strpos); PHP_FUNCTION(stripos); PHP_FUNCTION(strrpos); PHP_FUNCTION(strripos); PHP_FUNCTION(strrchr); PHP_FUNCTION(substr); </span><span>//</span><span> ...</span>
这就是一个典型的头文件(以.h后缀结尾的文件)的样子:单纯的函数列表,函数在其他地方定义。事实上,我们对这些并不感兴趣,因为我们已经知道我们要找的是什么。
第二个链接更有趣:它带我们到string.c
文件,这个文件包含了函数真正的源代码。
在我带你一步一步地查阅这个函数之前,我推荐你自己尝试理解这个函数。这是一个很简单的函数,尽管你不知道真正的细节,但大多数代码看起来都很清晰。
PHP函数的骨架
所有的PHP函数都使用同一个基本结构。在函数顶部定义了各个变量,然后调用zend_parse_parameters
函数,然后到了主要的逻辑,当中有RETURN_***
和php_error_docref
的调用。
那么,让我们以函数的定义来开始:
zval *<span>needle; char </span>*<span>haystack; char </span>*found = <span>NULL</span><span>; char needle_char[</span>2<span>]; long offset </span>= 0<span>; int haystack_len;</span>
<span class="hljs-title"><br></span>
第一行定义了一个指向zval
的指针needle
。zval是在PHP内部代表任意一个PHP变量的定义。它真正是怎么样的会在下一篇文章重点谈论。
第二行定义了指向单个字符的指针haystack
。这时候,你需要记住,在C语言里面,数组代表指向它们第一个元素的指针。比如说,haystack
变量会指向你所传递的$haystack
字符串变量的第一个字符。haystack + 1
会指向第二个字符,haystack + 2
指向第三个,以此类推。因此,通过逐个递增指针,可以读取整个字符串。
那么问题来了,PHP需要知道字符串在哪里结束。不然的话,它会一直递增指针而不会停止。为了解决这个问题,PHP也保存了明确的长度,这就是haystack_len
变量。
现在,在上面的定义中,我们感兴趣的是offset变量,这个变量用来保存函数的第三个参数:开始搜索的偏移量。它使用long来定义,跟int一样,也是整型数据类型。现在这两者的差异并不重要,但你需要知道的是在PHP中,整型值使用long来存储,字符串的长度使用int来存储。
现在来看看下面的三行:
<span>if</span> (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, <span>"</span><span>sz|l</span><span>"</span>, &haystack, &haystack_len, &needle, &offset) ==<span> FAILURE) { </span><span>return</span><span>; }</span>
<span class="hljs-keyword"><br></span>
这三行代码做的事情就是,获取传递到函数的参数,然后把它们存储到上面声明的变量中。
传递给函数的第一个参数是传递参数的数量。这个数字通过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_*宏)。

PHP는 주로 절차 적 프로그래밍이지만 객체 지향 프로그래밍 (OOP)도 지원합니다. Python은 OOP, 기능 및 절차 프로그래밍을 포함한 다양한 패러다임을 지원합니다. PHP는 웹 개발에 적합하며 Python은 데이터 분석 및 기계 학습과 같은 다양한 응용 프로그램에 적합합니다.

PHP는 1994 년에 시작되었으며 Rasmuslerdorf에 의해 개발되었습니다. 원래 웹 사이트 방문자를 추적하는 데 사용되었으며 점차 서버 측 스크립팅 언어로 진화했으며 웹 개발에 널리 사용되었습니다. Python은 1980 년대 후반 Guidovan Rossum에 의해 개발되었으며 1991 년에 처음 출시되었습니다. 코드 가독성과 단순성을 강조하며 과학 컴퓨팅, 데이터 분석 및 기타 분야에 적합합니다.

PHP는 웹 개발 및 빠른 프로토 타이핑에 적합하며 Python은 데이터 과학 및 기계 학습에 적합합니다. 1.PHP는 간단한 구문과 함께 동적 웹 개발에 사용되며 빠른 개발에 적합합니다. 2. Python은 간결한 구문을 가지고 있으며 여러 분야에 적합하며 강력한 라이브러리 생태계가 있습니다.

PHP는 현대화 프로세스에서 많은 웹 사이트 및 응용 프로그램을 지원하고 프레임 워크를 통해 개발 요구에 적응하기 때문에 여전히 중요합니다. 1.PHP7은 성능을 향상시키고 새로운 기능을 소개합니다. 2. Laravel, Symfony 및 Codeigniter와 같은 현대 프레임 워크는 개발을 단순화하고 코드 품질을 향상시킵니다. 3. 성능 최적화 및 모범 사례는 응용 프로그램 효율성을 더욱 향상시킵니다.

phphassignificallyimpactedwebdevelopmentandextendsbeyondit

PHP 유형은 코드 품질과 가독성을 향상시키기위한 프롬프트입니다. 1) 스칼라 유형 팁 : PHP7.0이므로 int, float 등과 같은 기능 매개 변수에 기본 데이터 유형을 지정할 수 있습니다. 2) 반환 유형 프롬프트 : 기능 반환 값 유형의 일관성을 확인하십시오. 3) Union 유형 프롬프트 : PHP8.0이므로 기능 매개 변수 또는 반환 값에 여러 유형을 지정할 수 있습니다. 4) Nullable 유형 프롬프트 : NULL 값을 포함하고 널 값을 반환 할 수있는 기능을 포함 할 수 있습니다.

PHP에서는 클론 키워드를 사용하여 객체 사본을 만들고 \ _ \ _ Clone Magic 메소드를 통해 클로닝 동작을 사용자 정의하십시오. 1. 복제 키워드를 사용하여 얕은 사본을 만들어 객체의 속성을 복제하지만 객체의 속성은 아닙니다. 2. \ _ \ _ 클론 방법은 얕은 복사 문제를 피하기 위해 중첩 된 물체를 깊이 복사 할 수 있습니다. 3. 복제의 순환 참조 및 성능 문제를 피하고 클로닝 작업을 최적화하여 효율성을 향상시키기 위해주의를 기울이십시오.

PHP는 웹 개발 및 컨텐츠 관리 시스템에 적합하며 Python은 데이터 과학, 기계 학습 및 자동화 스크립트에 적합합니다. 1.PHP는 빠르고 확장 가능한 웹 사이트 및 응용 프로그램을 구축하는 데 잘 작동하며 WordPress와 같은 CMS에서 일반적으로 사용됩니다. 2. Python은 Numpy 및 Tensorflow와 같은 풍부한 라이브러리를 통해 데이터 과학 및 기계 학습 분야에서 뛰어난 공연을했습니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

MinGW - Windows용 미니멀리스트 GNU
이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

DVWA
DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

SecList
SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기
