- カテゴリ: 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 では機能が限定(実質「読み取り専用フラグ」程度)
- シンボリックリンクには通常リンク先が適用対象
- 数値は8進数。文字列
概要
chmod() はファイル/ディレクトリのパーミッションを変更します。UNIX系では所有者または適切な権限が必要です。ディレクトリ配下を一括で変更する場合は再帰処理が必要です。ストリームラッパーでも使えますが、対応はラッパーやサーバー側機能に依存します(例:FTP サーバーの CHMOD 対応)。
構文 / シグネチャ
bool chmod(string $filename, int $permissions)
引数(表)
| 引数 | 型 | 必須 | 既定値 | 説明 |
|---|---|---|---|---|
$filename | string | ✅ | — | 対象パス(ローカル/対応ラッパー) |
$permissions | int | ✅ | — | 8進数で指定(例 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 | 所有者でない/権限不足、マウントオプションで制限 | 実行ユーザ/所有者を確認。必要に応じて sudo、chown、マウント設定を見直す |
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 経由に切替、またはサーバ設定の変更 |

