ホームページ バックエンド開発 PHPチュートリアル [翻訳] [php 拡張機能と埋め込み] 第 7 章 - パラメータの受け入れ

[翻訳] [php 拡張機能と埋め込み] 第 7 章 - パラメータの受け入れ

Jun 13, 2016 pm 12:52 PM
quot zend zval

[翻訳][php 拡張機能と埋め込み] 第 7 章 - パラメータの受け入れ

完全な翻訳コンテンツ PDF ドキュメントのダウンロード アドレス: http://download.csdn.net/detail/lgg201/5107012

この本は現在、laruence (http://www.laruence.com) と walu (http://www.walu.cc) によって github 上で翻訳されています。翻訳プロジェクトのアドレスは https://github.com です。 /walu/phpbook

Github 上のこの本のアドレス: https://github.com/goosman-lei/php-eae

将来的には、この本は独立したバージョンを保持したまま、部分的に phpbook プロジェクトに統合される可能性があります。


元のタイトル:

原作者:サラ・ゴーレモン

翻訳者: goosman.lei (Lei Guo)

翻訳者のメールアドレス: lgg860911@yahoo.com.cn

翻訳者ブログ: http://blog.csdn.net/lgg201

権利表明

この翻訳は、いかなる利益も得ることなく、制限なく自由に配布することができます。

いくつかの「プレビュー」例外を除いて、これまで扱ってきた拡張関数は単純で、返すだけのものでした。しかし、ほとんどの関数には、通常、いくつかのパラメーターを渡して期待する目的が 1 つだけあるわけではありません。値に基づいて値を受け取り、その他の追加の処理に役立つ応答を受け取ります。

zend_parse_parameters() の自動型変換

前の章で見た戻り値と同様に、パラメータの値も zval 参照への間接的なアクセスを中心に展開します。これらの zval* の値を取得する最も簡単な方法は、zend_parse_parameters を使用することです。 () 関数。

zend_parse_parameters() の呼び出しは、ほとんどの場合、ZEND_NUM_ARGS() マクロで始まり、その後に普遍的な TSRMLS_CC が続きます。その名前から推測できるように、ZEND_NUM_ARGS() の内部動作により、実際のパラメータ数が返されます。 zend_parse_parameters() メソッドでは、おそらく値を直接知る必要はないので、とりあえず

を渡してください。

zend_parse_parameters() の次のパラメータはフォーマット文字列パラメータです。これは、Zend エンジンでサポートされる基本的な型記述文字で構成される文字シーケンスであり、受け入れられる関数パラメータを記述するために使用されます。次の表は基本的な型です。文字:


文字を入力してください

ユーザー空間のデータ型

b

ブール値

l

整数

d

浮動小数点

文字列

r

リソース

a

配列

オブジェクト インスタンス

指定された型のオブジェクト インスタンス

z

非特定の zval

Z

非特定の zval を間接参照


zend_parse_parameters() の残りのパラメータは、フォーマット文字列で指定された型の説明によって異なります。たとえば、long データ型は次のようにデコードされます。

PHP_FUNCTION(sample_getlong)
{
    long foo;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
                         "l", &foo) == FAILURE) {
        RETURN_NULL();
    }
    php_printf("The integer value of the parameter you "
             "passed is: %ld\n", foo);
    RETURN_TRUE;
}
ログイン後にコピー

通常、integer と long の記憶域スペースは同じですが、まったく同じではありません。int 型データを long* パラメーターに逆参照しようとすると、特に 64 ではエラーが発生しやすくなります。 -bit プラットフォームでは、以下の表にリストされているデータ型を厳密に使用してください。


タイプ

C言語での対応するデータ型

b

zend_bool

l

長い

d

ダブル

char *, int

r

zval *

a

zval *

zval *

zval *、zend_class_entry *

z

zval *

Z

zval *


