php7模拟,认识PHP7虚拟机()三
原標題:認識PHP7虛擬機()三
動態函數調用
盡量不要使用動態的函數名去調用函數:
function foo() { }
foo();
number of ops: 4
compiled vars: none
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > NOP
3 1 INIT_FCALL 'foo'
2 DO_UCALL
3 > RETURN 1
NOP表示不做任何操作,只是將當前opline指向下一條OPCode,編譯器產生這條指令是由于歷史原因。為何到PHP7還不移除它呢= =
看看使用動態的函數名去調用函數:
function foo() { }
$a = 'foo';
$a();
number of ops: 5
compiled vars: !0 = $a
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > NOP
3 1 ASSIGN !0, 'foo'
4 2 INIT_DYNAMIC_CALL !0
3 DO_FCALL 0
4 > RETURN 1
不同點在于INIT_FCALL和INIT_DYNAMIC_CALL,看下兩個函數的源碼:
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *fname = EX_CONSTANT(opline->op2);
zval *func;
zend_function *fbc;
zend_execute_data *call;
fbc = CACHED_PTR(Z_CACHE_SLOT_P(fname)); /* 看下是否已經在緩存中了 */
if (UNEXPECTED(fbc == NULL)) {
func = zend_hash_find(EG(function_table), Z_STR_P(fname)); /* 根據函數名查找函數 */
if (UNEXPECTED(func == NULL)) {
SAVE_OPLINE();
zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(fname));
HANDLE_EXCEPTION();
}
fbc = Z_FUNC_P(func);
CACHE_PTR(Z_CACHE_SLOT_P(fname), fbc); /* 緩存查找結果 */
}
call = zend_vm_stack_push_call_frame_ex(
opline->op1.num, ZEND_CALL_NESTED_FUNCTION,
fbc, opline->extended_value, NULL, NULL);
call->prev_execute_data = EX(call);
EX(call) = call;
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
/* 200多行代碼,就不貼出來了,會根據CV的類型(字符串、對象、數組)做不同的函數查找 */
}
很顯然INIT_FCALL相比INIT_DYNAMIC_CALL要輕量許多。
類的延遲綁定
簡單地說,類A繼承類B,類B最好先于類A被定義。
class Bar { }
class Foo extends Bar { }
number of ops: 4
compiled vars: none
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > NOP
3 1 NOP
2 NOP
3 > RETURN
從生成的OPCode可以看出,上述PHP代碼在運行時,執行引擎不需要做任何操作。類的定義是比較耗性能的工作,例如解析類的繼承關系,將父類的方法/屬性添加進來,但編譯器已經做完了這些繁重的工作。
如果類A先于類B被定義:
class Foo extends Bar { }
class Bar { }
number of ops: 4
compiled vars: none
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > FETCH_CLASS 0 :0 'Bar'
1 DECLARE_INHERITED_CLASS '%00foo%2Fhome%2Froketyyang%2Ftest.php0x7fb192b7101f', 'foo'
3 2 NOP
3 > RETURN 1
這里定義了Foo繼承自Bar,但當編譯器讀取到Foo的定義時,編譯器并不知道任何關于Bar的情況,所以編譯器就生成相應的OPCode,使其定義延遲到執行時。在一些其他的動態類型的語言中,可能會產生錯誤:Parse error : class not found。
除了類的延遲綁定,像接口、traits都存在延遲綁定耗性能的問題。
對于定位PHP性能問題,通常都是先用xhprof或xdebug profile進行定位,需要通過查看OPCode定位性能問題的場景還是比較少的。
總結
希望通過這篇文章,能讓你了解到PHP虛擬機大致是如何工作的。具體opcode的執行,以及函數調用涉及到的上下文切換,有許多細節性的東西,限于本文篇幅,在另一篇文章:PHP 7 中函數調用的實現進行講解。返回搜狐,查看更多
責任編輯:
總結
以上是生活随笔為你收集整理的php7模拟,认识PHP7虚拟机()三的全部內容,希望文章能夠幫你解決所遇到的問題。