亚洲精品中文字幕无乱码_久久亚洲精品无码AV大片_最新国产免费Av网址_国产精品3级片

php語言

PHP內(nèi)部函數(shù)的定義

時間:2024-07-04 23:45:42 php語言 我要投稿
  • 相關(guān)推薦

PHP內(nèi)部函數(shù)的定義

  PHP(外文名:PHP: Hypertext Preprocessor,中文名:“超文本預(yù)處理器”)是一種通用開源腳本語言。語法吸收了C語言、Java和Perl的特點,利于學(xué)習(xí),使用廣泛,主要適用于Web開發(fā)領(lǐng)域。那么PHP內(nèi)部函數(shù)的定義是怎樣的呢?以下僅供參考!

  如何找到函數(shù)的定義

  作為開始,讓我們嘗試找出strpos函數(shù)的定義。

  嘗試的第一步,就是去PHP 5.4根目錄然后在頁面頂部的搜索框輸入strpos。搜索的結(jié)果是一個很大的列表,展示了strpos在PHP源碼中出現(xiàn)的位置。

  因為這個結(jié)果對我們并沒有太大的幫助,我們使用一個小技巧:我們搜索”PHP_FUNCTION strpos”(不要漏了雙引號,它們很重要),而不是strpos.

  現(xiàn)在我們得到兩個入口鏈接:

  /PHP_5_4/ext/standard/

  php_string.h 48 PHP_FUNCTION(strpos);

  string.c 1789 PHP_FUNCTION(strpos)

  第一個要注意的事情是,兩個位置都是在ext/standard文件夾。這就是我們希望找到的,因為strpos函數(shù)(跟大部分string,array和文件函數(shù)一樣)是standard擴展的一部分。

  現(xiàn)在,在新標(biāo)簽頁打開兩個鏈接,然后看看它們背后藏了什么代碼。

  你會看到第一個鏈接帶你到了php_string.h文件,它包含了下面的代碼:

  // ...

  PHP_FUNCTION(strpos);

  PHP_FUNCTION(stripos);

  PHP_FUNCTION(strrpos);

  PHP_FUNCTION(strripos);

  PHP_FUNCTION(strrchr);

  PHP_FUNCTION(substr);

  // ...

  這就是一個典型的頭文件(以.h后綴結(jié)尾的文件)的樣子:單純的函數(shù)列表,函數(shù)在其他地方定義。事實上,我們對這些并不感興趣,因為我們已經(jīng)知道我們要找的是什么。

  第二個鏈接更有趣:它帶我們到string.c文件,這個文件包含了函數(shù)真正的源代碼。

  在我?guī)阋徊揭徊降夭殚嗊@個函數(shù)之前,我推薦你自己嘗試?yán)斫膺@個函數(shù)。這是一個很簡單的函數(shù),盡管你不知道真正的細節(jié),但大多數(shù)代碼看起來都很清晰。

  PHP函數(shù)的骨架

  所有的PHP函數(shù)都使用同一個基本結(jié)構(gòu)。在函數(shù)頂部定義了各個變量,然后調(diào)用zend_parse_parameters函數(shù),然后到了主要的邏輯,當(dāng)中有RETURN_***和php_error_docref的調(diào)用。

  那么,讓我們以函數(shù)的定義來開始:

  zval *needle;

  char *haystack;

  char *found = NULL;

  char needle_char[2];

  long offset = 0;

  int haystack_len;

  第一行定義了一個指向zval的指針needle。zval是在PHP內(nèi)部代表任意一個PHP變量的定義。它真正是怎么樣的會在下一篇文章重點談?wù)摗?/p>

  第二行定義了指向單個字符的指針haystack。這時候,你需要記住,在C語言里面,數(shù)組代表指向它們第一個元素的指針。比如說,haystack變量會指向你所傳遞的$haystack字符串變量的第一個字符。haystack + 1會指向第二個字符,haystack + 2指向第三個,以此類推。因此,通過逐個遞增指針,可以讀取整個字符串。

  那么問題來了,PHP需要知道字符串在哪里結(jié)束。不然的話,它會一直遞增指針而不會停止。為了解決這個問題,PHP也保存了明確的長度,這就是haystack_len變量。

  現(xiàn)在,在上面的定義中,我們感興趣的是offset變量,這個變量用來保存函數(shù)的第三個參數(shù):開始搜索的偏移量。它使用long來定義,跟int一樣,也是整型數(shù)據(jù)類型,F(xiàn)在這兩者的差異并不重要,但你需要知道的是在PHP中,整型值使用long來存儲,字符串的長度使用int來存儲。

  現(xiàn)在來看看下面的三行:

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

  return;

  }

  這三行代碼做的事情就是,獲取傳遞到函數(shù)的參數(shù),然后把它們存儲到上面聲明的變量中。

  傳遞給函數(shù)的第一個參數(shù)是傳遞參數(shù)的數(shù)量。這個數(shù)字通過ZEND_NUM_ARGS()宏提供。

  下一個函數(shù)是TSRMLS_CC宏,這是PHP的一種特性。你會發(fā)現(xiàn)這個奇怪的宏分散在PHP代碼庫的很多地方。是線程安全資源管理器(TSRM)的一部分,它保證PHP不會在多線程之間混亂變量。這對我們來說不是很重要,當(dāng)你在代碼中看到TSRMLS_CC(或者TSRMLS_DC)的時候,忽略它就行。(有一個奇怪的地方你需要注意的是,在”argument”之前沒有逗號。這是因為不管你是否使用線程安全創(chuàng)建函數(shù),該宏會被解釋為空或者, trsm_ls。因此,逗號是宏的一部分。)

  現(xiàn)在,我們來到重要的東西:”sz|l”字符串標(biāo)記了函數(shù)接收的參數(shù)。:

  s // 第一個參數(shù)是字符串

  z // 第二個參數(shù)是一個zval結(jié)構(gòu)體,任意的變量

  | // 標(biāo)識接下來的參數(shù)是可選的

  l // 第三個參數(shù)是long類型(整型)

  除了s,z,l之外,還有更多的標(biāo)識類型,但是大部分都能從字符中清楚其意思。例如b是boolean,d是double(浮點型數(shù)字),a是array,f是回調(diào)(function),o是object。

  接下來的參數(shù)&haystack,&haystack_len,&needle,&offset指定了需要賦值的參數(shù)的變量。你可以看到,它們都是使用引用(&)傳遞的,意味著它們傳遞的不是變量本身,而是指向它們的指針。

  這個函數(shù)調(diào)用之后,haystack會包含haystack字符串,haystack_len是字符串的長度,needle是needle的值,offset是開始的偏移量。

  而且,這個函數(shù)使用FAILURE(當(dāng)你嘗試傳遞無效參數(shù)到函數(shù)時會發(fā)生,比如傳遞一個數(shù)組賦值到字符串)來檢查。這種情況下zend_parse_parameters函數(shù)會拋出警告,而此函數(shù)馬上返回(會返回null給PHP的用戶層代碼)。

  在參數(shù)解析完畢以后,主函數(shù)體開始:

  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函數(shù)拋出,然后函數(shù)使用RETURN_FALSE宏返回false。

  php_error_docref是一個錯誤函數(shù),你可以在擴展目錄找到它(比如,ext文件夾)。它的名字根據(jù)它在錯誤頁面中返回文檔參考(就是那些不會正常工作的函數(shù))定義。還有一個zend_error函數(shù),它主要被Zend Engine使用,但也經(jīng)常出現(xiàn)在擴展代碼中。

  兩個函數(shù)都使用sprintf函數(shù),比如格式化信息,因此錯誤信息可以包含占位符,那些占位符會被后面的參數(shù)填充。下面有一個例子:

  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

  讓我們繼續(xù)解析代碼:

  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為字符串的情況下執(zhí)行,而且如果它是空的話會拋出錯誤。然后到了比較有趣的一部分:php_memnstr被調(diào)用了,這個函數(shù)做了主要的工作。跟往常一樣,你可以點擊該函數(shù)名然后查看它的源碼。

  php_memnstr返回指向needle在haystack第一次出現(xiàn)的位置的指針(這就是為什么found變量要定義為char *,例如,指向字符的指針)。從這里可以知道,偏移量(offset)可以通過減法被簡單地計算,可以在函數(shù)的最后看到:

  RETURN_LONG(found - haystack);

  最后,讓我們來看看當(dāng)needle作為非字符串的時候的分支:

  else {

  if (php_needle_needle, needle_char TSRMLS_CC) != SUCCESS) {

  RETURN_FALSE;

  }

  needle_char[1] = 0;

  found = php_memnstr(haystack + offset,

  needle_char,

  1,

  haystack + haystack_len);

  }

  我只引用在手冊上寫的”如果 needle 不是一個字符串,那么它將被轉(zhuǎn)換為整型并被視為字符順序值。”這基本上說明,除了寫strpos($str, 'A'),你還可以寫strpos($str, 65),因為A字符的編碼是65。

  如果你再查看變量定義,你可以看到needle_char被定義為char needle_char[2],即有兩個字符的字符串,php_needle_char會將真正的字符(在這里是’A’)到needle_char[0]。然后strpos函數(shù)會設(shè)置needle_char[1]為0。這背后的原因是因為,在C里面,字符串是使用’’結(jié)尾,就是說,最后一個字符被設(shè)置為NUL(編碼為0的字符)。在PHP的語法環(huán)境里,這樣的情況不存在,因為PHP存儲了所有字符串的長度(因此它不需要0來幫助找到字符串的結(jié)尾),但是為了保證與C函數(shù)的兼容性,還是在PHP的內(nèi)部實現(xiàn)了。

  Zend functions

  我對strpos這個函數(shù)感覺好累,讓我們找另一個函數(shù)吧:strlen。我們使用之前的方法:

  從PHP5.4源碼根目錄開始搜索strlen。

  你會看到一堆無關(guān)的函數(shù)的使用,因此,搜索“PHP_FUNCTION strlen”。當(dāng)你這么搜索的時候,你會發(fā)現(xiàn)一些奇怪的事情發(fā)生了:沒有任何的結(jié)果。

  原因是,strlen是少數(shù)通過Zend Engine而不是PHP擴展定義的函數(shù)。這種情況下,函數(shù)不是使用PHP_FUNCTION(strlen)定義,而是ZEND_FUNCTION(strlen)。因此,我們也要搜索“ZEND_FUNCTION strlen”。

  我們都知道,我們需要點擊沒有分號結(jié)尾的鏈接跳到源碼的定義。這個鏈接帶我們到下面的函數(shù)定義:

  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);

  }

  這個函數(shù)實現(xiàn)太簡單了,我不覺得我還需要進一步的解釋。

  方法

  我們會談?wù)擃惡蛯ο笕绾喂ぷ鞯母嗉毠?jié)在其他文章里,但作為一個小小的劇透:你可以通過在搜索框搜索ClassName::methodName來搜索對象方法。例如,嘗試搜索SplFixedArray::getSize。

  下一部分

  下一部分會再次發(fā)表在。會談?wù)摰絲val是什么,它們是怎么工作的,以及它們是怎么在源碼中被使用的(所有的Z_***宏)。

【PHP內(nèi)部函數(shù)的定義】相關(guān)文章:

PHP分頁自定義函數(shù)09-08

php自定義函數(shù)實現(xiàn)漢字分割替換06-01

PHP如何自定義一個函數(shù)09-19

php自定義擴展名獲取函數(shù)示例06-10

PHP項目開發(fā)中最常用的自定義函數(shù)整理08-12

如何使用php自定義函數(shù)實現(xiàn)漢字分割替換08-18

用php自定義函數(shù)之遞歸刪除文件及目錄09-13

C語言函數(shù)的定義07-13

php常用的系統(tǒng)函數(shù)05-05

PHP類與構(gòu)造函數(shù)07-01