[PHP]posix_getpwuid関数の使い方を完全解説!ユーザーIDから情報を取得する方法

PHP

はじめに

PHPでシステムプログラミングを行う際、ユーザーID(UID)の数値だけではなく、ユーザー名やホームディレクトリなどの詳細情報も知りたいと思ったことはありませんか?

Unixライクなシステムでは、ユーザーは数値ID(UID)で管理されていますが、人間が理解しやすいようにユーザー名ホームディレクトリシェルといった詳細情報も保持されています。

そのUIDから詳細なユーザー情報を取得するのが**posix_getpwuid関数**です。この関数を使えば、数値のUIDから人間が読みやすいユーザー情報を簡単に取得できます。

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

posix_getpwuidとは?

posix_getpwuidは、ユーザーID(UID)を指定して、そのユーザーの詳細情報を取得する関数です。

基本構文

posix_getpwuid(int $uid): array|false

パラメータ

  • $uid: ユーザーID(整数)

戻り値

成功時は以下の要素を持つ連想配列:

キー説明
nameユーザー名“www-data”
passwd暗号化されたパスワード(通常は”x”)“x”
uidユーザーID33
gidプライマリグループID33
gecosユーザーのフルネームや情報“Web Server User”
dirホームディレクトリ“/var/www”
shellデフォルトシェル“/usr/sbin/nologin”

失敗時(存在しないUID): false

対応環境

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

対応バージョン

  • PHP 4.0.0 以降で使用可能

基本的な使い方

ユーザーIDから情報を取得

<?php
// ユーザーID 1000
$uid = 1000;
$user = posix_getpwuid($uid);

if ($user) {
    echo "ユーザーID: {$user['uid']}\n";
    echo "ユーザー名: {$user['name']}\n";
    echo "ホームディレクトリ: {$user['dir']}\n";
    echo "シェル: {$user['shell']}\n";
    echo "GECOS: {$user['gecos']}\n";
} else {
    echo "ユーザーID {$uid} は存在しません\n";
}

// 出力例:
// ユーザーID: 1000
// ユーザー名: john
// ホームディレクトリ: /home/john
// シェル: /bin/bash
// GECOS: John Doe,,,
?>

現在のプロセスのユーザー情報を取得

<?php
// 実ユーザーIDの情報を取得
$uid = posix_getuid();
$user = posix_getpwuid($uid);

echo "現在のプロセスのユーザー:\n";
echo "  UID: {$uid}\n";
echo "  名前: {$user['name']}\n";
echo "  ホーム: {$user['dir']}\n";

// 実効ユーザーIDの情報を取得
$euid = posix_geteuid();
$eff_user = posix_getpwuid($euid);

echo "\n実効ユーザー:\n";
echo "  UID: {$euid}\n";
echo "  名前: {$eff_user['name']}\n";
?>

存在しないUIDのエラーハンドリング

<?php
function getUserInfo($uid) {
    $user = posix_getpwuid($uid);
    
    if ($user === false) {
        return [
            'exists' => false,
            'error' => "ユーザーID {$uid} が見つかりません"
        ];
    }
    
    return [
        'exists' => true,
        'uid' => $user['uid'],
        'name' => $user['name'],
        'home' => $user['dir'],
        'shell' => $user['shell']
    ];
}

// 使用例
$info = getUserInfo(99999); // 存在しないUID

if ($info['exists']) {
    echo "ユーザー: {$info['name']}\n";
} else {
    echo "エラー: {$info['error']}\n";
}
?>

実践的な使用例

例1: ファイル所有者の詳細表示

