[PHP]realpath_cache_get関数の使い方を徹底解説!パフォーマンス最適化の秘訣

PHP

はじめに

PHPアプリケーションのパフォーマンスを最適化する際、意外と見落とされがちなのがrealpathキャッシュです。PHPは内部的にファイルパスの解決結果をキャッシュしており、これがパフォーマンスに大きく影響します。

realpath_cache_get関数を使うと、このキャッシュの状態を確認でき、パフォーマンスのボトルネックを発見できます。この記事では、realpath_cache_get関数の使い方から、実践的なパフォーマンスチューニング方法まで、詳しく解説していきます。

realpath_cache_get関数とは?

realpath_cache_get関数は、PHPが内部で保持しているrealpathキャッシュの内容を取得する関数です。ファイルパスの解決結果がどのようにキャッシュされているかを確認できます。

基本的な構文

<?php
realpath_cache_get(): array
?>
  • 引数: なし
  • 戻り値: キャッシュされたパス情報の配列

最もシンプルな使用例

<?php
// 何かファイル操作を実行
require 'config.php';
include 'functions.php';

// キャッシュの内容を取得
$cache = realpath_cache_get();
print_r($cache);
?>

realpathキャッシュとは?

PHPは、ファイルパスを解決する際(realpath()requireincludefile_exists()など)、その結果を内部的にキャッシュします。これにより、同じパスに対する繰り返しのファイルシステムアクセスを削減し、パフォーマンスを向上させます。

キャッシュされる情報

<?php
$cache = realpath_cache_get();

// キャッシュの各エントリには以下の情報が含まれます
foreach ($cache as $path => $info) {
    echo "パス: {$path}\n";
    echo "  key: {$info['key']}\n";           // キャッシュキー
    echo "  is_dir: {$info['is_dir']}\n";     // ディレクトリかどうか
    echo "  realpath: {$info['realpath']}\n"; // 解決後の絶対パス
    echo "  expires: {$info['expires']}\n";   // 有効期限(Unixタイムスタンプ)
    echo "\n";
}
?>

実践的な使用例

1. キャッシュ使用状況の確認

<?php
function getCacheStats() {
    $cache = realpath_cache_get();
    $size = realpath_cache_size();
    
    $stats = [
        'entries' => count($cache),
        'size' => $size,
        'size_mb' => round($size / 1024 / 1024, 2),
        'max_size' => ini_get('realpath_cache_size'),
        'ttl' => ini_get('realpath_cache_ttl')
    ];
    
    return $stats;
}

// 使用例
$stats = getCacheStats();
echo "キャッシュエントリ数: {$stats['entries']}\n";
echo "使用サイズ: {$stats['size_mb']} MB\n";
echo "最大サイズ: {$stats['max_size']}\n";
echo "TTL: {$stats['ttl']} 秒\n";
?>

2. 特定のパスがキャッシュされているか確認

<?php
function isPathCached($path) {
    $cache = realpath_cache_get();
    
    // 絶対パスに変換
    $absolutePath = realpath($path);
    
    if ($absolutePath === false) {
        return false;
    }
    
    // キャッシュに存在するかチェック
    return isset($cache[$absolutePath]);
}

// 使用例
$configPath = __DIR__ . '/config.php';

if (isPathCached($configPath)) {
    echo "{$configPath} はキャッシュされています\n";
} else {
    echo "{$configPath} はキャッシュされていません\n";
}
?>

3. キャッシュの詳細分析

<?php
function analyzeCacheUsage() {
    $cache = realpath_cache_get();
    
    $analysis = [
        'total_entries' => 0,
        'directories' => 0,
        'files' => 0,
        'expired' => 0,
        'active' => 0,
        'paths_by_extension' => []
    ];
    
    $now = time();
    
    foreach ($cache as $path => $info) {
        $analysis['total_entries']++;
        
        // ディレクトリとファイルのカウント
        if ($info['is_dir']) {
            $analysis['directories']++;
        } else {
            $analysis['files']++;
            
            // 拡張子別の集計
            $ext = pathinfo($path, PATHINFO_EXTENSION);
            if ($ext) {
                $analysis['paths_by_extension'][$ext] = 
                    ($analysis['paths_by_extension'][$ext] ?? 0) + 1;
            }
        }
        
        // 有効期限のチェック
        if ($info['expires'] < $now) {
            $analysis['expired']++;
        } else {
            $analysis['active']++;
        }
    }
    
    return $analysis;
}

