有时候,我们在使用php进行开发时,需要调用服务器上的脚本或者调用python脚本来实现某些功能,这时候就需要使用PHP的系统调用函数。不过实际调用中会出现许许多多的问题,就拿最近有关机器学习的项目,后台是PHP实现的,不过机器学习部分用的是Python(需要用到caffe),中间调用python就遇到了许许多多的坑。


实验环境

  • 系统: Ubuntu 14.04
  • PHP: 5.6.22
  • Python: 2.7.6

起步

在挖矿之前先介绍一下php的系统调用函数吧:

system()

  • 描述:执行外部程序,成功则返回命令输出的最后一行, 失败则返回 FALSE,特点直接输出
1
2
$command='pwd';
system($command);
1
/home/wwwroot/cifar/public

exec()

  • 描述:执行一个外部程序,返回命令执行结果的最后一行内容。不直接输出到屏幕.如果输出只有一行则按空格为分隔符获取内容,如果多行则按行来为分隔符来获取内容存入到数组中.
1
2
3
$command='pwd';
exec($command,$result); //命令输出内容存至result变量
print_r($result);
1
2
3
4
Array
(
[0] => /home/wwwroot/cifar/public
)

shell_exec()

  • 描述: 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。返回命令执行的输出。 如果执行过程中发生错误或者进程不产生输出,则返回 NULL。使用本函数无法通过返回值检测进程是否成功执行。 如果需要检查进程执行的退出码,请使用 exec() 函数。
1
2
3
$command='who';
$data=shell_exec($command);
print_r($data);
1
2
root tty1 Jun 25 14:46
root pts/0 Jun 27 12:44

passthru()

  • 描述:执行外部程序并且显示原始输出 常用来执行诸如 pbmplus 之类的可以直接输出图像流的命令。 通过设置 Content-type 为 image/gif, 然后调用 pbmplus 程序输出 gif 文件, 就可以从 PHP 脚本中直接输出图像到浏览器。特点直接输出,原始输出
1
2
$command='who';
passthru($command);
1
2
root tty1 Jun 25 14:46
root pts/0 Jun 27 12:44

综上

  • 要获取原始输出就用passthru
  • 听说exec跟表单(grep查询的结果)更配哦
  • 其他函数看着需求用就好

挖坑

起步完之后就会开始进入实践,就会遇到各种坑,准备好了吗?

第一坑: disable_functions 解除PHP禁用函数

虽说这个默认设置不是坑,但是99%的服务器在你部署完php之后,disable_funcitons设置上都会禁用掉上面所用到函数,所以先去php.ini里面解除系统调用函数设置

  1. vi /usr/local/php/etc/php.ini (我lnmp安装默认是这个路径的,php安装在其他地方的请自行调整)
  2. 打开配置文件后找到disable_functions(可以用vi在命令模式下/disable_functions来查找)
  3. 将你之后用到的系统调用函数删去(exec,system,passthru等等),如果不在此列则表示没有默认禁用该PHP函数
  4. 保存(Esc退出到命令模式然后:wq!退出),如果权限不够不能写则自行加权限解决
  5. 重启PHP/LNMP (lnmp restart)
  6. 然后就可以愉快地使用系统调用函数了

第二坑: 补充错误日志

在你调试命令的时候,最烦的不是出错,最烦的是出错了但没有错误提示,因此不能对症下药,所以,不妨在你调试命令时,顺便输出到文本文件,然后去文本文件里面找错误输出。

1
2
3
$command='ll / 2>/xxxxxx/log.txt'; //将错误重定向到log.txt
exec($command,$result); //命令输出内容存至result变量
print_r($result);

建议命令里面涉及路径的全部都用上绝对路径,并且给日志文件给上权限(777简单粗暴)

bash命令中的0代表标准输入,1代表标准输出,2代表标准错误输出

第三坑: 权限

在你补充完错误日志时,你会发现最多的错误就是有关于权限的,以及有关于sudoer的,因为当你浏览器发送一个请求给服务器的web应用时,web服务器(apache或者nginx)所用的用户(www)不具备root的一些权限,所以需要修改sudoer以及涉及到的文件权限(读写操作)

  1. visudo 直接编辑 (进入利用nano编辑器编辑sudoer)
  2. 如果发现文本中存在 defaults requiretty 的,则用#号注释掉如 #defaults requiretty
  3. apache用户的话一般系统会建立apache用户,nginx用户的话一般是www用户(可用vi /usr/local/nginx/conf/nginx.conf查看,第一行就是显示nginx所用的用户)
  4. 在visudo末尾中 添加 www ALL=NOPASSWD:ALL 或者 apache ALL=NOPASSWD:ALL(不同web服务器所用的用户不同)
  5. 然后ctrl+x 按Y保存退出nano
  6. 然后给你需要读写的文件加上对应权限,就可以用PHP系统调用进行文件读写等等需要权限的操作

第四坑: 调用Python脚本时import问题

就举我上面提到的例子,PHP调用机器学习的Python脚本,这个Python脚本中用到了刚编译好的caffe(非Python标准库),然后import caffe的时候,或许在shell运行没问题,但是在PHP调用Python的时候可能就会炸了。

例如会报 ImportError: No module named caffe 的错误

1
2
3
4
5
#修改前的 PHP调用时会报caffe模块缺失的错误
import numpy as np
import sys
import os
import caffe
1
2
3
4
5
6
import numpy as np
import sys
import os
sys.path.append("/(caffe-master路径)/caffe/python") #根据自己实际的绝对路径修改
sys.path.append("/(caffe-master路径)/caffe/python/caffe")#根据自己实际的绝对路径修改
import caffe

对于其他需要自行编译的模块,同理举一反三吧~

第五坑: PHP默认开始安全模式

如果PHP默认开启了安全模式,只要去php.ini里面把safe_mode(如果存在)用#号注释掉即关闭


小结

对于其他错误的话,根据错误日志慢慢解决就好,还有更多的坑需要填呢~最后说一句,既然PHP调用Python这么麻烦,为什么不直接用Python开发呢?因为PHP是世界上最好的语言啊(误) ^_^

PHP系统调用官方文档