topic: file-io (read / write / CSV / paths / upload概念) / ch05

ch05 — パス操作とディレクトリ走査

学習目標

  • __DIR__ / __FILE__ の違いを言える
  • dirname / basename / pathinfo で パスを 分解 できる
  • scandir でディレクトリ内のファイル一覧を取れる (. / .. の除外も含む)
  • realpath絶対パスに正規化 できると知っている

所要時間

スライド = 5 分 / ドリル = 約 20 分

ドリル

# 内容
01-path-info/ 与えられたパス文字列を分解して各部分 (dir / basename / filename / extension) を出力
02-list-dir/ tests/data/sample/ 配下のファイル名一覧 (. .. を除く) をソートして出力

採点

php scripts/grade.php topics/14-file-io/ch05-paths/drill/01-path-info/
php scripts/grade.php topics/14-file-io/ch05-paths/drill/02-list-dir/

確認のコツ

  • pathinfo は 連想配列 (dirname / basename / filename / extension) を返す
  • scandir の戻り値には . (カレント)・.. (親) が必ず混ざる。array_diff($files, ['.', '..']) で除く
  • ファイル一覧を アルファベット順 で揃えたいなら sort($files) を明示する (環境依存を消す)

解説スライド

教材スライド(Marp)を1枚ずつ表示します。矢印キー(←→)・スワイプ・ナビボタンで移動できます。

  1. <!-- LLM_CONTEXT: Lesson 14 / Chapter 5 目的: DIR / FILE / dirname / basename / pathinfo / realpath / scandir でパスを扱う 扱わない: アップロード (ch06) 読み上げ時間目安: 5 分 -->

    パス操作とディレクトリ走査

    Lesson 14 / Chapter 5

  2. __DIR____FILE__

    // /Users/you/proj/app/index.php というファイル内で...
    
    echo __FILE__;   // /Users/you/proj/app/index.php   (このファイル自身)
    echo __DIR__;    // /Users/you/proj/app             (このファイルが入っているディレクトリ)
    ▶ 3v4l で実行
    • __FILE__ : 実行中の PHP ファイルの 絶対パス
    • __DIR__ : それを dirname した結果 (= 親ディレクトリ)

    → どこから呼ばれても 「自分自身からの相対パス」 を組める。L14 で何度も登場。

  3. パスを分解する 3 つ + α

    $path = '/var/www/app/config/db.yaml';
    
    dirname($path);         // /var/www/app/config       (ディレクトリ部分)
    basename($path);        // db.yaml                   (ファイル名 + 拡張子)
    basename($path, '.yaml'); // db                      (拡張子を剥がす)
    pathinfo($path);
    // [
    //   'dirname'   => '/var/www/app/config',
    //   'basename'  => 'db.yaml',
    //   'extension' => 'yaml',
    //   'filename'  => 'db',
    // ]
    ▶ 3v4l で実行
    • dirname = 「親ディレクトリ」だけ
    • basename = 「最後の / 以降」 (= ファイル名)
    • pathinfo = 全部入りの 連想配列 (拡張子・拡張子無しの名前まで)
  4. pathinfo を実戦で使う

    <?php
    $info = pathinfo('/var/www/logs/access.2026-05-19.log');
    
    echo $info['dirname'];   // /var/www/logs
    echo $info['basename'];  // access.2026-05-19.log
    echo $info['filename'];  // access.2026-05-19
    echo $info['extension']; // log
    ▶ 3v4l で実行
    キー 中身
    dirname ディレクトリ部分
    basename ファイル名 (拡張子付き)
    filename ファイル名 (拡張子なし)
    extension 拡張子 (. なし)

    → アップロードファイルの拡張子検証 (ch06) でも pathinfo($name, PATHINFO_EXTENSION) がよく出る。

  5. realpath で絶対パスに正規化

    echo realpath('./tests/../tests/data/hello.txt');
    // /Users/you/proj/.../tests/data/hello.txt
    //   ↑ ".." や "./" を解決し、シンボリックリンクも展開して 絶対パスを返す
    
    echo realpath('does_not_exist.txt');   // false
    ▶ 3v4l で実行
    • 相対パス / ./ / ../ / シンボリックリンクを 全部解決
    • 実在しないパスには false を返す (= 存在チェック代わり にも使える)
    • セキュリティ上重要: ユーザー入力のパスは realpath で正規化してから 許可ディレクトリ配下か検証する
  6. scandir でディレクトリ一覧

    <?php
    $dir = __DIR__ . '/tests/data/sample';
    $files = scandir($dir);
    
    print_r($files);
    // [
    //   '.',           ← カレントディレクトリを示す特殊エントリ
    //   '..',          ← 親ディレクトリを示す特殊エントリ
    //   'a.txt',
    //   'b.txt',
    //   'c.txt',
    // ]
    ▶ 3v4l で実行
    • scandir. / .. を含む ことに必ず注意
    • 普通は除いて使う: array_diff($files, ['.', '..'])
    • 並び順は デフォルトで昇順 (SCANDIR_SORT_ASCENDING)。明示的にソートしたければ sort() を後段で
  7. scandir + 除外 + ソートのテンプレ

    <?php
    $dir = __DIR__ . '/tests/data/sample';
    
    $files = array_values(array_diff(scandir($dir), ['.', '..']));
    sort($files);   // 明示的にアルファベット順
    
    foreach ($files as $f) {
        echo $f . "\n";
    }
    ▶ 3v4l で実行
    • array_diff. .. を除く (array_values でキーを 0 から振り直す)
    • sort で明示ソート (環境差・PHP バージョン差を消す)
    • 必要なら is_file / is_dir でファイルとサブディレクトリを切り分け

    → ログ集計バッチ・テンプレートエンジンの 一覧取得などで頻出パターン。

  8. このチャプターでできるようになること

    __DIR__ / __FILE__ の違いを言える ✅ dirname / basename / pathinfo で パスを分解できる ✅ pathinfo の 4 つのキー (dirname / basename / filename / extension) を言える ✅ realpath の役割 (絶対パス化 + 存在チェック) を言える ✅ scandir の戻り値から . / .. を除いて ソートする定型が書ける

    → ドリルへ

