$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脚本运行

目前编程语言可以分为两大类:

  • 第一类是像C/C++, .NET, Java之类的编译型语言, 它们的共性是: 运行之前必须对源代码进行编译,然后运行编译后的目标文件。

  • 第二类比如:PHP, Javascript, Ruby, Python这些解释型语言, 他们都无需经过编译即可”运行”,虽然可以理解为直接运行,

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也是有返回值!】

PHP代码实现 1的更多相关文章

  1. 日期格式代码出现两次的错误 ORA-01810

    错误的原因是使用了两次MM . 一.Oracle中使用to_date()时格式化日期需要注意格式码 如:select to_date('2005-01-01 11:11:21','yyyy-MM-dd ...

  2. 可爱的豆子——使用Beans思想让Python代码更易维护

    title: 可爱的豆子--使用Beans思想让Python代码更易维护 toc: false comments: true date: 2016-06-19 21:43:33 tags: [Pyth ...

  3. iOS代码规范(OC和Swift)

    下面说下iOS的代码规范问题,如果大家觉得还不错,可以直接用到项目中,有不同意见 可以在下面讨论下. 相信很多人工作中最烦的就是代码不规范,命名不规范,曾经见过一个VC里有3个按钮被命名为button ...

  4. Jquery的点击事件,三句代码完成全选事件

    先来看一下Js和Jquery的点击事件 举两个简单的例子 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&q ...

  5. redux-amrc:用更少的代码发起异步 action

    很多人说 Redux 代码多,开发效率低.其实 Redux 是可以灵活使用以及拓展的,经过充分定制的 Redux 其实写不了几行代码.今天先介绍一个很好用的 Redux 拓展-- redux-amrc ...

  6. 编写高质量代码:改善Java程序的151个建议(第5章:数组和集合___建议75~78)

    建议75:集合中的元素必须做到compareTo和equals同步 实现了Comparable接口的元素就可以排序,compareTo方法是Comparable接口要求必须实现的,它与equals方法 ...

  7. 使用 .NET WinForm 开发所见即所得的 IDE 开发环境,实现不写代码直接生成应用程序

    直接切入正题,这是我09年到11年左右业余时间编写的项目,最初的想法很简单,做一个能拖拖拽拽就直接生成应用程序的工具,不用写代码,把能想到的业务操作全部封装起来,通过配置的方式把这些业务操作组织起来运 ...

  8. jsp前端实现分页代码

    前端需要订一page类包装,其参数为 private Integer pageSize=10; //每页记录条数=10 private Integer totalCount; //总记录条数 priv ...

  9. 【开源】简单4步搞定QQ登录,无需什么代码功底【无语言界限】

    说17号发超简单的教程就17号,qq核审通过后就封装了这个,现在放出来~~ 这个是我封装的一个开源项目:https://github.com/dunitian/LoTQQLogin ————————— ...

  10. 【.net 深呼吸】限制执行代码的权限

    前面好几篇文章,老周都跟大伙伴们聊了跟应用程序域有关的话题,干脆咱们一聊到底吧,做学问就应该这样,有恒心. App Domain的创建新应用程序域的方法中,有一个特殊的重载: public stati ...

随机推荐

  1. 20145104张家明 《Java程序设计》第一周学习总结

    20145104张家明 <Java程序设计>第1周学习总结 教材学习内容总结 在开学的第一周,通过了看书进行了系统的学习java,首先简单的了解java的发展历程,然后了解了JVM.JRE ...

  2. 防止putty的鼠标右键错误粘贴

    一.环境 发行版:Ubuntu 18.04.1 LTS 代号:bionic 内核版本:4.15.0-30-generic 二.背景 每次从putty复制时,会单击鼠标右击,以便复制出终端的内容,但是一 ...

  3. HDU 2457 DNA repair(AC自动机+DP)题解

    题意:给你几个模式串,问你主串最少改几个字符能够使主串不包含模式串 思路:从昨天中午开始研究,研究到现在终于看懂了.既然是多模匹配,我们是要用到AC自动机的.我们把主串放到AC自动机上跑,并保证不出现 ...

  4. pod状态为Back-off

    查看pod状态为CrashLoopBackOff [root@master yaml]# kubectl get pods NAME READY STATUS RESTARTS AGE mysql-7 ...

  5. php while循环

    <html> <body> <?php $i=; ) { echo "The number is " . $i . "<br>& ...

  6. json文件为空时读取会报错

    simplejson.errors.JSONDecodeError: Expecting value: line column () 提示说是解码错误 可以用下面的方法判断json文件是否为空 imp ...

  7. sql语句练习-基础篇

    本文内容源自改编http://blog.csdn.net/ochangwen/article/details/51297893, 针对mysql数据库做了语法更改 个人觉得原版有些不合理之处,改了部分 ...

  8. python json与字典对象互相转换

    改文章转自:https://www.cnblogs.com/Lin-Yi/p/7640147.html 1 import requests 2 import json 3 ''' 4 json.loa ...

  9. Android之修改用户头像并上传服务器(实现手机拍照和SD卡选择上传)

    写了这么多个的APP,最近才把他这个功能写上来,就抽取其中的用户修改头像的相关操作这个功能写了这篇博客,来与大家分享,希望对你有所帮助. 案例包含了: Xutil图片上传 拍照和SD卡选择图片 图片缓 ...

  10. oracle增加sequence

    (1)删除序列;  (2)重新创建: 这个方法比较简单粗暴. drop sequence  sequence_name; create sequence   sequence_name minvalu ...