注意, 所有其他的复杂类型实际上都解析为简单的zval. 这样做的原因和不使用RETURN_*()宏返回复杂数据类型一样, 都是受限于无法真正的模拟C空间中的这些结构. zend_parse_parameters()能为你的函数所做的, 是确保你接收到的zval *是正确的类型. 如果需要, 它甚至会执行隐式的类型转换, 比如将数组转换为stdClass的对象.

s和O类型需要单独说明, 因为它们一次调用需要两个参数. 在第10章"php4对象"和第11章"php5对象"中你将更进一部的了解O. 对于s这个类型, 我们对第5章"你的第一个扩展"的sample_hello_world()函数进行一次扩展, 让它可以跟指定的人名打招呼.

function sample_hello_world($name) {
    echo "Hello $name!\n";
}
/* 在C中, 你将使用zend_parse_parameters()函数接受一个字符串 */
PHP_FUNCTION(sample_hello_world)
{
    char *name;
    int name_len;


    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
                        &name, &name_len) == FAILURE) {
        RETURN_NULL();
    }
    php_printf("Hello ");
    PHPWRITE(name, name_len);
    php_printf("!\n");
}
ログイン後にコピー

zend_parse_parameters()函数可能由于函数传递的参数太少不能满足格式串, 或者因为其中某个参数不能转换为请求的类型而失败. 这种情况下, 它将自动的输出错误消息, 因此你的扩展不需要这样做.

要请求超过1个参数, 就需要扩充格式串, 包括其他字符, 并将zend_parse_parameters()调用的后面其他参数压栈. 参数和它们在用户空间函数定义的行为一致, 从左向右解析.

function sample_hello_world($name, $greeting) {
    echo "Hello $greeting $name!\n";
}
sample_hello_world('John Smith', 'Mr.');
Or:
PHP_FUNCTION(sample_hello_world)
{
    char *name;
    int name_len;
    char *greeting;
    int greeting_len;


    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
      &name, &name_len, &greeting, &greeting_len) == FAILURE) {
        RETURN_NULL();
    }
    php_printf("Hello ");
    PHPWRITE(greeting, greeting_len);
    php_printf(" ");
    PHPWRITE(name, name_len);
    php_printf("!\n");
}
ログイン後にコピー

除了基础类型, 还有3个元字符用于修改参数处理方式. 如下表所示:



可选参数

我们再来看一看修订版的sample_hello_world()示例, 下一步是增加一个可选的$greeting参数:

function sample_hello_world($name, $greeting='Mr./Ms.') {
    echo "Hello $greeting $name!\n";
}
ログイン後にコピー

sample_hello_world()现在可以只用$name参数调用, 也可以同时使用两个参数调用.

sample_hello_world('Ginger Rogers','Ms.');
sample_hello_world('Fred Astaire');
ログイン後にコピー

当不传递第二个参数时, 使用默认值. 在C语言实现中, 可选参数也是以类似的方式指定.

要完成这个功能, 我们需要使用zend_parse_parameters()格式串中的管道符(|). 管道符左侧的参数从调用栈解析, 所有管道符右边的参数如果在调用栈中没有提供, 则不会被修改(zend_parse_parameters()格式串后面对应的参数). 例如:

PHP_FUNCTION(sample_hello_world)
{
    char *name;
    int name_len;
    char *greeting = "Mr./Mrs.";
    int greeting_len = sizeof("Mr./Mrs.") - 1;

    /* 如果调用时没有传递第二个参数, 则greeting和greeting_len保持不变. */
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
      &name, &name_len, &greeting, &greeting_len) == FAILURE) {
        RETURN_NULL();
    }
    php_printf("Hello ");
    PHPWRITE(greeting, greeting_len);
    php_printf(" ");
    PHPWRITE(name, name_len);
    php_printf("!\n");
}
ログイン後にコピー

