1. 程式人生 > >菜鳥學php擴充套件 之 詳解擴充套件函式的傳參(如何獲取引數)(三)

菜鳥學php擴充套件 之 詳解擴充套件函式的傳參(如何獲取引數)(三)

前言

經過上一文 菜鳥學php擴充套件 之 自動生成的擴充套件框架詳解(二) ,對php擴充套件框架的整體瞭解,基本上可以說,對於扯淡如何寫php擴充套件和關鍵點有了一定的把握,但關鍵的還是在於如何寫PHP_FUNCTION的函式。
本文主要記錄一下,php在呼叫擴充套件的時候進行傳參,那麼擴充套件函式是怎麼接招的。當作自己的備忘錄

正文

1.zend_parse_parameters

獲取函式傳遞的引數,可以使用zend_parse_parameters函式,細心的同學會發現官方生成的預設的函式也是用這個函式來接收引數的。

這個函式怎麼用?

首先可以把這個看書當作php的scanf一樣使用。(這個函式不熟悉的自覺

這裡)

zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, &引數1,&引數2…);

第一個引數是傳遞給函式的引數個數。通常的做法是傳給它ZEND_NUM_ARGS()。(ZEND_NUM_ARGS() 來表示對傳入的引數“有多少要多少”)這是一個表示傳遞給函式引數總個數的巨集。

第二個引數是為了執行緒安全,總是傳遞TSRMLS_CC巨集。

第三個引數是一個字串,指定了函式期望的引數型別,後面緊跟著需要隨引數值更新的變數列表。由於PHP是弱型別語言,採用鬆散的變數定義和動態的型別判斷,而c語言是強型別的,而zend_parse_parameters()就是為了把不同型別的引數轉化為期望的型別。(當實際的值無法強制轉成期望的型別的時候,會發出一個警告)

第四第五直到第n個引數,都是要傳進來的值的數值。

第三個引數詳解

關於第三個引數,這裡提供一個供選擇的引數列表:

型別指定符 對應的C型別 描述
l long 符號整數
d double 浮點數
s char *, int 二進位制字串,長度
b zend_bool 邏輯型(1或0)
r zval * 資源(檔案指標,資料庫連線等)
a zval * 聯合陣列
o zval * 任何型別的物件
O zval * 指定型別的物件。需要提供目標物件的類型別
z zval * 無任何操作的zval

這邊有兩點需要特別注意
1.這裡的字串型別對應的c型別有兩個引數。是的。這說明在使用zend_parse_parameters()函式的時候需要這麼使用:

zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",&name, &name_len)

一個表示字串內容,另一個是字串長度。

2.需要大概知道一下,zval是啥?
zval是Zend引擎的值容器。無論這個變數是布林型,字串型或者其他任何型別,其資訊總會包含在一個zval聯合體中。來一波zval的結構:

typedef union _zval {

    long lval;

    double dval;

    struct {
        char *val;

        int len;

    } str;

    HashTable *ht;

    zend_object_value obj;

} zval;

ps:關於typedef就不解釋了,因為我發現有人已經寫的非常好了,請點選關於typedef的用法總結

這邊還涉及到額外的三個用法,來增強我們接收引數的能力:

符號 解釋
| 它之前的引數都是必須的,之後的都是非必須的,也就是有預設值的。
! 如果接收了一個PHP語言裡的null變數,則直接把其轉成C語言裡的NULL,而不是封裝成IS_NULL型別的zval。
/ 如果傳遞過來的變數與別的變數共用一個zval,而且不是引用,則進行強制分離,新的zval的is_ref__gc==0, and refcount__gc==1.

“|”和”!”將在下文有具體例子講解,關於”/”雖然大概懂意思,但沒想到具體的例子。

走一波與php的互動

正常的樣子

在PHP中

<?php
function my_function($msg) {
    echo "我收到引數啦: {$msg}!\n";
}
my_function('咖啡色的羊駝');

如果my_function寫成PHP擴充套件:

ZEND_FUNCTION(my_function) {
    char *msg;
    int msg_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",&msg, &msg_len) == FAILURE){
        RETURN_NULL();
    }
    php_printf("我收到引數啦:");
    PHPWRITE(msg, msg_len);
    php_printf("!\n");
}