<?php
class FileOwnerAnalyzer {
    /**
     * ファイルの所有者情報を詳細表示
     */
    public static function analyzeFileOwner($filepath) {
        if (!file_exists($filepath)) {
            echo "ファイルが存在しません: {$filepath}\n";
            return;
        }
        
        $stat = stat($filepath);
        $uid = $stat['uid'];
        $gid = $stat['gid'];
        
        $user = posix_getpwuid($uid);
        $group = posix_getgrgid($gid);
        
        echo "=== ファイル所有者分析 ===\n";
        echo "ファイル: {$filepath}\n\n";
        
        if (!$user) {
            echo "所有者: UID {$uid} (情報取得不可)\n";
        } else {
            echo "所有者情報:\n";
            echo "  UID: {$user['uid']}\n";
            echo "  ユーザー名: {$user['name']}\n";
            echo "  フルネーム: {$user['gecos']}\n";
            echo "  ホームディレクトリ: {$user['dir']}\n";
            echo "  シェル: {$user['shell']}\n";
        }
        
        echo "\nグループ情報:\n";
        echo "  GID: {$gid}\n";
        echo "  グループ名: {$group['name']}\n";
        
        // パーミッション
        $perms = $stat['mode'];
        echo "\nパーミッション:\n";
        echo "  8進数: " . sprintf('%o', $perms & 0777) . "\n";
        echo "  所有者: ";
        echo ($perms & 0400 ? 'r' : '-');
        echo ($perms & 0200 ? 'w' : '-');
        echo ($perms & 0100 ? 'x' : '-');
        echo "\n";
        
        // 現在のプロセスとの比較
        $current_uid = posix_getuid();
        $current_user = posix_getpwuid($current_uid);
        
        echo "\n現在のプロセス:\n";
        echo "  実行ユーザー: {$current_user['name']} (UID: {$current_uid})\n";
        
        if ($current_uid === $uid) {
            echo "  ✓ このファイルの所有者です\n";
        } elseif ($current_uid === 0) {
            echo "  ✓ root権限で実行中(すべてのファイルにアクセス可能)\n";
        } else {
            echo "  ✗ このファイルの所有者ではありません\n";
        }
    }
    
    /**
     * ディレクトリ内のファイル所有者を分析
     */
    public static function analyzeDirectoryOwners($directory) {
        if (!is_dir($directory)) {
            echo "ディレクトリが存在しません: {$directory}\n";
            return;
        }
        
        echo "=== ディレクトリ所有者分析 ===\n";
        echo "ディレクトリ: {$directory}\n\n";
        
        $files = scandir($directory);
        $owner_stats = [];
        
        foreach ($files as $file) {
            if ($file === '.' || $file === '..') continue;
            
            $filepath = $directory . '/' . $file;
            if (!file_exists($filepath)) continue;
            
            $stat = stat($filepath);
            $uid = $stat['uid'];
            
            if (!isset($owner_stats[$uid])) {
                $user = posix_getpwuid($uid);
                $owner_stats[$uid] = [
                    'uid' => $uid,
                    'name' => $user ? $user['name'] : "(不明)",
                    'count' => 0,
                    'files' => []
                ];
            }
            
            $owner_stats[$uid]['count']++;
            if (count($owner_stats[$uid]['files']) < 5) {
                $owner_stats[$uid]['files'][] = $file;
            }
        }
        
        // ソート
        uasort($owner_stats, fn($a, $b) => $b['count'] <=> $a['count']);
        
        echo sprintf("%-6s %-20s %-10s %s\n",
            "UID", "ユーザー名", "ファイル数", "例");
        echo str_repeat("-", 80) . "\n";
        
        foreach ($owner_stats as $stat) {
            $examples = implode(', ', array_slice($stat['files'], 0, 3));
            if (count($stat['files']) > 3) {
                $examples .= '...';
            }
            
            echo sprintf("%-6d %-20s %-10d %s\n",
                $stat['uid'],
                substr($stat['name'], 0, 20),
                $stat['count'],
                substr($examples, 0, 30)
            );
        }
        
        echo "\n総ファイル数: " . array_sum(array_column($owner_stats, 'count')) . "\n";
        echo "所有者種類: " . count($owner_stats) . "\n";
    }
}

