PHPStanでCustomRuleを作る / Make PHPStan CustomRule

PHPStanでCustomRuleを作る / Make PHPStan CustomRule

PHPカンファレンス福岡2019の発表資料です。
PHPStanのCustomRuleを使えば独自の静的検査をかけることが出来ます。具体例を通しながら、CustomRuleで何が実現できるのか、どうすれば自分たちで作れるようになるのかを解説しました。

B989cc865d53d8e26fdadac99727113c?s=128

Satoshi Kawashima

June 29, 2019
Tweet

Transcript

  1. 3.

    © - BASE, Inc. • PHPStanとは • 何故CustomRuleを作るのか • CustomRuleの基本

    • 望むCustomRuleを作るには • PHPStanのクラスを使いこなす • CustomRuleの具体例 ⽬次
  2. 7.

    © - BASE, Inc. • 未定義変数の使⽤ • タイプヒントとの不⼀致 • 存在しないファンクション、メソッド、プロ

    パティの呼び出し • 無意味なコードの検出(未使⽤変数や絶対⼊ らない条件分岐) • etc 問題点の検出例
  3. 12.

    © - BASE, Inc. • 全員がPhpStormが使っているとは限らな い • PHPStanの⽅がCIで実⾏させやすい •

    検査(というか結果の判定)が厳密 • 検査項⽬が豊富 • 独⾃の検査(CustomRule)が作れる それ、PhpStormで良くね?
  4. 17.

    © - BASE, Inc. • PHPStanとは • 何故CustomRuleを作るのか • CustomRuleの基本

    • 望むCustomRuleを作るには • PHPStanのクラスを使いこなす • CustomRuleの具体例 ⽬次
  5. 41.

    © - BASE, Inc. • PHPStanとは • 何故CustomRuleを作るのか • CustomRuleの基本

    • 望むCustomRuleを作るには • PHPStanのクラスを使いこなす • CustomRuleの具体例 ⽬次
  6. 43.

    © - BASE, Inc. よくあるPHPStanの実⾏例 $ vendor/bin/phpstan analyse -l -c

    phpstan.neon ./src -l オプションで検査レベルを指定する (レベルが上がるほど厳密になる)
  7. 46.

    © - BASE, Inc. • CustomRuleクラスを作って、configに 追加するだけ CustomRuleの追加の仕⽅ $ vendor/bin/phpstan

    analyse -l -c phpstan.config.neon ./src PHPStanのconfigファイルを作る (拡張⼦は.neon)
  8. 47.

    © - BASE, Inc. • CustomRuleクラスを作って、configに 追加するだけ CustomRuleの追加の仕⽅ $ vendor/bin/phpstan

    analyse -l -c phpstan.config.neon ./src PHPStanのconfigファイルを作る (拡張⼦は.neon) -c オプションに⾃分で作った configファイルを渡す
  9. 50.
  10. 52.

    © - BASE, Inc. • ソースコードをPHPにとっての最⼩の意 味単位まで分解する 1. 字句解析 T_OPEN_TAG

    T_NAMESPACE T_STRING T_CLASS T_STRING T_PRIVATE T_VARIABLE T_PUBLIC T_FUNCTION T_STRING T_STRING T_VARIABLE T_STRIN T_RETURN T
  11. 53.

    © - BASE, Inc. • nikic/PHP-Parserで構⽂解析し、 AST(abstract syntax tree)を⽣成する 2.

    構⽂解析 T_OPEN_TAG T_NAMESPACE T_STRING T_CLASS T_STRING T_PRIVATE T_VARIABLE T_PUBLIC T_FUNCTION T_STRING T_STRING T_VARIABLE T_STRIN T_RETURN T Namespace statement Class statement Property statement ClassMethod statement
  12. 54.

    © - BASE, Inc. 2. 構⽂解析 \PhpParser\Node\Stmt\Namespace_ \PhpParser\Node\Stmt\Return_ \PhpParser\Node\Stmt\Expression \PhpParser\Node\Stmt\ClassMethod

    \PhpParser\Node\Stmt\Property \PhpParser\Node\Stmt\Class_ \PhpParser\Node\Stmt\PropertyProperty \PhpParser\Node\VarLinkIdentifier \PhpParser\Node\Scalar\Identifier \PhpParser\Node\Expr\Assign \PhpParser\Node\Expr\Variable \PhpParser\Node\Scalar\String \PhpParser\Node\Expr\BinaryOp\ConCat \PhpParser\Node\Expr\Variable \PhpParser\Node\Expr\Variable
  13. 56.

    © - BASE, Inc. ノード=ASTの各頂点 \PhpParser\Node\Stmt\Namespace_ \PhpParser\Node\Stmt\Return_ \PhpParser\Node\Stmt\Expression \PhpParser\Node\Stmt\ClassMethod \PhpParser\Node\Stmt\Property

    \PhpParser\Node\Stmt\Class_ \PhpParser\Node\Stmt\PropertyProperty \PhpParser\Node\VarLinkIdentifier \PhpParser\Node\Scalar\Identifier \PhpParser\Node\Expr\Assign \PhpParser\Node\Expr\Variable \PhpParser\Node\Scalar\String \PhpParser\Node\Expr\BinaryOp\ConCat \PhpParser\Node\Expr\Variable \PhpParser\Node\Expr\Variable
  14. 57.

    © - BASE, Inc. . ASTの解析 NodeScopeResolver AST 解析 各Ruleにはどのノードを発⾒したときに

    検査メソッドを呼び出してほしいかが書いてある Rule Rule Rule Rule Rule Rule
  15. 64.

    © - BASE, Inc. • PHPStanとは • 何故CustomRuleを作るのか • CustomRuleの基本

    • 望むCustomRuleを作るには • PHPStanのクラスを使いこなす • CustomRuleの具体例 ⽬次
  16. 67.

    No

  17. 68.

    Scope Node Broker Type RunLevelHelper TokenIterator ParameterAcceptorSelector FunctionParameterCheck PhpDocParser Lexer

    Container ClassCaseSensitivityCheck 望む解析を⾏うには PHPStanの機能群を 使いこなす必要がある
  18. 71.

    © - BASE, Inc. • 主要3クラスの解説 • 具体例1: 新しく追加したメソッドには戻り 値型宣⾔を必須化する

    • DependencyInjectionの使い⽅ • 具体例2: 独⾃phpdocの作成 • PhpDocParserの使い⽅ この後のスライド
  19. 75.
  20. 76.

    © - BASE, Inc. ASTの頂点を表すクラス コレ \PhpParser\Node\Stmt\Namespace_ \PhpParser\Node\Stmt\Return_ \PhpParser\Node\Stmt\Expression \PhpParser\Node\Stmt\ClassMethod

    \PhpParser\Node\Stmt\Property \PhpParser\Node\Stmt\Class_ \PhpParser\Node\Stmt\PropertyProperty \PhpParser\Node\VarLinkIdentifier \PhpParser\Node\Scalar\Identifier \PhpParser\Node\Expr\Assign \PhpParser\Node\Expr\Variable \PhpParser\Node\Scalar\String \PhpParser\Node\Expr\BinaryOp\ConCat \PhpParser\Node\Expr\Variable \PhpParser\Node\Expr\Variable
  21. 78.

    © - BASE, Inc. • PhpParser\Nodeを基底とした複雑な継 承関係がある • 合計183個のクラス •

    解析したいコード近辺の情報はこいつを 辿りながら集めることになる PhpParser\Node
  22. 80.
  23. 87.

    © - BASE, Inc. Scopeの作られ⽅ NodeScopeResolver AST 解析 スコープの切れ⽬となるノードを発⾒するたび、 Scopeオブジェクトを新しく作る

    Rule Rule Rule Rule Rule Rule Ruleクラスの検査メソッドを呼び出すときには、 そのノードが属するScopeオブジェクトもセットで渡す 検査メソッドの呼び出し Scope Scope Scope Scope
  24. 89.

    © - BASE, Inc. • 解析対象のファイル内部に関わることな らだいたいScopeクラスに聞くことにな る • ファイル名、クラス情報を取得する

    • 現在のNodeのスコープ上の位置を聞く • 変数スコープの型解決(重要) Scopeクラスを使いこなす
  25. 102.
  26. 106.

    © - BASE, Inc. リフレクションを提供するクラス Broker autoloader \PHPStan\Reflection\ClassReflection \ReflectionClass config.neon

    Rule クラス定義 動的プロパティ‧メソッドの 定義情報 組み込みリフレクションをラップした、 動的プロパティ‧メソッド定義も含めた リフレクションの提供
  27. 107.

    © - BASE, Inc. • Broker: 仲買⼈、周旋屋販売⼈ • 解析対象のファイル以外からの(主にクラスや関数 の)リフレクションを供給する

    • PHPStanは動的メソッド‧プロパティも解析できる ようにサポートするための機能があり、それらを踏ま えた上での独⾃のリフレクションクラスを⽣成する • ⾃分でReflectionを取ろうとせずBrokerに委託する のがおそらく正しいPHPStanスタイル PHPStan\Broker\Broker
  28. 114.

    © - BASE, Inc. 1. 修正前後のファイルコンテンツを⼊⼿ 1. マージベースのコミットハッシュの取得 $ git

    show-branch --merge-base master HEAD b faefdc b d c b b d f ab a fd da ec e fc f b faefdc a f b master feature_branch
  29. 115.

    © - BASE, Inc. 1. 修正前後のファイルコンテンツを⼊⼿ a fd da ec

    e fc f b faefdc a f b master feature_branch 2. 修正後のファイルコンテンツを取得 $ git show HEAD:src/Hoge.php <?php declare(strict_types= );
  30. 116.

    © - BASE, Inc. 1. 修正前後のファイルコンテンツを⼊⼿ a fd da ec

    e fc f b faefdc a f b master feature_branch 3. 修正前(マージベース)のファイルコンテンツを取得 $ git show fc f :src/Hoge.php <?php declare(strict_types= );
  31. 117.

    © - BASE, Inc. . ASTの⼊⼿ ClassMethod ClassMethod ClassMethod ClassMethod

    ClassMethod PHP-Parserを使ってASTの取得 マージベース時点のファイルコンテンツ 現時点のファイルコンテンツ
  32. 123.

    © - BASE, Inc. 3. 実⾏時にneonファイルを渡す $ vendor/bin/phpstan analyze -c

    ./conf/phpstan.config.neon src/ -cオプションに渡す
  33. 125.
  34. 126.

    © - BASE, Inc. • traitをuseするのに必要なプロパティを 独⾃タグで⽰す • traitが持つ暗黙的依存を表現し、検査 する

    • class側でうっかり暗黙的依存を破って しまう事故を防⽌する 具体例2:traitの暗黙的依存を⽰す
  35. 139.

    © - BASE, Inc. • PSR- は個別のアプリケーション向けの独⾃タグ を考慮していて、以下の規約が定められている • タグの先頭にPHPスタイルの名前空間をつける

    • 例:@\Doctrine\Orm\Mapping\Entity() • タグの先頭にハイフンを付けたベンダ名をつける • 例:@phan-closure-scope 余談:PSR- における独⾃タグの規約