这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux
# main
把剩下的代码增加了下注释全部贴出来了(这个是简化后的main函数,去掉了一些无关紧要的代码段):
01 | int main(int argc, char *argv[]) |
02 | { |
03 | ... |
04 | sapi_module_struct *sapi_module = &cli_sapi_module; |
05 |
06 | argv = save_ps_args(argc, argv); //这里获取一次当前执行进程的参数,环境变量等。为的是对特定平台,修正下argv变量以供后续使用。 |
07 |
08 | cli_sapi_module.additional_functions = additional_functions; // cli模式特有的函数 |
09 |
10 | ... |
11 |
12 |
13 | #ifdef ZTS |
14 | tsrm_startup(1, 1, 0, NULL); |
15 | (void)ts_resource(0); |
16 | ZEND_TSRMLS_CACHE_UPDATE(); |
17 | # endif |
18 |
19 | zend_signal_startup(); // 设置信号,把一些需要反应的信号位设置为0 |
20 |
21 | // 获取参数,做一些对应的初始化行为,或者一些简单的操作,比如help |
22 | while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2))!=-1) { |
23 | switch (c) { // 这里的c是代表返回的字符串的ascii码值 |
24 | case 'c' : |
25 | ... |
26 | case 'n' : |
27 | ini_ignore = 1; // 不使用ini文件,通过代码或者其他指定ini值 |
28 | break ; |
29 | case 'd' : { // 配置ini的key,val值在命令行中,下面的行为都是修改ini_entries这个变量 |
30 | ... |
31 | } |
32 | case 'h' : /* help & quit */ |
33 | case '?' : |
34 | php_cli_usage(argv[0]); |
35 | goto out; |
36 | case 'i' : case 'v' : case 'm' : |
37 | sapi_module = &cli_sapi_module; |
38 | goto exit_loop; |
39 | case 'e' : /* enable extended info output */ |
40 | use_extended_info = 1; |
41 | break ; |
42 | } |
43 | } |
44 | exit_loop: |
45 |
46 | sapi_module->ini_defaults = sapi_cli_ini_defaults; // 设置初始化的ini值 |
47 | sapi_module->php_ini_path_override = ini_path_override; //设置重写后的ini_path地址,如果是php -c的话,这个就为非null |
48 | sapi_module->phpinfo_as_text = 1; // 打开打印phpinfo的开关,需要的时候可以把phpinfo打印出来 |
49 | sapi_module->php_ini_ignore_cwd = 1; // 不在当前路径寻找php.ini |
50 | sapi_startup(sapi_module); // sapi初始化行为,比如初始化全局变量SG |
51 | sapi_started = 1; // 标记,表示已经调用了startup,关闭的时候需要调用shundown |
52 | ... |
53 |
54 | // 开始调用sapi的startup方法,对cli模式,实际上是调用php_cli_startup方法 |
55 | if (sapi_module->startup(sapi_module) == FAILURE) { |
56 | exit_status = 1; |
57 | goto out; |
58 | } |
59 | module_started = 1; // 标记位,标记已经调用了module的startup方法 |
60 |
61 | ... |
62 |
63 | zend_first_try { |
64 | exit_status = do_cli(argc, argv); // 这个是实际上调用的内容 |
65 | } zend_end_try(); |
66 | out: // 这个代码段已经是要退出了 |
67 | if (ini_path_override) { |
68 | free(ini_path_override); |
69 | } |
70 | if (ini_entries) { |
71 | free(ini_entries); |
72 | } |
73 | if (module_started) { |
74 | php_module_shutdown(); |
75 | } |
76 | if (sapi_started) { |
77 | sapi_shutdown(); |
78 | } |
79 | #ifdef ZTS |
80 | tsrm_shutdown(); |
81 | # endif |
82 |
83 | cleanup_ps_args(argv); |
84 | exit (exit_status); |
85 | } |
其实看伪码很简单:
1 | tsrm_startup(1, 1, 0, NULL); // TSM启动 |
2 | zend_signal_startup(); // 信号设置 |
3 | sapi_startup(sapi_module); // SAPI启动 |
4 | sapi_module->startup(sapi_module); // 当前模块的startup |
5 | do_cli(argc, argv); // 做实际的行为 |
6 | php_module_shutdown(); // 当前模块的shutdown |
7 | sapi_shutdown(); // SAPI关闭 |
8 | tsrm_shutdown(); // TSM关闭 |
好了,其实看了一圈,里面最重的函数是do_cli了。
php参数
do_cli里面你会看到根据参数的不同,有很多分支,这里你就需要了解这些参数都是什么用的。
参数
作用
实例
do_cli
我们把do_cli函数的整个函数去掉多余代码,仅保留关键代码如下:
001 | static int do_cli(int argc, char **argv) |
002 | { |
003 | ... |
004 |
005 | zend_try { |
006 |
007 | // 这里处理了 i-输出phpinfo内容/ v-输出php版本 / m-输出扩展信息 |
008 | while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { |
009 | switch (c) { |
010 |
011 | case 'i' : // 输出phpinfo内容 |
012 | ... |
013 | php_print_info(0xFFFFFFFF); |
014 | ... |
015 | goto out; |
016 |
017 | case 'v' : // 输出php版本信息 |
018 | ... |
019 | get_zend_version() |
020 | ... |
021 | goto out; |
022 |
023 | case 'm' : // 列出所有模块 |
024 | ... |
025 | print_extensions(); |
026 | ... |
027 | goto out; |
028 |
029 | default : |
030 | break ; |
031 | } |
032 | } |
033 |
034 | ... |
035 |
036 | // 下面的代码做了几个事情: |
037 | // 1 根据参数设置了behavior参数 |
038 | // 2 有执行文件的就将文件存在script_file |
039 | while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { |
040 | switch (c) { |
041 |
042 | case 'a' : // php的交互模式 |
043 | ... |
044 | interactive=1; |
045 | ... |
046 | break ; |
047 |
048 | case 'C' : // 不要把cwd目录变成脚本所在的目录。这个默认就是cwd是当前执行路径,所以这里什么都不做。 |
049 | break ; |
050 |
051 | case 'F' : // php -F <file> 进入交互模式,每执行一行就执行一次<file>文件 |
052 | ... |
053 | behavior=PHP_MODE_PROCESS_STDIN; |
054 | script_file = php_optarg; |
055 | break ; |
056 |
057 | case 'f' : // php -f <file> 解析并执行文件 |
058 | ... |
059 | script_file = php_optarg; |
060 | break ; |
061 |
062 | case 'l' : // 检查文件的语法是否有错误 |
063 | ... |
064 | behavior=PHP_MODE_LINT; |
065 | break ; |
066 |
067 | case 'q' : // 安静模式,默认也是安静模式 |
068 | break ; |
069 |
070 | case 'r' : // 从命令行直接执行脚本 |
071 | ... |
072 | behavior=PHP_MODE_CLI_DIRECT; |
073 | exec_direct=php_optarg; |
074 | break ; |
075 |
076 | case 'R' : // 每行输入的时候执行一次code脚本,比如 php -R 'echo 12;' |
077 | ... |
078 | behavior=PHP_MODE_PROCESS_STDIN; |
079 | exec_run=php_optarg; |
080 | break ; |
081 |
082 | case 'B' : // 在每次输入开始之前执行一次code脚本 |
083 | ... |
084 | behavior=PHP_MODE_PROCESS_STDIN; |
085 | exec_begin=php_optarg; |
086 | break ; |
087 |
088 | case 'E' : // 在每次输入结束之后执行一次code脚本, 上面的 RBE可以参考一个例子:find conf.d | php -B '$l=0;' -R '$l += count(@file($argn));' -E 'echo "Total Lines: $l\n";' |
089 | ... |
090 | behavior=PHP_MODE_PROCESS_STDIN; |
091 | exec_end=php_optarg; |
092 | break ; |
093 |
094 | case 's' : // 使用html高亮方式显示代码,这个或许在一些代码显示的时候需要用到 |
095 | ... |
096 | behavior=PHP_MODE_HIGHLIGHT; |
097 | break ; |
098 |
099 | case 'w' : // php <file> -w 能把<file>中的评论和多余的空格去掉 |
100 | ... |
101 | behavior=PHP_MODE_STRIP; |
102 | break ; |
103 |
104 | case 'z' : // 加载外部扩展 |
105 | zend_load_extension(php_optarg); |
106 | break ; |
107 | case 'H' : // 隐藏所有参数 |
108 | hide_argv = 1; |
109 | break ; |
110 | case 10: // 显示function定义 |
111 | behavior=PHP_MODE_REFLECTION_FUNCTION; |
112 | reflection_what = php_optarg; |
113 | break ; |
114 | case 11: // 显示class定义 |
115 | behavior=PHP_MODE_REFLECTION_CLASS; |
116 | reflection_what = php_optarg; |
117 | break ; |
118 | case 12: // 显示扩展定义,注意这里是php扩展 |
119 | behavior=PHP_MODE_REFLECTION_EXTENSION; |
120 | reflection_what = php_optarg; |
121 | break ; |
122 | case 13: // 显示zend扩展定义, 比如xdebug |
123 | behavior=PHP_MODE_REFLECTION_ZEND_EXTENSION; |
124 | reflection_what = php_optarg; |
125 | break ; |
126 | case 14: // 显示扩展的对应配置 |
127 | behavior=PHP_MODE_REFLECTION_EXT_INFO; |
128 | reflection_what = php_optarg; |
129 | break ; |
130 | case 15: // 显示ini配置 |
131 | behavior = PHP_MODE_SHOW_INI_CONFIG; |
132 | break ; |
133 | default : |
134 | break ; |
135 | } |
136 | } |
137 |
138 | ... |
139 |
140 | // 初始化request之后,执行了request_startup |
141 | if (php_request_startup()==FAILURE) { |
142 | ... |
143 | goto err; |
144 | } |
145 | ... |
146 |
147 | zend_is_auto_global_str(ZEND_STRL( "_SERVER" )); |
148 |
149 | // 根据不同的行为做不同的具体操作,这个是核心方法 |
150 | switch (behavior) { |
151 | case PHP_MODE_STANDARD: // 标准,就是执行一个脚本文件 |
152 | ... |
153 | php_execute_script(&file_handle); |
154 | ... |
155 | break ; |
156 | case PHP_MODE_LINT: // 只检查文件有没有语法错误 |
157 | exit_status = php_lint_script(&file_handle); |
158 | ... |
159 | break ; |
160 | case PHP_MODE_STRIP: |
161 | ... |
162 | zend_strip(); |
163 | ... |
164 | break ; |
165 | case PHP_MODE_HIGHLIGHT: |
166 | ... |
167 | php_get_highlight_struct(&syntax_highlighter_ini); |
168 | zend_highlight(&syntax_highlighter_ini); |
169 | goto out; |
170 | break ; |
171 | case PHP_MODE_CLI_DIRECT: |
172 | ... |
173 | if (zend_eval_string_ex(exec_direct, NULL, "Command line code" , 1) == FAILURE) { |
174 | exit_status=254; |
175 | } |
176 | break ; |
177 |
178 | case PHP_MODE_PROCESS_STDIN: |
179 | ... |
180 | zend_eval_string_ex(exec_end, NULL, "Command line end code" , 1) |
181 | ... |
182 | break ; |
183 | case PHP_MODE_REFLECTION_FUNCTION: |
184 | case PHP_MODE_REFLECTION_CLASS: |
185 | case PHP_MODE_REFLECTION_EXTENSION: |
186 | case PHP_MODE_REFLECTION_ZEND_EXTENSION: |
187 | ... |
188 | ZVAL_STRING(&arg, reflection_what); |
189 | object_init_ex(&ref, pce); |
190 | ... |
191 | zend_call_method_with_1_params(&ref, pce, &pce->constructor, "__construct" , NULL, &arg); |
192 | ... |
193 | break ; |
194 | case PHP_MODE_REFLECTION_EXT_INFO: |
195 | ... |
196 | if ((module = zend_hash_str_find_ptr(&module_registry, lcname, len)) == NULL) { |
197 | ... |
198 | display_ini_entries(NULL); |
199 | ... |
200 | } |
201 | ... |
202 | break ; |
203 | case PHP_MODE_SHOW_INI_CONFIG: |
204 | ... |
205 | break ; |
206 | } |
207 | } zend_end_try(); |
208 |
209 | out: |
210 | ... |
211 | err: |
212 | ... |
213 | }</file></file></file></file></file> |
整个200行的代码就很好理解了,整个是包在一个zend_try...zend_catch中的。做了几步:
- 处理-i, -m, -v参数
- 对其他的参数设置behavior,script_file等变量
- 根据behavior做不同的行为
回到我们的初步计划,我们想要了解的事:
我们的根据-r的参数配置寻找。
它实际上时调用了
1 | zend_eval_string_ex(exec_direct, NULL, "Command line code" , 1) |
这里的exec_direct是 echo 12字符串