LoginSignup
12
6

More than 5 years have passed since last update.

PhanでPHPのコーディング規約を自動チェックしよう

Last updated at Posted at 2016-12-15

再演: 「PHP7で堅牢なコードを書く - 例外処理、表明プログラミング、契約による設計」 + αという勉強会で、「assertぐらいでエバルんじゃねえ!」というふざけたタイトルで発表してきました。

資料はこちら。

PHP7では内部的にASTを作るようになりまして、それをPHP側から使えるようにするphp-astというC拡張があります。これを使って型推論つきの静的解析をするツールがPhanです。

Phanでは未定義変数や型に関する間違いを警告してくれるのですが、そういう明らかなバグの他にも自前のプラグインを作ってエラーをチェックすることができます。

スライドの趣旨としては、assertの話から入るものの、assertのことが主題ではなく、Phanを使ってコードの自動チェックを充実させようという内容です。

Phanプラグインの作り方

Phanプラグインの書き方は一応ドキュメントがあるのですが、詳しくないのでコードを読む必要があります。

今回のスライドで使ったプラグインの場合は、プロジェクトの .phan/plugins ディレクトリに次のようなファイルを置いて、

<?php declare(strict_types=1);
# .phan/plugins/NonBoolAssertPlugin.php

use Phan\AST\AnalysisVisitor;
use Phan\CodeBase;
use Phan\Language\Context;
use Phan\Language\UnionType;
use Phan\Plugin;
use Phan\Plugin\PluginImplementation;
use ast\Node;
use Phan\Issue;

class NonBoolAssertPlugin extends PluginImplementation {

     public function analyzeNode(
         CodeBase $code_base,
         Context $context,
         Node $node,
         Node $parent_node = null
     ) {
         (new NonBoolAssertVisitor($code_base, $context, $this))(
             $node
         );
     }
}

class NonBoolAssertVisitor extends AnalysisVisitor {

    /** @var Plugin */
    private $plugin;

    public function __construct(
        CodeBase $code_base,
        Context $context,
        Plugin $plugin
    ) {
        parent::__construct($code_base, $context);

        $this->plugin = $plugin;
    }

    public function visit(Node $node)
    {
    }

    public function visitCall(Node $node) : Context
    {
        $expression = $node->children['expr'];

        if ($expression->kind === \ast\AST_NAME &&
            $expression->children['name'] === 'assert'
        ) {
            $union_type = UnionType::fromNode($this->context, $this->code_base, $node->children['args']->children[0]);
            if ($union_type->serialize() !== "bool") {
                $this->plugin->emitIssue(
                    $this->code_base,
                    $this->context,
                    'PhanPluginNonBoolAssert',
                    "Non bool value passed to assert"
                );
            }
        }

        return $this->context;
    }
}

return new NonBoolAssertPlugin;

.phan/config.php にこのような記述を追加すれば良いです。

    'plugins' => [
        '.phan/plugins/NonBoolAssertPlugin.php',
    ],
12
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
6