// 使用例
FileOwnerAnalyzer::analyzeFileOwner('/etc/passwd');
echo "\n";
FileOwnerAnalyzer::analyzeDirectoryOwners('/var/www/html');
?>

例2: システムユーザーの一覧表示

<?php
class SystemUserManager {
    /**
     * システムユーザー(UID < 1000)を一覧表示
     */
    public static function listSystemUsers() {
        echo "=== システムユーザー一覧 ===\n\n";
        echo sprintf("%-6s %-20s %-25s %-20s\n",
            "UID", "ユーザー名", "ホームディレクトリ", "シェル");
        echo str_repeat("-", 80) . "\n";
        
        for ($uid = 0; $uid < 1000; $uid++) {
            $user = posix_getpwuid($uid);
            
            if ($user) {
                echo sprintf("%-6d %-20s %-25s %-20s\n",
                    $user['uid'],
                    substr($user['name'], 0, 20),
                    substr($user['dir'], 0, 25),
                    basename($user['shell'])
                );
            }
        }
    }
    
    /**
     * 通常ユーザー(UID >= 1000)を一覧表示
     */
    public static function listRegularUsers() {
        echo "=== 通常ユーザー一覧 ===\n\n";
        
        $users = [];
        
        // /etc/passwdから取得
        $file = fopen('/etc/passwd', 'r');
        if ($file) {
            while (($line = fgets($file)) !== false) {
                $parts = explode(':', trim($line));
                if (count($parts) >= 7) {
                    $uid = (int)$parts[2];
                    
                    if ($uid >= 1000 && $uid < 65534) {
                        $user = posix_getpwuid($uid);
                        if ($user) {
                            $users[] = $user;
                        }
                    }
                }
            }
            fclose($file);
        }
        
        // ソート
        usort($users, fn($a, $b) => $a['uid'] <=> $b['uid']);
        
        echo sprintf("%-6s %-20s %-30s %-20s\n",
            "UID", "ユーザー名", "GECOS", "シェル");
        echo str_repeat("-", 80) . "\n";
        
        foreach ($users as $user) {
            $gecos = explode(',', $user['gecos'])[0];
            
            echo sprintf("%-6d %-20s %-30s %-20s\n",
                $user['uid'],
                substr($user['name'], 0, 20),
                substr($gecos, 0, 30),
                basename($user['shell'])
            );
        }
        
        echo "\n総ユーザー数: " . count($users) . "\n";
    }
    
    /**
     * 特定のシェルを使用しているユーザーを検索
     */
    public static function findUsersByShell($shell) {
        echo "=== シェル検索: {$shell} ===\n\n";
        
        $users = [];
        
        $file = fopen('/etc/passwd', 'r');
        if ($file) {
            while (($line = fgets($file)) !== false) {
                $parts = explode(':', trim($line));
                if (count($parts) >= 7 && $parts[6] === $shell) {
                    $uid = (int)$parts[2];
                    $user = posix_getpwuid($uid);
                    if ($user) {
                        $users[] = $user;
                    }
                }
            }
            fclose($file);
        }
        
        if (empty($users)) {
            echo "該当するユーザーはいません\n";
            return;
        }
        
        echo sprintf("%-6s %-20s %-30s\n",
            "UID", "ユーザー名", "ホームディレクトリ");
        echo str_repeat("-", 60) . "\n";
        
        foreach ($users as $user) {
            echo sprintf("%-6d %-20s %-30s\n",
                $user['uid'],
                $user['name'],
                $user['dir']
            );
        }
        
        echo "\n総ユーザー数: " . count($users) . "\n";
    }
}

// 使用例
SystemUserManager::listSystemUsers();
echo "\n";
SystemUserManager::listRegularUsers();
echo "\n";
SystemUserManager::findUsersByShell('/bin/bash');
?>

例3: ユーザー情報の比較

