PHPのオブジェクトの継承の負荷を見るの記事でオブジェクトの継承においてメモリの使用量で予想とは異なる結果になった。

予想を外した要因は関数とメソッドを含むクラスの読み込み周りの知識不足であるので、関数やメソッドの登録周りを知りたくなった。


例えば、下記のようなコードがあったとする。

<?php

function myFunc(){
	return null;
}

$a = myFunc();

最後の行でmyFunc関数を実行する際、予めメモリにmyFuncの番地を登録しておいて、関数呼び出し時にその番地を指定して実行する。


現時点での自分の知識レベルでは、関数登録時に関数名だけなのか?関数の中身の処理も登録しているのか?がわからないので、PHPのソースコードを見ていく必要があるのだけれども、その前にPHPの実行レベルでの関数の登録を見ていく。


PHPのビルトイン関数で定義済みの全ての関数を配列で返すというget_defined_functions関数というものがある。

PHP: get_defined_functions - Manual


この関数をmyFunc()の実行前に実行してみることにする。

func.php

<?php

function myFunc(){
	return null;
}

$funcs = get_defined_functions();
var_dump($funcs);

$a = myFunc();
$ php /path/to/dir/func.php
array(2) {
  'internal' =>
  array(1617) {
    [0] =>
    string(12) "zend_version"
    [1] =>
    string(13) "func_num_args"
    [2] =>
    string(12) "func_get_arg"
    [3] =>
    string(13) "func_get_args"
    [4] =>
    string(6) "strlen"
    [5] =>
    string(6) "strcmp"
    [6] =>
    string(7) "strncmp"
    [7] =>
    string(10) "strcasecmp"
    [8] =>
    string(11) "strncasecmp"
    [9] =>
    string(4) "each"
    [10] =>
    string(15) "error_reporting"
    [11] =>
    string(6) "define"
    [12] =>
    string(7) "defined"
    [13] =>
    string(9) "get_class"
    [14] =>
    string(16) "get_called_class"
    [15] =>
    string(16) "get_parent_class"
    [16] =>
    string(13) "method_exists"
    [17] =>
    string(15) "property_exists"
    [18] =>
    string(12) "class_exists"
    [19] =>
    string(16) "interface_exists"
    [20] =>
    string(12) "trait_exists"
    [21] =>
    string(15) "function_exists"
    [22] =>
    string(11) "class_alias"
    [23] =>
    string(18) "get_included_files"
    [24] =>
    string(18) "get_required_files"
    [25] =>
    string(14) "is_subclass_of"
    [26] =>
    string(4) "is_a"
    [27] =>
    string(14) "get_class_vars"
    [28] =>
    string(15) "get_object_vars"
    [29] =>
    string(23) "get_mangled_object_vars"
    [30] =>
    string(17) "get_class_methods"
    [31] =>
    string(13) "trigger_error"
    [32] =>
    string(10) "user_error"
    [33] =>
    string(17) "set_error_handler"
    [34] =>
    string(21) "restore_error_handler"
    [35] =>
    string(21) "set_exception_handler"
    [36] =>
    string(25) "restore_exception_handler"
    [37] =>
    string(20) "get_declared_classes"
    [38] =>
    string(19) "get_declared_traits"
    [39] =>
    string(23) "get_declared_interfaces"
    [40] =>
    string(21) "get_defined_functions"
    [41] =>
    string(16) "get_defined_vars"
    [42] =>
    string(15) "create_function"
    [43] =>
    string(17) "get_resource_type"
    [44] =>
    string(13) "get_resources"
    [45] =>
    string(21) "get_loaded_extensions"
    [46] =>
    string(16) "extension_loaded"
    [47] =>
    string(19) "get_extension_funcs"
    [48] =>
    string(21) "get_defined_constants"
    [49] =>
    string(15) "debug_backtrace"
    [50] =>
    string(21) "debug_print_backtrace"
    [51] =>
    string(13) "gc_mem_caches"
    [52] =>
    string(17) "gc_collect_cycles"
    [53] =>
    string(10) "gc_enabled"
    [54] =>
    string(9) "gc_enable"
    [55] =>
    string(10) "gc_disable"
    [56] =>
    string(9) "gc_status"
    [57] =>
    string(9) "strtotime"
    [58] =>
    string(4) "date"
    [59] =>
    string(5) "idate"
    [60] =>
    string(6) "gmdate"
    [61] =>
    string(6) "mktime"
    [62] =>
    string(8) "gmmktime"
    [63] =>
    string(9) "checkdate"
    [64] =>
    string(8) "strftime"
    [65] =>
    string(10) "gmstrftime"
    [66] =>
    string(4) "time"
    [67] =>
    string(9) "localtime"
    [68] =>
    string(7) "getdate"
    [69] =>
    string(11) "date_create"
    [70] =>
    string(21) "date_create_immutable"
    [71] =>
    string(23) "date_create_from_format"
    [72] =>
    string(33) "date_create_immutable_from_format"
    [73] =>
    string(10) "date_parse"
    [74] =>
    string(22) "date_parse_from_format"
    [75] =>
    string(20) "date_get_last_errors"
    [76] =>
    string(11) "date_format"
    [77] =>
    string(11) "date_modify"
    [78] =>
    string(8) "date_add"
    [79] =>
    string(8) "date_sub"
    [80] =>
    string(17) "date_timezone_get"
    [81] =>
    string(17) "date_timezone_set"
    [82] =>
    string(15) "date_offset_get"
    [83] =>
    string(9) "date_diff"
    [84] =>
    string(13) "date_time_set"
    [85] =>
    string(13) "date_date_set"
    [86] =>
    string(16) "date_isodate_set"
    [87] =>
    string(18) "date_timestamp_set"
    [88] =>
    string(18) "date_timestamp_get"
    [89] =>
    string(13) "timezone_open"
    [90] =>
    string(17) "timezone_name_get"
    [91] =>
    string(23) "timezone_name_from_abbr"
    [92] =>
    string(19) "timezone_offset_get"
    [93] =>
    string(24) "timezone_transitions_get"
    [94] =>
    string(21) "timezone_location_get"
    [95] =>
    string(25) "timezone_identifiers_list"
    [96] =>
    string(27) "timezone_abbreviations_list"
    [97] =>
    string(20) "timezone_version_get"
    [98] =>
    string(37) "date_interval_create_from_date_string"
    [99] =>
    string(20) "date_interval_format"
    [100] =>
    string(25) "date_default_timezone_set"
    [101] =>
    string(25) "date_default_timezone_get"
    [102] =>
    string(12) "date_sunrise"
    [103] =>
    string(11) "date_sunset"
    [104] =>
    string(13) "date_sun_info"
    [105] =>
    string(26) "libxml_set_streams_context"
    [106] =>
    string(26) "libxml_use_internal_errors"
    [107] =>
    string(21) "libxml_get_last_error"
    [108] =>
    string(19) "libxml_clear_errors"
    [109] =>
    string(17) "libxml_get_errors"
    [110] =>
    string(28) "libxml_disable_entity_loader"
    [111] =>
    string(33) "libxml_set_external_entity_loader"
    [112] =>
    string(26) "openssl_get_cert_locations"
    [113] =>
    string(16) "openssl_spki_new"
    [114] =>
    string(19) "openssl_spki_verify"
    [115] =>
    string(19) "openssl_spki_export"
    [116] =>
    string(29) "openssl_spki_export_challenge"
    [117] =>
    string(17) "openssl_pkey_free"
    [118] =>
    string(16) "openssl_pkey_new"
    [119] =>
    string(19) "openssl_pkey_export"
    [120] =>
    string(27) "openssl_pkey_export_to_file"
    [121] =>
    string(24) "openssl_pkey_get_private"
    [122] =>
    string(23) "openssl_pkey_get_public"
    [123] =>
    string(24) "openssl_pkey_get_details"
    [124] =>
    string(16) "openssl_free_key"
    [125] =>
    string(22) "openssl_get_privatekey"
    [126] =>
    string(21) "openssl_get_publickey"
    [127] =>
    string(17) "openssl_x509_read"

    (more elements)...
  }
  'user' =>
  array(1) {
    [0] =>
    string(6) "myfunc"
  }
}

