Skip to content

n

公開日:

Download PDF

スライドテキスト

Page 1

n
年塩漬けされていたプロジェクトを
8.2移行するときにやったこと

Migrate a PHP project that has been neglected for several years to 8.2.

pixiv Inc.
USAMI Kenta

2023-11-29

PHP勉強会@東京 #phpstudy

Page 2

お前誰よ

  • うさみけんた (@tadsan) / Zonu.EXE / にゃんだーすわん
  • ピクシブ株式会社 pixiv事業本部 Webエンジニアリングチーム PHPer
    • 2012年末から現職でPHPを書いているWebプログラマ
  • Emacs PHP Modeを開発しています (2017年-)
  • プログラミング言語にちょっとこだわりのある素人 (spcamp2010)

Page 3

さて

Page 4

PHP 8.3リリース
されましたね

🎉

Page 5

Page 6

みなさまが愛した
PHP7.xは全てEOL

Page 7

社内のプロダクトは
全て8.2移行済み

Page 8

その前にPHP
バージョンアップの
定石おさらい

Page 9

開発を止めずにバージョン更新する

  • アプリケーションコード内の非推奨記法をすべて置き換える
  • 依存パッケージを現行と移行先の両方で動作するバージョンにアップデート
  • ユニットテストを現行と移行先バージョンの両方で完動するように
  • CI(ユニットテスト・静的解析)が両バージョンで動作するようにし、
    開発メンバーには互換性を維持して開発してもらう
  • 本番環境をどうにかして新しいバージョンに切り替える

Page 10

というわけで無事に
PHP 8.2化

🎉

Page 11

……こいつ以外は

Page 12

ContestCMS

Page 13

ContestCMS

  • pixivのコンテスト機能のデータを入稿するための仕組み
  • 2013年に内製の独自フレームワークで書かれた
    • 入社1年目のtadsanが開発に参加、紆余曲折あって今まで運用担当
  • 弊社のPHPコードの多くは2013年頃からpixiv.gitという
    モノレポで運用しているが、歴史的経緯により合流せずに存続している
  • アプリケーション: 13,900行、テストコード3,430行の規模

Page 14

塩漬けにしていたのは
ほかならぬ私です

😇

Page 15

10年前のプロジェクト
ここが辛い

Page 16

年季の入った放置プロジェクトあるある

  • E_NOTICEをハンドリングしてない
  • 依存パッケージがめちゃくちゃ古い
  • PHPStanで解析しようにもフレームワークが型のことを
    まるで配慮していないのでひたすら警告が出まくる
    • `@return int|int[]` のような型はついてないのといっしょ!
  • そもそも既存バージョンでもPHPUnitが… 通ってないんだが…

Page 17

嘆いても
仕方ないので直す

Page 18

0. 通らないなりに PHPStanを入れる

Page 19

PHPStanの導入

  • いきなりレベルを高めに上げる必要はまったくない
  • 現状の問題をphpstan-baseline.neonに記録してコミットする
  • コードを弄ったり、依存パッケージのバージョンアップをしたときに
    その都度baselineを再生成して変な差分が出ていないかチェックする

Page 20

1. Syntaxの非互換

Page 21

文字列補間の非互換

  • いままでPHPの文字列に $var という変数を埋め込むときに
    シェル風の "${var}" と "{$var}" が両方使えた
    • PHP 8.2から "${var}" が非互換になったので "{$var}" に置き換える

Page 22

2. インターフェイスの
非互換

Page 23

PHP 8.1の非互換変更 (その1)

  • インターフェイスに型宣言が追加された
    • MySession::gc($max_lifetime) ↓
      SessionHandlerInterface::gc(int $max_lifetime): int|false
    • int|false なんてPHP 8.0にならないと書けないんだが???
    • メソッド定義に #[ReturnTypeWillChange] を付ければ黙ってくれる

Page 24

PHP 8.1の非互換変更 (その2)

  • PDO::query() の非互換

Page 25

PHP 8.1の非互換変更 (その2)

  • PDO::query() の非互換
    • こんなのどうしろという感じだが、トレイト動的定義という荒技で回避可能
    • ……そもそもそんな変なことをしなくても、PDO::prepare()を使えばいい
  • そもそもこの問題自体、PDOを自前拡張してるようなプロジェクト以外には
    刺さらない

Page 26

ここで手間暇かけて 両バージョン対応を
諦める
(社内サービスなので)

Page 27

3. PHPUnitの
バージョンを上げる

Page 28

PHPUnit 9.5

  • PHPUnit 9.5 は ≧PHP 7.3 をサポートしているので、単に上げればヨシ
  • PHP 7.3未満からPHP 8.xまで上げる場合はその方法が使えないので…
    • composer require --dev phpunit/phpunit:'^7.0|^8.0'

Page 29

4. Smartyの アップデート

Page 30

Smarty 3.x → 4.0に移行

  • 社内ではSmarty 2が多く残っているが、今回のプロジェクトは3だった
    • 3.xと4.xは互換が高いのであまり気にせず上げていい
    • Smartyはmodi|erという仕組みで $var|func と書くとプラグインが未 定義だったときに func($var) に置き換えられたが、4.0から非互換に
      • 明示的にmodi|er pluginとして定義してやればいい

Page 31

5.その他パッケージの
アップデート

Page 32

依存パッケージのアップデートなど

  • 依存パッケージに非互換の問題がある場合は手を入れなければいけない
    • cweagans/composer-patches プラグインで
      composer install時に書き換えられる
    • symplify/vendor-patches というツールを使うと
      composer-patches用のパッチを簡単に作れるのでおすすめ
  • composer-patchesは本番運用向けに必要なので、うっかり
    composer require --devしないように注意

Page 33

5. E_WARNINGを
潰す

Page 34

E_WARNING

  • いままで未定義変数アクセスや未定義配列アクセスはE_NOTICEだったが、
    PHP 8からE_WARNINGに昇格した
  • カバレッジが十分なプロジェクトならテストを通せば十分なはずだが…
  • レガシープロジェクトにそんなものはない
    • 地道に動かして動作チェックする…

Page 35

エラーハンドラのカスタマイズ

  • 未定義アクセスエラーは雨後の筍のように出てくる
  • エラーハンドリングはset_error_handler()でカスタマイズできる
  • E_WARNINGは無視しつつエラーログに流すようなコードを書けばいい
    • 流量などはいろいろな方法で調整しつつ、最終的には全量流す
  • エラーログに簡単にアクセスできるようにする or 無駄なものを だらだらとエラーログに流し続けない(フィルタできるようにする)

Page 36

まとめ

Page 37

PHP7.xからの移行は 最後は地道なもぐら叩き

Page 38

本番環境も活用しながら
完全は求めず
安全に移行しような