* vim
*/
PHP_FUNCTION(helloworld)
{
RETURN_STRING(INI_STR("ini_read.helloworld"),1);
}
5.4 結果验证
上記の手順の実行が完了したら、以前のインストールに従って、次の私の展 開をインストールします。
如果你設置完了扩展,那么次行的命令您就看到出了
php -r "echohelloworld();" 今回の出力はhellohellohello
です
6 全局变量
6.1 抛砖引玉
この量は、毎回のリクエストごとに独立しているようにすること、つまり、同じリクエストの量が同じであり、異なるリクエストで使用される量であることを保証します。同じものではありません。
说道这里我先抛出一问题:是然要实现上面的要求,那么我们该怎么办呢?我应该在哪里誂我的全局变量呢?
SAPI の実現には 3 つの方法、単一処理、多処理、多回線処理がありますが、毎回実行する必要がある処理は RINIT RSHUTDOWN… です。私は RINIT プロセス中に私を初期化したわけではなく、このように要求されるたびにこの量の値が承認値になります。
1. 最初に.h文件中に記載全局变量
2. RINIT 途中でこれを初期化します
3. 调用变量
6.2 实现方式
我们还是直上资源代码:我掴一部無用注释去掉了,希望大家别介意
头文件php-iamnew.h
#ifndef PHP_IAMNEW_H
#define PHP_IAMNEW_H
extern zend_module_entry iamnew_module_entry;
#phpext_iamnew_ptr &iamnew_module_entry を定義します
#ifdef PHP_WIN32
# definePHP_IAMNEW_API __declspec(dllexport)
#elif 定義(__GNUC__) && __GNUC__ >= 4
# definePHP_IAMNEW_API __attribute__ ((visibility("default")))
#else
# definePHP_IAMNEW_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
PHP_MINIT_FUNCTION(iamnew);
PHP_MSHUTDOWN_FUNCTION(iamnew);
PHP_RINIT_FUNCTION(iamnew);
PHP_RSHUTDOWN_FUNCTION(iamnew);
PHP_MINFO_FUNCTION(iamnew);
PHP_FUNCTION(confirm_iamnew_compiled); /* テストのため、後で削除します。 */
ZEND_BEGIN_MODULE_GLOBALS(iamnew)
ロング カウンター;
ZEND_END_MODULE_GLOBALS(iamnew)
#ifdef ZTS
#define IAMNEW_G(v) TSRMG(iamnew_globals_id,zend_iamnew_globals *, v)
#else
#define IAMNEW_G(v) (iamnew_globals.v)
#endif
#endif
PHP_FUNCTION(test_global_value);
ソース文件iamnew.c
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_iamnew.h"
ZEND_DECLARE_MODULE_GLOBALS(iamnew) //全局变量
static int le_iamnew;
const zend_function_entry iamnew_functions[]= {
PHP_FE(test_global_value,NULL)
{NULL,NULL,NULL} //此处修正以後不再解释
};
zend_module_entry iamnew_module_entry ={
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER、
#endif
"iamnew"、
iamnew_functions、
PHP_MINIT(iamnew)、
PHP_MSHUTDOWN(iamnew)、
PHP_RINIT(iamnew)、
PHP_RSHUTDOWN(iamnew)、
PHP_MINFO(iamnew)、
#if ZEND_MODULE_API_NO >= 20010901
「0.1」、
#endif
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_IAMNEW
ZEND_GET_MODULE(iamnew)
#endif
// この関数の前は注釈されており、注釈は解除されており、関数の内容は空即可です
static void php_iamnew_init_globals(zend_iamnew_globals*iamnew_globals)
{
}
PHP_MINIT_FUNCTION(iamnew)
{
ZEND_INIT_MODULE_GLOBALS(iamnew, php_iamnew_init_globals,NULL);
成功を返す;
}
PHP_MSHUTDOWN_FUNCTION(iamnew)
{
成功を返す;
}
PHP_RINIT_FUNCTION(iamnew)
{
IAMNEW_G(カウンター)= 0; //初化
成功を返す;
}
PHP_RSHUTDOWN_FUNCTION(iamnew)
{
成功を返す;
}
PHP_MINFO_FUNCTION(iamnew)
{
php_info_print_table_start();
php_info_print_table_header(2,"iamnew support","enabled");
php_info_print_table_end();
}
// 增加测试関数
PHP_FUNCTION(test_global_value)
{
IAMNEW_G(カウンター)++;
RETURN_LONG(IAMNEW_G(カウンター));
}
6.3 結果验证
修正が完了したら、编译インストール我们の扩展、次のコマンドを実行して测试
php -r "echotest_global_value(); test_global_value();"
今後、これらの個別の結果については検証できない可能性がありますが、学術的効果を考慮して、大家による独自の検証を行っておきます。
6.4 实现原理
6.4.1 知识点1
我们先看一下头文件中予告全局变量的二宏
ZEND_BEGIN_MODULE_GLOBALS
ZEND_END_MODULE_GLOBALS
我们看次这二人宏的展覧会内容:
#define ZEND_BEGIN_MODULE_GLOBALS(module_name)
typedef struct _zend_##module_name##_globals {
#define ZEND_END_MODULE_GLOBALS(module_name)
}zend_##module_name##_globals;
展情報でわかりますが、この 2 つの宏は、zend_##module##_globals を呼び出している構造体です。ZEND_BEGIN_MODULE_GLOBALS および ZEND_END_MODULE_GLOBALS は、zend_##module##_globals の構造体です。の成员量。
6.4.2 知识点2
我们再来看一下ZEND_DECLARE_MODULE_GLOBALSこの句话です
#define ZEND_DECLARE_MODULE_GLOBALS(module_name)
ts_rsrc_idmodule_name##_globals_id;
その ts_rsrc_id は int 型です、ts_rsrc_id の定義を知ることができます: typedef int ts_rsrc_id;
6.4.3 知识点3
一宏ZEND_INIT_MODULE_GLOBALS、次をご覧くださいこの宏の定義
#define ZEND_INIT_MODULE_GLOBALS(module_name, globals_ctor,globals_dtor)
ts_allocate_id(&module_name##_globals_id,sizeof(zend_##module_name##_globals), (ts_allocate_ctor) globals_ctor,(ts_allocate_dtor) globals_dtor);
定義から、ZEND_INIT_MODULE_GLOBALS は変数 module_name##_globals_id に ID を割り当てます。これはスレッドセーフなリソース ID です。また、 module_name##_globals_id は ZEND_DECLARE_MODULE_GLOBALS によって割り当てられた変数ではありません。
globals_ctor はコールバック関数ポインターです。ここではこれ以上の詳細は説明しません。
6.4.4 知識ポイント
IAMNEW_G マクロの定義を見てみましょう
#ifdef ZTS //スレッドセーフですか?
#defineIAMNEW_G(v) TSRMG(iamnew_globals_id,zend_iamnew_globals *, v)
#else
#defineIAMNEW_G(v) (iamnew_globals.v)
#endif
------------------------------------------------- ---
TSRMG は次のように定義されます:
#define TSRMG(id, type, element) (((type) (*((void ***)tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
tsrm_ls は次のように定義されます:
***tsrm_ls を無効にしてください;
マクロの定義全体を見ると、IAMNEW_Gが変数の値を取得していることがわかります。
実際、IAMNEW_G はグローバル変数の値です。
TSRMG に非常に興味がある場合は、このマクロの具体的な実装を確認することもできます。これは初心者にとっては確かに少し難しいです。
7 php.iniの設定を使用してグローバル変数を初期化します
先ほど、php.ini の設定を読み取る方法とグローバル変数を初期化する方法について説明しました。このセクションのタスクも非常に明確で、php.ini の設定を使用してグローバル変数を初期化する方法です。このセクションは教室の課題として扱い、個別に説明しません。
8パラメータ受信
先ほど、PHP 拡張機能開発の一般的なアーキテクチャについて説明しました。このセクションでは、拡張機能が PHP スクリプトで渡されるパラメーターを受け取る方法を紹介します。
8.1 通常パラメータ受信
タスク: PHP スクリプトによって渡されたパラメーターを出力する拡張機能を作成します。たとえば、php –r “echo hello(‘param test.’);” とすると、param test.
が出力されます。
まず、paramtest の拡張機能を作成します。提案、コンパイル、インストール、テストのプロセスについては、再度説明しません。
まず、hello という関数を作成する必要があります。作成プロセスは以前と同じなので、特に言うことはありません。
hello 関数の実装を詳しく見てみましょう。paramtest.c の hello 関数の実装は次のとおりです。
PHP_FUNCTION(こんにちは)
{
char* str_hello;
int int_hello_str_length;
if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"s",
)
&str_hello,&int_hello_str_length)==失敗)
{
RETURN_NULL();
}
php_printf("%s",str_hello);
RETURN_TRUE;
}
まず zend_parse_parameters の関数定義を見てみましょう: ZEND_NUM_ARGS() TSRMLS_CC は、実際には zend_parse_parameters の 2 つのパラメーターです。これら 2 つのマクロの定義は、主にパラメーター情報を渡してスレッドの安全性を確保するために使用されます。 。パラメータ「s」は実際にはフォーマット文字列であり、このパラメータが ZEND コンパイラに受け取ることができるパラメータの型がどのようなものであるかを伝えることを意味します。 Zend_parse_parameters の他のパラメータは、php スクリプト内の関数変数の値を具体的に受け取る役割を果たします。
注意: PHP スクリプトの関数で 1 つのパラメーターを渡しましたが、zend_parse_parameters でそれを受け取るには 2 つのパラメーターが必要です。理由を説明します。PHP スクリプトで渡したパラメーターは C の場合は文字列です。言語の違いにより、PHP スクリプトによって渡される文字列の長さは、関数 strlen を使用して直接取得することはできません。その理由は、PHP スクリプトを渡すことができるためです。
zend_parse_parameters には、多数の書式設定文字 (この例では「s」) があります。複数のパラメータを受け入れたい場合は、対応する型識別子を書式設定パラメータに追加し、受け入れる変数をパラメータに追加するだけです。たとえば、php 関数が 2 つの変数を渡す必要があり、最初の変数が挨拶文字列で、2 番目のパラメーターが出力するかどうかを示す bool である場合、zend_parse_parameters 関数は次のように記述できます: zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC) 、"sb "、&str_hello、&len、&is_output)。
その中で、フォーマット文字列は次のようにリストできます:
PHP変数の型
コード
C拡張変数型
ブール値
b
zend_bool
長い
l
長い
ダブル
d
ダブル
文字列
す
char*、int
リソース
r
ズヴァル*
配列
あ
ズヴァル*
オブジェクト
お
ズヴァル*
ズヴァル
z
ズヴァル*
上の表には多くの zval タイプがありますが、このタイプについては次のセクションで個別に紹介します。
もう 1 つ注意すべき点は、C 関数での出力に php_printf を使用していることです。その理由は考えられますか? php はコマンド ラインでスクリプトとして実行できること、または情報を stdout に出力する場合は、単一プロセス、マルチプロセス、またはマルチスレッド モードで Web サーバー経由で実行できることを前に述べました。 Webサーバーに接続すると、情報が出力できない、または正しく出力されない可能性があります。例を挙げると、Apache+php 環境でスクリプトを作成し、ブラウザ経由でこのスクリプトにアクセスする場合、printf を使用して文字列を出力すると、ブラウザは出力情報を確認できなくなります。ただし、php_printf を使用して出力すると、出力した情報をブラウザに表示できます。
8.2 オプションパラメータの受信
通常のパラメータを受け取る方法を学びました。PHP にはオプションのパラメータがあることは誰もが知っていますが、オプションのパラメータを受け取るにはどうすればよいでしょうか? (PHP でオプションのパラメーターを使用できることを知らない場合は、まず PHP の基本をブラッシュアップすることをお勧めします。)
これは非常に簡単です。コードに直接進みましょう:
PHP_FUNCTION(こんにちは)
{
char*str_hello;
int int_hello_str_length;
zend_bool is_output =0;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s|b",&str_hello,
)
&int_hello_str_length,&is_output)== 失敗)
{
RETURN_NULL();
}
if(is_output)
{
php_printf("%s", str_hello);
}
RETURN_TRUE;
}
オプションのパラメータがある場合は、フォーマット文字列に | を追加するだけで、残りは通常の変数と同じように受け取られます。
9 zval構造解析
9.1 zvalとの出会い
まず zval の定義を見てみましょう:
typedef Union_zvalue_value {
long lval; /* 長い値 */
double dval; /* double 値 */
構造体{
char *val;
int len;
} ストラ;
HashTable *ht; /* ハッシュ テーブルの値 */
zend_object_value obj;
}zvalue_value;
struct _zval_struct {
/* 可変情報*/
zvalue_value 値 /* 値 */
zend_uint refcount__gc;
zend_uchar タイプ; /*アクティブタイプ */
zend_uchar is_ref__gc;
};
typedef struct_zval_struct zval;
9.2 zvalの作成と使用
まずコードを見てみましょう:
PHP_FUNCTION(こんにちは)
{
zval* t;
ALLOC_INIT_ZVAL(t);
Z_TYPE_P(t)= IS_LONG;
Z_LVAL_P(t)= 1234567890;
zval_ptr_dtor(&t);
}
ALLOC_INIT_ZVAL マクロは、t にメモリを割り当て、t を空の変数に初期化するために使用されます。 Z_LVAL_P は、変数に値を割り当てるために使用されます。可変空間。
上記のコードを使用して変数を使用することに加えて、マクロ ZVAL_LONG を使用して変数をすばやく定義し、変数に値を割り当てることもできます。つまり、上記のコードの代わりに次のコードを使用できます。
PHP_FUNCTION(こんにちは)
{
zval* t;
ZVAL_LONG(t, 1234567890);
}
次のマクロを使用すると、zval 変数をすばやく定義して使用できます:
ZVAL_RESOURCE、 ZVAL_BOOL、ZVAL_NULL、ZVAL_LONG、ZVAL_DOUBLE
ZVAL_STRING、ZVAL_STRINGL、ZVAL_EMPTY_STRING、ZVAL_ZVAL
上記のマクロは非常に簡単です。わからない場合は、ソースコードを見てください。
10 関数の戻り値
ついに zval についての話が終わりました。先ほど関数の定義と使い方について話しましたが、関数の戻り値については話していませんでした。 C 拡張では関数の戻り値の型が zval であるため、このセクションを説明のためにここに置きます。
このセクションの課題では、加算、減算、乗算、除算の演算を完了するための簡単な計算機を作成します。 要件: 関数 Calculate(num1, num2, opt) を作成します。 num1 opt num2=?手術。上記のタスクを実行するコードを見てみましょう:
PHP_FUNCTION(計算)
{
int num1;
int num2;
char* オプション;
int opt_len;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"lls",
)
&num1,&num2,&opt,&opt_len)==失敗)
{
php_printf("パラメータエラー。例:calculate(123, 456, '+')n");
RETURN_NULL();
}
if(1!= opt_len)
{
php_printf("パラメータエラー。例:calculate(123, 456, '+')n");
RETURN_NULL();
}
スイッチ(opt[0])
{
ケース「+」:
return_value->type= IS_LONG;
return_value->value.lval= num1 + num2;
休憩;
ケース「-」:
return_value->type= IS_LONG;
return_value->value.lval= num1 - num2;
休憩;
ケース「*」:
return_value->type= IS_LONG;
return_value->value.lval= num1 * num2;
休憩;
ケース「/」:
return_value->type= IS_DOUBLE;
return_value->value.lval= num1 *1.0 / num2;
休憩;
デフォルト:
return_value->type= IS_LONG;
return_value->value.lval= 0;
休憩;
}
}
上記のコードを見て、return_value がどこから来たのかについて疑問を感じませんか?
return_value はマクロ PHP_FUNCTION で定義されています。PHP_FUNCTION はこの変数を宣言し、この変数を NULL に割り当てます。
マクロの定義を見てみましょう。
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FUNCTION(名前) ZEND_NAMED_FUNCTION(ZEND_FN(名前))
#define INTERNAL_FUNCTION_PARAMETERSint ht, zval *return_value,
zval **return_value_ptr、zval *this_ptr、intreturn_value_used TSRMLS_DC
上記のマクロ定義から、return_value は zval へのポインターです。PHP はこの変数を使用して関数の戻り値を指します。実際、単純な戻り値を実行できるマクロも用意されています。マクロ定義は次のとおりです。 RETVAL_*(v)、* はさまざまな変数タイプを表し、v は変数の値を表します (例: RETVAL_LONG(34))。その値は 34 です。
啦 00:14、とんでもないことを言う 子供の頃は正月が好きだったが、大人になると怖いことがわかった。あなたも同じように感じているでしょうか?
これ以上話す必要はありません。ご質問がございましたら、niijiaming0819@163.com までメールをお送りいただくか、qq947847775 までご連絡ください。
もう遅いです、皆さんおやすみなさい。
11 配列の使用法と HashTable の概要
このセクションでは、PHP 配列について説明します。PHP では、配列は HashTable を使用して実装されます。このセクションでは、まず HashTable について詳しく紹介し、その後、HashTable の使用方法について説明します
11.1 可変長構造
いわゆる可変長構造は、実際には C 言語構造の特別な使用法であり、それについては何も目新しいものではありません。まずは可変長構造体の一般的な定義方法を見てみましょう。
typedef 構造体バケット {
int;
文字キー[30];
文字値[1];
}バケツ;
バケット構造を定義しました。この構造を使用して生徒の個人プロファイルを保存したいと考えています。キーは学生の名前を保存するために使用され、値は学生のプロファイルを保存するために使用されます。興味があるかもしれませんが、この値は長さ 1 を宣言しています。1 文字にどれだけの情報を格納できるでしょうか?
実際、長い構造の場合、次のような変数を直接定義することはできません。 このように使用すると、Value には多くの情報が格納されなくなります。可変長構造体の場合、使用するときにまず可変長構造体へのポインタを宣言し、次に malloc 関数を通じて関数空間を割り当てる必要があります。使用する必要がある空間の長さに応じて malloc することができます。一般的な使用方法は次のとおりです。
バケット*pバケット;
pBucket =malloc(sizeof(Bucket)+ n *sizeof(char));
ここで、n は使用する値の長さです。このように使用すると、value が指す文字列はすぐに長くなりますか?
11.2 ハッシュテーブルの概要
まずHashTableの定義を見てみましょう
struct _hashtable;
typedef 構造体バケット {
ulong h;//要素が数値インデックスの場合に使用されます
uint nKeyLength;//文字列インデックスを使用する場合、この変数はインデックスの長さを表し、インデックス (文字列) は最後の要素 aKey に格納されます
void *pData;//保存されたデータがポインタの場合、pDataPtr はこのデータを指し、pData は pDataPtr を指します。
void *pDataPtr;
struct Bucket *pListNext;//前の要素
struct Bucket *pListLast;//次の要素
structbucket *pNext;//次のバケットへのポインタ
structbucket *pLast;//前のバケットへのポインタ
char arKey[1];//主に可変長構造を実現するために最後に配置する必要があります
}バケツ;
typedef struct _hashtable {
uint nTableSize //ハッシュテーブルのサイズ
uint nTableMask; //数値的には nTableSize - 1 に等しい
uint nNumOfElements; // 現在の HashTable に保存されているレコードの数を記録します
ulongnNextFreeElement //次の空きバケットを指します
Bucket *pInternalPointer; //この変数は配列の反転に使用されます
Bucket *pListHead //バケットの先頭を指します
Bucket *pListTail // Bucket の末尾を指します
バケット **arBuckets;
dtor_func_tpDestructor; // 配列の追加、削除、変更、またはチェック時に自動的に呼び出され、特定のクリーニング操作に使用される関数ポインター
zend_bool // 永続的ですか?
unsigned char nApplyCount;
zend_boolbApplyProtection; // nApplyCount と連携して、配列の走査中の無限再帰を防止します
#if ZEND_DEBUG
一貫性がありません;
#endif
}ハッシュテーブル;
上記の定義をよく見ていただければと思います。私が説明するときに明確に説明できないことがいくつかあります。コードを見ていただくとより明確になります。 PHP の配列は実際にはヘッド ノードを持つ二重リンク リストであり、HashTable がヘッドで、Bucket には特定のノード情報が格納されます。
11.3 HashTableの内部関数の解析
11.3.1 マクロ HASH_PROTECT_RECURSION
#defineHASH_PROTECT_RECURSION(ht)
if ((ht)->bApplyProtection) {
zend_error(E_ERROR, "ネストレベルが深すぎます - 再帰的な依存関係?");
}
このマクロは主に循環参照を防ぐために使用されます。
11.3.2 マクロ ZEND_HASH_IF_FULL_DO_RESIZE
#defineZEND_HASH_IF_FULL_DO_RESIZE(ht)
if ((ht)->nNumOfElements >(ht)->nTableSize) {
zend_hash_do_resize(ht);
}
このマクロの機能は、現在の HashTable 内の要素の数が HashTable の合計サイズより大きいかどうかを確認することです。その数が HashTable のサイズより大きい場合、スペースを再割り当てします。 zend_hash_do_resize を見てみましょう
静的 intzend_hash_do_resize(HashTable*ht)
{
バケツ **t;
IS_CONSISTENT(ht);
if ((ht->nTableSize<< 1) >0) { /* テーブルサイズを 2 倍にしましょう */
t = (バケット**) perrealloc_recoverable(ht->arBuckets,
)
(ht->nTableSizepersistent);
if (t){
HANDLE_BLOCK_INTERRUPTIONS();
ht->arBuckets = t;
ht->nTableSize = (ht->nTableSize
ZEND_API int_zend_hash_init(HashTable*ht, uint nSize,hash_func_t pHashFunction,dtor_func_t pDestructor,zend_bool 永続 ZEND_FILE_LINE_DC)