<?php
class UserComparator {
    /**
     * 2つのユーザーを比較
     */
    public static function compareUsers($uid1, $uid2) {
        $user1 = posix_getpwuid($uid1);
        $user2 = posix_getpwuid($uid2);
        
        if (!$user1 || !$user2) {
            echo "いずれかのユーザーが存在しません\n";
            return;
        }
        
        echo "=== ユーザー比較 ===\n\n";
        
        echo sprintf("%-25s %-30s %-30s\n", 
            "項目", 
            "{$user1['name']} (UID:{$uid1})",
            "{$user2['name']} (UID:{$uid2})"
        );
        echo str_repeat("-", 90) . "\n";
        
        $fields = [
            'UID' => 'uid',
            'プライマリGID' => 'gid',
            'GECOS' => 'gecos',
            'ホームディレクトリ' => 'dir',
            'シェル' => 'shell'
        ];
        
        foreach ($fields as $label => $field) {
            $value1 = $user1[$field];
            $value2 = $user2[$field];
            $match = ($value1 === $value2) ? ' [同じ]' : '';
            
            echo sprintf("%-25s %-30s %-30s%s\n",
                $label,
                substr($value1, 0, 30),
                substr($value2, 0, 30),
                $match
            );
        }
        
        // グループ所属の比較
        echo "\n【グループ所属】\n";
        
        $group1 = posix_getgrgid($user1['gid']);
        $group2 = posix_getgrgid($user2['gid']);
        
        echo "{$user1['name']}: {$group1['name']}\n";
        echo "{$user2['name']}: {$group2['name']}\n";
    }
    
    /**
     * ユーザー名から比較
     */
    public static function compareUsersByName($name1, $name2) {
        $user1 = posix_getpwnam($name1);
        $user2 = posix_getpwnam($name2);
        
        if (!$user1 || !$user2) {
            echo "いずれかのユーザーが存在しません\n";
            return;
        }
        
        self::compareUsers($user1['uid'], $user2['uid']);
    }
}

// 使用例
UserComparator::compareUsersByName('root', 'www-data');
?>

例4: ホームディレクトリの分析

<?php
class HomeDirectoryAnalyzer {
    /**
     * ホームディレクトリの存在と権限を確認
     */
    public static function analyzeHomeDirectory($uid) {
        $user = posix_getpwuid($uid);
        
        if (!$user) {
            echo "ユーザーID {$uid} が存在しません\n";
            return;
        }
        
        $home = $user['dir'];
        
        echo "=== ホームディレクトリ分析 ===\n";
        echo "ユーザー: {$user['name']} (UID: {$uid})\n";
        echo "ホームディレクトリ: {$home}\n\n";
        
        // 存在確認
        if (!file_exists($home)) {
            echo "✗ ホームディレクトリが存在しません\n";
            return;
        }
        
        echo "✓ ホームディレクトリが存在します\n\n";
        
        // ディレクトリか確認
        if (!is_dir($home)) {
            echo "✗ パスがディレクトリではありません\n";
            return;
        }
        
        // 所有者確認
        $stat = stat($home);
        $owner_uid = $stat['uid'];
        $owner = posix_getpwuid($owner_uid);
        
        echo "所有者情報:\n";
        echo "  UID: {$owner_uid}\n";
        echo "  ユーザー名: {$owner['name']}\n";
        
        if ($owner_uid === $uid) {
            echo "  ✓ 正しい所有者です\n";
        } else {
            echo "  ✗ 所有者が異なります(期待: {$user['name']})\n";
        }
        
        // パーミッション
        $perms = $stat['mode'];
        echo "\nパーミッション: " . sprintf('%o', $perms & 0777) . "\n";
        
        $owner_rwx = [
            ($perms & 0400) !== 0,
            ($perms & 0200) !== 0,
            ($perms & 0100) !== 0
        ];
        
        echo "  所有者: " . 
            ($owner_rwx[0] ? 'r' : '-') .
            ($owner_rwx[1] ? 'w' : '-') .
            ($owner_rwx[2] ? 'x' : '-') . "\n";
        
        // 推奨パーミッション
        $recommended = 0755;
        $current = $perms & 0777;
        
        if ($current !== $recommended) {
            echo "\n⚠ 推奨パーミッション: " . sprintf('%o', $recommended) . "\n";
        }
        
        // 容量確認
        $size = self::getDirectorySize($home);
        echo "\n使用容量: " . self::formatBytes($size) . "\n";
    }
    
