はじめに
PHPのmax
関数は、複数の値または配列から最大値を取得する関数です。数値比較、文字列比較、配列処理など、様々な場面で活用される基本的でありながら重要な関数の一つです。
今回は、max
関数の基本的な使い方から、実際の開発現場で役立つ応用テクニックまで詳しく解説します。
max関数とは
基本構文
mixed max(mixed $value, mixed ...$values)
mixed max(array $value_array)
パラメータ:
$value, ...$values
:比較する複数の値$value_array
:値を含む配列
戻り値:
- 最大値(数値、文字列、その他のデータ型)
基本的な使用例
// 複数の引数から最大値を取得
echo max(1, 3, 2, 5, 4) . "\n"; // 5
echo max(1.5, 2.8, 1.9) . "\n"; // 2.8
echo max('apple', 'banana', 'cherry') . "\n"; // cherry (辞書順)
// 配列から最大値を取得
$numbers = [10, 25, 3, 47, 18];
echo max($numbers) . "\n"; // 47
$fruits = ['apple', 'banana', 'cherry'];
echo max($fruits) . "\n"; // cherry
// 混在したデータ型
echo max(10, '20', 5.5) . "\n"; // '20' (文字列として比較)
データ型別の比較動作
数値の比較
function demonstrateNumericComparison() {
$test_cases = [
[1, 2, 3, 4, 5],
[-10, -5, 0, 5, 10],
[3.14, 2.71, 1.41, 1.73],
[0, 0.0, false, null, ''], // 特殊なケース
];
echo "=== 数値比較の例 ===\n";
foreach ($test_cases as $i => $values) {
$max_value = max($values);
echo "ケース " . ($i + 1) . ": " . json_encode($values) . " → {$max_value}\n";
echo " 型: " . gettype($max_value) . "\n";
}
}
demonstrateNumericComparison();
文字列の比較
function demonstrateStringComparison() {
$test_cases = [
['apple', 'banana', 'cherry'], // アルファベット順
['1', '10', '2', '20'], // 文字列としての比較
['a', 'A', 'b', 'B'], // 大文字小文字
['短い', 'もう少し長い文字列', 'a'], // 日本語とアルファベット
];
echo "\n=== 文字列比較の例 ===\n";
foreach ($test_cases as $i => $values) {
$max_value = max($values);
echo "ケース " . ($i + 1) . ": " . json_encode($values) . " → '{$max_value}'\n";
}
// ASCIIコード順での比較説明
echo "\n=== ASCII比較の詳細 ===\n";
$chars = ['a', 'A', 'z', 'Z', '0', '9'];
foreach ($chars as $char) {
echo "'{$char}' = ASCII " . ord($char) . "\n";
}
echo "max(['a', 'A', 'z', 'Z']) = '" . max(['a', 'A', 'z', 'Z']) . "'\n";
}
demonstrateStringComparison();
混在データ型の比較
function demonstrateMixedTypeComparison() {
$test_cases = [
[1, '2', 3.0], // 数値と文字列
[true, false, 1, 0], // 真偽値と数値
['10', 10, 10.0], // 同じ値の異なる型
[null, false, 0, ''], // falsy値
['apple', 123, true], // 完全に異なる型
];
echo "\n=== 混在データ型比較の例 ===\n";
foreach ($test_cases as $i => $values) {
$max_value = max($values);
echo "ケース " . ($i + 1) . ": ";
// 各値の型を表示
$value_info = [];
foreach ($values as $val) {
$value_info[] = json_encode($val) . "(" . gettype($val) . ")";
}
echo implode(", ", $value_info);
echo " → " . json_encode($max_value) . "(" . gettype($max_value) . ")\n";
}
}
demonstrateMixedTypeComparison();
実用的な応用例
1. 統計処理クラス
class StatisticsCalculator {
private $data = [];
public function __construct($data = []) {
$this->data = $data;
}
public function addValue($value) {
$this->data[] = $value;
}
public function addValues($values) {
$this->data = array_merge($this->data, $values);
}
public function getMax() {
if (empty($this->data)) {
throw new InvalidArgumentException('データが空です');
}
return max($this->data);
}
public function getMin() {
if (empty($this->data)) {
throw new InvalidArgumentException('データが空です');
}
return min($this->data);
}
public function getRange() {
if (empty($this->data)) {
throw new InvalidArgumentException('データが空です');
}
return max($this->data) - min($this->data);
}
public function getAverage() {
if (empty($this->data)) {
throw new InvalidArgumentException('データが空です');
}
return array_sum($this->data) / count($this->data);
}
public function getStats() {
if (empty($this->data)) {
throw new InvalidArgumentException('データが空です');
}
$sorted_data = $this->data;
sort($sorted_data);
$count = count($sorted_data);
return [
'count' => $count,
'min' => min($sorted_data),
'max' => max($sorted_data),
'range' => max($sorted_data) - min($sorted_data),
'sum' => array_sum($sorted_data),
'average' => array_sum($sorted_data) / $count,
'median' => $this->calculateMedian($sorted_data),
'mode' => $this->calculateMode($sorted_data)
];
}
private function calculateMedian($values) {
sort($values);
$count = count($values);
if ($count % 2 === 0) {
return ($values[$count / 2 - 1] + $values[$count / 2]) / 2;
} else {
return $values[($count - 1) / 2];
}
}
public function generateMarketReport() {
$report = "=== 価格比較市場レポート ===\n";
$report .= "分析日時: " . date('Y-m-d H:i:s') . "\n";
$report .= "商品数: " . count($this->products) . "\n\n";
// 最安商品TOP5
$best_deals = $this->findBestDeals(5);
$report .= "【最安商品 TOP5】\n";
$rank = 1;
foreach ($best_deals as $id => $product) {
$report .= "{$rank}. {$product['name']}: ¥" . number_format($product['min_price']) .
" (店舗数: {$product['store_count']})\n";
$rank++;
}
// 価格差の大きい商品
$price_ranges = $this->findHighestPriceRanges(3);
$report .= "\n【価格差の大きい商品 TOP3】\n";
$rank = 1;
foreach ($price_ranges as $id => $product) {
$report .= "{$rank}. {$product['name']}: ¥" . number_format($product['price_range']) .
"の価格差 (¥" . number_format($product['min_price']) . " - ¥" . number_format($product['max_price']) . ")\n";
$rank++;
}
// 価格異常の検出
$anomalies = $this->findPriceAnomalies();
if (!empty($anomalies)) {
$report .= "\n【価格異常の検出】\n";
foreach (array_slice($anomalies, 0, 3) as $anomaly) {
$report .= "⚠️ {$anomaly['product_name']} @ {$anomaly['store']}: ¥" . number_format($anomaly['price']) .
" (中央値の{$anomaly['price_ratio']}倍)\n";
}
}
return $report;
}
}
// 使用例
$comparator = new PriceComparator();
// サンプル商品データを追加
$comparator->addProduct('laptop_001', 'ゲーミングノートPC XZ-1000', [
'Store_A' => 89800,
'Store_B' => 92000,
'Store_C' => 87500,
'Store_D' => 95000,
'Store_E' => 88900
]);
$comparator->addProduct('smartphone_001', 'スマートフォン ABC-12', [
'Store_A' => 65000,
'Store_B' => 68000,
'Store_C' => 64500,
'Store_D' => 89000, // 異常に高い価格
'Store_E' => 66500
]);
$comparator->addProduct('tablet_001', 'タブレット DEF-8', [
'Store_A' => 35000,
'Store_B' => 37000,
'Store_C' => 36200,
'Store_D' => 38500,
'Store_E' => 34800
]);
echo "=== 価格比較システムの例 ===\n";
echo $comparator->generateMarketReport();
// 特定商品の比較
echo "\n=== 商品比較詳細 ===\n";
$comparison = $comparator->compareProducts(['laptop_001', 'smartphone_001']);
foreach ($comparison['products'] as $id => $product) {
echo "商品: {$product['name']}\n";
echo " 最安値: ¥" . number_format($product['min_price']) . " @ {$product['best_store']}\n";
echo " 最高値: ¥" . number_format($product['max_price']) . " @ {$product['worst_store']}\n";
echo " 平均価格: ¥" . number_format($product['average_price']) . "\n\n";
}
maxの特殊な使用場面
空の配列と例外処理
class SafeMaxCalculator {
public static function safeMax($values, $default = null) {
if (empty($values)) {
if ($default !== null) {
return $default;
}
throw new InvalidArgumentException('空の配列からは最大値を取得できません');
}
// null値を除外
$values = array_filter($values, function($value) {
return $value !== null;
});
if (empty($values)) {
if ($default !== null) {
return $default;
}
throw new InvalidArgumentException('すべてnull値のため最大値を取得できません');
}
return max($values);
}
public static function maxWithValidation($values, $validator = null) {
if (empty($values)) {
throw new InvalidArgumentException('空の配列です');
}
if ($validator !== null) {
$valid_values = array_filter($values, $validator);
if (empty($valid_values)) {
throw new InvalidArgumentException('有効な値がありません');
}
$values = $valid_values;
}
return max($values);
}
public static function demonstrateEdgeCases() {
$test_cases = [
'empty_array' => [],
'null_values' => [null, null, null],
'mixed_nulls' => [1, null, 3, null, 2],
'zero_values' => [0, 0, 0],
'negative_values' => [-5, -2, -8, -1],
'mixed_types' => [1, '10', 3.5, true, false],
];
echo "=== エッジケースのテスト ===\n";
foreach ($test_cases as $case_name => $values) {
echo "ケース: {$case_name}\n";
echo "値: " . json_encode($values) . "\n";
try {
// 通常のmax
if (!empty($values)) {
$normal_max = max($values);
echo "通常のmax: " . json_encode($normal_max) . " (" . gettype($normal_max) . ")\n";
} else {
echo "通常のmax: 空配列のためエラーが発生します\n";
}
// セーフmax(デフォルト値あり)
$safe_max = self::safeMax($values, 'N/A');
echo "セーフmax: " . json_encode($safe_max) . "\n";
// バリデーション付きmax(数値のみ)
$numeric_max = self::maxWithValidation($values, function($value) {
return is_numeric($value) && $value !== null;
});
echo "数値のみmax: " . json_encode($numeric_max) . "\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
echo "\n";
}
}
}
SafeMaxCalculator::demonstrateEdgeCases();
パフォーマンス最適化
class OptimizedMaxFinder {
public static function benchmarkMaxMethods($data_size = 100000) {
// テストデータ生成
$data = [];
for ($i = 0; $i < $data_size; $i++) {
$data[] = mt_rand(1, 1000000);
}
$methods = [
'built_in_max' => function($arr) { return max($arr); },
'foreach_loop' => function($arr) {
$max = $arr[0];
foreach ($arr as $value) {
if ($value > $max) {
$max = $value;
}
}
return $max;
},
'for_loop' => function($arr) {
$max = $arr[0];
$count = count($arr);
for ($i = 1; $i < $count; $i++) {
if ($arr[$i] > $max) {
$max = $arr[$i];
}
}
return $max;
},
'array_reduce' => function($arr) {
return array_reduce($arr, function($carry, $item) {
return $carry === null ? $item : max($carry, $item);
});
},
'sort_method' => function($arr) {
sort($arr);
return end($arr);
}
];
echo "=== max関数のパフォーマンス比較 ===\n";
echo "データサイズ: " . number_format($data_size) . "件\n\n";
$results = [];
foreach ($methods as $name => $method) {
$start_time = microtime(true);
$result = $method($data);
$end_time = microtime(true);
$execution_time = ($end_time - $start_time) * 1000;
$results[$name] = $execution_time;
echo sprintf("%-15s: %8.2f ms (結果: %d)\n", $name, $execution_time, $result);
}
// 最も高速な方法を特定
$fastest = array_keys($results, min($results))[0];
echo "\n最高速: {$fastest}\n";
return $results;
}
public static function findMaxWithIndex($array) {
if (empty($array)) {
throw new InvalidArgumentException('空の配列です');
}
$max_value = null;
$max_index = null;
foreach ($array as $index => $value) {
if ($max_value === null || $value > $max_value) {
$max_value = $value;
$max_index = $index;
}
}
return ['value' => $max_value, 'index' => $max_index];
}
public static function findTopN($array, $n = 3) {
if ($n <= 0) {
throw new InvalidArgumentException('nは1以上である必要があります');
}
if (empty($array)) {
return [];
}
// 値とインデックスを保持
$indexed_values = [];
foreach ($array as $index => $value) {
$indexed_values[] = ['value' => $value, 'index' => $index];
}
// 降順でソート
usort($indexed_values, function($a, $b) {
return $b['value'] <=> $a['value'];
});
return array_slice($indexed_values, 0, $n);
}
}
// パフォーマンステスト
OptimizedMaxFinder::benchmarkMaxMethods(50000);
echo "\n=== その他の最大値関連処理 ===\n";
// インデックス付きmax
$test_array = [10, 25, 8, 47, 32, 15];
$max_info = OptimizedMaxFinder::findMaxWithIndex($test_array);
echo "配列: " . json_encode($test_array) . "\n";
echo "最大値: {$max_info['value']} (インデックス: {$max_info['index']})\n\n";
// TOP-N取得
$scores = [85, 92, 78, 96, 88, 91, 79, 94, 87];
$top3 = OptimizedMaxFinder::findTopN($scores, 3);
echo "スコア: " . json_encode($scores) . "\n";
echo "TOP3:\n";
foreach ($top3 as $i => $item) {
echo " " . ($i + 1) . ". {$item['value']} (インデックス: {$item['index']})\n";
}
多次元配列での最大値処理
class MultiDimensionalMaxProcessor {
public static function findMaxInMatrix($matrix) {
if (empty($matrix)) {
throw new InvalidArgumentException('空の配列です');
}
$all_values = [];
foreach ($matrix as $row) {
if (is_array($row)) {
$all_values = array_merge($all_values, $row);
} else {
$all_values[] = $row;
}
}
return empty($all_values) ? null : max($all_values);
}
public static function findMaxByColumn($matrix, $column_index) {
if (empty($matrix)) {
throw new InvalidArgumentException('空の配列です');
}
$column_values = [];
foreach ($matrix as $row) {
if (is_array($row) && isset($row[$column_index])) {
$column_values[] = $row[$column_index];
}
}
return empty($column_values) ? null : max($column_values);
}
public static function findMaxByKey($array, $key) {
if (empty($array)) {
throw new InvalidArgumentException('空の配列です');
}
$values = [];
foreach ($array as $item) {
if (is_array($item) && isset($item[$key])) {
$values[] = $item[$key];
} elseif (is_object($item) && property_exists($item, $key)) {
$values[] = $item->$key;
}
}
return empty($values) ? null : max($values);
}
public static function findMaxRowSum($matrix) {
if (empty($matrix)) {
throw new InvalidArgumentException('空の配列です');
}
$row_sums = [];
foreach ($matrix as $row_index => $row) {
if (is_array($row)) {
$row_sums[$row_index] = array_sum($row);
}
}
if (empty($row_sums)) {
return null;
}
$max_sum = max($row_sums);
$max_row_index = array_search($max_sum, $row_sums);
return [
'max_sum' => $max_sum,
'row_index' => $max_row_index,
'row_data' => $matrix[$max_row_index]
];
}
public static function demonstrateMultiDimensional() {
echo "=== 多次元配列での最大値処理 ===\n";
// テストデータ
$sales_data = [
['店舗' => '新宿店', '1月' => 1200000, '2月' => 1350000, '3月' => 1180000],
['店舗' => '渋谷店', '1月' => 980000, '2月' => 1100000, '3月' => 1250000],
['店舗' => '池袋店', '1月' => 1050000, '2月' => 1200000, '3月' => 1080000],
];
echo "売上データ:\n";
foreach ($sales_data as $store) {
echo " {$store['店舗']}: 1月=" . number_format($store['1月']) .
", 2月=" . number_format($store['2月']) .
", 3月=" . number_format($store['3月']) . "\n";
}
// 最高売上を検索
$max_jan = self::findMaxByKey($sales_data, '1月');
$max_feb = self::findMaxByKey($sales_data, '2月');
$max_mar = self::findMaxByKey($sales_data, '3月');
echo "\n月別最高売上:\n";
echo " 1月: ¥" . number_format($max_jan) . "\n";
echo " 2月: ¥" . number_format($max_feb) . "\n";
echo " 3月: ¥" . number_format($max_mar) . "\n";
// 数値データのみを抽出して全体の最大値
$numeric_matrix = [
[1200000, 1350000, 1180000],
[980000, 1100000, 1250000],
[1050000, 1200000, 1080000],
];
$overall_max = self::findMaxInMatrix($numeric_matrix);
echo "\n全体の最高売上: ¥" . number_format($overall_max) . "\n";
// 店舗別合計売上の最大
$max_store_total = self::findMaxRowSum($numeric_matrix);
echo "\n最高売上店舗:\n";
echo " 店舗インデックス: {$max_store_total['row_index']}\n";
echo " 合計売上: ¥" . number_format($max_store_total['max_sum']) . "\n";
echo " 月別詳細: " . json_encode($max_store_total['row_data']) . "\n";
}
}
MultiDimensionalMaxProcessor::demonstrateMultiDimensional();
実用的なヘルパー関数集
class MaxUtilities {
/**
* 条件付き最大値取得
*/
public static function maxIf($array, $condition) {
$filtered = array_filter($array, $condition);
return empty($filtered) ? null : max($filtered);
}
/**
* 複数の最大値を取得(同値の場合)
*/
public static function findAllMax($array) {
if (empty($array)) {
return [];
}
$max_value = max($array);
$max_indices = [];
foreach ($array as $index => $value) {
if ($value === $max_value) {
$max_indices[] = $index;
}
}
return [
'value' => $max_value,
'indices' => $max_indices,
'count' => count($max_indices)
];
}
/**
* 範囲制限付き最大値
*/
public static function maxInRange($array, $min_limit = null, $max_limit = null) {
if (empty($array)) {
return null;
}
$filtered = array_filter($array, function($value) use ($min_limit, $max_limit) {
if ($min_limit !== null && $value < $min_limit) {
return false;
}
if ($max_limit !== null && $value > $max_limit) {
return false;
}
return true;
});
return empty($filtered) ? null : max($filtered);
}
/**
* 型安全な最大値取得
*/
public static function maxNumeric($array) {
$numeric_values = array_filter($array, 'is_numeric');
return empty($numeric_values) ? null : max($numeric_values);
}
public static function demonstrateUtilities() {
echo "=== 実用的なmax関連ユーティリティ ===\n";
$test_data = [10, 25, 8, 25, 32, 15, 25, 7];
echo "テストデータ: " . json_encode($test_data) . "\n\n";
// 条件付き最大値(20以上)
$max_over_20 = self::maxIf($test_data, function($value) {
return $value >= 20;
});
echo "20以上の最大値: {$max_over_20}\n";
// 全ての最大値を検索
$all_max = self::findAllMax($test_data);
echo "最大値の詳細:\n";
echo " 値: {$all_max['value']}\n";
echo " 出現位置: " . json_encode($all_max['indices']) . "\n";
echo " 出現回数: {$all_max['count']}\n";
// 範囲制限付き
$max_in_range = self::maxInRange($test_data, 10, 30);
echo "10-30の範囲での最大値: {$max_in_range}\n";
// 混在データでの数値のみ最大値
$mixed_data = [10, '25', 8.5, true, 'text', 32, null];
$numeric_max = self::maxNumeric($mixed_data);
echo "混在データでの数値最大値: {$numeric_max}\n";
echo " 元データ: " . json_encode($mixed_data) . "\n";
}
}
MaxUtilities::demonstrateUtilities();
まとめ
PHPのmax
関数は、シンプルながら非常に強力で汎用性の高い関数です。
主要なポイント:
- 基本機能:複数の値や配列から最大値を取得
- 型の柔軟性:数値、文字列、混在型すべてに対応
- 比較ルール:PHPの標準的な比較演算子に従う
- パフォーマンス:内蔵関数として最適化済み
実用的な応用場面:
- 統計処理とデータ分析
- 画像処理とリサイズ計算
- パフォーマンス監視システム
- 価格比較と市場分析
- 多次元配列での最大値検索
- 条件付きフィルタリングと組み合わせ
注意点:
- 空の配列では警告が発生
- データ型の混在時は予期しない結果の可能性
- 大量データでのメモリ使用量
- null値の取り扱い
max
関数を適切に活用し、エラーハンドリングや型チェックと組み合わせることで、堅牢で効率的なデータ処理システムを構築できます。Median($sorted_data) { $count = count($sorted_data); if ($count % 2 === 0) { // 偶数個の場合は中央2つの平均 $mid = $count / 2; return ($sorted_data[$mid – 1] + $sorted_data[$mid]) / 2; } else { // 奇数個の場合は中央値 return $sorted_data[($count – 1) / 2]; } }
private function calculateMode($data) {
$frequencies = array_count_values($data);
$max_frequency = max($frequencies);
$modes = [];
foreach ($frequencies as $value => $frequency) {
if ($frequency === $max_frequency) {
$modes[] = $value;
}
}
return count($modes) === 1 ? $modes[0] : $modes;
}
public function findOutliers($threshold = 1.5) {
if (count($this->data) < 4) {
return [];
}
$sorted = $this->data;
sort($sorted);
$count = count($sorted);
// 四分位数を計算
$q1_index = intval($count * 0.25);
$q3_index = intval($count * 0.75);
$q1 = $sorted[$q1_index];
$q3 = $sorted[$q3_index];
$iqr = $q3 - $q1;
// 外れ値の範囲
$lower_bound = $q1 - ($threshold * $iqr);
$upper_bound = $q3 + ($threshold * $iqr);
$outliers = [];
foreach ($this->data as $value) {
if ($value < $lower_bound || $value > $upper_bound) {
$outliers[] = $value;
}
}
return $outliers;
}
}
// 使用例 $calculator = new StatisticsCalculator([85, 90, 78, 92, 88, 76, 95, 82, 89, 91, 150]); // 150は外れ値
echo “=== 統計計算の例 ===\n”; $stats = $calculator->getStats(); foreach ($stats as $key => $value) { if (is_array($value)) { echo ucfirst($key) . “: ” . json_encode($value) . “\n”; } else { echo ucfirst($key) . “: ” . (is_float($value) ? round($value, 2) : $value) . “\n”; } }
$outliers = $calculator->findOutliers(); echo “Outliers: ” . json_encode($outliers) . “\n”;
### 2. 画像リサイズ処理
```php
class ImageResizer {
public function calculateOptimalSize($original_width, $original_height, $constraints) {
// 制約条件から最適なサイズを計算
$max_width = isset($constraints['max_width']) ? $constraints['max_width'] : PHP_INT_MAX;
$max_height = isset($constraints['max_height']) ? $constraints['max_height'] : PHP_INT_MAX;
$min_width = isset($constraints['min_width']) ? $constraints['min_width'] : 1;
$min_height = isset($constraints['min_height']) ? $constraints['min_height'] : 1;
// アスペクト比を維持しながらリサイズ
$width_ratio = $max_width / $original_width;
$height_ratio = $max_height / $original_height;
// より小さい比率を使用(アスペクト比維持)
$scale_ratio = min($width_ratio, $height_ratio, 1); // 拡大はしない
$new_width = intval($original_width * $scale_ratio);
$new_height = intval($original_height * $scale_ratio);
// 最小サイズ制約を適用
$new_width = max($new_width, $min_width);
$new_height = max($new_height, $min_height);
// 最大サイズ制約を再適用
$new_width = min($new_width, $max_width);
$new_height = min($new_height, $max_height);
return [
'width' => $new_width,
'height' => $new_height,
'scale_ratio' => $scale_ratio,
'original_width' => $original_width,
'original_height' => $original_height
];
}
public function generateMultipleSizes($original_width, $original_height, $size_presets) {
$results = [];
foreach ($size_presets as $name => $constraints) {
$size_info = $this->calculateOptimalSize($original_width, $original_height, $constraints);
$size_info['preset_name'] = $name;
$results[$name] = $size_info;
}
return $results;
}
public function calculateCropArea($original_width, $original_height, $target_width, $target_height, $crop_position = 'center') {
$scale_x = $target_width / $original_width;
$scale_y = $target_height / $original_height;
// より大きいスケールを使用(切り抜きでアスペクト比調整)
$scale = max($scale_x, $scale_y);
$scaled_width = intval($original_width * $scale);
$scaled_height = intval($original_height * $scale);
// 切り抜き位置を計算
$crop_x = 0;
$crop_y = 0;
if ($scaled_width > $target_width) {
switch ($crop_position) {
case 'left':
$crop_x = 0;
break;
case 'right':
$crop_x = $scaled_width - $target_width;
break;
case 'center':
default:
$crop_x = intval(($scaled_width - $target_width) / 2);
break;
}
}
if ($scaled_height > $target_height) {
switch ($crop_position) {
case 'top':
$crop_y = 0;
break;
case 'bottom':
$crop_y = $scaled_height - $target_height;
break;
case 'center':
default:
$crop_y = intval(($scaled_height - $target_height) / 2);
break;
}
}
return [
'scale' => $scale,
'scaled_width' => $scaled_width,
'scaled_height' => $scaled_height,
'crop_x' => $crop_x,
'crop_y' => $crop_y,
'crop_width' => min($scaled_width, $target_width),
'crop_height' => min($scaled_height, $target_height)
];
}
}
// 使用例
$resizer = new ImageResizer();
// 元画像のサイズ
$original_width = 1920;
$original_height = 1080;
// サイズプリセット
$presets = [
'thumbnail' => ['max_width' => 150, 'max_height' => 150, 'min_width' => 100, 'min_height' => 100],
'medium' => ['max_width' => 640, 'max_height' => 480],
'large' => ['max_width' => 1200, 'max_height' => 800],
'banner' => ['max_width' => 1000, 'max_height' => 300],
];
echo "=== 画像リサイズ計算の例 ===\n";
echo "元画像サイズ: {$original_width} × {$original_height}\n\n";
$sizes = $resizer->generateMultipleSizes($original_width, $original_height, $presets);
foreach ($sizes as $name => $info) {
echo "{$name}: {$info['width']} × {$info['height']} (スケール: " . round($info['scale_ratio'], 3) . ")\n";
}
// 切り抜き処理の例
echo "\n=== 切り抜き処理の例 ===\n";
$crop_info = $resizer->calculateCropArea($original_width, $original_height, 800, 600, 'center');
echo "800×600への切り抜き:\n";
echo "スケール: " . round($crop_info['scale'], 3) . "\n";
echo "スケール後サイズ: {$crop_info['scaled_width']} × {$crop_info['scaled_height']}\n";
echo "切り抜き位置: ({$crop_info['crop_x']}, {$crop_info['crop_y']})\n";
echo "切り抜きサイズ: {$crop_info['crop_width']} × {$crop_info['crop_height']}\n";
3. パフォーマンス監視システム
class PerformanceMonitor {
private $metrics = [];
private $thresholds = [];
public function __construct($thresholds = []) {
$this->thresholds = array_merge([
'response_time' => 1.0, // 1秒
'memory_usage' => 128 * 1024 * 1024, // 128MB
'cpu_usage' => 80.0, // 80%
'disk_usage' => 90.0, // 90%
], $thresholds);
}
public function recordMetric($name, $value, $timestamp = null) {
if ($timestamp === null) {
$timestamp = microtime(true);
}
if (!isset($this->metrics[$name])) {
$this->metrics[$name] = [];
}
$this->metrics[$name][] = [
'value' => $value,
'timestamp' => $timestamp
];
// 古いデータを削除(直近1時間のみ保持)
$cutoff_time = $timestamp - 3600; // 1時間前
$this->metrics[$name] = array_filter($this->metrics[$name], function($record) use ($cutoff_time) {
return $record['timestamp'] > $cutoff_time;
});
}
public function getMetricSummary($metric_name, $time_window = 300) { // 5分間のデフォルト
if (!isset($this->metrics[$metric_name])) {
throw new InvalidArgumentException("メトリック '{$metric_name}' が見つかりません");
}
$current_time = microtime(true);
$cutoff_time = $current_time - $time_window;
$recent_data = array_filter($this->metrics[$metric_name], function($record) use ($cutoff_time) {
return $record['timestamp'] > $cutoff_time;
});
if (empty($recent_data)) {
return null;
}
$values = array_column($recent_data, 'value');
return [
'count' => count($values),
'min' => min($values),
'max' => max($values),
'average' => array_sum($values) / count($values),
'latest' => end($values),
'time_window' => $time_window,
'threshold' => $this->thresholds[$metric_name] ?? null,
'is_critical' => isset($this->thresholds[$metric_name]) ?
max($values) > $this->thresholds[$metric_name] : false
];
}
public function getAllMetricsSummary($time_window = 300) {
$summary = [];
foreach (array_keys($this->metrics) as $metric_name) {
try {
$summary[$metric_name] = $this->getMetricSummary($metric_name, $time_window);
} catch (Exception $e) {
$summary[$metric_name] = null;
}
}
return $summary;
}
public function checkAlerts($time_window = 300) {
$alerts = [];
$summary = $this->getAllMetricsSummary($time_window);
foreach ($summary as $metric_name => $data) {
if ($data === null) {
continue;
}
if ($data['is_critical']) {
$alerts[] = [
'metric' => $metric_name,
'current_max' => $data['max'],
'threshold' => $data['threshold'],
'severity' => $this->calculateSeverity($data['max'], $data['threshold']),
'message' => "{$metric_name}が閾値を超えています: {$data['max']} > {$data['threshold']}"
];
}
}
return $alerts;
}
private function calculateSeverity($value, $threshold) {
$ratio = $value / $threshold;
if ($ratio >= 2.0) {
return 'critical';
} elseif ($ratio >= 1.5) {
return 'warning';
} else {
return 'info';
}
}
public function simulateLoad() {
// シミュレーション用のランダムメトリクス生成
$base_time = time();
for ($i = 0; $i < 100; $i++) {
$timestamp = $base_time - (100 - $i) * 10; // 10秒間隔
// レスポンス時間(通常は0.1-0.5秒、時々スパイク)
$response_time = mt_rand(100, 500) / 1000;
if (mt_rand(1, 10) === 1) { // 10%の確率でスパイク
$response_time = mt_rand(1000, 3000) / 1000;
}
$this->recordMetric('response_time', $response_time, $timestamp);
// メモリ使用量(80-150MB)
$memory_usage = mt_rand(80, 150) * 1024 * 1024;
$this->recordMetric('memory_usage', $memory_usage, $timestamp);
// CPU使用率(30-95%)
$cpu_usage = mt_rand(30, 95);
$this->recordMetric('cpu_usage', $cpu_usage, $timestamp);
// ディスク使用率(70-95%)
$disk_usage = mt_rand(70, 95);
$this->recordMetric('disk_usage', $disk_usage, $timestamp);
}
}
public function generateReport() {
$summary = $this->getAllMetricsSummary();
$alerts = $this->checkAlerts();
$report = "=== パフォーマンス監視レポート ===\n";
$report .= "生成日時: " . date('Y-m-d H:i:s') . "\n\n";
foreach ($summary as $metric_name => $data) {
if ($data === null) {
continue;
}
$report .= "【{$metric_name}】\n";
if ($metric_name === 'response_time') {
$report .= sprintf(" 最小: %.3f秒, 最大: %.3f秒, 平均: %.3f秒\n",
$data['min'], $data['max'], $data['average']);
} elseif ($metric_name === 'memory_usage') {
$report .= sprintf(" 最小: %sMB, 最大: %sMB, 平均: %sMB\n",
round($data['min'] / 1024 / 1024, 1),
round($data['max'] / 1024 / 1024, 1),
round($data['average'] / 1024 / 1024, 1));
} else {
$report .= sprintf(" 最小: %.1f%%, 最大: %.1f%%, 平均: %.1f%%\n",
$data['min'], $data['max'], $data['average']);
}
if ($data['threshold'] !== null) {
$status = $data['is_critical'] ? '⚠️ 警告' : '✅ 正常';
$report .= " 閾値: " . $data['threshold'] . " ({$status})\n";
}
$report .= " データ数: {$data['count']}件\n\n";
}
if (!empty($alerts)) {
$report .= "【アラート】\n";
foreach ($alerts as $alert) {
$icon = $alert['severity'] === 'critical' ? '🚨' :
($alert['severity'] === 'warning' ? '⚠️' : 'ℹ️');
$report .= "{$icon} {$alert['message']}\n";
}
} else {
$report .= "【アラート】\n";
$report .= "✅ アラートはありません\n";
}
return $report;
}
}
// 使用例
$monitor = new PerformanceMonitor([
'response_time' => 1.0,
'memory_usage' => 120 * 1024 * 1024, // 120MB
'cpu_usage' => 85.0,
'disk_usage' => 90.0,
]);
echo "=== パフォーマンス監視の例 ===\n";
// シミュレーションデータを生成
$monitor->simulateLoad();
// レポート生成
echo $monitor->generateReport();
4. 価格比較システム
class PriceComparator {
private $products = [];
public function addProduct($id, $name, $prices, $attributes = []) {
$this->products[$id] = [
'name' => $name,
'prices' => $prices,
'attributes' => $attributes,
'min_price' => min($prices),
'max_price' => max($prices),
'price_range' => max($prices) - min($prices),
'average_price' => array_sum($prices) / count($prices),
'store_count' => count($prices)
];
}
public function findBestDeals($max_results = 5) {
if (empty($this->products)) {
return [];
}
// 最安値順でソート
$sorted_products = $this->products;
uasort($sorted_products, function($a, $b) {
return $a['min_price'] <=> $b['min_price'];
});
return array_slice($sorted_products, 0, $max_results, true);
}
public function findHighestPriceRanges($max_results = 5) {
if (empty($this->products)) {
return [];
}
// 価格差が大きい順でソート
$sorted_products = $this->products;
uasort($sorted_products, function($a, $b) {
return $b['price_range'] <=> $a['price_range'];
});
return array_slice($sorted_products, 0, $max_results, true);
}
public function compareProducts($product_ids) {
$comparison = [];
foreach ($product_ids as $id) {
if (!isset($this->products[$id])) {
throw new InvalidArgumentException("商品ID '{$id}' が見つかりません");
}
$product = $this->products[$id];
$comparison[$id] = [
'name' => $product['name'],
'min_price' => $product['min_price'],
'max_price' => $product['max_price'],
'average_price' => round($product['average_price'], 2),
'price_range' => $product['price_range'],
'stores' => array_keys($product['prices']),
'best_store' => array_search($product['min_price'], $product['prices']),
'worst_store' => array_search($product['max_price'], $product['prices'])
];
}
// 比較メトリクスを追加
$min_prices = array_column($comparison, 'min_price');
$max_prices = array_column($comparison, 'max_price');
$overall_stats = [
'cheapest_product' => array_search(min($min_prices), $min_prices),
'most_expensive' => array_search(max($max_prices), $max_prices),
'price_spread' => max($max_prices) - min($min_prices),
];
return [
'products' => $comparison,
'overall' => $overall_stats
];
}
public function findPriceAnomalies($threshold_multiplier = 1.5) {
$anomalies = [];
foreach ($this->products as $id => $product) {
if ($product['store_count'] < 3) {
continue; // データが少ない場合はスキップ
}
$median_price = $this->calculateMedian(array_values($product['prices']));
$anomaly_threshold = $median_price * $threshold_multiplier;
foreach ($product['prices'] as $store => $price) {
if ($price > $anomaly_threshold) {
$anomalies[] = [
'product_id' => $id,
'product_name' => $product['name'],
'store' => $store,
'price' => $price,
'median_price' => $median_price,
'price_ratio' => round($price / $median_price, 2),
'anomaly_type' => 'high_price'
];
}
}
}
// 異常度の高い順でソート
usort($anomalies, function($a, $b) {
return $b['price_ratio'] <=> $a['price_ratio'];
});
return $anomalies;
}
private function calculate