除非调用时提供了可选参数的值, 否则不会修改它的初始值, 因此, 为可选参数设置初始的默认值非常重要. 多数情况下, 它的初始默认值是NULL/0, 不过有时候, 比如上面的例子, 默认的是有意义的其他值.

IS_NULL Vs. NULL

每个zval, 即便是非常简单的IS_NULL类型, 都会占用一块很小的内存空间. 从而, 它就需要一些(CPU)时钟周期去分配内存空间, 初始化值, 最后认为不再需要它的时候释放它.

对于很多函数, 在调用空间使用NULL参数只是标记参数不重要, 因此这个处理就没有意义. 幸运的是zend_parse_parameters()允许参数被标记为"允许NULL", 如果在一个格式描述字符后加上感叹号(!), 则表示对应参数如果传递了NULL, 则将zend_parse_parameters()调用时对应的参数设置为真正的NULL指针. 考虑下面两段代码, 一个有这个修饰符, 一个没有:

PHP_FUNCTION(sample_arg_fullnull)
{
    zval *val;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z",
                                    &val) == FAILURE) {
        RETURN_NULL();
    }
    if (Z_TYPE_P(val) == IS_NULL) {
        val = php_sample_make_defaultval(TSRMLS_C);
    }
...
PHP_FUNCTION(sample_arg_nullok)
{
    zval *val;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!",
                                    &val) == FAILURE) {
        RETURN_NULL();
    }
    if (!val) {
        val = php_sample_make_defaultval(TSRMLS_C);
    }
...
ログイン後にコピー

这两个版本的代码其实没什么不同, 前者表面上看起来需要更多的处理时间. 通常, 这个特性并不是很有用, 但最好还是知道有这么回事.

强制隔离

当一个变量传递到一个函数中后, 无论是否是引用传值, 它的refcount至少是2; 一个是变量自身, 另外一个是传递给函数的拷贝. 因此在修改zval之前(如果直接在参数传递进来的zval上), 将它从它所属的非引用集合中分离出来非常重要.

如果没有"/"格式修饰符, 这将是一个单调重复的工作. 这个格式修饰符自动的隔离所有写时拷贝的引用传值参数, 这样, 你的函数中就可以为所欲为了. 和NULL标记一样, 这个修饰符放在它要修饰的格式描述字符后面. 同样和NULL标记一样, 直到你真的有使用它的地方, 你可能才知道你需要这个特性.

zend_get_arguments()

如果你正在设计的代码计划在非常旧的php版本上工作, 或者你有一个函数, 它只需要zval *, 就可以考虑使用zend_get_parameters()的API调用.

zend_get_parameters()调用与它对应的新版本有一点不同. 首先, 它不会自动执行类型转换; 而是所有期望的参数都是zval *数据类型. 下面是zend_get_parameters()的最简单用法:

PHP_FUNCTION(sample_onearg)
{
    zval *firstarg;
    if (zend_get_parameters(ZEND_NUM_ARGS(), 1, &firstarg)
                                        == FAILURE) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING,
            "Expected at least 1 parameter.");
        RETURN_NULL();
    }
    /* Do something with firstarg... */
}
ログイン後にコピー

其次, 你可能已经注意到, 它需要手动处理错误消息, zend_get_parameters()并不会在失败的时候输出错误文本. 它对可选参数的处理也很落后. 如果你让它抓取4个参数, 最好至少给它提供4个参数, 否则可能返回FAILURE.

最后, 它不像parse, 这个get变种会自动的隔离所有写时复制的引用集合. 如果你想要跳过自动隔离, 就要使用它的兄弟接口: zend_get_parameters_ex().

除了不隔离写时复制集合, zend_get_parameters_ex()还有一个不同点是返回zval **指针而不是直接的zval *. 它们的差别同样可能直到你使用的时候才知道需要它们, 不过它们最终的使用非常相似:

