[PHP]posix_getcwd関数の使い方を完全解説!カレントディレクトリを取得する方法

PHP

はじめに

PHPでファイル操作やスクリプト実行を行う際、「現在どのディレクトリで作業しているのか?」を知りたいと思ったことはありませんか?

カレントディレクトリ(現在の作業ディレクトリ)を正確に把握することは、以下のような場面で重要です:

  • 相対パスでファイルにアクセスする場合
  • スクリプトの実行場所に依存する処理
  • ログファイルや一時ファイルの保存先を決定する場合
  • デバッグ時の環境確認

そんな時に活躍するのが**posix_getcwd関数**です。この関数を使えば、POSIX準拠システムで確実にカレントディレクトリのパスを取得できます。

この記事では、posix_getcwdの基本から実践的な活用方法まで、詳しく解説します。

posix_getcwdとは?

posix_getcwdは、現在の作業ディレクトリ(カレントディレクトリ)の絶対パスを返す関数です。POSIXシステムコールgetcwd()のPHPラッパーです。

基本構文

posix_getcwd(): string|false

パラメータ

この関数はパラメータを取りません。

戻り値

  • 成功時: カレントディレクトリの絶対パス(文字列)
  • 失敗時: false

対応環境

  • POSIX準拠システム(Linux、Unix、macOS)
  • Windows では利用不可(代わりにgetcwd()を使用)
  • POSIX拡張モジュールが必要

対応バージョン

  • PHP 4.0.0 以降で使用可能

基本的な使い方

カレントディレクトリの取得

<?php
$cwd = posix_getcwd();

if ($cwd !== false) {
    echo "現在のディレクトリ: {$cwd}\n";
} else {
    echo "カレントディレクトリの取得に失敗しました\n";
}

// 出力例:
// 現在のディレクトリ: /var/www/html
?>

getcwd()との比較

<?php
// 方法1: posix_getcwd()
$cwd1 = posix_getcwd();

// 方法2: getcwd()
$cwd2 = getcwd();

echo "posix_getcwd(): {$cwd1}\n";
echo "getcwd():       {$cwd2}\n";
echo "同じ値: " . ($cwd1 === $cwd2 ? 'はい' : 'いいえ') . "\n";

// 通常は同じ結果を返す
// ただし、posix_getcwd()はPOSIXシステム専用で、より低レベル
?>

実践的な使用例

例1: 環境情報の表示

<?php
function displayEnvironmentInfo() {
    echo "=== 実行環境情報 ===\n\n";
    
    // カレントディレクトリ
    $cwd = posix_getcwd();
    echo "カレントディレクトリ: {$cwd}\n";
    
    // スクリプトのディレクトリ
    $script_dir = dirname(__FILE__);
    echo "スクリプトのディレクトリ: {$script_dir}\n";
    
    // ホームディレクトリ
    $home = getenv('HOME');
    echo "ホームディレクトリ: {$home}\n";
    
    // 一致確認
    echo "\nディレクトリの関係:\n";
    echo "  CWDとスクリプト: " . ($cwd === $script_dir ? '同じ' : '異なる') . "\n";
    echo "  CWDとホーム: " . ($cwd === $home ? '同じ' : '異なる') . "\n";
    
    // 実ユーザー情報
    $uid = posix_getuid();
    $user = posix_getpwuid($uid);
    echo "\n実行ユーザー: {$user['name']} (UID: {$uid})\n";
    echo "ユーザーのホーム: {$user['dir']}\n";
}

displayEnvironmentInfo();
?>

例2: 相対パスと絶対パスの変換

<?php
class PathResolver {
    /**
     * 相対パスを絶対パスに変換
     */
    public static function resolveRelativePath($relative_path) {
        // すでに絶対パスの場合
        if (substr($relative_path, 0, 1) === '/') {
            return $relative_path;
        }
        
        // カレントディレクトリを取得
        $cwd = posix_getcwd();
        
        if ($cwd === false) {
            throw new RuntimeException("カレントディレクトリの取得に失敗しました");
        }
        
        // 相対パスを結合
        $absolute_path = $cwd . '/' . $relative_path;
        
        // パスを正規化(..や.を解決)
        return self::normalizePath($absolute_path);
    }
    
