Laravel入門 コーディング規約PSR12への対応とphp_codesniffer(第19回)

2021-01-01
8 min read

コーディング規約とは、視認性、保守性を一定以上にするためプログラムの書き方を定めるものです。 チーム開発する上で必要不可欠なものであり、開発チームに参加するためには標準的なコーディング規約を知っておく必要があります。

PHPのコーディング規約

Laravel を用いての開発で、従うべきコーディング規約は、特に定められてはいません。各々のプロジェクトが個別に定めることになります。 しかし、個別にコーディング規約を作成できるプロジェクトは大規模なプロジェクトか、歴戦の Laravel 職人を有するプロジェクトでないと難しいでしょう。 そこで、Laravel 本体が採用しているコーディング規約を取り入れます。

「Laravel 本体は PSR-2 コーディング規約と PSR-4 オートローディング規約に準拠しています」と公式ドキュメントの翻訳に記載されています。

https://readouble.com/laravel/7.x/ja/contributions.html

PSR (PHP Standards Recommendations) とは、PHP-FIG (PHP Framework Interop Group) が策定している PHP コーディング規約です。 しかし、その PHP-FIG によれば、「Deprecated - As of 2019-08-10 PSR-2 has been marked as deprecated. PSR-12 is now recommended as an alternative.」となっています。PSR-2 は廃止され PSR-12 がその代わりとなっているようです。

よって、PSR-12 を使うことになります。

コーディング規約をチェックするのは人力ではたいへんですので、ツールを使用します。 PSR-12 に対応しているツールとして、php_codesniffer というものを導入します。

php_codesnifferのインストールと設定

php_codesniffer をコンポーザーでプロジェクトにインストールします。

$ composer require --dev squizlabs/php_codesniffer

設定ファイルphpcs.xmlをプロジェクトルートに配置します。

$ touch phpcs.xml
<?xml version="1.0"?>
<ruleset name="PSR12">
    <!-- 拡張子が php のものにだけ適用 -->
    <arg name="extensions" value="php" />
    <!-- rule に PSR12 を指定可能 -->
    <rule ref="PSR12" />

    <!-- 変数名をキャメルケースに(Laravel固有ルール) -->
    <rule ref="Squiz.NamingConventions.ValidVariableName.NotCamelCaps"/>


    <!-- オプション p:進捗表示  s:エラー表示時にルールを表示 -->
    <arg value="ps" />

    <!-- 対象のファイル、ディレクトリ -->
    <file>app</file>
    <!-- 対象外のファイル、ディレクトリ -->
    <exclude-pattern>app/Http/Controllers/Controller.php</exclude-pattern>
    <exclude-pattern>app/Http/Controllers/BasicController.php</exclude-pattern>
    <exclude-pattern>app/Console/Kernel.php</exclude-pattern>

    <!-- testsディレクトリ配下はメソッド名のキャメルケースチェックを除外する -->
    <rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>
</ruleset>
補足 : VSCODEで自動フォーマット

VSCODE で、拡張機能として、PHP Sniffer & Beautifierをインストールします。 設定として、/var/www/html/.vscode/settings.jsonとファイルを作成して、下記を設定内容とします。

{
  "editor.formatOnSave": true,
  "phpsab.autoRulesetSearch": false,
  "phpsab.standard": "./todo/phpcs.xml",
  "phpsab.executablePathCS": "./todo/vendor/bin/phpcs",
  "phpsab.executablePathCBF": "./todo/vendor/bin/phpcbf",
  "phpsab.snifferMode": "onSave"
}

ファイル保存時に自動でフォーマットされます。

コーディングチェック

$./vendor/bin/phpcs --standard=phpcs.xml

コーディング規約違反があれば下記のようなものが表示されます。

E.......E......EEEEEEE. 23 / 23 (100%)



FILE: /var/www/html/todo/app/Task.php
---------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
---------------------------------------------------------------------------------------------------
 19 | ERROR | [x] Whitespace found at end of line
    |       |     (Squiz.WhiteSpace.SuperfluousWhitespace.EndLine)
