freeeの開発情報ポータルサイト

こんにちは!PHPの布教に来ました!

この記事は、freee Developers Advent Calendar 2025 の 3日目の記事です。

はじめに

はじめまして! フリーナンス のエンジニアをしている、the よしだです!

皆さんPHPはご存知ですか? フリーではRubyやGoを多く取り扱っていますが、最近フリーにジョインしたフリーナンスではPHPとGoを使用しています。

私自身、PHPカンファレンスのスタッフをするほどPHPを愛しており、フリーであえてPHPの魅力を啓蒙することが私のミッションだと考えています! 本日はそんな話になります!

PHPは1994年に生まれたプログラミング言語で、Web開発の普及と加速に大きく貢献しました。 学習コストの低さや、WordPressなど主要なCMSでの採用、豊富な情報とフレームワークの存在が特徴です。 PHPにより、多くの開発者がWebアプリケーション開発に容易に参入できるようになりました!

しかし、普及が進む中で、しばしば指摘されてきた点があります。 それは「PHPは遅い」というものです。 かつてはそのような側面もあったかもしれませんが、PHPは進化を続けており、特に近年のバージョンアップでは目覚ましいパフォーマンス改善を遂げています。

この記事では、PHPが実際にどれほど速くなっているのか、バージョンごとに速度を測定し、その進化の軌跡を明らかにします。 最新のPHPがもたらすパフォーマンスの恩恵を一緒に確認し、「PHPは遅い」というイメージを塗り替えましょう!

速度測定の前提条件

速度測定をする上での前提条件を定義します。

PHPのバージョン

測定に使うPHPのバージョンは下記の通りです。

  • 5.6
  • 7.4
  • 8.2
  • 8.4

OPcacheとJIT

PHPのパフォーマンス向上に欠かせない重要な技術である「OPcache(オーピーキャッシュ)」と「JIT(ジャストインタイム)コンパイラ」について簡単に解説させてください。

OPcache(PHP 5.5以降)

PHPのスクリプトは、実行されるたびにコンピュータが理解できる形式(オペコード)に変換されます。OPcacheは、この変換後のオペコードをメモリ上に保存し、次回以降の実行時には変換作業をスキップすることで、処理速度を大幅に向上させます。

JIT(PHP 8.0以降)

OPcacheで生成されたオペコードを、さらに踏み込んでネイティブマシンコード(コンピュータが直接実行できるコード)に変換し、実行します。これにより、処理の実行速度そのものがさらに高速化されます。

OPcacheとJITの利用

今回の測定では、PHPバージョンそのものの「地力」の違いを純粋に比較したいと考えています。

そのため、先述したOPcacheやJITはオフにした状態で測定を行います。

これにより、これらの高速化機能の恩恵を受けない、基本的なPHPエンジンの性能差を確認することができます。

測定方法

測定は、各PHPバージョンの実行環境(コンテナ)でベンチマークスクリプトを実行します。 ベンチマークスクリプトの作成はroo codeにお願いしました。

ベンチマーク項目
テスト項目 測定の焦点
算術演算 (加算・乗算) 基本的な数値演算のパフォーマンス
文字列連結 文字列の連結操作の効率性(メモリ再割り当て)
文字列操作 文字列検索・置換関数のパフォーマンス
配列操作 配列の追加、集計、フィルタリング処理
連想配列アクセス ハッシュテーブルのアクセス速度
関数呼び出し 関数呼び出しのオーバーヘッド
オブジェクト生成・メソッド呼び出し オブジェクト指向のパフォーマンス
正規表現マッチング 正規表現エンジンの処理速度
JSON エンコード/デコード JSONシリアライゼーションの効率
ファイル操作(メモリストリーム) ファイルI/O操作(メモリ上)
ハッシュ計算 (MD5) 暗号化ハッシュ関数の速度
配列ソート ソートアルゴリズムのパフォーマンス
再帰関数 (フィボナッチ n=20) 再帰呼び出しのオーバーヘッド
型変換 型キャストの処理速度
条件分岐 if-elseif-else分岐の効率性
ベンチマークスクリプト

ベンチマークスクリプト (クリックで切り替え)

<?php
/**
 * ベンチマーク結果をJSON形式で返すAPI
 */

header('Content-Type: application/json');

// メモリ使用量の初期値を記録
$initialMemory = memory_get_usage();

// ベンチマーク結果を格納する配列
$results = array();

/**
 * ベンチマーク実行関数
 */
