Slide 1

Slide 1 text

OSSから学ぶ技術 2024/04/13 PHPカンファレンス小田原 2024 LT

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

3 トーク概要 このトークでは、OSSから学べることの具体例を共有します。留意事項を添えて。 このトークではOSSから学ぶということの解像度をあげることを目指します。スキルアップへの引き出 しが一つ増えれば幸いです。 https://fortee.jp/phpconodawara-2024/proposal/134c1420-4a6a-40e0-b3d7-afd527c1168f

Slide 4

Slide 4 text

4 誰向けのトーク?

Slide 5

Slide 5 text

5 あなた (お約束)

Slide 6

Slide 6 text

6 あるいは ● 技術の学び方を模索されている方 ● “OSSに触れるとよい”的な言説にピンとこない方、懐疑的な方 ● OSSについて語りたい方(懇親会で聞かせて下さい!!)

Slide 7

Slide 7 text

7 あるいは ● 技術の学び方を模索されている方 ● “OSSに触れるとよい”的な言説にピンとこない方、懐疑的な方 ● OSSについて語りたい方(懇親会で聞かせて下さい!!)

Slide 8

Slide 8 text

8 免責事項

Slide 9

Slide 9 text

9 趣味・趣向・知識 が 静的解析に偏っています

Slide 10

Slide 10 text

10 私見に満ちたトークです

Slide 11

Slide 11 text

11 前置き

Slide 12

Slide 12 text

12 “読む”に向かう 2つのスタイル

Slide 13

Slide 13 text

13 1. 目的読み

Slide 14

Slide 14 text

14 2. 衝動読み

Slide 15

Slide 15 text

15 1. 目的読み 明確な目的を持って読むパターン

Slide 16

Slide 16 text

16 1. 目的読み 例えば ● 作り手の視点 ○ 〇〇という機能を作る参考にしたい ● 使用者の視点 ○ 挙動(仕様)を確かめたい

Slide 17

Slide 17 text

17 2. 衝動読み 気になるOSSをなんとなしに読みふけるパターン

Slide 18

Slide 18 text

18 学びの例

Slide 19

Slide 19 text

19 デザインパターン の実用例

Slide 20

Slide 20 text

20

Slide 21

Slide 21 text

21 概念はわかった気がする

Slide 22

Slide 22 text

22 でもどうやって使うの?

Slide 23

Slide 23 text

23 💡具体例から学ぼう💡

Slide 24

Slide 24 text

