Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

資料はこちら。

https://speakerdeck.com/edvakf/assertguraideebarunziyanee

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',
    ],
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away