PHP_FUNCTION(sample_onearg)
{
    zval **firstarg;
    if (zend_get_parameters_ex(1, &firstarg) == FAILURE) {
        WRONG_PARAM_COUNT;
    }
    /* Do something with firstarg... */
}
ログイン後にコピー

要注意的是_ex版本不需要ZEND_NUM_ARGS()参数. 这是因为增加_ex的版本时已经比较晚了, 当时Zend引擎已经不需要这个参数了.

在这个例子中, 你还使用了WRONG_PARAM_COUNT宏, 它用来处理E_WARNING错误消息的显示已经自动的离开函数.

处理任意数目的参数

还有两个zend_get_parameters()族的函数, 用于解出zval *和zval **指针集合, 它们适用于有很多参数或运行时才知道参数个数的引用场景.

考虑var_dump()函数, 它用来展示传递给它的任意数量的变量的内容:

PHP_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);
}
ログイン後にコピー

这里, var_dump()预分配了一个传给函数的参数个数大小的zval **指针向量. 接着使用zend_get_parameters_array_ex()一次性将参数摊入到该向量中. 你可能会猜到, 存在这个函数的另外一个版本: zend_get_parameters_array(), 它们仅有一点不同: 自动隔离, 返回zval *而不是zval **, 在第一个参数上要求传递ZEND_NUM_ARGS().

参数信息和类型暗示

在上一章已经简短的向你介绍了使用Zend引擎2的 参数信息结构进行类型暗示的概念. 我们应该记得, 这个特性是针对ZE2(Zend引擎2)的, 对于php4的ZE1(Zend引擎1)不可用.

我们重新从ZE2的参数信息结构开始. 每个参数信息都使用ZEND_BEGIN_ARG_INFO()或ZEND_BEGIN_ARG_INFO_EX()宏开始, 接着是0个或多个ZEND_ARG_*INFO()行, 最后以ZEND_END_ARG_INFO()调用结束.

这些宏的定义和基本使用可以在刚刚结束的第6章"返回值"的编译期引用传值一节中找到.

假设你想要重新实现count()函数, 你可能需要创建下面这样一个函数:

PHP_FUNCTION(sample_count_array)
{
    zval *arr;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a",
                                    &arr) == FAILURE) {
        RETURN_NULL();
    }
    RETURN_LONG(zend_hash_num_elements(Z_ARRVAL_P(arr)));
}
ログイン後にコピー

zend_parse_parameters()会做很多工作, 以保证传递给你的函数是一个真实的数组. 但是, 如果你使用zend_get_parameters()函数或某个它的兄弟函数, 你就需要自己在函数中做类型检查. 当然, 你也可以使用类型暗示! 通过定义下面这样一个arg_info结构:

static
    ZEND_BEGIN_ARG_INFO(php_sample_array_arginfo, 0)
        ZEND_ARG_ARRAY_INFO(0, "arr", 0)
    ZEND_END_ARG_INFO()
ログイン後にコピー

并在你的php_sample_function结构中声明该函数时使用它:

PHP_FE(sample_count_array, php_sample_array_arginfo)
ログイン後にコピー

这样就将类型检查的工作交给了Zend引擎. 同时你给了你的参数一个名字(arr), 它可以在产生错误消息时被使用, 这样在使用你API的脚本发生错误时, 更加容易跟踪问题.

在第6章第一次介绍参数信息结构时, 你可能注意到了对象, 也可以使用ARG_INFO宏进行类型暗示. 只需要在参数名后增加一个额外的参数来说明类型名(类名)

static
    ZEND_BEGIN_ARG_INFO(php_sample_class_arginfo, 0)
        ZEND_ARG_OBJECT_INFO(1, "obj", "stdClass", 0)
    ZEND_END_ARG_INFO()
ログイン後にコピー

要注意到这里的第一个参数(by_ref)被设置为1. 通常来说这个参数对对象来说并不是很重要, 因为ZE2中所有的对象默认都是引用方式的, 对它们的拷贝必须显式的通过clone来实现. 在函数调用内部强制clone可以做到, 但是它和强制引用完全不同.

