Slide 1

Slide 1 text

PHP 9 に備えよ 動的プロパティ、どうすればいぃ? 2024.05.11 PHP Conference Kagawa 2024 荒瀬 泰輔 #phpconkagawa #sotetsu

Slide 2

Slide 2 text

Agenda 自己紹介 動的プロパティが非推奨になった話 動的プロパティは静的解析で検知できるのか? 動的プロパティをどう修正していくか -ウチらの場合- まとめ (おまけ)PHP 9 で例外になる愉快な仲間たち 2

Slide 3

Slide 3 text

荒瀬 泰輔 @at_taisuke 徳島出身なんじょ〜 PHPer歴5年くらい サイボウズかき氷部の部長 2024月刊ぺちこん参加 北海道 東京 香川 福岡 3

Slide 4

Slide 4 text

動的プロパティが非推奨になった話 4

Slide 5

Slide 5 text

動的プロパティ Dynamic Property とは PHP ではクラスで宣言されていない未定義のプロパティに値を代入した場 合、エラーは発生せず動的にプロパティが生成されます class User {} $user = new User(); $user->name = 'udon'; var_dump($user->name); // 'udon' 動的プロパティが非推奨になった話 5

Slide 6

Slide 6 text

動的型付け言語に多く見られる // js class User {} const user = new User(); user.name = "udon"; // 新しいプロパティを追加 # python class User: pass user = User() user.name = "udon" # 新しい属性を追加 動的プロパティが非推奨になった話 6

Slide 7

Slide 7 text

他の言語では宣言が必要な場合が多い // Java public class User { // プロパティは事前に定義する必要がある private String name; } // swift class User { // プロパティは事前に定義する必要がある var name: String } 動的プロパティが非推奨になった話 7

Slide 8

Slide 8 text

ゆるくて楽ちんだなぁ 大変だね静的型付け言語は 動的プロパティが非推奨になった話 8

Slide 9

Slide 9 text

○してやるぞ動的プロパティ Nikita 氏によって動的プロパティを禁止にしようというRFCが2021年に 提出されました https://wiki.php.net/rfc/deprecate_dynamic_properties 賛成52、反対25で可決されました 動的プロパティが非推奨になった話 9

Slide 10

Slide 10 text

class User { public $name; } $user = new User; // Assigns declared property User::$name. $user->name = "foo"; // Oops, a typo: $user->nane = "foo"; // PHP <= 8.1: Silently creates dynamic $user->nane property. // PHP 8.2: Raises deprecation warning, still creates dynamic property. // PHP 9.0: Throws Error exception. PHP 8.2 から非推奨になり、PHP 9.0 からは例外が投げられるようにな ります 動的プロパティが非推奨になった話 https://wiki.php.net/rfc/deprecate_dynamic_properties より 10

Slide 11

Slide 11 text

レガシープロダクト勢(クソデカ主語) 「やべぇ」 動的プロパティが非推奨になった話 11

Slide 12

Slide 12 text

対処方法は? 動的プロパティを使わないことが推奨される(それはそう) 次点でクラスに #[\AllowDynamicProperties] を付加すること マニュアルには他の対処法も載ってます https://www.php.net/manual/ja/migration82.deprecated.php PHP 8 から導入された WeakMap を使うこと stdClass クラスを使用、継承して動的なプロパティを使うこと 動的プロパティが非推奨になった話 12

Slide 13

Slide 13 text

やるしかねぇ! PHP 9 に備えなければ! 動的プロパティが非推奨になった話 13

Slide 14

Slide 14 text

まずは静的解析だ! 動的プロパティが非推奨になった話 14

Slide 15

Slide 15 text

動的プロパティは静的解析で検知できるの か? 15

Slide 16

Slide 16 text

結論 大抵の場合は PHPStan を使えば検出できそうです ただし、コードが複雑なプロダクトの場合は PHPStan だけでは拾えない パターンもあったので、Phan や PhpStorm などの他の解析ツールも併用 すると良さそうです つまり静的解析も完璧ではなさそうなので、検知できなくて実際に動かし てみたら動的プロパティだった、みたいなこともありうると思います 動的プロパティは静的解析で検知できるのか 16

Slide 17

Slide 17 text

静的解析ツール何を使うか PHPStan Phan PhpStorm 動的プロパティは静的解析で検知できるのか 17

Slide 18

Slide 18 text

PHPStan での動的プロパティの検出1 Access to an undefined property として検出できます class Foo {} $foo = new Foo(); $foo->test = 'oh my'; // Access to an undefined property Foo::$test. echo $foo->test; ただしコードによっては動的プロパティ以前の問題として検出されてしま うので注意が必要です 動的プロパティは静的解析で検知できるのか https://phpstan.org/blog/phpstan-is-ready-for-php-8-2 より 18

Slide 19

