$30 off During Our Annual Pro Sale. View Details »

PHP標準関数との戦い/Fighting with PHP standard function

Y-KANOH
August 05, 2020

PHP標準関数との戦い/Fighting with PHP standard function

PHPを使っているサービスにとって、避けられない問題がPHPのバージョンアップです。
歴史が古いレガシーなシステムであればあるほど、バージョンアップ時の仕様変更に対応することが難しくなっていきます。
サービス開始から20年近く経っているメールディーラーの歴史もPHP バージョンアップとの格闘の連続でした。
今回は、そういった闘いの一例として PHPの標準関数の仕様変更対応をご紹介します。
何の変哲もなさそうな仕様変更の裏で行われた失敗と改善の記録をお楽しみください。

Y-KANOH

August 05, 2020
Tweet

More Decks by Y-KANOH

Other Decks in Programming

Transcript

  1. #RAKUSMeetup
    ©2020 RAKUS Co., Ltd.
    PHP標準関数との戦い
    加納 悠史

    View Slide

  2. #RAKUSMeetup
    加納 悠史
    カノウユウジ
    PHPer 趣味はダイビング
    業務内容:メールディーラー の開発
    @Ykanoh65
    株式会社 ラクス

    View Slide

  3. #RAKUSMeetup
    メールディーラーとは
    問合せメールを一元管理!
    顧客対応サポートツール

    View Slide

  4. #RAKUSMeetup
    メールディーラー
    ● 2001年にサービス提供開始
    ● 6,000社以上の導入実績
    ● メール/Webマーケティング市場 11年連続シェア No.1 !!※
    ※ 出典:ITR「ITR Market View:メール/Webマーケティング市場 2020」メール処理市場ベンダー別 売上金額シェア 2009-2019年度(予測値)

    View Slide

  5. #RAKUSMeetup
    メールディーラー
    ● 2001年にサービス提供開始
    ● 6,000社以上の導入実績
    ● メール/Webマーケティング市場 11年連続シェア No.1 !!
    ⇒ 19年モノ の レガシーシステム

    View Slide

  6. #RAKUSMeetup
    標準関数との戦い

    View Slide

  7. #RAKUSMeetup
    count() 関数

    View Slide

  8. #RAKUSMeetup
    ことの始まり
    PHP バージョンアップ
    ● v7.1 → v7.3
    ● count() 関数の仕様変更を発見

    View Slide

  9. #RAKUSMeetup
    count()関数の仕様変更
    PHP 7.2 以降の挙動
     → カウントできない型を引数にすると Warning が発生
    print( count(1) ); // 出力:1
    print( count("hoge") ); // 出力:1
    print( count(null) ); // 出力:0
    Warning: count(): Parameter must be an array or an object that implements Countable in %s on line %d
    code
    php.log

    View Slide

  10. #RAKUSMeetup
    対応案
    すべての count() 関数に対して...
    A. カウント可能な値のみ渡すよう修正
    - そもそも NULL や int をカウントしている方がおかしいでしょ
    B. カウントできない値も許容するオリジナル関数で置換え
    - 全 count() 関数なんて見てられない
    - オリジナル関数で NULL や int は 0 や 1 を返せばOK

    View Slide

  11. #RAKUSMeetup
    count() の数を確認

    View Slide

  12. #RAKUSMeetup
    2,232 個所

    View Slide

  13. #RAKUSMeetup
    対応案
    A. カウント可能な値のみ渡すよう修正
    - そもそも NULL や int をカウントしている方がおかしいでしょ
    B. カウントできない値も許容するオリジナル関数で置換え
    - 全 count() 関数なんて見てられない。 それが現実。
    - ラッパー関数にて NULL や int は 0 や 1 を返せばOK
    無理...
    すべての count() 関数に対して...
    採用

    View Slide

  14. #RAKUSMeetup
    採用した対策案
    ● 自作の関数 mdCount() を作成
    ○ 共通関数として 共通関数ファイルに定義
    ● すべての count() 関数を置き換え
    一件落着...

    View Slide

  15. #RAKUSMeetup
    対応完了後の コードレビューにて...
    あれ...?

    View Slide

  16. #RAKUSMeetup
    第一ノ問題
    消したはずの count() 関数が増殖している

    View Slide

  17. #RAKUSMeetup
    なぜ復活していたのか?
    ● 並行開発されていたブランチからの混入
    ● ルール周知不足による使用
    ⇒ 使わせない仕組みがなければ増殖する

    View Slide

  18. #RAKUSMeetup
    対策
    プロセスによる対策
    ● コーディング規約に追加
    ● コードレビュー観点に追加
    技術的な対策
    ● 自動検出(検討中)

    View Slide

  19. #RAKUSMeetup
    第二ノ問題
    オリジナル関数を
    使えない処理の存在が発覚

    View Slide

  20. #RAKUSMeetup
    おさらい:採用した対策案
    ● 自作の関数 mdCount() を作成
    ○ 共通関数として 共通関数ファイルに定義
    ● すべての count() 関数を置き換え

    View Slide

  21. #RAKUSMeetup
    おさらい:採用した対策案
    ● 自作の関数 mdCount() を作成
    ○ 共通関数として 共通関数ファイルに定義
    ● すべての count() 関数を置き換え

    View Slide

  22. #RAKUSMeetup
    おさらい:採用した対策案
    ● 自作の関数 mdCount() を作成
    ○ 共通関数として 共通関数ファイルに定義
    ● すべての count() 関数を置き換え
    問題の処理では
    共通関数ファイル を読み込んでいなかった!

    View Slide

  23. #RAKUSMeetup
    共通関数を読み込んでいない...
    メールディーラーの構成
    ● オートロードは利用していない
    ○ クラス化されていないため利用不可
    ● 共通関数ファイルは 処理の先頭で読み込むルール
    ○ 例:
    ○ 不用意に読み込みファイルを増やすと危険
    require_once "common.php";
    関数名の競合など...

    View Slide

  24. #RAKUSMeetup
    対応策
    新しい共通関数ファイルを作成
    ⇒ すべての処理で読み込むようにする
    1. mdCount2()(仮称)
    を作成
    2. 旧 mdCount() は mdCount2() を呼び出すよう変更

    View Slide

  25. #RAKUSMeetup
    対応策
    新しい共通関数ファイルを作成
    ⇒ すべての処理で読み込むようにする
    1. mdCount2()(仮称)
    を作成
    2. 旧 mdCount() は mdCount2() を呼び出すよう変更

    View Slide

  26. #RAKUSMeetup
    対策:auto_prepend_fileを活用
    auto_prepend_fileとは
    ● php.ini の設定値
    ● メイン処理の前に実行するファイルを指定
    → コードの修正を少なく前処理を定義することが可能
    https://www.php.net/manual/ja/ini.core.php#ini.auto-prepend-file

    View Slide

  27. #RAKUSMeetup
    対策:auto_prepend_fileを活用
    1. mdCount2()(仮称)
    を作成 → auto_prepend_file で読み込み
    2. 旧 mdCount() はどうする?
      → mdCount2()(仮称)
    を呼び出すよう変更
    修正コストを抑えつつ対応することに成功

    View Slide

  28. #RAKUSMeetup
    まとめ / ふりかえり
    対応が完了しても安心してはダメ!
    今回の対応策
    ● count() の仕様変更 ⇒ オリジナル共通関数で対応
    ● count() が増殖 ⇒ 規約とコードレビューで防止
    ● オリジナル関数が呼べない ⇒ 新しい共通関数ファイルを作成
    ● 新共通関数ファイル呼び出し ⇒ auto_prepend_file の活用で対応
    今後の対応
    ● 増殖防止策:ツールによる自動検知を導入
    ● 共通関数ファイルの mdCount() の廃止
    再発の芽を完全に摘む為に、
    count()との闘いはもう少しだけ続く・・・・

    View Slide