コーディング規約とは、視認性、保守性を一定以上にするためプログラムの書き方を定めるものです。 チーム開発する上で必要不可欠なものであり、開発チームに参加するためには標準的なコーディング規約を知っておく必要があります。
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 で、拡張機能として、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 で対応していきます。