    /**
     * パスを正規化
     */
    private static function normalizePath($path) {
        $parts = explode('/', $path);
        $normalized = [];
        
        foreach ($parts as $part) {
            if ($part === '' || $part === '.') {
                continue;
            }
            
            if ($part === '..') {
                array_pop($normalized);
            } else {
                $normalized[] = $part;
            }
        }
        
        return '/' . implode('/', $normalized);
    }
    
    /**
     * 現在位置からの相対パスを取得
     */
    public static function getRelativePath($target_path) {
        $cwd = posix_getcwd();
        
        if ($cwd === false) {
            throw new RuntimeException("カレントディレクトリの取得に失敗しました");
        }
        
        // 両方を絶対パスに正規化
        $from = self::normalizePath($cwd);
        $to = self::normalizePath($target_path);
        
        // 共通部分を除去
        $from_parts = explode('/', trim($from, '/'));
        $to_parts = explode('/', trim($to, '/'));
        
        // 共通の先頭部分を見つける
        $common_length = 0;
        $min_length = min(count($from_parts), count($to_parts));
        
        for ($i = 0; $i < $min_length; $i++) {
            if ($from_parts[$i] !== $to_parts[$i]) {
                break;
            }
            $common_length++;
        }
        
        // 相対パスを構築
        $up_levels = count($from_parts) - $common_length;
        $relative_parts = array_fill(0, $up_levels, '..');
        $relative_parts = array_merge(
            $relative_parts,
            array_slice($to_parts, $common_length)
        );
        
        return implode('/', $relative_parts);
    }
}

// 使用例
echo "=== パス解決 ===\n\n";

$relative = './data/config.txt';
$absolute = PathResolver::resolveRelativePath($relative);
echo "相対パス: {$relative}\n";
echo "絶対パス: {$absolute}\n\n";

$target = '/var/www/html/uploads/images/photo.jpg';
$relative = PathResolver::getRelativePath($target);
echo "ターゲット: {$target}\n";
echo "現在位置からの相対パス: {$relative}\n";
?>

例3: ディレクトリの移動と復元

<?php
class DirectoryNavigator {
    private $original_dir;
    private $history = [];
    
    public function __construct() {
        $this->original_dir = posix_getcwd();
        
        if ($this->original_dir === false) {
            throw new RuntimeException("カレントディレクトリの取得に失敗しました");
        }
    }
    
    /**
     * ディレクトリを変更(履歴を記録)
     */
    public function changeDirectory($path) {
        $current = posix_getcwd();
        
        if (!chdir($path)) {
            throw new RuntimeException("ディレクトリの変更に失敗: {$path}");
        }
        
        $this->history[] = $current;
        
        return posix_getcwd();
    }
    
    /**
     * 前のディレクトリに戻る
     */
    public function goBack() {
        if (empty($this->history)) {
            throw new RuntimeException("履歴が空です");
        }
        
        $previous = array_pop($this->history);
        
        if (!chdir($previous)) {
            throw new RuntimeException("ディレクトリの変更に失敗: {$previous}");
        }
        
        return posix_getcwd();
    }
    
    /**
     * 元のディレクトリに戻る
     */
    public function restore() {
        if (!chdir($this->original_dir)) {
            throw new RuntimeException(
                "元のディレクトリへの復帰に失敗: {$this->original_dir}"
            );
        }
        
        $this->history = [];
        
        return posix_getcwd();
    }
    
    /**
     * 現在のディレクトリを取得
     */
    public function getCurrentDirectory() {
        return posix_getcwd();
    }
    
    /**
     * 履歴を取得
     */
    public function getHistory() {
        return $this->history;
    }
    
    /**
     * 元のディレクトリを取得
     */
    public function getOriginalDirectory() {
        return $this->original_dir;
    }
}