    /**
     * ディレクトリサイズを取得
     */
    private static function getDirectorySize($dir, $max_depth = 3, $current_depth = 0) {
        $size = 0;
        
        if ($current_depth >= $max_depth) {
            return $size;
        }
        
        $files = @scandir($dir);
        if (!$files) {
            return $size;
        }
        
        foreach ($files as $file) {
            if ($file === '.' || $file === '..') continue;
            
            $filepath = $dir . '/' . $file;
            
            if (is_link($filepath)) {
                continue;
            }
            
            if (is_file($filepath)) {
                $size += @filesize($filepath);
            } elseif (is_dir($filepath)) {
                $size += self::getDirectorySize($filepath, $max_depth, $current_depth + 1);
            }
        }
        
        return $size;
    }
    
    /**
     * バイト数をフォーマット
     */
    private static 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];
    }
    
    /**
     * すべてのユーザーのホームディレクトリをチェック
     */
    public static function checkAllHomeDirectories() {
        echo "=== 全ユーザーのホームディレクトリチェック ===\n\n";
        
        $file = fopen('/etc/passwd', 'r');
        $issues = [];
        
        if ($file) {
            while (($line = fgets($file)) !== false) {
                $parts = explode(':', trim($line));
                if (count($parts) >= 7) {
                    $uid = (int)$parts[2];
                    $user = posix_getpwuid($uid);
                    
                    if ($user && $uid >= 1000 && $uid < 65534) {
                        $home = $user['dir'];
                        
                        if (!file_exists($home)) {
                            $issues[] = "{$user['name']}: ホームディレクトリが存在しません ({$home})";
                        } elseif (!is_dir($home)) {
                            $issues[] = "{$user['name']}: パスがディレクトリではありません ({$home})";
                        } else {
                            $stat = stat($home);
                            if ($stat['uid'] !== $uid) {
                                $owner = posix_getpwuid($stat['uid']);
                                $issues[] = "{$user['name']}: 所有者が異なります (実際: {$owner['name']})";
                            }
                        }
                    }
                }
            }
            fclose($file);
        }
        
        if (empty($issues)) {
            echo "✓ すべてのホームディレクトリは正常です\n";
        } else {
            echo "⚠ 以下の問題が見つかりました:\n\n";
            foreach ($issues as $issue) {
                echo "  - {$issue}\n";
            }
        }
    }
}

// 使用例
HomeDirectoryAnalyzer::analyzeHomeDirectory(1000);
echo "\n";
HomeDirectoryAnalyzer::checkAllHomeDirectories();
?>

例5: ユーザーキャッシュの実装

<?php
class UserCache {
    private static $cache = [];
    private static $hits = 0;
    private static $misses = 0;
    
    /**
     * キャッシュ付きでユーザー情報を取得
     */
    public static function getUserInfo($uid) {
        // キャッシュヒット
        if (isset(self::$cache[$uid])) {
            self::$hits++;
            return self::$cache[$uid];
        }
        
        // キャッシュミス - 実際に取得
        self::$misses++;
        $user = posix_getpwuid($uid);
        
        if ($user) {
            self::$cache[$uid] = $user;
        }
        
        return $user;
    }
    
    /**
     * 複数のUIDを一括取得
     */
    public static function getMultipleUsers($uids) {
        $results = [];
        
        foreach ($uids as $uid) {
            $user = self::getUserInfo($uid);
            if ($user) {
                $results[$uid] = $user;
            }
        }
        
        return $results;
    }
    