1 / 8
スライドを全部一気に読む(縦表示)

<!-- LLM_CONTEXT: Lesson 14 / Chapter 5 目的: DIR / FILE / dirname / basename / pathinfo / realpath / scandir でパスを扱う 扱わない: アップロード (ch06) 読み上げ時間目安: 5 分 -->

パス操作とディレクトリ走査

Lesson 14 / Chapter 5


__DIR____FILE__

// /Users/you/proj/app/index.php というファイル内で...

echo __FILE__;   // /Users/you/proj/app/index.php   (このファイル自身)
echo __DIR__;    // /Users/you/proj/app             (このファイルが入っているディレクトリ)
▶ 3v4l で実行
  • __FILE__ : 実行中の PHP ファイルの 絶対パス
  • __DIR__ : それを dirname した結果 (= 親ディレクトリ)

→ どこから呼ばれても 「自分自身からの相対パス」 を組める。L14 で何度も登場。


パスを分解する 3 つ + α

$path = '/var/www/app/config/db.yaml';

dirname($path);         // /var/www/app/config       (ディレクトリ部分)
basename($path);        // db.yaml                   (ファイル名 + 拡張子)
basename($path, '.yaml'); // db                      (拡張子を剥がす)
pathinfo($path);
// [
//   'dirname'   => '/var/www/app/config',
//   'basename'  => 'db.yaml',
//   'extension' => 'yaml',
//   'filename'  => 'db',
// ]
▶ 3v4l で実行
  • dirname = 「親ディレクトリ」だけ
  • basename = 「最後の / 以降」 (= ファイル名)
  • pathinfo = 全部入りの 連想配列 (拡張子・拡張子無しの名前まで)

pathinfo を実戦で使う

<?php
$info = pathinfo('/var/www/logs/access.2026-05-19.log');

echo $info['dirname'];   // /var/www/logs
echo $info['basename'];  // access.2026-05-19.log
echo $info['filename'];  // access.2026-05-19
echo $info['extension']; // log
▶ 3v4l で実行
キー 中身
dirname ディレクトリ部分
basename ファイル名 (拡張子付き)
filename ファイル名 (拡張子なし)
extension 拡張子 (. なし)

→ アップロードファイルの拡張子検証 (ch06) でも pathinfo($name, PATHINFO_EXTENSION) がよく出る。


realpath で絶対パスに正規化

echo realpath('./tests/../tests/data/hello.txt');
// /Users/you/proj/.../tests/data/hello.txt
//   ↑ ".." や "./" を解決し、シンボリックリンクも展開して 絶対パスを返す

echo realpath('does_not_exist.txt');   // false
▶ 3v4l で実行
  • 相対パス / ./ / ../ / シンボリックリンクを 全部解決
  • 実在しないパスには false を返す (= 存在チェック代わり にも使える)
  • セキュリティ上重要: ユーザー入力のパスは realpath で正規化してから 許可ディレクトリ配下か検証する

scandir でディレクトリ一覧

<?php
$dir = __DIR__ . '/tests/data/sample';
$files = scandir($dir);