// 使用例
try {
    $nav = new DirectoryNavigator();
    
    echo "開始: " . $nav->getCurrentDirectory() . "\n";
    
    $nav->changeDirectory('/tmp');
    echo "変更後: " . $nav->getCurrentDirectory() . "\n";
    
    $nav->changeDirectory('/var');
    echo "再変更: " . $nav->getCurrentDirectory() . "\n";
    
    $nav->goBack();
    echo "戻る: " . $nav->getCurrentDirectory() . "\n";
    
    $nav->restore();
    echo "元に戻す: " . $nav->getCurrentDirectory() . "\n";
    
} catch (RuntimeException $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}
?>

例4: 安全なファイル操作

<?php
class SafeFileOperator {
    /**
     * 一時的にディレクトリを変更して操作を実行
     */
    public static function executeInDirectory($directory, callable $operation) {
        $original_dir = posix_getcwd();
        
        if ($original_dir === false) {
            throw new RuntimeException("カレントディレクトリの取得に失敗しました");
        }
        
        try {
            // ディレクトリを変更
            if (!chdir($directory)) {
                throw new RuntimeException("ディレクトリの変更に失敗: {$directory}");
            }
            
            // 操作を実行
            $result = $operation();
            
            return $result;
            
        } finally {
            // 必ず元のディレクトリに戻る
            if (!chdir($original_dir)) {
                throw new RuntimeException(
                    "元のディレクトリへの復帰に失敗: {$original_dir}"
                );
            }
        }
    }
    
    /**
     * カレントディレクトリ内のファイルを安全に処理
     */
    public static function processCurrentDirectory($pattern = '*') {
        $cwd = posix_getcwd();
        
        if ($cwd === false) {
            throw new RuntimeException("カレントディレクトリの取得に失敗しました");
        }
        
        echo "=== ディレクトリ処理 ===\n";
        echo "場所: {$cwd}\n\n";
        
        $files = glob($pattern);
        
        foreach ($files as $file) {
            $full_path = $cwd . '/' . $file;
            $size = filesize($full_path);
            $type = is_dir($full_path) ? 'DIR' : 'FILE';
            
            echo sprintf(
                "%-6s %-40s %10s bytes\n",
                $type,
                $file,
                number_format($size)
            );
        }
        
        echo "\n総数: " . count($files) . "個\n";
    }
}

// 使用例1: 一時的にディレクトリを変更して操作
try {
    $result = SafeFileOperator::executeInDirectory('/tmp', function() {
        echo "現在のディレクトリ: " . posix_getcwd() . "\n";
        
        // /tmp内でファイル作成
        file_put_contents('test.txt', 'テストデータ');
        
        return "操作完了";
    });
    
    echo "結果: {$result}\n";
    echo "現在のディレクトリ: " . posix_getcwd() . "\n";
    
} catch (RuntimeException $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}

// 使用例2: カレントディレクトリの処理
echo "\n";
SafeFileOperator::processCurrentDirectory('*.php');
?>

例5: プロジェクトルートの検出

<?php
class ProjectRootDetector {
    /**
     * プロジェクトルートを検出
     */
    public static function findProjectRoot($marker_files = ['composer.json', '.git', 'package.json']) {
        $current = posix_getcwd();
        
        if ($current === false) {
            throw new RuntimeException("カレントディレクトリの取得に失敗しました");
        }
        
        return self::searchUpwards($current, $marker_files);
    }
    
    /**
     * 上位ディレクトリを検索
     */
    private static function searchUpwards($start_dir, $marker_files) {
        $dir = $start_dir;
        
        while ($dir !== '/') {
            foreach ($marker_files as $marker) {
                $marker_path = $dir . '/' . $marker;
                
                if (file_exists($marker_path)) {
                    return [
                        'root' => $dir,
                        'marker' => $marker,
                        'marker_path' => $marker_path,
                        'depth' => self::calculateDepth($start_dir, $dir)
                    ];
                }
            }
            
            // 親ディレクトリに移動
            $parent = dirname($dir);
            
            if ($parent === $dir) {
                break; // ルートに到達
            }
            
            $dir = $parent;
        }
        
        return null;
    }
    