兩個引數的樣子

在PHP中

<?php
function my_function($email, $msg) {
    echo "我收到引數啦: {$email}、 {$msg}!\n";
}
my_function('[email protected]', '咖啡色的羊駝');

如果my_function寫成PHP擴充套件:

ZEND_FUNCTION(my_function) {
    char *email;
    int email_len;
    char *msg;
    int msg_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",&msg, &msg_len,&email, &email_len) == FAILURE){
        RETURN_NULL();
    }
    php_printf("我收到引數啦:");
    PHPWRITE(email, email_len);
    PHPWRITE(msg, msg_len);
    php_printf("!\n");
}

兩個引數,其中一個可選且有預設值

<?php
function my_function($email, $msg = '咖啡色的羊駝') {
    echo "我收到引數啦: {$email}、 {$msg}!\n";
}
my_function('[email protected]');

如果my_function寫成PHP擴充套件:

ZEND_FUNCTION(my_function) {
    char *email;
    int email_len;
    char *msg = "咖啡色的羊駝";
    int msg_len = sizeof("咖啡色的羊駝") - 1;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",&msg, &msg_len,&email, &email_len) == FAILURE){
        RETURN_NULL();
    }
    php_printf("我收到引數啦:");
    PHPWRITE(email, email_len);
    PHPWRITE(msg, msg_len);
    php_printf("!\n");
}

這裡說明了”|”的使用

引數為null時,省記憶體的寫法

先來不省的寫法

ZEND_FUNCTION(my_function) {
    zval *val;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z",&val) == FAILURE) {
        RETURN_NULL();
    }
}

省記憶體

ZEND_FUNCTION(my_function) {
    zval *val;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!",&val) == FAILURE) {
        RETURN_NULL();
    }
}

這裡例子用上了”!”,每個zval,包括IS_NULL型的zval,都需要佔用一定的記憶體空間,需要cpu的計算資源來為它申請記憶體、初始化,並在它們完成工作後釋放掉。但是很多程式碼都都沒有意識到這一點。有很多程式碼都會把一個null型的值包裹成zval的IS_NULL型別,在擴充套件開發裡這種操作是可以優化的,我們可以把引數接收成C語言裡的NULL。
所以就差了一個!,第二個例子就更省了記憶體。

2.zend_get_arguments()

用法

例子是最好的用法講解,上例子:

ZEND_FUNCTION(my_function) {
    zval *email;
    if (zend_get_parameters(ZEND_NUM_ARGS(), 1, &email)== FAILURE) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING,"至少需要一個引數");
        RETURN_NULL();
    }
    // ... 
}

區別與特點

1.能夠相容老版本的PHP,並且只以zval為載體來接收引數。
2.直接獲取,而不做解析,不會進行型別轉換,所有的引數在擴充套件實現中的載體都需要是zval型別的。
3.接受失敗的時候,不會自己丟擲錯誤,也不能方便的處理有預設值的引數。
4.會自動的把所有符合copy-on-write的zval進行強制分離,生成一個嶄新的copy送到函式內部。

綜合評價:還是用zend_parse_parameters吧,這個函數了解下即可,不給力。

3.zend_get_parameters_ex()

用法

ZEND_FUNCTION(my_function) {
    zval **msg;
    if (zend_get_parameters_ex(1, &msg) == FAILURE) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING,"至少需要一個引數");
        RETURN_NULL();
    }
    // ...
}

不需要ZEND_NUM_ARGS()作為引數,因為它是在是在後期加入的,那個引數已經不再需要了。

區別與特點

1.此函式基本同zend_get_parameters()。
2.唯一不同的是它不會自動的把所有符合copy-on-write的zval進行強制分離,會用到老的zval的特性

綜合評價:極端情況下可能會用到,這個函數了解下即可。

zend_get_parameter_**

這個包括:zend_get_parameters_array_ex()和zend_get_parameters_array()

用法

