illuminate/databaseでクエリログはいつからデフォルトで取らないようになったのか

Laravel 9.x時点での話 📝

実行済みのクエリログを取得する場合、DB::connection()->enableQueryLog();のようにしてログを有効化し、$queries = DB::getQueryLog();のようにする。

クエリログはデフォルトでは取得しないようになっているので最初にenableQueryLog()をしないといけないのだが、昔はデフォルトで取得するようになっていた(=先述のメソッドを呼ぶ必要がない)らしい?ということを知り、調べてみた。


どうやら変更されたのは2015年頃、4.2から5.0にアップグレード時のようだ。

github.com

もう少し歴史をさかのぼってみると、どうやら元々はクエリログ取得のオンオフ機能はなかったようで、以下issueがきっかけで実装されたっぽい(たぶん)。これは2013年頃で3系。

github.com

github.com

なるほどね...ということでおわり。


余談だけど、大量のクエリログが溜まって「メモリ〜〜」とならないように気をつけよう。

些細なことでも課題に感じたことは言語化して共有できれば良いと思う

タイトルの主語がでかい。状況次第だろう。

ただ、例えば、チームでプロダクト開発をしているときに課題に感じることがあれば、些細なことであっても言語化して共有できれば良いと思っている。共有はテキストベースだと更に良いと思っている。

...のだが、なぜそう思っているのかをうまく言語化できなかったので、改めて「なぜ」を考えてみた。

考えたこと

前提として、チームやまわりの環境において やっていき、のっていき という考え方や動きが存在すると仮定する。

ref: やっていき、のっていき / The Secret of Leadership and Followership - Speaker Deck

  • どんなメリットがありそう?
    • 言語化し、共有するまでの過程で「なぜ課題に感じたのか」の思考を整理することができる
    • チームに共有することで「自分vs課題」から「チームvs課題」に変えることができる
    • 課題に感じた人にとっては些細なことかもしれないが、チームの他メンバーからすれば些細なことではない(=重要)かもしれない
    • 課題の解決案を考えるとき、1人よりチームのほうがより良い案が生まれる可能性がある
    • チームで考えることで新たな課題や可能性が見つかる可能性がある
    • 共有をテキストベースでやると、
      • パブリックな場でやればチーム外からの思わぬのっていきが生まれる可能性がある
      • ログが残るので後からでも参照できる
  • 懸念は?
    • 共有するまでに諸々の手間がかかる
      • → それはそうって感じだが、それに見合う価値があると思う
    • 自分が課題に感じたことなんて大した価値はないんじゃ...
      • → 課題は様々な視点から生まれる・気付くはずなので、そのとある視点から感じた課題には価値があると思う

まとめ

今回はチームでのプロダクト開発を例に考えてみたが、結果としてプロダクトの品質向上に繋がる可能性はそれなりにあるだろうし、やはり言語化や共有はできれば良いと改めて感じた。

もちろん状況次第なところもあるし、この考え方をまわりに強制するものではないが、個人的にはオトクだと感じているので今後もそのような動きができると良いな〜と思う。


もしこの記事をご覧になった方で「こんな考え方もあるのでは?」というご意見などあれば、何かしらの手段でご教示いただけるとありがたいです。

PHPStanの実行結果をreviewdogとGitHub Actionsを用いてPRにレビューコメントできるようにする

PHPStanというPHPの静的解析ツールがある。

github.com

歴史あるPHPプロダクトにこれを導入する場合、解析レベルを低めに設定したとしても、既存コードで多くのエラーが出ることがある。もちろんそれらのエラーを潰していくことも大事だが、ひとまず小さく導入していくために「PRの追加分に対してのみPHPStanの実行結果が分かる状態にする&CIは落とさない」ことを目指していきたい。

今回はこれを実現するためにreviewdogとGitHub Actionsを使う。

github.com

設定は簡単で、こんな感じでやれば良い。

name: ci

on: pull_request

jobs:
  reviewdog:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - uses: reviewdog/action-setup@v1
        with:
          reviewdog_version: latest

      - name: setup composer
        run: composer install

      - name: static code analysis
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: ./vendor/bin/phpstan analyse --no-progress --error-format=raw | reviewdog -f=phpstan -reporter=github-pr-review

そうすると、よろしくないコードを書くとこんな感じで自動でレビューコメントしてくれる。

f:id:purple_jwl:20210606222235p:plain

PRの追加分に対してのみレビューコメントがつくし、CIは落とさないので、導入の段階でPHPStanの解析レベルを高めに設定することも可能になる。

結果として、漸進的な改善に繋がったり、型を意識した開発をする習慣がついたりするだろう。また、チーム開発においては、自動でレビューコメントがつく分、レビュアー(人間)のレビューコストが下がり、開発効率の向上も見込めると思う。

PHP-Parser入門した

最近「PHPのコードをパースしていい感じにしたい」ということがあった。その時にPHP-Parserについて調べたので、使い方などを軽くメモしておく。

PHP-Parserとは

このライブラリのこと。

github.com

PHPのコードを静的解析して抽象構文木(AST)を生成し、そのASTに対して任意の操作を行ったり、PHPのコードに戻したりできる。ASTについてはインターネット上にいろんな記事があるのでそちらを参照してほしい。

使用例

【注意】以下のサンプルコードは、執筆時点で最新のv4.10.4を使用したものである。

PHPのコードをASTにして、何もせずPHPのコードに戻す

<?php

require_once __DIR__ . '/vendor/autoload.php';

use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter\Standard;

$code = file_get_contents('./Hoge.php');

