1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PHPのバージョンアップで涙した話(5.6→7.4)

Posted at

目次

  • 経緯
  • PHPのバージョンについて
  • 苦行を紹介
    • Case1.ここはどこ?私は誰?
    • Case2.地雷原をゆく
  • どのように修正していったか

経緯

2023年末、LinuxサーバーリプレースPJが発足。
デフォルトでインストールされているPHPのバージョンが上がるため、プログラムを更新しなければならなくなりました。
このバージョンアップPJを取り仕切ることになった若手PGの奮闘が誰かの参考になれば幸いです。

※コンテナ使えばよかったじゃん ← 野暮なのでなしです
※今更PHP7の話されても ← 野暮なのでなしです

PHPのバージョンについて

公式こちらのサイトを参考に、対象バージョンのみをまとめました。

Version 初回リリース日 アクティブサポート期限 セキュリティサポート期限
7.4 2019年11月28日 2021年11月28日 2022年11月28日
5.6 2014年8月28日 2017年12月31日 2018年12月31日

アップ先のPHP7.4も切れてます。笑
PHP6系は開発段階で凍結されたため、5系の次が7系となります。
実質1バージョンのアップでした。

苦行を紹介

変更点の中で特に辛かった2点を紹介します。

Case1.ここはどこ?私は誰?

PHP5.6で書かれた以下のコードがあります。

class Main {
    function run() {
        $this->callDynamic();
        $this->callStatic();
    }
    function callDynamic() {
        $helper = new Helper();
        $helper->say();
    }
    function callStatic() {
        Helper::say();
    }
}

class Helper {
    function say() {
        echo "My name is " . get_class($this);
    }
}

この時、出力は以下のようになります。

// -> callDynamicとcallStaticが順番に呼ばれる
$main = new Main()
$main->run()

// My name is Helper
// My name is Main

ここで注目したいのは、callStatic内でHelper::say()として呼び出された際の$thisコンテキストがMainとなっていることです。

PHP5系では、静的呼び出しをすることで呼び出し元の$thisコンテキストを引き継ぐ挙動をとります。

「Helperのsayはstatic修飾子がついていないのに、静的呼び出ししていいの?」という疑問が湧きますが、PHP5系ではエラーになりません。(ただし非推奨)

私の経験したプロジェクトではこの「非staticな関数を静的呼び出しして$thisを引き継ぐ」という方法が乱用されていました。

つまり...

class Helper {
    function say() {
        Helper2::say();
    }
}
class Helper2 {
    function say() {
        Helper3::say();
    }
}

呼び出し元の this コンテキストを引き継ぐので、
Helper がさらに Helper2 を静的呼び出し、さらにそこから...
(果てしなく長い旅)
あれっ、僕は誰...?

Case2.地雷原をゆく

Case1で判明した「PHP5系のダブルコロン呼び出しは$thisコンテキストを引き継ぐ」という挙動が、別の問題を生み出しているので紹介します。

先ほどのコードを少し書き換えてみます。
StaticHelperクラスのsayにstatic修飾子をつけました。

class StaticHelper {
    static function say() {
        echo "My name is " . get_class($this);
    }
}

...ん?

staticの中に$this!?
しかも動くの!?

PHP7系からFatalErrorになりました。
もう一点、$thisコンテキストについて仕様変更がありました。

class Main {
    function run() {
        StaticHelper::say();
    }
}

class StaticHelper {
    // ダブルコロン呼び出しを想定していない関数
    function say() {
        echo "My name is " . get_class($this); // Fatal error!
    }
}

このように非staticな関数を静的呼び出ししたとき、PHP5系まではCase1の通り呼び出し元の$thisコンテキストを引き継いていましたが、PHP7系ではFatalErrorになります。

いかなる関数でも、静的呼び出しをすることで$thisコンテキストを失うようになりました。

「非staticな関数の静的呼び出し」の蔓延していた当プロジェクトでシステムエラー祭りになったことは想像に難くないでしょう。

どのように修正していったか

ローラー作戦をやりました。
同じ経験をする方がいらっしゃるか分かりませんが、誰かの参考になれば幸いです。


手順

「インスタンスメソッドの静的呼び出し」を全て洗い出し

  • PHP の Lint 様が Deprecated として青波線で出してくれた(幸)
  • 出さない部分もあったため「::」で検索して目検品 → 法則性を見つける

対象となるメソッドを全行確認

  • 対象メソッドが呼び出した先のメソッドは$thisを参照する?していない?

都度判断・改修

  • static をつける?
  • $this をバケツリレーする?
  • ロジックの構造を変える? etc...

「非staticな関数の静的呼び出し」を使った途端、$thisコンテキストが失われるので、依存先の関数で$thisを参照する記述がないか、ローラーで確認します。

今だとAIがありますので、このような機械的な作業は格段にやりやすくなったでしょう。これを手動でやる労力より、会社にClaudeCodeの稟議を通す労力を費やした方が圧倒的にいいです。本当に。


以上になります。
かなり大変だったことを思い出したので誰かに見てもらいたく、記事にしました。
読みづらい部分も多かったかと思いますが、ここまで読んでくださりありがとうございました!!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?