    /**
     * キャッシュ統計を表示
     */
    public static function displayStats() {
        $total = self::$hits + self::$misses;
        $hit_rate = $total > 0 ? (self::$hits / $total) * 100 : 0;
        
        echo "=== ユーザーキャッシュ統計 ===\n";
        echo "キャッシュヒット: " . self::$hits . "\n";
        echo "キャッシュミス: " . self::$misses . "\n";
        echo "ヒット率: " . number_format($hit_rate, 2) . "%\n";
        echo "キャッシュサイズ: " . count(self::$cache) . "エントリ\n";
    }
    
    /**
     * ユーザー名から検索(キャッシュ利用)
     */
    public static function findByName($name) {
        // キャッシュ内を検索
        foreach (self::$cache as $user) {
            if ($user['name'] === $name) {
                self::$hits++;
                return $user;
            }
        }
        
        // 見つからなければ直接取得
        self::$misses++;
        $user = posix_getpwnam($name);
        
        if ($user) {
            self::$cache[$user['uid']] = $user;
        }
        
        return $user;
    }
    
    /**
     * キャッシュをクリア
     */
    public static function clearCache() {
        self::$cache = [];
        self::$hits = 0;
        self::$misses = 0;
    }
}

// 使用例
$uids = [0, 1000, 33, 1000, 33, 0]; // 重複あり

foreach ($uids as $uid) {
    $user = UserCache::getUserInfo($uid);
    if ($user) {
        echo "UID {$uid}: {$user['name']}\n";
    }
}

echo "\n";
UserCache::displayStats();
?>

posix_getpwnamとの関係

UIDから検索 vs 名前から検索

<?php
// 方法1: UIDから検索
$user_by_uid = posix_getpwuid(1000);
echo "UIDから: {$user_by_uid['name']}\n";

// 方法2: 名前から検索
$user_by_name = posix_getpwnam('john');
echo "名前から: {$user_by_name['name']}\n";

// 両方とも同じ情報を返す
echo "\n同じユーザー: ";
echo ($user_by_uid['uid'] === $user_by_name['uid']) ? 'はい' : 'いいえ';
echo "\n";
?>

まとめ

posix_getpwuidは、ユーザーIDから詳細なユーザー情報を取得する関数です。

主な特徴:

  • ✅ UIDからユーザー名、ホームディレクトリなどを取得
  • ✅ ユーザーの詳細情報を連想配列で返す
  • ✅ 存在しないUIDの場合はfalseを返す
  • ✅ システムユーザー管理に不可欠

戻り値の構造:

  • name – ユーザー名
  • passwd – パスワード(通常は”x”)
  • uid – ユーザーID
  • gid – プライマリグループID
  • gecos – ユーザーのフルネームや情報
  • dir – ホームディレクトリ
  • shell – デフォルトシェル

使用場面:

  • UIDを人間が読める形式に変換
  • ファイル所有者の確認
  • ホームディレクトリの取得
  • ユーザー管理ツールの作成

関連関数:

  • posix_getpwnam() – ユーザー名から情報を取得
  • posix_getuid() – 実ユーザーIDを取得
  • posix_geteuid() – 実効ユーザーIDを取得
  • posix_getgrgid() – グループIDから情報を取得

ベストプラクティス:

  • 存在チェックを必ず行う(falseの確認)
  • 頻繁に参照する場合はキャッシュを実装
  • gecosフィールドはカンマ区切りで複数の情報を含む場合がある
  • ホームディレクトリの存在を確認してから使用
  • セキュリティ上、パスワード情報は取得できない(”x”が返る)

GECOSフィールドについて: GECOSフィールドは通常、カンマで区切られた以下の情報を含みます:

  1. フルネーム
  2. オフィス番号
  3. オフィス電話番号
  4. 自宅電話番号
  5. その他

例: “John Doe,Room 123,555-1234,555-5678”

この関数を理解して、より詳細なユーザー情報管理とアクセス制御を実現しましょう!

参考リンク


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

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