深入理解php内核


TIPI TIPI:PHP www.php-internal.com RELEASE_2011-07-29_V0.6.7 reeze http://reeze.cn er http://www.zhangabc.com phppan http://www.phppan.com TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHPPHP PHP PHP *nixWindows PHP PHPPHP PHP PHP PHPPHPPHPPHP PHP6 PHP *nix PHP (*nixUnixLinuxFreeBSD OpenSolaris Mac OS X ) 1.PHP PHPPHPPHP http://php.net/downloads.php svn/gitsvn/git #svn cd ~ svn co http://svn.php.net/repository/php/php-src/branches/PHP_5_2 php-src-5.2 #5.2 svn co http://svn.php.net/repository/php/php-src/branches/PHP_5_3 php-src-5.3 #5.3 #git cd ~ git clone git://github.com/php/php-src.git php-src PHP () PHPSVNgit svngit git (DVCS) PythonDVCS TIPI:PHP RELEASE_2011-07-29_V0.6.7 2. *nixUbuntuapt sudo apt-get install build-essential Mac OSXcodeXcodeMac OS X Apple ID http://developer.apple.com/ 3. ApachePHPMysql svn/git buildconf cd ~/php-src ./buildconf configureconfigure ./configure --help # PHP ./configure --disable-all configurePHP configureconfigure ./configure --disable-all make *nixconfigure makemake installmake make install installconfigureprefix PHP make sapi/cliphp . ./sapi/cli/php -n -v -nphp.ini-vphp make install$prefix/bin/phpphp prefix$PATHphp makemake install TIPI:PHP RELEASE_2011-07-29_V0.6.7 makemake install php-cliphp-src/sapi/cli/php configure make make && make install linuxsir.orgmake http://www.linuxsir.org/main/doc/gnumake/GNUmake_v3.80- zh_CN_html/index.html Autotools A Practioner's Guide PHP PHPPHP : / README /README.PHP4-TO-PHP5-THIN-CHANGES PHP4PHP5 /CODING_STANDARDSPHP build buildconf ext PHParraypdospl main PHPPHPZend Zend Zend Zendopcode pear “PHP ”PEAR sapi apachemod_phpcgifastcgifpm TSRM PHPTSRMPHP*GTSRM TSRM(Thread Safe Resource Manager) tests PHPPHP win32 WindowssokcetWindows*Nix WindowsPHP PHPPHPPHPphp/run-testsphp testsphpt php /tests/basic/001phpt TIPI:PHP RELEASE_2011-07-29_V0.6.7 --TEST-- Trivial "Hello World" test --FILE-- --EXPECT-- Hello World --FILE--PHP--EXPECT-- Fatal Error php system()exec() phpproc_open() PHP VIM + Ctags Linux*NixVIM Emacs Emacs Linuxctags ctags : VIMEMACS UNIX #PHP(/server/php-src): $ cd /server/php-src $ ctags -R #tags # ctags -R /server/ ctags #~/.vimrc: set tags+=/server/php-src/tags #vim: :set tags+=/server/php-src/tags /sever/php-srctags {tagname}{tagfile}{tagaddress} EG Zend/zend_globals_macros.h /^# define EG(/;" d EG VIMtagsCTRL+] VIMtags TIPI:PHP RELEASE_2011-07-29_V0.6.7 Ctrl+] Ctrl+t “” VIM IDE Mac OS XctagsMac OS Xctags ctagshomebrew IDE VIMIDEWindowsVisual Studio 2010 Express NetbeansEclipse EclipseNetbeansCTRL VS win32 Win32 Console Application F12 CTRL + F12 F3: Shift+F3: Ctrl+G: CTRL + - CTRL + SHIFT + - Editplus Editplus ... PHP PHP 1. "##""#" C/C++ PHP "##""#" TIPI:PHP RELEASE_2011-07-29_V0.6.7 (##) C"##" concatenator (Token) #define PHP_FUNCTION ZEND_FUNCTION #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) #define ZEND_FN(name) zif_##name #define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS) #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, \ zval *this_ptr, int return_value_used TSRMLS_DC PHP_FUNCTION(count); // PHP_FUCNTION(count); void zif_count(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC) ZEND_FN(name)"##"zifname (#) "#" (Token) : #define STR(x) #x int main(int argc char** argv) { printf("%s\n", STR(It's a long string)); // It's a long str return 0; } It's a long string STRprintf 2. do-while PHPPHP5.3 TIPI:PHP RELEASE_2011-07-29_V0.6.7 #define ALLOC_ZVAL(z) \ do { \ (z) = (zval*)emalloc(sizeof(zval_gc_info)); \ GC_ZVAL_INIT(z); \ } while (0) do{ }while(0) PHP C/C++ do-while? do-while do{ }while(0)false do-while? #define TEST(a, b) a++;b++; if (expr) TEST(a, b); else do_else(); if (expr) a++;b++; else do_else(); if-elseifdo-while {} ifTEST {} do-while(0) do-while""PHP sapi\thttpd\thttpd.c VEC_FREE(): #ifdef SERIALIZE_HEADERS # define VEC_FREE() smart_str_free(&vec_str) #else # define VEC_FREE() do {} while (0) #endif SERIALIZE_HEADERSVEC_FREE() VEC_FREE() “”Debug #ifdef DEBUG TIPI:PHP RELEASE_2011-07-29_V0.6.7 # define LOG_MSG printf #else # define LOG_MSG(...) #endif DEBUGLOG_MSGprintf LOG_MSG() 3. #line #line 838 "Zend/zend_language_scanner.c" #line__LINE____FILE__ 838Zend/zend_language_scanner.c C 4.PHP PHPPG() EG()PHP $PHP_SRC/main/php_globals.h PGPHP #ifdef ZTS // # define PG(v) TSRMG(core_globals_id, php_core_globals *, v) extern PHPAPI int core_globals_id; #else # define PG(v) (core_globals.v) // extern ZEND_API struct _php_core_globals core_globals; #endif ZTS PHP struct _php_core_globals { zend_bool magic_quotes_gpc; // GET/POST/Cookie zend_bool magic_quotes_runtime; // zend_bool magic_quotes_sybase; // Sybase zend_bool safe_mode; // zend_bool allow_call_time_pass_reference; // zend_bool implicit_flush; //PHP long output_buffering; //() TIPI:PHP RELEASE_2011-07-29_V0.6.7 char *safe_mode_include_dir; // UID/GID zend_bool safe_mode_gid; //UID zend_bool sql_safe_mode; zend_bool enable_dl; //dl()dl()PHPapache char *output_handler; // char *unserialize_callback_func; // unserialize() long serialize_precision; //( ) char *safe_mode_exec_dir; // long memory_limit; //(KM ) long max_input_time; // (POST, GET, upload) () zend_bool track_errors; //$php_errormsg zend_bool display_errors; // zend_bool display_startup_errors; //PHP zend_bool log_errors; // error_log long log_errors_max_len; // zend_bool ignore_repeated_errors; // zend_bool ignore_repeated_source; // zend_bool report_memleaks; // char *error_log; // char *doc_root; //PHP”” char *user_dir; //php /~username char *include_path; //require(), include(), fopen_with_path() char *open_basedir; // PHP() char *extension_dir; //()PHP char *upload_tmp_dir; // long upload_max_filesize; // char *error_append_string; // char *error_prepend_string; // char *auto_prepend_file; // char *auto_append_file; // arg_separators arg_separator; //PHPURL char *variables_order; // PHP Environment, GET, POST, Cookie, Server HashTable rfc1867_protected_variables; // RFC1867 main/rfc1867.c TIPI:PHP RELEASE_2011-07-29_V0.6.7 short connection_status; // short ignore_user_abort; // unsigned char header_is_being_sent; // zend_llist tick_functions; // mainphp_ticks.c register_tick_function zval *http_globals[6]; // GETPOSTSERVER zend_bool expose_php; // php zend_bool register_globals; // E, G, P, C, S zend_bool register_long_arrays; // (HTTP_*_VARS) zend_bool register_argc_argv; // $argv$argc(GET ) zend_bool auto_globals_jit; // $_SERVER$_ENV () zend_bool y2k_compliance; //2000(Y2K ) char *docref_root; // html_errorsPHP char *docref_ext; //(’.') zend_bool html_errors; //HTML zend_bool xmlrpc_errors; long xmlrpc_error_number; zend_bool activated_auto_globals[8]; zend_bool modules_activated; // zend_bool file_uploads; //HTTP zend_bool during_request_startup; // zend_bool allow_url_fopen; // zend_bool always_populate_raw_post_data; // $HTTP_RAW_POST_DATA(POST) zend_bool report_zend_debug; // zend debugmain/main.c int last_error_type; // char *last_error_message; // char *last_error_file; // int last_error_lineno; // char *disable_functions; // char *disable_classes; // zend_bool allow_url_include; //include/require zend_bool exit_on_timeout; // #ifdef PHP_WIN32 zend_bool com_initialized; #endif long max_input_nesting_level; // zend_bool in_user_include; // char *user_ini_filename; // ini long user_ini_cache_ttl; // ini char *request_order; // variables_orderrequest TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_bool mail_x_header; // ext/standard/mail.c char *mail_log; zend_bool in_error_log; }; php.ini PHPphp.ini ini_get()ini_set() PHPPG G PHP PHP PHPPHPPHP PHP PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHPWeb PHP PHPPHP PHP PHP Zend : SAPI SAPI(Server Application Programming Interface)PHP PC PCPC PHP Web ApacheNginxWebPHPPHP Web PHPWeb PHP urlPHP SAPISAPI Apachemod_php SAPIApache Apache SAPI SAPI PHP MINIT SAPI(Apache ) RINIT url RINIT PHPMINITMINIT PHP_MINIT_FUNCTION(myphpextension) TIPI:PHP RELEASE_2011-07-29_V0.6.7 { // return SUCCESS; } PHPPHP PHP RINIT RINITMINIT PHP_RINIT_FUNCTION(myphpextension) { // // return SUCCESS; } exit()die() PHP (RSHUWDOWN RINIT) SAPIWeb (MSHUTDOWNMINIT) PHP_RSHUTDOWN_FUNCTION(myphpextension) { // return SUCCESS; } SAPI CLI/CGIPHPSAPI - - - SAPI2.1 TIPI:PHP RELEASE_2011-07-29_V0.6.7 2.1 SAPI SAPI PHPapachePHPApache Apache fork fork Apache - 2.2 2.2 SAPI TIPI:PHP RELEASE_2011-07-29_V0.6.7 SAPI - 2.3 SAPI Zend ZendPHPPHP PHPPHP( facebookhiphopPHP) PHP Web(SAPI) PHPZendPHP Zend API ZendPHPPHPZendPHPPHP Zend API PHPZend PHP PHP JavaJVMIBMProject Zero JVMPHP .NET HipHop PHPPearRuby C PHP PHP HipHopPHP ZendAPI TIPI:PHP RELEASE_2011-07-29_V0.6.7 Extending and Embedding PHP SAPI PHPSAPI PHPSAPIPHP Apachemod_phpfastcgi SAPI SAPI _sapi_module_structSAPI PHP SAPI 2.4SAPI 2.4 SAPI cgiapache2 cgi_sapi_module.startup(&cgi_sapi_module) // cgi cgi/cgi_main.c apache2_sapi_module.startup(&apache2_sapi_module); // apache2 apache2handler/sapi_apache2.c cgi_sapi_modulesapi_module_struct startup php_cgi_startupstartup struct _sapi_module_struct { char *name; // char *pretty_name; // int (*startup)(struct _sapi_module_struct *sapi_module); // int (*shutdown)(struct _sapi_module_struct *sapi_module); // int (*activate)(TSRMLS_D); // int (*deactivate)(TSRMLS_D); // int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC); // (unbuffered write) void (*flush)(void *server_context); // flush TIPI:PHP RELEASE_2011-07-29_V0.6.7 struct stat *(*get_stat)(TSRMLS_D); // get uid char *(*getenv)(char *name, size_t name_len TSRMLS_DC); // getenv void (*sapi_error)(int type, const char *error_msg, ...); /* error handler */ int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC); /* header handler */ /* send headers handler */ int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC); void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC); /* send header handler */ int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC); /* read POST data */ char *(*read_cookies)(TSRMLS_D); /* read Cookies */ /* register server variables */ void (*register_server_variables)(zval *track_vars_array TSRMLS_DC); void (*log_message)(char *message); /* Log message */ time_t (*get_request_time)(TSRMLS_D); /* Request Time */ void (*terminate_process)(TSRMLS_D); /* Child Terminate */ char *php_ini_path_override; // ini ... ... }; Apache2 static sapi_module_struct apache2_sapi_module = { "apache2handler", "Apache 2.0 Handler", php_apache2_startup, /* startup */ php_module_shutdown_wrapper, /* shutdown */ ... } PHPIISWeb 2.5 TIPI:PHP RELEASE_2011-07-29_V0.6.7 2.5 SAPI PHPSAPIPHP SAPI PECL APC SAPI SAPI.cSAPI.h sapi_module PHP PHP zend_module_entry sapi_module_struct PHPzend_module_entry request_startup_func PHP_RINITrequest_startup_func VLDPHP_RINIT(vld), PHP_RINIT_FUNCTION(vld) { } SAPI Apache ApacheApacheWeb Web Apache TIPI:PHP RELEASE_2011-07-29_V0.6.7 mod_authmod_sslSSLTLS mod_rewriteURLApacheApache PerlPythonTclPHP PHPApachemod_php5 mod_php5ApachePHP ApacheApachePHPmod_php5 PHP apache2ap_hook_post_configApachePHP Apache HUPAP_SIG_GRACEFUL Apache mod_somod_so Apache Apache Apachemod_php5 Apache httpd.conf LoadModule php5_module modules/mod_php5.so LoadModule HUP AP_SIG_GRACEFULApache Apache Apache“mod_”PHPmod_php5.c ApacheApachemodule module nameSTANDARD20_MODULE_STUFF__FILE__ mod_php5 Apache ApacheApache modulemagic magicSTANDARD20_MODULE_STUFF magicMODULE_MAGIC_COOKIE MODULE_MAGIC_COOKIE #define MODULE_MAGIC_COOKIE 0x41503232UL /* "AP22" */ Apache(ap_add_loaded_module) (ap_top_modules ap_top_modulesApache ApachePHPApache2mod_php5 TIPI:PHP RELEASE_2011-07-29_V0.6.7 Apache2mod_php5 Apache2mod_php5sapi/apache2handlersapi/apache2filter apache2_handle/mod_php5.c AP_MODULE_DECLARE_DATA module php5_module = { STANDARD20_MODULE_STUFF, /* __FILE__ */ create_php_config, /* create per-directory config structure */ merge_php_config, /* merge per-directory config structures */ NULL, /* create per-server config structure */ NULL, /* merge per-server config structures */ php_dir_cmds, /* */ php_ap2_register_hook /* ap_hoo_ */ }; Apachemodulemodule typedef struct module_struct module; struct module_struct { int version; int minor_version; int module_index; const char *name; void *dynamic_load_handle; struct module_struct *next; unsigned long magic; void (*rewrite_args) (process_rec *process); void *(*create_dir_config) (apr_pool_t *p, char *dir); void *(*merge_dir_config) (apr_pool_t *p, void *base_conf, void *new_conf); void *(*create_server_config) (apr_pool_t *p, server_rec *s); void *(*merge_server_config) (apr_pool_t *p, void *base_conf, void *new_conf); const command_rec *cmds; void (*register_hooks) (apr_pool_t *p); } mod_php5.c STANDARD20_MODULE_STUFF 8 STANDARD20_MODULE_STUFF /** Use this in all standard modules */ #define STANDARD20_MODULE_STUFF MODULE_MAGIC_NUMBER_MAJOR, \ MODULE_MAGIC_NUMBER_MINOR, \ -1, \ __FILE__, \ NULL, \ NULL, \ MODULE_MAGIC_COOKIE, \ NULL /* rewrite args spot */ php5_modulephp_dir_cmds const command_rec php_dir_cmds[] = TIPI:PHP RELEASE_2011-07-29_V0.6.7 { AP_INIT_TAKE2("php_value", php_apache_value_handler, NULL, OR_OPTIONS, "PHP Value Modifier"), AP_INIT_TAKE2("php_flag", php_apache_flag_handler, NULL, OR_OPTIONS, "PHP Flag Modifier"), AP_INIT_TAKE2("php_admin_value", php_apache_admin_value_handler, NULL, ACCESS_CONF|RSRC_CONF, "PHP Value Modifier (Admin)"), AP_INIT_TAKE2("php_admin_flag", php_apache_admin_flag_handler, NULL, ACCESS_CONF|RSRC_CONF, "PHP Flag Modifier (Admin)"), AP_INIT_TAKE1("PHPINIDir", php_apache_phpini_set, NULL, RSRC_CONF, "Directory containing the php.ini file"), {NULL} }; mod_php5command_rec Apache mod_php5 php_value5 php_ap2_register_hook void php_ap2_register_hook(apr_pool_t *p) { ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE); } pre_configpost_confighandlerchild_init 4 pre_configpost_configchild_init handler post_configphp php_apache_server_startup php_apache_server_startupsapi_startupsapi php_apache2_startupsapi module struct php_module_startupPHP ZENDzend_module_struct treat_data(php_startup_sapi_content_types) Apachemod_php5SAPI mod_php5Apachesapi_module_struct: static sapi_module_struct apache2_sapi_module = { "apache2handler", "Apache 2.0 Handler", php_apache2_startup, /* startup */ php_module_shutdown_wrapper, /* shutdown */ NULL, /* activate */ NULL, /* deactivate */ php_apache_sapi_ub_write, /* unbuffered write */ php_apache_sapi_flush, /* flush */ php_apache_sapi_get_stat, /* get uid */ php_apache_sapi_getenv, /* getenv */ TIPI:PHP RELEASE_2011-07-29_V0.6.7 php_error, /* error handler */ php_apache_sapi_header_handler, /* header handler */ php_apache_sapi_send_headers, /* send headers handler */ NULL, /* send header handler */ php_apache_sapi_read_post, /* read POST data */ php_apache_sapi_read_cookies, /* read Cookies */ php_apache_sapi_register_variables, php_apache_sapi_log_message, /* Log message */ php_apache_sapi_get_request_time, /* Request Time */ NULL, /* Child Terminate */ STANDARD_SAPI_MODULE_PROPERTIES }; ApachecookieApachePHP Cookie SAPIread_cookies SG(request_info).cookie_data = sapi_module.read_cookies(TSRMLS_C); sapi_moduleApachesapi_module apache2_sapi_module read_cookiesphp_apache_sapi_read_cookies SAPI Apache Apache Apache root*nixAdministrator(Windows) (http.conf)( mod_phpmod_perl) Apache Apache Apache ApacheHTTP 11 Post-Read- RequestURI TranslationHeader ParsingAccess ControlAuthenticationAuthorization MIME Type CheckingFixUpResponseLoggingCleanUp Apache Hook Apache HookApache ( mod_php5.somod_perl.so) Apache (Hook)Apache mod_php5.so/ php5apache2.dllHookApacheApache php HookWindowsWindows TIPI:PHP RELEASE_2011-07-29_V0.6.7 apachehookapachephp5 Apache Apachehttpd.hhttpd.hApache API request_rec Apacherequest_recApache request_rec request_recHTTP Apache server_rec server_recWEB server_rec server_recApachehttpd server_rec request_rec conn_rec conn_recTCPApache The Apache Modules Book--Application Development with Apache PHPPHPPHPCLISAPI SAPI=> =>=>=> PHP PHP Lua Javascript PHP PHPWeb PHPPHPPHP #include #ifdef ZTS void ***tsrm_ls; #endif /* Extension bits */ zend_module_entry php_mymod_module_entry = { TIPI:PHP RELEASE_2011-07-29_V0.6.7 STANDARD_MODULE_HEADER, "mymod", /* extension name */ NULL, /* function entries */ NULL, /* MINIT */ NULL, /* MSHUTDOWN */ NULL, /* RINIT */ NULL, /* RSHUTDOWN */ NULL, /* MINFO */ "1.0", /* version */ STANDARD_MODULE_PROPERTIES }; /* Embedded bits */ static void startup_php(void) { int argc = 1; char *argv[2] = { "embed5", NULL }; php_embed_init(argc, argv PTSRMLS_CC); zend_startup_module(&php_mymod_module_entry); } static void execute_php(char *filename) { zend_first_try { char *include_script; spprintf(&include_script, 0, "include '%s'", filename); zend_eval_string(include_script, NULL, filename TSRMLS_CC); efree(include_script); } zend_end_try(); } int main(int argc, char *argv[]) { if (argc <= 1) { printf("Usage: embed4 scriptfile";); return -1; } startup_php(); execute_php(argv[1]); php_embed_shutdown(TSRMLS_CC); return 0; } Extending and Embedding PHP20 PHP PHP PHPPHP PHPPHP PHP #include sapiembedPHP php_embed_initphp_embed_shutdown 24 #ifdef ZTS void ***tsrm_ls; #endif ZTSZend Thread SafetyTSRM TIPI:PHP RELEASE_2011-07-29_V0.6.7 617 zend_module_entry php_mymod_module_entry = { STANDARD_MODULE_HEADER, "mymod", /* extension name */ NULL, /* function entries */ NULL, /* MINIT */ NULL, /* MSHUTDOWN */ NULL, /* RINIT */ NULL, /* RSHUTDOWN */ NULL, /* MINFO */ "1.0", /* version */ STANDARD_MODULE_PROPERTIES }; PHPNULL sapi/embed/php_embed.cphp_embed_shutdown zend/zend_modules.h startup_php: static void startup_php(void) { int argc = 1; char *argv[2] = { "embed5", NULL }; php_embed_init(argc, argv PTSRMLS_CC); zend_startup_module(&php_mymod_module_entry); } php_embed_initzend_startup_module php_embed_init sapi/embed/php_embed.cPHP zend_startup_modulePHPAPImymod zend_module_entry execute_php: static void execute_php(char *filename) { zend_first_try { char *include_script; spprintf(&include_script, 0, "include '%s'", filename); zend_eval_string(include_script, NULL, filename TSRMLS_CC); efree(include_script); } zend_end_try(); } PHP sprrintfinclude zend_eval_stringinclude zend_eval_string zend_eval_stringlPHP zend_op_array opcode phpphp PHPCPHPPHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 = 0) { SG(server_context) = (void *) &request; init_request_info(TSRMLS_C); CG(interactive) = 0; ... } fcgi_accept_requestaccept fcgi_accept_request safe_read [main() -> fcgi_accept_request() -> fcgi_read_request() -> safe_read()] static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count) { size_t n = 0; do { ... // win32 ret = read(req->fd, ((char*)buf)+n, count-n); // win ... // } while (n != count); } TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHP PHP_MODE_STANDARDphp_execute_scriptPHP zend_execute_scriptsPHP zend_execute main fcgi_finish_request(&request, 1); fcgi_finish_requestfastcgi.c int fcgi_finish_request(fcgi_request *req, int force_close) { int ret = 1; if (req->fd >= 0) { if (!req->closed) { ret = fcgi_flush(req, 1); req->closed = 1; } fcgi_close(req, force_close, 1); } return ret; } socket fcgi_flush safe_writesafe_writewinlinux Win32TCPsendTCPwinwrite #ifdef _WIN32 if (!req->tcp) { ret = write(req->fd, ((char*)buf)+n, count-n); } else { ret = send(req->fd, ((char*)buf)+n, count-n, 0); if (ret <= 0) { errno = WSAGetLastError(); } } #else ret = write(req->fd, ((char*)buf)+n, count-n); #endif CGI fcgi_close fcgi_closefcgi_finish_request winwin winwrite TCPPHPCGIPHP SAPIsapi_module_struct cgi_sapi_module /* {{{ sapi_module_struct cgi_sapi_module */ static sapi_module_struct cgi_sapi_module = { "cgi-fcgi", /* name */ TIPI:PHP RELEASE_2011-07-29_V0.6.7 "CGI/FastCGI", /* pretty name */ php_cgi_startup, /* startup */ php_module_shutdown_wrapper, /* shutdown */ sapi_cgi_activate, /* activate */ sapi_cgi_deactivate, /* deactivate */ sapi_cgibin_ub_write, /* unbuffered write */ sapi_cgibin_flush, /* flush */ NULL, /* get uid */ sapi_cgibin_getenv, /* getenv */ php_error, /* error handler */ NULL, /* header handler */ sapi_cgi_send_headers, /* send headers handler */ NULL, /* send header handler */ sapi_cgi_read_post, /* read POST data */ sapi_cgi_read_cookies, /* read Cookies */ sapi_cgi_register_variables, /* register server variables */ sapi_cgi_log_message, /* Log message */ NULL, /* Get request time */ NULL, /* Child terminate */ STANDARD_SAPI_MODULE_PROPERTIES }; /* }}} */ cookieCGIPHPCookie SAPIread_cookies SAPIcookie SAPI SG(request_info).cookie_data = sapi_module.read_cookies(TSRMLS_C); PHPSAPIApache sapi_module apache2_sapi_moduleread_cookiesphp_apache_sapi_read_cookies cookiesapi_cgi_read_cookies SAPI http://www.fastcgi.com/drupal/node/2 http://baike.baidu.com/view/641394.htm PHP PHPPHPSAPISAPIPHP Zend PHP : TIPI:PHP RELEASE_2011-07-29_V0.6.7 C/C++ .NET Java : :PHP Javascript Ruby Python " " PHPopcode(APC xcache eAccelerator)Python pyc/pyo PHP phpapachemod_php PHPSAPI PHPPHP PHP array ( 0 => 368, // 1 => ' 1, ), 1 => array ( 0 => 371, 1 => ' ', 2 => 2, ), 2 => '=', 3 => array ( 0 => 371, 1 => ' ', 2 => 2, ), 4 => array ( 0 => 315, 1 => '"Hello, Tipi "', 2 => 2, ), 5 => ';', 6 => array ( 0 => 371, 1 => ' ', 2 => 3, ), 7 => array ( 0 => 316, 1 => 'echo', 2 => 4, ), 8 => array ( 0 => 371, 1 => ' ', 2 => 4, ), 9 => ';', Zend(PHP bison $PHP_SRC/Zend/zend_language_parsery) bison Zend Zend TIPI:PHP RELEASE_2011-07-29_V0.6.7 opcode Zendopcode opcode - evalinclude/require echoZEND_ECHO C zend_print_variable(zval* z) opcode Opcodephpvld: http://pecl.php.net/package/vldWindll PHPopcode ( ) lex/yacc (compiler)(interpreter) 1. 2. LexYacc 1. (token) 2. (hierarchical structure) lex/yacc(flex/bison) PHPRubyPythonMySQLSQL LexYaccUnix Lex(:A Lexical Analyzer Generator) Yacc(Yet Another Compiler-Compiler) Lex/Flex LexFlexBison Flexlex TIPI:PHP RELEASE_2011-07-29_V0.6.7 BisonYacc .lflex%% %% %% %option noyywrap %{ int chars = 0; int words = 0; int lines = 0; %} %% [a-zA-Z]+ { words++; chars += strlen(yytext); } \n { chars++; lines++; } . { chars++; } %% main(int argc, char **argv) { if(argc > 1) { if(!(yyin = fopen(argv[1], "r"))) { perror(argv[1]); return (1); } yylex(); printf("%8d%8d%8d\n", lines, words, chars); } } PHPPHPflexPHPre2c $PHP_SRC/Zend/zend_language_scanner.l re2c re2c Yacc/Bison PHPLemon LemonSQLiteSQLite SQL Lemno BisonFlex%%Bison(token) Bison(BNF) TIPI:PHP RELEASE_2011-07-29_V0.6.7 phpechoecho PHP echo_expr_list: echo_expr_list ',' expr { zend_do_echo(&$3 TSRMLS_CC); } | expr { zend_do_echo(&$1 TSRMLS_CC); } ; echo_expr_list echozend_do_echo $3 expr zend_do_echoopcode CJavaGCCJava lex/yaccYacc Lex PHPopcode opcode opcode IO opcode: (byte codes) Java(JVM).NET (CIL: Common Intermeditate Language) PHPopcode PHPopcodePHPZend(Zend VM)PHP opcodeZend Zend PHPopcode struct _zend_op { opcode_handler_t handler; // opcode znode result; znode op1; znode op2; ulong extended_value; uint lineno; zend_uchar opcode; // opcode }; CPUopcodeopcodePHP extended_value result TIPI:PHP RELEASE_2011-07-29_V0.6.7 print: void zend_do_print(znode *resultconst znode *arg TSRMLS_DC) { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->result.op_type = IS_TMP_VAR; opline->result.u.var = get_temporary_variable(CG(active_op_array)); opline->opcode = ZEND_PRINT; opline->op1 = *arg; SET_UNUSED(opline->op2); *result = opline->result; } zend_op(IS_TMP_VAR) opcodeZEND_PRINTopcode opcode Zend echo: void zend_do_echo(const znode *arg TSRMLS_DC) { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_ECHO; opline->op1 = *arg; SET_UNUSED(opline->op2); } echoopcodeechoop1opcode result printechoprintecho null echo PHPopcodeop_array struct _zend_op_array { /* Common elements */ zend_uchar type; char *function_name; // zend_class_entry *scope; zend_uint fn_flags; union _zend_function *prototype; zend_uint num_args; zend_uint required_num_args; zend_arg_info *arg_info; zend_bool pass_rest_by_reference; unsigned char return_reference; /* END of common elements */ zend_bool done_pass_two; zend_uint *refcount; zend_op *opcodes; // opcode zend_uint lastsize; zend_compiled_variable *vars; TIPI:PHP RELEASE_2011-07-29_V0.6.7 int last_varsize_var; // ... } opcodesexecute ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) { // ... op_arrayopcodeop_arrayopcode } opcodeopcode_handler_topcode ? Zend PHPopcode:CALLSWITCHGOTOPHPCALL opcodePHP SWITCHGOTO GOTO CPU opcode opcodePHPopcode opcode PHP5.1PHPopcodeCALLSWITCH GOTO CALLZend Debug PHPopcodeopcode Zend/zend_vm_execute.hexecute ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) { ... zend_vm_enter: .... if ((ret = EX(opline)->handler(execute_data TSRMLS_CC)) > 0) { switch (ret) { case 1: EG(in_execution) = original_in_execution; return; case 2: op_array = EG(active_op_array); goto zend_vm_enter; case 3: execute_data = EG(current_execute_data); default: TIPI:PHP RELEASE_2011-07-29_V0.6.7 break; } ... } EX(opline)->handler *execute_data->opline->handler gdbexecutep (gdb) p *execute_data->opline->handler $1 = {int (zend_execute_data *)} 0x10041f394 debug gdb PHPopcodeopcode zend_vm_get_opcode_handler() static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op) { static const int zend_vm_decode[] = { _UNUSED_CODE, /* 0 */ _CONST_CODE, /* 1 = IS_CONST */ _TMP_CODE, /* 2 = IS_TMP_VAR */ _UNUSED_CODE, /* 3 */ _VAR_CODE, /* 4 = IS_VAR */ _UNUSED_CODE, /* 5 */ _UNUSED_CODE, /* 6 */ _UNUSED_CODE, /* 7 */ _UNUSED_CODE, /* 8 = IS_UNUSED */ _UNUSED_CODE, /* 9 */ _UNUSED_CODE, /* 10 */ _UNUSED_CODE, /* 11 */ _UNUSED_CODE, /* 12 */ _UNUSED_CODE, /* 13 */ _UNUSED_CODE, /* 14 */ _UNUSED_CODE, /* 15 */ _CV_CODE /* 16 = IS_CV */ }; return zend_opcode_handlers[ opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]]; } opcodephp opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type] zend_init_opcodes_handlers 3851 TIPI:PHP RELEASE_2011-07-29_V0.6.7 opcode opcodeopcodephp // DO_FCALL ==> ZEND_DO_FCALL_SPEC_CONST_HANDLER // ASSIGN => ZEND_ASSIGN_SPEC_VAR_CONST_HANDLER ZEND_ASSIGN_SPEC_VAR_TMP_HANDLER ZEND_ASSIGN_SPEC_VAR_VAR_HANDLER ZEND_ASSIGN_SPEC_VAR_CV_HANDLER // ASSIGN_SUB => ZEND_ASSIGN_SUB_SPEC_VAR_CONST_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_TMP_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_VAR_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_UNUSED_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_CV_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_CONST_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_TMP_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_VAR_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_UNUSED_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_CV_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_CONST_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_TMP_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_VAR_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_UNUSED_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_CV_HANDLER, ZEND_[opcode]_SPEC_(1)_(2)_HANDLER 12 VAR TMP CV UNUSED CONST zend_vm_get_opcode_handler static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op) { static const int zend_vm_decode[] = { _UNUSED_CODE, /* 0 */ _CONST_CODE, /* 1 = IS_CONST */ _TMP_CODE, /* 2 = IS_TMP_VAR */ _UNUSED_CODE, /* 3 */ _VAR_CODE, /* 4 = IS_VAR */ _UNUSED_CODE, /* 5 */ TIPI:PHP RELEASE_2011-07-29_V0.6.7 _UNUSED_CODE, /* 6 */ _UNUSED_CODE, /* 7 */ _UNUSED_CODE, /* 8 = IS_UNUSED */ _UNUSED_CODE, /* 9 */ _UNUSED_CODE, /* 10 */ _UNUSED_CODE, /* 11 */ _UNUSED_CODE, /* 12 */ _UNUSED_CODE, /* 13 */ _UNUSED_CODE, /* 14 */ _UNUSED_CODE, /* 15 */ _CV_CODE /* 16 = IS_CV */ }; //opcode/tmp/php.log int op_index; op_index = opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]; FILE *stream; if((stream = fopen("/tmp/php.log", "a+")) != NULL){ fprintf(stream, "opcode: %d , zend_opcode_handlers_index:%d\n", opcode, op_index); } fclose(stream); return zend_opcode_handlers[ opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]]; } /tmp/php.log: opcode: 38 , zend_opcode_handlers_index:970 opcode http://php.net/manual/en/internals2.opcodes.list.php static const opcode_handler_t labels[] Zend/zend_vm_execute.h 30077 php5.3.43851 970 PHPPHPSAPI PHPopcode PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 TIPI:PHP RELEASE_2011-07-29_V0.6.7 index keykey( key) keyindex hash slotkey key O(n) PHPHashTable key PHP 1. 2. 3. TIPI:PHP RELEASE_2011-07-29_V0.6.7 typedef struct _Bucket { char *key; void *value; struct _Bucket *next; } Bucket; typedef struct _HashTable { int size; Bucket* buckets; } HashTable; PHP key Bucketkey keyindex key(slotbucket) key static int hash_str(char *key) { int hash = 0; char *cur = key; while(*(cur++) != '\0') { hash += *cur; } return hash; } // key #define HASH_INDEX(ht, key) (hash_str((key)) % (ht)->size) PHP DJBX33A MysqlOpenSSL TIPI:PHP RELEASE_2011-07-29_V0.6.7 int hash_init(HashTable *ht); // int hash_lookup(HashTable *ht, char *key, void **result); // key int hash_insert(HashTable *ht, char *key, void *value); // int hash_remove(HashTable *ht, char *key); // key int hash_destroy(HashTable *ht); int hash_insert(HashTable *ht, char *key, void *value) { // check if we need to resize the hashtable resize_hash_table_if_needed(ht); // // int index = HASH_INDEX(ht, key); // key Bucket *org_bucket = ht->buckets[index]; Bucket *bucket = (Bucket *)malloc(sizeof(Bucket)); // bucket->key = strdup(key); // bucket->value = value; LOG_MSG("Insert data p: %p\n", value); ht->elem_num += 1; // if(org_bucket != NULL) { // LOG_MSG("Index collision found with org hashtable: %p\n", org_bucket); bucket->next = org_bucket; } ht->buckets[index]= bucket; LOG_MSG("Element inserted at index %i, now we have: %i elements\n", index, ht->elem_num); return SUCCESS; } key keykey int hash_lookup(HashTable *ht, char *key, void **result) { int index = HASH_INDEX(ht, key); Bucket *bucket = ht->buckets[index]; TIPI:PHP RELEASE_2011-07-29_V0.6.7 if(bucket == NULL) return FAILED; // // while(bucket) { if(strcmp(bucket->key, key) == 0) { LOG_MSG("HashTable found key in index: %i with key: %s value: %p\n", index, key, bucket->value); *result = bucket->value; return SUCCESS; } bucket = bucket->next; } LOG_MSG("HashTable lookup missed the key: %s\n", key); return FAILED; } PHP PHP Bucket PHP HashTablePHP HashTable$TIPI_ROOT/book/sample/chapt03/03-01-01- hashtable PHP PHP PHP PHP Zend PHP Zend PHPZend/zend_hash.cPHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHPHashTable Bucket typedef struct _hashtable { uint nTableSize; // hash Bucket82x uint nTableMask; // nTableSize-1 uint nNumOfElements; // hash Bucketcount() ulong nNextFreeElement; // Bucket *pInternalPointer; // foreachfor Bucket *pListHead; // Bucket *pListTail; // Bucket **arBuckets; // hash dtor_func_t pDestructor; zend_bool persistent; unsigned char nApplyCount; // hash Bucket zend_bool bApplyProtection;// hash 3 #if ZEND_DEBUG int inconsistent; #endif } HashTable; nTableSize8: ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC) { uint i = 3; //... if (nSize >= 0x80000000) { /* prevent overflow */ ht->nTableSize = 0x80000000; } else { while ((1U << i) < nSize) { i++; } ht->nTableSize = 1 << i; } // ... ht->nTableMask = ht->nTableSize - 1; /* Uses ecalloc() so that Bucket* == NULL */ if (persistent) { tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket *)); if (!tmp) { return FAILURE; } ht->arBuckets = tmp; } else { tmp = (Bucket **) ecalloc_rel(ht->nTableSize, sizeof(Bucket *)); if (tmp) { ht->arBuckets = tmp; } } return SUCCESS; } TIPI:PHP RELEASE_2011-07-29_V0.6.7 1016 2 HashTable 8100 : 100 % 8 = 40 3PHP h = zend_inline_hash_func(arKey, nKeyLength); nIndex = h & ht->nTableMask; _zend_hash_init()ht->nTableMaskht->nTableSize -1 & mask key 21 8mask7 10101 & 111 = 101 5 2-1N1 PHP HashTablenNumOfElementsunset count() nNextFreeElementPHP 'Hello'); $a[] = 'TIPI'; var_dump($a); // ouput array(2) { [10]=> string(5) "Hello" [11]=> string(5) "TIPI" } PHP C nNextFreeElement key key + 110key11 TIPI:PHP RELEASE_2011-07-29_V0.6.7 typedef struct bucket { ulong h; // char *keyhash uint nKeyLength; // hash0 void *pData; // value pDataPtr void *pDataPtr; //valuepData struct bucket *pListNext; // hash struct bucket *pListLast; // struct bucket *pNext; // hash Bucket struct bucket *pLast; // bucket char arKey[1]; /*1char *key */ } Bucket; hkeyPHP hnKeyLength key nKeyLength0PHP PHP'10''11'10 11 Zend BucketpNextpLast TIPI:PHP RELEASE_2011-07-29_V0.6.7 pListNextpListLast HashTable pListHeadpListTail PHParray_shift()array_pop() PHPnext()prev() pInternalPointer Bucket1Bucket2Bucket3 1. Bucket111 Bucket1 Bucket1pDatapDataPtrBucket1 pNext pLastpListNextpListLastHashTable HashTablepListHeadpListTail Bucket1 2. Bucket2Bucket2keyBucket1keyBucket2 Bucket2Bucket2.pNextBucket1Bucket2 Bucket1.pListNextBucket2Bucket2 HashTable.pListTailBucket2 3. Bucket3key1Bucket2.pListNextBucket3Bucket3 HashTable.pListTailBucket3 Bucket PHP zend_hash_init() HashTable RubystRubyhash TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHP PHP zval PHP typedef struct _zend_constant { zval value; /* zvalPHP */ int flags; /* CONST_PERSISTENT | CONST_CS */ char *name; /* */ uint name_len; int module_number; /* */ } zend_constant; Zend/zend_constants.h33 zval PHP define('TIPI', 'Thinking In PHP Internal'); PHPdefineTIPI zval define define definePHPZend/zend_builtin_functions.c /* {{{ proto bool define(string constant_name, mixed value, boolean case_insensitive=false) Define a new constant */ ZEND_FUNCTION(define) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &name, &name_len, &val, &non_cs) == FAILURE) { return; } ... // TIPI:PHP RELEASE_2011-07-29_V0.6.7 ... // c.value = *val; zval_copy_ctor(&c.value); if (val_free) { zval_ptr_dtor(&val_free); } c.flags = case_sensitive; /* non persistent */ c.name = zend_strndup(name, name_len); c.name_len = name_len+1; c.module_number = PHP_USER_CONSTANT; if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ zend_constant falsezend_constantflags zend_bool non_cs = 0; // int case_sensitive = CONST_CS; // 1 if(non_cs) { // case_sensitive = 0; } c.flags = case_sensitive; // definePHP define('^_^', 'smile'); if (defined('^_^')) { echo 'yes'; }else{ echo 'no'; } //$var = ^_^; // $var = constant("^_^"); defined‘^_^’ constant defined define definedZend/zend_builtin_functions.c zend_get_constant_ex TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_get_constant_ex zend_get_constantzend_get_constant zend_hash_find(EG(zend_constants), name, name_len+1, (void **) &c) name definePHP_USER_CONSTANT E_ALL cgi [php_cgi_startup() -> php_module_startup() -> zend_startup() -> zend_register_standard_constants()] void zend_register_standard_constants(TSRMLS_D) { ... // REGISTER_MAIN_LONG_CONSTANT REGISTER_MAIN_LONG_CONSTANT("E_ALL", E_ALL, CONST_PERSISTENT | CONST_CS); ... } REGISTER_MAIN_LONG_CONSTANTzend_register_long_constant zend_register_long_constantzend_constant zend_register_constant [php_cgi_startup() -> php_module_startup() -> zend_startup() -> zend_register_standard_constants() -> zend_register_constant] ZEND_API void zend_register_long_constant(const char *name, uint name_len, long lval, int flags, int module_number TSRMLS_DC) { zend_constant c; c.value.type = IS_LONG; c.value.value.lval = lval; c.flags = flags; c.name = zend_strndup(name, name_len-1); c.name_len = name_len; c.module_number = module_number; zend_register_constant(&c TSRMLS_CC); } zend_register_constantc->flags "\\" EG(zend_constants) EG(zend_constants)HashTable HashTable zend_hash_add(EG(zend_constants), name, c->name_len, (void *) c, TIPI:PHP RELEASE_2011-07-29_V0.6.7 sizeof(zend_constant), NULL)==FAILURE) php_module_startupzend_startup REGISTER_MAIN_LONG_CONSTANTPHP_VERSIONPHP_OS PHP()HashTable (symbol_table) PHP: $_GET$_POST$_SERVER PHP $GLOBALS cgi$GLOBALS cgi_main.cmain [main() -> php_request_startup() -> zend_activate() -> init_executor() ] ... // zend_hash_init(&EG(symbol_table), 50, NULL, ZVAL_PTR_DTOR, 0); { zval *globals; ALLOC_ZVAL(globals); Z_SET_REFCOUNT_P(globals, 1); Z_SET_ISREF_P(globals); Z_TYPE_P(globals) = IS_ARRAY; Z_ARRVAL_P(globals) = &EG(symbol_table); zend_hash_update(&EG(symbol_table), "GLOBALS", sizeof("GLOBALS"), &globals, sizeof(zval *), NULL); // GLOBALS } ... // zend_hash_updateGLOBALS EG(symbol_table) EG(symbol_table)HashTable $_GET $_GET$_POST $_GET$_COOKIE$_SERVER$_ENV$_FILES$_REQUEST [main() -> php_request_startup() -> php_hash_environment() ] php_hash_environment php_hash_environment$_POST TIPI:PHP RELEASE_2011-07-29_V0.6.7 /* {{{ php_hash_environment */ int php_hash_environment(TSRMLS_D) { char *p; unsigned char _gpc_flags[5] = {0, 0, 0, 0, 0}; zend_bool jit_initialization = (PG(auto_globals_jit) && !PG(register_globals) && !PG(register_long_arrays)); struct auto_global_record { char *name; uint name_len; char *long_name; uint long_name_len; zend_bool jit_initialization; } auto_global_records[] = { { "_POST", sizeof("_POST"), "HTTP_POST_VARS", sizeof("HTTP_POST_VARS"), 0 }, { "_GET", sizeof("_GET"), "HTTP_GET_VARS", sizeof("HTTP_GET_VARS"), 0 }, { "_COOKIE", sizeof("_COOKIE"), "HTTP_COOKIE_VARS", sizeof("HTTP_COOKIE_VARS"), 0 }, { "_SERVER", sizeof("_SERVER"), "HTTP_SERVER_VARS", sizeof("HTTP_SERVER_VARS"), 1 }, { "_ENV", sizeof("_ENV"), "HTTP_ENV_VARS", sizeof("HTTP_ENV_VARS"), 1 }, { "_FILES", sizeof("_FILES"), "HTTP_POST_FILES", sizeof("HTTP_POST_FILES"), 0 }, }; size_t num_track_vars = sizeof(auto_global_records)/sizeof(struct auto_global_record); size_t i; /* jit_initialization = 0; */ for (i=0; i"static" { return T_STATIC; } 2. tokentokenZend/zend_language_parser.y | T_STATIC static_var_list ';' static_var_list: static_var_list ',' T_VARIABLE { zend_do_fetch_static_variable(&$3, NULL, ZEND_FETCH_STATIC TSRMLS_CC); } | static_var_list ',' T_VARIABLE '=' static_scalar { zend_do_fetch_static_variable(&$3, &$5, ZEND_FETCH_STATIC TSRMLS_CC); } | T_VARIABLE { zend_do_fetch_static_variable(&$1, NULL, ZEND_FETCH_STATIC TSRMLS_CC); } | T_VARIABLE '=' static_scalar { zend_do_fetch_static_variable(&$1, &$3, ZEND_FETCH_STATIC TSRMLS_CC); } ; TIPI:PHP RELEASE_2011-07-29_V0.6.7 opcode staticzend_do_fetch_static_variable 3. opcode zend_do_fetch_static_variableopcode void zend_do_fetch_static_variable(znode *varname, const znode *static_assignment, int fetch_type TSRMLS_DC) { zval *tmp; zend_op *opline; znode lval; znode result; ALLOC_ZVAL(tmp); if (static_assignment) { *tmp = static_assignment->u.constant; } else { INIT_ZVAL(*tmp); } if (!CG(active_op_array)->static_variables) { /* */ ALLOC_HASHTABLE(CG(active_op_array)->static_variables); zend_hash_init(CG(active_op_array)->static_variables, 2, NULL, ZVAL_PTR_DTOR, 0); } // zend_hash_update(CG(active_op_array)->static_variables, varname- >u.constant.value.str.val, varname->u.constant.value.str.len+1, &tmp, sizeof(zval *), NULL); ...// opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = (fetch_type == ZEND_FETCH_LEXICAL) ? ZEND_FETCH_R : ZEND_FETCH_W; /* fetch_type=ZEND_FETCH_STATICZEND_FETCH_W*/ opline->result.op_type = IS_VAR; opline->result.u.EA.type = 0; opline->result.u.var = get_temporary_variable(CG(active_op_array)); opline->op1 = *varname; SET_UNUSED(opline->op2); opline->op2.u.EA.type = ZEND_FETCH_STATIC; /* */ result = opline->result; if (varname->op_type == IS_CONST) { zval_copy_ctor(&varname->u.constant); } fetch_simple_variable(&lval, varname, 0 TSRMLS_CC); /* Relies on the fact that the default fetch is BP_VAR_W */ if (fetch_type == ZEND_FETCH_LEXICAL) { ...// } else { zend_do_assign_ref(NULL, &lval, &result TSRMLS_CC); // } CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED; TIPI:PHP RELEASE_2011-07-29_V0.6.7 } CG(active_op_array)- >static_variables ZEND_FETCH_W ZEND_ASSIGN_REF ZEND_FETCH_Wzend_do_fetch_static_variableZEND_ASSIGN_REF zend_do_fetch_static_variablezend_do_assign_ref 4. opcodeopcode Zend/zend_vm_opcodes.h opcodeopcode #define ZEND_FETCH_W 83 #define ZEND_ASSIGN_REF 39 [][from-op-code-to-handler]opcode ZEND_FETCH_W ZEND_FETCH_W_SPEC_CV_HANDLER static int ZEND_FASTCALL ZEND_FETCH_W_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { return zend_fetch_var_address_helper_SPEC_CV(BP_VAR_W, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CV(int type, ZEND_OPCODE_HANDLER_ARGS) { ...// if (opline->op2.u.EA.type == ZEND_FETCH_STATIC_MEMBER) { retval = zend_std_get_static_property(EX_T(opline- >op2.u.var).class_entry, Z_STRVAL_P(varname), Z_STRLEN_P(varname), 0 TSRMLS_CC); } else { // EG(active_op_array)->static_variables target_symbol_table = zend_get_target_symbol_table(opline, EX(Ts), type, varname TSRMLS_CC); ...// if (zend_hash_find(target_symbol_table, varname->value.str.val, varname->value.str.len+1, (void **) &retval) == FAILURE) { switch (type) { ...// // type = case BP_VAR_Wcase BP_VAR_W case BP_VAR_W: { zval *new_zval = &EG(uninitialized_zval); Z_ADDREF_P(new_zval); zend_hash_update(target_symbol_table, varname- >value.str.val, varname->value.str.len+1, &new_zval, sizeof(zval *), (void **) &retval); // } TIPI:PHP RELEASE_2011-07-29_V0.6.7 break; EMPTY_SWITCH_DEFAULT_CASE() } } switch (opline->op2.u.EA.type) { ...// case ZEND_FETCH_STATIC: zval_update_constant(retval, (void*) 1 TSRMLS_CC); break; case ZEND_FETCH_GLOBAL_LOCK: if (IS_CV == IS_VAR && !free_op1.var) { PZVAL_LOCK(*EX_T(opline->op1.u.var).var.ptr_ptr); } break; } } ...// } zend_get_target_symbol_table op_arrayzend_op_array struct _zend_op_array { /* Common elements */ zend_uchar type; char *function_name; zend_uint num_args; zend_uint required_num_args; zend_arg_info *arg_info; zend_bool pass_rest_by_reference; unsigned char return_reference; /* END of common elements */ zend_bool done_pass_two; zend_uint *refcount; zend_op *opcodes; zend_uint last, size; /* static variables support */ HashTable *static_variables; zend_op *start_op; int backpatch_count; zend_uint this_var; // ... } zend_op_arrayfunction_name static inline HashTable *zend_get_target_symbol_table(const zend_op *opline, const temp_variable *Ts, int type, const zval *variable TSRMLS_DC) { switch (opline->op2.u.EA.type) { ...// case ZEND_FETCH_STATIC: TIPI:PHP RELEASE_2011-07-29_V0.6.7 if (!EG(active_op_array)->static_variables) { ALLOC_HASHTABLE(EG(active_op_array)->static_variables); zend_hash_init(EG(active_op_array)->static_variables, 2, NULL, ZVAL_PTR_DTOR, 0); } return EG(active_op_array)->static_variables; break; EMPTY_SWITCH_DEFAULT_CASE() } return NULL; } zend_do_fetch_static_variableop2.u.EA.typeZEND_FETCH_STATIC zend_get_target_symbol_tableEG(active_op_array)->static_variables op_array Zend(zend_op_array) PHP PHP is_numeric() instanceof instanceof instanceof PHP 5 is_a() PHP5 PHP PHP5.1PHP function array_print(Array $arr) { print_r($arr); } array_print(1); PHP5.1 Catchable fatal error: Argument 1 passed to array_print() must be an array, integer given, called in ... print_r TIPI:PHP RELEASE_2011-07-29_V0.6.7 function 1. 2. function array_print(Array $arr = 1) { print_r($arr); } array_print(array(1)); Array Fatal error: Default value for parameters with array type hint can only be an array or NULL Zend/zend_language_parser.y zend_do_receive_arg 5 znode *class_type 1. 2. PHP 3. tokenT_ARRAYArray zend_do_receive_argclass_type PHPZend/zend_complie.c zend_do_receive_arg opcodeZEND_RECV opcodeZEND_RECV_SPEC_HANDLER static int ZEND_FASTCALL ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { ...// if (param == NULL) { char *space; char *class_name = get_active_class_name(&space TSRMLS_CC); zend_execute_data *ptr = EX(prev_execute_data); if (zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, NULL, opline->extended_value TSRMLS_CC)) { TIPI:PHP RELEASE_2011-07-29_V0.6.7 ...// } ...// } else { ...// zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, opline->extended_value TSRMLS_CC); ...// } ...// } ZEND_RECV_SPEC_HANDLERzend_verify_arg_type static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval *arg, ulong fetch_type TSRMLS_DC) { ...// if (cur_arg_info->class_name) { const char *class_name; if (!arg) { need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC); return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, "none", "" TSRMLS_CC); } if (Z_TYPE_P(arg) == IS_OBJECT) { // , // , need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC); if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) { return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC); } } else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) { // NULL, , // , need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC); return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, zend_zval_type_name(arg), "" TSRMLS_CC); } } else if (cur_arg_info->array_type_hint) { // if (!arg) { return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be an array", "", "none", "" TSRMLS_CC); } if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be an array", "", zend_zval_type_name(arg), "" TSRMLS_CC); } } return 1; } zend_verify_arg_type3.1 TIPI:PHP RELEASE_2011-07-29_V0.6.7 3.1 zend_verify_arg_type zend_verify_arg_class_kind zend_verify_arg_error static inline char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, ulong fetch_type, const char **class_name, zend_class_entry **pce TSRMLS_DC) { *pce = zend_fetch_class(cur_arg_info->class_name, cur_arg_info- >class_name_len, (fetch_type | ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD) TSRMLS_CC); *class_name = (*pce) ? (*pce)->name: cur_arg_info->class_name; if (*pce && (*pce)->ce_flags & ZEND_ACC_INTERFACE) { return "implement interface "; } else { return "be an instance of "; } } static inline int zend_verify_arg_error(const zend_function *zf, zend_uint arg_num, const zend_arg_info *cur_arg_info, const char *need_msg, const char *need_kind, const char *given_msg, char *given_kind TSRMLS_DC) { zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; char *fname = zf->common.function_name; char *fsep; TIPI:PHP RELEASE_2011-07-29_V0.6.7 char *fclass; if (zf->common.scope) { fsep = "::"; fclass = zf->common.scope->name; } else { fsep = ""; fclass = ""; } if (ptr && ptr->op_array) { zend_error(E_RECOVERABLE_ERROR, "Argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d and defined", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, ptr->op_array->filename, ptr->opline->lineno); } else { zend_error(E_RECOVERABLE_ERROR, "Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); } return 0; } Argument passed tocalled in PHPzval PHP ZEopcodeopcodeexecute (Zend/zend_vm_execute.h:46) excute while (1) { ... if ((ret = EX(opline)->handler(execute_data TSRMLS_CC)) > 0) { switch (ret) { case 1: EG(in_execution) = original_in_execution; return; case 2: op_array = EG(active_op_array); goto zend_vm_enter; case 3: execute_data = EG(current_execute_data); default: break; } } ... } TIPI:PHP RELEASE_2011-07-29_V0.6.7 EX(opline)->handler(...)op_array ZEND_ASSIGN_SPEC_CV_CONST_HANDLER() ZEND_ASSIGN_SPEC_CV_CONST_HANDLERzval zval ZEND_ASSIGN_SPEC_CV_CONST_HANDLERZEND_VM_NEXT_OPCODE()op_array opline ZEZE //_zend_compiler_globals zend_compiler_globals *compiler_globals; //_zend_executor_globals zend_executor_globals *executor_globals; //_php_core_globals php.ini php_core_globals *core_globals; //_sapi_globals_struct SAPI sapi_globals_struct *sapi_globals; _zend_executor_globals _zend_executor_globals struct _zend_executor_globals { ... /* symbol table cache */ HashTable *symtable_cache[SYMTABLE_CACHE_SIZE]; HashTable **symtable_cache_limit; HashTable **symtable_cache_ptr; zend_op **opline_ptr; HashTable *active_symbol_table; /* active symbol table */ HashTable symbol_table; /* main symbol table */ HashTable included_files; /* files already included */ ... } active_symbol_table() EG ZEop_arrayHashTable FREE_HASHTABLE() unset ZEND_UNSET_VAR_SPEC_CV_HANDLER PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHP $a = 10; VLD ASSIGN ZEND_ASSIGN_SPEC_CV_CONST_HANDLER $aCV10 CONSTASSIGN Opcode CVPHP5.1 VLDPHP compiled vars: !0 = $a $aop_typeIS_CV IS_CV Zend/zend_complie.czend_do_end_variable_parse zval *value = &opline->op2.u.constant; zval **variable_ptr_ptr = _get_zval_ptr_ptr_cv(&opline->op1, EX(Ts), BP_VAR_W TSRMLS_CC); constant _get_zval_ptr_ptr_cvzval [_get_zval_ptr_ptr_cv] --> [_get_zval_cv_lookup] _get_zval_cv_lookup zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void **)ptr) HashTableEG(active_symbol_table)cv->name ptr ZEND_ASSIGN_SPEC_CV_CONST_HANDLERvariable_ptr_ptr -- zend_assign_to_variable zend_assign_to_variable if zvalis_ref__gc0 $a = 10; $b = &$a; xdebug_debug_zval('a'); TIPI:PHP RELEASE_2011-07-29_V0.6.7 $a = 20; xdebug_debug_zval('a'); $b = &$a; ; ; ZE if (PZVAL_IS_REF(variable_ptr)) { if (variable_ptr!=value) { zend_uint refcount = Z_REFCOUNT_P(variable_ptr); garbage = *variable_ptr; *variable_ptr = *value; Z_SET_REFCOUNT_P(variable_ptr, refcount); Z_SET_ISREF_P(variable_ptr); if (!is_tmp_var) { zendi_zval_copy_ctor(*variable_ptr); } zendi_zval_dtor(garbage); return variable_ptr; } } PZVAL_IS_REF(variable_ptr)is_ref__gc0 zval*value a: (refcount=2, is_ref=1),int 10 a: (refcount=2, is_ref=1),int 20 1 $a = 10; $a = $a; 110 $a if (Z_DELREF_P(variable_ptr)==0) { // if (!is_tmp_var) { if (variable_ptr==value) { Z_ADDREF_P(variable_ptr); // } ...// TIPI:PHP RELEASE_2011-07-29_V0.6.7 1 PHP $a = 10; $b = &$a; $c = $a; $c = $a; ZENDzval 1 $c$azval garbage = *variable_ptr; *variable_ptr = *value; INIT_PZVAL(variable_ptr); // zval zval_copy_ctor(variable_ptr); zendi_zval_dtor(garbage); return variable_ptr; $c = $a; $c = &$a;$a$b$c $b = &$a; $b = $a; 1 $a = 10; $c = $a; PHP5.3 PHP Z_ADDREF_P(value); // 1 *variable_ptr_ptr = value; if (variable_ptr != &EG(uninitialized_zval)) { GC_REMOVE_ZVAL_FROM_BUFFER(variable_ptr); // zval_dtor(variable_ptr); efree(variable_ptr); // } return value; 00 TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHP $a = 10; $b = $a; $va = 20; $vb = &$va; $a = $va; xdebug$a1$va 2$va ALLOC_ZVAL(variable_ptr); // zval *variable_ptr_ptr = variable_ptr; *variable_ptr = *value; zval_copy_ctor(variable_ptr); Z_SET_REFCOUNT_P(variable_ptr, 1); // 1 zval1$a zend_assign_to_variable PHPunset unset $a = 10; unset($a); VLD compiled vars: !0 = $a line # * op fetch ext return operands ------------------------------------------------------------------------------- -- 2 0 > EXT_STMT 1 ASSIGN !0, 10 3 2 EXT_STMT 3 UNSET_VAR !0 4 > RETURN 1 unset UNSET_VARunse Zend/zend_vm_execute.h ZEND_UNSET_VAR_SPEC_CV_HANDLER target_symbol_table = zend_get_target_symbol_table(opline, EX(Ts), TIPI:PHP RELEASE_2011-07-29_V0.6.7 BP_VAR_IS, varname TSRMLS_CC); if (zend_hash_quick_del(target_symbol_table, varname->value.str.val, varname->value.str.len+1, hash_value) == SUCCESS) { ...// } HashTableunset HashTable GC HashTable << (HashTable) >> last_var * (EG(active_symbol_table) ? 1 : 2) + sizeof(temp_variable) * op_array->T TSRMLS_CC); EX(symbol_table) = EG(active_symbol_table); EX(prev_execute_data) = EG(current_execute_data); EG(current_execute_data) = execute_data; global“”*active_symbol_table gloal PHPEXzend_execute_data #define EX(element) execute_data->element global globalglobal TIPI:PHP RELEASE_2011-07-29_V0.6.7 1. Zend/zend_language_scanner.l global "global" { return T_GLOBAL; } 2. tokentokenZend/zend_language_parser.y | T_GLOBAL global_var_list ';' global_var_list: global_var_list ',' global_var { zend_do_fetch_global_variable(&$3, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); } | global_var { zend_do_fetch_global_variable(&$1, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); } ; $3global_varyaccyacc zend_do_fetch_global_variable Zend/zend_compile.c void zend_do_fetch_global_variable(znode *varname, const znode *static_assignment, int fetch_type TSRMLS_DC) { ...// opline->opcode = ZEND_FETCH_W; /* the default mode must be Write, since fetch_simple_variable() is used to define function arguments */ opline->result.op_type = IS_VAR; opline->result.u.EA.type = 0; opline->result.u.var = get_temporary_variable(CG(active_op_array)); opline->op1 = *varname; SET_UNUSED(opline->op2); opline->op2.u.EA.type = fetch_type; result = opline->result; ... // fetch_simple_variable(&lval, varname, 0 TSRMLS_CC); /* Relies on the fact that the default fetch is BP_VAR_W */ zend_do_assign_ref(NULL, &lval, &result TSRMLS_CC); CG(active_op_array)->opcodes[CG(active_op_array)- >last-1].result.u.EA.type |= EXT_TYPE_UNUSED; } /* }}} */ opcodeZEND_FETCH_Wzend_do_assign_ref zend_do_assign_ref TIPI:PHP RELEASE_2011-07-29_V0.6.7 void zend_do_assign_ref(znode *result, const znode *lvar, const znode *rvar TSRMLS_DC) /* {{{ */ { zend_op *opline; ... // opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_ASSIGN_REF; ...// if (result) { opline->result.op_type = IS_VAR; opline->result.u.EA.type = 0; opline->result.u.var = get_temporary_variable(CG(active_op_array)); *result = opline->result; } else { /* SET_UNUSED(opline->result); */ opline->result.u.EA.type |= EXT_TYPE_UNUSED; } opline->op1 = *lvar; opline->op2 = *rvar; } zend_do_fetch_global_variablezend_do_assign_ref globalZEND_FETCH_WZEND_ASSIGN_REF 3. ZEND_FETCH_W ZEND_FETCH_W = 83 op->op1.op_type = 4 op->op2.op_type = 0 zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]]; ZEND_FETCH_W_SPEC_CV_HANDLER static int ZEND_FASTCALL ZEND_FETCH_W_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { return zend_fetch_var_address_helper_SPEC_CV(BP_VAR_W, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } zend_fetch_var_address_helper_SPEC_CV target_symbol_table = zend_get_target_symbol_table(opline, EX(Ts), type, varname TSRMLS_CC); zend_get_target_symbol_table TIPI:PHP RELEASE_2011-07-29_V0.6.7 static inline HashTable *zend_get_target_symbol_table(const zend_op *opline, const temp_variable *Ts, int type, const zval *variable TSRMLS_DC) { switch (opline->op2.u.EA.type) { ... // case ZEND_FETCH_GLOBAL: case ZEND_FETCH_GLOBAL_LOCK: return &EG(symbol_table); break; ... // } return NULL; } ZEND_FETCH_GLOBAL_LOCK &EG(symbol_table); global PHPPHPZVAL ZVAL PHP PHP: PHP 1 PHP $string = "To love someone sincerely means to love all the people, to love the world and life, too." $integer = 10; $string = $integer; $string VLD zend_assign_to_variable $stringZVAL $integer $integer1$string 2 PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 : cast_object) { zval *org; TSRMLS_FETCH(); ALLOC_ZVAL(org); *org = *op; if (Z_OBJ_HT_P(op)->cast_object(org, op, IS_NULL TSRMLS_CC) == SUCCESS) { zval_dtor(org); return; } *op = *org; FREE_ZVAL(org); } } zval_dtor(op); Z_TYPE_P(op) = IS_NULL; } NULLIS_NULL (unset)$aunset($a) $aNULL CPHPPHP PHPsettype()gettype() gettype()ext/standard/type.csettype PHP_FUNCTION(settype) { zval **var; char *type; int type_len = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zs", &var, &type, &type_len) == FAILURE) { return; } if (!strcasecmp(type, "integer")) { convert_to_long(*var); } else if (!strcasecmp(type, "int")) { convert_to_long(*var); } else if (!strcasecmp(type, "float")) { convert_to_double(*var); } else if (!strcasecmp(type, "double")) { /* deprecated */ convert_to_double(*var); } else if (!strcasecmp(type, "string")) { convert_to_string(*var); } else if (!strcasecmp(type, "array")) { convert_to_array(*var); } else if (!strcasecmp(type, "object")) { TIPI:PHP RELEASE_2011-07-29_V0.6.7 convert_to_object(*var); } else if (!strcasecmp(type, "bool")) { convert_to_boolean(*var); } else if (!strcasecmp(type, "boolean")) { convert_to_boolean(*var); } else if (!strcasecmp(type, "null")) { convert_to_null(*var); } else if (!strcasecmp(type, "resource")) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot convert to resource type"); RETURN_FALSE; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type"); RETURN_FALSE; } RETVAL_TRUE; } PHP PHP C PHP PHP5 PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 function foo($arg_1, $arg_2, ..., $arg_n) { echo "Example function.\n"; return $retval; } PHP PHP 3 PHP 4 PHP countstrposimplode issetemptyeval PHP ext/standard PHP 5.3 create_function PHP5.3--ClosurePHP 5.3 Closure”Static” PHP PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 $func = 'print_r'; $func('i am print_r function.'); echo PHP PHP PHPPHP #define ZEND_INTERNAL_FUNCTION 1 #define ZEND_USER_FUNCTION 2 #define ZEND_OVERLOADED_FUNCTION 3 #define ZEND_EVAL_CODE 4 #define ZEND_OVERLOADED_FUNCTION_TEMPORARY 5 ZEND_USER_FUNCTIONZEND_INTERNAL_FUNCTION PHP 1.(ZEND_USER_FUNCTION) Hi! PHP PHP““NULL << >> ZE _zend_execute_data struct _zend_execute_data { //... zend_function_state function_state; zend_function *fbc; /* Function Being Called */ //... }; TIPI:PHP RELEASE_2011-07-29_V0.6.7 function_statefunction_state typedef struct _zend_function_state { zend_function *function; void **arguments; } zend_function_state; **arguments*function *function zend_function typedef union _zend_function { zend_uchar type; /* MUST be the first element of this struct! */ struct { zend_uchar type; /* never used */ char *function_name; // zend_class_entry *scope; // zend_uint fn_flags; // #define ZEND_USER_FUNCTION 2 union _zend_function *prototype; // zend_uint num_args; // zend_uint required_num_args; // zend_arg_info *arg_info; // zend_bool pass_rest_by_reference; unsigned char return_reference; // } common; zend_op_array op_array; // zend_internal_function internal_function; } zend_function; zend_functionop_arrayZE op_arrayopline VLD 2.(ZEND_INTERNAL_FUNCTION) ZEND_INTERNAL_FUNCTIONZend/PHP“C/C++” typedef struct _zend_internal_function { /* Common elements */ zend_uchar type; char * function_name; zend_class_entry *scope; zend_uint fn_flags; union _zend_function *prototype; zend_uint num_args; zend_uint required_num_args; zend_arg_info *arg_info; zend_bool pass_rest_by_reference; unsigned char return_reference; /* END of common elements */ void (*handler)(INTERNAL_FUNCTION_PARAMETERS); struct _zend_module_entry *module; TIPI:PHP RELEASE_2011-07-29_V0.6.7 } zend_internal_function; ZEfunction_entry (module->functions) zend_internal_function type ZEND_INTERNAL_FUNCTION(HashTable; Zend/zend_API.c zend_register_functions handler. ZEND_INTERNAL_FUNCTION ZE zend_execute_internalzend_internal_function.handler module typetypetype 3. PHP PHP $func $func = 'print_r'; $func('i am print_r function.'); VLD function name: (null) number of ops: 9 compiled vars: !0 = $func line # * op fetch ext return operands ------------------------------------------------------------------------------- - - 2 0 > EXT_STMT 1 ASSIGN !0, 'print_r' 3 2 EXT_STMT 3 INIT_FCALL_BY_NAME !0 4 EXT_FCALL_BEGIN 5 SEND_VAL 'i+am+print_r+function.' 6 DO_FCALL_BY_NAME 1 7 EXT_FCALL_END 8 > RETURN 1 print_r TIPI:PHP RELEASE_2011-07-29_V0.6.7 print_r('i am print_r function.'); VLD function name: (null) number of ops: 6 compiled vars: none line # * op fetch ext return operands ------------------------------------------------------------------------------- - - 2 0 > EXT_STMT 1 EXT_FCALL_BEGIN 2 SEND_VAL 'i+am+print_r+function.' 3 DO_FCALL 1 'print_r' 4 EXT_FCALL_END 5 > RETURN 1 DO_FCALL_BY_NAME DO_FCALL Zend/zend_complie.c zend_do_end_function_call if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) { opline->opcode = ZEND_DO_FCALL; opline->op1 = *function_name; ZVAL_LONG(&opline->op2.u.constant, zend_hash_func(Z_STRVAL(function_name->u.constant), Z_STRLEN(function_name- >u.constant) + 1)); } else { opline->opcode = ZEND_DO_FCALL_BY_NAME; SET_UNUSED(opline->op1); } ZEND_DO_FCALLZEND_DO_FCALL_BY_NAME Zend/zend_complie.czend_do_pass_param ZEND_SEND_VAL_SPEC_CONST_HANDLER 4. << >> TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHPfunction function foo($var) { echo $var; } functionfunction Zend/zend_language_scanner.l "function" { return T_FUNCTION; } functionT_FUNCTION Zend/zend_language_parser.y function: T_FUNCTION { $$.u.opline_num = CG(zend_lineno); } ; is_reference: /* empty */ { $$.op_type = ZEND_RETURN_VAL; } | '&' { $$.op_type = ZEND_RETURN_REF; } ; unticked_function_declaration_statement: function is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); } '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } ; function is_reference T_STRINGfunction T_FUNCTION zend_do_begin_function_declaration Zend/zend_complie.c void zend_do_begin_function_declaration(znode *function_token, znode TIPI:PHP RELEASE_2011-07-29_V0.6.7 *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC) /* {{{ */ { ...// function_token->u.op_array = CG(active_op_array); lcname = zend_str_tolower_dup(name, name_len); orig_interactive = CG(interactive); CG(interactive) = 0; init_op_array(&op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC); CG(interactive) = orig_interactive; ...// if (is_method) { ...// } else { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_DECLARE_FUNCTION; opline->op1.op_type = IS_CONST; build_runtime_defined_function_key(&opline->op1.u.constant, lcname, name_len TSRMLS_CC); opline->op2.op_type = IS_CONST; opline->op2.u.constant.type = IS_STRING; opline->op2.u.constant.value.str.val = lcname; opline->op2.u.constant.value.str.len = name_len; Z_SET_REFCOUNT(opline->op2.u.constant, 1); opline->extended_value = ZEND_DECLARE_FUNCTION; zend_hash_update(CG(function_table), opline- >op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &op_array, sizeof(zend_op_array), (void **) &CG(active_op_array)); } } /* }}} */ ZEND_DECLARE_FUNCTION op_type ZEND_DECLARE_FUNCTION_SPEC_HANDLER function T() { echo 1; } function t() { echo 2; } TIPI:PHP RELEASE_2011-07-29_V0.6.7 Fatal error: Cannot redeclare t() (previously declared in ...) PHPTt Zend/zend_vm_execute.h ZEND_DECLARE_FUNCTION ZEND_DECLARE_FUNCTION_SPEC_HANDLER do_bind_function do_bind_function(EX(opline), EG(function_table), 0); EX(opline)EG(function_table) EG(function_table) HashTabledo_bind_functionHashTable zend_hash_add << >> zend_do_receive_arg CG(active_op_array)->arg_info = erealloc(CG(active_op_array)->arg_info, sizeof(zend_arg_info)*(CG(active_op_array)->num_args)); cur_arg_info = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-1]; cur_arg_info->name = estrndup(varname->u.constant.value.str.val, varname->u.constant.value.str.len); cur_arg_info->name_len = varname->u.constant.value.str.len; cur_arg_info->array_type_hint = 0; cur_arg_info->allow_null = 1; cur_arg_info->pass_by_reference = pass_by_reference; cur_arg_info->class_name = NULL; cur_arg_info->class_name_len = 0; arg_infoarg_info arg_info typedef struct _zend_arg_info { const char *name; /* */ zend_uint name_len; /* */ const char *class_name; /* */ TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_uint class_name_len; /* */ zend_bool array_type_hint; /* */ zend_bool allow_null; /* NULL*/ zend_bool pass_by_reference; /* */ zend_bool return_reference; int required_num_args; } zend_arg_info; pass_by_reference arg_nums **zend_do_receive_arg×× 1. CG(active_op_array)->num_args++; CG(active_op_array)->num_args-1 . cur_arg_info = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-1]; func_num_args func_get_args Zend\zend_builtin_functions.c func_num_args /* {{{ proto int func_num_args(void) Get the number of arguments that were passed to the function */ ZEND_FUNCTION(func_num_args) { zend_execute_data *ex = EG(current_execute_data)->prev_execute_data; if (ex && ex->function_state.arguments) { RETURN_LONG((long)(zend_uintptr_t)*(ex->function_state.arguments)); } else { zend_error(E_WARNING, "func_num_args(): Called from the global scope - no function context"); RETURN_LONG(-1); } } /* }}} */ ex->function_state.argumentsex->function_state.arguments -1 EG(current_execute_data) zend_execute_data *ex = EG(current_execute_data)->prev_execute_data; function_state func_num_argsfunc_get_args ex- TIPI:PHP RELEASE_2011-07-29_V0.6.7 >function_state.arguments count /* {{{ proto int count(mixed var [, int mode]) Count the number of elements in a variable (usually an array) */ PHP_FUNCTION(count) { zval *array; long mode = COUNT_NORMAL; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) { return; } ... // } ZEND_NUM_ARGS() #define ZEND_NUM_ARGS() (ht) PHP3 ARG_COUNT ht Zend/zend.h INTERNAL_FUNCTION_PARAMETERS ht #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC PHP zend_parse_parameters ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...) num_argsZEND_NUM_ARGS() “” TSRMLS_CC type_spec printf TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHP zend_parse_parameters() SUCCESS FAILURE l - d - s - () b - r - zval* a - zval* o - zval * O - class entry zval * z - zval* | - / - SEPARATE_ZVAL_IF_NOT_REF() ! - NULL aoOrz NULL NULL pascal procedure PHPreturnreturn NULL return returnPHP return Zend/zend_language_parser.y zend_do_return void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ { zend_op *opline; int start_op_number, end_op_number; TIPI:PHP RELEASE_2011-07-29_V0.6.7 if (do_end_vparse) { if (CG(active_op_array)->return_reference && !zend_is_function_or_method_call(expr)) { zend_do_end_variable_parse(expr, BP_VAR_W, 0 TSRMLS_CC);/* */ } else { zend_do_end_variable_parse(expr, BP_VAR_R, 0 TSRMLS_CC);/* */ } } ...// opline->opcode = ZEND_RETURN; if (expr) { opline->op1 = *expr; if (do_end_vparse && zend_is_function_or_method_call(expr)) { opline->extended_value = ZEND_RETURNS_FUNCTION; } } else { opline->op1.op_type = IS_CONST; INIT_ZVAL(opline->op1.u.constant); } SET_UNUSED(opline->op2); } /* }}} */ ZEND_RETURN IS_CONST ZEND_RETURN ZEND_RETURN_SPEC_CONST_HANDLER ZEND_RETURN_SPEC_TMP_HANDLERZEND_RETURN_SPEC_TMP_HANDLER ZEND_RETURN_SPEC_CONST_HANDLER static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { zend_op *opline = EX(opline); zval *retval_ptr; zval **retval_ptr_ptr; if (EG(active_op_array)->return_reference == ZEND_RETURN_REF) { // if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { /* Not supposed to happen, but we'll allow it */ zend_error(E_NOTICE, "Only variable references \ should be returned by reference"); goto return_by_value; } retval_ptr_ptr = NULL; // if (IS_CONST == IS_VAR && !retval_ptr_ptr) { zend_error_noreturn(E_ERROR, "Cannot return string offsets by reference"); } TIPI:PHP RELEASE_2011-07-29_V0.6.7 } if (IS_CONST == IS_VAR && !Z_ISREF_PP(retval_ptr_ptr)) { if (opline->extended_value == ZEND_RETURNS_FUNCTION && EX_T(opline->op1.u.var).var.fcall_returned_reference) { } else if (EX_T(opline->op1.u.var).var.ptr_ptr == &EX_T(opline->op1.u.var).var.ptr) { if (IS_CONST == IS_VAR && !0) { /* undo the effect of get_zval_ptr_ptr() */ PZVAL_LOCK(*retval_ptr_ptr); } zend_error(E_NOTICE, "Only variable references \ should be returned by reference"); goto return_by_value; } } if (EG(return_value_ptr_ptr)) { // SEPARATE_ZVAL_TO_MAKE_IS_REF(retval_ptr_ptr); // is_ref__gc 1 Z_ADDREF_PP(retval_ptr_ptr); // refcount__gc1 (*EG(return_value_ptr_ptr)) = (*retval_ptr_ptr); } } else { return_by_value: retval_ptr = &opline->op1.u.constant; if (!EG(return_value_ptr_ptr)) { if (IS_CONST == IS_TMP_VAR) { } } else if (!0) { /* Not a temp var */ if (IS_CONST == IS_CONST || EG(active_op_array)->return_reference == ZEND_RETURN_REF || (PZVAL_IS_REF(retval_ptr) && Z_REFCOUNT_P(retval_ptr) > 0)) { zval *ret; ALLOC_ZVAL(ret); INIT_PZVAL_COPY(ret, retval_ptr); // zval_copy_ctor(ret); *EG(return_value_ptr_ptr) = ret; } else { *EG(return_value_ptr_ptr) = retval_ptr; // Z_ADDREF_P(retval_ptr); } } else { zval *ret; ALLOC_ZVAL(ret); INIT_PZVAL_COPY(ret, retval_ptr); // *EG(return_value_ptr_ptr) = ret; } } return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); // } *EG(return_value_ptr_ptr)ZE return ZEzend_leave_helper_SPEC ZE TIPI:PHP RELEASE_2011-07-29_V0.6.7 NULL return PHPPHP“ “NULL “” zend_do_end_function_declaration zend_do_return(NULL, 0 TSRMLS_CC); NULLreturnNULL return_value PHP_FUNCTION zval return_value MAKE_STD_ZVAL zval ZEND RETURN_RESOURCE(resource) RETURN_BOOL(bool) RETURN_NULL() RETURN_LONG(long) RETURN_DOUBLE(double) RETURN_STRING(string, duplicate) duplicate estrdup() RETURN_STRINGL(string, length, duplicate) RETURN_STRING RETURN_EMPTY_STRING() RETURN_FALSE RETURN_TRUE RETVAL_RESOURCE(resource) RETVAL_BOOL(bool) RETVAL_NULL RETVAL_LONG(long) RETVAL_DOUBLE(double) RETVAL_STRING(string, duplicate) duplicate RETURN_STRING TIPI:PHP RELEASE_2011-07-29_V0.6.7 RETVAL_STRINGL(string, length, duplicate) RETVAL_STRING RETVAL_EMPTY_STRING RETVAL_FALSE RETVAL_TRUE array_init() object_init() hash return_value return_value*EG(return_value_ptr_ptr) <<>>. ( ) Zend “”php opcodes function name: (null) line # * op fetch ext return operands ------------------------------------------------------------------------------- -- DO_FCALL 0 'foo' NOP > RETURN 1 function name: foo line # * op fetch ext return operands ------------------------------------------------------------------------------- -- 4 0 > ECHO 'I%27m+foo%21' 5 1 > RETURN null opcodesopcodes TIPI:PHP RELEASE_2011-07-29_V0.6.7 foo fooOPCODE“DO_FCALL“ DO_FCALLZEfunction_table str_tolowerPHP) “Call to undefined function xxx()"; zend_function function.type zend_execute_internalzend_internal_function.handler zend_execute zend_op_array opcodes"function name:" phpphp “” php“”op_lineop_array CC [php] opcodes line # * op fetch ext return operands ------------------------------------------------------------------------------- -- 2 0 > ASSIGN !0, 'test' 3 1 SEND_VAR !0 2 DO_FCALL 1 'print_r' 4 3 > RETURN 1 opcodesDO_FCALL zend_do_fcall_common_helper_SPEC() ((zend_internal_function *) EX(function_state).function)->handler(opline- >extended_value, EX_T(opline->result.u.var).var.ptr, EX(function_state).function->common .return_reference?&EX_T(opline- >result.u.var).var.ptr:NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); zend_execute_internalZend/zend.c zend_startup TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_execute_internal = NULL; NULLif (!zend_execute_internal) count<< >> handler C EG(function_table) php_module_startup --> php_register_extensions --> zend_register_internal_module --> zend_register_module_ex --> zend_register_functions zend_register_functions(NULL, module->functions, NULL, module->type TSRMLS_CC) standardmodule zend_module_entry basic_functions_module = { /* {{{ */ STANDARD_MODULE_HEADER_EX, NULL, standard_deps, "standard", /* extension name */ basic_functions, /* function list */ ... // } module->functionsbasic_functionsbasic_functions.c basic_functions const zend_function_entry basic_functions[] = { /* {{{ */ ...// PHP_FE(count, arginfo_count) ...// } #define PHP_FE ZEND_FE #define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0) #define ZEND_FN(name) zif_##name #define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags }, countzif_countcount namezend_function_entry zend_function_entry typedef struct _zend_function_entry { const char *fname; void (*handler)(INTERNAL_FUNCTION_PARAMETERS); const struct _zend_arg_info *arg_info; zend_uint num_args; zend_uint flags; } zend_function_entry; TIPI:PHP RELEASE_2011-07-29_V0.6.7 handler zend_do_fcall_common_helper_SPEC() if (EX(function_state).function->type == ZEND_USER_FUNCTION || EX(function_state).function->common.scope) { should_change_scope = 1; EX(current_this) = EG(This); EX(current_scope) = EG(scope); EX(current_called_scope) = EG(called_scope); EG(This) = EX(object); EG(scope) = (EX(function_state).function->type == ZEND_USER_FUNCTION || !EX(object)) ? EX(function_state).function->common.scope : NULL; EG(called_scope) = EX(called_scope); } EGThisscope zend_executeexecute op_array if (zend_execute == execute && !EG(exception)) { EX(call_opline) = opline; ZEND_VM_ENTER(); } else { zend_execute(EG(active_op_array) TSRMLS_CC); } Zend/zend.czend_startupzend_execute zend_execute = execute; executeopcodes executezend_op_array ret = EX(opline)->handler(execute_data TSRMLS_CC) opcodeexecute_dataexecute Lisp JavascriptC#PHP5.3 C++ C++0x (Closure) TIPI:PHP RELEASE_2011-07-29_V0.6.7 (Lexical Closure) create_function()"" PHP5.3 : create_function PHP4.1PHP5 : ASSIGN !0, 100 1 ZEND_DECLARE_LAMBDA_FUNCTION '%00%7Bclosure 2 ASSIGN !1, ~1 3 INIT_FCALL_BY_NAME !1 4 DO_FCALL_BY_NAME 0 5 > RETURN 1 function name: {closure} number of ops: 5 compiled vars: !0 = $i line # * op fetch ext return operands ------------------------------------------------------------------------------- - 3 0 > FETCH_R static $0 'i' 1 ASSIGN !0, $0 4 2 SEND_VAR !0 3 DO_FCALL 1 'debug_zval_dump' 5 4 > RETURN null 1100!0$i ZEND_DECLARE_LAMBDA_FUNCTION opcode opcode$PHP_SRC/Zend/zend_vm_execute.h: static int ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { zend_op *opline = EX(opline); zend_function *op_array; if (zend_hash_quick_find(EG(function_table), Z_STRVAL(opline- >op1.u.constant), Z_STRLEN(opline->op1.u.constant), Z_LVAL(opline- >op2.u.constant), (void *) &op_arra y) == FAILURE || TIPI:PHP RELEASE_2011-07-29_V0.6.7 op_array->type != ZEND_USER_FUNCTION) { zend_error_noreturn(E_ERROR, "Base lambda function for closure not found"); } zend_create_closure(&EX_T(opline->result.u.var).tmp_var, op_array TSRMLS_CC); ZEND_VM_NEXT_OPCODE(); } zend_create_closure(), $PHP_SRC/Zend/zend_closures.czend_create_closure() ZEND_API void zend_create_closure(zval *res, zend_function *func TSRMLS_DC) { zend_closure *closure; object_init_ex(res, zend_ce_closure); closure = (zend_closure *)zend_object_store_get_object(res TSRMLS_CC); closure->func = *func; if (closure->func.type == ZEND_USER_FUNCTION) { // if (closure->func.op_array.static_variables) { HashTable *static_variables = closure- >func.op_array.static_variables; // ALLOC_HASHTABLE(closure->func.op_array.static_variables); zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0); // zval_copy_static_var zend_hash_apply_with_arguments(static_variables TSRMLS_CC, (apply_func_args_t)zval_copy_static_var, 1, closure- >func.op_array.static_variables); } (*closure->func.op_array.refcount)++; } closure->func.common.scope = NULL; } , zval_copy_static_var(): static int zval_copy_static_var(zval **p TSRMLS_DC, int num_args, va_list args, zend_hash_key *key) /* {{{ */ { HashTable *target = va_arg(args, HashTable*); zend_bool is_ref; // use if (Z_TYPE_PP(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) { is_ref = Z_TYPE_PP(p) & IS_LEXICAL_REF; if (!EG(active_symbol_table)) { zend_rebuild_symbol_table(TSRMLS_C); } TIPI:PHP RELEASE_2011-07-29_V0.6.7 // if (zend_hash_quick_find(EG(active_symbol_table), key->arKey, key- >nKeyLength, key->h, (void **) &p) == FAILURE) { if (is_ref) { zval *tmp; // ALLOC_INIT_ZVAL(tmp); Z_SET_ISREF_P(tmp); zend_hash_quick_add(EG(active_symbol_table), key->arKey, key- >nKeyLength, key->h, &tmp, sizeof(zval*), (void**)&p); } else { // p = &EG(uninitialized_zval_ptr); zend_error(E_NOTICE,"Undefined variable: %s", key->arKey); } } else { // if (is_ref) { SEPARATE_ZVAL_TO_MAKE_IS_REF(p); } else if (Z_ISREF_PP(p)) { SEPARATE_ZVAL(p); } } } if (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, p, sizeof(zval*), NULL) == SUCCESS) { Z_ADDREF_PP(p); } return ZEND_HASH_APPLY_KEEP; } zend_hash_apply_with_arguments() hash use use PHP op_array C C ? PHP5.3 TIPI:PHP RELEASE_2011-07-29_V0.6.7 TIPI:PHP RELEASE_2011-07-29_V0.6.7 C 1. classJavascript Javascript(Function) 2. 3. 4. 5. PHP PHPPHP PHPPHP4PHP4 PHP5 PHPObject Model final PHP5.3 Late Static Binding __callStatic() __invoke() PHPreference PHP -- PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHPPHPPHP classPHP PHP class ParentClass { } interface Ifce { public function iMethod(); } final class Tipi extends ParentClass implements Ifce { public static $sa = 'aaa'; const CA = 'bbb'; public function __constrct() { } public function iMethod() { } private function _access() { } public static function access() { } } ParentClassIfceTipiParentClass Ifce$sa CA Zend : struct _zend_class_entry { char type; // ZEND_INTERNAL_CLASS / ZEND_USER_CLASS char *name;// zend_uint name_length; // sizeof(name) - 1 struct_zend_class_entry *parent; // intrefcount; // zend_bool constants_updated; zend_uint ce_flags; // ZEND_ACC_IMPLICIT_ABSTRACT_CLASS: abstract // ZEND_ACC_EXPLICIT_ABSTRACT_CLASS: abstract TIPI:PHP RELEASE_2011-07-29_V0.6.7 // ZEND_ACC_FINAL_CLASS // ZEND_ACC_INTERFACE HashTable function_table; // HashTable default_properties; // HashTable properties_info; // HashTable default_static_members;// HashTable *static_members; // type == ZEND_USER_CLASS &default_static_members; // type == ZEND_INTERAL_CLASSNULL HashTable constants_table; // struct _zend_function_entry *builtin_functions;// union _zend_function *constructor; union _zend_function *destructor; union _zend_function *clone; /* */ union _zend_function *__get; union _zend_function *__set; union _zend_function *__unset; union _zend_function *__isset; union _zend_function *__call; union _zend_function *__tostring; union _zend_function *serialize_func; union _zend_function *unserialize_func; zend_class_iterator_funcs iterator_funcs;// /* */ zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC); zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object, intby_ref TSRMLS_DC); /* */ int(*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type TSRMLS_DC); /* */ int(*serialize)(zval *object unsignedchar**buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC); int(*unserialize)(zval **object, zend_class_entry *ce, constunsignedchar*buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC); zend_class_entry **interfaces; // zend_uint num_interfaces; // char *filename; // zend_uint line_start; // zend_uint line_end; // char *doc_comment; zend_uint doc_comment_len; struct _zend_module_entry *module; // EG(current_module) }; PHP 5.1 TIPI:PHP RELEASE_2011-07-29_V0.6.7 ParentClass Ifce Tipi name ParentClass Ifce Tipi type 2 2 2 parent ParentClass refcount 1 1 2 ce_flags 0 144 524352 function_table function_name=iMethod | type=2 | fn_flags=258 function_name=__construct | type=2 | fn_flags=8448 function_name=iMethod | type=2 | fn_flags=65800 function_name=_access | type=2 | fn_flags=66560 function_name=access | type=2 | fn_flags=257 interfaces Ifce 1 filename /tipi.php /tipi.php /ipi.php line_start 15 18 22 line_end 16 20 38 type12 : #define ZEND_INTERNAL_CLASS 1 #define ZEND_USER_CLASS 2 struct_zend_class_entry union _zend_function *constructor; NULL TIPI:PHP RELEASE_2011-07-29_V0.6.7 classZend/zend_language_scanner.lclasstoken T_CLASS tokenZend/zend_language_parser.y unticked_class_declaration_statement: class_entry_type T_STRING extends_from { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } implements_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); } | interface_entry T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } interface_extends_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); } ; class_entry_type: T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = 0; } | T_ABSTRACT T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } | T_FINAL T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = ZEND_ACC_FINAL_CLASS; } ; class_entry_type(T_CLASS) (T_ABSTRACT T_CLASS)final(T_FINAL T_CLASS ) : (T_CLASS) type=0 (T_ABSTRACT T_CLASS) type=ZEND_ACC_EXPLICIT_ABSTRACT_CLASS final(T_FINAL T_CLASS) type=ZEND_ACC_FINAL_CLASS abstract abstracttype=ZEND_ACC_IMPLICIT_ABSTRACT_CLASS classabstract type=ZEND_ACC_INTERFACEinterface interface_entry: Zend/zend_complie.h #define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS 0x10 #define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20 #define ZEND_ACC_FINAL_CLASS 0x40 #define ZEND_ACC_INTERFACE 0x80 00 final TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_do_begin_class_declarationzend_do_end_class_declaration zend_do_begin_class_declaration zend_do_end_class_declaration Zend/zend_complie.c zend_do_begin_class_declaration [zend_initialize_class_data] --> [zend_hash_init_ex] zend_hash_init_ex(&ce->default_properties, 0, NULL, zval_ptr_dtor_func, persistent_hashes, 0); HashTablezend_hash_init_ex HashTable zend_do_declare_property final PHPfinal class Tipi { public final $var; } Fatal error: Cannot declare property Tipi::$var final, the final modifier is allowed only for methods and classes in .. zend_do_declare_property TIPI:PHP RELEASE_2011-07-29_V0.6.7 if (access_type & ZEND_ACC_FINAL) { zend_error(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, the final modifier is allowed only for methods and classes", CG(active_class_entry)->name, var_name- >u.constant.value.str.val); } ALLOC_ZVAL(property); // if (value) { // *property = value->u.constant; } else { INIT_PZVAL(property); Z_TYPE_P(property) = IS_NULL; } ZVALIS_NULL zend_declare_property_ex default_properties get_class_vars() varname => value if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC) == FAILURE) { RETURN_FALSE; } else { array_init(return_value); zend_update_class_constants(*pce TSRMLS_CC); add_class_vars(*pce, &(*pce)->default_properties, return_value TSRMLS_CC); add_class_vars(*pce, CE_STATIC_MEMBERS(*pce), return_value TSRMLS_CC); } zend_lookup_classclass_namepce HashTablezend_hash_quick_findEG(class_table) FALSE PHP default_static_members PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 class Tipi { public static $var = 10; } Tipi::$var; $var VLD function name: (null) number of ops: 6 compiled vars: !0 = $var line # * op fetch ext return operands ------------------------------------------------------------------------------- - - 2 0 > EXT_STMT 1 NOP 6 2 EXT_STMT 3 ZEND_FETCH_CLASS :1 'Tipi' 4 FETCH_R static member 'var' 5 > RETURN 1 branch: # 0; line: 2- 6; sop: 0; eop: 5 path #1: 0, Class Tipi: [no user functions] Tipi::$var; VLDPHPTipi::$var;ZEND_FETCH_CLASS FETCH_R PHP zend_do_fetch_static_member zend_do_fetch_class ZEND_FETCH_CLASS ZEND_FETCH_CLASS_SPEC_CONST_HANDLER zend_fetch_class Zend/zend_execute_API.c zend_fetch_class zend_lookup_class_ex zend_std_get_static_property ZEND_FETCH_STATIC_MEMBER static_members zend_update_class_constants5.1 TIPI:PHP RELEASE_2011-07-29_V0.6.7 5.1 TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_function HashTablezend_function zend_initialize_class_data HashTable class Tipi{ public function t() { echo 1; } } zend_do_begin_function_declaration is_method 1 interface Ifce { private function method(); } Fatal error: Access type for interface method Ifce::method() must be omitted in zend_do_begin_function_declaration if (is_method) { if (CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE) { if ((Z_LVAL(fn_flags_znode->u.constant) & ~(ZEND_ACC_STATIC|ZEND_ACC_PUBLIC))) { zend_error(E_COMPILE_ERROR, "Access type for interface method %s::%s() must be omitted", CG(active_class_entry)->name, function_name- >u.constant.value.str.val); } Z_LVAL(fn_flags_znode->u.constant) |= ZEND_ACC_ABSTRACT; /* propagates to the rest of the parser */ } fn_flags = Z_LVAL(fn_flags_znode->u.constant); /* must be done *after* the above check */ } else { fn_flags = 0; } function_talbe static__call()__callStatic() __get() interface ifce { public static function __get(); } WarningWarning: The magic method __get() must have public visibility and cannot be static in TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_do_begin_function_declaration if (CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE) { if ((name_len == sizeof(ZEND_CALL_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)-1))) { if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { zend_error(E_WARNING, "The magic method __call() must have public visibility and cannot be static"); } } else if() { // } } class Tipi { public static function __get($var) { } } Warning: The magic method __get() must have public visibility and cannot be static in --get_class_methods() PHP 4.0.6 PHP function_table class Tipi{ public static function t() { echo 1; } } Tipi::t(); VLD number of ops: 8 compiled vars: none line # * op fetch ext return operands ------------------------------------------------------------------------------- -- TIPI:PHP RELEASE_2011-07-29_V0.6.7 2 0 > EXT_STMT 1 NOP 8 2 EXT_STMT 3 ZEND_INIT_STATIC_METHOD_CALL 'Tipi','t' 4 EXT_FCALL_BEGIN 5 DO_FCALL_BY_NAME 0 6 EXT_FCALL_END 9 7 > RETURN 1 branch: # 0; line: 2- 9; sop: 0; eop: 7 path #1: 0, Class Tipi: Function t: Finding entry points Branch analysis from position: 0 ZEND_INIT_STATIC_METHOD_CALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER zend_fetch_classEG(class_table) if (ce->get_static_method) { EX(fbc) = ce->get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); } else { EX(fbc) = zend_std_get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); } get_static_method zend_std_get_static_method PHPget_static_methodNULL zend_std_get_static_method ce->function_table zend_class_entry.function_table Zend review () PHPcreate_function PHPQuick and Dirty Ruby staticFunc(); // static instancestatic $this$this Zenderror_reportingE_STRICT E_STRICT: Strict Standards: Non-static method A::instanceFunc() should not be called statically E_STRICT PHP ”“ () PHPpublicprotectedprivate : private () protectd public public public member_modifier: TIPI:PHP RELEASE_2011-07-29_V0.6.7 T_PUBLIC { Z_LVAL($$.u.constant) = ZEND_ACC_PUBLIC; } | T_PROTECTED { Z_LVAL($$.u.constant) = ZEND_ACC_PROTECTED; } | T_PRIVATE { Z_LVAL($$.u.constant) = ZEND_ACC_PRIVATE; } PHPZend/zend_complic.h #define ZEND_ACC_PUBLIC 0x100 #define ZEND_ACC_PROTECTED 0x200 #define ZEND_ACC_PRIVATE 0x400 #define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE) 16 0x100 0001 0000 0000 0x2000010 0000 0000 0x4000100 0000 0000 ZEND_ACC_PUBLIC0 0x010x10 &:0011 0000 0000 0001 0000 0000 & 01 public PHP public method_modifiers: /* empty */ { Z_LVAL($$.u.constant) = ZEND_ACC_PUBLIC; } | non_empty_member_modifiers { $$ = $1; if (!(Z_LVAL($$.u.constant) & ZEND_ACC_PPP_MASK)) { Z_LVAL($$.u.constant) |= ZEND_ACC_PUBLIC; } } ; PHP zend_do_begin_function_declaration // ... fn_flags = Z_LVAL(fn_flags_znode->u.constant); // ... op_array.fn_flags |= fn_flags; // ... class Tipi{ private static function t() { echo 1; } } Tipi::t(); TIPI:PHP RELEASE_2011-07-29_V0.6.7 public private Fatal error: Call to private method Tipi::t() from context '' in... zend_std_get_static_method if (fbc->op_array.fn_flags & ZEND_ACC_PUBLIC) { // } else if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) { // } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) { // } fbc->op_array.fn_flags & ZEND_ACC_PUBLIC == op_array.fn_flagsflag fn_flags commonfn_flagsop_arrayfn_flags money = 10000000000; } public function getMoney() { return $this->money; } } $b = new A(); echo $b->getMoney(); // 10000 $a = new A(); $a->doSth($b); echo $b->getMoney(); // 10000000000; $adoSth()$bmoney PHP PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 static int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce TSRMLS_DC) /* {{{ */ { switch (property_info->flags & ZEND_ACC_PPP_MASK) { case ZEND_ACC_PUBLIC: return 1; case ZEND_ACC_PROTECTED: return zend_check_protected(property_info->ce, EG(scope)); case ZEND_ACC_PRIVATE: if ((ce==EG(scope) || property_info->ce == EG(scope)) && EG(scope)) { return 1; } else { return 0; } break; } return 0; } doSth()$bmoneyZend$b Zend$bpublic protectedzend_check_protected() privatedoSth() EG(scope)Ace$b $bA 1 () PHPextends parent PHP""VLD extendsZend/zend_complie.c zend_do_inheritance(): [zend_do_early_binding] --> [do_bind_inherited_class()] --> [zend_do_inheritance()] ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry TIPI:PHP RELEASE_2011-07-29_V0.6.7 *parent_ce TSRMLS_DC) { // ... final // ... /* Inherit interfaces */ zend_do_inherit_interfaces(ce, parent_ce TSRMLS_CC); /* Inherit properties */ zend_hash_merge(&ce->default_properties, &parent_ce->default_properties, (void (*)(void *)) zval_add_ref, NULL, sizeof(zval *), 0); if (parent_ce->type != ce->type) { /* User class extends internal class */ zend_update_class_constants(parent_ce TSRMLS_CC); zend_hash_apply_with_arguments(CE_STATIC_MEMBERS(parent_ce) TSRMLS_CC, (apply_func_args_t)inherit_static_prop, 1, &ce->default_static_members); } else { zend_hash_apply_with_arguments(&parent_ce->default_static_members TSRMLS_CC, (apply_func_args_t)inherit_static_prop, 1, &ce- >default_static_members); } zend_hash_merge_ex(&ce->properties_info, &parent_ce->properties_info, (copy_ctor_func_t) (ce->type & ZEND_INTERNAL_CLASS ? zend_duplicate_property_info_internal : zend_duplicate_property_info), sizeof(zend_property_info), (merge_checker_func_t) do_inherit_property_access_check, ce); zend_hash_merge(&ce->constants_table, &parent_ce->constants_table, (void (*)(void *)) zval_add_ref, NULL, sizeof(zval *), 0); zend_hash_merge_ex(&ce->function_table, &parent_ce->function_table, (copy_ctor_func_t) do_inherit_method, sizeof(zend_function), (merge_checker_func_t) do_inherit_method_check, ce); do_inherit_parent_constructor(ce); if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS && ce->type == ZEND_INTERNAL_CLASS) { ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } else if (!(ce->ce_flags & ZEND_ACC_IMPLEMENT_INTERFACES)) { /* The verification will be done in runtime by ZEND_VERIFY_ABSTRACT_CLASS */ zend_verify_abstract_class(ce TSRMLS_CC); } } zend_do_inherit_interfaces interfaces num_interfaces HashTablemerge do_inherit_parent_constructor(ce) PHP class Base { public function __construct() { echo 'Base __construct
'; } } TIPI:PHP RELEASE_2011-07-29_V0.6.7 class Foo extends Base { } $foo = new Foo(); PHPBase __construct PHP privateprotected private privatePHP protected protected do_inherit_method_check PHP class Base { private function privateMethod() { } } class Child extends Base{ public function publicMethod() { } } $c = new Child(); if (method_exists($c, 'privateMethod')) { echo 1; }else{ echo 0; } 1PHP Liskov Substitution PrincipleLSPdependence inversion principleDIP interface Animal { public function run(); } TIPI:PHP RELEASE_2011-07-29_V0.6.7 class Dog implements Animal { public function run() { echo 'dog run'; } } class Cat implements Animal{ public function run() { echo 'cat run'; } } class Context { private $_animal; public function __construct(Animal $animal) { $this->_animal = $animal; } public function run() { $this->_animal->run(); } } $dog = new Dog(); $context = new Context($dog); $context->run(); $cat = new Cat(); $context = new Context(); $context->run(); PHPPHP5 PHP << >> zend_verify_arg_type if (Z_TYPE_P(arg) == IS_OBJECT) { need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC); if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) { return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC); } } Z_TYPE_P zend_verify_arg_class_kind Zend/zend_execute.c zend_fetch_class "implement interface""be an instance of" instanceof_function TIPI:PHP RELEASE_2011-07-29_V0.6.7 1 1 0 instanceof_function ZEND_API zend_bool instanceof_function_ex(const zend_class_entry *instance_ce, const zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC) /* {{{ */ { zend_uint i; for (i=0; inum_interfaces; i++) { // if (instanceof_function(instance_ce->interfaces[i], ce TSRMLS_CC)) { return 1; } } if (!interfaces_only) { while (instance_ce) { // if (instance_ce == ce) { return 1; } instance_ce = instance_ce->parent; } } return 0; } zend_verify_arg_error zend_error PHP PHP instanceof_function zend_class_entry << >> PHP type=ZEND_ACC_INTERFACE interface_entry: T_INTERFACE { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = ZEND_ACC_INTERFACE; } ; zend_do_begin_class_declaration ce_flags new_class_entry->ce_flags |= class_token->u.EA.type; TIPI:PHP RELEASE_2011-07-29_V0.6.7 ce_flags ce_flags parent num_interfaces zend_do_implement_interface zend_do_implement_interface PHPabstractabstract PHPzend_class_entry.ce_flags ZEND_ACC_EXPLICIT_ABSTRACT_CLASS ZEND_ACC_IMPLICIT_ABSTRACT_CLASS zend_do_begin_function_declaration if (fn_flags & ZEND_ACC_ABSTRACT) { CG(active_class_entry)->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; } } ce_flagsZEND_ACC_IMPLICIT_ABSTRACT_CLASS zend_class_entry Zend new new: static int ZEND_FASTCALL ZEND_NEW_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { zend_op *opline = EX(opline); zval *object_zval; zend_function *constructor; if (EX_T(opline->op1.u.var).class_entry->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT _CLASS)) { TIPI:PHP RELEASE_2011-07-29_V0.6.7 char *class_type; if (EX_T(opline->op1.u.var).class_entry->ce_flags & ZEND_ACC_INTERFACE) { class_type = "interface"; } else { class_type = "abstract class"; } zend_error_noreturn(E_ERROR, "Cannot instantiate %s %s", class_type, EX_T(opline->op1.u.var).class_entry->name); } // ... } PHP: PHPPHP"__" "__" :__autoload() __autoload()PHP __autoload() __construct() __destuct() PHP () PHP_zend_class_entry : _zend_class_entry; ZendVM; ; _zend_class_entry_zend_class_entry TIPI:PHP RELEASE_2011-07-29_V0.6.7 struct _zend_class_entry { ... // __construct union _zend_function *constructor; // __destruct union _zend_function *destructor; // __clone union _zend_function *clone; union _zend_function *__get; union _zend_function *__set; union _zend_function *__unset; union _zend_function *__isset; union _zend_function *__call; union _zend_function *__callstatic; union _zend_function *__tostring; // union _zend_function *serialize_func; // union _zend_function *unserialize_func; ... } Zend VM __construct __construct JAVAPHP ”“ **__construct** newZEND_NEW_SPEC_HANDLER Zend VM newOPCODEGDB #0 ZEND_NEW_SPEC_HANDLER (execute_data=0x100d00080) at zend_vm_execute.h:461 #1 0x000000010041c1f0 in execute (op_array=0x100a1fd60) at zend_vm_execute.h:107 #2 0x00000001003e9394 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /Volumes/DEV/C/php-5.3.4/Zend/zend.c:1194 #3 0x0000000100368031 in php_execute_script (primary_file=0x7fff5fbff890) at /Volumes/DEV/C/php-5.3.4/main/main.c:2265 #4 0x00000001004d4b5c in main (argc=2, argv=0x7fff5fbffa30) at /Volumes/DEV/C/php-5.3.4/sapi/cli/php_cli.c:1193 newnew ZEND_NEW_SPEC_HANDLER ZEND_NEW_SPEC_HANDLERZend VM __construct ... constructor = Z_OBJ_HT_P(object_zval)->get_constructor(object_zval TSRMLS_CC); if (constructor == NULL){ ... } else { ... } TIPI:PHP RELEASE_2011-07-29_V0.6.7 //get_constructor ZEND_API union _zend_function *zend_std_get_constructor(zval *object TSRMLS_DC) { zend_object *zobj = Z_OBJ_P(object); zend_function *constructor = zobj->ce->constructor; if(constructor){ ... } else { ...} ... } ZendVMzend_object->ce->constructor Z_OBJ_P(zval); Z_OBJ_Pzvalzend_object __constructZEND_NEW_SPEC_HANDLER EX(called_scope) ZEND_VM_NEXT_OPCODE();opline__construct op_array__construct [c] EX(object) = object_zval; EX(fbc) = constructor; EX(called_scope) = EX_T(opline->op1.u.var).class_entry; ZEND_VM_NEXT_OPCODE(); __destruct __destruct __destruct __destruct // #0 zend_call_function () at /..//php-5.3.4/Zend/zend_execute_API.c:767 #1 zend_call_method () at /..//php-5.3.4/Zend/zend_interfaces.c:97 #2 zend_objects_destroy_object () at /..//php-5.3.4/Zend/zend_objects.c:112 #3 zend_objects_store_del_ref_by_handle_ex () at /..//php- 5.3.4/Zend/zend_objects_API.c:206 #4 zend_objects_store_del_ref () at /..//php-5.3.4/Zend/zend_objects_API.c:172 #5 _zval_dtor_func () at /..//php-5.3.4/Zend/zend_variables.c:52 #6 _zval_dtor () at zend_variables.h:35 #7 _zval_ptr_dtor () at /..//php-5.3.4/Zend/zend_execute_API.c:443 #8 _zval_ptr_dtor_wrapper () at /..//php-5.3.4/Zend/zend_variables.c:189 #9 zend_hash_apply_deleter () at /..//php-5.3.4/Zend/zend_hash.c:614 #10 zend_hash_reverse_apply () at /..//php-5.3.4/Zend/zend_hash.c:763 #11 shutdown_destructors () at /..//php-5.3.4/Zend/zend_execute_API.c:226 #12 zend_call_destructors () at /..//php-5.3.4/Zend/zend.c:874 #13 php_request_shutdown () at /..//php-5.3.4/main/main.c:1587 #14 main () at /..//php-5.3.4/sapi/cli/php_cli.c:1374 __destructzend_objects_destroy_object ZendVMphp_request_shutdown __destruct TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_objects_destroy_object__construct ZendVMzend_object->ce- >destructorzend_call_method__destruct __destruct__construct__destructZendVM zend_call_function __call__callStatic __call; __callStatic; __call__callStatic zend_do_fcall_common_helper_SPEC() __call [ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER]-> [zend_do_fcall_common_helper_SPEC]-> [zend_std_call_user_call]-> [zend_call_method]- >[zend_call_function] zend_do_fcall_common_helper_SPEC zend_call_function__call __callStatic [ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER]-> [zend_do_fcall_common_helper_SPEC]-> [zend_std_callstatic_user_call]-> [zend_call_method]->[zend_call_function] zend_do_fcall_common_helper_SPEC zend_call_function__callStatic PHP _zend_class_entry __set zend_std_call_setter() Zend/zend_object_handlers.c __get zend_std_call_getter() Zend/zend_object_handlers.c __isset zend_std_call_issetter() Zend/zend_object_handlers.c __unset zend_std_call_unsetter() Zend/zend_object_handlers.c __sleep php_var_serialize_intern() ext/standard/var.c __wakeup php_var_unserialize() ext/standard/var_unserializer.c __toString zend_std_cast_object_tostring() Zend/zend_object_handlers.c __invoke ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER() Zend/zend_vm_execute.h TIPI:PHP RELEASE_2011-07-29_V0.6.7 __set_state php_var_export_ex() ext/standard/var.c __clone ZEND_CLONE_SPEC_CV_HANDLER() Zend/zend_vm_execute.h PHP PHP 5.3.0PHP “” “” static:: ”“ staticstatic: function_call: ...// | class_name T_PAAMAYIM_NEKUDOTAYIM T_STRING '(' { $4.u.opline_num = zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } function_call_parameter_list ')' { zend_do_end_function_call($4.u.opline_num?NULL:&$3, &$$, &$6, $4.u.opline_num, $4.u.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} ...// class_name: T_STATIC { $$.op_type = IS_CONST; ZVAL_STRINGL(&$$.u.constant, "static", sizeof("static")-1, 1);} static(class_name)zend_do_begin_class_member_function_call class_nameop_typeIS_CONSTzend_get_class_fetch_type ZEND_FETCH_CLASS_STATIC extended_value ZEND_FETCH_CLASSZEND_FETCH_CLASS_SPEC_CONST_HANDLER (fetch_type)zend_fetch_class EX_T(opline->result.u.var).class_entry = zend_fetch_class(Z_STRVAL_P(class_name), Z_STRLEN_P(class_name), opline->extended_value TSRMLS_CC); PHP PHP JavaScriptArrayNumber PHPDirectorystdClassExceptionPDO PHP PHPselfstaticparentselfparentstatic PHP5.3 PHPstatic: TIPI:PHP RELEASE_2011-07-29_V0.6.7 static (PHP5.3)(::) ""PHPself parentstatic static : string(4) "self" error_reporting(E_ALL): “ Notice: Use of undefined constant self - assumed 'self'“ PHPself ”self" NOTICEself opcode = ZEND_FETCH_CLASS; if (class_name->op_type == IS_CONST) { int fetch_type; fetch_type = zend_get_class_fetch_type(class_name- >u.constant.value.str.val, class_name->u.constant.value.str.len); switch (fetch_type) { case ZEND_FETCH_CLASS_SELF: case ZEND_FETCH_CLASS_PARENT: case ZEND_FETCH_CLASS_STATIC: SET_UNUSED(opline->op2); opline->extended_value = fetch_type; zval_dtor(&class_name->u.constant); break; default: zend_resolve_class_name(class_name, &opline->extended_value, 0 TSRMLS_CC); opline->op2 = *class_name; break; } } else { opline->op2 = *class_name; } // ... } fetch_typezend_get_class_fetch_type() int zend_get_class_fetch_type(const char *class_name, uint class_name_len) /* {{{ */ { if ((class_name_len == sizeof("self")-1) && !memcmp(class_name, "self", sizeof("self")-1)) { return ZEND_FETCH_CLASS_SELF; } else if ((class_name_len == sizeof("parent")-1) && !memcmp(class_name, "parent", sizeof("parent")-1)) { return ZEND_FETCH_CLASS_PARENT; } else if ((class_name_len == sizeof("static")-1) && !memcmp(class_name, "static", sizeof("static")-1)) { return ZEND_FETCH_CLASS_STATIC; } else { return ZEND_FETCH_CLASS_DEFAULT; } } Zendselfparent opcodeZEND_FETCH_CLASS: zend_class_entry *zend_fetch_class(const char *class_name, uint class_name_len, int fetch_type TSRMLS_DC) /* {{{ */ { zend_class_entry **pce; int use_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) == 0; int silent = (fetch_type & ZEND_FETCH_CLASS_SILENT) != 0; fetch_type &= ZEND_FETCH_CLASS_MASK; TIPI:PHP RELEASE_2011-07-29_V0.6.7 check_fetch_type: switch (fetch_type) { case ZEND_FETCH_CLASS_SELF: if (!EG(scope)) { zend_error(E_ERROR, "Cannot access self:: when no class scope is active"); } return EG(scope); case ZEND_FETCH_CLASS_PARENT: if (!EG(scope)) { zend_error(E_ERROR, "Cannot access parent:: when no class scope is active"); } if (!EG(scope)->parent) { zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent"); } return EG(scope)->parent; case ZEND_FETCH_CLASS_STATIC: if (!EG(called_scope)) { zend_error(E_ERROR, "Cannot access static:: when no class scope is active"); } return EG(called_scope); case ZEND_FETCH_CLASS_AUTO: { fetch_type = zend_get_class_fetch_type(class_name, class_name_len); if (fetch_type!=ZEND_FETCH_CLASS_DEFAULT) { goto check_fetch_type; } } break; } if (zend_lookup_class_ex(class_name, class_name_len, use_autoload, &pce TSRMLS_CC) == FAILURE) { if (use_autoload) { if (!silent && !EG(exception)) { if (fetch_type == ZEND_FETCH_CLASS_INTERFACE) { zend_error(E_ERROR, "Interface '%s' not found", class_name); } else { zend_error(E_ERROR, "Class '%s' not found", class_name); } } } } return NULL; } return *pce; } selfEG(scope)EG(scope) parentEG(scope)->parentstatic EG(called_scope) EGEG: struct _zend_executor_globals { // ... zend_class_entry *scope; zend_class_entry *called_scope; /* Scope of the calling class */ // ... } TIPI:PHP RELEASE_2011-07-29_V0.6.7 struct _zend_class_entry { char type; char *name; zend_uint name_length; struct _zend_class_entry *parent; } #define struct _zend_class_entry zend_class_entry zend_class_entryPHPzend_class_entryparent EGcalled_scopecalled_scope : >PHPzend_object_value TIPI:PHP RELEASE_2011-07-29_V0.6.7 typedef struct _zend_object_value { zend_object_handle handle; // unsigned intEG(objects_store).object_buckets zend_object_handlers *handlers; } zend_object_value; PHPEG(objects_store) handleobject_bucketsPHP PHPhandle typedef struct _zend_object { zend_class_entry *ce; HashTable *properties; HashTable *guards; /* protects from __get/__set ... recursion */ } zend_object; cepropertiesHashTable zend_object_valuehandlers zend_object_handlersZend/zend_object_handlers.h PHP new ZEND_NEW_SPEC_HANDLER ZEND_NEW_SPEC_HANDLER ce_flagsZEND_ACC_INTERFACEZEND_ACC_IMPLICIT_ABSTRACT_CLASS ZEND_ACC_EXPLICIT_ABSTRACT_CLASS ZVAL object_init_ex [object_init_ex()] --> [_object_init_ex()] --> [_object_and_properties_init()] _object_and_properties_init zvalIS_OBJECT Z_TYPE_P(arg) = IS_OBJECT; zend_object zend_objects_new ZEND_API zend_object_value zend_objects_new(zend_object **object, zend_class_entry *class_type TSRMLS_DC) { TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_object_value retval; *object = emalloc(sizeof(zend_object)); (*object)->ce = class_type; retval.handle = zend_objects_store_put(*object, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) zend_objects_free_object_storage, NULL TSRMLS_CC); retval.handlers = &std_object_handlers; (*object)->guards = NULL; return retval; } zend_objects_new zend_objects_store_put std_object_handlers Zend/zend_object_handles.c -- PHP EG(objects_store) PHP zend_objects_store; typedef struct _zend_objects_store { zend_object_store_bucket *object_buckets; zend_uint top; zend_uint size; int free_list_head; } zend_objects_store; typedef struct _zend_object_store_bucket { zend_bool destructor_called; zend_bool valid; union _store_bucket { struct _store_object { void *object; zend_objects_store_dtor_t dtor; zend_objects_free_object_storage_t free_storage; zend_objects_store_clone_t clone; const zend_object_handlers *handlers; zend_uint refcount; gc_root_buffer *buffered; } obj; struct { int next; } free_list; } bucket; } zend_object_store_bucket; PHPAPIZend/zend_objects_API.c zend_objects_store_init TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_objects_store_init [php_request_startup] --> [php_start_sapi] --> [zend_activate] --> [init_executor] 1024zend_object_store_bucket zend_objects_store_destroyefree zend_objects_store_mark_destructed zend_objects_store_free_object_storage zend_objects_store_put APIbucket zend_objects_store_get_refcount zend_objects_store_add_ref1 zend_objects_store_add_ref_by_handle handle1 zend_objects_store_del_ref 1 zend_objects_store_del_ref_by_handle_ex handle1 1 zend_objects_store_clone_obj APIbucket zend_object_store_get_object bucket zend_object_store_get_object_by_handle bucket properties read_property zend_std_read_property; write_property zend_std_write_property dom /* {{{ PHP_MINIT_FUNCTION(dom) */ PHP_MINIT_FUNCTION(dom) { zend_class_entry ce; memcpy(&dom_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); dom_object_handlers.read_property = dom_read_property; dom_object_handlers.write_property = dom_write_property; // ... } domread_property zend_std_read_property properties __get type=BP_VAR_IS &EG(uninitialized_zval_ptr) zend_std_write_property TIPI:PHP RELEASE_2011-07-29_V0.6.7 properties __set properties HashTable NULLSPL PHP_MINIT_FUNCTION(spl_iterators) { // ... memcpy(&spl_handlers_dual_it, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); spl_handlers_dual_it.get_method = spl_dual_it_get_method; /*spl_handlers_dual_it.call_method = spl_dual_it_call_method;*/ spl_handlers_dual_it.clone_obj = NULL; // ... } PHP class Tipi { public function t() { echo 'tipi'; } } $obj = new Tipi(); $obj->t(); t$obj VLD ZEND_INIT_METHOD_CALL CVCONST ZEND_INIT_METHOD_CALL_SPEC_CV_CONST_HANDLER Call to a member function t on a non- object get_method this TIPI:PHP RELEASE_2011-07-29_V0.6.7 get_methodget_method Zend/zend_object_handlers.czend_std_get_method zend_std_get_method zobj->ce->function_table __callzend_get_user_call_function NULL __call protected fbc Namespaceidentifier context PHP 1. PHP//// 2. () PHP5.3.0 u.constant), Z_STRLEN(class_name->u.constant)); } tipi\Exceptiontipi\ zend_do_build_namespace_name zend_do_build_full_name zend_do_begin_function_call TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_resolve_non_class_name zend_resolve_non_class_name 1. "\" \strlen\\tipi\Exception 2. HashTable 3. / unix PHP PHP PHP use zend_do_use CG(current_import)HashTable zend_do_useHashTable PHP PHPJava/C++ PHP final PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 TIPI:PHP RELEASE_2011-07-29_V0.6.7 CPU CPU CPUI/O CPU C/C++ Java, :PHP/Python/Ruby PHP PHP PHP5.3 PHP5.3PHP PHP 3DWeb CPU TIPI:PHP RELEASE_2011-07-29_V0.6.7 (MMU) CPU malloc Web CPU PHPPHPPHP PHP : Fatal error: Allowed memory size of X bytes exhausted (tried to allocate Y bytes) PHP PHP32M, 32Mphp.ini : memory_limit = 32M phpPHPini_set() : >ZEND_MM_ALIGNMENT_LOG2)- (ZEND_MM_ALIGNED_MIN_HEADER_SIZE>>ZEND_MM_ALIGNMENT_LOG2)) ZEND_MM_ALIGNMENT8ZEND_MM_ALIGNMENT 8ZEND_MM_ALIGNED_MIN_HEADER_SIZE=16 true_size=256((256>>3)- (16>>3))= 30 ZEND_MM_BUCKET_INDEXZEND_MM_SMALL_SIZE ZEND_MM_SMALL_SIZE 272Byteindex31 free_buckets PHPfree_buckets free_buckets[] free_block zend_mm_init 0 p = ZEND_MM_SMALL_FREE_BUCKET(heap, 0); for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) { p->next_free_block = p; p->prev_free_block = p; p = (zend_mm_free_block*)((char*)p + sizeof(zend_mm_free_block*) * 2); heap->large_free_buckets[i] = NULL; } free_bitmap free_buckets6.2 TIPI:PHP RELEASE_2011-07-29_V0.6.7 6.2 free_buckets free_buckets index indexindex free_bucketslarge_free_buckets large_free_bucketshashfree_buckets hashhash #define ZEND_MM_LARGE_BUCKET_INDEX(S) zend_mm_high_bit(S) static inline unsigned int zend_mm_high_bit(size_t _size) { ..// unsigned int n = 0; while (_size != 0) { _size = _size >> 1; n++; TIPI:PHP RELEASE_2011-07-29_V0.6.7 n++; } return n-1; } hashsizesize1-1 size512Byte large_free_buckets 51210000000001 index0 K0 K C large_free_buckets6.3 TIPI:PHP RELEASE_2011-07-29_V0.6.7 6.3 large_free_buckets heaprest_buckets heap->rest_buckets[0] ZEND_MM_SEG_SIZEheapblock_size ZEND_MM_ALIGNED_SEGMENT_SIZE(8)ZEND_MM_ALIGNED_HEADER_SIZE(8) rest_buckts[0] free_bucket hash heap PHP "Storage Manager" /* Heaps with user defined storage */ typedef struct _zend_mm_storage zend_mm_storage; typedef struct _zend_mm_segment { size_t size; struct _zend_mm_segment *next_segment; } zend_mm_segment; TIPI:PHP RELEASE_2011-07-29_V0.6.7 typedef struct _zend_mm_mem_handlers { const char *name; zend_mm_storage* (*init)(void *params); // void (*dtor)(zend_mm_storage *storage); // void (*compact)(zend_mm_storage *storage); zend_mm_segment* (*_alloc)(zend_mm_storage *storage, size_t size); // zend_mm_segment* (*_realloc)(zend_mm_storage *storage, zend_mm_segment *ptr, size_t size); // void (*_free)(zend_mm_storage *storage, zend_mm_segment *ptr); // } zend_mm_mem_handlers; struct _zend_mm_storage { const zend_mm_mem_handlers *handlers; // void *data; }; name6.1PHP4 : mallocwin32mmap_anonmmap_zeromalloc ZEND_WIN32windowsHeapAlloc PHP The Zend MM can be tweaked using ZEND_MM_MEM_TYPE and ZEND_MM_SEG_SIZE environment variables. Default values are “malloc” and “256K”. Dependent on target system you can also use “mmap_anon”, “mmap_zero” and “win32″ storage managers. 4zend_mm_mem_handlers /* mmap */ # define ZEND_MM_MEM_MMAP_ANON_DSC {"mmap_anon", zend_mm_mem_dummy_init, zend_mm_mem_dummy_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_mmap_anon_alloc, zend_mm_mem_mmap_realloc, zend_mm_mem_mmap_free} /* mmap /dev/zero*/ # define ZEND_MM_MEM_MMAP_ZERO_DSC {"mmap_zero", zend_mm_mem_mmap_zero_init, zend_mm_mem_mmap_zero_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_mmap_zero_alloc, zend_mm_mem_mmap_realloc, zend_mm_mem_mmap_free} /* HeapAlloc windows VirtualAlloc() to allocate memoryHeapAlloc*/ # define ZEND_MM_MEM_WIN32_DSC {"win32", zend_mm_mem_win32_init, zend_mm_mem_win32_dtor, zend_mm_mem_win32_compact, zend_mm_mem_win32_alloc, zend_mm_mem_win32_realloc, zend_mm_mem_win32_free} /* malloc ZEND_WIN32win32*/ # define ZEND_MM_MEM_MALLOC_DSC {"malloc", zend_mm_mem_dummy_init, zend_mm_mem_dummy_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_malloc_alloc, zend_mm_mem_malloc_realloc, zend_mm_mem_malloc_free} static const zend_mm_mem_handlers mem_handlers[] = { #ifdef HAVE_MEM_WIN32 ZEND_MM_MEM_WIN32_DSC, TIPI:PHP RELEASE_2011-07-29_V0.6.7 #endif #ifdef HAVE_MEM_MALLOC ZEND_MM_MEM_MALLOC_DSC, #endif #ifdef HAVE_MEM_MMAP_ANON ZEND_MM_MEM_MMAP_ANON_DSC, #endif #ifdef HAVE_MEM_MMAP_ZERO ZEND_MM_MEM_MMAP_ZERO_DSC, #endif {NULL, NULL, NULL, NULL, NULL, NULL} }; win32PHP ZEND_MM_MEM_WIN32_DSCmem_handlers USE_ZEND_ALLOC malloc emalloc malloc-type emalloc Zend PHP free_buckets large_free_buckets rest_buckets ZendMM ZendMMheap ZendMM PHP phpemalloc PHP PHPemallocmallocPHPZendMM ZendMMheap ZEND_ASSIGN_SPEC_CV_CONST_HANDLER (......) -> ALLOC_ZVAL(......) -> ZEND_FAST_ALLOC(......) -> emalloc (......) -> _emalloc(......) TIPI:PHP RELEASE_2011-07-29_V0.6.7 -> _zend_mm_alloc_int(.....) void *_emalloc _emalloc ZendMM heapZendMM _zend_mm_heap_malloc ; _malloc mallocwin32mmap_anonmmap_zero; ZendMMenable-debug ZendMM_zend_mm_alloc_int: 6.1 PHP ZendMM 1. memory_limit Out of Memory; TIPI:PHP RELEASE_2011-07-29_V0.6.7 2. fastcache()5; 3. ZendMMheap, ZendMM ZEND_MM_MAX_SMALL_SIZE largesmallsmall zend_mm_low_bit mm_heapfree_buckets large zend_mm_search_large_block“” _zend_mm_heap->large_free_buckets rest_buckets block5; 4. 3 ZEND_MM_STORAGE_ALLOC ZEND_MM_SEG_SIZE6; 5. zend_mm_remove_from_free_listblockzend_mm_free_block; 6. zend_mm_heaplarge_free_buckets peaksize; 7. ; PHPPHPPHPweb “”PHP5.2 PHP5.3 PHP5.3GC ZendMMunset ZendMM small,large,free _efree_efreecache ZEND_MM_SMALL_SIZEcache ZEND_MM_CACHE_SIZE zend_mm_blockmm_heap->cache cache zend_mm_block *mm_block; // zend_mm_block *next_block; ... next_block = ZEND_MM_BLOCK_AT(mm_block, size); if (ZEND_MM_IS_FREE_BLOCK(next_block)) { zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block); size += ZEND_MM_FREE_BLOCK_SIZE(next_block); } if (ZEND_MM_PREV_BLOCK_IS_FREE(mm_block)) { mm_block = ZEND_MM_PREV_BLOCK(mm_block); zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) mm_block); size += ZEND_MM_FREE_BLOCK_SIZE(mm_block); } if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && TIPI:PHP RELEASE_2011-07-29_V0.6.7 ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(mm_block, size))) { zend_mm_del_segment(heap, (zend_mm_segment *) ((char *)mm_block - ZEND_MM_ALIGNED_SEGMENT_SIZE)); } else { ZEND_MM_BLOCK(mm_block, ZEND_MM_FREE_BLOCK, size); zend_mm_add_to_free_list(heap, (zend_mm_free_block *) mm_block); } mm_blockzend_mm_heap free; free; zend_mm_add_to_free_list zend_mm_del_segment ZendMMzend_mm_heap GC PythonPHP EiffelC#Ruby 2060MITLisp 2090Java PHP PHP PHP5.3PHP0 PHP PHP /PHP PHP PHPPHP5.3 PHP5.3 (Concurrent Cycle Collection in Reference Counted Systems) PHP(Collecting Cycles) PHPPHP5.3 PHP PHP PHP5.3 TIPI:PHP RELEASE_2011-07-29_V0.6.7 struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; }; PHP5.3refcountis_ref__gc PHP ALLOC_ZVALPHP5.3PHP emalloc ALLOC_ZVALzval_gc_info /* The following macroses override macroses from zend_alloc.h */ #undef ALLOC_ZVAL #define ALLOC_ZVAL(z) \ do { \ (z) = (zval*)emalloc(sizeof(zval_gc_info)); \ GC_ZVAL_INIT(z); \ } while (0) zend_gc.hzend.h749#include “zend_gc.h” 237 zend_alloc.hALLOC_ZVAL zval_gc_info typedef struct _zval_gc_info { zval z; union { gc_root_buffer *buffered; struct _zval_gc_info *next; } u; } zval_gc_info; ZVALzvalzval zval_gc_infozvalzval :u ugc_root_bufferbufferedzval_gc_infonext zval_gc_info ALLOC_ZVALGC_ZVAL_INIT zvalzval_gc_info zval_gc_infoubufferedNULL NULL PHPzval zval_gc_infozval PHPPHP5.3 zend.enable_gc php.ini php.inizend.enable_gc=0zend.enable_gc=off php.inizend.enable_gc gc_enable()/gc_disable()/ PHPgc_collect_cycles() TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHP zend.c static ZEND_INI_MH(OnUpdateGCEnabled) /* {{{ */ { OnUpdateBool(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); if (GC_G(gc_enabled)) { gc_init(TSRMLS_C); } return SUCCESS; } /* }}} */ ZEND_INI_BEGIN() ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting) STD_ZEND_INI_BOOLEAN("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals) #ifdef ZEND_MULTIBYTE STD_ZEND_INI_BOOLEAN("detect_unicode", "1", ZEND_INI_ALL, OnUpdateBool, detect_unicode, zend_compiler_globals, compiler_globals) #endif ZEND_INI_END() zend.enable_gcZEND_INI_MH(OnUpdateGCEnabled) GC_G(gc_enabled)gc_init gc_init zend/zend_gc.c 121 malloc10000gc_root_buffer 10000 GC_ROOT_BUFFER_MAX_ENTRIESPHP gc_initgc_resetgc (gc_runs)gc(collected)0 typedef struct _zend_gc_globals { zend_bool gc_enabled; /* */ zend_bool gc_active; /* */ gc_root_buffer *buf; /* 10000preallocated arrays of buffers */ gc_root_buffer roots; /* list of possible roots of cycles */ gc_root_buffer *unused; /* (list of unused buffers) */ gc_root_buffer *first_unused; /* pointer to first unused buffer */ gc_root_buffer *last_unused; /* (pointer to last unused buffer) */ zval_gc_info *zval_to_free; /* zval temporaryt list of zvals to free */ zval_gc_info *free_list; /* */ zval_gc_info *next_to_free; /* */ zend_uint gc_runs; /* gc */ TIPI:PHP RELEASE_2011-07-29_V0.6.7 zend_uint collected; /* gc */ // ... } unset zval_dtorzval_ptr_dtor PHPunset ZEND_UNSETZend/zend_vm_execute.h zval_ptr_dtor Zend/zend_variables.h _zval_ptr_dtor Zend/zend_execute_API.c 424 ZEND_API void _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) /* {{{ */ { #if DEBUG_ZEND>=2 printf("Reducing refcount for %x (%x): %d->%d\n", *zval_ptr, zval_ptr, Z_REFCOUNT_PP(zval_ptr), Z_REFCOUNT_PP(zval_ptr) - 1); #endif Z_DELREF_PP(zval_ptr); if (Z_REFCOUNT_PP(zval_ptr) == 0) { TSRMLS_FETCH(); if (*zval_ptr != &EG(uninitialized_zval)) { GC_REMOVE_ZVAL_FROM_BUFFER(*zval_ptr); zval_dtor(*zval_ptr); efree_rel(*zval_ptr); } } else { TSRMLS_FETCH(); if (Z_REFCOUNT_PP(zval_ptr) == 1) { Z_UNSET_ISREF_PP(zval_ptr); } GC_ZVAL_CHECK_POSSIBLE_ROOT(*zval_ptr); } } /* }}} */ zval 10 10 GC_ZVAL_CHECK_POSSIBLE_ROOT gc_zval_check_possible_root gc_zval_possible_root ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC) { TIPI:PHP RELEASE_2011-07-29_V0.6.7 if (UNEXPECTED(GC_G(free_list) != NULL && GC_ZVAL_ADDRESS(zv) != NULL && GC_ZVAL_GET_COLOR(zv) == GC_BLACK) && (GC_ZVAL_ADDRESS(zv) < GC_G(buf) || GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) { /* The given zval is a garbage that is going to be deleted by * currently running GC */ return; } if (zv->type == IS_OBJECT) { GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv); return; } GC_BENCH_INC(zval_possible_root); if (GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) { GC_ZVAL_SET_PURPLE(zv); if (!GC_ZVAL_ADDRESS(zv)) { gc_root_buffer *newRoot = GC_G(unused); if (newRoot) { GC_G(unused) = newRoot->prev; } else if (GC_G(first_unused) != GC_G(last_unused)) { newRoot = GC_G(first_unused); GC_G(first_unused)++; } else { if (!GC_G(gc_enabled)) { GC_ZVAL_SET_BLACK(zv); return; } zv->refcount__gc++; gc_collect_cycles(TSRMLS_C); zv->refcount__gc--; newRoot = GC_G(unused); if (!newRoot) { return; } GC_ZVAL_SET_PURPLE(zv); GC_G(unused) = newRoot->prev; } newRoot->next = GC_G(roots).next; newRoot->prev = &GC_G(roots); GC_G(roots).next->prev = newRoot; GC_G(roots).next = newRoot; GC_ZVAL_SET_ADDRESS(zv, newRoot); newRoot->handle = 0; newRoot->u.pz = zv; GC_BENCH_INC(zval_buffered); GC_BENCH_INC(root_buf_length); GC_BENCH_PEAK(root_buf_peak, root_buf_length); } } } gc_zval_check_possible_root gc_zval_possible_root GC_ZOBJ_CHECK_POSSIBLE_ROOT TIPI:PHP RELEASE_2011-07-29_V0.6.7 zval gc_zval_possible_rootgc_collect_cycles 628 B 1 “1”1 629 C 0 0 11 630 D (zval roots) [gc_collect_cycles() -> gc_collect_roots() -> zval_collect_white() ] zval_to_free PHP GC_WHITE GC_PURPLE GC_GREY refcount GC_BLACK #define GC_COLOR 0x03 #define GC_BLACK 0x00 #define GC_WHITE 0x01 #define GC_GREY 0x02 #define GC_PURPLE 0x03 #define GC_ADDRESS(v) \ ((gc_root_buffer*)(((zend_uintptr_t)(v)) & ~GC_COLOR)) #define GC_SET_ADDRESS(v, a) \ (v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & GC_COLOR) | ((zend_uintptr_t)(a)))) #define GC_GET_COLOR(v) \ (((zend_uintptr_t)(v)) & GC_COLOR) #define GC_SET_COLOR(v, c) \ (v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & ~GC_COLOR) | (c))) #define GC_SET_BLACK(v) \ (v) = ((gc_root_buffer*)(((zend_uintptr_t)(v)) & ~GC_COLOR)) #define GC_SET_PURPLE(v) \ (v) = ((gc_root_buffer*)(((zend_uintptr_t)(v)) | GC_PURPLE)) TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHP Cache Cache CPU “” PHP “” PHP PHP ZEND_MM_CACHEZend/zend_alloc.c 400 size_tsize_t 4PHPPHP128K(32 * 4 * 1024) #define ZEND_MM_NUM_BUCKETS (sizeof(size_t) << 3) #define ZEND_MM_CACHE 1 #define ZEND_MM_CACHE_SIZE (ZEND_MM_NUM_BUCKETS * 4 * 1024) ZEND_MM_CACHE0PHP ZEND_MM_CACHE1 struct _zend_mm_heap { #if ZEND_MM_CACHE unsigned int cached; // zend_mm_free_block *cache[ZEND_MM_NUM_BUCKETS]; // #endif cachedzend_mm_free_block zend_mm_init #if ZEND_MM_CACHE heap->cached = 0; TIPI:PHP RELEASE_2011-07-29_V0.6.7 memset(heap->cache, 0, sizeof(heap->cache)); #endif 0 PHP ZEND_MM_MAX_SMALL_SIZE(272) 100Byte PHP _zend_mm_free_int #if ZEND_MM_CACHE if (EXPECTED(ZEND_MM_SMALL_SIZE(size)) && EXPECTED(heap->cached < ZEND_MM_CACHE_SIZE)) { size_t index = ZEND_MM_BUCKET_INDEX(size); zend_mm_free_block **cache = &heap->cache[index]; ((zend_mm_free_block*)mm_block)->prev_free_block = *cache; *cache = (zend_mm_free_block*)mm_block; heap->cached += size; ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_CACHED); #if ZEND_MM_CACHE_STAT if (++heap->cache_stat[index].count > heap- >cache_stat[index].max_count) { heap->cache_stat[index].max_count = heap->cache_stat[index].count; } #endif return; } #endif zend_mm_free_cache Zend/zend_alloc.c909 ZEND_MM_CACHE ZEND_MM_CACHE_STAT Copy-On-Write Copy-on-WriteCOW COW*nix C++STL PHPCOW COW TIPI:PHP RELEASE_2011-07-29_V0.6.7 //---------- foo: (refcount=1, is_ref=0)=1 foo: (refcount=2, is_ref=0)=1 foo: (refcount=1, is_ref=0)=1 $foo$foo1 $foo $barPHP$bar $foo$bar “ ” refcount=2; $bar PHP 2 $foo refcount=1 COW COW xdebug_debug_zval()xdebug zend xdebugdebug_zval_dump() http://www.php.net/manual/zh/function.debug-zval-dump.php zend_assign_to_variable()Zend/zend_execute.c if (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) { ALLOC_ZVAL(variable_ptr); *variable_ptr_ptr = variable_ptr; *variable_ptr = *value; Z_SET_REFCOUNT_P(variable_ptr, 1); zval_copy_ctor(variable_ptr); } else { *variable_ptr_ptr = value; Z_ADDREF_P(value); } &, valuezval_value.refcount1 TIPI:PHP RELEASE_2011-07-29_V0.6.7 zvalZend/zend.h typedef struct _zval_struct zval; ... struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; }; PHPrefcount__gcis_ref__gc “” PHP PHP& PHP unset() unset $o_o$tipi Co_o PHP unset() tipihashtable 1 $tipi PHPC BUGPHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 2 $tipi$foo, & PHP PHPPHP PHP PHP5.3PHP PHP PHP PHP TIPI:PHP RELEASE_2011-07-29_V0.6.7 A: PHPZend API TIPI:PHP RELEASE_2011-07-29_V0.6.7 C VLD VLD(Vulcan Logic Dumper)Zend PHP ZendPHP Derick Rethans, VLDXDebug VLD WinVC6.0dll (VC6.0 VLD) *nixconfigue,make,make installGoogle ,t.php $a = 10; echo $a; VLD php -dvld.active=1 t.php -dvld.active=1VLDVLDCMD Branch analysis from position: 0 Return found filename: D:\work\xampp\xampp\php\t.php function name: (null) number of ops: 5 compiled vars: !0 = $a line # * op fetch ext return operands ------------------------------------------------------------------------------- -- 2 0 > EXT_STMT 1 ASSIGN !0, 10 3 2 EXT_STMT 3 ECHO !0 4 4 > RETURN 1 branch: # 0; line: 2- 4; sop: 0; eop: 4 path #1: 0, 10 VLDPHP Branch analysis from position Return found filename function name VLD number of ops compiled vars PHP5 TIPI:PHP RELEASE_2011-07-29_V0.6.7 PHPIS_CV op list -dvld.activeVLD-dvld.verbosity php -dvld.active=1 -dvld.verbosity=3 t.php -dvld.verbosity=3VLD Finding entry points Branch analysis from position: 0 Add 0 Add 1 Add 2 Add 3 Add 4 Return found filename: D:\work\xampp\xampp\php\t.php function name: (null) number of ops: 5 compiled vars: !0 = $a line # * op fetch ext return operands ------------------------------------------------------------------------------- - - 2 0 > EXT_STMT RES[ IS_UNUSED ] OP1[ IS_UNUSED ] OP2[ IS_UNUSED ] 1 ASSIGN OP1[IS_CV !0 ] OP2[ , IS_CONST (0) 10 ] 3 2 EXT_STMT RES[ IS_UNUSED ] OP1[ IS_UNUSED ] OP2[ IS_UNUSED ] 3 ECHO OP1[IS_CV !0 ] 4 > RETURN OP1[IS_CONST (0) 1 ] branch: # 0; line: 2- 3; sop: 0; eop: 4 path #1: 0, 10 -dvld.verbosity=3Add IS_CVIS_CONST PHP$a = 10; 10IS_CONST$a IS_CV PHP-dvld.execute=0 php -dvld.active=1 -dvld.execute=0 t.php 10 VLD.dot php -dvld.active=1 -dvld.save_dir='D:\tmp' -dvld.save_paths=1 - TIPI:PHP RELEASE_2011-07-29_V0.6.7 dvld.dump_paths=1 t.php D:/tmp/paths.dot -dvld.save_dir -dvld.save_paths-dvld.dump_paths 0 1 paths.dot VLD -dvld.active PHPVLD0-dvld.active=1 -dvld.skip_prepend php.iniauto_prepend_file 0 - dvld.execute=0 -dvld.skip_append php.iniauto_append_file 0 - dvld.execute=0 -dvld.execute PHP1-dvld.execute=0 -dvld.format 0-dvld.format=1 -dvld.col_sep -dvld.col_sep -dvld.format "\t" -dvld.verbosity 10123 0 00.13 3 3 -dvld.save_dir /tmp -dvld.save_paths 0 -dvld.dump_paths 011 TIPI:PHP RELEASE_2011-07-29_V0.6.7
还剩180页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享pdf获得金币 ] 13 人已下载

下载pdf

pdf贡献者

a29534987

贡献于2011-12-16

下载需要 15 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf