PHPのexec関数が本番環境で動作しない

作成: 2021年01月05日

更新: 2021年01月05日

やりたいこと

markdownをHTMLに変換するNodeスクリプトをPHP(Laravel)サーバー上で実行したい.

調べるとexecという関数でPythonやShellスクリプトが動かせるみたいなのでNodeスクリプトも動かせそう.

実際に開発環境で以下のようにするとちゃんとNodeスクリプトが動いた.

exec("../scripts/generate-article.js " . $article->id);

しかし本番環境では動作しなかった.

php.ini

調べるとphp.iniのdisable_functionsでexec関数が無効化されている場合があるらしいというのがあった.

PHP exec won't work - no output produced - Stack Overflow

しかし自分の環境ではdisable_functionsにはexec関数はなかった.

disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsigna     led,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_     signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_ex     ec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,

解決方法

デバッグ

exec関数は第二引数から標準出力は取れるが標準エラー出力は取れないので以下のようにした標準エラー出力を標準出力に流し込んでログファイルに書き出してみた.(参考: PHP - exec()のエラーハンドリングと標準エラー出力の関係をまとめる - Qiita)

$output = null;
exec("../scripts/generate-article.js " . $article->id . "2>&1", $output);
Log::debug($output);

原因と解決方法

書き換えながら何度か繰り返すと以下のことが分かった.

  • shebang(#! /usr/bin/env node)が本番環境では機能していない
  • 本番環境と開発環境でカレントディレクトリが違う
  • 自分のNodeスクリプトはresources以下のファイルを扱うがそれらの権限が775で書き込めない

よって最終的に以下のように書き換えた

exec("node " . base_path() . "/scripts/generate-article.js " . $article->id);
  • nodeコマンドを使う
  • Laravelのbase_path関数を用いカレントディレクトリに依存しないようにした
  • resources以下のパーミッションを777にした

これで想定通りに実行できた.