function runBenchmark($name, $callback, $iterations = 100000) {
    // ガベージコレクション(PHP 5.3+)
    if (function_exists('gc_collect_cycles')) {
        gc_collect_cycles();
    }
    
    $startTime = microtime(true);
    $startMemory = memory_get_usage();
    
    $callback($iterations);
    
    $endTime = microtime(true);
    $endMemory = memory_get_usage();
    
    return array(
        'name' => $name,
        'time' => ($endTime - $startTime) * 1000, // ミリ秒
        'memory' => $endMemory - $startMemory,
        'iterations' => $iterations
    );
}

// ===== ベンチマークテスト =====

// 1. 算術演算
$results[] = runBenchmark('算術演算 (加算・乗算)', function($n) {
    $result = 0;
    for ($i = 0; $i < $n; $i++) {
        $result = $i + $i * 2;
    }
}, 1000000);

// 2. 文字列連結
$results[] = runBenchmark('文字列連結', function($n) {
    $str = '';
    for ($i = 0; $i < $n; $i++) {
        $str .= 'a';
    }
}, 10000);

// 3. 文字列操作(検索・置換)
$results[] = runBenchmark('文字列操作', function($n) {
    $text = str_repeat('Hello World! ', 100);
    for ($i = 0; $i < $n; $i++) {
        $result = str_replace('World', 'PHP', $text);
        $pos = strpos($result, 'PHP');
    }
}, 10000);

// 4. 配列操作
$results[] = runBenchmark('配列操作', function($n) {
    $arr = array();
    for ($i = 0; $i < $n; $i++) {
        $arr[] = $i;
    }
    $sum = array_sum($arr);
    $filtered = array_filter($arr, function($val) {
        return $val % 2 === 0;
    });
}, 10000);

// 5. 連想配列アクセス
$results[] = runBenchmark('連想配列アクセス', function($n) {
    $data = array();
    for ($i = 0; $i < 1000; $i++) {
        $data['key_' . $i] = $i;
    }
    for ($i = 0; $i < $n; $i++) {
        $val = isset($data['key_500']) ? $data['key_500'] : null;
    }
}, 100000);

// 6. 関数呼び出し
function testFunction($a, $b, $c) {
    return $a + $b + $c;
}

$results[] = runBenchmark('関数呼び出し', function($n) {
    for ($i = 0; $i < $n; $i++) {
        testFunction($i, $i + 1, $i + 2);
    }
}, 100000);

// 7. オブジェクト生成
class TestClass {
    public $prop1;
    public $prop2;
    public $prop3;
    
    public function __construct($a, $b, $c) {
        $this->prop1 = $a;
        $this->prop2 = $b;
        $this->prop3 = $c;
    }
    
    public function calculate() {
        return $this->prop1 + $this->prop2 + $this->prop3;
    }
}

$results[] = runBenchmark('オブジェクト生成・メソッド呼び出し', function($n) {
    for ($i = 0; $i < $n; $i++) {
        $obj = new TestClass($i, $i + 1, $i + 2);
        $result = $obj->calculate();
    }
}, 10000);

// 8. 正規表現
$results[] = runBenchmark('正規表現マッチング', function($n) {
    $text = 'test@example.com, user@domain.co.jp, admin@site.org';
    for ($i = 0; $i < $n; $i++) {
        preg_match_all('/[\w\.-]+@[\w\.-]+\.\w+/', $text, $matches);
    }
}, 10000);

// 9. JSON エンコード/デコード
$results[] = runBenchmark('JSON エンコード/デコード', function($n) {
    $data = array(
        'name' => 'Test User',
        'email' => 'test@example.com',
        'age' => 30,
        'items' => array('item1', 'item2', 'item3')
    );
    for ($i = 0; $i < $n; $i++) {
        $json = json_encode($data);
        $decoded = json_decode($json, true);
    }
}, 10000);

// 10. ファイル操作(メモリ上)
$results[] = runBenchmark('ファイル操作(メモリストリーム)', function($n) {
    for ($i = 0; $i < $n; $i++) {
        $fp = fopen('php://memory', 'r+');
        fwrite($fp, 'Test data ' . $i);
        rewind($fp);
        $content = fread($fp, 1024);
        fclose($fp);
    }
}, 1000);

// 11. ハッシュ計算
$results[] = runBenchmark('ハッシュ計算 (MD5)', function($n) {
    $data = 'This is a test string for hashing';
    for ($i = 0; $i < $n; $i++) {
        $hash = md5($data . $i);
    }
}, 10000);