---------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
---------------------------------------------------------------------------------------------------


FILE: /var/www/html/todo/app/Http/Middleware/TrimStrings.php
---------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
---------------------------------------------------------------------------------------------------
 44 | ERROR | [x] Whitespace found at end of line
    |       |     (Squiz.WhiteSpace.SuperfluousWhitespace.EndLine)
---------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
---------------------------------------------------------------------------------------------------


FILE: /var/www/html/todo/app/Http/Controllers/Auth/LoginController.php
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 22 | ERROR | [x] The first trait import statement must be declared on the first non-comment line after the class opening brace
    |       |     (PSR12.Traits.UseDeclaration.UseAfterBrace)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------


FILE: /var/www/html/todo/app/Http/Controllers/Auth/RegisterController.php
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 25 | ERROR | [x] The first trait import statement must be declared on the first non-comment line after the class opening brace
    |       |     (PSR12.Traits.UseDeclaration.UseAfterBrace)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------


FILE: /var/www/html/todo/app/Http/Controllers/Auth/VerificationController.php
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 22 | ERROR | [x] The first trait import statement must be declared on the first non-comment line after the class opening brace
    |       |     (PSR12.Traits.UseDeclaration.UseAfterBrace)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------


FILE: /var/www/html/todo/app/Http/Controllers/Auth/ForgotPasswordController.php
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 21 | ERROR | [x] The first trait import statement must be declared on the first non-comment line after the class opening brace
    |       |     (PSR12.Traits.UseDeclaration.UseAfterBrace)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------


FILE: /var/www/html/todo/app/Http/Controllers/Auth/ResetPasswordController.php
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 22 | ERROR | [x] The first trait import statement must be declared on the first non-comment line after the class opening brace
    |       |     (PSR12.Traits.UseDeclaration.UseAfterBrace)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------


FILE: /var/www/html/todo/app/Http/Controllers/Auth/ConfirmPasswordController.php
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 22 | ERROR | [x] The first trait import statement must be declared on the first non-comment line after the class opening brace
    |       |     (PSR12.Traits.UseDeclaration.UseAfterBrace)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------


FILE: /var/www/html/todo/app/Http/Controllers/TaskController.php
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 6 ERRORS AFFECTING 6 LINES
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  17 | ERROR | [x] No space found after comma in argument list (Generic.Functions.FunctionCallArgumentSpacing.NoSpaceAfterComma)
  18 | ERROR | [x] No space found after comma in argument list (Generic.Functions.FunctionCallArgumentSpacing.NoSpaceAfterComma)
  19 | ERROR | [x] Whitespace found at end of line (Squiz.WhiteSpace.SuperfluousWhitespace.EndLine)
  20 | ERROR | [x] Function closing brace must go on the next line following the body; found 1 blank lines before brace
     |       |     (PSR2.Methods.FunctionClosingBrace.SpacingBeforeClose)
  95 | ERROR | [x] Whitespace found at end of line (Squiz.WhiteSpace.SuperfluousWhitespace.EndLine)
 111 | ERROR | [x] Expected 1 space between comma and argument "$afterStatus"; 0 found (Squiz.Functions.FunctionDeclarationArgumentSpacing.NoSpaceBeforeArg)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 6 MARKED SNIFF VIOLATIONS AUTOMATICALLY
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Time: 108ms; Memory: 10MB

ソース自動修正

上記の出力結果で、[x]が付いているものは自動で修正できます。 上記の場合は、すべてのものに[x]が付いています。

以下のコマンドで修正します。

$./vendor/bin/phpcbf --standard=phpcs.xml

以下のように修正結果が表示されます。

F.......F......FFFFFFF. 23 / 23 (100%)