ZEND_FUNCTION(var_dump) {
    int i, argc = ZEND_NUM_ARGS();
    zval ***args;

    args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
    if (ZEND_NUM_ARGS() == 0 || zend_get_parameters_array_ex(argc, args) == FAILURE) {
        efree(args);
        WRONG_PARAM_COUNT;
    }
    for (i=0; i<argc; i++) {
        php_var_dump(args[i], 1 TSRMLS_CC);
    }
    efree(args);
}

這個有點複雜,需要解釋一下:
程式首先獲取引數數量,然後通過safe_emalloc函式申請了相應大小的記憶體來存放這些zval**型別的引數。這裡使用了zend_get_parameters_array_ex()函式來把傳遞給函式的引數填充到args中。
是的
這個引數專門用於解決像php裡面的var_dump的一樣,可以無限傳引數進去的函式的實現

區別與特點

1.用於應對無限引數的擴充套件函式的實現。
2.zend_get_parameters_array與zend_get_parameters_array_ex唯一不同的是它將zval*型別的引數填充到args中,並且需要ZEND_NUM_ARGS()作為引數。

綜合評價:當遇到確實需要處理無限引數的時候,真的要用這個函數了。zend_parse_parameters真的做不到啊~

總結

拋開一切,最少也要學會zend_parse_parameters()的用法。好的。擴充套件函式傳引數的技能get了。

相關推薦

php擴充套件 擴充套件函式(如何獲取引數)()

前言 經過上一文 菜鳥學php擴充套件 之 自動生成的擴充套件框架詳解(二) ,對php擴充套件框架的整體瞭解,基本上可以說,對於扯淡如何寫php擴充套件和關鍵點有了一定的把握,但關鍵的還是在於如何寫PHP_FUNCTION的函式。 本文主要記錄一下,p

php擴充套件 hello world(一)

前言 這是一篇拖了很久就想寫的備忘錄,編寫php擴充套件一百度都是文章,但是很多文章是很老的了。有的例子都跑不通。有點尷尬。 此文用於記錄自己的筆記,當作備忘錄。 正文 1. 下載php安裝包 下載地址:php下載快鏈 本文選取的是php

PHP 學習路():一個簡單的連線資料庫並查詢的小程式(1)

上一篇文章已經寫了一個簡單的 Hello World 程式了,好吧,其實那不是我的第一個 PHP 程式,我的第一個程式是簡單地連線資料庫的(我不會介紹 PHP 語法的,至少不會專門去介紹,免得誤人子弟,要看語法介紹的自行上 W3CSchool 這個網站),話不多說,直接開始

《TCP IP卷:協議 原書第2版》pdf附網盤下載連結+(附一個的java學習路)

技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好的作用。 對於每一章的知識,先閱讀標題,弄懂大概講的是什麼主題,再去快速看一遍,不懂也沒有關係,但是一定要在不懂的

學習IntelliJ IDEAMaven工程多模組繼承和聚合建立()

一、前言IntelliJ IDEA開發環境搭建:具體參考《IntelliJ IDEA教程之如何配置Maven》Maven環境搭建:具體參考《Windows下Maven安裝以及配置》主要模擬企業開發是如何搭建Maven工程的,以Spring+SpringMVC+MyBatis為

前端----圖示字型使用

字型圖示使用方法         字型圖示最大的優點就是可以用程式碼修改樣式,看過幾個關於使用字型圖示的教學視訊,我認為最重要的還是學會根據開發人員提供的demo來學習使用,這篇文章主要是講學習的方法.       第一步,下載字型.       先從網上下載字型圖示h

Linux:atime、mtime、ctime

Linux、atime、mtime、ctatime、mtime、ctime三者稱為文件的時間戳,是文件的元信息中的七個之一,包含在inode中。其中:atime(Accesstime)指的是文件最後一次被訪問的時間;mtime(Modifytime)指的是文件內容被修改的時間,但不包括權限的修改,比如用vim

Linux:用戶賬戶管理下的/etc/passwd和/etc/shadow

Linux、/etc/passwd用戶賬戶管理主要有如下幾個配置文件:/etc/passwd、/etc/shadow、/etc/group和用戶郵箱/var/spool/mail,今天著重要說明一下/etc/passwd和/etc/shadow兩個配置文件/etc/passwd:記錄了Linux系統上所有的帳