Slide 19 text

PHPStan での動的プロパティの検出2 明示的に mixed にしてしまっている場合 class User {} function getUser(): mixed { return new User(); } $user = getUser(); $user->name = 'udon'; // Cannot access property $name on mixed. 動的プロパティは静的解析で検知できるのか 19

Slide 20

Slide 20 text

PHPStan での動的プロパティの検出3 プロパティを持たない型が入る可能性がある場合 class User {} function getUser(bool $isUser = true): User|string { return $isUser ? new User() : 'string'; } $user = getUser(); $user->name = 'udon'; // Cannot access property $name on string|User. 動的プロパティは静的解析で検知できるのか 20

Slide 21

Slide 21 text

PHPStan での動的プロパティの検出4 引数の型が不明な場合 function setUserName($user): string { return $user->name = 'udon'; } // Function setUserName() has parameter $user with no type specified. 動的プロパティは静的解析で検知できるのか 21

Slide 22

Slide 22 text

Phan での動的プロパティの検出1 PhanUndeclaredProperty として検出できます Phan は現在CIに組み込んでいるので使ってみるか〜くらいの感じだった のですが、 PHPStan(Lv.9)では検出できていないものや、PhpStorm でなぜかエラ ーとして出ていないけど動的プロパティが使われる可能性があるような箇 所が検出できていました やるやん 動的プロパティは静的解析で検知できるのか 22

Slide 23

Slide 23 text

Phan での動的プロパティの検出2 少し詳しくみてみたところ、Phan だけで検出できたものには以下のものがありました PHPStan では Access to an undefined property ではなく別のエラーとして検 出されているものを拾っていた PhpStorm でなぜか検知できていない型があいまいな変数に対しても検知ができてい た これに関してはもしかしたら PhpStorm が賢すぎるのかも? 動的プロパティは静的解析で検知できるのか 23

Slide 24

Slide 24 text

Phan での動的プロパティの検出3 親クラスでは定義されていないけど子クラスでは定義されている、みたいないわゆるポリ モーフィックなコードも検知していた class ParentClass {} class ChildClass extends ParentClass { public $property = 1; } function getProperty(ParentClass $a) { echo $a->property; } getProperty(new ChildClass()); //will work, since $property is defined in Child getProperty(new ParentClass()); //won't work, since $property is not defined in Parent 動的プロパティは静的解析で検知できるのか https://pleiades.io/help/phpstorm/php-possible-polymorphic-call.html より 24

Slide 25

Slide 25 text

PhpStorm での動的プロパティの検出 コードインスペクション機能で動的プロパティを検出できます ただし全部やるとメモリをめっちゃ食うのと、 検出すべきところで検出できてないみたいなことが起きたので、 検索対象ファイルを絞る、検出する警告を選ぶなどして、 これから詳しく使い方を模索していきます 動的プロパティは静的解析で検知できるのか 25

Slide 26

Slide 26 text

動的プロパティをどう修正していくか -ウチらの場合- 26

Slide 27

Slide 27 text

ウチらについて サイボウズで Garoon というグループウェアの開発をやっています PHP 4 の時代から20年以上続くレガシー由緒正しいプロダクトです♡ PHP 8.0 のときの比較演算子の挙動変更でもとっても苦労しました 興味ある方はこちらの発表をご覧ください 20年ものの巨大レガシープロダクトをPHP 8.0にアップデートした 際の対策と得られた知見 by 赤間 仁志 https://fortee.jp/phpcon-2022/proposal/8f29f20e-1275- 49eb-89c0-fe684e28d110 動的プロパティをどう修正していくか -ウチらの場合- 27

Slide 28

Slide 28 text

動的プロパティ撲滅計画 PHP 8.2 リリースまでにやること(もうやった) 静的解析でどれくらい動的プロパティが使われているのか観測する エラーハンドラで動的プロパティの E_DEPRECATED をキャッチして握りつぶす コーディング規約の修正 & composer ライブラリのアップデート PHP 9.0 リリースまでにやること(これからやる) 静的解析の結果からわかった動的プロパティの出現箇所をひとつずつ修正する エラーハンドラでキャッチした動的プロパティをログに出し、修正する 動的プロパティをどう修正していくか -ウチらの場合- 28

Slide 29

Slide 29 text

静的解析してみた結果 めっちゃ動的プロパティ使ってました PHPStan の Access to an undefined property だけで 500件くらい Phan の PhanUndeclaredProperty も 500件くらい 重複もあるけど片方しか検知していない箇所もある これ全部を PHP 8.2 リリースまでに修正はできない、と判断し、 PHP 8.2 リリースを優先してリリース後に修正する方針にしました 無理しなくてえらい 動的プロパティをどう修正していくか -ウチらの場合- 29

Slide 30

Slide 30 text

