PHP7の拡張機能を「hello、world」よりも難しくし、目立たないようにする方法。 パート1

なんで?



この記事を書いているのは、少なくとも1年かかった道を読者が数時間で歩けるようにするためです。 私の個人的な経験が示したように、Cでの単純なプログラミングは、PHPの機能を拡張するよりも簡単です。 ここでは、トライとしてよく知られているプレフィックスツリーを実装するlibtrieライブラリの例を使用して、拡張機能を作成する方法について可能な限り説明します。 新しくインストールしたLubuntu 18.04システムで、記述したアクションを記述し、同時に実行します。



始めましょう。



ソフトウェアのインストール



Php



  1. まず、php7.2-devパッケージを配置します。このパッケージには、拡張機能のビルドに必要なphpizeスクリプトが含まれています。 さらに、PHPの作業バージョンが必要になります。このバージョンで拡張機能を確認します。 このパッケージをインストールすると、多数の依存パッケージがプルアップされます。提供されるすべてのものを配置します。



    sudo apt install php7.2-dev
          
          





  2. php.net Webサイトにアクセスし、ダウンロードセクションに移動して、最新の安定バージョンのphpを含むアーカイブへのリンクを引き出します。現在はバージョン7.2.11です。

    PHPソースアーカイブをダウンロードします。



     cd /tmp && wget http://it2.php.net/get/php-7.2.11.tar.gz/from/this/mirror -O php7.tar.gz
          
          





  3. アーカイブを自分で解凍します。



     sudo tar -xvf php7.tar.gz -C /usr/local/src
          
          







コードエディター



私は通常2つのコードエディターを使用します。 JetBrainsのシンプルで高速なギーニーとかなりオタクですが、非常に高度なクリオン。 標準のカブUbuntuからGeanyをインストールします。



 sudo apt install geany
      
      





JetBrainsの公式WebサイトからClionをダウンロードします。



 cd ~/Downloads && wget https://download.jetbrains.com/cpp/CLion-2018.2.5.tar.gz -O clion.tar.gz
      
      





 sudo tar -xvf clion.tar.gz -C /usr/share
      
      





コンソールからclionを実行するのに便利なリンクを作成しましょう。



 sudo ln -s /usr/share/clion-2018.2.5/bin/clion.sh /usr/bin/clion
      
      





最初の起動後、clionはLXpanelシェルメニューからそれ自体のショートカットを作成しますが、最初に手動で起動する必要があります。



 # clion
      
      





拡張機能を作成



ここには少なくとも3つのオプションがあります。



  1. ダウンロードしたphpソースから生の標準ディスクを取得します。
  2. 特別なext_skelスクリプトを使用して、標準ディスクを少しファイルします。
  3. ここから良いミニマリストディスクを入手してください


3番目のオプションが最も好きですが、失敗した場合に2番目のオプションを使用して、間違える可能性のある場所の数を最小限に抑えます。 開発者の空白を選ぶことはまだ喜びですが:-)



  1. 標準のPHP拡張機能があるディレクトリに移動しましょう。



     cd /usr/local/src/php-7.2.11/ext
          
          





    スクリプトの名前に加えて、protoファイルを介していくつかの拡張パラメーターを指定できます。 このすべてを行うことはできません。 私はすべてを手作業で行いますが、プロトがどのように機能するかを示します。 トライしますので、拡張機能をlibtrieと名付けましょう。 / usr / local / srcディレクトリで作業するには、管理者権限が必要です。sudoを作成しないように、昇格された権限を持つbashを含めます。



     sudo bash
          
          





  2. ここでは、作成した拡張機能が実装する1つの関数のみのパラメーターを設定します。 これは、これがどのように行われるかを示すための単なるデモ関数です。



    標準機能の完全なアナログを行います



     array array_fill ( int $start_index , int $num , mixed $value )
          
          





    protoファイルの構文は非常に単純で、関数の名前を指定するだけです。 情報をもっと見やすくするために、もう少し書きます。



     echo my_array_fill \( int start_index , int num , mixed value \) >> libtrie.proto
          
          





  3. ext_skelスクリプトを実行して、拡張子の名前と作成したprotoファイルを指定します。



     ./ext_skel --extname=libtrie --proto=./libtrie.proto
          
          





  4. 拡張機能を使用してディレクトリを作成しました。 それに行きましょう。



     cd libtrie
          
          







ファイル構造とアセンブリの原則



ファイル構造
 config.m4 -           phpize   ./configure,       makefile. CREDITS -  ,     ,    libtrie.c -      php_libtrie.h -     config.w32 -        windows EXPERIMENTAL -  .    ,    . libtrie.php -  php      . tests -  
      
      







