SlideShare a Scribd company logo
php-src
の歩き方
2018/06/15
PHP Conference Fukuoka
Reject Conference
do_aki
@do_aki
@do_aki
http://do-aki.net/
php-src
• PHP 本体のソースコードが置かれているリポジトリの名前
– 主成分は C言語
• Chromium のソースコードの歩き方
(https://nhiroki.jp/2017/12/01/chromium-sourcecode) を
見て、これの PHP版を書こうと思った
• というのを PHPの現場 (https://php-genba.shin1x1.com/17)
で話したので、PHPカンファレンス福岡に応募した
• 落選したのでリジェクトコンへ (今ここ)
対象
 php-src のコードを読んだことがない人
 PHPの処理系や拡張がどのように実装されている
か知りたい人
 PHP言語は扱えるけど、C言語はあまり知らない人
 php-src を読むきっかけになってくれれば
あじぇんだ
• ソースコードへのアクセス
• ファイル配置
• マクロ
• (時間が余ったら)テスト
ソースコードへのアクセス
• http://git.php.net/
– php 本体、仕様、各種 pecl
• https://github.com/php/
– github mirror
– https://github.com/php/php-src (本体)
– https://github.com/php/php-langspec (仕様)
• http://php.net/downloads.php
– tar ball
– コンパイルする場合はこちらのほうが便利
• https://php-lxr.adamharvey.name/source/
– 検索したいときにはこちらを使うと便利
PHP の 仕様 (php-langspec)
• 元は、Facebook の中の人が作った言語仕様
• 現在は本家もこれに準拠
• 言語仕様を変える場合はこちらも修正される
• https://github.com/php/php-langspec
– 文法や構文
– メモリモデル
– 名前空間
– クラスが満たすべき仕様 などなど
ファイル配置
ソースコード直下のディレクトリ構成
php-src/
+ TSRM/ ZTS版用ライブラリ (TS Resource Mgr)
+ Zend/ 主に Zend Engine
+ appveyor/ AppVeyor (Windows CI) 用ファイル
+ build/ PHP をビルドするために必要なスクリプト等
+ ext/ バンドルされた 各種 Extension
+ main/ SAPI, Extension 共通の処理
+ pear/ 現在は pear installer のみ
+ sapi/ 各種 SAPI モジュール
+ scripts/ phpt ジェネレータ, phpize
+ tests/ php 本体のテストコード (phpt)
+ travis/ Travis CI 用ファイル
+ win32/ Windows 環境互換用のコード
- files readme 等 (ソースコードはなし)
Zend ディレクトリ
php-src/
- Zend/
+ tests/ Zend Engine のテストケース
- LICENSE
- Makefile.am ビルドのためのファイルがいくつか(大文字始まり)
- Makefile.frag
- ...
- bench.php ベンチマークスクリプト。処理系のコードを変更した際
- micro_bench.php 性能劣化がないか調べるために利用されたり
- zend.c
- zend.h
- zend.ico
- zend_API.c
- zend_API.h
- zend_alloc.c
- zend_alloc.h
- ... 以下、ほぼすべてコード
Zend ディレクトリ内のコードの主な内訳
PHP Script Compiler
Lexer (zend_language_scanner.l/.c)
Parser (zend_language_parser.y/.c)
AST Generator (zend_ast.c)
Opcode Generator (zend_compile.c)
MBCS Support (zend_multibyte.c)
Memory Management
Memory Allocator (zend_alloc.c)
Cycles Collector (zend_gc.c)
Virtual Machine
Def (zend_vm_def.h,zend_vm_execute.skl)
Gen (zend_vm_gen.php)
zend_vm_execute.h/zend_execute.c/zend_exec
ute_API.c/zend_vm_opcodes.c/zend_opcode.c
Built-in objects
Function (zend_builtin_functions.c)
Class (zend_closures.c,zend_generators.c)
Interface (zend_interfaces.c)
Exception (zend_exceptions.c)
zend_default_classes.c
Data Structures & Algos
HashTable (zend_hash.c)
zend_string (zend_interfaces.c)
Algorithm (zend_sort.c,zend_strtod.c)
zend_list.c/zend_llist.c/zend_smart_str.c/zen
d_smart_string.c/zend_stack.c/zend_ts_hash.c/
zend_stream.c/zend_ptr_stack.c
(low level)
Type & Op
zend_types.h,zend_operators.
c,zend_variables.c,
zend_multiply.h, zend_long.h
Others
ini File Parser
(zend_ini.c,zend_ini_parser.y,zend_ini_scanner.l)
Signal Handling (zend_signal.c)
Extension Management (zend_extensions.c)
Constants (zend_constants.c)
FPU Control (zend_float.c)
DTrace Support (dtrace.c)
CPU Arch (zend_cpuinfo.c)
Highlight (zend_highlight.c)
OOP zend_inheritance.c,zend_objects.c,zend_object_
handlers.c,zend_objects_API.c,zend_iteratos.c
Core
zend.c /
zend_API.c
sapi/main ディレクトリ
php-src/
- main
- fastcgi.c FastCGI の実装(cgi/fpm)
- getopt.c コマンドライン引数の処理
- output.c output buffering
- php_variables.c GET/POSTパラメタ等の処理
- rfc1867.c file upload 処理
+ stream/ stream wrapper
- …
- sapi
+ apache2handler/ mod_php
+ cgi/ CGI版 PHP (FastCGI もサポート)
+ cli/ CLI版 PHP (cli-server 含む)
+ embed/ 組み込み用PHP NGINX Unit でも使われてる
+ fpm/ PHP-FPM の主にプロセス管理部分
+ litespeed/ LiteSpeed (LSAPI) PHP
+ phpdbg/ PHP 組み込みデバッガ
main := 各SAPI が
共通して利用できる
コード群
ext ディレクトリ
php-src/
- ext/
- date/
+ lib/ timelib ライブラリがバンドルされている
+ tests/ date 拡張の テスト
- CREDITS メンテナのクレジット
- config.w32 Windows ビルド用
- config0.m4 大抵は config.m4 これもビルド用
- php_date.c date拡張本体
- php_date.h date拡張定義
+ opcache/ 各種環境ごとの実装や最適化の実装
+ intl/ ICU本体を含まないものの構成要素が多いためファイル多い (C++)
+ json/ PHP独自のjson パーサ搭載
+ mbstring/ libmbfl と oniguruma を同梱した重量級拡張
+ mysqlnd/ libmysql 相当を独自に実装
+ reflection/ php の class,function がどのような実装になっているかを知るのに便利
+ skeleton/ このディレクトリだけ拡張ではない (拡張を作るためのひな形)
+ …
ext 直下に各拡張のディレクトリがあり、その下は拡張によって異なる。
多くは date ディレクトリのように tests ディレクトリ + buildのためのファイル + 拡張の実装 (+ ラ
イブラリをバンドル) という構成。
opcache,intl のように実装が複雑な場合はややファイルが多くなる
ext/sapi -> main -> Zend
(->TSRM/win32)
• PHP のコードは Zend / main / sapi / ext
に加え、TSRM,win32 ですべて
– TSRM は ZTS を実装するためのライブラリ
– win32 は Windows 環境で動かすための互換コード
• ext および sapi のコードは main あるいは
Zend に依存, main のコードは Zend に依存
– 環境によってこれらは TSRM/win32 にも依存
[TOPIC] 定義と実装
• C言語は 関数やデータ構造の定義 と 関数実装 を異なる
ファイルに分離するのが一般的
• php-src においては
– xxx.h(定義) xxx.c (実装) というファイル名
– 基本的にはすべて 定義と実装は同じディレクトリに配置されて
いる
– ex: Zend/zend_API.c Zend/zend_API.h
• 他の言語実装だと include ディレクトリ という定義のみを
まとめたディレクトリがあることが多い気がする
マクロ
C言語におけるマクロとは
• プログラムを書き換える事前処理
• C言語の文法を無視して 文字列置換 が行われる
– 駆使すると、見た目は全くC言語ではないコードにも
なりうる
• php-src で多用されてる
– 独自マクロが読解できれば大体読める
– 大文字のみで構成されたシンボルは大抵がマクロ
定数定義
• この使い方だけなら php の define と
ほぼ同じ
/* Zend/zend_vm_opcodes.h より抜粋 */
#define ZEND_NOP 0
#define ZEND_ADD 1
/* ext/opcache/Optimizer/nop_removal.c より抜粋 */
while (target->opcode == ZEND_NOP) {
target--;
}
if (target == opline) {
/* only NOPs */
opline->opcode = ZEND_NOP;
}
環境によって異なるコードを実行
• コード中どこにも #define ZEND_WIN32 は記述されていないが、
Windows 環境でコンパイルする際には指定される
– Windows の場合は OutputDebugString が、 それ以外では fprintf が
実行される。
– 通常の if と異なるのは、実行されないコードは存在自体がなくなるとい
う点。
• デバッグ時のみ実行するコードを記述する際にもよく利用される
– #if ZEND_DEBUG
/* Zend/zend_alloc.c より抜粋 */
#ifdef ZEND_WIN32
OutputDebugString(output_buf);
#else
fprintf(stderr, "%s", output_buf);
#endif
関数に別名を付ける
• _zend_hash_update 関数を zend_hash_update という名前で
コールできる
– php-src の作法として、前者を直接利用するのは NG
• デバッグ時に、呼び出し元のファイルや行を出力するための仕掛け
– 通常の if と異なり、非デバッグ時性能低下がゼロ
/* Zend/zend_hash.h より抜粋 */
#define zend_hash_update(ht, key, pData) 
_zend_hash_update(ht, key, pData ZEND_FILE_LINE_CC)
/* Zend/zend_portability.h */
#if ZEND_DEBUG
#define ZEND_FILE_LINE_C __FILE__, __LINE__
#define ZEND_FILE_LINE_CC , ZEND_FILE_LINE_C
#else
#define ZEND_FILE_LINE_C
#define ZEND_FILE_LINE_CC
#endif
• コードの流れを追うだけなら、呼んでいる関数の実体がどのような関数で
あるか ということさえわかっていれば十分
• 詳細を追いたいならば、どのように展開されるか まで知っておくと便利
/* ZEND_DEBUG が定義されている場合 */
_zend_hash_update(static_variables, var_name, var, __FILE__, __LINE__);
/* ZEND_DEBUG が定義されていない場合 */
_zend_hash_update(static_variables, var_name, var);
/* Zend/zend_closures.c より抜粋 */
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name,
zval *var)
{
zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
HashTable *static_variables = closure->func.op_array.static_variables;
zend_hash_update(static_variables, var_name, var);
}
グローバル変数へのアクセスを隠蔽
• 有名どころだと EG および CG マクロ
– executor_globals あるいは
compiler_globals というグローバル変数への
アクセスを抽象化
– 拡張ごとに hogeG というグローバル変数が用意
されていることが多い (ex: GMPG,ZCG)
– EG の利用例
• https://php-
lxr.adamharvey.name/source/xref/PHP-
7.2/Zend/zend_builtin_functions.c#1371
• EG(class_table) に 定義済みのクラス名が格納され
てる
その他 知っておくと便利なマクロ
• ZEND_FUNCTION(name) / PHP_FUNCTION(name)
– これ自身は、 PHP の関数として登録するための呼び出し規約や引数を準備してくれ
るもの
– name がそのまま PHP の関数名であることが多い
• ex: PHP_FUNCTION(is_array)
• ZEND_METHOD(class_name, name) / PHP_METHOD(class_name, name)
– クラスメソッド
– ex: PHP_METHOD(DateTime, __construct)
• ちなみに、 以下のように定義されてるのでそれぞれ同じです
–
/* main/php.h より抜粋 */
#define PHP_FUNCTION ZEND_FUNCTION
#define PHP_METHOD ZEND_METHOD
[TOPIC] 関数は拡張に属している
• PHP Script から呼び出し可能な組み込みの関数は、すべて
なにかしらの拡張に属している
– 拡張の一覧は get_loaded_extensions 関数で取得可能
– 指定した拡張に属する関数の一覧は get_extension_funcs 関
数で取得可能
– だいたい 拡張名 = ext直下のディレクトリ
• core 拡張だけは特殊で Zend 以下にある
– Zend/zend_builtin_functions.c
– strlen, define 等
テスト
独自のテストハーネス
• php-src 直下の run-tests.php で実行
– make test で実行されるやつ
• テスト実行時に ini 設定を上書きできる
• 各テストケースは 各モジュールのディレクトリ
内に設置された tests ディレクトリに設置
• PHPT フォーマット
(http://qa.php.net/write-test.php)
php-src/tests/basic/001.phpt
--TEST--
Trivial "Hello World" test
--FILE--
<?php echo "Hello World"?>
--EXPECT--
Hello World
テスト名称
テストコード
(PHP スクリプト)
期待値 (出力)
php-src/tests/basic/030.phpt
--TEST--
Bug#55504 (Content-Type header is not parsed correctly on HTTP POST request)
--INI--
file_uploads=1
--POST_RAW--
Content-Type: multipart/form-data; boundary=BVoyv; charset=iso-8859-1
--BVoyv
Content-Disposition: form-data; name="data"
abc
--BVoyv--
--FILE--
<?php
var_dump($_POST);
?>
--EXPECT--
array(1) {
["data"]=>
string(3) "abc"
}
https://bugs.php.net/bug.php?id=55504
boundary が Content-Type の途中に来た場合、
セミコロンも boundary の一部として解釈されて
いたために、適切に POSTデータを処理できていな
かったバグ に対するテスト (php-5.5 で修正)
以上

More Related Content

PPTX
PHP と SAPI と ZendEngine3 と
PDF
JIT のコードを読んでみた
PPTX
PHP AST 徹底解説
PDF
PHP7で変わること ——言語仕様とエンジンの改善ポイント
PDF
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
PDF
OPcache の最適化器の今
PDF
PHP 5.5ネーティブキャッシュの話
PDF
LLVM Backend の紹介
PHP と SAPI と ZendEngine3 と
JIT のコードを読んでみた
PHP AST 徹底解説
PHP7で変わること ——言語仕様とエンジンの改善ポイント
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
OPcache の最適化器の今
PHP 5.5ネーティブキャッシュの話
LLVM Backend の紹介

What's hot (20)

PPTX
最速C# 7.x
PDF
PHP7の内部実装から学ぶ性能改善テクニック
PDF
Phpをいじり倒す10の方法
PPTX
C#や.NET Frameworkがやっていること
PDF
.NET Core 3.0時代のメモリ管理
PDF
PHPの今とこれから2023
PDF
PHP の GC の話
PDF
PHP unserialization vulnerabilities: What are we missing?
PDF
C++ マルチスレッド 入門
PPTX
AVX-512(フォーマット)詳解
PPTX
How Functions Work
PDF
Quick tour of PHP from inside
PPTX
PDF
オブジェクト指向の設計と実装の学び方のコツ
PDF
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
PDF
Vmlinux: anatomy of bzimage and how x86 64 processor is booted
PDF
PHPの今とこれから2021
PDF
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
PDF
【Unite 2018 Tokyo】60fpsのその先へ!スマホの物量限界に挑んだSTG「アカとブルー」の開発設計
PDF
組み込みでこそC++を使う10の理由
最速C# 7.x
PHP7の内部実装から学ぶ性能改善テクニック
Phpをいじり倒す10の方法
C#や.NET Frameworkがやっていること
.NET Core 3.0時代のメモリ管理
PHPの今とこれから2023
PHP の GC の話
PHP unserialization vulnerabilities: What are we missing?
C++ マルチスレッド 入門
AVX-512(フォーマット)詳解
How Functions Work
Quick tour of PHP from inside
オブジェクト指向の設計と実装の学び方のコツ
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
Vmlinux: anatomy of bzimage and how x86 64 processor is booted
PHPの今とこれから2021
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
【Unite 2018 Tokyo】60fpsのその先へ!スマホの物量限界に挑んだSTG「アカとブルー」の開発設計
組み込みでこそC++を使う10の理由
Ad

Similar to php-src の歩き方 (20)

PDF
PHPの今とこれから 2013
PDF
PHP で実行中のスクリプトの動作を下から覗き見る
PPTX
Php in ruby
PPTX
PHPCON_TOKYO_2022_Bigginer.pptx
PDF
PHPの今とこれから2014
PPTX
詳説ぺちぺち
PDF
Modern PHP Programming @ PFI Seminar
PPTX
PHP基礎勉強会
PDF
PHP初心者セッション2023 〜ChatGPT時代の簡単な始め方〜
PDF
YAPC::Asia 2014 - 半端なPHPDisでPHPerに陰で笑われないためのPerl Monger向け最新PHP事情
PDF
スクリプト言語PHP攻略法
PPTX
PHP AST 徹底解説(補遺)
PDF
PhpStormを使おう --高槻からは快速急行が早くなります #jbugj
PDF
PHPの今とこれから2019
PPTX
Php development efficiency improvement
KEY
Behat+Symfony2ではじめるBDD超入門
PDF
PHP & Queue
KEY
WCO2012「PHP教室」
KEY
あらためてPHP5.3
PDF
PHPの今とこれから2024 at PHP Conference Japan 2024
PHPの今とこれから 2013
PHP で実行中のスクリプトの動作を下から覗き見る
Php in ruby
PHPCON_TOKYO_2022_Bigginer.pptx
PHPの今とこれから2014
詳説ぺちぺち
Modern PHP Programming @ PFI Seminar
PHP基礎勉強会
PHP初心者セッション2023 〜ChatGPT時代の簡単な始め方〜
YAPC::Asia 2014 - 半端なPHPDisでPHPerに陰で笑われないためのPerl Monger向け最新PHP事情
スクリプト言語PHP攻略法
PHP AST 徹底解説(補遺)
PhpStormを使おう --高槻からは快速急行が早くなります #jbugj
PHPの今とこれから2019
Php development efficiency improvement
Behat+Symfony2ではじめるBDD超入門
PHP & Queue
WCO2012「PHP教室」
あらためてPHP5.3
PHPの今とこれから2024 at PHP Conference Japan 2024
Ad

More from do_aki (20)

PPTX
Tritonn から Elasticsearch への移行話
PPTX
PHPとシグナル、その裏側
PPTX
再考:列挙型
PPTX
signal の話 或いは Zend Signals とは何か
PPTX
Writing php extensions in golang
PPTX
php7's ast
PPTX
N対1 レプリケーション + Optimizer Hint
PPTX
20150212 プレゼンテーションzen
PPTX
MySQL Casual Talks 7 「N:1 レプリケーション ~進捗どうですか?~」
PPTX
20141017 introduce razor
PPTX
20141011 mastering mysqlnd
PPTX
php in ruby
PPTX
PHP から Groonga を使うにはこんなコードになるよ!
PPTX
N:1 Replication meets MHA
PDF
Php radomize
PPTX
php and sapi and zendengine2 and...
PPTX
セキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editor
PPTX
マスタN対スレーブ1レプリケーションの作り方 ~あれから~
PPTX
Immortal
PPTX
Excel is image viewer
Tritonn から Elasticsearch への移行話
PHPとシグナル、その裏側
再考:列挙型
signal の話 或いは Zend Signals とは何か
Writing php extensions in golang
php7's ast
N対1 レプリケーション + Optimizer Hint
20150212 プレゼンテーションzen
MySQL Casual Talks 7 「N:1 レプリケーション ~進捗どうですか?~」
20141017 introduce razor
20141011 mastering mysqlnd
php in ruby
PHP から Groonga を使うにはこんなコードになるよ!
N:1 Replication meets MHA
Php radomize
php and sapi and zendengine2 and...
セキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editor
マスタN対スレーブ1レプリケーションの作り方 ~あれから~
Immortal
Excel is image viewer

php-src の歩き方

  • 3. php-src • PHP 本体のソースコードが置かれているリポジトリの名前 – 主成分は C言語 • Chromium のソースコードの歩き方 (https://nhiroki.jp/2017/12/01/chromium-sourcecode) を 見て、これの PHP版を書こうと思った • というのを PHPの現場 (https://php-genba.shin1x1.com/17) で話したので、PHPカンファレンス福岡に応募した • 落選したのでリジェクトコンへ (今ここ)
  • 4. 対象  php-src のコードを読んだことがない人  PHPの処理系や拡張がどのように実装されている か知りたい人  PHP言語は扱えるけど、C言語はあまり知らない人  php-src を読むきっかけになってくれれば
  • 7. • http://git.php.net/ – php 本体、仕様、各種 pecl • https://github.com/php/ – github mirror – https://github.com/php/php-src (本体) – https://github.com/php/php-langspec (仕様) • http://php.net/downloads.php – tar ball – コンパイルする場合はこちらのほうが便利 • https://php-lxr.adamharvey.name/source/ – 検索したいときにはこちらを使うと便利
  • 8. PHP の 仕様 (php-langspec) • 元は、Facebook の中の人が作った言語仕様 • 現在は本家もこれに準拠 • 言語仕様を変える場合はこちらも修正される • https://github.com/php/php-langspec – 文法や構文 – メモリモデル – 名前空間 – クラスが満たすべき仕様 などなど
  • 10. ソースコード直下のディレクトリ構成 php-src/ + TSRM/ ZTS版用ライブラリ (TS Resource Mgr) + Zend/ 主に Zend Engine + appveyor/ AppVeyor (Windows CI) 用ファイル + build/ PHP をビルドするために必要なスクリプト等 + ext/ バンドルされた 各種 Extension + main/ SAPI, Extension 共通の処理 + pear/ 現在は pear installer のみ + sapi/ 各種 SAPI モジュール + scripts/ phpt ジェネレータ, phpize + tests/ php 本体のテストコード (phpt) + travis/ Travis CI 用ファイル + win32/ Windows 環境互換用のコード - files readme 等 (ソースコードはなし)
  • 11. Zend ディレクトリ php-src/ - Zend/ + tests/ Zend Engine のテストケース - LICENSE - Makefile.am ビルドのためのファイルがいくつか(大文字始まり) - Makefile.frag - ... - bench.php ベンチマークスクリプト。処理系のコードを変更した際 - micro_bench.php 性能劣化がないか調べるために利用されたり - zend.c - zend.h - zend.ico - zend_API.c - zend_API.h - zend_alloc.c - zend_alloc.h - ... 以下、ほぼすべてコード
  • 12. Zend ディレクトリ内のコードの主な内訳 PHP Script Compiler Lexer (zend_language_scanner.l/.c) Parser (zend_language_parser.y/.c) AST Generator (zend_ast.c) Opcode Generator (zend_compile.c) MBCS Support (zend_multibyte.c) Memory Management Memory Allocator (zend_alloc.c) Cycles Collector (zend_gc.c) Virtual Machine Def (zend_vm_def.h,zend_vm_execute.skl) Gen (zend_vm_gen.php) zend_vm_execute.h/zend_execute.c/zend_exec ute_API.c/zend_vm_opcodes.c/zend_opcode.c Built-in objects Function (zend_builtin_functions.c) Class (zend_closures.c,zend_generators.c) Interface (zend_interfaces.c) Exception (zend_exceptions.c) zend_default_classes.c Data Structures & Algos HashTable (zend_hash.c) zend_string (zend_interfaces.c) Algorithm (zend_sort.c,zend_strtod.c) zend_list.c/zend_llist.c/zend_smart_str.c/zen d_smart_string.c/zend_stack.c/zend_ts_hash.c/ zend_stream.c/zend_ptr_stack.c (low level) Type & Op zend_types.h,zend_operators. c,zend_variables.c, zend_multiply.h, zend_long.h Others ini File Parser (zend_ini.c,zend_ini_parser.y,zend_ini_scanner.l) Signal Handling (zend_signal.c) Extension Management (zend_extensions.c) Constants (zend_constants.c) FPU Control (zend_float.c) DTrace Support (dtrace.c) CPU Arch (zend_cpuinfo.c) Highlight (zend_highlight.c) OOP zend_inheritance.c,zend_objects.c,zend_object_ handlers.c,zend_objects_API.c,zend_iteratos.c Core zend.c / zend_API.c
  • 13. sapi/main ディレクトリ php-src/ - main - fastcgi.c FastCGI の実装(cgi/fpm) - getopt.c コマンドライン引数の処理 - output.c output buffering - php_variables.c GET/POSTパラメタ等の処理 - rfc1867.c file upload 処理 + stream/ stream wrapper - … - sapi + apache2handler/ mod_php + cgi/ CGI版 PHP (FastCGI もサポート) + cli/ CLI版 PHP (cli-server 含む) + embed/ 組み込み用PHP NGINX Unit でも使われてる + fpm/ PHP-FPM の主にプロセス管理部分 + litespeed/ LiteSpeed (LSAPI) PHP + phpdbg/ PHP 組み込みデバッガ main := 各SAPI が 共通して利用できる コード群
  • 14. ext ディレクトリ php-src/ - ext/ - date/ + lib/ timelib ライブラリがバンドルされている + tests/ date 拡張の テスト - CREDITS メンテナのクレジット - config.w32 Windows ビルド用 - config0.m4 大抵は config.m4 これもビルド用 - php_date.c date拡張本体 - php_date.h date拡張定義 + opcache/ 各種環境ごとの実装や最適化の実装 + intl/ ICU本体を含まないものの構成要素が多いためファイル多い (C++) + json/ PHP独自のjson パーサ搭載 + mbstring/ libmbfl と oniguruma を同梱した重量級拡張 + mysqlnd/ libmysql 相当を独自に実装 + reflection/ php の class,function がどのような実装になっているかを知るのに便利 + skeleton/ このディレクトリだけ拡張ではない (拡張を作るためのひな形) + … ext 直下に各拡張のディレクトリがあり、その下は拡張によって異なる。 多くは date ディレクトリのように tests ディレクトリ + buildのためのファイル + 拡張の実装 (+ ラ イブラリをバンドル) という構成。 opcache,intl のように実装が複雑な場合はややファイルが多くなる
  • 15. ext/sapi -> main -> Zend (->TSRM/win32) • PHP のコードは Zend / main / sapi / ext に加え、TSRM,win32 ですべて – TSRM は ZTS を実装するためのライブラリ – win32 は Windows 環境で動かすための互換コード • ext および sapi のコードは main あるいは Zend に依存, main のコードは Zend に依存 – 環境によってこれらは TSRM/win32 にも依存
  • 16. [TOPIC] 定義と実装 • C言語は 関数やデータ構造の定義 と 関数実装 を異なる ファイルに分離するのが一般的 • php-src においては – xxx.h(定義) xxx.c (実装) というファイル名 – 基本的にはすべて 定義と実装は同じディレクトリに配置されて いる – ex: Zend/zend_API.c Zend/zend_API.h • 他の言語実装だと include ディレクトリ という定義のみを まとめたディレクトリがあることが多い気がする
  • 18. C言語におけるマクロとは • プログラムを書き換える事前処理 • C言語の文法を無視して 文字列置換 が行われる – 駆使すると、見た目は全くC言語ではないコードにも なりうる • php-src で多用されてる – 独自マクロが読解できれば大体読める – 大文字のみで構成されたシンボルは大抵がマクロ
  • 19. 定数定義 • この使い方だけなら php の define と ほぼ同じ /* Zend/zend_vm_opcodes.h より抜粋 */ #define ZEND_NOP 0 #define ZEND_ADD 1 /* ext/opcache/Optimizer/nop_removal.c より抜粋 */ while (target->opcode == ZEND_NOP) { target--; } if (target == opline) { /* only NOPs */ opline->opcode = ZEND_NOP; }
  • 20. 環境によって異なるコードを実行 • コード中どこにも #define ZEND_WIN32 は記述されていないが、 Windows 環境でコンパイルする際には指定される – Windows の場合は OutputDebugString が、 それ以外では fprintf が 実行される。 – 通常の if と異なるのは、実行されないコードは存在自体がなくなるとい う点。 • デバッグ時のみ実行するコードを記述する際にもよく利用される – #if ZEND_DEBUG /* Zend/zend_alloc.c より抜粋 */ #ifdef ZEND_WIN32 OutputDebugString(output_buf); #else fprintf(stderr, "%s", output_buf); #endif
  • 21. 関数に別名を付ける • _zend_hash_update 関数を zend_hash_update という名前で コールできる – php-src の作法として、前者を直接利用するのは NG • デバッグ時に、呼び出し元のファイルや行を出力するための仕掛け – 通常の if と異なり、非デバッグ時性能低下がゼロ /* Zend/zend_hash.h より抜粋 */ #define zend_hash_update(ht, key, pData) _zend_hash_update(ht, key, pData ZEND_FILE_LINE_CC) /* Zend/zend_portability.h */ #if ZEND_DEBUG #define ZEND_FILE_LINE_C __FILE__, __LINE__ #define ZEND_FILE_LINE_CC , ZEND_FILE_LINE_C #else #define ZEND_FILE_LINE_C #define ZEND_FILE_LINE_CC #endif
  • 22. • コードの流れを追うだけなら、呼んでいる関数の実体がどのような関数で あるか ということさえわかっていれば十分 • 詳細を追いたいならば、どのように展開されるか まで知っておくと便利 /* ZEND_DEBUG が定義されている場合 */ _zend_hash_update(static_variables, var_name, var, __FILE__, __LINE__); /* ZEND_DEBUG が定義されていない場合 */ _zend_hash_update(static_variables, var_name, var); /* Zend/zend_closures.c より抜粋 */ void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) { zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv); HashTable *static_variables = closure->func.op_array.static_variables; zend_hash_update(static_variables, var_name, var); }
  • 23. グローバル変数へのアクセスを隠蔽 • 有名どころだと EG および CG マクロ – executor_globals あるいは compiler_globals というグローバル変数への アクセスを抽象化 – 拡張ごとに hogeG というグローバル変数が用意 されていることが多い (ex: GMPG,ZCG) – EG の利用例 • https://php- lxr.adamharvey.name/source/xref/PHP- 7.2/Zend/zend_builtin_functions.c#1371 • EG(class_table) に 定義済みのクラス名が格納され てる
  • 24. その他 知っておくと便利なマクロ • ZEND_FUNCTION(name) / PHP_FUNCTION(name) – これ自身は、 PHP の関数として登録するための呼び出し規約や引数を準備してくれ るもの – name がそのまま PHP の関数名であることが多い • ex: PHP_FUNCTION(is_array) • ZEND_METHOD(class_name, name) / PHP_METHOD(class_name, name) – クラスメソッド – ex: PHP_METHOD(DateTime, __construct) • ちなみに、 以下のように定義されてるのでそれぞれ同じです – /* main/php.h より抜粋 */ #define PHP_FUNCTION ZEND_FUNCTION #define PHP_METHOD ZEND_METHOD
  • 25. [TOPIC] 関数は拡張に属している • PHP Script から呼び出し可能な組み込みの関数は、すべて なにかしらの拡張に属している – 拡張の一覧は get_loaded_extensions 関数で取得可能 – 指定した拡張に属する関数の一覧は get_extension_funcs 関 数で取得可能 – だいたい 拡張名 = ext直下のディレクトリ • core 拡張だけは特殊で Zend 以下にある – Zend/zend_builtin_functions.c – strlen, define 等
  • 27. 独自のテストハーネス • php-src 直下の run-tests.php で実行 – make test で実行されるやつ • テスト実行時に ini 設定を上書きできる • 各テストケースは 各モジュールのディレクトリ 内に設置された tests ディレクトリに設置 • PHPT フォーマット (http://qa.php.net/write-test.php)
  • 28. php-src/tests/basic/001.phpt --TEST-- Trivial "Hello World" test --FILE-- <?php echo "Hello World"?> --EXPECT-- Hello World テスト名称 テストコード (PHP スクリプト) 期待値 (出力)
  • 29. php-src/tests/basic/030.phpt --TEST-- Bug#55504 (Content-Type header is not parsed correctly on HTTP POST request) --INI-- file_uploads=1 --POST_RAW-- Content-Type: multipart/form-data; boundary=BVoyv; charset=iso-8859-1 --BVoyv Content-Disposition: form-data; name="data" abc --BVoyv-- --FILE-- <?php var_dump($_POST); ?> --EXPECT-- array(1) { ["data"]=> string(3) "abc" } https://bugs.php.net/bug.php?id=55504 boundary が Content-Type の途中に来た場合、 セミコロンも boundary の一部として解釈されて いたために、適切に POSTデータを処理できていな かったバグ に対するテスト (php-5.5 で修正)