
ãã®èšäºã§ã¯ãPHP 7ã®ä»®æ³ãã·ã³ïŒZendä»®æ³ãã·ã³ïŒã§ã®æé©åã«ã€ããŠè©³ããèŠãŠãããŸãã æåã«ã颿°åŒã³åºãã®èžã¿å°ã®çè«ã«è§ŠããŠãããããããPHP 7ã§ã©ã®ããã«æ©èœããããåŠç¿ããŸãããã¹ãŠãå®å
šã«çè§£ãããå Žåã¯ãZendä»®æ³ãã·ã³ã«ã€ããŠããçè§£ããããšããå§ãããŸãã PHP 5ã§VM
ãã©ã®ããã«æ©èœãããã
èªãã«ã¯ãPHP 7 VMã«ã€ããŠèª¬æããŸããåèšèšãããŠããŸãããPHP 7ãšã»ãŒåãããã«åäœããŸãããããã£ãŠãPHP 5 VMãçè§£ããŠããå Žåã¯ã PHP VM 7ã¯åé¡ãããŸããã
ããã¯ãã¹ãŠååž°ã§ã
ã¹ããŒãžã£ã³ããèããããšããªãå Žåã¯ãããããHaskellãScalaãªã©ã®èšèªã«è§Šããããšããªãã§ãããã 颿°åŒã³åºãã©ã³ãã¯ãé«åºŠãªããã°ã©ãã³ã°ã³ãŒã¹ã§äžè¬çã«æããããããªãã¯ã§ãã èžã¿å°ã®åœ¹å²ã¯ã颿°åŒã³åºãã®ååž°ãé²ãããšã§ãã ããã¯çè«çæ ¹æ ã§ãã ååž°ãšã¯äœãããããªãå Žåã¯ããŸãèŠãŠãã ããã è€éãªããšã¯äœããããŸããã
ã¢ããªã±ãŒã·ã§ã³ã«ã¹ããªã³ã°ããŒãã¡ã«ããºã ãå®è£
ããã«ã¯å€ãã®æ¹æ³ããããŸãã ç°¡åãªäŸããå§ããŸãããïŒ
function factorial($n) { if ($n == 1) { return $n; } return $n * factorial($n-1); }
ååž°ãçè§£ããæãç°¡åãªæ¹æ³ã¯ãããç¥ãããŠããéä¹é¢æ°ã§ãã 圌女ã«ã¯ã»ãšãã©æç€ºããªããäœãããèªåèªèº«ãåŒã³åºããŸãã
ãåç¥ã®ããã«ããã¹ãŠã®é¢æ°åŒã³åºãã§å€ãã®ããšãèµ·ãããŸãã äžäœã¬ãã«ã§ã¯ãã³ã³ãã€ã©ãŒã¯
颿°åŒã³åºãèŠçŽã䜿çšããŠåŒã³åºãã®æºåãããŸãã å®éãã³ã³ãã€ã©ãŒã¯æåã«åŒæ°ãšæ»ãã¢ãã¬ã¹ãã¹ã¿ãã¯ã«æž¡ããæ¬¡ã«CALLãªãã³ãŒããçæããŸããããã«ãããããã»ããµãŒã颿°æ¬äœã®æåã®åœä»€ã«è»¢éãããŸãã å®äºãããšãRETURNãªãã³ãŒãã䜿çšããïŒé¢æ°æ¬äœã«ãªãå Žåã¯ã³ã³ãã€ã©ã«ãã£ãŠçæãããŸãïŒãããã»ããµã«åŒæ°ã¹ã¿ãã¯ãåãé€ãïŒã¹ã¿ãã¯ãã€ã³ã¿ããªã»ããïŒããªã¿ãŒã³ã¢ãã¬ã¹ã«æ»ãããã«æç€ºããŸãã
ãã®ã¢ãã«ã®åé¡ã¯ãã¹ã¿ãã¯ãã¡ã¢ãªã®äžéšã§ãããæéã§ãµã€ãºãå°ããããšã§ãã Linuxã§ã¯ãéåžž8 MBãã¹ã¿ãã¯ã«å²ãåœãŠãããŸãïŒ
ulimit -a ïŒã ååž°ã¬ãã«ã®åã¬ã³ãŒãã¯ã¹ã¿ãã¯äžã«æ°ãããã¬ãŒã ãäœæãããããååž°é¢æ°ã¯ã¹ã¿ãã¯ãéåžžã«ç©æ¥µçã«äœ¿çšããŸãã ããŸãã«ã倢äžã«ãªã£ãããã¹ã¿ãã¯å
šäœãåããããšãã§ããŸãã ãã®å Žåãã«ãŒãã«ã¯éåžžãSIGBUSã·ã°ãã«ãããã»ããµã«çºè¡ãã以åã«ã¯ã©ãã·ã¥ããªãå ŽåïŒããšãã°
alloca()
ã䜿çšããå Žå
alloca()
ã¹ã¿ãã¯ãçµäºããŸãã
ã¹ã¿ãã¯äžã®å Žæã¯ãã£ãã«çµäºããŸãããïŒããã°ã©ã ã®ãã°ãé€ãïŒãååž°é¢æ°ã«å ããŠãã¹ã¿ãã¯ãäœæããïŒé¢æ°ãæ»ã£ããšãã«ïŒåŒã³åºããã¬ãŒã ãç Žå£ãããšã害ãåãŒãå¯èœæ§ããããŸãã ããã«ãããããã»ããµãµã€ã¯ã«ã®äžéšããªããªãïŒ
mov
ã
pop
ã
push
call
ãªã©ã®ã¹ã¿ãã¯æåã®åœä»€ã®å ŽåïŒãåžžã«ã¡ã€ã³ã¡ã¢ãªãžã®ã¢ã¯ã»ã¹ãå¿
èŠã«ãªããŸãã ãããŠ-ããã¯é
ãã§ãã åãåŒã³åºããã«ããã°ã©ã ãåäžã®é¢æ°ã§åäœããå ŽåãããéãåäœããŸãïŒããã»ããµã¯ãã¹ã¿ãã¯ãç¡éã«äœæããã³åé€ããå¿
èŠããªããããã°ã©ã ãçŽæ¥äœ¿çšããªãã¡ã¢ãªã®ãããã¯ãç§»åããå¿
èŠã¯ãããŸããããããã¯åã«ã¢ãŒããã¯ãã£ã®äžéšã§ãã çŸåšãããã»ããµã¯éåžžãã¬ãžã¹ã¿ã䜿çšããŠã¹ã¿ãã¯åŒæ°ãä¿æããããã¢ãã¬ã¹ãè¿ãããããŸãïŒLinuxã®LP64ãªã©ïŒããæãæ·±ãã¬ãã«ã§ãã£ãŠãååž°ãåé¿ããŸãã
ååž°é²æ¢
颿°ãåŒã³åºããšãã«ååž°ãé²ãæ¹æ³ã¯ããã€ããããŸãã PHPã䜿çšããŠãç°¡åãªæ¹æ³ãæ¢ããŸãã springboard颿°ã䜿çšããããäŒçµ±çãªæ¹æ³ãç ç©¶ããæ¬¡ã«PHPãœãŒã¹ã³ãŒããäŸãšããŠäœ¿çšããŠãPHP 7ã®ã³ã¢ã«è¿œå ããããã®ã¡ã«ããºã ã®æäœãæ€èšããŸãã
ããŒã«ã³ãŒã«é¢æ°ãšã«ãŒã
ååž°ã¯äžçš®ã®ãµã€ã¯ã«ã§ãããXXXã§ã¯ãç§ã¯èªåèªèº«ã«ææŠããŸããã ãã®ãããååž°é¢æ°ã¯ãããèªäœãåŒã³åºããã«ã«ãŒãïŒå Žåã«ãã£ãŠã¯è€æ°ïŒã䜿çšããŠæžãæããããšãã§ããŸãã ãã ããããã¯ç°¡åãªã¿ã¹ã¯ã§ã¯ãªãã颿°èªäœã«äŸåããŠããããšãèŠããŠãããŠãã ãããããã¯äœåãã©ã®ããã«åŒã³åºããã§ãã
幞ããªããšã«ãéä¹é¢æ°ã¯ç°¡åã«ãè±ç·ãã§ããŸãã ãããè¡ãã«ã¯ãæ«å°ŸåŒã³åºã倿ãšåŒã°ããã¡ãœããã䜿çšããŸãã éä¹é¢æ°ãå±éããã«ã¯ããããååž°çãªæ«å°ŸåŒã³åºã颿°ã«å€æããç¹å®ã®ã«ãŒã«ãé©çšããŸãã æåã«å€æãè¡ããŸãããïŒ
function tail_factorial($n, $acc = 1) { if ($n == 1) { return $acc; } return tail_factorial($n-1, $n * $acc); }
ããã§ã¯ãããããããããªãŒã䜿çšããŸããã æåŸã«ãæ«å°ŸåŒã³åºã颿°ãååŸããå¿
èŠããããŸãã æãåºãããŠãã ãããããã¯é¢æ°ã®ååã§ãããããèªäœãè¿ãããšã«ãªããšãä»ã®æäœãå®è¡ããã«ãããè¡ããŸãã ã€ãŸããreturnåŒã¯ã远å ã®æäœãªãã§
ãååž°é¢æ°
ã®ã¿ã転éããŸãã ããšãã°ãåäžã®åœä»€ã¹ããªãŒã ã䜿çšããåå
¥åïŒåäžåœä»€åå
¥å¯èœïŒã ãããã£ãŠãã³ã³ãã€ã©ã¯æåŸã®åŒã³åºããæé©åããŸãã颿°ã¯ããèªäœãè¿ãã ããªã®ã§ãæ°ãããã¬ãŒã ãäœæããã®ã§ã¯ãªããã¹ã¿ãã¯ã®çŸåšã®ãã¬ãŒã ãåå©çšããããšã§ãã¹ã¿ãã¯ã®äœæãç°¡çŽ åãããŸãã ãŸããæ¬äœãåãªãã«ãŒãã§ãããã®æ«å°ŸåŒã³åºã颿°ã倿ããããšãã§ããŸãã 倿ŽãããåŒæ°ã§é¢æ°ãã³ãŒã«ããã¯ãã代ããã«ã颿°ã®å
é ã«å床ãžã£ã³ãããå¿
èŠããããŸãïŒååž°åŒã³åºãã®ããã«ïŒããåŒæ°ã倿ŽããŠã次ã®ã«ãŒããåŒæ°ã®æ£ããå€ã§å®è¡ãããããã«ããŸãïŒååž°é¢æ°ãè¡ãããã«ïŒã ååŸãããã®ïŒ
function unrolled_factorial($n) { $acc = 1; while ($n > 1) { $acc *= $n--; } return $acc; }
ãã®é¢æ°ã¯ãå
ã®
factorial()
ãšåãããšãè¡ããŸãããããèªäœãåŒã³åºããŸããã å®è¡æã«ãããã¯ååž°çãªä»£æ¿ææ®µãããã¯ããã«çç£çã§ãã
ãŸãã
goto
ãã©ã³ãã䜿çšããããšãã§ããŸãã
function goto_factorial($n) { $acc = 1; f: if ($n == 1) { return $acc; } $acc *= $n--; goto f; }
ãŸããååž°ã¯ãããŸããã
factorial()
ãèšå€§ãªæ°ã§å®è¡ããŠã¿ãŠãã ããïŒã¹ã¿ãã¯ã䜿ãæããããšã³ãžã³ã®ã¡ã¢ãªå¶éã«éããŸãïŒä»®æ³ãã·ã³ã®ã¹ã¿ãã¯ãã¬ãŒã ãããŒãã«é
眮ãããããïŒã å¶éïŒ
memory_limit ïŒãç¡å¹ã«ãããšãPHPãZendä»®æ³ãã·ã³ãç¡éååž°ã«å¯Ÿããä¿è·ãæããªããããPHPã¯ã¯ã©ãã·ã¥ããŸãã ãã®çµæãããã»ã¹ã¯åŽ©å£ããŸãã åãåŒæ°
unrolled_factorial()
ãŸãã¯
goto_factorial()
å®è¡ããŠã¿ãŠãã ããã ã·ã¹ãã ã¯èœã¡ãŸããã ããã«ã¯å®è¡ãããªããããããŸããããã¯ã©ãã·ã¥ããããšã¯ãªããã¹ã¿ãã¯äžã®å ŽæïŒPHPããŒãã«å²ãåœãŠãããŠããïŒã¯çµäºããŸããã ãã ããå®è¡é床ã¯ååž°é¢æ°ã®å Žåãããã¯ããã«é«éã«ãªããŸãã
ããŒã«ã³ãŒã«ã³ã³ãããŒã«ã¹ããªã³ã°ããŒãæ©èœ
颿°ã®ååž°ã¯ç°¡åã§ã¯ãªãããšããããŸãã éä¹ã¯åçŽã§ãããä»ã®ããã€ãã¯ãã£ãšè€éã§ãã ããšãã°ãããŸããŸãªå ŽæãããŸããŸãªæ¡ä»¶ãªã©ã§èªåèªèº«ãåŒã³åºã颿°ïŒ
bsearch()
åçŽãªå®è£
ãªã©ïŒã
ãã®ãããªå Žåãååž°ãæå¶ããããã«
èžã¿å°ãå¿
èŠã«ãªãå ŽåããããŸãã åºæ¬çãªååž°é¢æ°ãïŒååž°ã®ããã«ïŒæžãæããå¿
èŠããããŸãããä»åã¯ããèªäœãåŒã³åºãããšãã§ããŸãã ãããã®åŒã³åºãã¯ãçŽæ¥ã§ã¯ãªããã¹ããªã³ã°ããŒãã䜿çšããŠå®è¡ããããšã§åçŽã«ãã¹ã¯ããŸãã ãããã£ãŠãååž°ã¯å¶åŸ¡ãããŒïŒã¹ããªã³ã°ããŒãïŒã®ååšäžã§å±éããã颿°ã®ååŒã³åºããå¶åŸ¡ããŸãã è€éãªé¢æ°ãååž°ããæ¹æ³ã«ã€ããŠå°æããå¿
èŠã¯ãããããŸãããåã«ã©ããããŠãã¹ããªã³ã°ããŒããšåŒã°ããå¶åŸ¡ã³ãŒããä»ããŠå®è¡ããã ãã§ãã
ãã®æŠå¿µãPHPã§äœ¿çšããäŸãèŠãŠã¿ãŸãããã çºæ³ã¯ã颿°ã倿ããŠãåŒã³åºãå
ã®ã³ãŒããååž°ãéå§ããã¿ã€ãã³ã°ãšçµäºããã¿ã€ãã³ã°ã決å®ã§ããããã«ããããšã§ãã ãããååž°åŒã³åºãèªäœã«é©çšãããšãã¹ããªã³ã°ããŒããåŒã³åºãããã¹ã¿ãã¯ãå¶åŸ¡ããŸãã 圌ãçµæãè¿ããšãã¹ããªã³ã°ããŒãã¯ããã«æ°ã¥ãã忢ããŸãã
ãã®ããã«ïŒ
function trampo_factorial($n, $acc = 1) { if ($n == 1) { return $acc; } return function() use ($n, $acc) { return trampo_factorial($n-1, $n * $acc); }; }
ããã§ã¯ã颿°ã¯ãŸã èªåèªèº«ãåŒã³åºããŠããŸãã ãã ããããã¯çŽæ¥è¡ãããŸããããååž°åŒã³åºããã¯ããŒãžã£ãŒã«ã©ããããŸãã çµå±ãååž°é¢æ°ãçŽæ¥ã§ã¯ãªããèžã¿å°ãéããŠå®è¡ãããã®ã§ãã ã¯ããŒãžã£ãæ»ã£ãããšã確èªãããšã颿°ãéå§ããŸãã ã¯ããŒãžã£ã§ãªãå Žåã颿°ãè¿ããŸãã
function trampoline(callable $c, ...$args) { while (is_callable($c)) { $c = $c(...$args); } return $c; }
ã§ãã ãã®æ¹æ³ã䜿çšããŸãã
echo trampoline('trampo_factorial', 42);
èžã¿å°ã¯ãååž°åé¡ã®éåžžã®è§£æ±ºçã§ãã 颿°ããªãã¡ã¯ã¿ãªã³ã°ããŠååž°åŒã³åºããé€å€ã§ããªãå Žåã¯ãã¹ããªã³ã°ããŒããä»ããŠå®è¡ã§ããæ«å°ŸåŒã³åºã颿°ã«å€æããŸãã ãã¡ããããžã£ã³ãã¯æ«å°ŸåŒã³åºã颿°ã§ã®ã¿æ©èœããŸãã
ã¹ããªã³ã°ããŒãã䜿çšãããšãåŒã³åºããã颿°ã¯å¿
èŠãªåæ°ã ãèµ·åãããŸãããååž°çã«åŒã³åºãããšã¯ã§ããŸããã ã¹ããªã³ã°ããŒãã¯åŒã³åºããããã®ãšããŠæ©èœããŸãã ä»»æã®ååž°é¢æ°ã«é©çšã§ããã¯ããã«æ®éçãªæ¹æ³ã§ååž°åé¡ã解決ããŸããã
ããã§ã¯ãã¢ã€ãã¢ã®æ¬è³ªã説æããããã ãã«PHPã䜿çšããŸããïŒãããã®è¡ãèªãã ãšãã«PHPã«ããåºããããšæããŸãïŒã ãããããã®èšèªã§ãžã£ã³ããäœæããããšã¯ãå§ãããŸããã PHPã¯é«æ°Žæºèšèªã§ããããã®ãããªæ§é ã¯æ¥åžžã®äœæ¥ã§ã¯å¿
èŠãããŸããã å€ãã®å Žåãååž°é¢æ°ã¯å¿
èŠãããŸããããŸããå
éšã§
is_callable()
ãåŒã³åºãã«ãŒãã¯ããã»ã©è»œéã§ã¯ãããŸããã
ããã§ããPHPãšã³ãžã³ãããã«æ·±ãæãäžããŠãPHPä»®æ³ãã·ã³ã®ã¡ã€ã³ãã£ã¹ãããã«ãŒãã§ã®ã¹ã¿ãã¯ã®ååž°ãé²ãããã«ããã§ãžã£ã³ããã©ã®ããã«å®è£
ãããããèŠãŠã¿ãŸãããã
Zendä»®æ³ãã·ã³ã§ã®ååž°
ãã£ã¹ããããµã€ã¯ã«ãäœã§ããããå¿ããªãã§ãã ããã
ããªãã®èšæ¶ã§ããããªãã¬ãã·ã¥ãããŠãã ããã ãã¹ãŠã®ä»®æ³ãã·ã³ã¯ãããã€ãã®äžè¬çãªã¢ã€ãã¢ã«åºã¥ããŠæ§ç¯ãããŠããããã®äžã«ã¯ãã£ã¹ããããµã€ã¯ã«ããããŸãã ç¡éã«ãŒã
opline
ãåå埩ã§ãä»®æ³ãã·ã³ã®1ã€ã®
handler()
ïŒ
opline
ïŒãå®è¡ãããŸãïŒ
handler()
ïŒã ãã®åœä»€ã®ãã¬ãŒã ã¯ãŒã¯å
ã§ã¯å€ãã®ããšãèµ·ãããŸãããæåŸã«ã¯åžžã«ã«ãŒããžã®ã³ãã³ãããããŸããéåžžãããã¯æ¬¡ã®å埩ïŒgoto nextïŒã«é²ãã³ãã³ãã§ãã ç¡éã«ãŒãããã®æ»ãã³ãã³ããŸãã¯ãã®æäœãžã®ç§»è¡ã³ãã³ãããããŸãã
ããã©ã«ãã§ã¯ããšã³ãžã³ä»®æ³ãã·ã³ã®ãã£ã¹ããããµã€ã¯ã«ã¯
execute_ex()
颿°ã«ä¿åãããŸãã 以äžã¯ãç§ã®ã³ã³ãã¥ãŒã¿ãŒçšã«æé©åãããPHP 7ã®äŸã§ãïŒIPããã³FPã¬ãžã¹ã¿ãŒã䜿çšïŒïŒ
#define ZEND_VM_FP_GLOBAL_REG "%r14" #define ZEND_VM_IP_GLOBAL_REG "%r15" register zend_execute_data* volatile execute_data __asm__(ZEND_VM_FP_GLOBAL_REG); register const zend_op* volatile opline __asm__(ZEND_VM_IP_GLOBAL_REG); ZEND_API void execute_ex(zend_execute_data *ex) { const zend_op *orig_opline = opline; zend_execute_data *orig_execute_data = execute_data; execute_data = ex; opline = execute_data->opline; while (1) { opline->handler(); if (UNEXPECTED(!opline)) { execute_data = orig_execute_data; opline = orig_opline; return; } } zend_error_noreturn(E_CORE_ERROR, "Arrived at end of main loop which shouldn't happen"); }
while(1)
æ§é
while(1)
泚æããŠãã ããã ååž°ã¯ã©ãã§ããïŒ ã©ãããã®ïŒ
ãã¹ãŠãã·ã³ãã«ã§ãã
execute_ex()
颿°ã®äžéšãšããŠ
while(1)
ãéå§ããŸããã 1ã€ã®åœä»€ïŒ
opline->handler()
ïŒã
execute_ex()
ã©ããªããŸããïŒ ååž°ããããŸãã ããã¯æªãã§ãã ãã€ãã®ããã«ïŒã¯ããããããã«ãã¬ãã«ã«ãªãå Žåã
ãã®å Žåã
execute_ex()
ã¯
execute_ex()
åŒã³åºããŸããïŒ ããã§ã¯ãå€ãã®éèŠãªæ
å ±ãèŠèœãšãå¯èœæ§ããããããä»®æ³ãã·ã³ãšã³ãžã³ã«ã€ããŠè©³ãã説æããŸããã ç°¡åã«ããããã«ãããã¯
execute_ex()
ãåŒã³åºãPHP颿°ã®åŒã³åºãã§ãããšæ³å®ããŸãã
PHP颿°ãåŒã³åºããã³ã«ãCèšèªã¬ãã«ã§æ°ããã¹ã¿ãã¯ãã¬ãŒã ãäœæããããã£ã¹ããããµã€ã¯ã«ã®æ°ããããŒãžã§ã³ãéå§ãããå®è¡ããæ°ããåœä»€ã§
execute_ex()
æ°ããåŒã³åºããåå
¥åãããŸãã ãã®ã«ãŒãã衚瀺ããããšãPHP颿°åŒã³åºããå®äºããã³ãŒãå
ã§ãªã¿ãŒã³ããã·ãŒãžã£ãå®è¡ãããŸãã ãã®çµæãã¹ã¿ãã¯äžã®çŸåšã®ãã¬ãŒã ã®çŸåšã®ã«ãŒãã¯ãåã®ãã¬ãŒã ã®ãªã¿ãŒã³ã§çµäºããŸãã ããã¯ããŠãŒã¶ãŒç©ºéã®PHP颿°ã§ã®ã¿çºçããããšã«æ³šæããŠãã ããã ãã®çç±ã¯ããŠãŒã¶ãŒå®çŸ©ã®PHP颿°ã¯ãéå§åŸã«ã«ãŒãã§å®è¡ããããªãã³ãŒãã ããã§ãã ãã ããCã§éçºãããã«ãŒãã«ãŸãã¯æ¡åŒµæ©èœã«ããå
éšPHP颿°ã¯ããªãã³ãŒããå®è¡ããå¿
èŠã¯ãããŸããã ãããã¯çŽç²ãªCã®åœä»€ã§ãããããç°ãªããã£ã¹ããããµã€ã¯ã«ãšç°ãªããã¬ãŒã ã¯äœæãããŸããã
__CallïŒïŒäœ¿ç𿹿³
ããã§ã
__call()
äœ¿çšæ¹æ³ã説æããŸãã ããã¯ããŠãŒã¶ãŒç©ºéããã®PHP颿°ã§ãã ãã¹ãŠã®ãŠãŒã¶ãŒå®çŸ©é¢æ°ãšåæ§ã«ããã®å®è¡ã«ãã
execute_ex()
æ°ããåŒã³åºããè¡ãããŸãã ããããå®éã«ã¯
__call()
ãè€æ°ååŒã³åºããŠãå€ãã®ãã¬ãŒã ãäœæã§ããŸãã ã¯ã©ã¹ã§å®çŸ©ããã
__call()
ã䜿çšããŠããªããžã§ã¯ãã®ã³ã³ããã¹ãã§äžæãªã¡ãœãããåŒã³åºããããã³ã«ã
PHP 7ã§ã¯ã远å ã®
__call()
ãã¹ã¿ãªã³ã°åŒã³åºãã䜿çšããããšã«ããããŸã
__call()
ã®å Žåã«
execute_ex()
ååž°åŒã³åºããé²ãããšã«ããããšã³ãžã³ãæé©åãããŸããã
PHP 5.6
__call()
ïŒ

