ホームページ  >  記事  >  バックエンド開発  >  PHP 拡張機能開発 - v1.0_PHP チュートリアル

PHP 拡張機能開発 - v1.0_PHP チュートリアル

WBOY
WBOYオリジナル
2016-07-14 10:08:42887ブラウズ

1 はじめに
この記事は、PHP 言語と PHP 拡張機能の開発が好きなすべての同僚や学生に送る私の個人的な学習メモです。
2 準備
これ以上のゴシップはやめてください。PHP 拡張機能の開発を学びたい場合は、すでに PHP の基本知識をある程度理解していると思います。 C 言語をまだ知らない場合は、PHP 拡張機能は C 言語で書かれています
そこにはポインタのようなものがありますが、皆さんは Tan Haoqiang の「C 言語プログラミング」を批判していますが、それでもこの本を注意深く読むことをお勧めします。
PHP開発を行う前に、まず環境を準備する必要があります。言うのは簡単ですが、一部の初心者にとっては悪夢かもしれません。 UBUNTU LINUX をインストールすることをお勧めします。これ以上説明する必要はないと思います。
Linux がインストールされていることを前提としています。私は debain を使用しましたが、Ubuntu の学生も心配する必要はありません。ubuntu は debain のバリエーションであり、この諺は今も真実であり、ubuntu を使用しても同じ効果が得られると思います。
2.1 開発環境のセットアップ
まず、PHP 開発環境をインストールしましょう: PHP+APACHE2+MYSQL
UBUNTU でのインストールは非常に簡単で、次のコマンドを実行するだけで済みます。
sudo apt-get install apache2
sudo apt-get install php5
sudo apt-get install libapache2-mod-php5
sudo apt-get install mysql-server
sudo apt-get install libapache2-mod-auth-mysql
sudo apt-get install php5-mysql
sudo /etc/init.d/apache2 再起動
2.2 ソースコードのデプロイメント
PHP 拡張開発を実行するには、PHP ソース コードもダウンロードする必要があります。PHP 拡張開発を例として説明します。ダウンロード リンクが無効になるのを防ぐために、以下にいくつかのダウンロード アドレスを示します。もちろん、PHPのバージョンは変わりません^_^
http://cn2.php.net/get/php-5.3.19.tar.gz/from/this/mirror
www.php.net にアクセスし、[ダウンロード] をクリックしてソース コードをダウンロードすることもできます。
ソースコードを持っている、または入手したと仮定して、以下のソースコードを解凍しましょう tar xzvf php-5.3.19 このコマンドが正しく解凍できない場合は、tar xvf php-5.3.19 を使用して試してください。そうでない場合は、rar を使用して解凍してください。そうですね、このファイルはいつでも解凍できると思います ~ すごいことです - _ -
私たちの PHP 拡張機能は Linux 上で開発されています。Win 上で開発したい場合、この記事は適していない可能性があります。ただし、テクノロジーなどはすべて類推に基づいています。win に環境をデプロイしてから私の記事を読んでいただければと思います。
3 すべては HelloWorld から始まります
初めてコードを書いたとき、C 言語で HelloWorld を出力したときのことを今でも覚えています。その日はとても興奮しました。このページの内容を読んだ後、あなたも helloworld php 拡張機能を自分で書いていただければ幸いです。
最初の拡張プログラムの書き方は?
実際はとても簡単です~~~始めましょう~~
前回、最初のステップはphpのソースコードをアンインストールすることであると言いました。ダウンロードしましたか?
提案: 開発時には PHP を 1 つだけインストールすることをお勧めします。実際には、システムに複数の PHP をインストールできますが、初期段階で多くの不必要な説明を避けるために、読者は 1 つだけインストールすることをお勧めします。 PHP に習熟すれば、複数の PHP を使用することは問題ではなくなります。
3.1 拡張機能ディレクトリ cd php-5.3.19/ext に移動します
ext フォルダーには php 拡張機能のソース コードが保存されます。同様に、拡張機能を作成するときもここで行う必要があります。
ext フォルダーの下の各フォルダーの名前を確認してください: mysql、xml、zip、sqlite... これらはすべて php の拡​​張機能です
3.2 拡張機能開発フレームワークを確立する ./ext_skel --extname=helloworld
上記のコマンドを実行して拡張開発のフレームワークを確立すると、ext フォルダーの下に helloworld という追加フォルダーがあり、コマンド ラインからいくつかのテキストも出力されることがわかります。
説明は省略し、後で出力コマンドの意味を見てみましょう。
記者の写真、以下で分析します
3.3 PHP ソースコードのルートディレクトリに入り、ファイル vi ext/helloworld/config.m4 を編集します
このファイルのコメントを数行削除してください
いわゆるキャンセルアノテーションは、16 ~ 18 行目の前の DNL を削除することです (バージョンによって PHP ソースコードの行数が異なる場合があります。注意してください)
コメントをキャンセルしたら、ファイルを保存して終了します
3.4 php ルート ディレクトリでコマンド ./buildconf --force を実行します。この手順については、次の記事で説明します
3.5 php ソース コードのルート ディレクトリで php プログラムをコンパイルします。コマンドは ./configure--with-helloworld であることに注意してください。
3.6 拡張ディレクトリ helloworld に入り、コマンド phpize を実行します
3.7 helloworld ディレクトリで拡張機能をコンパイルします。/configure --with-php-config=/usr/local/bin/php-config (独自の環境の php-config を使用してください) --enable-helloworld
3.8 makeコマンドmakeを実行する
make を実行すると、システムがエラーを報告し、エラー メッセージは次のとおりでした
/home/work/src/php-5.3.19/ext/helloworld/helloworld.c:43: エラー: 'PHP_FE_END' がここで宣言されていません (関数内ではありません)
make: *** [helloworld.lo] エラー 1
この際、helloworld.cの43行目のPHP_FE_ENDを{NULL, NULL, NULL}に置き換える方法を教えてください
置き換えが完了したら、makeコマンドを実行して再コンパイルしてください
3.9 拡張機能をインストールします
PHP プログラムが makeinstall を使用してインストールできることを知っている学生もいるかもしれませんが、PHP の動作原理をよりよく理解するには、
PHP 拡張機能の手動インストールを使用します。実際、PHP のインストールは非常に簡単です。
3.9.1 PHP拡張機能のインストールディレクトリを見つける
work@baidu:~$ php -r "phpinfo();" |
extension_dir => /usr/lib/php5/20090626+lfs =>/usr/lib/php5/20090626+lfs
拡張機能を /usr/lib/php5/20090626+lfs ディレクトリにインストールする必要があります。
helloworld ディレクトリの下でコマンドを実行します
sudo cpmodules/helloworld.so /usr/lib/php5/20090626+lfs ディレクトリ /usr/lib/php5/20090626+lfs は実際の状況に応じて変更する必要があります
3.9.2 php.ini で拡張機能を開きます
同様に以下のコマンドを実行してください
work@baidu:~$ php -r "phpinfo();"
設定ファイル (php.ini) パス => /etc/php5/cli
ロードされた設定ファイル => /etc/php5/cli/php.ini
php.iniのパスが/etc/php5/cli/php.iniであることがわかります
sudo vi /etc/php5/cli/php.ini
ファイルの最後の行にコード extension=helloworld.so
を追加します。
3.10 拡張機能が正常にインストールされているかどうかを確認します
次のコマンドを実行して確認できます php -r "phpinfo();" |
正常にインストールされた場合は、次のメッセージが表示されるはずです
root@baidu:/usr/lib/php5/20090626+lfs# php -r "phpinfo();" |grep helloworld
ハローワールド
helloworld サポート => 有効化
この時点で、php 拡張機能のインストールは完了です
helloworld をどのように出力すればよいのかと疑問に思われるかもしれませんが、心配しないでください。以下の helloworld プログラムに従います。
3.11 拡張 helloworld ディレクトリに入り、ファイル php_helloworld.h を編集し、関数 PHP_FUNCTION(fun_helloworld);
を最後の行に追加します。
3.12 helloworld.c に関数を実装します
fun_helloworld 関数を helloworld_functions[] に追加します
3.13 拡張機能 make をコンパイルする
3.14 ステップ 9-2 に従って拡張機能を再インストールします
3.15 検証拡張機能
php -r "echo fun_helloworld();"
すべてがうまくいけば、Hello World! という挨拶が表示されます。
3.16 裏面に書いてあること
おそらくあなたは私の手順を最後までフォローし、私たちの挨拶「Hello World」をご覧になったかもしれませんが、私は開発プロセスをできるだけ詳しく説明するよう最善を尽くしました。どうか私を叱らないでください。実際、プログラム開発のプロセスは常に問題を解決するプロセスです。上記の手順で問題が発生した場合は、能力を向上させる機会が得られたことを祝福したいと思います。問題を解決する過程では、Baidu と Google が最良の助っ人です。他のチュートリアルや公式ドキュメントを通じて問題を解決することもできますが、初心者にとっては Baidu と Google が最も早く問題を解決できる方法です。
ここまで言いましたが、皆さんに言いたいのは、学習プロセス中に問題に遭遇することを恐れないでください。これらの問題は私たちの進歩の基礎だからです。
4 SAPI の概要
PHP 拡張機能をうまく書きたい場合は、SAPI を理解することが間違いなく不可欠です。このセクションを読んでもまだ SAPI を十分に使いこなすことができないかもしれませんが、それは問題ではありません。ただし、このセクションを持っている場合は、このセクションを注意深く読むことをお勧めします。ご質問がありましたら、私に連絡してください。
SAPI (ServerApplication Programming Interface) は、PHP の特定のアプリケーション プログラミング インターフェイスを指します。 PHP スクリプトを実行するには、さまざまな方法があります。最も一般的な方法は、PHP を Web サーバーに配置し、コマンド ラインで php test.php を通じて直接実行することです。実際、PHP スクリプトを他のプログラムに埋め込んで実行することもできます。
現在一般的な SAPI 実装方法には、Apache モジュール、fpm、cgi、cli などが含まれており、これらの実装はシングルプロセス SAPI、マルチプロセス SAPI、マルチスレッド SAPI の 3 つのカテゴリに分類できます。
4.1 単一プロセス SAPI
CLI/CGI モード PHP は、単一プロセスの SAPI 実装 (または実装モデルと呼ばれます) です。このタイプのリクエストは一度処理されてから閉じられます。そのライフサイクルは次のとおりです:
覚えておいてください: 上記のプロセスはすべて、リクエストごとに実行されます。
説明しましょう: コマンドラインを使用して php ファイルを実行するか、Web サーバー経由で CGI モードで php ファイルにアクセスする場合、リクエストごとに次のプロセスを実行します:
1. 各拡張MINITメソッドを実行します
2. 各拡張機能のRINITメソッドを実行します
3. アクセスしたいphpファイルを実行します
4. 各拡張機能のRSHUTDOWNメソッドを実行します
5. クリーニング作業を実行します(実行したいphpファイルに対して)
6. 各拡張機能の MSHUTDOWN メソッドを実行します
7.phpの実行が終了します
MINIT、RINIT、RSHUTDWON、MSSHUTDOWN を見たことがありますか?
4.2 マルチプロセス SAPI
PHP リクエストを処理するために PHP が Apache のモジュールにコンパイルされる場合、Apache は通常マルチプロセス モードを採用し、各サブプロセスはその存続期間を通じて複数のリクエストを処理する可能性があります。サイクル。これらの子プロセスは、Apache がシャットダウンされた場合にのみシャットダウンされます。このモデルの SAPI ライフサイクルは次のとおりです:
説明: 上の図の上部にある赤い部分は、Apache が子プロセスをフォークした後に単独で表示され、中央の青い色は、その部分がリクエストごとに 1 回のみ実行されることを示します。子プロセスが 1 つあり、上図の下の赤い部分が子プロセスの終了時に実行されます。 MINIT 関数はプロセスごとに 1 回だけ実行されますが、RINIT RSHUTDOWN 関数はリクエストごとに実行され、MSHUTDOWN 関数はプロセスの終了時に実行されます。
4.3 マルチスレッド SAPI
マルチスレッド モードは、マルチプロセスのプロセスに似ています。違いは、プロセス全体が宣言サイクル中にリクエストの開始 -> リクエストの終了を並行して繰り返すことです。
Apacheのリクエスト処理処理、PHPファイルの処理処理、PHP拡張機能の実行処理を順に説明していきます。拡張機能を深く理解して拡張機能の開発を成功させたい場合は、このセクションを詳しく読むことをお勧めします。理解できないかもしれませんが、後で詳しく説明しますので、問題ありません。
5 php 拡張機能は php.ini 設定を呼び出します
このセクションでは、php.ini ファイルの設定を読み取ります。実際、php.ini 設定を読み取る方法は多数ありますが、今回はその 1 つについてのみ説明します。興味がある場合は、個別に学習してください。
さっそく、先ほどの説明方法を変えて、今度はコードに直接進みます。 (実はphp拡張機能が設定ファイルを読み込むのが簡単すぎるためです)
(ini_read の拡張機能を作成し、コードの変更された部分が黄色でマークされているとします
5.1 設定を変更する
php.iniに設定ini_read.helloworld=hellohellohelloを追加します
5.2 ヘッダーファイルの変更
php_ini_read.hの変更点は以下の通りです: (変更部分は黄色を使用しています)
#ifndef PHP_INI_READ_H
#define PHP_INI_READ_H
externzend_module_entry ini_read_module_entry;
#define phpext_ini_read_ptr &ini_read_module_entry
#ifdef PHP_WIN32
# PHP_INI_READ_API __declspec(dllexport) を定義します
#elif 定義(__GNUC__) &&__GNUC__ >= 4
# PHP_INI_READ_API __attribute__((visibility("default"))) を定義します
#else
#PHP_INI_READ_API を定義する
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
PHP_MINIT_FUNCTION(ini_read);
PHP_MSHUTDOWN_FUNCTION(ini_read);
PHP_RINIT_FUNCTION(ini_read);
PHP_RSHUTDOWN_FUNCTION(ini_read);
PHP_MINFO_FUNCTION(ini_read);
/*
BEGIN の間に必要なグローバル変数を宣言します
および END マクロはここにあります:
ZEND_BEGIN_MODULE_GLOBALS(ini_read)
long global_value;
char *global_string;
ZEND_END_MODULE_GLOBALS(ini_read)
*/
/* 追加するすべてのユーティリティ関数で変数を使用する必要があります
php_ini_read_globals で TSRMLS_FETCH(); を呼び出します。その他を宣言した後
その関数で使用される変数、またはさらに良いのは、TSRMLS_CC で渡すことです
最後の関数引数の後にユーティリティ関数を宣言します
最後に宣言された引数の後に TSRMLS_DC を付けます。  いつも参考にさせていただきます
関数内のグローバルを INI_READ_G(変数) として指定します。  あなたは
これらのマクロの名前を短いものに変更することをお勧めします。
を参照してください。
他の php モジュール ディレクトリ内の例。
*/
#ifdef ZTS
#define INI_READ_G(v)TSRMG(ini_read_globals_id, zend_ini_read_globals *, v)
#else
#define INI_READ_G(v)(ini_read_globals.v)
#endif
#endif /* PHP_INI_READ_H */
/*
* ローカル変数:
* タブ幅: 4
* c-basic-offset: 4
* 終了:
* vim600: noet sw=4 ts=4 fdm=marker
* vim
*/
PHP_FUNCTION(helloworld);
5.3 ソースファイルの変更
ini_read.c の変更は以下の通り
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include"ext/standard/info.h"
#include "php_ini_read.h"
/* php_ini_read.h でグローバルを宣言する場合は、次のコメントを解除してください:
ZEND_DECLARE_MODULE_GLOBALS(ini_read)
*/
/* 真のグローバル リソース - ここに事前の安全性は必要ありません */
static int le_ini_read;
/* {{{ ini_read_functions[]
*
* ユーザーに表示されるすべての関数には、ini_read_functions[] にエントリが必要です。
*/
constzend_function_entry ini_read_functions[]= {
PHP_FE(helloworld, NULL) /* テストのため、後で削除します。 */
{NULL、NULL、NULL}
};
/* }}} */
/* {{{ ini_read_module_entry
*/
zend_module_entry ini_read_module_entry={
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER、
#endif
「ini_read」、
ini_read_functions、
PHP_MINIT(ini_read)、
PHP_MSHUTDOWN(ini_read)、
NULL、
NULL、
NULL、
#if ZEND_MODULE_API_NO >= 20010901
"0.1",/* 拡張機能のバージョン番号に置き換えます */
#endif
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_INI_READ
ZEND_GET_MODULE(ini_read)
#endif
/* {{{ PHP_INI
*/
PHP_INI_BEGIN()
PHP_INI_ENTRY("ini_read.helloworld", "foobar",PHP_INI_ALL, NULL)
PHP_INI_END()
/* }}} */
/* {{{ php_ini_read_init_globals
*/
/* INI エントリがある場合は、この関数のコメントを解除します
static voidphp_ini_read_init_globals(zend_ini_read_globals *ini_read_globals)
{
ini_read_globals->global_value = 0;
ini_read_globals->global_string = NULL;
}
*/
/* }}} */
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(ini_read)
{
REGISTER_INI_ENTRIES();
成功を返します;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(ini_read)
{
UNREGISTER_INI_ENTRIES();
成功を返します;
}
/* }}} */
/* request start で何もすることがなければ削除 */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(ini_read)
{
成功を返します;
}
/* }}} */
/* リクエスト終了時に何もすることがなければ削除 */
/* {{{ PHP_RSHUTDOWN_FUNCTION
*/
PHP_RSHUTDOWN_FUNCTION(ini_read)
{
成功を返します;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(ini_read)
{
php_info_print_table_start();
php_info_print_table_header(2,"ini_read support","enabled");
php_info_print_table_end();
/* php.ini にエントリがある場合はコメントを削除します
DISPLAY_INI_ENTRIES();
*/
}
/* }}} */
/* }}} */
/* 前の行は vimand emacs 用なので、正しく折りたたむことができます
ソースコードで関数を展開します。直前の対応するマークを参照してください
関数定義。関数の目的も文書化されています。お願いします
他の人がコードを編集する際の便宜のために、この規則に従ってください。
*/
/*
* ローカル変数:
* タブ幅: 4
* c-basic-offset: 4
* 終了:
* vim600: noet sw=4 ts=4 fdm=marker
* 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
ht->nTableMask = ht->nTableSize- 1;
zend_hash_rehash(ht);
HANDLE_UNBLOCK_INTERRUPTIONS();
成功を返す;
}
返品失敗;
}
成功を返します;
}
上記のコードから、HashTable がスペースを割り当てるとき、新しく割り当てられたスペースは元のスペースの 2 倍に等しいことがわかります。
11.3.3 関数_zend_hash_init
この関数は HashTable を初期化するために使用されます。まずコードを見てみましょう。
ZEND_API int_zend_hash_init(HashTable*ht, uint nSize,hash_func_t pHashFunction,dtor_func_t pDestructor,zend_bool 永続 ZEND_FILE_LINE_DC)
{
uint i = 3; //デフォルトでは HashTable のサイズは 2 の 3 乗ではありません
バケツ**tmp;
SET_INCONSISTENT(HT_OK);
if (nSize>= 0x80000000){
ht->nTableSize = 0x80000000;
} 他{
while ((1U
i++;
}
ht->nTableSize = 1 <
}
ht->nTableMask = ht->nTableSize-1;
ht->pDestructor = pDestructor;
ht->arBuckets =NULL;
ht->pListHead =NULL;
ht->pListTail =NULL;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
ht->pInternalPointer = NULL;
ht->persistent = 永続的;
ht->nApplyCount =0;
ht->bApplyProtection = 1;
/* ecalloc() を使用して、Bucket* == NULL */
if (永続的){
tmp = (バケット **)calloc(ht->nTableSize,sizeof(バケット*));
if (!tmp){
失敗を返す;
}
ht->arBuckets = tmp;
} 他{
tmp = (バケット **)ecalloc_rel(ht->nTableSize,sizeof(バケット*));
if (tmp){
ht->arBuckets = tmp;
}
}
成功を返します;
}
HashTable のサイズが 2 の n 乗に初期化されていることがわかります。さらに、2 つのメモリ メソッドがあることがわかります。1 つは calloc で、もう 1 つは ecalloc_rel です。これら 2 つのメモリ割り当てメソッドについて詳しく説明しました。興味があれば、自分で調べてみてください。
11.3.4 Function_zend_hash_add_or_update
この関数はHashTableに要素情報を追加または変更します
ZEND_API int_zend_hash_add_or_update(HashTable*ht,const char *arKey, uintnKeyLength,void *pData, uintnDataSize,void **pDest,int フラグ ZEND_FILE_LINE_DC)
{
うろんh;
uint nIndex;
バケツ *p;
IS_CONSISTENT(ht);
if (nKeyLength<= 0){
#if ZEND_DEBUG
ZEND_PUTS("zend_hash_update: 空のキーンを置くことはできません");
#endif
失敗を返す;
}
h = zend_inline_hash_func(arKey, nKeyLength);
nIndex = h & ht->nTableMask;
p = ht->arBuckets[nIndex];
while (p!= NULL){
if ((p->h== h)&& (p->nKeyLength==nKeyLength)){
if (!memcmp(p->arKey, arKey,nKeyLength)){
if (フラグ&HASH_ADD){
失敗を返す;
}
HANDLE_BLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
if (p->pData == pData){
ZEND_PUTS("zend_hash_update の致命的なエラー: p->pData == pDatan");
HANDLE_UNBLOCK_INTERRUPTIONS();
失敗を返す;
}
#endif
if (ht->pDestructor){
ht->pDestructor(p->pData);
}
UPDATE_DATA(ht, p, pData,nDataSize);
if (pDest) {
*pDest = p->pData;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
成功を返す;
}
}
p = p->pNext;
}
p = (バケット*)pemalloc(sizeof(バケット)- 1 +nKeyLength, ht->persistent);
if (!p){
失敗を返す;
}
memcpy(p->arKey, arKey, nKeyLength);
p->nKeyLength = nKeyLength;
INIT_DATA(ht, p, pData, nDataSize);
p->h = h;
CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
if (pDest){
*pDest = p->pData;
}
HANDLE_BLOCK_INTERRUPTIONS();
CONNECT_TO_GLOBAL_DLLIST(p, ht);
ht->arBuckets[nIndex]= p;
HANDLE_UNBLOCK_INTERRUPTIONS();
ht->nNumOfElements++;
ZEND_HASH_IF_FULL_DO_RESIZE(ht);       /* ハッシュ テーブルがいっぱいの場合は、サイズを変更します */
成功を返す;
}
11.3.5 宏CONNECT_TO_BUCKET_DLLIST
#define CONNECT_TO_BUCKET_DLLIST(element, list_head)
(要素)->pNext= (list_head);                        
(要素)->pLast= NULL;                                
if((要素)->pNext) {
(要素)->pNext->pLast= (要素);                
}
この宏はバケットをバケット表に追加します
11.3.6 その他の関数数または者宏定义
HashTable について簡単に紹介します。HashTable を詳しく理解したい場合は、HashTable のコードは Zend/zend_hash.h と Zend/zend_hash.c にあります。
zend_hash_add_empty_element は関数に空の要素を追加します
zend_hash_del_key_or_index はインデックスに基づいて要素を削除します
zend_hash_reverse_apply 逆走査ハッシュテーブル
zend_hash_copy コピー
_zend_hash_merge マージ
zend_hash_find 文字列インデックス検索
zend_hash_index_find 数値インデックス方式検索
zend_hash_quick_find 上記 2 つの関数のカプセル化
zend_hash_exists インデックスが存在するかどうか
zend_hash_index_exists インデックスが存在するかどうか
zend_hash_quick_exists は上記の 2 つのメソッドをカプセル化します

www.bkjia.comtru​​ehttp://www.bkjia.com/PHPjc/477765.html技術記事 1 はじめに この記事は、PHP 言語と PHP 拡張機能の開発が好きなすべての同僚や学生に送る私の個人的な学習メモです。 2 下準備 ネタバレしたくない方はご退席ください...
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。