Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What We Can Learn From OSS

inouehi
April 13, 2024

What We Can Learn From OSS

『OSSから学ぶ技術』

PHPカンファレンス小田原
2024-04-13 18:00〜 かま
https://phpcon-odawara.jp/

inouehi

April 13, 2024
Tweet

More Decks by inouehi

Other Decks in Programming

Transcript

  1. 20

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

    $argv[3] $argv[0] 配列という名のフリーダム
  3. operations、ファイル名、PHPのコード(テキスト)が入力される。 • 形式は様々 ◦ php-parse ◦ -d ◦ --dump ◦

    file.php ◦ "<?php code" • 複数入力されうるし入力されないこともある • 順不同 43 コマンドラインインタフェースが持つ複雑さとは
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 52 PHPStan - bin/phpstan $application = new \Symfony\Component\Console\Application( 'PHPStan -

    PHP Static Analysis Tool', ComposerHelper::getPhpStanVersion() ); // 略 $application->run();
  10. 56

  11. 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
  12. 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
  13. 65

  14. 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'
  15. 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 ※大部分を省略しています
  16. 75 getNodeTypes()の違い PHPStan public function getNodeType(): string { return ClassMethod::class;

    } Rector public function getNodeTypes(): array { return [ClassMethod::class]; }
  17. 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)
  18. 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; }
  19. 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
  20. 79 ところで トラーバスでノードを総当たりしつつも不要なノードはスキップするという機構が 個人的にはなるほどポイントでもありました。 public final function enterNode(\PhpParser\Node $node) {

    $nodeClass = \get_class($node); if (!$this->isMatchingNodeType($nodeClass)) { // スキップ return null; } // 略 $node = $this->refactor($node); // リファクタを実行
  21. 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
  22. 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); }
  23. 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; } }
  24. 99 • OSSは、デザインパターンやイディオムの宝箱 • あるいは、理論、原則、基礎の実践的事例 • 同じコンセプトにも異なる実装がありえる • ただし、その実装が常に最適とは限らない •

    実践知に気づくには、ベースとなる教養を育むことも重要 • 目的があるととっつきやすく、目的外の収穫もある • 目的外の収穫≒セレンデピティ(偶然得られた幸運)を増やすには機会を増やすこと