【PHP8.x】exec関数の使い方

exec関数の使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

exec関数は、外部プログラムを実行する関数です。この関数は、PHPスクリプトからオペレーティングシステムのコマンドライン上で実行可能なプログラムを呼び出し、その結果を文字列として取得するために使用されます。exec関数は、セキュリティ上のリスクを伴う可能性があるため、使用には十分な注意が必要です。

具体的には、exec関数は指定されたコマンドを実行し、そのコマンドの標準出力(stdout)を変数に格納することができます。オプションで、コマンドの終了ステータス(リターンコード)を取得することも可能です。これにより、実行された外部プログラムが正常に終了したかどうかを判断できます。

exec関数の基本的な構文は以下の通りです。

string exec ( string $command [, array &$output [, int &$return_var ]] )

ここで、$commandは実行するコマンド、$outputはコマンドの出力が格納される配列(オプション)、$return_varはコマンドの終了ステータスが格納される変数(オプション)です。

exec関数を使用する際には、以下の点に注意する必要があります。

  1. セキュリティ: ユーザーからの入力など、信頼できないデータに基づいてコマンドを生成することは避けるべきです。コマンドインジェクション攻撃のリスクがあります。入力値を適切に検証し、エスケープ処理を行う必要があります。
  2. 権限: PHPスクリプトを実行しているユーザー(通常はWebサーバーのユーザー)が、実行しようとしているコマンドに対する実行権限を持っている必要があります。
  3. 出力の扱い: コマンドの出力は、大量のデータを返す可能性があります。必要に応じて、出力を制限したり、適切に処理したりする必要があります。
  4. エラー処理: コマンドが正常に実行されなかった場合(例えば、ファイルが見つからない、権限がないなど)、適切なエラー処理を行う必要があります。

exec関数は、システム管理ツールや外部サービスとの連携など、特定の状況下では非常に有用な機能です。しかし、セキュリティと権限に注意し、慎重に使用する必要があります。より安全な代替手段として、proc_open関数やshell_exec関数なども検討できます。

構文(syntax)

1<?php
2$output = [];
3exec('ls -l', $output);
4print_r($output);
5?>

引数(parameters)

string $command, array &$output = null, int &$result_code = null

  • string $command: 実行したいコマンドライン文字列
  • array &$output = null: コマンドの実行結果の各行を格納する配列(参照渡し)
  • int &$result_code = null: コマンドの終了コードを格納する整数(参照渡し)

戻り値(return)

string|false

exec関数は、実行したコマンドの標準出力を文字列として返します。コマンドの実行に失敗した場合はfalseを返します。

サンプルコード

PHP exec コマンド実行とデバッグ

