出於某些原因,我們有時會在PHP中通過exec來呼叫Python程式碼,有可能是某些功能只能用Python實現(或用Python實現比較方便),有可能是出於效能考慮(Python可以執行耗時任務)。
但我們有時會發現,在控制檯用命令列的方式執行python指令碼一切正常,在 php 中用 exec
呼叫就報 ModuleNotFoundError: No module named 'xxx'
錯誤。
本文是在 Ubuntu 20.04 上以 ubuntu
使用者身份進行的測試。
這種錯誤一般都是因為執行指令碼的使用者不同導致的,php用exec
呼叫python指令碼時,使用的使用者一般是 www-data
,而我們在控制檯一般都是 root
或 ubuntu
使用者。
這個可以通過 whoami
命令來驗證。
php程式碼如下:
$pythonScript = "whoami";
Log::info("exec script:" . $pythonScript);
exec($pythonScript, $output, $returnValue);
Log::info("exec output:" . json_encode($output));
Log::info("exec returnValue:" . $returnValue);
輸出如下:
[2023-07-13 10:34:27] local.INFO: exec script:whoami
[2023-07-13 10:34:27] local.INFO: exec output:["www-data"]
[2023-07-13 10:34:27] local.INFO: exec returnValue:0
為什麼使用者不同就會導致 ModuleNotFoundError: No module named 'xxx'
這個錯誤呢,根本原因還是許可權問題。
我們在控制檯寫python指令碼時,一般會通過 pip[3] install [xxx]
的形式安裝依賴的包,這時包一般會安裝在使用者目錄。
下面做個測試,我們安裝 python-dotenv
這個包,然後檢視包的安裝位置:
可以看到這個包安裝在了 /home/ubuntu/.local/lib/python3.8/site-packages
這個目錄。
下面我們試一下用 www-data
使用者的身份是否有許可權呼叫。
Python測試程式碼:
from dotenv import load_dotenv
load_dotenv()
分別用當前使用者和www-data
呼叫:
可以看到用www-data
呼叫時果然報ModuleNotFoundError: No module named 'dotenv'
錯誤。
我們檢視一下我們安裝的python-dotenv
對www-data
使用者是否可用:
sudo -u www-data pip3 show python-dotenv
可以看到確實是沒有的。
即然原因確定了,接下來就好辦了。
即然是控制檯使用者可以執行指令碼,我們把Web伺服器使用者改為控制檯使用者就可以了,以 apache 為例具體步驟如下:
1.開啟apache組態檔:sudo vim /etc/apache2/apache2.conf
2.更改以下兩行,將執行的使用者和組設定為自己所需的:
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
#更改為
User ubuntu
Group ubuntu
3.重啟apache:sudo service apache2 restart
注:這種方案能解決問題,但並不好,因為許可權給的太大了,有很大的安全風險,不建議用。
在安裝之前我們確認一下www-data使用者是否沒有安裝python-dotenv包:
sudo -u www-data pip3 show python-dotenv
下面我們給 www-data 使用者安裝python-dotenv包:
#安裝
sudo -u www-data pip3 install python-dotenv
#顯示安裝路徑
sudo -u www-data pip3 show python-dotenv
我們可以看到,安裝到了 /var/www/.local/lib/python3.8/site-packages
目錄下。
我們來驗證一下:
sudo -u www-data python3 pyscripts/test.py
可以看到不報錯了。