// 使用例
$analysis = analyzeCacheUsage();
echo "=== realpathキャッシュ分析 ===\n";
echo "総エントリ数: {$analysis['total_entries']}\n";
echo "ディレクトリ: {$analysis['directories']}\n";
echo "ファイル: {$analysis['files']}\n";
echo "有効: {$analysis['active']}\n";
echo "期限切れ: {$analysis['expired']}\n";
echo "\n拡張子別:\n";
foreach ($analysis['paths_by_extension'] as $ext => $count) {
    echo "  .{$ext}: {$count}\n";
}
?>

4. キャッシュヒット率の測定

<?php
class RealpathCacheMonitor {
    private $initialCache = [];
    private $operations = 0;
    private $hits = 0;
    
    public function __construct() {
        $this->initialCache = realpath_cache_get();
    }
    
    public function trackOperation($path) {
        $this->operations++;
        
        // 操作前にキャッシュに存在するかチェック
        $absolutePath = @realpath($path);
        
        if ($absolutePath !== false && isset($this->initialCache[$absolutePath])) {
            $this->hits++;
        }
        
        // 実際の操作を実行(ここでは例として空)
        // file_exists($path), require $path など
    }
    
    public function getHitRate() {
        if ($this->operations === 0) {
            return 0;
        }
        
        return round(($this->hits / $this->operations) * 100, 2);
    }
    
    public function getStats() {
        return [
            'operations' => $this->operations,
            'hits' => $this->hits,
            'misses' => $this->operations - $this->hits,
            'hit_rate' => $this->getHitRate() . '%'
        ];
    }
}

// 使用例
$monitor = new RealpathCacheMonitor();

$files = ['config.php', 'functions.php', 'classes/User.php'];
foreach ($files as $file) {
    $monitor->trackOperation($file);
}

$stats = $monitor->getStats();
echo "キャッシュヒット率: {$stats['hit_rate']}\n";
?>

5. パフォーマンスボトルネックの検出

<?php
function detectCacheBottlenecks() {
    $cache = realpath_cache_get();
    $size = realpath_cache_size();
    $maxSize = ini_get('realpath_cache_size');
    
    // バイト単位に変換
    $maxSizeBytes = parseSize($maxSize);
    
    $issues = [];
    
    // キャッシュサイズのチェック
    $usage = ($size / $maxSizeBytes) * 100;
    if ($usage > 80) {
        $issues[] = [
            'type' => 'cache_size',
            'severity' => 'high',
            'message' => sprintf(
                'キャッシュ使用率が高い: %.2f%% (%s / %s)',
                $usage,
                formatBytes($size),
                $maxSize
            ),
            'recommendation' => 'realpath_cache_sizeを増やすことを検討してください'
        ];
    }
    
    // エントリ数のチェック
    $entryCount = count($cache);
    if ($entryCount > 10000) {
        $issues[] = [
            'type' => 'entry_count',
            'severity' => 'medium',
            'message' => "キャッシュエントリが非常に多い: {$entryCount}",
            'recommendation' => 'オートローダーの最適化やファイル構成の見直しを検討してください'
        ];
    }
    
    // TTLのチェック
    $ttl = (int)ini_get('realpath_cache_ttl');
    if ($ttl < 120) {
        $issues[] = [
            'type' => 'ttl',
            'severity' => 'medium',
            'message' => "TTLが短い: {$ttl}秒",
            'recommendation' => '本番環境ではrealpath_cache_ttlを300秒以上に設定することを推奨'
        ];
    }
    
    return $issues;
}

function parseSize($size) {
    $unit = strtolower(substr($size, -1));
    $value = (int)$size;
    
    switch ($unit) {
        case 'g': return $value * 1024 * 1024 * 1024;
        case 'm': return $value * 1024 * 1024;
        case 'k': return $value * 1024;
        default: return $value;
    }
}

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

// 使用例
$issues = detectCacheBottlenecks();

if (empty($issues)) {
    echo "問題は検出されませんでした\n";
} else {
    echo "=== パフォーマンスの問題 ===\n";
    foreach ($issues as $issue) {
        echo "[{$issue['severity']}] {$issue['message']}\n";
        echo "  推奨: {$issue['recommendation']}\n\n";
    }
}
?>

6. キャッシュ内容の可視化

<?php
function visualizeCacheContents($limit = 20) {
    $cache = realpath_cache_get();
    $now = time();
    
    echo "=== realpathキャッシュの内容(上位{$limit}件) ===\n\n";
    
    $count = 0;
    foreach ($cache as $path => $info) {
        if ($count >= $limit) break;
        
        $type = $info['is_dir'] ? 'DIR' : 'FILE';
        $expiresIn = $info['expires'] - $now;
        $status = $expiresIn > 0 ? "有効({$expiresIn}秒)" : "期限切れ";
        
        echo "[{$type}] {$path}\n";
        echo "  実パス: {$info['realpath']}\n";
        echo "  状態: {$status}\n";
        echo "\n";
        
        $count++;
    }
    
    $total = count($cache);
    if ($total > $limit) {
        echo "... 他 " . ($total - $limit) . " 件\n";
    }
}

// 使用例
visualizeCacheContents(10);
?>

パフォーマンスチューニング

php.iniでの設定

realpath キャッシュの設定を最適化することが重要です:

; キャッシュサイズ(デフォルト: 4M)
; 大規模アプリケーションでは16M以上を推奨
realpath_cache_size = 16M

; キャッシュTTL(デフォルト: 120秒)
; 本番環境では長めに設定(300-600秒)
realpath_cache_ttl = 600

設定の確認と動的変更

<?php
// 現在の設定を確認
echo "realpath_cache_size: " . ini_get('realpath_cache_size') . "\n";
echo "realpath_cache_ttl: " . ini_get('realpath_cache_ttl') . "\n";

// 注意: これらの設定は実行時に変更できません
// php.ini、.htaccess、またはphp-fpm設定で変更する必要があります
?>

最適な設定値の決定

<?php
function recommendCacheSettings() {
    $cache = realpath_cache_get();
    $currentSize = realpath_cache_size();
    $entryCount = count($cache);
    
    // エントリあたりの平均サイズを計算
    $avgEntrySize = $entryCount > 0 ? $currentSize / $entryCount : 0;
    
    // 推奨サイズを計算(バッファ20%を含む)
    $recommendedSize = ceil($currentSize * 1.2);
    
    // 最小でも4MB
    if ($recommendedSize < 4 * 1024 * 1024) {
        $recommendedSize = 4 * 1024 * 1024;
    }
    
    // 推奨TTL
    $recommendedTtl = 300; // 5分(本番環境)
    
    return [
        'current_size' => formatBytes($currentSize),
        'current_entries' => $entryCount,
        'avg_entry_size' => formatBytes($avgEntrySize),
        'recommended_size' => formatBytes($recommendedSize),
        'recommended_size_ini' => ceil($recommendedSize / 1024 / 1024) . 'M',
        'recommended_ttl' => $recommendedTtl,
        'php_ini_config' => sprintf(
            "realpath_cache_size = %dM\nrealpath_cache_ttl = %d",
            ceil($recommendedSize / 1024 / 1024),
            $recommendedTtl
        )
    ];
}

// 使用例
$recommendations = recommendCacheSettings();
echo "=== 推奨設定 ===\n";
echo "現在のサイズ: {$recommendations['current_size']}\n";
echo "現在のエントリ数: {$recommendations['current_entries']}\n";
echo "推奨サイズ: {$recommendations['recommended_size']}\n";
echo "推奨TTL: {$recommendations['recommended_ttl']}秒\n\n";
echo "php.iniに追加:\n";
echo $recommendations['php_ini_config'] . "\n";
?>

モニタリングとデバッグ

デバッグ用のヘルパー関数

<?php
class RealpathCacheDebugger {
    /**
     * キャッシュの完全な情報をダンプ
     */
    public static function dump() {
        $cache = realpath_cache_get();
        $size = realpath_cache_size();
        
        echo "=== Realpath Cache Dump ===\n";
        echo "Total Size: " . formatBytes($size) . "\n";
        echo "Total Entries: " . count($cache) . "\n";
        echo "Max Size: " . ini_get('realpath_cache_size') . "\n";
        echo "TTL: " . ini_get('realpath_cache_ttl') . " seconds\n\n";
        
        echo "=== Cache Entries ===\n";
        foreach ($cache as $path => $info) {
            self::dumpEntry($path, $info);
        }
    }
    
    /**
     * 単一エントリの詳細表示
     */
    private static function dumpEntry($path, $info) {
        $now = time();
        $expiresIn = $info['expires'] - $now;
        
        echo "Path: {$path}\n";
        echo "  Real Path: {$info['realpath']}\n";
        echo "  Type: " . ($info['is_dir'] ? 'Directory' : 'File') . "\n";
        echo "  Key: {$info['key']}\n";
        echo "  Expires: " . date('Y-m-d H:i:s', $info['expires']) . 
             " (in {$expiresIn}s)\n";
        echo "\n";
    }
    
    /**
     * 特定のディレクトリ配下のキャッシュエントリを表示
     */
    public static function showDirectory($directory) {
        $cache = realpath_cache_get();
        $realDir = realpath($directory);
        
        if ($realDir === false) {
            echo "ディレクトリが存在しません: {$directory}\n";
            return;
        }
        
        echo "=== {$realDir} 配下のキャッシュ ===\n";
        
        $count = 0;
        foreach ($cache as $path => $info) {
            if (strpos($path, $realDir) === 0) {
                $relativePath = substr($path, strlen($realDir) + 1);
                echo ($info['is_dir'] ? '[DIR]  ' : '[FILE] ') . $relativePath . "\n";
                $count++;
            }
        }
        
        echo "\n合計: {$count} エントリ\n";
    }
    
    /**
     * キャッシュのJSON出力(API用)
     */
    public static function toJson() {
        $cache = realpath_cache_get();
        $size = realpath_cache_size();
        
        $data = [
            'metadata' => [
                'size' => $size,
                'size_formatted' => formatBytes($size),
                'entries' => count($cache),
                'max_size' => ini_get('realpath_cache_size'),
                'ttl' => ini_get('realpath_cache_ttl'),
                'timestamp' => time()
            ],
            'entries' => []
        ];
        
        foreach ($cache as $path => $info) {
            $data['entries'][] = [
                'path' => $path,
                'realpath' => $info['realpath'],
                'is_dir' => (bool)$info['is_dir'],
                'expires' => $info['expires']
            ];
        }
        
        return json_encode($data, JSON_PRETTY_PRINT);
    }
}

// 使用例

// 完全ダンプ
// RealpathCacheDebugger::dump();

// 特定ディレクトリ
RealpathCacheDebugger::showDirectory(__DIR__);

// JSON出力
// echo RealpathCacheDebugger::toJson();
?>

本番環境でのモニタリング

<?php
/**
 * 本番環境用の軽量モニタリング
 */
class RealpathCacheMonitoring {
    private $logFile;
    
    public function __construct($logFile = '/var/log/php-realpath-cache.log') {
        $this->logFile = $logFile;
    }
    
    /**
     * キャッシュメトリクスをログに記録
     */
    public function logMetrics() {
        $stats = [
            'timestamp' => date('Y-m-d H:i:s'),
            'size' => realpath_cache_size(),
            'entries' => count(realpath_cache_get()),
            'max_size' => ini_get('realpath_cache_size'),
            'ttl' => ini_get('realpath_cache_ttl')
        ];
        
        $logLine = json_encode($stats) . "\n";
        file_put_contents($this->logFile, $logLine, FILE_APPEND);
    }
    
    /**
     * アラート条件をチェック
     */
    public function checkAlerts() {
        $size = realpath_cache_size();
        $maxSize = parseSize(ini_get('realpath_cache_size'));
        $usage = ($size / $maxSize) * 100;
        
        $alerts = [];
        
        if ($usage > 90) {
            $alerts[] = [
                'level' => 'critical',
                'message' => "realpathキャッシュ使用率が90%を超えています: {$usage}%"
            ];
        } elseif ($usage > 80) {
            $alerts[] = [
                'level' => 'warning',
                'message' => "realpathキャッシュ使用率が80%を超えています: {$usage}%"
            ];
        }
        
        return $alerts;
    }
}

// 使用例(cronやアプリケーション起動時に実行)
$monitor = new RealpathCacheMonitoring();
$monitor->logMetrics();

$alerts = $monitor->checkAlerts();
foreach ($alerts as $alert) {
    error_log("[{$alert['level']}] {$alert['message']}");
}
?>

よくある問題と解決策

問題1: キャッシュがすぐに満杯になる

<?php
// 原因: realpath_cache_sizeが小さすぎる

// 解決策: php.iniで増やす
// realpath_cache_size = 32M  // デフォルトの4Mから増やす

// 現在の使用状況を確認
$size = realpath_cache_size();
$maxSize = ini_get('realpath_cache_size');
echo "使用率: " . round(($size / parseSize($maxSize)) * 100, 2) . "%\n";
?>

問題2: 開発環境でファイル変更が反映されない

<?php
// 原因: TTLが長すぎて、ファイル変更がキャッシュに反映されない

// 開発環境用の設定(php.ini)
// realpath_cache_ttl = 0  // キャッシュを無効化

// 本番環境用の設定
// realpath_cache_ttl = 600  // 10分
?>

問題3: パフォーマンスが低下している

<?php
function diagnoseCachePerformance() {
    $cache = realpath_cache_get();
    $size = realpath_cache_size();
    $maxSize = parseSize(ini_get('realpath_cache_size'));
    
    echo "=== パフォーマンス診断 ===\n";
    
    // 使用率チェック
    $usage = ($size / $maxSize) * 100;
    echo "キャッシュ使用率: " . round($usage, 2) . "%\n";
    
    if ($usage > 80) {
        echo "⚠ 警告: キャッシュが満杯に近づいています\n";
        echo "  推奨: realpath_cache_sizeを増やしてください\n";
    }
    
    // エントリ数チェック
    $entryCount = count($cache);
    echo "エントリ数: {$entryCount}\n";
    
    if ($entryCount > 10000) {
        echo "⚠ 警告: エントリ数が非常に多いです\n";
        echo "  推奨: オートローダーの最適化を検討してください\n";
    }
    
    // TTLチェック
    $ttl = ini_get('realpath_cache_ttl');
    echo "TTL: {$ttl}秒\n";
    
    if ($ttl < 300) {
        echo "💡 ヒント: 本番環境ではTTLを300秒以上に設定することを推奨\n";
    }
}

diagnoseCachePerformance();
?>

まとめ

realpath_cache_get関数のポイントをおさらいしましょう:

  1. PHPの内部キャッシュの状態を確認できる
  2. パフォーマンスボトルネックの発見に役立つ
  3. キャッシュサイズとTTLの最適化が重要
  4. 大規模アプリケーションではキャッシュサイズを増やす
  5. 本番環境ではTTLを長めに設定(300秒以上)
  6. 開発環境ではTTLを短く、または0に設定
  7. 定期的なモニタリングでパフォーマンスを維持

realpathキャッシュは、PHPアプリケーションのパフォーマンスに大きく影響する重要な要素です。realpath_cache_get関数を活用して、適切にチューニングしましょう!

参考リンク


この記事が役に立ったら、ぜひシェアしてください!PHPに関する他の記事もお楽しみに。

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