Slide 1

Slide 1 text

2023/11/10 Qiita Night〜PHP〜 ちょこっとラクするPHPコード読解術

Slide 2

Slide 2 text

2 ● Hiroki Inoue ● Software Engineer ● Engineering Manager @ WHITEPLUS, Inc. About Me

Slide 3

Slide 3 text

3 PHPで書かれたコードの読み方に興味がある方向けのトークです。 恐らく主に初心者の方向けだと思います。 誰向けのLT?

Slide 4

Slide 4 text

4 アジェンダ 前提 伝えたいこと 01 02 結論 解決したい課題 03 04 Xxxxxxの活用方法 05 まとめ 06

Slide 5

Slide 5 text

5 静的解析(Psalm, PHPStan, RectorやPHP Parser)等の仕様を理解する目的で読むことが多いです。 IDEはPHPStormです。 前提

Slide 6

Slide 6 text

6 あなたのコードの読み方を教えて下さい! でもその前に私のやり方を紹介します。 #QiitaNight 伝えたいこと

Slide 7

Slide 7 text

7 Xdebugを活用しよう! 結論

Slide 8

Slide 8 text

8 Xdebugを活用しよう! デバッグ以外に利用したっていいじゃない。 結論

Slide 9

Slide 9 text

9 解決したい課題

Slide 10

Slide 10 text

10 コードが込み入ってくると迷子になりやすい。 覚えることと考えることを同時にやるのは負担が大きい。 覚えることを減らして、考えることに集中したい。 解決したい課題

Slide 11

Slide 11 text

11 解決したい課題 コードが込み入ってくると迷子になりやすい。 覚えることと考えることを同時にやるのは負担が大きい。 覚えることを減らして、考えることに集中したい。 ↓ ● コードベース全体を理解しようとしない。 ● 思考過程やメモをコード中に書きながら読み進める。 ● 確認したいことをどこかに箇条書きしながら読み進める。 ● 簡単化する。 ● 機械に委譲する。☜本日のスコープ

Slide 12

Slide 12 text

12 解決策(再掲) ● コードベース全体を理解しようとしない。 ○ 目的を絞る、確認する機能を絞る。 ○ issueを起点に読み始めるようにしている。 ● 思考過程やメモをコード中に書きながら読み進める。 ○ コードの外に書こうとすると、メモと該当箇所の紐付けが煩わしい。 ● 確認したいことをどこかに箇条書きしながら読み進める。 ○ 読んでいる内に目的を忘れるのを防ぐ。読むのに集中できる。 ● 簡単化する。 ○ シンプルな処理で挙動を確認する。 ○ 徐々に複雑にしながら、挙動の差異に着目して処理を追う。 ● 機械に委譲する。☜本日のスコープ

Slide 13

Slide 13 text

13 解決策(再掲) ● コードベース全体を理解しようとしない。 ○ 目的を絞る、確認する機能を絞る。 ○ issueを起点に読み始めるようにしている。 ● 思考過程やメモをコード中に書きながら読み進める。 ○ コードの外に書こうとすると、メモと該当箇所の紐付けが煩わしい。 ● 確認したいことをどこかに箇条書きしながら読み進める。 ○ 読んでいる内に目的を忘れるのを防ぐ。読むのに集中できる。 ● 簡単化する。 ○ シンプルな処理で挙動を確認する。 ○ 徐々に複雑にしながら、挙動の差異に着目して処理を追う。 ● 機械に委譲する。☜本日のスコープ

Slide 14

Slide 14 text

14 Xxxxxxの活用方法 Xdebugの活用方法

Slide 15

Slide 15 text

15 1行ずつ読み間違うことなく読み進められる。 ステップ実行

Slide 16

Slide 16 text

16 例えばLaravelの起動処理を知りたいと思った時 ステップ実行

Slide 17

Slide 17 text

17 index.phpを読みに行くと… ステップ実行 $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response);

Slide 18

Slide 18 text

18 ジャンプできない… ステップ実行 $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response);

Slide 19

Slide 19 text

19 ジャンプできない⇨前の処理を読みクラスを特定してメソッドを探すという手間と脳内メモリ消費… ステップ実行 $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response);

Slide 20

Slide 20 text

20 ステップ実行すると… ステップ実行 $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class);_____________________________________________________ $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response);

Slide 21

Slide 21 text

