PHP構文ハック

PHPのコアを拡張する方法について考えたことはありますか? 新しいキーワードを作成したり、新しい構文を開発したりするには何が必要ですか? C言語の基本的な知識があれば、小さな変更の作成に関する問題は発生しないはずです。 はい、これは少し無意味かもしれませんが、それは問題ではありません-結局面白いです。



クラスを定義する別の方法を作成しましょう。 PHPで許可されているものを判断する最も簡単な方法は次のとおりです。



<?php class ClassName {}
      
      





構文を単純化し、中括弧をセミコロンに置き換えることができます。



 <?php class ClassName;
      
      





このコードを実行しようとすると、明らかにエラーがスローされます。 問題ではなく、修正できます。



最初のステップは、ソフトウェアをインストールすることです。



 $ sudo apt-get install bison re2c
      
      





PHPはCで記述されていますが、パーサーはBisonを使用して開発されています。 Bisonはパーサージェネレーターです。 公式サイトは、タグ付きコンテキストフリー文法をLALRパーサーテーブル(Look-Ahead LRパーサー-約Per。)を使用して決定論的LRまたは一般化LR(GLR)アナライザーに変換する汎用パーサージェネレーターとして定義しています。



これは非常に強力なソフトウェアであり、本全体を書くことができます。 詳細を知りたい場合は、 ドキュメントをよく理解することをお勧めします。 簡単ではありませんが、良い例が含まれています。 そして、プログラミング言語を作成したい場合、それは良い起動パッドになることができます。



http://php.netにアクセスして、最新のPHPソースをダウンロードしてください。



 $ tar xvjf php-5.4.14.tar.bz2 $ cd php-5.4.14 $ ./configure $ cd Zend $ ls
      
      





これはPHPのコアであるため、脱いでください。 これらのファイルのコードは、ほとんどのWebサーバーを制御します。 それを探検しましょう。



デフォルトでは、Bisonジェネレーターファイルには「y」拡張子が使用されます。



 $ ls *.y zend_ini_parser.y zend_language_parser.y
      
      





「ini」構文をいじりたくないので、「zend_language_parser.y」のみが残ります。 お気に入りのエディターで開きます。



ここで、「クラス」という単語を検索すると、次のことがわかります。



 %token T_CLASS "class (T_CLASS)"
      
      





パーサーはトークンの操作が大好きです。 クラストークンは「 T_CLASS 」です。 テキストで「 T_CLASS 」を探すと、次のようなものが見つかります。



 class_entry_type: T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = 0; } | T_ABSTRACT T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } | T_TRAIT { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_TRAIT; } | T_FINAL T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_FINAL_CLASS; } ;
      
      





以下に、クラスを定義する4つの異なる方法を示します。

  1. クラス
  2. 抽象クラス
  3. 特性
  4. final(leaf、final)クラス(finalクラス)


中括弧では、いくつかの低レベルの割り当てを確認できます。 なぜ必要なのか推測することしかできません。 それらに触れないようにしましょう。



私たちは正しい軌道に乗っていますが、それは私たちが探しているものとはまったく異なります。 これらの4つのクラス定義を組み合わせたフレーズ「class_entry_type」を探します。 彼女はあなたを目的地に導きます。 理解するのは簡単ですが、初めて読むのは難しいです。



 unticked_class_declaration_statement: class_entry_type T_STRING extends_from { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } implements_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); } | interface_entry T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } interface_extends_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, NULL TSRMLS_CC); } ;
      
      





ここには2つの広告があります。 1つはクラス用で、もう1つはインターフェース用です。 最初に興味があります。 「 class_entry_type 」で始まります。これにより、次の構成体が許可されます。 抽象クラス| 特性| 最終クラス。 次の要素はT_STRINGトークンです。 将来的には、クラス名が代わりになります。 「 extends_from 」はグループです。 この項目は、T_STRINGを拡張するように変換するか、空白のままにすることができます。



その後、パーサーはZendエンジンを呼び出してクラス宣言を開始します。



 { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); }
      
      





この関数は、 zend_compiler.cファイルにあります。



 void zend_do_begin_class_declaration(const znode *class_token, znode *class_name, const znode *parent_class_name TSRMLS_DC)
      
      





ここの最初の引数はクラス「 class_entry_type 」のトークン、2番目はクラス「 T_STRING 」の名前、最後は「 extends_from 」の親クラスです。



以下はimplements_listグループです。 なぜそれが必要なのか知っていると思います。 確かに、インターフェイスを定義します。 次の行は、クラスの必須の本体を形成します:開始中括弧 " { "、グループ " class_statement_list "および終了中括弧 " } "。 最後に、パーサーはクラス宣言が終了したことをZendエンジンに伝えます。



 { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); }
      
      





このコードを複製する必要がありますが、クラス本体はありません。



 unticked_class_declaration_statement: class_entry_type T_STRING extends_from { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } ';' { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); } | class_entry_type T_STRING extends_from { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } implements_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); } | interface_entry T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } interface_extends_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, NULL TSRMLS_CC); } ;
      
      





とても簡単でしたよね? あとは、変更をコンパイルするだけです。



 $ cd .. $ make
      
      





最初のコンパイルには常に時間がかかります。



 $ vim test.php
      
      





テストするコードを入力します。



 <?php class FooBar; $a = new FooBar; $a->bar = 10; print_r($a);
      
      





今それをテストします。



 $ sapi/cli/php test.php FooBar Object ( [bar] => 10 )
      
      





よかったね!



他のことをしましょう。 PHPでは、キーワード「 class 」を使用してクラスを宣言します。 短くしてみてはいかがですか? 「 cls 」はそうだと思います。



レクサーファイルを探しています。



 $ cd Zend/ $ ls *.l zend_ini_scanner.l zend_language_scanner.l
      
      





Bisonファイルはトークンで動作しました。 レクサーを使用すると、コードをトークンに変換する方法を決定できます。 zend_language_scanner.lを開き、「 class 」という単語を検索します。



 <ST_IN_SCRIPTING>"class" { return T_CLASS; }
      
      





このブロックを複製し、クラスをclsに変更します。



 <ST_IN_SCRIPTING>"cls" { return T_CLASS; } <ST_IN_SCRIPTING>"class" { return T_CLASS; }
      
      





ジョブが完了しました。 コードをコンパイルすると、「 class 」の代わりにキーワード「 cls 」を使用できます。



面白くないですか? 私と同じように楽しんでいただけたでしょうか。 興味を持って、探検してください。 本当に気に入った場合は、 https://bugs.php.net/でいくつかのエラーを修正することを検討してください



All Articles