当尝试通过 PHP shell_exec() 函数执行两个不同的 Python 3 脚本时,我遇到了奇怪的行为。第一个 Python 脚本在 PHP 中的调用方式如下:
$jsondatash = escapeshellarg($jsondata);
// Execute the python script with the JSON data
$resultpy = shell_exec("/usr/bin/python3 /var/www/testsite/py/script1.py 2>&1 $jsondatash");
我转义了一些 json 数据 ($jsondatash) 并通过 shell_exec() 对此数据运行 Python 函数 (script1.py)。 Python 工作流程的结果存储在 PHP 数组 $resultpy 中。 Python 测试脚本如下所示:
from collections import OrderedDict
import sys, json
import scipy
import scipy.cluster.hierarchy as sch
import pandas as pd
import matplotlib
# Load the data that PHP sent
try:
data = json.loads(sys.argv[1], object_pairs_hook=OrderedDict)
except (ValueError, TypeError, IndexError, KeyError) as e:
print (json.dumps({'error': str(e)}))
sys.exit(1)
print (json.dumps(data))
它只是读取数据并通过 shell 将其发送回 PHP。然而,结果我得到一个空对象,表明 Python 3 代码无法执行。这是 Apache2 错误日志:
PHP Fatal error: Uncaught TypeError: array_keys(): Argument #1 ($array) must be of type array, null given
没有Python特有的错误信息,只有PHP中空数组的错误。当我从 Python 测试脚本中注释掉 pandas 和 matplotlib 时
from collections import OrderedDict
import sys, json
import scipy
import scipy.cluster.hierarchy as sch
#import pandas as pd
#import matplotlib
# Load the data that PHP sent
try:
data = json.loads(sys.argv[1], object_pairs_hook=OrderedDict)
except (ValueError, TypeError, IndexError, KeyError) as e:
print (json.dumps({'error': str(e)}))
sys.exit(1)
print (json.dumps(data))
,我可以通过 shell_exec() 成功运行脚本。因此,当通过 PHP 执行 Python 脚本时,两个 Python 3 库必定存在某种加载错误。
在将 PHP 中的 json 数据写入文件(inputfile.json)、用 Python 读取该文件、将 Python 中的数据写入另一个文件(outputfile.json)以及将该文件读回 PHP 时,我遇到了同样的行为。这是 PHP 代码:
$jsondata = fopen('/var/www/testsite/files/inputfile.json', 'w');
fwrite($jsondata, $data);
fclose($jsondata);
shell_exec("/usr/bin/python3 /var/www/testsite/py/script2.py 2>&1");
$resultpy = file_get_contents('/var/www/testsite/files/outputfile.json');
这是对应的script2.py Python代码:
from collections import OrderedDict
import sys, json
import scipy
import scipy.cluster.hierarchy as sch
import pandas as pd
import matplotlib
# Load the data from json file
try:
with open('/var/www/testsite/files/inputfile.json', 'r') as inputfile:
data = json.load(inputfile, object_pairs_hook=OrderedDict)
except (ValueError, TypeError, IndexError, KeyError) as e:
print (json.dumps({'error': str(e)}))
sys.exit(1)
# write data to json outputfile
with open('/var/www/testsite/files/outputfile.json', 'w') as outputfile:
json.dump(data, outputfile)
直接在shell中执行Python代码时
/usr/bin/python3 /var/www/testsite/py/script2.py 2>&1
,我得到了预期的输出,但是当使用 shell_exec() 在 PHP 中执行 Python 代码时,输出文件 outputfile.json 为空。同样,如果我从 Python 脚本中删除 pandas 和 matplotlib,它也可以通过 PHP shell_exec() 成功运行。
这些测试显示以下内容: (1) 两个 Python 脚本都可以通过 PHP shell_exec() 执行,但前提是我从 Python 脚本中删除加载 pandas 和 matplotlib。 (2) 加载 pandas 和 matplotlib 本身不是问题,因为我可以直接在 shell 中执行包含这两个库的 Python script2.py 并获得预期的输出。
为什么我可以直接在 shell 中在 Apache2 上运行完全相同的 python 代码,但不能通过 PHP 中的 shell_exec() 运行?从我的测试来看,这似乎与 pandas 和 matplotlib 未能加载和终止 python 脚本有关。
编辑: Python3版本是3.8.10,PHP版本是8.2.8,这是安装pandas和matplotlib后的输出:
pip install pandas
> Successfully installed pandas-2.0.3 python-dateutil-2.8.2 pytz-2023.3 tzdata-2023.3
pip install matplotlib
> Successfully installed contourpy-1.1.0 cycler-0.11.0 fonttools-4.40.0 importlib-resources-6.0.0
> kiwisolver-1.4.4 matplotlib-3.7.2 packaging-23.1 pillow-10.0.0 pyparsing-3.0.9 zipp-3.15.0
Edit2: 我想知道可以加载的 scipy 和加载失败的 pandas 是否位于同一个路径,确实是:
Name: scipy
Version: 1.10.1
Location: /home/ubuntu/.local/lib/python3.8/site-packages
Name: pandas
Version: 1.5.3
Location: /home/ubuntu/.local/lib/python3.8/site-packages
我测试了旧版本的 pandas(v.1.5.3 - 见上文),现在已经安装了当前版本 v.2.0.3。两者都会在导入时终止 python 脚本。 pandas 所需的所有附加软件包均已安装。
Name: pandas
Version: 2.0.3
Location: /home/ubuntu/.local/lib/python3.8/site-packages
Python 3 sys.path 是:
['', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/home/ubuntu/.local/lib/python3.8/site-packages', '/usr/local/lib/python3.8/dist-packages', '/usr/lib/python3/dist-packages']
Edit3:我发现以下方法(https://stackoverflow.com/a/44128762/8008652)允许我检索PHP中的Python 3错误并将其打印在html输出中:
exec('/usr/bin/python3 /var/www/testsite/py/script2.py 2>&1', $output, $return_var);
if ($return_var>0) {
var_dump($output);
}
这是 Python 3 错误:
array(14) { [0]=> string(34) "Traceback (most recent call last):" [1]=> string(77) " File "/var/www/testsite/py/script2.py", line 6, in " [2]=> string(23) " import pandas as pd" [3]=> string(80) " File "/usr/lib/python3/dist-packages/pandas/__init__.py", line 55, in " [4]=> string(33) " from pandas.core.api import (" [5]=> string(79) " File "/usr/lib/python3/dist-packages/pandas/core/api.py", line 5, in " [6]=> string(44) " from pandas.core.arrays.integer import (" [7]=> string(92) " File "/usr/lib/python3/dist-packages/pandas/core/arrays/__init__.py", line 10, in " [8]=> string(53) " from .interval import IntervalArray # noqa: F401" [9]=> string(92) " File "/usr/lib/python3/dist-packages/pandas/core/arrays/interval.py", line 38, in " [10]=> string(60) " from pandas.core.indexes.base import Index, ensure_index" [11]=> string(89) " File "/usr/lib/python3/dist-packages/pandas/core/indexes/base.py", line 74, in " [12]=> string(49) " from pandas.core.strings import StringMethods" [13]=> string(139) "ImportError: cannot import name 'StringMethods' from 'pandas.core.strings' (/usr/lib/python3/dist-packages/pandas/core/strings/__init__.py)" }
正如我从之前的测试中怀疑的那样,这是与 pandas 库相关的导入错误(ImportError:无法从 'pandas.core.strings 导入名称 'StringMethods')。
我发现Python库安装在服务器上的两个不同路径中:一个是'/usr/local/lib/python3.8/dist-packages',另一个是'/usr/lib/python3/dist-包'.
当我直接从服务器上的命令行执行脚本时,它确实从“/usr/local/lib/python3.8/dist-packages”访问 Pandas 包,而它是使用 shell_exec( )来自“/usr/lib/python3/dist-packages”,这意味着它在从 PHP 执行 Python 脚本时确实加载了旧版本的 Pandas。
因此,从命令行和 PHP 执行相同的 Python3 脚本确实从不同的目录导入了包。
我确实删除了 Python3 和两个包目录,重新安装了 Python3 以及 Pandas 和 Matplotlib 包,这解决了我的问题。