拡張機能を正常にビルドするには、3つのファイルのみが必要です。 上記の拡張機能のミニマリストディスクには、3つのファイルしかありません。



 config.m4 php_libtrie.h libtrie.c
      
      





私は、PHPで受け入れられている標準の命名法が好きではありません。ヘッダーファイルと、プログラムの本文に同じ名前が付けられているファイルが好きです。 したがって、名前を変更します

libtrie.c







php_libtrie.c







 mv libtrie.c php_libtrie.c
      
      





config.m4の編集



デフォルトのconfig.m4ファイルには文字通りコンテンツが詰め込まれていますが、その豊富さは混乱を招きます。 前述したように、このファイルはconfigureスクリプトを作成するために必要です。 詳細についてはこちらをご覧ください



 geany config.m4 &
      
      





私たちはこれだけを残します:



 PHP_ARG_ENABLE(libtrie, whether to enable libtrie support, [ --enable-libtrie Enable libtrie support]) if test "$PHP_LIBTRIE" != "no"; then #    -    # PHP_ADD_INCLUDE() #   PHP_NEW_EXTENSION(libtrie, php_libtrie.c, $ext_shared) # PHP_NEW_EXTENSION(libtrie, php_libtrie.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi
      
      









最初のマクロは、生成された構成スクリプトの実行時に拡張機能を有効または無効にする機能を作成します。



2番目のブロックは最も重要です。拡張子の一部としてコンパイルされるファイル、拡張子が.soファイルを介して動的に接続されるか、拡張子が静的でPHPビルド中に統合されるかを決定します。 私たちはダイナミックになります。

ファイルを保存します。



ルートモードで動作しないように、ファイルをユーザーディレクトリにコピーします。



 #    exit
      
      





コピー:



 cp /usr/local/src/php-7.2.11/ext/libtrie ~/Documents/ -r
      
      





デモ機能



array_fill()の完全なアナログを行うことを思い出させてください。 clionを使用して作業しますが、このガイドはどのエディターでも実行できます。 Clionは、基本的な構文チェックを自動的に行うことができ、Ctrlキーを押しながらクリックすることでファイルまたは関数をすばやく移動できるため、優れています。 このような移行が拡張機能で機能するには、CMakeLists.txtファイルを構成する必要があります



clionが適切に機能するためには、cmakeビルドシステムをインストールする必要があります。これにより、多数の依存パッケージがインストールされます。 次のコマンドでこれをすべてインストールします。



 sudo apt install cmake
      
      





cmakeを構成する



clionを拡張してカタログを開きます。 画面上部のルートディレクトリの名前をクリックして、コンテキストメニューから次の内容のCMakeLists.txtファイルを作成します。



 cmake_minimum_required(VERSION 3.12) project(php-ext-libtrie C) set(CMAKE_C_STANDARD 11) #   phproot,       php set(phproot /usr/local/src/php-7.2.11/) #   ,      #      clion       php include_directories(${phproot}) include_directories(${phproot}TSRM/) include_directories(${phproot}main/) include_directories(${phproot}Zend/) #    clion          add_executable(php-ext-libtrie php_libtrie.c)
      
      









たぶん、誰かがこのファイルを短くする方法を知っているので、clionはプロジェクトファイルのインデックス作成を開始します。 もっと短い方法は見つかりませんでした。 誰かが知っているなら、コメントを書いてください。



デモ機能コード



拡張子php_libtrie.c



本文でファイルを開き、

混乱しないようにすべてのコメントを削除してください。











Clionは、コードで使用されているすべての関数とマクロが宣言されているかどうかを確認し、宣言されていない場合はエラーをスローします。 明らかに、PHP開発者はclionを使用しません。そうでなければ、おそらくそれについて何かをするでしょう。 拡張機能でこれらのエラーが発生するのを防ぐには、不足しているヘッダーファイルを含めてください。



すべてを合理化するために、私はこれを行います:

すべてのincludeヘッダーをphp_libtrie.c



ファイルからphp_libtrie.h



に転送します。最初のファイルには1つのエントリのみが残ります。



 #include "php_libtrie.h"
      
      









php_libtrie.h



ファイルには、他のすべての必要なインクルードが含まれます。







ヘッダーファイルの内容
 #ifndef PHP_LIBTRIE_H #define PHP_LIBTRIE_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <stdarg.h> //   va_start() #include <inttypes.h> //    //  #if defined(__GNUC__) && __GNUC__ >= 4 # define ZEND_API __attribute__ ((visibility("default"))) # define ZEND_DLEXPORT __attribute__ ((visibility("default"))) #else # define ZEND_API # define ZEND_DLEXPORT #endif # define SIZEOF_SIZE_T 8 //   ZVAL_COPY_VALUE() #ifndef ZEND_DEBUG #define ZEND_DEBUG 0 #endif //  ,      #include "php.h" #include "php_ini.h" #include "zend.h" #include "zend_types.h" //ZVAL_COPY_VALUE #include "ext/standard/info.h" #include "zend_API.h" #include "zend_modules.h" #include "zend_string.h" #include "spprintf.h" extern zend_module_entry libtrie_module_entry; ...
      
      