$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$ast = $parser->parse($code);

$printer = new Standard;
$result = $printer->prettyPrintFile($ast);

var_dump($result);

この例だと、空行が削除されるなどの余計なフォーマット処理が走ってしまう。v4.0からそれを回避する機能が実験的に追加されたようで、以下のように書き換えできる。

<?php

require_once __DIR__ . '/vendor/autoload.php';

use PhpParser\Lexer;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor;
use PhpParser\Parser;
use PhpParser\PrettyPrinter\Standard;

$code = file_get_contents('./Hoge.php');

$lexer = new Lexer\Emulative([
    'usedAttributes' => [
        'comments',
        'startLine', 'endLine',
        'startTokenPos', 'endTokenPos',
    ],
    'phpVersion' => Lexer\Emulative::PHP_7_4,
]);
$parser = new Parser\Php7($lexer);

$oldStmts = $parser->parse($code);
$oldTokens = $lexer->getTokens();

$traverser = new NodeTraverser;
$traverser->addVisitor(new NodeVisitor\CloningVisitor);
$newStmts = $traverser->traverse($oldStmts);

$printer = new Standard;
$result = $printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens);

var_dump($result);

ref: https://github.com/nikic/PHP-Parser/blob/master/doc/component/Pretty_printing.markdown#formatting-preserving-pretty-printing

PHPのコードをASTにして、一部コードを書き換えてからPHPのコードに戻す

例えば、こんなコード(Hoge.php)があるとする。

<?php

class Hoge
{
    public function getMessage() : string
    {
        return 'hoge';
    }
}

このgetMessageの戻り値をhogeからpoyoに変えたい場合は、以下のようなコードを用意すれば良い。

<?php

require_once __DIR__ . '/vendor/autoload.php';

use PhpParser\Lexer;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor;
use PhpParser\NodeVisitorAbstract;
use PhpParser\Parser;
use PhpParser\PrettyPrinter\Standard;

$code = file_get_contents('./Hoge.php');

$lexer = new Lexer\Emulative([
    'usedAttributes' => [
        'comments',
        'startLine', 'endLine',
        'startTokenPos', 'endTokenPos',
    ],
    'phpVersion' => Lexer\Emulative::PHP_7_4,
]);
$parser = new Parser\Php7($lexer);

$oldStmts = $parser->parse($code);
$oldTokens = $lexer->getTokens();

$traverser = new NodeTraverser;
$traverser->addVisitor(new NodeVisitor\CloningVisitor);
$newStmts = $traverser->traverse($oldStmts);

$traverser = new NodeTraverser;
$traverser->addVisitor(new class extends NodeVisitorAbstract {
    public function enterNode(Node $node)
    {
        if ($node instanceof Node\Stmt\Return_ &&
            $node->expr instanceof Node\Scalar\String_ &&
            $node->expr->value === 'hoge'
        ) {
            $node->expr->value = 'poyo';
        }
    }
});
$newStmts = $traverser->traverse($newStmts);

$printer = new Standard;
$result = $printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens);

var_dump($result);

本質は$traverser->addVisitor(new class ...で処理を差し込んでいる部分のみ。NodeTraverserを用いることでノードの走査中に実行したい処理を差し込むことができる。処理の実行タイミングは4種類あり、enterNodeはその一種。

ref: https://github.com/nikic/PHP-Parser/blob/master/doc/component/Walking_the_AST.markdown#node-visitors

2回に分けて走査しているが、これは1回目で各ノードの初期状態を複製しておくため。最後のコード出力のprintFormatPreservingで必要。

さいごに

PHP-Parserで最初にやりそうなことを使用例と共にメモしてみた。ここで書いた内容が何となく分かれば、あとは公式ドキュメントを見ながらいろいろ試せるようになると思う。

tblsによるデータベースドキュメントの更新をGitHub Actionsで自動化してみる

tblsという非常に便利なデータベースドキュメント生成ツールがあり、よく利用させてもらっている。「tbls is 何?」という人は各自で調べてみてほしい。

github.com

これまでドキュメントを更新するときは、ローカルでtbls doc -fして、差分をpushしていた。しかし、これではドキュメントの更新し忘れが発生するため、自動化したいと思い、GitHub Actionsと組み合わせてみることにした。

name: ci

on: push

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      database:
        image: mysql:5.7
        ports:
          - 3306:3306
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
          MYSQL_DATABASE: "testdb"
        options: --health-cmd "mysqladmin ping -h localhost" --health-interval 10s --health-timeout 5s --health-retries 5

    steps:
      - uses: actions/checkout@v2

      - name: setup git
        run: |
          git config --local user.email "メールアドレス"
          git config --local user.name "ユーザー名"

      - name: setup db
        run: |
          mysql -uroot -h127.0.0.1 -P3306 testdb < schema.sql

      - name: tbls
        run: |
          curl -sL https://git.io/use-tbls > /tmp/use-tbls.tmp && . /tmp/use-tbls.tmp
          tbls diff || (tbls doc -f && git add . && git commit -m 'auto commit' && git push)

こんな感じ。メインの処理は下2行のみ。公式のREADMEに書いてあるような方法でtblsコマンドを使える状態にし、tbls diffで差分が出ていればドキュメントを再生成してコミットとプッシュをするようにしている。

やっている事自体はとてもシンプルなんだけど、このような自動化によってドキュメントの更新し忘れを防ぐことができるので便利。

今回はtblsについての話だったが、例えば、フォーマッター関連処理も同じような感じで自動化しても良いかもしれない。