// 12. 配列ソート
$results[] = runBenchmark('配列ソート', function($n) {
    for ($i = 0; $i < $n; $i++) {
        $arr = array();
        for ($j = 0; $j < 100; $j++) {
            $arr[] = rand(1, 1000);
        }
        sort($arr);
    }
}, 1000);

// 13. 再帰関数(フィボナッチ)
function fibonacci($n) {
    if ($n <= 1) return $n;
    return fibonacci($n - 1) + fibonacci($n - 2);
}

$results[] = runBenchmark('再帰関数 (フィボナッチ n=20)', function($n) {
    for ($i = 0; $i < $n; $i++) {
        fibonacci(20);
    }
}, 100);

// 14. 型変換
$results[] = runBenchmark('型変換', function($n) {
    for ($i = 0; $i < $n; $i++) {
        $str = (string)$i;
        $int = (int)$str;
        $float = (float)$int;
        $bool = (bool)$float;
    }
}, 100000);

// 15. 条件分岐
$results[] = runBenchmark('条件分岐', function($n) {
    $result = 0;
    for ($i = 0; $i < $n; $i++) {
        if ($i % 3 === 0) {
            $result += 1;
        } elseif ($i % 3 === 1) {
            $result += 2;
        } else {
            $result += 3;
        }
    }
}, 100000);

// ===== 結果の計算 =====

$totalTime = 0;
$totalMemory = 0;

foreach ($results as $result) {
    $totalTime += $result['time'];
    $totalMemory += $result['memory'];
}

// JSON出力
$output = array(
    'php_version' => phpversion(),
    'timestamp' => date('Y-m-d H:i:s'),
    'summary' => array(
        'total_time' => $totalTime,
        'total_memory' => $totalMemory,
        'peak_memory' => memory_get_peak_usage(),
        'test_count' => count($results)
    ),
    'results' => $results
);

echo json_encode($output, JSON_PRETTY_PRINT);

測定結果

それでは測定結果を発表します!

テスト項目 反復回数 PHP 5.6 (ms) PHP 7.4 (ms) PHP 8.2 (ms) PHP 8.4 (ms)
合計実行時間 190.90 89.99 80.10 79.53
--- --- --- --- --- ---
算術演算 (加算・乗算) 1,000,000 回 23.939 12.569 12.163 11.452
文字列連結 10,000 回 0.239 0.164 0.171 0.152
文字列操作 10,000 回 14.190 35.016 23.651 24.121
配列操作 10,000 回 1.848 0.364 0.559 0.420
連想配列アクセス 100,000 回 3.923 1.586 1.764 1.635
関数呼び出し 100,000 回 9.040 1.447 2.044 1.455
オブジェクト生成・メソッド呼び出し 10,000 回 2.443 0.591 0.798 0.664
正規表現マッチング 10,000 回 5.099 1.658 1.888 1.686
JSON エンコード/デコード 10,000 回 11.593 6.080 6.110 6.803
ファイル操作(メモリストリーム) 1,000 回 0.406 0.182 0.231 0.237
ハッシュ計算 (MD5) 10,000 回 2.072 1.614 1.639 1.941
配列ソート 1,000 回 11.906 6.095 6.398 6.518
再帰関数 (フィボナッチ n=20) 100 回 94.669 18.731 18.787 18.676
型変換 100,000 回 7.082 2.801 2.852 2.789
条件分岐 100,000 回 2.447 1.090 1.045 0.978

測定結果で一番大きな動きを見せたのが、PHP 5.6から7.4への移行です。 ここでは、PHPの実行エンジンが大幅に改善され、内部の構造が最適化されました。

また、今回の測定では、PHP 8系の最大の武器である「JITコンパイラ」をオフにしました。 そのため、PHP7.4との比較に大きな差がでなかったとも言えます。

しかし、8.4は7.4に比べてわずかですが確実に速くなっており、これは新機能を追加しながらもコアな「地力」を着実に高めている証拠です。

まとめ

「PHPは遅い」という時代は、PHP 7.4の登場で完全に過去のものとなりました。

現在のPHPは、7.4で得た革新的なスピードを土台に、8.x系で安定性とさらなる効率を追い求めている、非常にモダンで高性能な言語に進化したと言えます。

皆さんもPHPを使ってみませんか?

明日は、岡崎さんから、Vector についての記事が公開されます。 Vector の知見がないので、非常に楽しみにしています!