execute_ex()
ã¯3ã€ã®åŒã³åºãããããŸãã ããã¯ããªããžã§ã¯ãã®ã³ã³ããã¹ãã§äžæãªã¡ãœãããåŒã³åºãPHPã¹ã¯ãªããããååŸãããå¥ã®ãªããžã§ã¯ãã®ã³ã³ããã¹ãã§äžæãªã¡ãœãããåŒã³åºããŸãïŒã©ã¡ãã®å Žåããã¯ã©ã¹ã«ã¯
__call()
ãå«ãŸããŸãïŒã ãããã£ãŠãæåã®
execute_ex()
ã¯ã¡ã€ã³ã¹ã¯ãªããïŒåŒã³åºãã¹ã¿ãã¯ã®6
execute_ex()
ã®å®è¡ã§ããããªã¹ãã®äžçªäžã«ã¯ä»ã®2ã€ã®
execute_ex()
ãŸãã
PHP 7ã§åãã¹ã¯ãªãããå®è¡ããŸãã

éãã¯æããã§ããã¹ã¿ãã¯ãã¬ãŒã ã¯ã¯ããã«èãã
execute_ex()
åŒã³åºãã¯1ã€ã ãã§ããã€ãŸãã
execute_ex()
call
__call()
åŒã³åºããå«ããã¹ãŠã®åœä»€ãå¶åŸ¡ãã1ã€ã®ãã£ã¹ããããµã€ã¯ã«ã§ãã
__callïŒïŒåŒã³åºããã¹ããªã³ã°ããŒãåŒã³åºãã«å€æããŸã
PHP 5ã§ã¯ã
execute_ex()
ã®ã³ã³ããã¹ãã§
execute_ex()
ãåŒã³åºããŸããã ã€ãŸããçŸåšèŠæ±ãããŠãã
__call()
ãªãã³ãŒããå®è¡ããæ°ãããã£ã¹ããããµã€ã¯ã«ãæºåããŸããã
ããšãã°ã
fooBarDontExist()
ãªã©ã®ã¡ãœãããå®è¡ããŸãã ããã€ãã®æ§é äœãã¡ã¢ãªã«ä¿åãããŠãŒã¶ãŒç©ºéããå€å
žçãªé¢æ°åŒã³åºããå®è¡ããå¿
èŠããããŸãã ãã®ãããªãã®ïŒç°¡ç¥åïŒïŒ
ZEND_API void zend_std_call_user_call(INTERNAL_FUNCTION_PARAMETERS) { zend_internal_function *func = (zend_internal_function *)EG(current_execute_data)->function_state.function; zval *method_name_ptr, *method_args_ptr; zval *method_result_ptr = NULL; zend_class_entry *ce = Z_OBJCE_P(this_ptr); ALLOC_ZVAL(method_args_ptr); INIT_PZVAL(method_args_ptr); array_init_size(method_args_ptr, ZEND_NUM_ARGS()); ALLOC_ZVAL(method_name_ptr); INIT_PZVAL(method_name_ptr); ZVAL_STRING(method_name_ptr, func->function_name, 0); zend_call_method_with_2_params(&this_ptr, ce, &ce->__call, ZEND_CALL_FUNC_NAME, &method_result_ptr, method_name_ptr, method_args_ptr); if (method_result_ptr) { RETVAL_ZVAL_FAST(method_result_ptr); zval_ptr_dtor(&method_result_ptr); } zval_ptr_dtor(&method_args_ptr); zval_ptr_dtor(&method_name_ptr); efree(func); }
ãã®åŒã³åºããè¡ãã«ã¯ãå€ãã®äœæ¥ãå¿
èŠã§ãã ãã®ããããããã©ãŒãã³ã¹ãåäžãããããã«
__call()
ãé¿ããŠã¿ãŠãã ããããšããèšèãããè³ã«ããŸãïŒãã®ä»ã®çç±ããããŸãïŒã æ¬åœã«ããã§ãã
PHP 7ã«ã€ããŠãã¹ããªã³ã°ããŒãçè«ãèŠããŠããŸããïŒ ããã§ã¯ãã¹ãŠãã»ãŒåãã§ãã
execute_ex()
ååž°åŒã³åºããé¿ããå¿
èŠããããŸãã ãããè¡ãã«ã¯ãå¿
èŠãªåŒæ°ã倿ŽããŠã
execute_ex()
ãšåãã³ã³ããã¹ãã®ãŸãŸããã·ãŒãžã£ãå床ååž°ããå
é ã«ãªãã€ã¬ã¯ãïŒååå²ïŒããŸãã
execute_ex()
ããäžåºŠèŠãŠã¿ãŸãããïŒ
ZEND_API void execute_ex(zend_execute_data *ex) { const zend_op *orig_opline = opline; zend_execute_data *orig_execute_data = execute_data; execute_data = ex; opline = execute_data->opline; while (1) { opline->handler(); if (UNEXPECTED(!opline)) { execute_data = orig_execute_data; opline = orig_opline; return; } } zend_error_noreturn(E_CORE_ERROR, "Arrived at end of main loop which shouldn't happen"); }
ãããã£ãŠãååž°åŒã³åºããé²ãããã«ãå°ãªããšã
opline
ããã³
execute_data
倿°ã倿Žããå¿
èŠããããŸãïŒæ¬¡ã®opcodeãå«ã¿ãoplineã¯å®è¡ãããçŸåšã®ãopcodeã§ãïŒã
__call()
ã«äŒããšãïŒ
opline
ãšexecute_data
opline
ãŸãã- è¿éããŸãã
- çŸåšã®ãã£ã¹ããããµã€ã¯ã«ã«æ»ããŸãã
- æ°ãã倿Žãããæ°ãããªãã³ãŒãã«å¯ŸããŠãåŒãç¶ãå®è¡ããŸãã
- ãã®çµæãå
ã®äœçœ®ã«åŒ·å¶çã«æ»ããŸãïŒãããã£ãŠã
orig_opline
ãšorig_execute_data
ãŸããä»®æ³ãã·ã³ãããŒãžã£ãŒã¯ãã©ãããã§ãåå²ã§ããããã«ãåžžã«ãããã©ãããæ¥ãããèŠããŠããå¿
èŠããããŸãïŒã
ããã¯ãæ°ãããªãã³ãŒã
ZEND_CALL_TRAMPOLINE
ãPHP 7ã§è¡ãããšãšãŸã£ããåãã§ãã
__call()
åŒã³åºããè¡ãããã¹ãå Žæã§äœ¿çšãããŸãã ç°¡æçãèŠãŠã¿ãŸãããã
#define ZEND_VM_ENTER() execute_data = (executor_globals.current_execute_data); opline = ((execute_data)->opline); return static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { zend_array *args; zend_function *fbc = EX(func); zval *ret = EX(return_value); uint32_t call_info = EX_CALL_INFO() & (ZEND_CALL_NESTED | ZEND_CALL_TOP | ZEND_CALL_RELEASE_THIS); uint32_t num_args = EX_NUM_ARGS(); zend_execute_data *call; SAVE_OPLINE(); call = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { call->symbol_table = NULL; i_init_func_execute_data(call, &fbc->op_array, ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); if (EXPECTED(zend_execute_ex == execute_ex)) { ZEND_VM_ENTER(); }
倿°
execute_data
ããã³
opline
ããã¯ã
ZEND_VM_ENTER()
ã䜿çšããŠå¹æçã«å€æŽãããããšã«æ°ä»ãã§ãããã æ¬¡ã®
execute_data
call
倿°ã§æºåããããããã®ãã€ã³ãã¯
i_init_func_execute_data()
颿°ã«ãã£ãŠå®è¡ãããŸãã æ¬¡ã«ã
ZEND_VM_ENTER()
ã䜿çšããŠããã£ã¹ããããµã€ã¯ã«ã®æ°ããå埩ãå®è¡ããã倿°ã次ã®ãµã€ã¯ã«ã«åãæ¿ãããããçŸåšã®ãµã€ã¯ã«ã®ãããªã¿ãŒã³ãã§å
¥åããå¿
èŠããããŸãã
åã¯éããããçµãããŸããã
ã¡ã€ã³ã«ãŒãã«æ»ãæ¹æ³ ããã¯ããŠãŒã¶ãŒå®çŸ©é¢æ°ãçµäºããZEND_RETURN
ZEND_RETURN
ã§è¡ãããŸãã
#define LOAD_NEXT_OPLINE() opline = ((execute_data)->opline) + 1 #define ZEND_VM_LEAVE() return static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) { zend_execute_data *old_execute_data; uint32_t call_info = EX_CALL_INFO(); if (EXPECTED(ZEND_CALL_KIND_EX(call_info) == ZEND_CALL_NESTED_FUNCTION)) { zend_object *object; i_free_compiled_variables(execute_data); if (UNEXPECTED(EX(symbol_table) != NULL)) { zend_clean_and_cache_symbol_table(EX(symbol_table)); } zend_vm_stack_free_extra_args_ex(call_info, execute_data); old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); LOAD_NEXT_OPLINE(); ZEND_VM_LEAVE(); }
ã芧ã®ãšãããåŒã³åºããããŠãŒã¶ãŒå®çŸ©é¢æ°ãè¿ãå Žåã
ZEND_RETURN
ã䜿çšã
ZEND_RETURN
ãããã¯ããã¥ãŒå
ã®æ¬¡ã眮ãæããŠãåã®
prev_execute_data
åŒã³åºãã®åã®åœä»€ã§å®è¡ããŸãã æ¬¡ã«ãoplineãããŒãããã¡ã€ã³ãã£ã¹ããããµã€ã¯ã«ã«æ»ããŸãã
ãããã«
ã¢ã³ããŒã«é¢æ°åŒã³åºãã®èåŸã«ããçè«ã調ã¹ãŸããã ååž°åŒã³åºãã¯ä¿®æ£ã§ããŸãããéåžžã«é£ããå ŽåããããŸãã æ®éçãªè§£æ±ºçã¯ãã¹ããªã³ã°ããŒãã®éçºã§ããããã¯ãååž°é¢æ°ã®åã¹ããŒãžã®èµ·åãå¶åŸ¡ããã·ã¹ãã ã§ãããããèªäœãåŒã³åºãããšãèš±å¯ããããããã£ãŠãå¶åŸ¡äžèœãªã¹ã¿ãã¯ãã¬ãŒã ã®çæãé²ããŸãã ãã¹ããªã³ã°ããŒããã³ãŒãã¯ãã£ã¹ãããã£ã«ããããããå¶åŸ¡ããŠååž°ãé²ããŸãã
ãŸããPHPã§ã®äžè¬çãªå®è£
ã調ã¹ãPHP 7ã®äžéšã§ããæ°ããZend 3ãšã³ãžã³ã§ã®ã¹ããªã³ã°ããŒãã®å®è£
ã調ã¹ãŸãã
__call()
call
__call()
ãåŒã³åºãããšãæããªãCã¬ãã«ïŒãããã¯PHP 7ãšã³ãžã³ã®æ¹åç¹ã®1ã€ã§ãã