因为当设置了zend.ze1_compatiblity_mode标记时, 你可能关心ZEND_ARG_OBJECT_INFO一行的by_ref设置. 这种特殊情况下, 对象可能仍然传递的是一个拷贝而不是引用. 因为你在处理对象的时候, 可能需要的是一个真正的引用, 因此设置这个标记你就不用担心这一方面的影响.

不要忘记了数组和对象的参数信息宏中有一个allow_null选项. 关于允许NULL的细节请参考前一章的编译期引用传值一节.

当然, 使用使用参数信息进行类型暗示只在ZE2中支持, 如果你想让你看的扩展兼容php4, 需要使用zend_get_parameters(), 这样就只能将类型验证放到函数内部, 手动的通过测试Z_TYPE_P(value)或使用第2章看到的convert_to_type()方法进行自动类型转换来完成.

小结

现在你的手头可能已经有点脏乱了, 和用户空间通信的功能代码通过简单的输入/输出函数实现. 已经比较深入的了解了zval的引用计数系统, 并学习了控制变量传递到你的内部函数方法和时机.

下一章将开始学习数组数据类型, 并了解用户空间的数组表示怎样映射到内部的HashTable实现. 此外还将看到一大批可选的Zend以及php api函数, 它们用于操纵这些复杂的结构体.


目录

上一章: 返回值
下一章: 在数组和哈希表上工作

类型修改符

含义

|

接下来是可选参数了.当指定它时,所有之前的参数都被认为是必须的,所有后续的参数都被认为是可选的.

!

! の前の修飾子に対応するパラメータが NULL の場合、 によって提供される内部変数は実数の に設定されます。 NULLポインタ.

/

/ の前の修飾子に対応するパラメーターはコピーオンライト として指定されており、 は自動的に新しい zval(is_ref = 0、refcount = 1)

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Zend Framework でのアクセス許可制御に ACL (アクセス コントロール リスト) を使用する方法 Zend Framework でのアクセス許可制御に ACL (アクセス コントロール リスト) を使用する方法 Jul 29, 2023 am 09:24 AM

Zend Framework でのアクセス許可制御に ACL (AccessControlList) を使用する方法 はじめに: Web アプリケーションでは、アクセス許可制御は重要な機能です。これにより、ユーザーはアクセスを許可されたページと機能にのみアクセスできるようになり、不正アクセスが防止されます。 Zend フレームワークは、ACL (AccessControlList) コンポーネントを使用してアクセス許可制御を実装する便利な方法を提供します。この記事では、Zend Framework で ACL を使用する方法を紹介します。

PHP 実装フレームワーク: Zend Framework 入門チュートリアル PHP 実装フレームワーク: Zend Framework 入門チュートリアル Jun 19, 2023 am 08:09 AM

PHP 実装フレームワーク: ZendFramework 入門チュートリアル ZendFramework は、PHP によって開発されたオープン ソースの Web サイト フレームワークであり、現在 ZendTechnologies によって保守されています。ZendFramework は、MVC デザイン パターンを採用し、Web2.0 アプリケーションと Web サーブの実装に役立つ一連の再利用可能なコード ライブラリを提供します。 。 ZendFramework は PHP 開発者に非常に人気があり、尊敬されており、幅広い機能を備えています。

PHP は変数をどのように保存するのでしょうか? zval構造を理解していますか? PHP は変数をどのように保存するのでしょうか? zval構造を理解していますか? May 26, 2022 am 09:47 AM

PHP で変数を定義するときに型を宣言する必要はありません。最初に変数 $a に整数値を代入し、後で他の型に簡単に変更できます。では、この変数 $a は PHP ソース コードにどのように格納されるのでしょうか?この疑問を念頭に置いて、PHP のソース コードを見てみましょう。