1<?php
2
3/**
4 * 外部コマンドを実行し、その詳細な結果と、
5 * コマンドが「実行されない」場合に考えられる原因のヒントを出力します。
6 *
7 * システムエンジニアを目指す初心者向けに、exec関数の使い方とデバッグ方法を示します。
8 *
9 * @param string $command 実行するコマンド文字列。
10 * @return void
11 */
12function runExternalCommandWithDebug(string $command): void
13{
14    echo "--- コマンド実行テスト: '{$command}' ---\n";
15
16    // コマンドの全出力行が格納される配列を初期化
17    $outputLines = [];
18    // コマンドの終了コードが格納される変数を初期化 (通常、成功は0、失敗は非0)
19    $returnCode = 0;
20
21    // exec関数を実行。
22    // 第1引数: 実行するコマンド。
23    // 第2引数: コマンドの全出力行が配列として格納される (参照渡し)。
24    // 第3引数: コマンドの終了コードが整数として格納される (参照渡し)。
25    // 戻り値: コマンド出力の最終行、またはexec関数が実行できない場合は false。
26    $lastLine = exec($command, $outputLines, $returnCode);
27
28    // exec関数自体が実行できない場合(例: php.iniでdisable_functionsに登録されている)
29    if ($lastLine === false) {
30        echo "エラー: PHPのexec関数が実行できませんでした。\n";
31        echo "  - 考えられる原因:\n";
32        echo "    1. PHPの設定ファイル (php.ini) で 'disable_functions' に exec が含まれていないか確認してください。\n";
33        echo "    2. サーバーのセキュリティポリシーにより、外部コマンド実行が完全に制限されている可能性があります。\n";
34        echo "    3. PHPの実行環境に致命的な問題がある可能性があります。\n";
35        echo "  - 解決策: php.iniを確認し、必要であればサーバー管理者に連絡してください。\n";
36        return; // 以降の処理はスキップ
37    }
38
39    echo "--- 実行結果詳細 ---\n";
40    echo "  - コマンド出力の最終行 (lastLine): " . ($lastLine === '' ? '(出力なし)' : $lastLine) . "\n";
41    echo "  - コマンドの終了コード (returnCode): {$returnCode}\n";
42
43    echo "  - 全てのコマンド出力 (outputLines配列):\n";
44    if (empty($outputLines)) {
45        echo "    (出力なし)\n";
46    } else {
47        foreach ($outputLines as $line) {
48            echo "    > {$line}\n";
49        }
50    }
51
52    // コマンドの終了コードが0以外の場合、コマンドが正常に終了しなかったことを意味します。
53    if ($returnCode !== 0) {
54        echo "--- 注意: コマンドはエラー終了しました (終了コード: {$returnCode}) ---\n";
55        echo "  - 考えられる原因:\n";
56        echo "    1. コマンドのパスが正しくない、またはコマンドが存在しない可能性があります。(例: '/usr/bin/ls' のようにフルパスを指定してみてください)\n";
57        echo "    2. コマンドのスペルミス、または引数が間違っている可能性があります。\n";
58        echo "    3. PHPプロセスを実行しているユーザーに、コマンドの実行権限がない可能性があります。\n";
59        echo "    4. コマンド自体がエラーメッセージを出力している場合、上記の '全てのコマンド出力' でエラー内容を確認できます。\n";
60    } else {
61        echo "--- コマンドは正常に終了しました。---\n";
62    }
63    echo "\n";
64}
65
66// --- 実際のコマンド実行例 ---
67
68// 1. 正常に動作するコマンドの例 (現在ディレクトリのファイル一覧表示)
69// Linux/macOSの場合: 'ls -al'
70// Windowsの場合: 'dir'
71runExternalCommandWithDebug('ls -al');
72// runExternalCommandWithDebug('dir'); // Windows環境でテストする場合
73
74// 2. 存在しないコマンドの例 (エラー終了し、returnCodeが非ゼロになることを確認)
75runExternalCommandWithDebug('this_command_does_not_exist_xyz');
76
77// 3. エラーメッセージを出力するが、実行自体は可能なコマンドの例 (例: lsに無効なオプション)
78runExternalCommandWithDebug('ls -z'); // '-z' は 'ls' コマンドの無効なオプション
79
80// 4. 標準出力を伴うシンプルなコマンドの例
81runExternalCommandWithDebug('echo "Hello from PHP exec!"');
82
83// セキュリティに関する注意:
84// ユーザーからの入力に基づいてコマンドを構築する場合、コマンドインジェクションのリスクがあります。
85// そのような場合は、必ず `escapeshellarg()` や `escapeshellcmd()` を使用して
86// コマンド引数を適切にエスケープしてください。

PHPのexec関数は、サーバー上で外部コマンドを実行するために使用します。この関数は、PHPスクリプトからOSのコマンドライン操作を行いたい場合に便利です。

第1引数には、実行したいコマンドを文字列で指定します。例えば、'ls -al'のように指定すると、ファイル一覧を取得するコマンドが実行されます。第2引数に配列変数を渡すと、コマンドの出力結果が1行ずつその配列に格納されます。第3引数に変数を渡すと、コマンドの終了コード(リターンステータス)が格納されます。終了コードは、コマンドが正常に成功したか(通常は0)、エラーで終了したか(0以外)を判断するための重要な指標です。

exec関数の戻り値は、コマンドが出力した内容の最終行のみを文字列として返します。もしPHPの設定でexec関数自体が無効化されているなど、コマンドを実行できなかった場合はfalseが返ります。

このサンプルコードは、これらの引数と戻り値をすべて活用し、コマンドが「実行されない」場合に考えられる原因をデバッグする方法を示しています。戻り値がfalseになるか、第3引数の終了コードが0以外になるかを確認することで、PHPの設定に問題があるのか、それとも実行しようとしたコマンド側に問題があるのかを効率的に切り分けることができます。

exec関数が期待通りに動作しない場合、PHPの設定で無効化されているケースと、実行したコマンド自体が失敗しているケースの切り分けが重要です。関数の戻り値がfalseならPHP側の設定を、第3引数の終了コードが0以外ならコマンド側の問題を疑います。コマンド側のエラーでは、パスが通っていない、実行権限がないといった原因が考えられます。コマンドのエラー出力を確認したい場合は、コマンドの末尾に 2>&1 を追記し、エラーを標準出力にリダイレクトさせる手法が有効です。また、ユーザーが入力した値をコマンドに使う際は、escapeshellarg関数で必ずエスケープ処理を行い、コマンドインジェクションという深刻な脆弱性を防ぐ必要があります。