すべてが正しく行われると、クリオンテスターの右上隅に黄色または緑色の正方形が表示されます。これは、重大なエラーがないことを意味します。







小さな理論的余談



拡張機能が正しく機能するには、2つのことが必要です。



  1. 以下を含む特別な構造zend_module_entryを初期化する必要があります。



     zend_module_entry libtrie_module_entry = { STANDARD_MODULE_HEADER, //  "libtrie", //  libtrie_functions, //     PHP_MINIT(libtrie), //,     PHP_MSHUTDOWN(libtrie), //   PHP_RINIT(libtrie), /* Replace with NULL if there's nothing to do at request start */ PHP_RSHUTDOWN(libtrie), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(libtrie), // ,    php  phpinfo() PHP_LIBTRIE_VERSION, // ,     STANDARD_MODULE_PROPERTIES //    };
          
          





  2. 拡張機能のすべての関数を含む同じ配列を初期化します。



    ここでは、特別なマクロラッパーPHP_FE()を通じて、拡張機能のすべての関数の名前が設定されます。 一般的に、PHPはマクロを非常に積極的に使用します。他のマクロを単純に参照するマクロがたくさんあり、それらはさらに進んでいます。 あなたはそれに慣れる必要があります。 Ctrlキーを押しながらクリックすることで、マクロを使用するかどうかを判断できます。 ここでは、クリオンは不可欠です。



    プロトタイプファイルを覚えていますか? そこに1つの関数my_array_fill()を設定します。 したがって、ここには3つの要素があります。



     const zend_function_entry libtrie_functions[] = { PHP_FE(confirm_libtrie_compiled, NULL) /* For testing, remove later. */ PHP_FE(my_array_fill, NULL) PHP_FE_END /* Must be the last line in libtrie_functions[] */ };
          
          





    最初の行は、拡張機能が機能することを示すテスト関数です。2行目は関数であり、3行目は特別なマクロです。これはこの配列の最後になります。



関数を見つけます:



 PHP_FUNCTION(my_array_fill)
      
      





ご覧のとおり、マクロを介して初期化されます。 問題は、すべてのphp関数がC内で何も返さない(正確にはvoidを返す)ため、引数を変更できないことです。 どこか便利です。



ファイル構造(左側のウィンドウの一部)を見ると、ファイルのすべての機能がここにリストされていますが、マクロのプリコンパイル後の形式に既になっています。 スクリーンショットは、関数my_array_fillが実際にzif_my_array_fillになることを示しています。







phpの腸からC関数への引数は、マクロで取得します。 このマクロの詳細は、ファイルに記載されています。



 /usr/local/src/php-7.2.11/README.PARAMETER_PARSING_API
      
      





以下に、アナログ関数のコードと詳細な説明を示します。



コード
 PHP_FUNCTION(my_array_fill) { //   ,     //    2 : // zend_execute_data *execute_data, zval *return_value //     ,      //zend_long  int64_t  x64   int32_t  x86  //      zend_long zend_long start_index; //1  , zend_long num; //2   zval *value; //   mixed ,   zval,      //    ,          if (zend_parse_parameters(ZEND_NUM_ARGS(), "llz", &start_index, &num, &value) == FAILURE) { /*     *    RETURN_    * return_value     */ RETURN_FALSE; } //   ,   -    if (num <= 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "argument 2 must be > 0"); //     RETURN_FALSE; } //zval *return_value  ,        //       zval,     ,  -  //   zend_long  unsigned int32. //      + -. ..   1,    3,     4  array_init_size(return_value, (uint32_t)(start_index + num)); //  ,   ,   for(zend_long i = start_index, last = start_index + num; i < last; ++i) { //   zval       add_index_zval(return_value, i, value); } //   ,      return_value return; }
      
      











拡張機能をビルドしてテストする



まず、phpizeを実行します。これにより、ファイルが構成されます。



 phpize
      
      





ここで./configureを実行すると、メイクファイルが実行されます。



 ./configure
      
      





最後にmakeを実行すると、拡張機能が収集されます。



 make
      
      





何をしたかを確認しましょう。



 #    php,       # modules.  -a  php      php -d extension=modules/libtrie.so -a
      
      





PHPコンソールに入力します。



 print_r(my_array_fill(50, 2, "hello, baby!"));
      
      





結果をお楽しみください。







誰かが尋ねます、ここのトライはどこですか? 第二部でトライを実装する関数について書きます。



お楽しみに。



All Articles