21 July 2025

WebとCLI(コマンドライン)の両対応PHPスクリプト

Webサービスとして作成したPHPスクリプトを、サーバに接続したコンソールから簡易的に動作確認できる「WebとCLI(コマンドライン)の両対応」とする例。

ここでは、単純にテキストファイルを表示する機能を両対応としてみた。

コマンドラインかWebの判定は php_sapi_name() という関数が cli という文字列を返すかどうかという仕組みを利用している。

<?php

/**
 * PHPスクリプトがコンソールから呼ばれたかどうかを判定
 * 
 * @return bool : コンソールから呼ばれた場合は True, それ以外(Webなど)の場合はFalse
 */
function isCli(): bool
{
    return (php_sapi_name() === 'cli');
}

/**
 * コマンドラインで、引数なし(または引数書式エラー)の場合に表示するヘルプメッセージ
 */
function showUsage(): void
{
    echo "\n";
    echo "使い方:\n";
    echo "  php " . basename($_SERVER['PHP_SELF']) . " [-a] [-b] filename\n";
    echo "オプション:\n";
    echo "  -a      オプションAを有効にします\n";
    echo "  -b      オプションBを有効にします\n";
    echo "  filename 対象ファイル名を指定します\n";
}

// ********* MAIN

if (isCli()) {
    // コンソールから起動された場合

    // スクリプト起動時の引数
    $args = $_SERVER['argv'];
    // 引数なし(count==1)の場合、使い方を表示
    if (count($args) <= 1) {
        showUsage();
        exit(0);
    }

    array_shift($args); // スクリプト名を除去

    $paramA = false;
    $paramB = false;
    $filename = '';

    foreach ($args as $arg) {
        if (substr($arg, 0, 1) === '-') {
            // スイッチ判定
            switch ($arg) {
                case '-a':
                    $paramA = true;
                    break;
                case '-b':
                    $paramB = true;
                    break;
                default:
                    // 未定義スイッチを検出
                    echo "エラー: 未定義スイッチ {$arg} が指定されました\n";
                    showUsage();
                    exit(1);
            }
        } else {
            if ($filename === '') {
                $filename = $arg;
            } else {
                // ファイル名が2個以上渡された場合はエラー
                showUsage();
                exit(1);
            }
        }
    }
    // 実行内容確認
    echo "paramA: " . ($paramA ? 'true' : 'false') . "\n";
    echo "paramB: " . ($paramB ? 'true' : 'false') . "\n";
    echo "filename: " . ($filename ?: '(none)') . "\n";

    if (!file_exists($filename)) {
        print("Error : ファイル" . $filename . "が存在しません\n");
    } elseif (!is_file($filepath)) {
        print("Error : " . $filename . "はファイル以外です\n");
    } elseif (!is_readable($filepath)) {
        print("Error : ファイル" . $filename . "の読み取り権限がありません\n");
    } else {
        $text = file_get_contents($filename);
        if (!$text) {
            print("Error : ファイル" . $filename . "が読み込めません\n");
        }
        print($text);
    }
} else {
?>
    <!DOCTYPE html>
    <html lang="ja">

    <head>
        <meta charset="utf-8" />
    </head>

    <body>
        <p>アップロードしたテキストファイルを表示します</p>
        <form method="post" enctype="multipart/form-data">
            <input type="file" name="textfile" accept="text/plain" required>
            <button type="submit">アップロードして表示</button>
        </form>
        <?php
        // アップロード処理
        if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['textfile'])) {
            $uploaded = $_FILES['textfile'];

            if (!is_file($uploaded['tmp_name']) || !is_readable($uploaded['tmp_name'])) {
                echo "<p style='color:red;'>一時ファイルの属性エラー。</p>";
                return;
            } elseif (!$uploaded['error'] === UPLOAD_ERR_OK || !mime_content_type($uploaded['tmp_name']) === 'text/plain') {
                echo "<p style='color:red;'>正しいTEXTファイルをアップロードしてください。</p>";
            } elseif ($uploaded['size'] > 2048) {
                echo "<p style='color:red;'>2048バイト以下のテキストファイルしか受け付けません。</p>";
            } else {
                $rawText = file_get_contents($uploaded['tmp_name']);
                echo "<pre style='white-space: pre-wrap; word-break: break-word;'>\n";
                echo htmlspecialchars($rawText, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
                echo "\n</pre>\n";
            }
        }
        ?>
    </body>

    </html>
<?php
}