PHP的資源資料類型

來源:互聯網
上載者:User

資源資料類型

迄今為止, 你都是工作在非常基礎的使用者空間資料類型上, 字串, 數值, TRUE/FALSE等值. 即便上一章你已經開始接觸數組了, 但也只是收集這些基礎資料類型的數組.

複雜的結構體

現實世界中, 你通常需要在更加複雜的資料集合下工作, 通常涉及到晦澀的結構體指標. 一個常見的晦澀的結構體指標樣本就是stdio的檔案描述符, 即便是在C語言中也只是一個指標.

#include <stdio.h>  int main(void)  {      FILE *fd;      fd = fopen("/home/jdoe/.plan", "r");      fclose(fd);      return 0;  }

stdio的檔案描述符和其他多數檔案描述符一致, 都像是一個書籤. 你擴充的調用應用僅需要在feof(), fread(), fwrite(), fclose()這樣的實現函數調用時傳遞這個值. 有時, 這個書籤必須是使用者空間代碼可訪問的; 因此, 就需要在標準的php變數或者說zval *中有表示它的方法.

這裡就需要一種新的資料類型. RESOURCE資料類型在zval *中儲存一個簡單的整型值, 使用作為登入資源的索引用來尋找. 資源條目包含了資源索引所表示的內部資料類型, 以及儲存資源資料的指標等資訊.

定義資源類型

為了使註冊的資源條目所包含的資源資訊更加明確, 需要定義資源的類型. 首先在你的sample.c中已有的函數實現下增加下面的程式碼片段

static int le_sample_descriptor;  PHP_MINIT_FUNCTION(sample)  {      le_sample_descriptor = zend_register_list_destructors_ex(                  NULL, NULL, PHP_SAMPLE_DESCRIPTOR_RES_NAME,                  module_number);      return SUCCESS;  }

接下來, 滾動到你的代碼檔案末尾, 修改sample_module_entry結構體, 將NULL, /* MINIT */一行替換為下面的內容. 就像你給這個結構中增加函數列表結構時一樣, 你需要確認在這一行末尾保留一個逗號.

PHP_MINIT(sample), /* MINIT */  

最後, 你需要在php_sample.h中定義PHP_SAMPLE_DESCRIPTOR_RES_NAME, 將下面的代碼放到你的其他常量定義下面:

#define PHP_SAMPLE_DESCRIPTOR_RES_NAME "File Descriptor"  

PHP_MINIT_FUNCTION()代表第1章"PHP生命週期"中介紹的4個特殊的啟動和終止操作中的第一個, 關於生命週期, 在第12章"啟動, 終止以及之間的幾個關鍵點"和第13章"INI設定"中還將深入討論.

這裡需要知道的非常重要的一點是MINIT函數在你的擴充第一次載入時執行一次, 它會在所有請求到達之前被執行. 這裡我們利用這個機會註冊了解構函式, 不過它們是NULL值, 不過在通過一個唯一整型ID足以知道一個資源類型時, 你很快就會修改它.

註冊資源

現在引擎已經知道了你要儲存一些資源資料, 是時候給使用者空間的代碼一種方式去產生實際的資源了. 要做到這一點, 需要如下重新實現fopen()命令:

PHP_FUNCTION(sample_fopen)  {      FILE *fp;      char *filename, *mode;      int filename_len, mode_len;      if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",                          &filename, &filename_len,                          &mode, &mode_len) == FAILURE) {          RETURN_NULL();      }      if (!filename_len || !mode_len) {          php_error_docref(NULL TSRMLS_CC, E_WARNING,                  "Invalid filename or mode length");          RETURN_FALSE;      }      fp = fopen(filename, mode);      if (!fp) {          php_error_docref(NULL TSRMLS_CC, E_WARNING,                  "Unable to open %s using mode %s",                  filename, mode);          RETURN_FALSE;      }      ZEND_REGISTER_RESOURCE(return_value, fp,                                  le_sample_descriptor);  }

為了讓編譯器知道什麼是FILE *, 你需要包含stdio.h. 這可以放在sample.c中, 但是為了本章後面部分做準備, 我還是要求你放到php_sample.h中.

如果你對前面的章節付出了努力, 最後一行前面的所有內容都應該可以讀懂. 這一行代碼執行的任務是將fp指標儲存到資源的索引中, 將它和MINIT中定義的類型關聯起來, 並儲存一個可用於尋找的key到return_value中.

如果需要儲存多於一個指標的值, 或者儲存一個直接量, 則必須新分配一段記憶體用來儲存資料, 接著將指向這段記憶體的指標註冊為資源.

譯註:

1. 資源資料類型的註冊實際上是在list_destructors(Zend/zend_list.c中定義的靜態全域變數HashTable)中插入一個新構建的zend_rsrc_list_dtors_entry結構體, 這個結構體描述了這個資源類型的資訊.

2. 資源資料的註冊(ZEND_REGISTER_RESOURCE)實際上是在EG(regular_list)中使用zend_hash_next_free_element()得到下一個數值下標, 作為資源的ID, 並將傳入的資源指標(封裝為zend_rsrc_list_entry結構體)儲存到EG(regular_list)中這個下標對應的元素中.

3. EG(regular_list)的初始化是在請求初始化階段完成的, 通過跟蹤代碼, 可以看到其函數調用流程如下: php_request_startup(main/main.c) --> zend_active(Zend/zend.c) --> init_compiler(Zend/zend_compile.c) --> zend_init_rsrc_list(Zend/zend_list.c). 通過觀察zend_init_rsrc_list()函數可以看出EG(regular_list)的解構函式是list_entry_destructor(Zend/zend_list.c). 而list_entry_destructor()的邏輯是從list_destructors(上面第一步所述的靜態全域變數)中尋找要釋放的資來源物件類型的資訊, 接著按照註冊資源類型時所指定的析構器進行析構.

4. 按照上面幾點, 可以很容易理清本章前面所述內容. 首先註冊一個資源類型, 這個資源類型中包含了諸如所屬模組編號, 析構器控制代碼這樣的資訊. 接著, 在建立具體的資來源物件時, 將資來源物件和資源類型做了一個關聯.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.