たくさんの結果が返ってきた。

internalの方がビルトイン関数で、userの方がユーザー定義関数になる。

PHP: 内部(ビルトイン)関数 - Manual

PHP: ユーザー定義関数 - Manual


ビルトイン関数は読み込んだ拡張モジュールだけ出力される。

PHP: 拡張モジュールの一覧/分類 - Manual


話は戻って、ユーザー定義関数の方にmyFuncが登録(定義)されていた。

関数を実行する時はこの定義された一覧から探し出して実行することになるはずだ。




ここで一つ疑問が生じる。

<?php

$funcs = get_defined_functions();
var_dump($funcs);

$a = myFunc();

function myFunc(){
	return null;
}

上記のように関数の記述の前に定義された関数の確認と実行を行ったらどうなるのだろう?

実行してみると、

$ php /path/to/dir/func.php
array(2) {
  'internal' =>
  array(1617) {
    [0] =>
    string(12) "zend_version"
    [1] =>
    string(13) "func_num_args"
    [2] =>
    string(12) "func_get_arg"
    [3] =>
    string(13) "func_get_args"
    [4] =>
    string(6) "strlen"
    [5] =>
    string(6) "strcmp"
    [6] =>
    string(7) "strncmp"
    [7] =>
    string(10) "strcasecmp"
    [8] =>
    string(11) "strncasecmp"
    [9] =>
    string(4) "each"
    [10] =>
    string(15) "error_reporting"
    
    (more elements)...
  }
  'user' =>
  array(1) {
    [0] =>
    string(6) "myfunc"
  }
}

※internalの方の関数の11番目以降は省略


結果は同じだった。

おそらくPHPはコンパイラでいうマルチパス方式と採用しているのだろう。

※ファイルを一度最後まで読み切って関数等の登録を行い、再び最初からコードの実行をはじめる。

コンパイラ#ワンパスとマルチパス - Wikipedia


もう一つ疑問が生じたけれども、それは次回に記載しよう。