PHPCBF RESULT SUMMARY
-----------------------------------------------------------------------------------------------
FILE                                                                           FIXED  REMAINING
-----------------------------------------------------------------------------------------------
/var/www/html/todo/app/Task.php                                                1      0
/var/www/html/todo/app/Http/Middleware/TrimStrings.php                         1      0
/var/www/html/todo/app/Http/Controllers/Auth/LoginController.php               1      0
/var/www/html/todo/app/Http/Controllers/Auth/RegisterController.php            1      0
/var/www/html/todo/app/Http/Controllers/Auth/VerificationController.php        1      0
/var/www/html/todo/app/Http/Controllers/Auth/ForgotPasswordController.php      1      0
/var/www/html/todo/app/Http/Controllers/Auth/ResetPasswordController.php       1      0
/var/www/html/todo/app/Http/Controllers/Auth/ConfirmPasswordController.php     1      0
/var/www/html/todo/app/Http/Controllers/TaskController.php                     6      0
-----------------------------------------------------------------------------------------------
A TOTAL OF 14 ERRORS WERE FIXED IN 9 FILES
-----------------------------------------------------------------------------------------------

Time: 189ms; Memory: 10MB

Laravel 特有のコーディング規約

PSR-12 では定められていない Laravel 特有の規約や慣習というものをベストプラクティスとしてまとめられているものがあります。

https://github.com/alexeymezenin/laravel-best-practices/blob/master/japanese.md

これを土台としてプロジェクトに合わせて取捨選択してコーディング規約を作成していくのがベターです。

たとえば、「コミュニティに受け入れられた標準の Laravel ツールを使う」という章では、開発環境は Docker ではなく Homestead を使うべきとなってはいます。 しかし、少し古いです。Laravel 自身も Ver8 にてLaravel Sailという Docker ベースの開発環境を出してきました。

こういったものもあるので、取捨選択が必要となってきます。

ただ、コーディング規約の中に命名規則というものがあります。これについては、このベストプラクティスをそのまま導入した方がよいでしょう。

以下、命名規則をhttps://github.com/alexeymezenin/laravel-best-practices/blob/master/japanese.mdより抜粋

対象 規則 Good Bad
コントローラ 単数形 ArticleController ArticlesController
ルート 複数形 articles/1 article/1
名前付きルート スネークケースとドット表記 users.show_active users.show-active, show-active-users
モデル 単数形 User Users
hasOne または belongsTo 関係 単数形 articleComment articleComments, article_comment
その他すべての関係 複数形 articleComments articleComment, article_comments
テーブル 複数形 article_comments article_comment, articleComments
Pivotテーブル 単数形 モデル名のアルファベット順 article_user user_article, articles_users
テーブルカラム スネークケース モデル名は含めない meta_title MetaTitle; article_meta_title
モデルプロパティ スネークケース $model->created_at $model->createdAt
外部キー 単数形 モデル名の最後に_idをつける article_id ArticleId, id_article, articles_id
主キー - id custom_id
マイグレーション - 2017_01_01_000000_create_articles_table 2017_01_01_000000_articles
メソッド キャメルケース getAll get_all
リソースコントローラのメソッド 下記で説明 store saveArticle
テストクラスのメソッド キャメルケース testGuestCannotSeeArticle test_guest_cannot_see_article
変数 キャメルケース $articlesWithAuthor $articles_with_author
コレクション 説明的、 複数形 $activeUsers = User::active()->get() $active, $data
オブジェクト 説明的, 単数形 $activeUser = User::active()->first() $users, $obj
設定ファイルと言語ファイルのインデックス スネークケース articles_enabled ArticlesEnabled; articles-enabled
ビュー ケバブケース show-filtered.blade.php showFiltered.blade.php, show_filtered.blade.php
コンフィグ スネークケース google_calendar.php googleCalendar.php, google-calendar.php
契約 (インターフェイス) 形容詞または名詞 AuthenticationInterface Authenticatable, IAuthentication
Trait 形容詞 Notifiable NotificationTrait

しかし、これを絶対唯一の正解と考えない事です。プロジェクトによって多少異なってきます。 上記えは、ビューのファイル名の命名規則は、ケバブケースでshow-filtered.blade.phpとなっています。一方、別のベストプラクティス https://www.laravelbestpractices.com/ では、show_filtered.blade.php とかかれており、スネークケースです。

リソースコントローラのメソッド

現状のルートを表示するコマンドが下記になります。

$ php artisan route:list