print_r($files);
// [
//   '.',           ← カレントディレクトリを示す特殊エントリ
//   '..',          ← 親ディレクトリを示す特殊エントリ
//   'a.txt',
//   'b.txt',
//   'c.txt',
// ]
▶ 3v4l で実行
  • scandir. / .. を含む ことに必ず注意
  • 普通は除いて使う: array_diff($files, ['.', '..'])
  • 並び順は デフォルトで昇順 (SCANDIR_SORT_ASCENDING)。明示的にソートしたければ sort() を後段で

scandir + 除外 + ソートのテンプレ

<?php
$dir = __DIR__ . '/tests/data/sample';

$files = array_values(array_diff(scandir($dir), ['.', '..']));
sort($files);   // 明示的にアルファベット順

foreach ($files as $f) {
    echo $f . "\n";
}
▶ 3v4l で実行
  • array_diff. .. を除く (array_values でキーを 0 から振り直す)
  • sort で明示ソート (環境差・PHP バージョン差を消す)
  • 必要なら is_file / is_dir でファイルとサブディレクトリを切り分け

→ ログ集計バッチ・テンプレートエンジンの 一覧取得などで頻出パターン。


このチャプターでできるようになること

__DIR__ / __FILE__ の違いを言える ✅ dirname / basename / pathinfo で パスを分解できる ✅ pathinfo の 4 つのキー (dirname / basename / filename / extension) を言える ✅ realpath の役割 (絶対パス化 + 存在チェック) を言える ✅ scandir の戻り値から . / .. を除いて ソートする定型が書ける

→ ドリルへ

演習問題の詳細

この章の演習問題の内容を読めます。実際に手元で解くには教材リポジトリを clone してください。

ドリル 01 — `pathinfo` でパスを分解する

問題

以下のパス文字列を pathinfo で分解し、4 つの要素 (dirname / basename / filename / extension) を コロン + 半角スペース 区切りで 1 行ずつ出力してください。

$path = '/var/www/logs/access.2026-05-19.log';
▶ 3v4l で実行

期待される出力:

dirname: /var/www/logs
basename: access.2026-05-19.log
filename: access.2026-05-19
extension: log

採点

php scripts/grade.php topics/14-file-io/ch05-paths/drill/01-path-info/

ヒント

$info = pathinfo($path);
echo "dirname: {$info['dirname']}\n";
echo "basename: {$info['basename']}\n";
echo "filename: {$info['filename']}\n";
echo "extension: {$info['extension']}\n";
▶ 3v4l で実行
キー 中身
dirname ディレクトリ部分 (/var/www/logs)
basename ファイル名 (拡張子付き) (access.2026-05-19.log)
filename ファイル名 (拡張子なし) (access.2026-05-19)
extension 拡張子 (log — 先頭の . は付かない)

つまづいたら

  • extension.log (先頭にドット) になる → pathinfoextension. 抜き で返す。自分で . を付けないこと
  • filenameaccess (途中まで) になる → pathinfo「最後の . より前」filename とする。仕様通りなのでこれが正解

ドリル 02 — `scandir` でディレクトリ一覧

問題

__DIR__ . '/tests/data/sample' 配下のファイル名を、... を除いた 上で 昇順 にソートして 1 行ずつ出力してください。

tests/data/sample/ の中身 (このリポジトリにコミット済み):

apple.txt
banana.txt
cherry.txt

期待される出力:

apple.txt
banana.txt
cherry.txt

採点

php scripts/grade.php topics/14-file-io/ch05-paths/drill/02-list-dir/

ヒント

$dir = __DIR__ . '/tests/data/sample';
$files = array_values(array_diff(scandir($dir), ['.', '..']));
sort($files);
foreach ($files as $f) {
    echo $f . "\n";
}
▶ 3v4l で実行
  • scandir($dir) はディレクトリ内のエントリ名を配列で返す
  • 戻り値には 必ず . (カレント)・.. (親) が含まれる ので array_diff で除く
  • array_valuesキーを 0 から振り直す (array_diff は元のキーを保つため、後段の sort 不要な順序ズレを防ぐ)
  • sort($files)明示的に昇順 (PHP / OS 環境差を消す)

つまづいたら

  • 出力の先頭に . .. が混ざる → array_diff(scandir($dir), ['.', '..']) でカレント・親エントリを除く
  • 順序がバラバラ → sort($files) を明示的に呼ぶ。scandir のデフォルトは昇順だが、OS / ファイルシステム差で揺れることもある
  • 出力が 1 行に詰まる → echo の末尾に "\n" を付けるのを忘れている

演習問題(2問)

  1. ドリル 01 — `pathinfo` でパスを分解する

    README.md starter.php answer.php

  2. ドリル 02 — `scandir` でディレクトリ一覧

    README.md starter.php answer.php

サイト内で問題文・雛形・解答例を確認できます。実際に手元で解くには教材リポジトリを clone してください。