エラーハンドラで E_DEPRECATED をキャ ッチ Garoon では独自のフレームワークを使っているので、 E_DEPRECATED も そのままだと画面に出てしまう仕様になっています なのでエラーハンドラ部分を修正し、動的プロパティの E_DEPRECATED をキャッチしてそのまま return することで握りつぶすことにしました 動的プロパティをどう修正していくか -ウチらの場合- 30

Slide 31

Slide 31 text

コーディング規約の修正 PhpStorm の言語レベルを 8.2 にしておけば叱ってくれますが、念の為社 内のコーディング規約も修正して全体にお知らせしておきました composer ライブラリのアップデート 使用しているライブラリの現行バージョンが PHP 8.2 に対応しているか調 査 対応していない場合はライブラリをバージョンアップ 一部対応しているかわからなかったライブラリについては個別調査 動的プロパティをどう修正していくか -ウチらの場合- 31

Slide 32

Slide 32 text

ここまでは終わっていて… Garoon、 PHP 8.2 で動きました! 2024年6月リリース予定! 動的プロパティをどう修正していくか -ウチらの場合- 32

Slide 33

Slide 33 text

ここから今後の話 PHP 9 に向けてやること 動的プロパティをどう修正していくか -ウチらの場合- 33

Slide 34

Slide 34 text

静的解析で見えている箇所をひとつずつ修正 PHPStan, Phan, PhpStorm など、使える静的解析ツールを使って 動的プロパティの出現箇所を観測 直せるところから直していく クラスにプロパティを宣言する なるべく技術的負債にならないように本当に動的プロパティを生 やすべきなのか見ていく テストが大変… 動的プロパティをどう修正していくか -ウチらの場合- 34

Slide 35

Slide 35 text

静的解析だけでは検知できない(かもしれな い)動的プロパティは? エラーハンドラで握りつぶしているところで、ログに記録するように する ログが出たらお知らせがくるようにして、出てきた箇所をつぶしてい く 静的解析で見えている箇所を修正が終わってからの方が良いので、そ れが終わったら取り入れたい ここまでやれば PHP 9 までには駆逐でき…そう…? 動的プロパティをどう修正していくか -ウチらの場合- 35

Slide 36

Slide 36 text

まとめ 36

Slide 37

Slide 37 text

動的プロパティはPHP 8.2で非推奨、PHP 9.0 から例外になる 動的プロパティは静的解析で検知できる、でも万能じゃないか もしれない Garoon では静的解析で検知できたものをまずは修正していっ て、漏れたものはログに出して検知、修正していく PHP をより良くしてくださっている皆さん本当にありがとう ございます (唐突) まとめ 37

Slide 38

Slide 38 text

(おまけ)PHP 9 で例外になる愉快な仲間たち 38

Slide 39

Slide 39 text

未定義の変数の使用 Undefined Variable Error Promotion var_dump($undefined); // PHP 9.0: Throws Error exception. if ($user->admin) { $restricted = false; } if ($restricted) { die('You do not have permission to be here'); } (おまけ)PHP 9 で例外になる愉快な仲間たち https://wiki.php.net/rfc/undefined_variable_error_promotion より 39

Slide 40

Slide 40 text

未定義のプロパティの使用 Undefined Property Error Promotion class User {} $user = new User(); var_dump($user->name); // PHP 9.0 : Throws Error exception. // プロパティの有無は isset や empty で検知する if (isset($obj->name)) { echo "Hello " . $obj->name; } (おまけ)PHP 9 で例外になる愉快な仲間たち https://wiki.php.net/rfc/undefined_property_error_promotion より 40

Slide 41

Slide 41 text

文字列内での変数展開の文法の削除 Deprecate ${} string interpolation $foo = 'value'; $bar = 'foo'; var_dump( "$foo", // value "{$foo}", // value "{${$bar}}", // value "${foo}", // value PHP 9.0 : Throws Error exception. "${$bar}", // value PHP 9.0 : Throws Error exception. ); (おまけ)PHP 9 で例外になる愉快な仲間たち https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation および https://qiita.com/rana_kualu/items/fc4b02e2daaf102aa92f より 41

Slide 42

Slide 42 text

メソッド引数の暗黙のデフォルトnull値 Deprecate implicitly nullable parameter types function foo(T $var = null) {} // PHP 8.4: Deprecated: Implicitly marking parameter $var as nullable is deprecated, // the explicit nullable type must be used instead // PHP 9: remove support for implicitly nullable types in PHP 9 // こうやって書く function foo(?T $var = null) {} function foo(T|null $var = null) {} (おまけ)PHP 9 で例外になる愉快な仲間たち https://wiki.php.net/rfc/deprecate-implicitly-nullable-types より 42

Slide 43

Slide 43 text

PHP 9 をお迎えするために 非推奨になるコードを減らして 備えておきましょう! 43