RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

PHPの標準関数を読んでみる ~PHPコアコードの読み方~

f:id:tech-rakus:20210409120611p:plain

はじめに

こんにちは。開発エンジニアのシカタです。
今回はPHPの標準関数の読み方について紹介します。
こんな経験をしたことはありませんか?

  • PHPのバージョンアップで挙動が変わった
  • 関数が想定外の値を返してきた
  • 想定外の挙動をしている…
  • リファレンスに書かれていないけど、この引数って使える…?

こういうときに、PHP本体のコードがどう実装されているのか読むことができればスッキリしそうですよね。
今回、PHP本体のソースコードを読む方法について、具体例を交えて紹介します。

PHPソースコードについて

PHPソースコード自体はC言語で実装されています。
C言語と聞くだけでハードルが高く感じるかもしれませんが、標準関数のコードを読んでみる程度であれば意外と雰囲気で読めます。
C言語の基本的な文法が分かる、雰囲気で上から下へソースコードが読むことができれば十分です。

標準関数のソースコードを読むコツ

  • キーワードは「PHP_FUNCTION(関数名)」
  • 雰囲気で理解する
  • ある程度納得したらやめておく、適度に追いかける
  • PHP本体のコア部分は追わない

以上を抑えておけば雰囲気で読むことができます。

PHP本体のソースコードを読んでみる

今回はsleep関数のコードを例に追ってみます。
sleep ( int $seconds ) : int
https://www.php.net/manual/ja/function.sleep.php
この関数の引数が0だったとき、どんな挙動になるのか気にしながら見ていきたいと思います。

  1. まずはソースコードを入手
    php-srcを下記から入手します
    https://github.com/php/php-src
    git環境があるならclone、ソースコードを読みたいだけなのでzipで入手しても良いと思います。
  2. 入手したソースコードを開く
    規模のあるソースコードなので、ソースコード全体を検索しやすいエディタ、IDEがオススメです。
    今回はVSCodeを使います。
    「File > Open Folder」でphp-srcを開きます。
    f:id:d_shr:20210407192546p:plain
  3. 関数の定義を探す
    PHPの標準関数は「PHP_FUNCTION(function_name)」で定義されています。
    PHPでは、独自の関数(マクロ)を作成することができますが、
    こちらでも関数は「PHP_FUNCTION(function_name)」で定義するものだと説明されています。
    関数の定義方法を詳しく知りたい場合には参考になります。
    http://php.adamharvey.name/manual/ja/internals2.funcs.php
    今回はsleep関数を追ってみるので、php-srcの中で「PHP_FUNCTION(sleep)」で検索をしてみます。
    下記のようにすぐ見つかります。
    f:id:d_shr:20210407192551p:plain
  4. 関数の中身を読んで見る
    上から見ていきます。
    まず、「ZEND_XXXX」が出てきますが、ここはPHPのコアなところです。
    PHP構文解析や実行エンジンを担っているコアなところは「Zend Engine」と言われています。
    PHPソースコード内で出てくる「zend」はコアなところに繋がると思って良いと思います。
    そのため、雰囲気で読むだけあればここを追う必要はありません。
    ちなみに、ここは「PARSE」や「PARAMETERS、PARAM」と名前が付いたものが呼ばれているぐらいなので 引数を解析しているぐらいの理解で大丈夫だと思います。
    その次は、引数が負の値だった場合は、エラー処理をしています。
    最後に、「php_sleep」というマクロが呼ばれてます。
    ここを追っていきます。
    f:id:d_shr:20210407192738p:plain
  5. 関数定義を追っていく
    php_sleep」の定義を探してみます。
    2つのマクロ定義が見つかりますが、よくよく見るとwindowsとそれ以外みたいな分岐があります。
    実行環境がwindowsUnix系で違うんだ…ぐらいの理解で大丈夫です。
    windowsの場合は、さらに「SleepEx」が呼ばれているので、さらに深追いしましょう。
    もう一方は、「sleep」なので、ここでC言語のsleepを叩いているようです。
    f:id:d_shr:20210407192834p:plain
  6. さらに深追い①(windowsの場合の挙動を追う)
    ここまでと同様に「SleepEx」の定義を探してみます。
    しかし、先ほどのマクロ定義のところでしか引っかかりません。
    PHP内の定義されていない場合は、ライブラリまたはC言語で定義されたものである可能性が高いです。
    あとは適当にググってみます。
    f:id:d_shr:20210407192900p:plain
    すると、SleepExはWin32APIで定義されている関数みたいです。
    https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleepex
    windowsの場合の挙動なので納得できます。
    0秒の場合の挙動についても書かれています。
    追うのはここまでにしておきます。
  7. さらに深追い(windows以外の場合の挙動を追う)
    C言語の「sleep」まではたどり着けましたが、そこから先は明記されていませんでした。
    https://www.ibm.com/support/knowledgecenter/ja/SSLTBW_2.2.0/com.ibm.zos.v2r2.bpxbd00/rtsle.htm
    ここから先はC言語の仕様に依存する部分なので、今日はこのくらいにしておきましょう。
    0秒を渡したときは、最終的にC言語のsleepがいい感じに0秒スリープしてくれる、はずです。

まとめ

PHPのコアコードの読み方を解説してみました。
身近な関数の実装であれば、意外と雰囲気だけで読むことはできます。
今回紹介した手順を参考にしていただければ、他の関数でも試してもらえますので、
一度、普段使っているPHPの標準関数のソースコードを読んでみてはいかがでしょうか。

Copyright © RAKUS Co., Ltd. All rights reserved.