Laradock生态下, 如何使用PHP函数exec()或shell_exec()执行某些系统命令?
诉求
由于项目需要调用一个java编写的算法接口, 所以就在workspace
中安装了java8环境并将此算法代码通过javac
成功编译后实测在命令行中可以直接运行java main
得到结果. 这样在项目业务中就可以直接使用诸如:exec('java main', $res)
来获取调用java算法的结果. 可实际情况却并不顺利, 问题在下面描述.
环境信息如下:
Component | Version |
---|---|
PHP | 7.3.15 |
Swoole | 4.5.2 |
LaravelS | 3.7.8 |
Laravel Framework | 5.8.31 |
Java jdk | 1.8.0_212 |
在环境的基础上新增了java8环境
/laradock/workspace/Dockerfile
新增如下:
.
.
.
###########################################################################
# Java8 jdk-8u212
###########################################################################
USER laradock
# 解压jdk8包至指定目录
ADD ./jdk8/jdk-8u212-linux-x64.tar.gz /home/laradock/.java
# 添加算法所需jar包
COPY ./jdk8/jars/fastjson-1.2.70.jar /home/laradock/.java/jdk1.8.0_212/lib
# 设置laradock用户的环境变量
RUN echo "" >> ~/.bashrc && \
echo 'export JAVA_HOME="$HOME/.java/jdk1.8.0_212"' >> ~/.bashrc && \
echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> ~/.bashrc && \
echo 'export CLASSPATH="$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/fastjson-1.2.70.jar"' >> ~/.bashrc
# ENV JAVA_HOME $JAVA_HOME
# ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/fastjson-1.2.70.jar
# Add JAVA binaries to root's .bashrc
USER root
# 将.java所有权更改为laradock(注意此时的用户是root)并添加root用户的环境变量
RUN chown -R laradock:laradock /home/laradock/.java && \
echo "" >> ~/.bashrc && \
echo 'export JAVA_HOME="/home/laradock/.java/jdk1.8.0_212"' >> ~/.bashrc && \
echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> ~/.bashrc && \
echo 'export CLASSPATH="$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/fastjson-1.2.70.jar"' >> ~/.bashrc
# Add PATH For Java
ENV PATH $PATH:/home/laradock/.java/jdk1.8.0_212/bin
修改Dockerfile后重新构建容器workspace(docker-compose up -d --build --force-recreate workspace
)
下图为命令行执行php脚本打印的结果:
困惑
而在实际项目运行时得到的结果却是:
为了方便描述问题, 这里采用了在浏览器控制台直接打印出结果
我们再对比下命令行的环境变量 $PATH
:
以及php
的路径:
从上图中可以看出, 命令行下的用户是laradock
, 和控制台打印的用户是同一个, 然而php
命令的路径却不一样? 环境变量也完全不一样, 这是为何….? 这个问题困扰了我很久, 也google了很多资料, 还是不知道如何解决…
其实通过php调用shell命令做法不怎么好,开了这些函数,也会带来安全隐患。 可以要求java方,提供RPC、HTTP之类的接口吗。
@Brad-Wen java方暂时不接受任何修改, 他只提供了源码 :sob:
你看那个报错,我大概能猜出报错原因,你想想,你用命令行能执行成功的话,因为你是root用户,但是如果你是以网页访问用户去访问,这时候php-fpm的执行用户可能是nobody之类的,那么你的java执行文件,对于nobody用户之类的来说,是没有权限的,也是不可见的,所以可能就报错找不到,当然,具体是不是这么回事,还得你结合我的描述去确认,因为我能看到的你的信息有限。
@Brad-Wen 在命令行和php脚本中同时使用
whoami
打印出的用户是同一个都是laradock
用户, 而且which php
在命令行得到的php路径是/usr/bin/php
然而控制台打印的php路径确是/usr/local/bin/php
@_mars 因为上下文环境的问题,我建议你在脚本,所有都应该写绝对路径,这样就能避免此类问题,降低你排查问题的难度。例如:你应该是使用
/usr/bin/php
而不是php
@_mars 另外,为了确认是否权限问题引起的。有两个方法可尝试,当然,不建议你在生产环境做此操作,因为这是一个危险行为,只建议你在测试环境做调试。 方法一:php-fpm 使用 root 用户来运行。 方法二:给涉及到的命令、或文件,赋予 777 权限,例如你提到的 java、php 等命令,来确保执行权限充足。
@Brad-Wen 首先感谢大佬的耐心回答. 上述所说的这些方法我都测试过
@hedeqiang bug
@hedeqiang 大佬怎么说?
@_mars 刚去查了一下手册,exec 函数,返回的是输出结果的最后一行,我在想,会不会忽略了一些关键性的报错信息,你可以尝试一下获取全部输出。例如:
@Brad-Wen 你看文中的命令都是以


2>&1
结尾的, 其实就是打印错误信息的(如果不这么写, 那么命令有问题的话exec()
返回的结果是空字符串). 你的写法是赋值到Array $output
中. 结果参照浏览器控制台截图:@_mars 你的理解有误。2>&1 的意思是将错误重定向到标准输出,并不代表将输出格式化成单行输出,所以你要明白,这是两回事。当然,你需要同时获取到
标准输出
以及错误输出
信息的时候,重定向操作是必须的。所以,你想获取
exec()
所有的输出,还是必须通过我前面说的操作去做,因为你并不能确定,程序输出中,没有存在换行,多行输出的可能。@_mars 可以给你举个形象的例子代码,你就能体会到两者写法的区别:
请求方式不一样,最终走的是php-fpm,我试过了,如果在php-fpm里面安装java环境需要注意用户和环境变量。