24 Object-Oriented Conference 2024公式ガイドブック(https://booth.pm/ja/items/5596314)第13章 ● ストラテジパターン@PHPUnit ● オブザーバパターン@Laravel ● ビジターパターン@PHP-Parser etc.

Slide 25

Slide 25 text

25 理論、原則、基礎の類は 教科書から

Slide 26

Slide 26 text

26 実践知はプロダクトから

Slide 27

Slide 27 text

27 ちなみに ● 〇〇パターンを探そうとして見つけたわけではない。 ● 色々読んでるうちに偶然見つけた。

Slide 28

Slide 28 text

28 目的読みには 目的外の収穫もある

Slide 29

Slide 29 text

29 PHPでも並列処理したい

Slide 30

Slide 30 text

30 ReactPHPがあるじゃない

Slide 31

Slide 31 text

31 でもどうやって使うの?

Slide 32

Slide 32 text

32 チュートリアルだけじゃ 心許ない

Slide 33

Slide 33 text

33 PHPStanがあるじゃない

Slide 34

Slide 34 text

34 生きた事例

Slide 35

Slide 35 text

35 稼働実績のある信頼感

Slide 36

Slide 36 text

36 といいつつ盲信するなかれ

Slide 37

Slide 37 text

37 CLIツールが作りたい

Slide 38

Slide 38 text

38 作りたい物の既存実装を探す という視点

Slide 39

Slide 39 text

39 コマンドライン入力 一つとっても実はちょっと奥が深い

Slide 40

Slide 40 text

40 PHP Parser $ ./vendor/bin/php-parse file.php -r --var-dump

Slide 41

Slide 41 text

41 PHP Parser $ ./vendor/bin/php-parse file.php -r --var-dump $argv[1] $argv[2] $argv[3] $argv[0]

Slide 42

Slide 42 text

42 PHP Parser $ ./vendor/bin/php-parse file.php -r --var-dump $argv[1] $argv[2] $argv[3] $argv[0] 配列という名のフリーダム

Slide 43

Slide 43 text

operations、ファイル名、PHPのコード(テキスト)が入力される。 ● 形式は様々 ○ php-parse ○ -d ○ --dump ○ file.php ○ "

Slide 44

Slide 44 text

44 様々な入力パターンを 想定する必要がある 要するに

Slide 45

Slide 45 text

45 PHP Parser list($operations, $files, $attributes) = parseArgs($argv); https://github.com/nikic/PHP-Parser/blob/46be4560c4cd4bab2b74882c0da39a4548a5cfbe/bin/php-parse

Slide 46

Slide 46 text

46 PHP Parser - parseArgs() function parseArgs($args) { // 略 foreach ($args as $arg) { // 引数を評価(後述) } return [$operations, $files, $attributes]; } https://github.com/nikic/PHP-Parser/blob/46be4560c4cd4bab2b74882c0da39a4548a5cfbe/bin/php-parse

Slide 47

Slide 47 text

47 PHP Parser - parseArgs() function parseArgs($args) { // 略 foreach ($args as $arg) { // 引数を評価(後述) } return [$operations, $files, $attributes]; } https://github.com/nikic/PHP-Parser/blob/46be4560c4cd4bab2b74882c0da39a4548a5cfbe/bin/php-parse

Slide 48

Slide 48 text

48 PHP Parser - parseArgs() function parseArgs($args) { // 略 foreach ($args as $arg) { // 引数を評価(後述) } return [$operations, $files, $attributes]; } https://github.com/nikic/PHP-Parser/blob/46be4560c4cd4bab2b74882c0da39a4548a5cfbe/bin/php-parse

Slide 49

Slide 49 text

49 PHP Parser - parseArgs() switch ($arg) { case '--dump': case '-d': $operations[] = 'dump'; break; case '--with-column-info': case '-c'; $attributes['with-column-info'] = true; break; https://github.com/nikic/PHP-Parser/blob/46be4560c4cd4bab2b74882c0da39a4548a5cfbe/bin/php-parse

Slide 50

Slide 50 text

50 PHP Parser - parseArgs() default: if (preg_match('/^--version=(.*)$/', $arg, $matches)) { $attributes['version'] = PhpParser\PhpVersion::fromString($matches[1]); } elseif ($arg[0] === '-' && \strlen($arg[0]) > 1) { showHelp("Invalid operation $arg."); } else { $files[] = $arg; } https://github.com/nikic/PHP-Parser/blob/46be4560c4cd4bab2b74882c0da39a4548a5cfbe/bin/php-parse

Slide 51

Slide 51 text

51 ちなみに PHPStan の場合 https://symfony.com/doc/current/components/console.html Symfony Console Componentを使用している。

Slide 52

Slide 52 text

52 PHPStan - bin/phpstan $application = new \Symfony\Component\Console\Application( 'PHPStan - PHP Static Analysis Tool', ComposerHelper::getPhpStanVersion() ); // 略 $application->run();

Slide 53

Slide 53 text

53 巨人も巨人の肩に乗っている

Slide 54

Slide 54 text

54 巨人を通じて別の巨人を知る

Slide 55

Slide 55 text

55 プログレスバー

Slide 56

Slide 56 text

56

Slide 57

Slide 57 text

57 ところで 人は安心を得るために生きる らしい

Slide 58

Slide 58 text

58 進捗報告は安心の素 という説があるらしい

Slide 59

Slide 59 text

59 PHPStan $preFileCallback = null; $postFileCallback = static function (int $step) use ($errorOutput): void { $errorOutput->getStyle()->progressAdvance($step); }; $errorOutput->getStyle()->progressStart($allAnalysedFilesCount); $errorOutput->getStyle()->progressAdvance($allAnalysedFilesCount - $filesCount); // 略 if (!$debug) { $errorOutput->getStyle()->progressFinish(); } https://github.com/phpstan/phpstan-src/blob/9bb4ef961960a1056060209b3ed7e09b93c06f36/src/Command/AnalyseApplication.php

Slide 60

Slide 60 text

60 PHPStan $preFileCallback = null; $postFileCallback = static function (int $step) use ($errorOutput): void { $errorOutput->getStyle()->progressAdvance($step); }; $errorOutput->getStyle()->progressStart($allAnalysedFilesCount); $errorOutput->getStyle()->progressAdvance($allAnalysedFilesCount - $filesCount); // 略 if (!$debug) { $errorOutput->getStyle()->progressFinish(); } https://github.com/phpstan/phpstan-src/blob/9bb4ef961960a1056060209b3ed7e09b93c06f36/src/Command/AnalyseApplication.php

Slide 61

Slide 61 text

61 プログレスバーの実装方法 1. progressStart() して 2. progressAdvance() を繰り返して 3. progressFinish() する

Slide 62

Slide 62 text

62 実はこれも

Slide 63

Slide 63 text

63 Symfony https://symfony.com/doc/current/console/style.html#symfony-style-progressbar きょじん

Slide 64

Slide 64 text

64 其れ、PHPUnitにもあるよね プログレスバー

Slide 65

Slide 65 text

65

Slide 66

Slide 66 text

66 実装の違いを愉しむ という道もある

Slide 67

Slide 67 text

67 実装の違いから学ぶ

Slide 68

Slide 68 text

68 autoload の実装の違い

Slide 69

Slide 69 text

69 autoloadが持つ複雑性とは 開発環境 . ├─ bin │ └─ my-cli └─ vendor └─ autoload.php プロダクション環境 . └─ vendor ├─ autoload.php └─ repository-name └─ package-name └─ bin └─ my-cli __DIR__ . '/../vendor/autoload.php' __DIR__ . '/../../../autoload.php'

Slide 70

Slide 70 text

70 PHP Parser https://github.com/nikic/PHP-Parser/blob/46be4560c4cd4bab2b74882c0da39a4548a5cfbe/bin/php-parse foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) { if (file_exists($file)) { require $file; break; } }

Slide 71

Slide 71 text

71 Rector public function includeDependencyOrRepositoryVendorAutoloadIfExists() : void { $this->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/autoload.php'); } public function autoloadProjectAutoloaderFile() : void { $this->loadIfExistsAndNotLoadedYet(__DIR__ . '/../../../autoload.php'); } public function autoloadRectorInstalledAsGlobalDependency() : void { $this->loadIfExistsAndNotLoadedYet('vendor/autoload.php'); } https://github.com/rectorphp/rector/blob/11b9220a05ee3c1bcaa75d76c8b5e5baa8d01484/bin/rector.php ※大部分を省略しています

Slide 72

Slide 72 text

72 getNodeTypes() の実装の違い

Slide 73

Slide 73 text

73 getNodeTypes() is 何? とある静的解析ツールにおいて 解析対象のノードを特定するメソッド

Slide 74

Slide 74 text

74 getNodeTypes() ● PHPStanとRectorが同じコンセプトの機構を持っているが、詳細が異なる。

Slide 75

Slide 75 text

75 getNodeTypes()の違い PHPStan public function getNodeType(): string { return ClassMethod::class; } Rector public function getNodeTypes(): array { return [ClassMethod::class]; }

Slide 76

Slide 76 text

76 getNodeTypes()の違い PHPStan[1] public function getNodeType(): string { return ClassMethod::class; } Rector[2] public function getNodeTypes(): array { return [ClassMethod::class]; } 1. https://github.com/phpstan/phpstan-src/blob/3d43198f80f47da6fee60fb3d86ae004eee41f13/src/Rules/Methods/MethodAttributesRule.php 2. https://github.com/rectorphp/rector/blob/0e57251e460fb68e7ad8e1fdd0467e9db6ca82c8/rules/Php80/Rector/ClassMethod/SetStateToStaticRector.php PHPStanは1つ、Rectorは複数のノードを指定できるが、この裏側にあるメカニズムに違いがある。 (詳細はリンク先のコードを辿るなどして下さいmm)

Slide 77

Slide 77 text

77 getNodeTypes()の違い超概要 ● PHPStanはノード毎にルールを対応づける。 $rules[$rule->getNodeType()][] = $rule; ● Rectorはトラバース中にノードをスキップする。 private function isMatchingNodeType(string $nodeClass) : bool { foreach ($this->getNodeTypes() as $nodeType) { if (\is_a($nodeClass, $nodeType, \true)) { return \true; } } return \false; }

Slide 78

Slide 78 text

78 トラバースって何?? 参考資料 ● 『PHP Parserで学ぶPHP』[1] ● 『PHP Parserで学ぶPHPと静的解析』[2] ● 『木を見て!森を見て!目で見てわかるAST(抽象構文木)』[3] 1. https://speakerdeck.com/inouehi/php-parserdexue-buphp 2. https://speakerdeck.com/inouehi/learning-php-and-static-analysis-with-php-parser 3. https://speakerdeck.com/inouehi/understanding-ast-by-looking

Slide 79

Slide 79 text

79 ところで トラーバスでノードを総当たりしつつも不要なノードはスキップするという機構が 個人的にはなるほどポイントでもありました。 public final function enterNode(\PhpParser\Node $node) { $nodeClass = \get_class($node); if (!$this->isMatchingNodeType($nodeClass)) { // スキップ return null; } // 略 $node = $this->refactor($node); // リファクタを実行

Slide 80

Slide 80 text

80 名もなき断片にも学びがある

Slide 81

Slide 81 text

81 イディオムを引き出しにしまう

Slide 82

Slide 82 text

82 インタフェースのデフォルト実装 というイディオム

Slide 83

Slide 83 text

83 PHP Parserのビジター ● トラバーサ… ノードを一つずつ辿る。 ● ビジター … ノードを処理する。

Slide 84

Slide 84 text

84 ビジターって何?? 参考資料 ● 『PHP Parserで学ぶPHP』[1] ● 『PHP Parserで学ぶPHPと静的解析』[2] ● 『木を見て!森を見て!目で見てわかるAST(抽象構文木)』[3] 1. https://speakerdeck.com/inouehi/php-parserdexue-buphp 2. https://speakerdeck.com/inouehi/learning-php-and-static-analysis-with-php-parser 3. https://speakerdeck.com/inouehi/understanding-ast-by-looking

Slide 85

Slide 85 text

85 PHP Parserのビジター ビジターはNodeVisitorを実装する。 ※メソッドを抜粋 interface NodeVisitor { public function beforeTraverse(array $nodes); public function enterNode(Node $node); public function leaveNode(Node $node); public function afterTraverse(array $nodes); }

Slide 86

Slide 86 text

86 PHP Parserのビジター ビジターはNodeVisitorを実装する。あるいはNodeVisitorAbstractを拡張する。 デフォルト実装 を担っている

Slide 87

Slide 87 text

87 PHP Parserのビジター - NodeVisitorAbstract abstract class NodeVisitorAbstract implements NodeVisitor { public function beforeTraverse(array $nodes) { return null; } public function enterNode(Node $node) { return null; } public function leaveNode(Node $node) { return null; } public function afterTraverse(array $nodes) { return null; } }

Slide 88

Slide 88 text

88 PHP Parserのビジター - NodeVisitorAbstract 抽象クラスにデフォルト実装を宣言しておくことで、 例えばenterNode()だけを実装したい時、その他のメソッドの実装を省ける。

Slide 89

Slide 89 text

89 (再)目的読みには 目的外の収穫もある

Slide 90

Slide 90 text

90 目的外の収穫を求めて 目的を作ってみてはどうか?!

Slide 91

Slide 91 text

91 留意事項

Slide 92

Slide 92 text

92 1. OSSにただ触れるだけで技術が身につくわけではない。

Slide 93

Slide 93 text

93 1. OSSにただ触れるだけで技術が身につくわけではない。 ○ 設計の技法、コンセプトに気づくにも教養的なものが必要。

Slide 94

Slide 94 text

94 1. OSSにただ触れるだけで技術が身につくわけではない。 ○ 設計の技法、コンセプトに気づくにも教養的なものが必要。 ○ 技術書や、人との議論等からナレッジを摂取しておくことで、抽象や概念と具体が繋がる。 その時腹落ちして使いこなせる状態に近づく。

Slide 95

Slide 95 text

95 2. 実践知と言ったものの、その実装が常に最適とは限らない。 ○ 1次情報で裏取りする、学びをアップデートするなど、基盤となる所作は忘れずに。

Slide 96

Slide 96 text

96 3. 誰かにとっての学びが他の誰かにとっても学びであるとは限らない。逆も真。

Slide 97

Slide 97 text

97 3. 誰かにとっての学びが他の誰かにとっても学びであるとは限らない。逆も真。 ○ 自分の目でも確かめることに価値がありそう。

Slide 98

Slide 98 text

98 まとめ

Slide 99

Slide 99 text

99 ● OSSは、デザインパターンやイディオムの宝箱 ● あるいは、理論、原則、基礎の実践的事例 ● 同じコンセプトにも異なる実装がありえる ● ただし、その実装が常に最適とは限らない ● 実践知に気づくには、ベースとなる教養を育むことも重要 ● 目的があるととっつきやすく、目的外の収穫もある ● 目的外の収穫≒セレンデピティ(偶然得られた幸運)を増やすには機会を増やすこと

Slide 100

Slide 100 text

100 目的外の収穫を求めて 目的を作ってみてはどうか?!

Slide 101

Slide 101 text

101 おわりに

Slide 102

Slide 102 text

102 自分なんかはむしろOSSとの関わりがまだ浅い方だと自認しています。 今回、OSS初心者目線で学びについてお話しました。 目線が違えば視界も変わると思います。

Slide 103

Slide 103 text

103 ● このトークが物足りないと感じた方へ

Slide 104

Slide 104 text

104 ● このトークが物足りないと感じた方へ ○ あなたの学び、気づき(の流儀)を教えて下さい!!

Slide 105

Slide 105 text

105 ● いいね👍と感じた方へ ○ 推しのOSS(からの学び)を教えて下さい!!

Slide 106

Slide 106 text

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