    /**
     * ディレクトリの深さを計算
     */
    private static function calculateDepth($from, $to) {
        $from_parts = explode('/', trim($from, '/'));
        $to_parts = explode('/', trim($to, '/'));
        
        return count($from_parts) - count($to_parts);
    }
    
    /**
     * プロジェクト情報を表示
     */
    public static function displayProjectInfo() {
        echo "=== プロジェクト情報 ===\n\n";
        
        $cwd = posix_getcwd();
        echo "現在のディレクトリ: {$cwd}\n";
        
        $root = self::findProjectRoot();
        
        if ($root === null) {
            echo "\nプロジェクトルートが見つかりませんでした\n";
            return;
        }
        
        echo "\nプロジェクトルート: {$root['root']}\n";
        echo "検出マーカー: {$root['marker']}\n";
        echo "マーカーパス: {$root['marker_path']}\n";
        echo "階層の深さ: {$root['depth']}\n";
        
        // 相対パスを計算
        $relative = str_replace($root['root'] . '/', '', $cwd);
        echo "ルートからの相対位置: {$relative}\n";
        
        // プロジェクト構造を表示
        if (is_dir($root['root'])) {
            echo "\nプロジェクト構造:\n";
            self::displayDirectoryTree($root['root'], 0, 2);
        }
    }
    
    /**
     * ディレクトリツリーを表示
     */
    private static function displayDirectoryTree($dir, $level = 0, $max_level = 2) {
        if ($level >= $max_level) {
            return;
        }
        
        $items = scandir($dir);
        $indent = str_repeat('  ', $level);
        
        foreach ($items as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }
            
            // 隠しファイルをスキップ(.git以外)
            if ($item[0] === '.' && $item !== '.git') {
                continue;
            }
            
            $path = $dir . '/' . $item;
            
            if (is_dir($path)) {
                echo "{$indent}📁 {$item}/\n";
                self::displayDirectoryTree($path, $level + 1, $max_level);
            } else {
                echo "{$indent}📄 {$item}\n";
            }
        }
    }
}

// 使用例
try {
    ProjectRootDetector::displayProjectInfo();
} catch (RuntimeException $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}
?>

ディレクトリ情報の取得

詳細情報の取得

<?php
function getDirectoryDetails() {
    $cwd = posix_getcwd();
    
    if ($cwd === false) {
        echo "カレントディレクトリの取得に失敗しました\n";
        return;
    }
    
    echo "=== ディレクトリ詳細情報 ===\n\n";
    echo "パス: {$cwd}\n";
    
    // ディレクトリ名
    echo "ディレクトリ名: " . basename($cwd) . "\n";
    
    // 親ディレクトリ
    echo "親ディレクトリ: " . dirname($cwd) . "\n";
    
    // 統計情報
    $stat = stat($cwd);
    if ($stat) {
        echo "\n統計情報:\n";
        echo "  inode: {$stat['ino']}\n";
        echo "  デバイス: {$stat['dev']}\n";
        echo "  パーミッション: " . sprintf('%o', $stat['mode'] & 0777) . "\n";
        
        // 所有者情報
        $owner = posix_getpwuid($stat['uid']);
        $group = posix_getgrgid($stat['gid']);
        echo "  所有者: {$owner['name']} (UID: {$stat['uid']})\n";
        echo "  グループ: {$group['name']} (GID: {$stat['gid']})\n";
        
        // タイムスタンプ
        echo "  アクセス: " . date('Y-m-d H:i:s', $stat['atime']) . "\n";
        echo "  変更: " . date('Y-m-d H:i:s', $stat['mtime']) . "\n";
        echo "  inode変更: " . date('Y-m-d H:i:s', $stat['ctime']) . "\n";
    }
    
    // ディスク使用量
    $free = disk_free_space($cwd);
    $total = disk_total_space($cwd);
    $used = $total - $free;
    $usage_percent = ($used / $total) * 100;
    
    echo "\nディスク使用量:\n";
    echo "  合計: " . self::formatBytes($total) . "\n";
    echo "  使用: " . self::formatBytes($used) . "\n";
    echo "  空き: " . self::formatBytes($free) . "\n";
    echo "  使用率: " . number_format($usage_percent, 2) . "%\n";
}