php提交表单通过后,弹出的对话框怎样在当前页弹出,该如何解决 php提交表单通过后,弹出的对话框怎样在当前页弹出,该如何解决 Jun 13, 2016 am 10:23 AM

php提交表单通过后,弹出的对话框怎样在当前页弹出php提交表单通过后,弹出的对话框怎样在当前页弹出而不是在空白页弹出?想实现这样的效果:而不是空白页弹出:------解决方案--------------------如果你的验证用PHP在后端,那么就用Ajax;仅供参考:HTML code

PHP が ZendOptimizer を認識しないのですが、どうすれば解決できますか? PHP が ZendOptimizer を認識しないのですが、どうすれば解決できますか? Mar 19, 2024 pm 01:09 PM

PHP が ZendOptimizer を認識しないのですが、どうすれば解決できますか? PHP 開発では、PHP が ZendOptimizer を認識できない状況が発生し、一部の PHP コードが正しく実行されなくなることがあります。この場合、問題を解決するために何らかの措置を講じる必要があります。考えられるいくつかの回避策を、具体的なコード例とともに以下に説明します。 1. ZendOptimizer が正しくインストールされているかどうかを確認します。まず、ZendOptimizer が正しくインストールされていることを確認する必要があります。

Window2003 IIS+MySQL+PHP+Zend 環境の設定方法 Window2003 IIS+MySQL+PHP+Zend 環境の設定方法 Jun 02, 2023 pm 09:56 PM

Windows 2003 インストール パッケージには、Zend、PHP5.2.17、PHPWind8.7、および PHPMyadmin3.5.2 が含まれています。インストール パッケージを直接ダウンロードして、リソースを検索する時間を節約できます。ただし、MySQL はアップロード制限を超えているため、MySQL 公式 Web サイトにアクセスしてダウンロードする必要があります。次に、以下に示すように、解凍して D ドライブにコピーします。 MySQLinDdisk WindowsIIS+FTP をインストールして構成します。 [スタート] > [コントロール パネル] > [プログラムの追加と削除] をクリックします。 PG の追加または削除 [Windows コンポーネントの追加と削除] (A) をクリックします。追加する

PHP フレームワーク Zend を使用して効率的な ERP 管理プラットフォームを開発する方法 PHP フレームワーク Zend を使用して効率的な ERP 管理プラットフォームを開発する方法 Jun 26, 2023 pm 11:00 PM

情報技術の急速な発展に伴い、情報管理の必要性を認識する企業が増えています。 ERP (エンタープライズ リソース プランニング) 管理プラットフォームは、現代の企業管理にとって重要なツールであり、企業がリソース プランニング、コラボレーション、制御、最適化、および管理を実現するのに役立ちます。中でも、PHP フレームワーク Zend は、開発者が ERP システムを迅速かつ効率的に開発するのに役立つ優れた開発ツールです。この記事では、Zend を使用して効率的な ERP 管理プラットフォームを開発する方法を紹介します。 1. 開発プロセスを開始する前に要件分析を決定する

Laravel と Zend: 大規模なアプリケーションの開発にはどちらのフレームワークが適していますか? Laravel と Zend: 大規模なアプリケーションの開発にはどちらのフレームワークが適していますか? Jun 19, 2023 am 08:52 AM

インターネット アプリケーションの継続的な開発に伴い、大規模なアプリケーション開発の需要も増加しています。この文脈では、自分に合った開発フレームワークを選択することが特に重要です。 Laravel と Zend は広く使用されている PHP フレームワークで、それぞれに独自の利点がありますが、大規模なアプリケーションの開発にはどちらが適しているでしょうか。 Laravel は人気のある開発フレームワークであり、PHP 開発者にとって推奨されるフレームワークの 1 つとなっています。モダンなデザインコンセプトを採用しており、EloquentORなどのさまざまな強力な組み込み機能とツールを備えています。

See all articles