chmod — ファイル/ディレクトリのパーミッションを変更する

PHP
  • カテゴリ: PHP
  • 掲載バージョン: PHP 8.4
  • 名前空間 / FQCN / コマンド: chmod
  • 関連: umask / mkdir / fileperms / chown / chgrp
  • 変更履歴: PHP 8.1 で 0o 形式の8進数リテラルが追加(関数仕様は変更なし)

要点(TL;DR)

  • 用途:ファイル/ディレクトリの権限(rwx)を8進数で変更
  • 最低限の使い方:chmod($path, 0o644); // または 0644
  • よくある罠:
    • 数値は8進数。文字列 "0644"NG(10進として解釈される)
    • Windows では機能が限定(実質「読み取り専用フラグ」程度)
    • シンボリックリンクには通常リンク先が適用対象

概要

chmod() はファイル/ディレクトリのパーミッションを変更します。UNIX系では所有者または適切な権限が必要です。ディレクトリ配下を一括で変更する場合は再帰処理が必要です。ストリームラッパーでも使えますが、対応はラッパーやサーバー側機能に依存します(例:FTP サーバーの CHMOD 対応)。

構文 / シグネチャ

bool chmod(string $filename, int $permissions)

引数(表)

引数必須既定値説明
$filenamestring対象パス(ローカル/対応ラッパー)
$permissionsint8進数で指定(例 0o644 / 0644)。特殊ビット 4xxx(SUID) / 2xxx(SGID) / 1xxx(sticky)
  • 戻り値true(成功)/ false(失敗)
  • 例外/副作用
    • 失敗時に E_WARNING(エラーハンドラ設定次第で例外化し得る)
    • 権限変更はファイルシステムに永続反映
    • シンボリックリンクには通常リンクへ適用(OS依存)

使用例

最小例

<?php
// そのまま動くシンプルな例
$file = sys_get_temp_dir() . '/example.txt';
file_put_contents($file, "hello");

// どちらも8進数:0o644(PHP 8.1+)/ 0644(従来表記)
chmod($file, 0o644);

printf("perm=%o\n", fileperms($file) & 0o777); // 644 を表示
unlink($file);

実務例(ディレクトリを再帰的に:ファイル0644 / ディレクトリ0755)

<?php
declare(strict_types=1);

/**
 * 再帰 chmod(シンボリックリンクは辿らない)
 * 失敗時は ErrorException を投げる
 */
function chmod_recursive(string $root, int $filePerm = 0o644, int $dirPerm = 0o755): void
{
    $wrap = static function (string $path, int $perm): void {
        set_error_handler(static function ($errno, $errstr) use ($path) {
            throw new ErrorException($errstr . " ($path)");
        });
        try {
            if (!@chmod($path, $perm)) {
                throw new ErrorException("chmod failed ($path)");
            }
        } finally {
            restore_error_handler();
        }
    };

    if (is_link($root)) {
        return; // リンクはスキップ
    }
    if (is_dir($root)) {
        $wrap($root, $dirPerm);
        $it = new FilesystemIterator($root, FilesystemIterator::SKIP_DOTS);
        foreach ($it as $node) {
            if ($node->isLink()) {
                continue;
            }
            chmod_recursive($node->getPathname(), $filePerm, $dirPerm);
        }
    } elseif (is_file($root)) {
        $wrap($root, $filePerm);
    }
}

// 例:アップロードディレクトリの権限整備
$uploadDir = __DIR__ . '/storage/uploads';
chmod_recursive($uploadDir, 0o640, 0o750);

よくある落とし穴・注意

  • 8進数指定chmod($p, 644)10進→結果が意図とズレます。0o644(8.1+)または 0644 を使う。"0644"文字列も不可。
  • Windows の制限:実質的に読み取り専用属性の切替に近い挙動。UNIX と同等の細粒度制御は不可。
  • リンクの扱い:多くの環境でリンクに適用。リンク自体のパーミッション変更は通常不可。
  • 所有権/権限:対象の所有者または適切な権限が必要。コンテナ/マウント(CIFS/NFS)ではサーバ設定やマウントオプションで無効化される場合あり。
  • umask の誤解umask()作成時の権限に影響。chmod() で直接設定する際は通常 umask は関与しません。
  • ストリームラッパーftp:// などはサーバ側の CHMOD 対応に依存(非対応なら失敗)。

代替・関連APIとの比較

  • umask():ファイル作成時の初期権限に影響。既存ファイルの変更には chmod()
  • mkdir($dir, $mode):作成時にディレクトリ権限を指定(umask の影響あり)。後から正確に揃えるなら chmod() で再設定。
  • fileperms():現在のパーミッション取得(& 0o777 で下位9ビット抽出)。
  • chown() / chgrp():所有者/グループ変更(多くの場合 root 権限が必要)。

選定基準:既存の権限を正確に変更→chmod()、作成時に大枠→mkdir()+umask()、権限確認→fileperms()

テスト例(Pest)

<?php
// tests/ChmodTest.php
it('sets file permission to 0600 on *nix', function () {
    if (PHP_OS_FAMILY === 'Windows') {
        $this->markTestSkipped('Windowsではchmodの粒度が限定されます');
    }
    $file = tempnam(sys_get_temp_dir(), 't_');
    file_put_contents($file, 'x');

    expect(chmod($file, 0o600))->toBeTrue();
    expect(fileperms($file) & 0o777)->toBe(0o600);

    unlink($file);
});

トラブルシュート(エラー別)

症状/エラー原因対処
chmod(): Operation not permitted所有者でない/権限不足、マウントオプションで制限実行ユーザ/所有者を確認。必要に応じて sudochown、マウント設定を見直す
chmod(): No such file or directoryパス誤り、リンク切れ絶対パス化や file_exists() で存在確認
期待した権限にならない10進/文字列で指定している、CIFS/NFS でマップが異なる0o644/0644 で指定し直し、リモートFSの権限マッピング仕様を確認
ディレクトリ配下がバラバラ再帰処理をしていない上記 chmod_recursive() のように再帰で適用
Windowsで効かないOS仕様(読み取り専用属性のみ)UNIX互換環境で実行、または OS 依存の代替(ACL 操作等)を検討
FTPで失敗サーバが SITE CHMOD 非対応SFTP/SSH 経由に切替、またはサーバ設定の変更

参考リンク

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする