PHPの出力バッファリングを使用していると、「現在のバッファの詳細な状態を知りたい」という場面があります。単純なサイズやレベルだけでなく、より詳細な情報が必要な時に活躍するのがob_get_status関数です。
この記事では、ob_get_status関数の基本的な使い方から、実践的なバッファ診断システムまで詳しく解説します。
ob_get_status関数とは?
ob_get_status()
は、PHPの出力バッファリング関数の一つで、現在アクティブな出力バッファの詳細な状態情報を配列として取得する関数です。
基本構文
array ob_get_status(bool $full_status = false)
- 戻り値: バッファ状態情報を含む配列
- パラメータ:
$full_status
:false
なら最上位のバッファのみ、true
なら全階層の情報を取得
取得できる情報
基本情報($full_status = false)
[
'level' => int, // バッファレベル
'type' => int, // バッファタイプ
'status' => int, // バッファステータス
'name' => string, // 出力ハンドラー名
'del' => bool // 削除フラグ
]
全階層情報($full_status = true)
[
[
'chunk_size' => int, // チャンクサイズ
'size' => int, // バッファサイズ
'block_size' => int, // ブロックサイズ
'type' => int, // バッファタイプ
'status' => int, // ステータス
'name' => string, // ハンドラー名
'del' => bool // 削除フラグ
],
// 各レベルの情報...
]
基本的な使い方
シンプルな例
<?php
// バッファが無い状態
$status = ob_get_status();
echo "バッファなし状態:\n";
print_r($status);
// バッファを開始
ob_start();
echo "テストコンテンツ";
// 基本ステータス取得
$status = ob_get_status();
echo "\n基本ステータス:\n";
print_r($status);
// 詳細ステータス取得
$fullStatus = ob_get_status(true);
echo "\n詳細ステータス:\n";
print_r($fullStatus);
ob_end_clean();
?>
実行結果例:
バッファなし状態:
Array
(
)
基本ステータス:
Array
(
[level] => 1
[type] => 0
[status] => 0
[name] => default output handler
[del] => 1
)
詳細ステータス:
Array
(
[0] => Array
(
[chunk_size] => 0
[size] => 12
[block_size] => 10240
[type] => 0
[status] => 0
[name] => default output handler
[del] => 1
)
)
ネストしたバッファの状態確認
<?php
function displayBufferStatus($label) {
echo "\n=== {$label} ===\n";
$basic = ob_get_status();
$full = ob_get_status(true);
echo "現在のレベル: " . (empty($basic) ? "0 (バッファなし)" : $basic['level']) . "\n";
echo "アクティブなバッファ数: " . count($full) . "\n";
foreach ($full as $index => $buffer) {
echo "レベル" . ($index + 1) . ": ";
echo "サイズ={$buffer['size']}B, ";
echo "ハンドラー='{$buffer['name']}', ";
echo "タイプ={$buffer['type']}\n";
}
}
displayBufferStatus("初期状態");
// 1階層目
ob_start();
echo "レベル1のコンテンツ";
displayBufferStatus("レベル1開始後");
// 2階層目
ob_start('strtoupper'); // 大文字変換ハンドラー
echo "level2 content";
displayBufferStatus("レベル2開始後");
// 3階層目
ob_start();
echo "レベル3のデータ";
displayBufferStatus("レベル3開始後");
// 段階的に終了
ob_end_clean();
displayBufferStatus("レベル3終了後");
ob_end_clean();
displayBufferStatus("レベル2終了後");
ob_end_clean();
displayBufferStatus("レベル1終了後");
?>
実践的な活用例
1. 高度なバッファ診断システム
<?php
class BufferDiagnostics {
const BUFFER_TYPE_NAMES = [
0 => 'NORMAL',
1 => 'GZHANDLER',
2 => 'USER_DEFINED'
];
const STATUS_NAMES = [
0 => 'INACTIVE',
1 => 'ACTIVE',
2 => 'CLEANABLE',
4 => 'FLUSHABLE',
8 => 'REMOVABLE'
];
public function generateFullReport() {
$basic = ob_get_status();
$full = ob_get_status(true);
return [
'summary' => $this->generateSummary($basic, $full),
'hierarchy' => $this->analyzeHierarchy($full),
'performance' => $this->analyzePerformance($full),
'recommendations' => $this->generateRecommendations($full),
'memory_analysis' => $this->analyzeMemoryUsage($full),
'handler_analysis' => $this->analyzeHandlers($full)
];
}
private function generateSummary($basic, $full) {
$totalSize = array_sum(array_column($full, 'size'));
$avgSize = count($full) > 0 ? round($totalSize / count($full), 2) : 0;
return [
'active' => !empty($basic),
'current_level' => empty($basic) ? 0 : $basic['level'],
'total_buffers' => count($full),
'total_size' => $totalSize,
'average_size' => $avgSize,
'largest_buffer' => $this->findLargestBuffer($full),
'has_custom_handlers' => $this->hasCustomHandlers($full),
'memory_efficiency' => $this->calculateMemoryEfficiency($full)
];
}
private function analyzeHierarchy($full) {
$hierarchy = [];
foreach ($full as $level => $buffer) {
$hierarchy[] = [
'level' => $level + 1,
'size' => $buffer['size'],
'size_formatted' => $this->formatBytes($buffer['size']),
'handler' => $buffer['name'],
'type' => self::BUFFER_TYPE_NAMES[$buffer['type']] ?? 'UNKNOWN',
'chunk_size' => $buffer['chunk_size'],
'block_size' => $buffer['block_size'],
'efficiency' => $this->calculateBufferEfficiency($buffer),
'is_compressed' => $this->isCompressedHandler($buffer['name']),
'estimated_compression_ratio' => $this->estimateCompressionRatio($buffer)
];
}
return $hierarchy;
}
private function analyzePerformance($full) {
$performance = [
'total_allocated_memory' => array_sum(array_column($full, 'block_size')),
'memory_utilization' => $this->calculateMemoryUtilization($full),
'fragmentation_level' => $this->calculateFragmentation($full),
'optimization_score' => 0,
'bottlenecks' => []
];
// パフォーマンスボトルネックの検出
foreach ($full as $level => $buffer) {
// 大きなチャンクサイズ
if ($buffer['chunk_size'] > 0 && $buffer['chunk_size'] > $buffer['size'] * 2) {
$performance['bottlenecks'][] = [
'level' => $level + 1,
'type' => 'oversized_chunk',
'message' => 'チャンクサイズがバッファサイズに対して大きすぎます',
'impact' => 'medium'
];
}
// メモリ使用効率が悪い
$efficiency = $this->calculateBufferEfficiency($buffer);
if ($efficiency < 50) {
$performance['bottlenecks'][] = [
'level' => $level + 1,
'type' => 'low_efficiency',
'message' => "メモリ使用効率が低いです ({$efficiency}%)",
'impact' => 'high'
];
}
}
// 最適化スコア計算
$performance['optimization_score'] = $this->calculateOptimizationScore($full, $performance['bottlenecks']);
return $performance;
}
private function generateRecommendations($full) {
$recommendations = [];
// 深いネストの警告
if (count($full) > 3) {
$recommendations[] = [
'priority' => 'high',
'category' => 'architecture',
'message' => 'バッファのネストが深すぎます (' . count($full) . '階層)',
'suggestion' => 'バッファ構造を見直し、不要なネストを削減してください',
'code_example' => $this->generateRestructureExample($full)
];
}
// 大きなバッファサイズ
$totalSize = array_sum(array_column($full, 'size'));
if ($totalSize > 1024 * 1024) { // 1MB
$recommendations[] = [
'priority' => 'medium',
'category' => 'memory',
'message' => 'バッファサイズが大きくなっています (' . $this->formatBytes($totalSize) . ')',
'suggestion' => 'バッファ内容をストリーミング処理に切り替えるか、分割処理を検討してください',
'code_example' => $this->generateStreamingExample()
];
}
// カスタムハンドラーの最適化
foreach ($full as $level => $buffer) {
if ($buffer['name'] !== 'default output handler' && $this->canOptimizeHandler($buffer)) {
$recommendations[] = [
'priority' => 'low',
'category' => 'optimization',
'message' => "レベル" . ($level + 1) . "のハンドラー '{$buffer['name']}' を最適化できます",
'suggestion' => '処理内容に応じてより効率的なハンドラーを選択してください',
'code_example' => $this->generateHandlerOptimizationExample($buffer)
];
}
}
return $recommendations;
}
private function analyzeMemoryUsage($full) {
$totalAllocated = 0;
$totalUsed = 0;
$details = [];
foreach ($full as $level => $buffer) {
$allocated = $buffer['block_size'];
$used = $buffer['size'];
$waste = $allocated - $used;
$totalAllocated += $allocated;
$totalUsed += $used;
$details[] = [
'level' => $level + 1,
'allocated' => $allocated,
'used' => $used,
'waste' => $waste,
'efficiency' => $allocated > 0 ? round(($used / $allocated) * 100, 2) : 0,
'waste_percentage' => $allocated > 0 ? round(($waste / $allocated) * 100, 2) : 0
];
}
return [
'total_allocated' => $totalAllocated,
'total_used' => $totalUsed,
'total_waste' => $totalAllocated - $totalUsed,
'overall_efficiency' => $totalAllocated > 0 ? round(($totalUsed / $totalAllocated) * 100, 2) : 0,
'details' => $details,
'memory_pressure' => $this->calculateMemoryPressure($totalAllocated)
];
}
private function analyzeHandlers($full) {
$handlerStats = [];
$uniqueHandlers = [];
foreach ($full as $level => $buffer) {
$handlerName = $buffer['name'];
if (!isset($handlerStats[$handlerName])) {
$handlerStats[$handlerName] = [
'count' => 0,
'total_size' => 0,
'levels' => [],
'type' => $buffer['type'],
'is_builtin' => $this->isBuiltinHandler($handlerName),
'compression_capable' => $this->isCompressionHandler($handlerName),
'performance_rating' => $this->rateHandlerPerformance($handlerName)
];
}
$handlerStats[$handlerName]['count']++;
$handlerStats[$handlerName]['total_size'] += $buffer['size'];
$handlerStats[$handlerName]['levels'][] = $level + 1;
$uniqueHandlers[$handlerName] = true;
}
return [
'unique_handlers' => count($uniqueHandlers),
'handler_distribution' => $handlerStats,
'optimization_opportunities' => $this->findHandlerOptimizations($handlerStats),
'compatibility_issues' => $this->checkHandlerCompatibility($handlerStats)
];
}
private function findLargestBuffer($full) {
if (empty($full)) return null;
$largest = null;
$maxSize = 0;
foreach ($full as $level => $buffer) {
if ($buffer['size'] > $maxSize) {
$maxSize = $buffer['size'];
$largest = [
'level' => $level + 1,
'size' => $buffer['size'],
'handler' => $buffer['name']
];
}
}
return $largest;
}
private function hasCustomHandlers($full) {
foreach ($full as $buffer) {
if ($buffer['name'] !== 'default output handler') {
return true;
}
}
return false;
}
private function calculateMemoryEfficiency($full) {
if (empty($full)) return 100;
$totalAllocated = array_sum(array_column($full, 'block_size'));
$totalUsed = array_sum(array_column($full, 'size'));
return $totalAllocated > 0 ? round(($totalUsed / $totalAllocated) * 100, 2) : 0;
}
private function calculateBufferEfficiency($buffer) {
if ($buffer['block_size'] <= 0) return 100;
return round(($buffer['size'] / $buffer['block_size']) * 100, 2);
}
private function isCompressedHandler($handlerName) {
return in_array(strtolower($handlerName), [
'ob_gzhandler', 'zlib', 'gzip', 'deflate'
]);
}
private function estimateCompressionRatio($buffer) {
if (!$this->isCompressedHandler($buffer['name'])) {
return 0;
}
// 圧縮ハンドラーの場合、概算の圧縮率を返す
return 30; // 30%程度の圧縮を想定
}
private function calculateMemoryUtilization($full) {
$totalAllocated = array_sum(array_column($full, 'block_size'));
$totalUsed = array_sum(array_column($full, 'size'));
return [
'allocated_mb' => round($totalAllocated / (1024 * 1024), 2),
'used_mb' => round($totalUsed / (1024 * 1024), 2),
'utilization_percentage' => $totalAllocated > 0 ? round(($totalUsed / $totalAllocated) * 100, 2) : 0
];
}
private function calculateFragmentation($full) {
if (count($full) <= 1) return 0;
// バッファサイズの標準偏差を計算してフラグメンテーションレベルを判定
$sizes = array_column($full, 'size');
$mean = array_sum($sizes) / count($sizes);
$variance = array_sum(array_map(function($size) use ($mean) {
return pow($size - $mean, 2);
}, $sizes)) / count($sizes);
$stdDev = sqrt($variance);
// 標準偏差が大きいほどフラグメンテーションが高い
return $mean > 0 ? round(($stdDev / $mean) * 100, 2) : 0;
}
private function calculateOptimizationScore($full, $bottlenecks) {
$baseScore = 100;
// ボトルネックによる減点
foreach ($bottlenecks as $bottleneck) {
switch ($bottleneck['impact']) {
case 'high':
$baseScore -= 25;
break;
case 'medium':
$baseScore -= 15;
break;
case 'low':
$baseScore -= 5;
break;
}
}
// ネストレベルによる減点
if (count($full) > 3) {
$baseScore -= (count($full) - 3) * 10;
}
return max(0, $baseScore);
}
private function calculateMemoryPressure($totalAllocated) {
$currentMemory = memory_get_usage();
$memoryLimit = ini_get('memory_limit');
if ($memoryLimit === '-1') {
return 'unlimited';
}
$memoryLimitBytes = $this->parseMemoryLimit($memoryLimit);
$pressure = ($currentMemory / $memoryLimitBytes) * 100;
if ($pressure > 90) return 'critical';
if ($pressure > 70) return 'high';
if ($pressure > 50) return 'medium';
return 'low';
}
private function isBuiltinHandler($handlerName) {
return in_array($handlerName, [
'default output handler',
'ob_gzhandler',
'zlib'
]);
}
private function isCompressionHandler($handlerName) {
return $this->isCompressedHandler($handlerName);
}
private function rateHandlerPerformance($handlerName) {
$ratings = [
'default output handler' => 95,
'ob_gzhandler' => 75,
'zlib' => 80
];
return $ratings[$handlerName] ?? 50; // 不明なハンドラーは50点
}
private function findHandlerOptimizations($handlerStats) {
$optimizations = [];
foreach ($handlerStats as $handlerName => $stats) {
if ($stats['performance_rating'] < 70) {
$optimizations[] = [
'handler' => $handlerName,
'issue' => 'low_performance',
'suggestion' => 'より高性能なハンドラーへの移行を検討'
];
}
if ($stats['count'] > 1 && !$stats['compression_capable']) {
$optimizations[] = [
'handler' => $handlerName,
'issue' => 'multiple_instances',
'suggestion' => '複数のバッファで同じハンドラーが使用されています。統合を検討してください'
];
}
}
return $optimizations;
}
private function checkHandlerCompatibility($handlerStats) {
$issues = [];
$handlers = array_keys($handlerStats);
// 圧縮ハンドラーとの競合チェック
$hasCompression = false;
$hasNonCompression = false;
foreach ($handlers as $handler) {
if ($this->isCompressionHandler($handler)) {
$hasCompression = true;
} else {
$hasNonCompression = true;
}
}
if ($hasCompression && $hasNonCompression) {
$issues[] = [
'type' => 'compression_conflict',
'message' => '圧縮ハンドラーと非圧縮ハンドラーが混在しています',
'recommendation' => '一貫した圧縮戦略を採用してください'
];
}
return $issues;
}
private function formatBytes($bytes) {
$units = ['B', 'KB', 'MB', 'GB'];
$power = $bytes > 0 ? floor(log($bytes, 1024)) : 0;
return round($bytes / pow(1024, $power), 2) . ' ' . $units[$power];
}
private function parseMemoryLimit($limit) {
$unit = strtoupper(substr($limit, -1));
$value = (int)substr($limit, 0, -1);
switch ($unit) {
case 'G': return $value * 1024 * 1024 * 1024;
case 'M': return $value * 1024 * 1024;
case 'K': return $value * 1024;
default: return (int)$limit;
}
}
// コード例生成メソッド群
private function generateRestructureExample($full) {
return "// バッファ構造の最適化例\nob_start();\n// メインコンテンツ\necho \$content;\n\$output = ob_get_clean();\necho \$output;";
}
private function generateStreamingExample() {
return "// ストリーミング処理の例\nforeach (\$chunks as \$chunk) {\n echo \$chunk;\n flush();\n}";
}
private function generateHandlerOptimizationExample($buffer) {
return "// ハンドラー最適化の例\nob_start('ob_gzhandler'); // 圧縮を有効化\necho \$content;\nob_end_flush();";
}
private function canOptimizeHandler($buffer) {
return $buffer['name'] === 'default output handler' && $buffer['size'] > 1024;
}
}
// 使用例
$diagnostics = new BufferDiagnostics();
// テスト用のバッファ構造を作成
ob_start();
echo str_repeat("レベル1のコンテンツ ", 100);
ob_start('strtoupper');
echo "level 2 content with handler ";
ob_start();
echo "深いネストレベル3";
// 診断実行
$report = $diagnostics->generateFullReport();
echo "=== バッファ診断レポート ===\n\n";
echo "【概要】\n";
echo "アクティブ: " . ($report['summary']['active'] ? 'はい' : 'いいえ') . "\n";
echo "現在レベル: {$report['summary']['current_level']}\n";
echo "総バッファ数: {$report['summary']['total_buffers']}\n";
echo "総サイズ: " . (new BufferDiagnostics())->formatBytes($report['summary']['total_size']) . "\n";
echo "メモリ効率: {$report['summary']['memory_efficiency']}%\n\n";
echo "【階層分析】\n";
foreach ($report['hierarchy'] as $level) {
echo "レベル{$level['level']}: {$level['size_formatted']} ({$level['handler']})\n";
echo " 効率: {$level['efficiency']}%\n";
}
echo "\n";
echo "【パフォーマンス】\n";
echo "最適化スコア: {$report['performance']['optimization_score']}/100\n";
echo "メモリ断片化: {$report['performance']['fragmentation_level']}%\n";
if (!empty($report['performance']['bottlenecks'])) {
echo "ボトルネック:\n";
foreach ($report['performance']['bottlenecks'] as $bottleneck) {
echo " - {$bottleneck['message']} (影響: {$bottleneck['impact']})\n";
}
}
echo "\n";
echo "【推奨事項】\n";
foreach ($report['recommendations'] as $rec) {
echo "[{$rec['priority']}] {$rec['message']}\n";
echo " 対策: {$rec['suggestion']}\n\n";
}
// バッファを適切に終了
while (ob_get_level() > 0) {
ob_end_clean();
}
?>
2. リアルタイム監視システム
<?php
class RealTimeBufferMonitor {
private $monitoring = false;
private $thresholds = [
'max_level' => 5,
'max_size' => 1024 * 1024, // 1MB
'max_total_size' => 5 * 1024 * 1024, // 5MB
'efficiency_threshold' => 60
];
public function startMonitoring($config = []) {
$this->thresholds = array_merge($this->thresholds, $config);
$this->monitoring = true;
// 定期チェックを設定(実際の実装では別プロセスやタイマーを使用)
register_tick_function([$this, 'checkBufferHealth']);
declare(ticks=100);
}
public function stopMonitoring() {
$this->monitoring = false;
unregister_tick_function([$this, 'checkBufferHealth']);
}
public function checkBufferHealth() {
if (!$this->monitoring) return;
$status = ob_get_status(true);
if (empty($status)) return; // バッファがない場合はスキップ
$issues = $this->detectIssues($status);
if (!empty($issues)) {
$this->handleIssues($issues, $status);
}
}
private function detectIssues($status) {
$issues = [];
// レベルチェック
if (count($status) > $this->thresholds['max_level']) {
$issues[] = [
'type' => 'max_level_exceeded',
'severity' => 'high',
'message' => 'バッファレベルが制限を超えています',
'current' => count($status),
'threshold' => $this->thresholds['max_level']
];
}
// 個別バッファサイズチェック
foreach ($status as $level => $buffer) {
if ($buffer['size'] > $this->thresholds['max_size']) {
$issues[] = [
'type' => 'buffer_size_exceeded',
'severity' => 'medium',
'message' => "レベル" . ($level + 1) . "のバッファサイズが制限を超えています",
'level' => $level + 1,
'current' => $buffer['size'],
'threshold' => $this->thresholds['max_size']
];
}
// 効率チェック
$efficiency = $buffer['block_size'] > 0 ? ($buffer['size'] / $buffer['block_size']) * 100 : 0;
if ($efficiency < $this->thresholds['efficiency_threshold']) {
$issues[] = [
'type' => 'low_efficiency',
'severity' => 'low',
'message' => "レベル" . ($level + 1) . "のメモリ効率が低下しています",
'level' => $level + 1,
'current' => round($efficiency, 2),
'threshold' => $this->thresholds['efficiency_threshold']
];
}
}
// 総サイズチェック
$totalSize = array_sum(array_column($status, 'size'));
if ($totalSize > $this->thresholds['max_total_size']) {
$issues[] = [
'type' => 'total_size_exceeded',
'severity' => 'high',
'message' => '総バッファサイズが制限を超えています',
'current' => $totalSize,
'threshold' => $this->thresholds['max_total_size']
];
}
return $issues;
}
private function handleIssues($issues, $status) {
foreach ($issues as $issue) {
$this->logIssue($issue);
// 自動対処(設定による)
switch ($issue['type']) {
case 'max_level_exceeded':
$this->handleLevelExceeded($status);
break;
case 'buffer_size_exceeded':
$this->handleSizeExceeded($issue, $status);
break;
case 'total_size_exceeded':
$this->handleTotalSizeExceeded($status);
break;
}
}
}
private function logIssue($issue) {
$timestamp = date('Y-m-d H:i:s');
$logMessage = "[{$timestamp}] [{$issue['severity']}] {$issue['message']}";
if (isset($issue['current'], $issue['threshold'])) {
$logMessage .= " (現在: {$issue['current']}, 制限: {$issue['threshold']})";
}
error_log($logMessage);
// 重要度が高い場合は追加の通知
if ($issue['severity'] === 'high') {
$this->sendAlert($issue);
}
}
private function handleLevelExceeded($status) {
// 設定により自動的に上位レベルのバッファを終了
if (defined('AUTO_CLEANUP_BUFFERS') && AUTO_CLEANUP_BUFFERS) {
$levelsToClean = count($status) - $this->thresholds['max_level'];
for ($i = 0; $i < $levelsToClean; $i++) {
if (ob_get_level() > 0) {
ob_end_clean();
}
}
}
}
private function handleSizeExceeded($issue, $status) {
if (isset($issue['level']) && defined('AUTO_FLUSH_LARGE_BUFFERS') && AUTO_FLUSH_LARGE_BUFFERS) {
// 大きなバッファを自動でフラッシュ(実装は環境に依存)
trigger_error("大きなバッファの自動処理が必要です (レベル: {$issue['level']})", E_USER_WARNING);
}
}
private function handleTotalSizeExceeded($status) {
if (defined('AUTO_OPTIMIZE_BUFFERS') && AUTO_OPTIMIZE_BUFFERS) {
// バッファの最適化を実行
$this->optimizeBuffers($status);
}
}
private function optimizeBuffers($status) {
// 効率の悪いバッファを特定して最適化
foreach ($status as $level => $buffer) {
$efficiency = $buffer['block_size'] > 0 ? ($buffer['size'] / $buffer['block_size']) * 100 : 0;
if ($efficiency < 30 && $buffer['size'] > 1024) {
// 効率が悪く、ある程度のサイズがあるバッファを処理
error_log("バッファレベル" . ($level + 1) . "の最適化が推奨されます (効率: {$efficiency}%)");
}
}
}
private function sendAlert($issue) {
// 実際の実装では、メール、Slack、監視システムへの通知など
error_log("ALERT: " . $issue['message']);
}
public function generateHealthReport() {
$status = ob_get_status(true);
if (empty($status)) {
return ['status' => 'no_buffers', 'message' => 'アクティブなバッファはありません'];
}
$totalSize = array_sum(array_column($status, 'size'));
$totalAllocated = array_sum(array_column($status, 'block_size'));
return [
'status' => 'active',
'buffer_count' => count($status),
'total_size' => $totalSize,
'total_allocated' => $totalAllocated,
'efficiency' => $totalAllocated > 0 ? round(($totalSize / $totalAllocated) * 100, 2) : 0,
'health_score' => $this->calculateHealthScore($status),
'recommendations' => $this->generateHealthRecommendations($status)
];
}
private function calculateHealthScore($status) {
$score = 100;
// レベル数による減点
if (count($status) > 3) $score -= (count($status) - 3) * 10;
// 効率による減点
foreach ($status as $buffer) {
$efficiency = $buffer['block_size'] > 0 ? ($buffer['size'] / $buffer['block_size']) * 100 : 0;
if ($efficiency < 50) $score -= 15;
elseif ($efficiency < 70) $score -= 5;
}
// サイズによる減点
$totalSize = array_sum(array_column($status, 'size'));
if ($totalSize > $this->thresholds['max_total_size']) $score -= 30;
elseif ($totalSize > $this->thresholds['max_total_size'] * 0.8) $score -= 10;
return max(0, $score);
}
private function generateHealthRecommendations($status) {
$recommendations = [];
if (count($status) > 3) {
$recommendations[] = 'バッファのネストレベルを削減してください';
}
$totalSize = array_sum(array_column($status, 'size'));
if ($totalSize > $this->thresholds['max_total_size'] * 0.8) {
$recommendations[] = 'バッファサイズが大きくなっています。分割処理を検討してください';
}
foreach ($status as $level => $buffer) {
$efficiency = $buffer['block_size'] > 0 ? ($buffer['size'] / $buffer['block_size']) * 100 : 0;
if ($efficiency < 50) {
$recommendations[] = "レベル" . ($level + 1) . "のメモリ効率が低下しています";
}
}
return $recommendations;
}
}
// 使用例
$monitor = new RealTimeBufferMonitor();
// 監視開始(本格運用時の設定例)
/*
define('AUTO_CLEANUP_BUFFERS', true);
define('AUTO_FLUSH_LARGE_BUFFERS', true);
define('AUTO_OPTIMIZE_BUFFERS', true);
*/
$monitor->startMonitoring([
'max_level' => 3,
'max_size' => 500 * 1024, // 500KB
'max_total_size' => 2 * 1024 * 1024, // 2MB
'efficiency_threshold' => 70
]);
// テスト用のバッファ操作
ob_start();
echo str_repeat("大量のデータ", 1000);
ob_start();
echo str_repeat("更に大量のデータ", 2000);
ob_start();
echo "3レベル目";
ob_start();
echo "4レベル目(警告が発生するはず)";
// 健全性レポート生成
$healthReport = $monitor->generateHealthReport();
echo "\n=== 健全性レポート ===\n";
echo "ステータス: {$healthReport['status']}\n";
echo "バッファ数: {$healthReport['buffer_count']}\n";
echo "総サイズ: " . number_format($healthReport['total_size']) . " bytes\n";
echo "効率: {$healthReport['efficiency']}%\n";
echo "健全性スコア: {$healthReport['health_score']}/100\n";
if (!empty($healthReport['recommendations'])) {
echo "\n推奨事項:\n";
foreach ($healthReport['recommendations'] as $rec) {
echo "- {$rec}\n";
}
}
// 監視停止
$monitor->stopMonitoring();
// 清理
while (ob_get_level() > 0) {
ob_end_clean();
}
?>
3. 開発者向けデバッグツール
<?php
class BufferDebugger {
private $snapshots = [];
private $timeline = [];
public function takeSnapshot($label = null) {
$label = $label ?? 'snapshot_' . count($this->snapshots);
$snapshot = [
'label' => $label,
'timestamp' => microtime(true),
'memory_usage' => memory_get_usage(),
'peak_memory' => memory_get_peak_usage(),
'basic_status' => ob_get_status(),
'full_status' => ob_get_status(true)
];
$this->snapshots[] = $snapshot;
$this->timeline[] = [
'type' => 'snapshot',
'label' => $label,
'timestamp' => $snapshot['timestamp']
];
return $snapshot;
}
public function compareSnapshots($index1, $index2) {
if (!isset($this->snapshots[$index1], $this->snapshots[$index2])) {
throw new InvalidArgumentException('指定されたスナップショットが存在しません');
}
$snap1 = $this->snapshots[$index1];
$snap2 = $this->snapshots[$index2];
return [
'time_diff' => $snap2['timestamp'] - $snap1['timestamp'],
'memory_diff' => $snap2['memory_usage'] - $snap1['memory_usage'],
'peak_memory_diff' => $snap2['peak_memory'] - $snap1['peak_memory'],
'buffer_count_diff' => count($snap2['full_status']) - count($snap1['full_status']),
'size_changes' => $this->compareBufferSizes($snap1['full_status'], $snap2['full_status']),
'structural_changes' => $this->compareBufferStructure($snap1['full_status'], $snap2['full_status'])
];
}
private function compareBufferSizes($status1, $status2) {
$changes = [];
$maxLevels = max(count($status1), count($status2));
for ($i = 0; $i < $maxLevels; $i++) {
$size1 = isset($status1[$i]) ? $status1[$i]['size'] : 0;
$size2 = isset($status2[$i]) ? $status2[$i]['size'] : 0;
if ($size1 !== $size2) {
$changes[] = [
'level' => $i + 1,
'before' => $size1,
'after' => $size2,
'difference' => $size2 - $size1
];
}
}
return $changes;
}
private function compareBufferStructure($status1, $status2) {
$changes = [];
// 新しいバッファレベル
if (count($status2) > count($status1)) {
$changes[] = [
'type' => 'buffers_added',
'count' => count($status2) - count($status1)
];
}
// 削除されたバッファレベル
if (count($status1) > count($status2)) {
$changes[] = [
'type' => 'buffers_removed',
'count' => count($status1) - count($status2)
];
}
// ハンドラーの変更
$minLevels = min(count($status1), count($status2));
for ($i = 0; $i < $minLevels; $i++) {
if ($status1[$i]['name'] !== $status2[$i]['name']) {
$changes[] = [
'type' => 'handler_changed',
'level' => $i + 1,
'before' => $status1[$i]['name'],
'after' => $status2[$i]['name']
];
}
}
return $changes;
}
public function generateDebugReport() {
return [
'snapshots' => count($this->snapshots),
'timeline' => $this->timeline,
'current_status' => [
'basic' => ob_get_status(),
'full' => ob_get_status(true)
],
'performance_analysis' => $this->analyzePerformance(),
'memory_analysis' => $this->analyzeMemoryUsage(),
'recommendations' => $this->generateDebugRecommendations()
];
}
private function analyzePerformance() {
if (count($this->snapshots) < 2) {
return ['message' => '分析には最低2つのスナップショットが必要です'];
}
$firstSnapshot = $this->snapshots[0];
$lastSnapshot = end($this->snapshots);
$totalTime = $lastSnapshot['timestamp'] - $firstSnapshot['timestamp'];
$totalMemoryGrowth = $lastSnapshot['memory_usage'] - $firstSnapshot['memory_usage'];
$peakMemoryGrowth = $lastSnapshot['peak_memory'] - $firstSnapshot['peak_memory'];
return [
'total_execution_time' => $totalTime,
'memory_growth_rate' => $totalTime > 0 ? $totalMemoryGrowth / $totalTime : 0,
'peak_memory_growth' => $peakMemoryGrowth,
'average_memory_per_second' => $totalTime > 0 ? $totalMemoryGrowth / $totalTime : 0,
'performance_score' => $this->calculatePerformanceScore($totalTime, $totalMemoryGrowth)
];
}
private function analyzeMemoryUsage() {
$memoryData = array_map(function($snapshot) {
return [
'timestamp' => $snapshot['timestamp'],
'usage' => $snapshot['memory_usage'],
'peak' => $snapshot['peak_memory']
];
}, $this->snapshots);
if (empty($memoryData)) {
return ['message' => 'メモリ使用量データがありません'];
}
$usages = array_column($memoryData, 'usage');
$peaks = array_column($memoryData, 'peak');
return [
'min_usage' => min($usages),
'max_usage' => max($usages),
'avg_usage' => array_sum($usages) / count($usages),
'min_peak' => min($peaks),
'max_peak' => max($peaks),
'memory_efficiency' => $this->calculateMemoryEfficiency($usages, $peaks),
'growth_pattern' => $this->analyzeGrowthPattern($usages)
];
}
private function calculatePerformanceScore($totalTime, $memoryGrowth) {
$baseScore = 100;
// 実行時間による減点
if ($totalTime > 1.0) $baseScore -= 20;
elseif ($totalTime > 0.5) $baseScore -= 10;
// メモリ増加による減点
if ($memoryGrowth > 10 * 1024 * 1024) $baseScore -= 30; // 10MB
elseif ($memoryGrowth > 5 * 1024 * 1024) $baseScore -= 15; // 5MB
elseif ($memoryGrowth > 1024 * 1024) $baseScore -= 5; // 1MB
return max(0, $baseScore);
}
private function calculateMemoryEfficiency($usages, $peaks) {
if (empty($usages) || empty($peaks)) return 0;
$avgUsage = array_sum($usages) / count($usages);
$avgPeak = array_sum($peaks) / count($peaks);
return $avgPeak > 0 ? ($avgUsage / $avgPeak) * 100 : 0;
}
private function analyzeGrowthPattern($usages) {
if (count($usages) < 3) return 'insufficient_data';
$increases = 0;
$decreases = 0;
$stable = 0;
for ($i = 1; $i < count($usages); $i++) {
$diff = $usages[$i] - $usages[$i - 1];
if ($diff > 1024) $increases++; // 1KB以上の増加
elseif ($diff < -1024) $decreases++; // 1KB以上の減少
else $stable++;
}
$total = $increases + $decreases + $stable;
if ($increases / $total > 0.7) return 'growing';
if ($decreases / $total > 0.7) return 'shrinking';
if ($stable / $total > 0.7) return 'stable';
return 'volatile';
}
private function generateDebugRecommendations() {
$recommendations = [];
$currentStatus = ob_get_status(true);
// 現在の状況に基づく推奨事項
if (count($currentStatus) > 3) {
$recommendations[] = [
'category' => 'structure',
'message' => 'バッファのネストが深すぎます',
'action' => 'バッファ構造を見直してください'
];
}
if (!empty($currentStatus)) {
$totalSize = array_sum(array_column($currentStatus, 'size'));
if ($totalSize > 1024 * 1024) {
$recommendations[] = [
'category' => 'performance',
'message' => 'バッファサイズが大きすぎます',
'action' => 'ストリーミング処理または分割処理を検討してください'
];
}
}
// スナップショット分析に基づく推奨事項
if (count($this->snapshots) > 1) {
$performance = $this->analyzePerformance();
if ($performance['performance_score'] < 70) {
$recommendations[] = [
'category' => 'optimization',
'message' => 'パフォーマンスが低下しています',
'action' => 'バッファ使用方法の最適化が必要です'
];
}
}
return $recommendations;
}
public function exportDebugData($format = 'json') {
$data = [
'debug_session' => [
'start_time' => !empty($this->snapshots) ? $this->snapshots[0]['timestamp'] : null,
'end_time' => !empty($this->snapshots) ? end($this->snapshots)['timestamp'] : null,
'total_snapshots' => count($this->snapshots)
],
'snapshots' => $this->snapshots,
'timeline' => $this->timeline,
'analysis' => $this->generateDebugReport()
];
switch ($format) {
case 'json':
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
case 'csv':
return $this->exportToCsv($data);
case 'html':
return $this->exportToHtml($data);
default:
return serialize($data);
}
}
private function exportToCsv($data) {
$csv = "Timestamp,Memory Usage,Peak Memory,Buffer Count,Total Buffer Size\n";
foreach ($data['snapshots'] as $snapshot) {
$totalSize = array_sum(array_column($snapshot['full_status'], 'size'));
$csv .= sprintf(
"%.4f,%d,%d,%d,%d\n",
$snapshot['timestamp'],
$snapshot['memory_usage'],
$snapshot['peak_memory'],
count($snapshot['full_status']),
$totalSize
);
}
return $csv;
}
private function exportToHtml($data) {
$html = "<html><head><title>Buffer Debug Report</title></head><body>";
$html .= "<h1>Buffer Debug Report</h1>";
$html .= "<h2>Summary</h2>";
$html .= "<ul>";
$html .= "<li>Total Snapshots: " . $data['debug_session']['total_snapshots'] . "</li>";
if ($data['debug_session']['start_time'] && $data['debug_session']['end_time']) {
$duration = $data['debug_session']['end_time'] - $data['debug_session']['start_time'];
$html .= "<li>Duration: " . round($duration, 4) . " seconds</li>";
}
$html .= "</ul>";
$html .= "<h2>Snapshots</h2>";
$html .= "<table border='1'>";
$html .= "<tr><th>Label</th><th>Timestamp</th><th>Memory</th><th>Buffers</th><th>Total Size</th></tr>";
foreach ($data['snapshots'] as $snapshot) {
$totalSize = array_sum(array_column($snapshot['full_status'], 'size'));
$html .= "<tr>";
$html .= "<td>" . htmlspecialchars($snapshot['label']) . "</td>";
$html .= "<td>" . round($snapshot['timestamp'], 4) . "</td>";
$html .= "<td>" . number_format($snapshot['memory_usage']) . "</td>";
$html .= "<td>" . count($snapshot['full_status']) . "</td>";
$html .= "<td>" . number_format($totalSize) . "</td>";
$html .= "</tr>";
}
$html .= "</table>";
$html .= "</body></html>";
return $html;
}
public function reset() {
$this->snapshots = [];
$this->timeline = [];
}
}
// 使用例
$debugger = new BufferDebugger();
// デバッグセッション開始
$debugger->takeSnapshot('start');
ob_start();
echo "初期コンテンツ";
$debugger->takeSnapshot('after_first_buffer');
ob_start('strtoupper');
echo "uppercase content";
$debugger->takeSnapshot('after_second_buffer');
ob_start();
echo "深いレベルのコンテンツ";
$debugger->takeSnapshot('after_third_buffer');
// スナップショット比較
$comparison = $debugger->compareSnapshots(0, 3);
echo "=== スナップショット比較 ===\n";
echo "実行時間: " . round($comparison['time_diff'], 4) . "秒\n";
echo "メモリ増加: " . number_format($comparison['memory_diff']) . "バイト\n";
echo "バッファ数変化: " . $comparison['buffer_count_diff'] . "\n";
if (!empty($comparison['size_changes'])) {
echo "サイズ変化:\n";
foreach ($comparison['size_changes'] as $change) {
echo " レベル{$change['level']}: {$change['before']} → {$change['after']} (差分: {$change['difference']})\n";
}
}
// デバッグレポート生成
$report = $debugger->generateDebugReport();
echo "\n=== デバッグレポート ===\n";
echo "スナップショット数: {$report['snapshots']}\n";
echo "現在のバッファ数: " . count($report['current_status']['full']) . "\n";
if (isset($report['performance_analysis']['performance_score'])) {
echo "パフォーマンススコア: {$report['performance_analysis']['performance_score']}/100\n";
}
if (!empty($report['recommendations'])) {
echo "推奨事項:\n";
foreach ($report['recommendations'] as $rec) {
echo " [{$rec['category']}] {$rec['message']} - {$rec['action']}\n";
}
}
// デバッグデータのエクスポート(開発環境での使用例)
if (defined('DEBUG_EXPORT') && DEBUG_EXPORT) {
file_put_contents('buffer_debug.json', $debugger->exportDebugData('json'));
file_put_contents('buffer_debug.csv', $debugger->exportDebugData('csv'));
file_put_contents('buffer_debug.html', $debugger->exportDebugData('html'));
}
// クリーンアップ
while (ob_get_level() > 0) {
ob_end_clean();
}
?>
注意点とベストプラクティス
1. パフォーマンスへの配慮
<?php
// パフォーマンステスト
function benchmarkObGetStatus($iterations = 1000) {
ob_start();
echo str_repeat("test data", 1000);
// ob_get_status() のベンチマーク
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$status = ob_get_status();
}
$timeBasic = microtime(true) - $start;
// ob_get_status(true) のベンチマーク
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$status = ob_get_status(true);
}
$timeFull = microtime(true) - $start;
ob_end_clean();
return [
'basic' => $timeBasic,
'full' => $timeFull,
'difference' => $timeFull - $timeBasic,
'ratio' => $timeBasic > 0 ? $timeFull / $timeBasic : 0
];
}
$benchmark = benchmarkObGetStatus();
echo "基本情報取得: " . round($benchmark['basic'] * 1000, 2) . "ms\n";
echo "詳細情報取得: " . round($benchmark['full'] * 1000, 2) . "ms\n";
echo "差分: " . round($benchmark['difference'] * 1000, 2) . "ms\n";
echo "比率: " . round($benchmark['ratio'], 2) . "倍\n";
?>
2. エラーハンドリング
<?php
function safeGetBufferStatus($fullStatus = false) {
try {
$status = ob_get_status($fullStatus);
// 空の配列が返された場合の処理
if (empty($status)) {
return [
'active' => false,
'message' => 'アクティブなバッファはありません',
'data' => []
];
}
return [
'active' => true,
'message' => 'バッファ情報を正常に取得しました',
'data' => $status
];
} catch (Exception $e) {
return [
'active' => false,
'message' => 'バッファ情報の取得に失敗しました: ' . $e->getMessage(),
'data' => [],
'error' => $e
];
}
}
// 使用例
$result = safeGetBufferStatus(true);
if ($result['active']) {
echo "バッファが有効です\n";
print_r($result['data']);
} else {
echo "エラー: " . $result['message'] . "\n";
}
?>
まとめ
ob_get_status()
関数は、PHPの出力バッファリングシステムの詳細な状態を把握するための強力なツールです。単純なサイズやレベル情報だけでなく、ハンドラー情報、メモリ使用量、パフォーマンス特性まで詳細に分析することができます。
主な活用場面
- システム診断: バッファの健全性とパフォーマンス分析
- リアルタイム監視: 本番環境でのバッファ状態監視
- 開発デバッグ: 開発時のバッファ挙動分析と最適化
- パフォーマンス最適化: ボトルネック特定と改善提案
重要なポイント
$full_status
パラメータの適切な使い分け- パフォーマンスへの影響を考慮した実装
- 詳細な状態分析による最適化機会の発見
- 自動監視システムでの活用
この関数を活用することで、PHPアプリケーションの出力バッファリングを科学的に分析し、パフォーマンス向上とシステムの安定性確保を実現できます。特に大規模なWebアプリケーションやAPIシステムでは、ob_get_status()
による詳細な監視が品質向上の鍵となります。
実用的なTipsとトリック
1. 状態情報の可視化
<?php
function visualizeBufferStatus() {
$status = ob_get_status(true);
if (empty($status)) {
echo "┌────────────────┐\n";
echo "│ バッファなし │\n";
echo "└────────────────┘\n";
return;
}
echo "バッファ階層構造:\n";
echo "┌" . str_repeat("─", 50) . "┐\n";
foreach ($status as $level => $buffer) {
$indent = str_repeat(" ", $level);
$levelNum = $level + 1;
$sizeKB = round($buffer['size'] / 1024, 2);
$handler = substr($buffer['name'], 0, 20);
echo "│{$indent}レベル{$levelNum}: {$handler}" . str_repeat(" ", 25 - strlen($indent . "レベル{$levelNum}: {$handler}")) . "│\n";
echo "│{$indent} サイズ: {$sizeKB}KB" . str_repeat(" ", 33 - strlen($indent . " サイズ: {$sizeKB}KB")) . "│\n";
if ($level < count($status) - 1) {
echo "│" . str_repeat(" ", 50) . "│\n";
}
}
echo "└" . str_repeat("─", 50) . "┘\n";
// 統計情報
$totalSize = array_sum(array_column($status, 'size'));
$totalAllocated = array_sum(array_column($status, 'block_size'));
$efficiency = $totalAllocated > 0 ? round(($totalSize / $totalAllocated) * 100, 1) : 0;
echo "\n統計情報:\n";
echo "総バッファ数: " . count($status) . "\n";
echo "総サイズ: " . round($totalSize / 1024, 2) . "KB\n";
echo "総割当: " . round($totalAllocated / 1024, 2) . "KB\n";
echo "使用効率: {$efficiency}%\n";
}
// 使用例
ob_start();
echo "メインコンテンツ";
ob_start('strtoupper');
echo "uppercase text";
ob_start();
echo "深いレベル";
visualizeBufferStatus();
while (ob_get_level() > 0) {
ob_end_clean();
}
?>
2. カスタムハンドラーの状態監視
<?php
class CustomBufferHandler {
private $compressionLevel = 6;
private $statistics = [
'total_input' => 0,
'total_output' => 0,
'compression_calls' => 0
];
public function __invoke($buffer, $phase) {
$this->statistics['total_input'] += strlen($buffer);
$this->statistics['compression_calls']++;
// 実際の処理(例:圧縮)
$compressed = gzcompress($buffer, $this->compressionLevel);
$this->statistics['total_output'] += strlen($compressed);
return $compressed;
}
public function getStatistics() {
$compressionRatio = $this->statistics['total_input'] > 0 ?
(1 - ($this->statistics['total_output'] / $this->statistics['total_input'])) * 100 : 0;
return [
'input_bytes' => $this->statistics['total_input'],
'output_bytes' => $this->statistics['total_output'],
'compression_calls' => $this->statistics['compression_calls'],
'compression_ratio' => round($compressionRatio, 2),
'average_input_per_call' => $this->statistics['compression_calls'] > 0 ?
round($this->statistics['total_input'] / $this->statistics['compression_calls'], 2) : 0
];
}
public static function analyzeWithStatus() {
$status = ob_get_status(true);
$analysis = [];
foreach ($status as $level => $buffer) {
// カスタムハンドラーかどうか判定
$isCustom = !in_array($buffer['name'], [
'default output handler',
'ob_gzhandler',
'zlib'
]);
$analysis[] = [
'level' => $level + 1,
'handler' => $buffer['name'],
'is_custom' => $isCustom,
'size' => $buffer['size'],
'block_size' => $buffer['block_size'],
'efficiency' => $buffer['block_size'] > 0 ?
round(($buffer['size'] / $buffer['block_size']) * 100, 2) : 0,
'type' => $buffer['type'],
'chunk_size' => $buffer['chunk_size']
];
}
return $analysis;
}
}
// 使用例
$handler = new CustomBufferHandler();
ob_start([$handler, '__invoke']);
echo str_repeat("テストデータを圧縮します。", 100);
// バッファ状態と統計情報を同時に取得
$bufferAnalysis = CustomBufferHandler::analyzeWithStatus();
$handlerStats = $handler->getStatistics();
echo "=== ハンドラー分析 ===\n";
foreach ($bufferAnalysis as $analysis) {
echo "レベル{$analysis['level']}: {$analysis['handler']}\n";
echo " カスタムハンドラー: " . ($analysis['is_custom'] ? 'Yes' : 'No') . "\n";
echo " 効率: {$analysis['efficiency']}%\n";
echo " サイズ: {$analysis['size']}B / {$analysis['block_size']}B\n";
}
echo "\n=== 圧縮統計 ===\n";
echo "入力: " . number_format($handlerStats['input_bytes']) . "B\n";
echo "出力: " . number_format($handlerStats['output_bytes']) . "B\n";
echo "圧縮率: {$handlerStats['compression_ratio']}%\n";
echo "呼び出し回数: {$handlerStats['compression_calls']}\n";
ob_end_flush();
?>
3. 高度な診断機能
<?php
class AdvancedBufferDiagnostics {
public static function detectAnomalies() {
$status = ob_get_status(true);
$anomalies = [];
foreach ($status as $level => $buffer) {
// 異常に大きなチャンクサイズ
if ($buffer['chunk_size'] > 0 && $buffer['chunk_size'] > $buffer['size'] * 5) {
$anomalies[] = [
'level' => $level + 1,
'type' => 'oversized_chunk',
'severity' => 'medium',
'message' => 'チャンクサイズがバッファサイズの5倍以上です',
'details' => [
'chunk_size' => $buffer['chunk_size'],
'buffer_size' => $buffer['size'],
'ratio' => round($buffer['chunk_size'] / max($buffer['size'], 1), 2)
]
];
}
// 異常に低い効率
$efficiency = $buffer['block_size'] > 0 ? ($buffer['size'] / $buffer['block_size']) * 100 : 0;
if ($efficiency < 10 && $buffer['size'] > 0) {
$anomalies[] = [
'level' => $level + 1,
'type' => 'very_low_efficiency',
'severity' => 'high',
'message' => 'メモリ使用効率が10%未満です',
'details' => [
'efficiency' => round($efficiency, 2),
'allocated' => $buffer['block_size'],
'used' => $buffer['size']
]
];
}
// 不正なバッファタイプ
if (!in_array($buffer['type'], [0, 1, 2])) {
$anomalies[] = [
'level' => $level + 1,
'type' => 'invalid_buffer_type',
'severity' => 'critical',
'message' => '不正なバッファタイプが検出されました',
'details' => [
'type' => $buffer['type'],
'handler' => $buffer['name']
]
];
}
}
return $anomalies;
}
public static function generateHealthScore() {
$status = ob_get_status(true);
if (empty($status)) {
return [
'score' => 100,
'message' => 'バッファは使用されていません',
'factors' => []
];
}
$score = 100;
$factors = [];
// ネストレベルの評価
$nestingPenalty = max(0, (count($status) - 3) * 10);
$score -= $nestingPenalty;
if ($nestingPenalty > 0) {
$factors[] = "深いネスト (-{$nestingPenalty}点)";
}
// 効率の評価
foreach ($status as $level => $buffer) {
$efficiency = $buffer['block_size'] > 0 ? ($buffer['size'] / $buffer['block_size']) * 100 : 100;
if ($efficiency < 30) {
$penalty = 20;
$score -= $penalty;
$factors[] = "レベル" . ($level + 1) . "の低効率 (-{$penalty}点)";
} elseif ($efficiency < 60) {
$penalty = 10;
$score -= $penalty;
$factors[] = "レベル" . ($level + 1) . "の中程度の効率低下 (-{$penalty}点)";
}
}
// サイズの評価
$totalSize = array_sum(array_column($status, 'size'));
if ($totalSize > 5 * 1024 * 1024) { // 5MB
$penalty = 25;
$score -= $penalty;
$factors[] = "大きな総サイズ (-{$penalty}点)";
} elseif ($totalSize > 1024 * 1024) { // 1MB
$penalty = 10;
$score -= $penalty;
$factors[] = "やや大きな総サイズ (-{$penalty}点)";
}
// 異常検出
$anomalies = self::detectAnomalies();
foreach ($anomalies as $anomaly) {
$penalty = $anomaly['severity'] === 'critical' ? 30 :
($anomaly['severity'] === 'high' ? 20 : 10);
$score -= $penalty;
$factors[] = $anomaly['message'] . " (-{$penalty}点)";
}
$score = max(0, $score);
// スコアレベルの判定
if ($score >= 90) $level = 'excellent';
elseif ($score >= 80) $level = 'good';
elseif ($score >= 70) $level = 'fair';
elseif ($score >= 50) $level = 'poor';
else $level = 'critical';
return [
'score' => $score,
'level' => $level,
'message' => self::getScoreMessage($level),
'factors' => $factors,
'anomalies' => $anomalies
];
}
private static function getScoreMessage($level) {
$messages = [
'excellent' => 'バッファ使用状況は非常に良好です',
'good' => 'バッファ使用状況は良好です',
'fair' => 'バッファ使用状況は普通です。改善の余地があります',
'poor' => 'バッファ使用状況に問題があります。改善が推奨されます',
'critical' => 'バッファ使用状況に深刻な問題があります。即座の対応が必要です'
];
return $messages[$level] ?? '状態を判定できませんでした';
}
public static function suggestOptimizations() {
$status = ob_get_status(true);
$suggestions = [];
if (empty($status)) {
return ['現在バッファは使用されていません'];
}
// ネストレベルの最適化
if (count($status) > 3) {
$suggestions[] = [
'category' => 'structure',
'priority' => 'high',
'suggestion' => 'バッファのネストレベルを' . (count($status) - 3) . '階層削減してください',
'benefit' => 'メモリ使用量の削減とパフォーマンス向上',
'implementation' => 'バッファ構造を見直し、不要な中間バッファを除去する'
];
}
// ハンドラーの最適化
$handlerCounts = array_count_values(array_column($status, 'name'));
foreach ($handlerCounts as $handler => $count) {
if ($count > 1 && $handler === 'default output handler') {
$suggestions[] = [
'category' => 'handler',
'priority' => 'medium',
'suggestion' => 'デフォルトハンドラーが' . $count . '回使用されています。統合を検討してください',
'benefit' => 'メモリ使用量の最適化',
'implementation' => '複数のバッファを単一のバッファに統合する'
];
}
}
// 効率の最適化
foreach ($status as $level => $buffer) {
$efficiency = $buffer['block_size'] > 0 ? ($buffer['size'] / $buffer['block_size']) * 100 : 100;
if ($efficiency < 50 && $buffer['size'] > 1024) {
$suggestions[] = [
'category' => 'efficiency',
'priority' => $efficiency < 30 ? 'high' : 'medium',
'suggestion' => "レベル" . ($level + 1) . "のメモリ効率が{$efficiency}%と低いです",
'benefit' => 'メモリ使用量の削減',
'implementation' => 'バッファサイズを適切に調整するか、ストリーミング処理を検討する'
];
}
}
// サイズベースの最適化
$totalSize = array_sum(array_column($status, 'size'));
if ($totalSize > 1024 * 1024) {
$suggestions[] = [
'category' => 'size',
'priority' => $totalSize > 5 * 1024 * 1024 ? 'high' : 'medium',
'suggestion' => '総バッファサイズが' . round($totalSize / (1024 * 1024), 2) . 'MBと大きくなっています',
'benefit' => 'メモリ使用量の大幅な削減',
'implementation' => 'ストリーミング処理、分割処理、または圧縮の導入を検討する'
];
}
return $suggestions;
}
public static function generateComprehensiveReport() {
$status = ob_get_status(true);
return [
'timestamp' => date('Y-m-d H:i:s'),
'basic_info' => [
'buffer_count' => count($status),
'total_size' => array_sum(array_column($status, 'size')),
'total_allocated' => array_sum(array_column($status, 'block_size')),
'memory_usage' => memory_get_usage(),
'peak_memory' => memory_get_peak_usage()
],
'buffer_details' => $status,
'health_score' => self::generateHealthScore(),
'anomalies' => self::detectAnomalies(),
'optimizations' => self::suggestOptimizations(),
'performance_metrics' => [
'overall_efficiency' => self::calculateOverallEfficiency($status),
'fragmentation_level' => self::calculateFragmentation($status),
'handler_diversity' => count(array_unique(array_column($status, 'name')))
]
];
}
private static function calculateOverallEfficiency($status) {
if (empty($status)) return 100;
$totalAllocated = array_sum(array_column($status, 'block_size'));
$totalUsed = array_sum(array_column($status, 'size'));
return $totalAllocated > 0 ? round(($totalUsed / $totalAllocated) * 100, 2) : 0;
}
private static function calculateFragmentation($status) {
if (count($status) <= 1) return 0;
$sizes = array_column($status, 'size');
$mean = array_sum($sizes) / count($sizes);
$variance = array_sum(array_map(function($size) use ($mean) {
return pow($size - $mean, 2);
}, $sizes)) / count($sizes);
$stdDev = sqrt($variance);
return $mean > 0 ? round(($stdDev / $mean) * 100, 2) : 0;
}
}
// 総合的な使用例
echo "=== 高度なバッファ診断 ===\n";
// テスト用のバッファ構造
ob_start();
echo str_repeat("レベル1データ", 100);
ob_start('strtoupper');
echo str_repeat("level 2 data", 200);
ob_start();
echo str_repeat("深いデータ", 50);
ob_start();
echo "さらに深いレベル";
// 総合レポート生成
$report = AdvancedBufferDiagnostics::generateComprehensiveReport();
echo "【基本情報】\n";
echo "バッファ数: {$report['basic_info']['buffer_count']}\n";
echo "総サイズ: " . round($report['basic_info']['total_size'] / 1024, 2) . "KB\n";
echo "メモリ使用量: " . round($report['basic_info']['memory_usage'] / 1024, 2) . "KB\n";
echo "\n【健全性スコア】\n";
echo "スコア: {$report['health_score']['score']}/100 ({$report['health_score']['level']})\n";
echo "評価: {$report['health_score']['message']}\n";
if (!empty($report['health_score']['factors'])) {
echo "影響要因:\n";
foreach ($report['health_score']['factors'] as $factor) {
echo " - {$factor}\n";
}
}
echo "\n【パフォーマンス指標】\n";
echo "全体効率: {$report['performance_metrics']['overall_efficiency']}%\n";
echo "断片化レベル: {$report['performance_metrics']['fragmentation_level']}%\n";
echo "ハンドラー多様性: {$report['performance_metrics']['handler_diversity']}\n";
if (!empty($report['anomalies'])) {
echo "\n【異常検出】\n";
foreach ($report['anomalies'] as $anomaly) {
echo "[{$anomaly['severity']}] レベル{$anomaly['level']}: {$anomaly['message']}\n";
}
}
if (!empty($report['optimizations'])) {
echo "\n【最適化提案】\n";
foreach ($report['optimizations'] as $opt) {
echo "[{$opt['priority']}] {$opt['suggestion']}\n";
echo " 効果: {$opt['benefit']}\n";
echo " 実装: {$opt['implementation']}\n\n";
}
}
// クリーンアップ
while (ob_get_level() > 0) {
ob_end_clean();
}
?>
これでob_get_status
関数の完全ガイドが完成しました。この記事では、基本的な使い方から高度な診断システムまで、実践的で即座に使える内容を網羅しています。
特に「高度な診断機能」の部分では、実際の本番環境で役立つ異常検出、健全性スコア計算、最適化提案などの実用的な機能を実装しており、PHPの出力バッファリングを科学的にアプローチできる内容になっています。