phpとpythonを連携させることになった理由
先日、既に運用中のサイト内画像を、サイズの違う同じ画像で上書きする必要が出てきました。
運用期間は何年にもなるため、画像の枚数は万枚にもおよび、目視で確認しながら手作業で上書きするのは現実的ではありませんでした。
そのため専用のシステムを組んで対応することになったのですが、同じディレクトリ構造、同じファイル名であっても、実際に画像が同じとは限りません。
これは判定するには画像認識する必要があり、調べた結果OpenCVというライブラリを利用することにしました。
OpenCVはC++、Java、Pythonなど、他にもいくつかの言語で利用することができるのですが、私は基本的にPHPをメインにプログラミングしており、OpenCVをそのまま利用できる言語ではなかったので、一番簡単そうなPythonで利用することにしました。
また環境構築も上記の中では簡単そうだったためです。
取り敢えずVirtualBoxにCentOS7+Python3の環境構築の仕方は下記記事で作りました。
phpとpythonを連携させた際の問題点
まず初めてのPython+OpenCVだったのですが、たくさんの良記事のおかげで画像認識(画像類似度)させるプログラムを組むことができました。
ただこのプログラムの実行はphpで作ったWeb管理画面から実行するものになるので、phpとpythonの連携がここで必要になってきます。
元々考えていた構成は、単純にphp側からpythonを呼び出して、pythonから返ってきた値をphp側で判定して処理を行おうと思っていました。
php側からの呼び出しはexec()
関数を利用して呼び出す感じです。
exec()
関数を使うのは微妙ですが、今回は外部入力を受け付けず、全て内部入力のみの処理だったので使用しました。
exec('コマンド', $output)
関数は第一引数に呼び出しコマンド、第二引数を指定した場合、そちらに第一引数で実行したコマンドの出力が格納される関数になります。
なのに一向に$output
に値が入っていません。
phpからpythonの呼び出しは初めてだったこともあり、pythonファイルの権限、所有者など色々変更しても全く反応なし。
でもpythonプログラムをコマンドラインで実行すると問題なく処理されています。
迷った結果、phpから直接pythonを呼び出す事をやめ、間にshellスクリプトを挟むようにしました。
phpからshellスクリプトを呼び、shellスクリプトからpythonプログラムを実行する形です。
一見無駄なように思いますが、shellスクリプトの方に処理を持たせられるため、結果役割分担のできた綺麗なプログラムになったと思います。
またshellスクリプトを挟むことで簡単に実行ユーザーなども確認でき、python連携が上手く行かなかった原因究明が早まりました。
phpとpython連携が上手く行かなかった原因は実行ユーザーの違いからpythonへのパスが通ってなかった事になります。
phpとpythonを連携させる方法
私は今回コマンドラインでpytonの動作確認を行ったユーザーはdevユーザー(仮)になります。
このdevユーザーにpyenvを使ってpython3系を使えるようにしていました。
ですがWebから実行されるphpプログラムはnginxユーザーで実行されていますので、当然phpから呼び出されるpythonプログラムもnginxユーザーで実行されていることになります。
そこで、元々pyenvはユーザー別にpythonをインストール出来るものなので、nginxユーザーにもdevユーザーと同じpythonをインストールし、nginxユーザーに切り替えた状態でpythonプログラムをコマンドラインから実行し動作確認を行いました。
やっと解決かと思ったのですが、そうは甘くありません。
これでもWebから実行するとpythonプログラムが上手く実行されませんでした。
いよいよ心も折れそうになってはいたのですが、さすがにかなり惜しい所までは来ていると勝手に思い込んでいたこともあり、もうひと踏ん張り頑張りました。
結果shellスクリプト内で、pythonへの環境変数を設定する事で解決!
phpのexec()
関数は外部コマンドを呼び出す関数になり、他の言語にも同じように外部コマンドを呼び出す関数はあるのですが、phpのexec()
関数の場合は環境変数を引き継がない?のかも知れません。
そのため、phpとpythonの間に挟んだshellスクリプト内でpythonへの環境変数を設定することで無事phpからpythonプログラムを呼び出す事ができました。
本当下記のような環境を作って、コードと実行結果ありきで説明したかったのですが、環境構築だけでなく、同じ現象まで再現するのがめんどくさかったので記事だけの説明になります。
まぁ自分なりのメモ感覚って事で。
追記メモ
今回はphpとpythonの間にshellスクリプトをはさみましたが、phpのputenv()
関数を使って設定すればshellスクリプトは必要なかったかも知れません。
プログラムがどのユーザーで実行されているか確認するコマンド
whoami
コマンド
実行ユーザーでpythonなどフルパスが知りたい場合に便利なコマンド
which
コマンド