baiyan所有视频:https://segmentfault.com/a/11...回顾while语法的实现op1);//这里的op1就是字符串1.phpnew_op_array=zend_include_or_eval(inc_filename,opline->extended_value);...这个handler处理函数的核心是函数zend_include_or_eval(),返回一个新的op_array:zvaltmp_inc_文件名;...}else{开关(类型){{caseZEND_INCLUDE_ONCE:caseZEND_REQUIRE_ONCE:{//此处带有存档zend_file_handlefile_handle;zend_string*resolved_pa??th;resolved_pa??th=zend_resolve_path(Z_STRVAL_P(inc_filename),(int)Z_STRLEN_P(inc_filename));if(resolved_pa??th){if(zend_hash_exists(&EG(included_files),resolved_pa??th)){gotoalready_compiled;}}else{resolved_pa??th=zend_string_copy(Z_STR_P(inc_filename));}if(SUCCESS==zend_stream_open(ZSTR_VAL(resolved_pa??th),&file_handle)){if(!file_handle.opened_pa??th){file_handle.opened_pa??th=zend_string_copy(resolved_pa??th);}if(zend_hash_add_empty_element(&EG(included_files),file_handle.opened_pa??th)){//加入绑定的哈希表中zend_op_array*op_array=zend_compile_file(&file_handle,(type==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE));zend_destroy_file_handle(&file_handle);zend_string_release(resolved_pa??th);如果(Z_TYPE(tmp_inc_filename)!=IS_UNDEF){zend_string_release(Z_STR(tmp_inc_filename));}返回op_array;}else{zend_file_handle_dtor(&file_handle);already_compiled:new_op_array=ZEND_FAKE_OP_ARRAY;}}else{if(type==ZEND_INCLUDE_ONCE){zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN,Z_STRVAL_P(inc_filename));}别的{zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN,Z_STRVAL_P(inc_filename));}}zend_string_release(resolved_pa??th);}休息;caseZEND_INCLUDE:caseZEND_REQUIRE:new_op_array=compile_filename(type,inc_filename);//关键调使用break;...returnnew_op_array;}在ZEND_INCLUDE_ONCE分支中可以观察到,如果是include_once或者require_once的情况,会先去缓存中找,那么这个缓存是怎么实现的呢?最容易想到的就是哈希表,key是文件名,value是文件内容,这样就可以直接从缓存中读取文件,而不用再次加载文件,提高效率。让我们回到主题include语法。在ZEND_INCLUDE分支中我们可以看到,这里调用了一个新的函数compile_filename(),它返回一个新的op_array。因为include包含了另外一个外部文件,而op_array是一个脚本指令集,所以我们需要新建一个op_array来存放另外一个文件的指令集,继续跟进compile_filename():zend_op_array*compile_filename(inttype,zval*文件名){zend_file_handle文件句柄;zvaltmp;zend_op_array*retval;zend_string*opened_pa??th=NULL;...retval=zend_compile_file(&file_handle,类型);//corecallreturnretval;}这个函数会继续调用zend_compile_file()函数,它是一个指向compile_file()函数的函数指针:ZEND_APIzend_op_array*compile_file(zend_file_handle*file_handle,inttype){...zend_op_array*op_array=NULL;if(open_file_for_scanning(file_handle)==FAILURE){...}else{op_array=zend_compile(ZEND_USER_FUNCTION);//corecall}returnop_array;}我们可以看到最终还是调用了zend_compile函数。我们熟悉吗?没错,就是PHP脚本编译的入口。然后,通过调用该函数,就可以对导入的外部脚本1.php进行词法分析、语法分析等编译操作。现在想一个问题,这个函数返回一个op_array,也就是新引入的外部脚本1.php的op_array,那么原老脚本2.php的op_array的状态和数据应该怎么存储呢?op_array存放在zend_execute_data栈中,所以新脚本1.php的op_array可以继续添加到zend_execute_data栈中。执行包含脚本时,只需将其从堆栈中弹出即可。和递归的原理一样,递归也是用栈。当你不断递归时,数据不断地被压入栈中,当达到最终递归终止条件时,才可以逐渐出栈,所以递归非常缓慢,效率极低。其他PHP脚本的执行过程我们之前也提到过,PHP脚本的执行入口是main函数(我们在代码层面是看不到的,是虚拟机帮我们添加的)。从main函数进入后,PHP脚本的执行一共有5个阶段:CLI模式(命令行界面,即命令行模式。例如在命令行下执行脚本:php1.php):php_module_startup:模块初始化php_request_startup:请求初始化php_execute_script:执行脚本php_request_shutdown:请求关闭php_module_shutdown:模块关闭在CLI模式下,运行一次后直接退出,不常驻内存。接下来看看我们用的最多的FPM模式,常驻内存。一旦请求来了,PHP-FPM就会去处理,所以在php_request_startup、php_execute_script、php_request_shutdown这三个阶段,会出现一个死循环,这样PHP-FPM就可以常驻内存,不断地对传入的请求进行一个一个的处理.但是这种方式会有问题。每次请求过来的时候,都会重新进行词法分析和语法分析……效率很低。为了解决这个问题,PHP中我们常说的opcache就要登场了。它会将之前解析过的opcode缓存起来,下次遇到相同的opcode时,就不需要再次解析,提高性能。nginx+php-fpm架构初探LNMP架构下,发送前端请求时,首先会通过nginx代理,然后通过fastcgi协议转发到上游php-fpm,而php-fpm将实际处理请求。我们知道,nginx是一个多进程架构的反向代理web服务器,它由一个master进程和多个worker进程组成:master进程:管理所有worker进程(比如worker进程的创建和销毁)worker进程:负责处理客户端发送进来的请求,杀死master进程后,worker进程仍然存在,可以为客户端提供服务。当worker进程被kill掉(且当前没有其他worker进程),master进程会重新创建worker进程,以保证nginx服务的正常运行,下一篇我们将讲解fastcgi协议,逐步揭开nginx+的神秘面纱php-fpm架构交流