PHP exec でコマンドを非同期実行する

1<?php
2
3/**
4 * コマンドを非同期(バックグラウンド)で実行します。
5 *
6 * PHPのexec関数は通常、指定したコマンドが終了するまで処理をブロックしますが、
7 * コマンドの末尾に特定の記号を追加することで、コマンドの終了を待たずに
8 * PHPスクリプトの処理を続行させることができます(非同期実行)。
9 *
10 * このサンプルでは、5秒かかる処理をバックグラウンドで実行し、
11 * メインスクリプトは待たずに即座に終了します。
12 *
13 * @param string $command 実行したいコマンド文字列
14 * @return void
15 */
16function executeCommandAsynchronously(string $command): void
17{
18    // OSファミリーを判定
19    if (PHP_OS_FAMILY === 'Windows') {
20        // Windowsの場合: `start /B` を先頭につけ、出力をNULにリダイレクト
21        $asyncCommand = sprintf('start /B %s > NUL 2>&1', $command);
22    } else {
23        // LinuxやmacOSなどのUnix系OSの場合: 末尾に `&` をつけ、出力を/dev/nullにリダイレクト
24        $asyncCommand = sprintf('%s > /dev/null 2>&1 &', $command);
25    }
26
27    // exec関数で非同期コマンドを実行
28    // 第2、第3引数は非同期実行では通常使われません
29    exec($asyncCommand);
30}
31
32// --- 以下、実行サンプル ---
33
34// バックグラウンドで実行させたい時間のかかるコマンドの例
35// (5秒間スリープした後に、現在時刻をファイルに書き込むPHPコマンド)
36$longRunningTask = 'php -r "sleep(5); file_put_contents(\'result.txt\', \'Completed at \' . date(\'H:i:s\'));"';
37
38echo '非同期処理を開始します。現在時刻: ' . date('H:i:s') . PHP_EOL;
39
40// 上記のコマンドを非同期で実行
41executeCommandAsynchronously($longRunningTask);
42
43echo 'メインスクリプトは待機せずに次の処理へ進みます。' . PHP_EOL;
44echo 'スクリプトの実行はここで終了します。現在時刻: ' . date('H:i:s') . PHP_EOL;
45echo '約5秒後に "result.txt" というファイルが作成されるか確認してください。' . PHP_EOL;
46

PHPのexec関数は、サーバー上で外部のコマンドを実行するための関数です。第一引数に実行したいコマンドの文字列を渡します。通常、exec関数は指定したコマンドの処理が完了するまでPHPスクリプトの実行を停止させます。これを同期実行と呼びます。

このサンプルコードは、時間のかかる処理を待たずに行う「非同期実行」の方法を示しています。これを実現するために、OSの特性を利用してコマンドの記述を工夫します。LinuxやmacOSのようなUnix系OSではコマンドの末尾に&を、Windowsでは先頭にstart /Bを付け加えます。これにより、PHPスクリプトはコマンドの終了を待たずに次の処理へ進むことができます。また、> /dev/null 2>&1という部分は、コマンドの出力結果を破棄するための記述で、これによってもスクリプトが待機しなくなります。

exec関数の第二引数$outputと第三引数$result_codeは、それぞれコマンドの出力と終了コードを受け取るための変数ですが、非同期実行の場合はコマンドの終了を待たないため、これらの情報を取得することはできません。戻り値は通常、コマンド出力の最終行を返しますが、このサンプルでは出力を破棄しているため、意味のある値は返りません。

このサンプルでは、5秒間スリープする処理を非同期で実行しています。そのため、メインスクリプトは5秒待つことなく即座に終了メッセージを表示します。その裏側でコマンドが実行され続け、約5秒後にファイルが作成されることで非同期処理の成功を確認できます。

exec関数にユーザー入力などの外部変数を渡す際は、escapeshellarg()関数で必ずエスケープしてください。これを怠ると、悪意のあるコマンドを実行される「コマンドインジェクション」という脆弱性の原因になります。また、この非同期実行の方法は、コマンドの実行結果や成功・失敗を知ることができません。サーバーの設定によってはexec関数自体が無効化されている場合もあります。バックグラウンドで多数のプロセスを起動するとサーバーに大きな負荷がかかるため、頻繁な実行は避けるべきです。より本格的な非同期処理には、専用のジョブキューシステムの利用を検討しましょう。

関連コンテンツ