$PHP-SRC/run-test.php
- 因为如果在同一个进程中执行, 测试就会停止,后面的测试也将无法执行,php中有很多将脚本隔离的方法比如: system(),exec()等函数,这样可以使用主测试进程服务调度被测脚本和检测测试结果,通过这些外部调用执行测试。 php测试使用了proc_open()函数, 这样就可以保证测试脚本和被测试脚本之间能隔离开。
如果只需要简单的进程单向进程通道 可以使用 popen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
<?php function foo() { echo date('Y-m-d H:i:s')."\n"; echo shell_exec('php -r \'sleep(1); echo date("Y-m-d H:i:s")." by shell_exec:blocking\n";\''); //阻塞 $pipe1 = popen('php -r \'sleep(2); echo date("Y-m-d H:i:s")." by popen1:non-blocking\n";\'', 'r'); //非阻塞(管道) $pipe2 = popen('php -r \'sleep(1); echo date("Y-m-d H:i:s")." by popen2:non-blocking\n";\'', 'r'); //非阻塞(管道) echo date('Y-m-d H:i:s')."\n"; register_shutdown_function(function() use ($pipe1, $pipe2) { //事件驱动(脚本结束事件),异步回调 echo stream_get_contents($pipe1); //输出子进程返回的数据 echo stream_get_contents($pipe2); //输出子进程返回的数据 pclose($pipe1); pclose($pipe2); }); } foo();
|
但是如果需要双方向的功能更多的进程控制 可以使用proc_open()打开新的子进程非阻塞的执行PHP脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
function foo() { $proc = proc_open( //task.php 内容为 $arr = unserialize(stream_get_contents(STDIN)); $arr['time'] = date('Y-m-d H:i:s', $arr['time']); echo serialize($arr); '/png/php/5.4.45/bin/php /home/eechen/task.php', array( 0 => array('pipe','r'), //stdin (用fwrite写入数据给管道) 1 => array('pipe','w'), //stdout(用stream_get_contents获取管道输出) 2 => array('pipe','w'), //stderr(用stream_get_contents获取管道输出) //2 => array('file','/tmp/err.txt','a') //stderr(写入到文件) ), $pipes, //管道(stdin/stdout/stderr) '/tmp', //当前PHP进程的工作目录 array('foo' => 'bar') //php.ini 配置 variables_order = "EGPCS" 其中E表示$_ENV,否则$_ENV输出为空 要执行脚本所需要的环境变量 ); //var_dump($pipes); //resource of type (stream) if(is_resource($proc)) { //stdin $stdin = serialize(array('time' => time())); fwrite($pipes[0], $stdin); //把参数传给脚本task.php fclose($pipes[0]); //fclose关闭管道后proc_close才能退出子进程,否则会发生死锁 register_shutdown_function(function() use($pipes, $proc) { //事件驱动(脚本结束事件),异步回调 //stdout $stdout = stream_get_contents($pipes[1]); fclose($pipes[1]); //stderr $stderr = stream_get_contents($pipes[2]); fclose($pipes[2]); //exit code (返回进程的终止状态码,如果发生错则返回-1) $status = proc_close($proc); $data = array( 'stdout' => $stdout, 'stderr' => $stderr, 'status' => $status, ); var_export($data); //echo json_encode($data); }); } } foo();
|
宏定义 C
PHP代码中的宏定义包含 # 和 ##
‘##’代表 连接符 用来把两个语言符号(Token)组合成单个语言符号。 这里的语言符号不一定是宏的变量。并且双井号不能作为第一个或最后一个元素存在。
1 2 3 4 5 6
|
#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, \
|
“#”是一种预处理运算符,它的功能是将其后面的宏参数进行 字符串化操作
PHP中代码的do-while宏
1 2 3 4 5
|
#define ALLOC_ZVAL(z) \ do { \ (z) = (zval*)emalloc(sizeof(zval_gc_info)); \ GC_ZVAL_INIT(z); \ } while (0)
|
显而易见代码只执行一次,常用用例举例
1 2 3 4 5 6
|
#define TEST(a, b) a++;b++;
if (expr) TEST(a, b); else do_else();
|
如果直接编译会造成if 后面的单句规则被破坏。
以及需要考虑到平台移植,空操作也使用 do{}while(0)来进行宏定义
PHP运行时的全局参数 PHP CORE GLOBALS
首先会根据ZTS 或者NTS (线程安全) 来有一些变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
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; //输出缓冲区大小(字节)
char *safe_mode_include_dir; //在安全模式下,该组目录和其子目录下的文件被包含时,将跳过UID/GID检查。 zend_bool safe_mode_gid; //在安全模式下,默认在访问文件时会做UID比较检查 zend_bool sql_safe_mode; zend_bool enable_dl; //是否允许使用dl()函数。dl()函数仅在将PHP作为apache模块安装时才有效。
char *output_handler; // 将所有脚本的输出重定向到一个输出处理函数。
char *unserialize_callback_func; // 如果解序列化处理器需要实例化一个未定义的类,这里指定的回调函数将以该未定义类的名字作为参数被unserialize()调用, long serialize_precision; //将浮点型和双精度型数据序列化存储时的精度(有效位数)。
char *safe_mode_exec_dir; //在安全模式下,只有该目录下的可执行程序才允许被执行系统程序的函数执行。
long memory_limit; //一个脚本所能够申请到的最大内存字节数(可以使用K和M作为单位)。 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; //PHP所产生的URL中用来分隔参数的分隔符。
char *variables_order; // PHP注册 Environment, GET, POST, Cookie, Server 变量的顺序。
HashTable rfc1867_protected_variables; // RFC1867保护的变量名,在main/rfc1867.c文件中有用到此变量
short connection_status; // 连接状态,有三个状态,正常,中断,超时 short ignore_user_abort; // 是否即使在用户中止请求后也坚持完成整个请求。
unsigned char header_is_being_sent; // 是否头信息正在发送
zend_llist tick_functions; // 仅在main目录下的php_ticks.c文件中有用到,此处定义的函数在register_tick_function等函数中有用到。
zval *http_globals[6]; // 存放GET、POST、SERVER等信息
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_errors指令,PHP将会在出错信息上显示超连接, 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 debug,仅在main/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_order高,在request变量生成时用到,个人觉得是历史遗留问题
zend_bool mail_x_header; // 仅在ext/standard/mail.c文件中使用, char *mail_log;
zend_bool in_error_log; };
|
PHP 生命周期
PHPINIT 调用模块前的初始化:
初始化若干全局变量 大都设置为NULL
初始化若干常量 PHP_Version等
初始化zend Engine 和 核心组件
- 包括内存管理初始化、 全局使用的函数指针初始化(如前面所说的zend_printf等),对PHP源文件进行词法分析、语法分析、 中间代码执行的函数指针的赋值,初始化若干HashTable(比如函数表,常量表等等),为ini文件解析做准备, 为PHP源文件解析做准备,注册内置函数(如strlen、define等),注册标准常量(如E_ALL、TRUE、NULL等)、注册GLOBALS全局变量等。
解析PHPini到PHP core globals
全局操作函数初始化
初始化静态构建的模块和共享模块MINIT 模块初始化 扩展模块的函数初始化 注册常量 定义模块的使用 Web Server启动或者脚本开始执行
- 通过php_register_internal_extensions_func函数用来注册静态构建的模块,也就是默认加载的模块, 我们可以将其认为内置模块
RINIT 模块激活阶段 开始使用模块
解析
RSHUTDOWN 停用模块
MSHUTDOWN WebServer关闭或者脚本停止使用
关于扩展重用
因为PHP的实现是和zend引擎实现紧耦合,所以PHP的扩展并不具有很好的移植性,比如移植到JVM虚拟机,移植到HHVM等,如果实现解耦合,那么扩展的重用将会容易。
PHP的运行模式
FastCGI、CGI、SAPI模块形式、嵌入式
SAPI:Server Application Program Interface
CGI:“通用网关接口”(Common Gateway Interface)
FastCGI:long-live 常驻内存型升级版高性能CGI 防止CGI的fork-and-execute模式。
PHP脚本运行
目前编程语言可以分为两大类:
PHP完成基本准备工作
启动PHP以及Zend引擎
加载注册扩展模块
Zend引擎进行
lex生成的 词法分析:将代码转换为一个个标记token
- 很多编程语言都使用lex/yacc或他们的变体(flex/bison)来作为语言的词法语法分析生成器
语法分析 bison生成 语法分析器
转换为opcode
opcode结构体
1 2 3 4 5 6 7 8 9
|
struct _zend_op { // 这里是struct实现 opcode_handler_t handler; // 执行该opcode时调用的处理函数 znode result; znode op1; znode op2; ulong extended_value; // 脚本执行时候需要的其他信息 uint lineno; zend_uchar opcode; // opcode代码 };
|
函数是在编译器遇到echo语句的时候进行编译的函数:
1 2 3 4 5 6 7 8 9 10
|
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);
}
|
编译器遇到print语句的时候进行编译的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
void zend_do_print(znode *result,const 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; }
|
【这里可以看到print和echo的根本区别 print有返回值 echo并没有 两者都是语法结构,返回null也是有返回值!】
- 日期格式代码出现两次的错误 ORA-01810
错误的原因是使用了两次MM . 一.Oracle中使用to_date()时格式化日期需要注意格式码 如:select to_date('2005-01-01 11:11:21','yyyy-MM-dd ...
- 可爱的豆子——使用Beans思想让Python代码更易维护
title: 可爱的豆子--使用Beans思想让Python代码更易维护 toc: false comments: true date: 2016-06-19 21:43:33 tags: [Pyth ...
- iOS代码规范(OC和Swift)
下面说下iOS的代码规范问题,如果大家觉得还不错,可以直接用到项目中,有不同意见 可以在下面讨论下. 相信很多人工作中最烦的就是代码不规范,命名不规范,曾经见过一个VC里有3个按钮被命名为button ...
- Jquery的点击事件,三句代码完成全选事件
先来看一下Js和Jquery的点击事件 举两个简单的例子 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&q ...
- redux-amrc:用更少的代码发起异步 action
很多人说 Redux 代码多,开发效率低.其实 Redux 是可以灵活使用以及拓展的,经过充分定制的 Redux 其实写不了几行代码.今天先介绍一个很好用的 Redux 拓展-- redux-amrc ...
- 编写高质量代码:改善Java程序的151个建议(第5章:数组和集合___建议75~78)
建议75:集合中的元素必须做到compareTo和equals同步 实现了Comparable接口的元素就可以排序,compareTo方法是Comparable接口要求必须实现的,它与equals方法 ...
- 使用 .NET WinForm 开发所见即所得的 IDE 开发环境,实现不写代码直接生成应用程序
直接切入正题,这是我09年到11年左右业余时间编写的项目,最初的想法很简单,做一个能拖拖拽拽就直接生成应用程序的工具,不用写代码,把能想到的业务操作全部封装起来,通过配置的方式把这些业务操作组织起来运 ...
- jsp前端实现分页代码
前端需要订一page类包装,其参数为 private Integer pageSize=10; //每页记录条数=10 private Integer totalCount; //总记录条数 priv ...
- 【开源】简单4步搞定QQ登录,无需什么代码功底【无语言界限】
说17号发超简单的教程就17号,qq核审通过后就封装了这个,现在放出来~~ 这个是我封装的一个开源项目:https://github.com/dunitian/LoTQQLogin ————————— ...
- 【.net 深呼吸】限制执行代码的权限
前面好几篇文章,老周都跟大伙伴们聊了跟应用程序域有关的话题,干脆咱们一聊到底吧,做学问就应该这样,有恒心. App Domain的创建新应用程序域的方法中,有一个特殊的重载: public stati ...
随机推荐
- 【安装】Nginx安装
系统平台:CentOS release 6.5 (Final) 64位. 安装编译工具及库文件 yum -y install make zlib zlib-devel gcc-c++ libtool ...
- 机器学习-ID3决策树算法(附matlab/octave代码)
ID3决策树算法是基于信息增益来构建的,信息增益可以由训练集的信息熵算得,这里举一个简单的例子 data=[心情好 天气好 出门 心情好 天气不好 出门 心情不好 天气好 出门 心情不好 天气不好 ...
- Bigtable阶段性总结(版本1)
Bigtable的角色:为大规模的结构化数据提供高效的存储.管理与查询. Bigtable的针对性: 大规模数据需要大规模集群支持,带来了存储.管理.查询.容错上的复杂性. 关系型数据库在数据规模较大 ...
- platforms
1.Qt551x86_vs2013 所有的 platforms文件夹 的路径: 1.1.E:\ZC_ProgramFiles_161104\Qt551_vs2013\5.5\msvc2013\plug ...
- JavaScript Number() Vs new Number()
最近在优化一个页面时候.IDEA 提示我错误的使用了包装类.当时感觉很诧异. 随后.我上Stack Overflow上面查了一下,终于发现了问题所在. new Number('123') 与 Numb ...
- thinkphp3.2笔记(3)视图渲染 模板的赋值与显示 系统变量
一 视图 1 视图渲染 渲染模板输出最常用的是使用display方法,调用格式:display('[模板文件]'[,'字符编码'][,'输出类型'])模板文件的写法支持下面几种:用法 描述不带任何 ...
- JavaScript--语法2--语句结构
JavaScript--语句结构 一.心得 判断的时候常量放左边java中switch只支持四种类型,javaScript是弱类型,所有的都支持. 显示方法: 77 // alert("x= ...
- 雷林鹏分享:Ruby Dir 类和方法
Ruby Dir 类和方法 Dir 是一个表示用于给出操作系统中目录中的文件名的目录流.Dir 类也拥有与目录相关的操作,比如通配符文件名匹配.改变工作目录等. 类方法 序号方法 & 描述 1 ...
- 2018-2019-2 20165332 《网络对抗技术》Exp4 恶意代码分析
2018-2019-2 20165332 <网络对抗技术>Exp4 恶意代码分析 原理与实践说明 1.实践目标 监控你自己系统的运行状态,看有没有可疑的程序在运行. 分析一个恶意软件,就分 ...
- JS级联下拉框
//Ajax级联获取SDKfunction GetDropDownList(parent_ddlID, fill_dllID, url, param) { this.pId = parent_d ...