資料庫——Windows 10安裝MySQL 8.0.12 壓版

文章目錄 下載 解壓 配置環境變數 初始化 安裝、啟動服務 修改密碼 結束 下載 下載地址 解壓 解壓到你想要的位置 配置環境變數 新建 MYSQL_HOME ,對應的值為你剛剛解壓的目錄,如

爬蟲爬取網易新聞

學習了python基本語法後,對爬蟲產生了很大的興趣,廢話不多說,今天來爬取網易新聞,實戰出真知。 開啟網易新聞(https://news.163.com/)可以發現新聞分為這樣的幾個板塊: 這次選擇國內板塊來爬取文章。 1.準備 環境:python3 編譯器:PyChar

PythonPython多版本共存安裝

Python的安裝 進入Python官方網站:www.python.org下載系統對應的Python版本 按照提示步奏安裝,安裝路徑選擇自定義,方便查詢 安裝完成後,按win+R鍵,輸入cmd進入cmd程式,輸入Python,如果能夠進入互動環境,則表示安裝完成 Python多版本共存

Python數據類型

雙引號 數據類型 height tuple 多個 ruby 一個 加法 hat 數據類型(預了解)   1.數字類型 整型:int 即不帶小數點的數,通常用來標識年齡,賬號,身份證號,等級等整數。 浮點型:float 即帶有小數點的數,通常用來標記身高,體重,科學

python第二十四天(面向物件三大特性多型)

面向物件三大特性之多型 什麼是多型 不同物件響應同一種方法的表現出不同的行為,產生不同的結果 用基類建立一套統一的規則,強制子類去遵循(使用抽象類實現),這樣便可以 在不用考慮物件具體型別的前提下而直接使用物件下的方法 為什麼要有多型 增加了程式的靈活性 以不變應萬變,不論

【 專欄 】- mongodb11天屠龍寶刀:mongodb

mongodb11天之屠龍寶刀:菜鳥學mongodb MongoDB是一個NoSQL資料庫。 它是一個開源,跨平臺,面向文件的資料庫。此MongoDB chat包括MongoDB資料庫的安裝,IDE選擇,基本操作,地理資訊檢索,p

matlab智慧演算法(2)——————————BP神經網路演算法

一.演算法背景和理論 BP(Back Propagation)神經網路是1986年由Rumelhart和McCelland為首的科學家小組提出,是一種按誤差逆傳播演算法訓練的多層前饋網路,是目前應用最廣泛的神經網路模型之一。BP網路能學習和存貯大量的輸入-輸出

ITIP基礎

IT菜鳥,以後研究的方向是雲端計算,從基礎的開始,這是第一篇博文。有不對的地方希望大家指正。IP是網路知識的基礎,今天就開始學習IP。 IP地址格式:IP地址就是“網路地址+主機地址”。 IP地址分類: IP地址是由32個二進位制數來表示,為了方便記憶,分成四段8位二進位

Java美[從到高手演練]Linux篇——壓縮及壓縮命令tar的使用

-z :是否同時具有 gzip 的屬性?亦即是否需要用 gzip 壓縮-j :是否同時具有 bzip2 的屬性?亦即是否需要用 bzip2 壓縮-v :壓縮的過程中顯示檔案!這個常用,但不建議用在背景執行過程-f :使用檔名,請留意,在 f 之後要立即接檔名喔!不要再加引數   例如使用『 tar -zcvf

SSHStruts2的配置

 一、下載struts2 我用的是struts-2.3.14-all.zip這個版本,下載完後,解壓到本地磁碟,該資料夾包含如下檔案結構: 2、建立一個web project專案 3、匯入Struts2所需jar包 4、配置web.xml

PHP擴充套件

安裝PHP擴充套件很簡單,在php.ini中去掉擴充套件前的分號,重啟伺服器即可,但有些擴充套件僅僅這樣做是不能安裝的,比如php_ldap.dll,它需要先在System32中放入libeay32.dll, ssleay32.dll。下面列舉了PHP擴充套件安裝的注意項,供大家參考。 PHP 擴充套件庫