21 ステップ実行 class Application public function make($abstract, array $parameters = []) { $this->loadDeferredProviderIfNeeded($abstract = $this->getAlias($abstract));__________ return parent::make($abstract, $parameters); } 実際の処理順に従いメソッドの中に進む

Slide 22

Slide 22 text

22 ステップ実行 if文があると… protected function loadDeferredProviderIfNeeded($abstract) { if ($this->isDeferredService($abstract) && ! isset($this->instances[$abstract])) { $this->loadDeferredProvider($abstract); } }

Slide 23

Slide 23 text

23 ステップ実行 if文があると… protected function loadDeferredProviderIfNeeded($abstract) { if ($this->isDeferredService($abstract) && ! isset($this->instances[$abstract])) { $this->loadDeferredProvider($abstract); } } メソッド

Slide 24

Slide 24 text

24 ステップ実行 if文があると… protected function loadDeferredProviderIfNeeded($abstract) { if ($this->isDeferredService($abstract) && ! isset($this->instances[$abstract])) { $this->loadDeferredProvider($abstract); } } メソッド 変数 変数

Slide 25

Slide 25 text

25 ステップ実行 if文があると… protected function loadDeferredProviderIfNeeded($abstract) { if ($this->isDeferredService($abstract) && ! isset($this->instances[$abstract])) { $this->loadDeferredProvider($abstract); } } メソッド 変数 変数 プロパティ

Slide 26

Slide 26 text

26 ステップ実行 trueなのかfalseなのか目で確認できる protected function loadDeferredProviderIfNeeded($abstract) { if ($this->isDeferredService($abstract) && ! isset($this->instances[$abstract])) { $this->loadDeferredProvider($abstract);__________________________________________ } }

Slide 27

Slide 27 text

27 ステップ実行 処理が複雑になると、もうどこから手をつけようか…という気分に

Slide 28

Slide 28 text

28 ステップ実行 処理が複雑になると、もうどこから手をつけようか…という気分に 1行ずつ着実にみていける

Slide 29

Slide 29 text

29 変数の中身が見れる⇨変数を覚えておく必要がない 変数

Slide 30

Slide 30 text

30 変数

Slide 31

Slide 31 text

31 変数

Slide 32

Slide 32 text

32 変数 とてもじゃないが覚えきれない

Slide 33

Slide 33 text

33 スタックトレースがみれる⇨処理経路に気づける。また、それを覚える必要がない。 スタックトレース

Slide 34

Slide 34 text

34 例えばEloquentのscopeがどのように呼び出されるのかを知りたい時 スタックトレース

Slide 35

Slide 35 text

35 scopeメソッドの中にブレークポイントを仕掛けて処理を実行すると… スタックトレース class Post extends Model { use HasFactory; public function scopeTitle($query, $str) { ⚫ return $query->where('name', $str);__________________________________________ } }

Slide 36

Slide 36 text

36 スタックトレース

Slide 37

Slide 37 text

37 一つ遡ると、scopeTitle()メソッドを組み立てていることがわかる スタックトレース public function callNamedScope($scope, array $parameters = []) { return $this->{'scope'.ucfirst($scope)}(...$parameters); }

Slide 38

Slide 38 text

38 更にいくつか遡ると、Post::title("xxxx");からマジックメソッドがトリガーされていることがわかる スタックトレース public static function __callStatic($method, $parameters) { return (new static)->$method(...$parameters); }

Slide 39

Slide 39 text

39 スタックトレースから当該箇所にジャンプできる。 スタックトレースからのジャンプ

Slide 40

Slide 40 text

40 スタックトレースから当該箇所にジャンプできる。 スタックトレースからのジャンプ ☜ click!!_

Slide 41

Slide 41 text

41 スタックトレースから当該箇所にジャンプできるし、変数の中身も見れる。 スタックトレースからのジャンプからの変数

Slide 42

Slide 42 text

42 ● Xdebugを使って静的解析を読む時は並列処理をoffにしましょう。 ● Xdebug以外にも便利なツールたち。 ○ IDE ○ ChatGPT ○ PHPUnit and more... ● とはいえ前提知識が多いに越したことはない。 ○ 例えば、PHPのマジックメソッドを知らないとなぜ呼び出されるのかわからない おまけのTips

Slide 43

Slide 43 text

43 まとめ

Slide 44

Slide 44 text

44 まとめ ● コードは読むものじゃない、(機械に)読ませるものだ。 ● Xdebugに助けてもらいましょう。

Slide 45

Slide 45 text

ご清聴ありがとうございました