PHPの自動コード検証

Johannes Schmitt によるPHPの自動コードレビューの記事の翻訳を紹介します。 個人的に、彼女は私が彼女のアプリケーションを開発し、テストするプロセスで少し違った見方をするのを助けました。 そして、少なくとも著者のテストに対する独自のアプローチは注目に値します。

あなたも興味があるなら、猫へようこそ。



Trevisが登場してから、継続的な統合をすべてのPHPプロジェクトに即座に統合できます。 これにより、コードの品質が向上するだけでなく、プルリクエストでアセンブリに関する情報を直接提供することでライブラリのサポートが大幅に簡素化され、フィードバックの受信時間が短縮されます。 Travisは非常に優れていますが、他のテストツールと同様に、遺伝性疾患に苦しんでいます。何かをするには、テストが必要です。 正直なところ、100%のテストやそれに近いテストでカバーされている単一のプロジェクトがないことに賭ける準備をしてください。 私はまだあなたがテストを書くことを望んでいます。



ご存知かもしれませんが、私はSymfony2およびスタンドアロンPHPライブラリのかなりの数のバンドルをサポートしています。 そして、コミュニティのおかげで(みんなありがとう、それを維持してください)、私はリポジトリで常に更新リクエストを受け取ります。 いくつかのクエリは完全に役に立たず、いくつかは注意に値し、いくつかはメインブランチに追加できます。 しかし、リクエストをどれだけ慎重にチェックしても、時々、機能しない、または機能しないが、常にではない何かが追加されることがあります。



数ヶ月前、私はこの状況を変えようとしましたが、アイデアは非常にシンプルでした。更新リクエストのコードをチェックし、フィードバックを提供するシステムを作成しました。 私はすぐにプロトタイプを作成し、いくつかの簡単なチェックを追加しました。 次に、たとえばメソッドを呼び出すことができるかどうかを確認するなど、より複雑なものを追加したいと考えました。 このようなチェックの利点を理解するには、次の例を見てください。

<?php class UserProvider { /** @return User|null */ public function loadUser($username) { /** ... */ } public function refreshUser(User $user) { if (null === $user = $this->loadUser($user->getUsername())) { throw new RuntimeException( sprintf('User "%s" was not found.', $user->getUsername())); } return $user; } }
      
      





したがって、 refreshUserメソッドは、 loadUserメソッドを使用してUserクラスのオブジェクトを受け取り、このオブジェクトを返します。 そして、オブジェクトが見つからない場合、例外をスローします。 すべてがシンプルに思えますが、本当にそうですか? そして、私がこれについて尋ねると、明らかにそうではなく、あなたの多くはすでに間違いに気づいています。 if $ userブロックの内部はnullであり、 getUserNameメソッド呼び出すことはできません。 このようなエラーを見つけるために、いくつかの簡単な解決策を試しましたが、非常に特定の場合にのみ機能することがすぐに明らかになりました。 もっと良いものが必要でした。



PHPコードの型推論


データ フロー、制御フロー、および抽象解釈の概念深く掘り下げて調査しました。 それ自体はかなり複雑に見え、この記事の範囲外です。 しかし、ほんのいくつかの例を挙げて、これらの概念の一般的な考え方を説明しましょう。



制御フロー分析


このスレッドの分析により、コードのさまざまなブロックが実行される順序を決定できます。

 <?php function fooBar($i) { if ($i > 0) { echo 'foo'; } else { echo 'bar'; } }
      
      





このコードの場合、制御フローは次のようになります。



ifから始めて、「foor」または「bar」に進み、最後に終了します。 それ自体では、これが私たちにとって何の助けにもなりそうにありませんが、次のステップの基礎として役立ちます。



データフロー分析


データフローの分析により、制御フローの分析で決定したスキームに従って移動中に実行コンテキストがどのように変化するかを判断できます。

 <?php $x = null; // $x  null. if ($y) { $x = new DateTime(); $x->format(); //   ,     $x   DateTime } else { $x = 0; } $x->format(); // $x  DateTime   "integer",     //        
      
      





コードの順序がわからない場合、$ xはnull、数値、またはDateTimeであると結論付けることができます。 ただし、これはformatメソッドを呼び出すことができるかどうかを確認するのに役立ちません。



抽象解釈


私たちの場合、この概念は「条件式の結果を知っている場合、どのような仮定を立てることができるか」という質問に要約されます。 別の例を見てみましょう。

 <?php class Foo { private $logger; public function __construct(Logger $logger = null) { $this->logger = $logger; } public function doSth() { if (null !== $this->logger) { $this->logger->log('doing sth'); } } }
      
      





この場合、「条件式」はヌルです!== $ this->ロガー。 この条件が当てはまる場合、質問は次のように言い換えることができます:「式null!== $ this-> loggerがtrueの場合、$ this-> loggerについてどのような仮定を立てることができますか?」 nullまたはロガーの場合があります。 しかし、抽象解釈のおかげで、ifブロック内で$ this-> loggerが常にLoggerのインスタンスになるため、メソッドを呼び出すことができます。



自動検証システム



このすべての用途は何ですか、あなたは尋ねます。 記事の冒頭で、私の目標は自動コード検証システムを作成することだと言いました。 そして今、それは広く使われ議論する準備ができていると思います。 Zend Framework 2、Symfony2、Doctrine、Propelなど、主要なPHPライブラリをシステムでテストしました。 使用および構成できる100を超える検証ルールが含まれています。 GithubにPHPプロジェクトがある場合は、簡単に試すことができます。 http://jmsyst.com/automated-code-reviewsにログインして、必要なリポジトリを選択するだけです。 気に入らなければ、いつでもオフにすることができます。



PHPプログラマーがコードの品質についてあまり真剣でないと誰かが言ったら、私に送ってください。



All Articles