function formatBytes($bytes) {
    $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    $i = 0;
    
    while ($bytes >= 1024 && $i < count($units) - 1) {
        $bytes /= 1024;
        $i++;
    }
    
    return number_format($bytes, 2) . ' ' . $units[$i];
}

getDirectoryDetails();
?>

エラーハンドリング

失敗時の処理

<?php
function safeGetCurrentDirectory() {
    $cwd = posix_getcwd();
    
    if ($cwd === false) {
        $errno = posix_get_last_error();
        $errmsg = posix_strerror($errno);
        
        throw new RuntimeException(
            "カレントディレクトリの取得に失敗しました: {$errmsg} (errno: {$errno})"
        );
    }
    
    return $cwd;
}

// 使用例
try {
    $cwd = safeGetCurrentDirectory();
    echo "カレントディレクトリ: {$cwd}\n";
} catch (RuntimeException $e) {
    echo "エラー: " . $e->getMessage() . "\n";
    
    // フォールバック: getcwd()を試す
    $fallback = getcwd();
    if ($fallback !== false) {
        echo "フォールバック使用: {$fallback}\n";
    }
}
?>

クロスプラットフォーム対応

<?php
class DirectoryHelper {
    /**
     * カレントディレクトリを取得(クロスプラットフォーム)
     */
    public static function getCurrentDirectory() {
        // POSIX環境
        if (function_exists('posix_getcwd') && PHP_OS_FAMILY !== 'Windows') {
            $cwd = posix_getcwd();
            if ($cwd !== false) {
                return $cwd;
            }
        }
        
        // フォールバック: 標準のgetcwd()
        $cwd = getcwd();
        if ($cwd !== false) {
            return $cwd;
        }
        
        // 最終手段: __DIR__
        return __DIR__;
    }
    
    /**
     * 使用している取得方法を返す
     */
    public static function getMethod() {
        if (function_exists('posix_getcwd') && PHP_OS_FAMILY !== 'Windows') {
            return 'posix_getcwd';
        }
        return 'getcwd';
    }
}

// 使用例
echo "取得方法: " . DirectoryHelper::getMethod() . "\n";
echo "カレントディレクトリ: " . DirectoryHelper::getCurrentDirectory() . "\n";
?>

まとめ

posix_getcwdは、カレントディレクトリの絶対パスを取得するための関数です。

主な特徴:

  • ✅ 現在の作業ディレクトリの絶対パスを返す
  • ✅ POSIX準拠システムで動作
  • getcwd()と同等の機能を提供
  • ✅ より低レベルのシステムコールへのアクセス

使用場面:

  • 相対パスの解決
  • ファイル操作の基準ディレクトリ確認
  • プロジェクトルートの検出
  • 一時的なディレクトリ移動の実装

getcwd()との違い:

  • posix_getcwd() – POSIX専用、システムコール直接
  • getcwd() – クロスプラットフォーム、推奨

関連関数:

  • getcwd() – 標準のカレントディレクトリ取得
  • chdir() – ディレクトリ変更
  • posix_access() – ディレクトリアクセス権限チェック

ベストプラクティス:

  • クロスプラットフォーム対応が必要ならgetcwd()を使用
  • エラーハンドリングを必ず実装
  • ディレクトリ変更後は元に戻す処理を実装
  • try-finallyで確実にディレクトリを復元

この関数を理解して、より柔軟なファイルシステム操作を実現しましょう!

参考リンク


この記事が役に立ったら、ぜひシェアしてください!

タイトルとURLをコピーしました