tasks に関係するものは下記になります。

項目番号 Method URI Name Action Middleware
1 POST tasks task.submit App\Http\Controllers\TaskController@store web
2 GET tasks/create task.new App\Http\Controllers\TaskController@create web
3 GET tasks/{id} task.edit App\Http\Controllers\TaskController@edit web
4 PUT tasks/{id} task.update App\Http\Controllers\TaskController@update web
5 DELETE tasks/{id} task.delete App\Http\Controllers\TaskController@destroy web
6 GET tasks/{id}/status/{afterstatus} task.updateStatus App\Http\Controllers\TaskController@updateStatus web
7 GET tabindex/{tabindex} tasklist App\Http\Controllers\TaskController@index web
8 GET / home App\Http\Controllers\TaskController@index web

Laravel の標準的なルートは下記になります。 https://laravel.com/docs/master/controllers#resource-controllers

項目番号 Verb URI Action Route Name
A GET /photos index photos.index
B GET /photos/create create photos.create
C POST /photos store photos.store
D GET /photos/{photo} show photos.show
E GET /photos/{photo}/edit edit photos.edit
F PUT/PATCH /photos/{photo} update photos.update
G DELETE /photos/{photo} destroy photos.destroy

現状のルートの命名と Larave の標準的なものとを比較します。

項番1 新規登録

1 に該当するのが C になります。 name が task.submit としていますが、tasks.store の方が望ましいようです。 修正します。

項番2 新規登録表示

2 に該当するのが B になります。 name が task.new としていますが、tasks.create の方が望ましいようです。 修正します。

項番3 編集表示

3 に該当するのが E になります。 URI が、tasks/{id}としていますが、/tasks/{id}}/editの方が望ましいようです。 修正します。

項番4 更新

4 に該当するのが F になります。 name が task.update としていますが、tasks.update の方が望ましいようです。 修正します。

項番5 削除

5 に該当するのが G になります。

name が task.delete としていますが、tasks.destroy の方が望ましいようです。 修正します。

項番6 ステータス更新

6 に該当するのはありません。 しかし、name が task.updateStatus としていますが、tasks.updateStatus の方が望ましく思われます。 修正します。

項番7 タブ切り替え

リソースに対するものではないので、該当するものはありません。

項番8 index

8 に該当するのが A になります。 しかし、ここでは tasks というリソースを表示するというよりもホーム画面に戻るという意味が強いのでこのままにします。

その他

テストコードにて、URI を直接指定している箇所があります。Route Name を使用するように修正します。

修正後のテスト

フューチャーテストを一括で行います。

$ ./vendor/bin/phpunit tests/Feature/

エラーがある場合は、個別にテストを実行してエラー修正してください。

個別テストOK。しかし全体一括NGの場合

個別にテストを実行していくと、テストに合格するが、一括でテストをするとエラーとなる場合があります。 PHPUnit の設定を修正する必要があります。

プロジェクト直下のphpunit.xmlを修正します。 PHPUnit の項目に、processIsolation=“true” を追加してください。

<phpunit 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" 
  processIsolation="true"
  bootstrap="vendor/autoload.php" 
  colors="true">

詳細なエラー表示

TaskTabChangeTest クラスの testReadyListOnly()メソッドのエラーを詳細に知りたい場合は、下記のようにメソッドの頭に$this->withoutExceptionHandling();を追加してください。

class TaskTabChangeTest extends TestCase
{
    use RefreshDatabase;

    public function testReadyListOnly()
    {
        $this->withoutExceptionHandling();

メソッドを指定してテストを実行するには、--filterを使用します。

./vendor/bin/phpunit tests/Feature/TaskTabChangeTest.php --filter 'testReadyListOnly'

ブラウザテストを一括で行います。

$ php artisan dusk

Tests\Browser\TodoListsTest::testExample がエラーになります。 これは、役目が終わったテストコードですので削除してください。

まとめ

コーディング規約以外にも、静的解析ツール・PHPDoc の規約・コード補完のためのツールなど取り入れるべきものがあります。 それについてはシーズン 2 で対応していきます。