CodeIgniter DevKit で php-cs-fixer を使いコーディングスタイルを修正する

この記事は CodeIgniter Advent Calendar 2022 - Qiita の19日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

CodeIgniter DevKit とは?

CodeIgniterのライブラリとプロジェクトのための開発用のツールキットです。

以下のツール(主なもの)が含まれています。

  • php-cs-fixer (CodeIgniter4のコーディング標準)
  • PHPStan
  • Psalm
  • Deptrac
  • Infection
  • Rector

DevKit のインストール

Composerからインストールします。

minimum-stabilitydev に変更します。

$ composer config minimum-stability dev
phpstan/extension-installer contains a Composer plugin which is currently not in your allow-plugins config. See https://getcomposer.org/allow-plugins
Do you trust "phpstan/extension-installer" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [y,n,d,?] y

prefer-stabletrue に変更します。

$ composer config prefer-stable true

(2022-12-21 追記) DevKit v1.1.0 が少し古くなっていたため、インストールするバージョンを dev-develop に変更しました。

インストールします。

$ composer require --dev codeigniter4/devkit:dev-develop
./composer.json has been updated
Running composer update codeigniter4/devkit
Loading composer repositories with package information
Info from https://repo.packagist.org: #StandWithUkraine
Updating dependencies
Lock file operations: 51 installs, 0 updates, 0 removals
  - Locking amphp/amp (v2.6.2)
  - Locking amphp/byte-stream (v1.8.1)
  - Locking codeigniter/coding-standard (v1.7.0)
  - Locking codeigniter4/devkit (dev-develop c4726ff)
  - Locking composer/package-versions-deprecated (1.11.99.5)
  - Locking composer/pcre (3.1.0)
  - Locking composer/semver (3.3.2)
  - Locking composer/xdebug-handler (3.0.3)
  - Locking dnoegel/php-xdg-base-dir (v0.1.1)
  - Locking doctrine/annotations (1.14.2)
  - Locking doctrine/deprecations (v1.0.0)
  - Locking doctrine/lexer (2.1.0)
  - Locking fakerphp/faker (v1.21.0)
  - Locking felixfbecker/advanced-json-rpc (v3.2.1)
  - Locking felixfbecker/language-server-protocol (v1.5.2)
  - Locking fidry/cpu-core-counter (0.4.1)
  - Locking friendsofphp/php-cs-fixer (v3.13.1)
  - Locking netresearch/jsonmapper (v4.1.0)
  - Locking nexusphp/cs-config (v3.8.0)
  - Locking nexusphp/tachycardia (v1.4.0)
  - Locking phpdocumentor/reflection-common (2.2.0)
  - Locking phpdocumentor/reflection-docblock (5.3.0)
  - Locking phpdocumentor/type-resolver (1.6.2)
  - Locking phpstan/extension-installer (1.2.0)
  - Locking phpstan/phpstan (1.9.4)
  - Locking phpstan/phpstan-deprecation-rules (1.1.1)
  - Locking phpstan/phpstan-phpunit (1.3.2)
  - Locking psr/cache (3.0.0)
  - Locking psr/container (2.0.2)
  - Locking psr/event-dispatcher (1.0.0)
  - Locking roave/security-advisories (dev-latest ae56b09)
  - Locking spatie/array-to-xml (2.17.0)
  - Locking symfony/console (v6.2.2)
  - Locking symfony/deprecation-contracts (v3.2.0)
  - Locking symfony/event-dispatcher (v6.2.2)
  - Locking symfony/event-dispatcher-contracts (v3.2.0)
  - Locking symfony/filesystem (v6.2.0)
  - Locking symfony/finder (v6.2.0)
  - Locking symfony/options-resolver (v6.2.0)
  - Locking symfony/polyfill-ctype (v1.27.0)
  - Locking symfony/polyfill-intl-grapheme (v1.27.0)
  - Locking symfony/polyfill-intl-normalizer (v1.27.0)
  - Locking symfony/polyfill-mbstring (v1.27.0)
  - Locking symfony/polyfill-php80 (v1.27.0)
  - Locking symfony/polyfill-php81 (v1.27.0)
  - Locking symfony/process (v6.2.0)
  - Locking symfony/service-contracts (v3.2.0)
  - Locking symfony/stopwatch (v6.2.0)
  - Locking symfony/string (v6.2.2)
  - Locking vimeo/psalm (5.4.0)
  - Locking webmozart/assert (1.11.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 2 installs, 3 updates, 3 removals
  - Downloading codeigniter4/devkit (dev-develop c4726ff)
  - Removing webmozart/path-util (2.3.0)
  - Removing rector/rector (0.15.1)
  - Removing openlss/lib-array2xml (1.0.0)
  - Installing spatie/array-to-xml (2.17.0): Extracting archive
  - Installing fidry/cpu-core-counter (0.4.1): Extracting archive
  - Upgrading vimeo/psalm (4.30.0 => 5.4.0): Extracting archive
  - Downgrading phpunit/php-code-coverage (9.2.22 => 9.2.21): Extracting archive
  - Upgrading codeigniter4/devkit (v1.1.0 => dev-develop c4726ff): Extracting archive
6 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
58 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
phpstan/extension-installer: Extensions installed
infection/extension-installer: No extensions found
Generating autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
Generated autoload files
No security vulnerability advisories found

インストールされました。

インストールしただけでは特に何も動きません。

vendor/codeigniter4/devkit/src/Template 以下に設定ファイルのテンプレートがあるので、 それをプロジェクトにコピーして設定を調整します。

php-cs-fixer の設定

それでは、php-cs-fixer の設定をします。

vendor/codeigniter4/devkit/src/Template/.php-cs-fixer.dist.php をプロジェクトルートにコピーします。

そして、コピーした .php-cs-fixer.dist.php をプロジェクトに合わせて調整します。

<?php

declare(strict_types=1);

use CodeIgniter\CodingStandard\CodeIgniter4;
use Nexus\CsConfig\Factory;
use PhpCsFixer\Finder;

$finder = Finder::create()
    ->files()
    ->in([
        __DIR__ . '/app/',
        __DIR__ . '/tests/',
    ])
    ->exclude([
        'build',
        'Views',
    ])
    ->append([
        __FILE__,
        __DIR__ . '/rector.php',
    ]);

$overrides = [
    'declare_strict_types' => true,
    'void_return'          => true,
];

$options = [
    'finder'    => $finder,
    'cacheFile' => 'build/.php-cs-fixer.cache',
];

return Factory::create(new CodeIgniter4(), $overrides, $options)->forProjects();

ここでは、上記のようにしました。

なお、php-cs-fixer の設定については、 https://mlocati.github.io/php-cs-fixer-configurator/ を見るとわかります。 気に入らない設定があれば、$overrides に設定して変更してください。

CodeIgniter4の設定については、 https://github.com/CodeIgniter/coding-standard/blob/master/src/CodeIgniter4.php で確認できます。

composer.json の設定

実行するためのコマンドを composer.json に追加します。

--- a/composer.json
+++ b/composer.json
@@ -23,7 +23,10 @@
        "test": "phpunit",
        "post-update-cmd": [
            "@composer dump-autoload"
-       ]
+       ],
+       "cs": "php-cs-fixer fix --ansi --verbose --dry-run --diff",
+       "cs-fix": "php-cs-fixer fix --ansi --verbose --diff --using-cache=yes",
+       "style": "@cs-fix"
    },
    "support": {
        "forum": "http://forum.codeigniter.com/",

これで、composer cs-fix でソースコードのコーディングスタイルが修正されます。

コーディングスタイルの修正

それではコーディングスタイルを修正してみましょう。

$ composer cs-fix
> php-cs-fixer fix --ansi --verbose --diff --using-cache=yes
PHP CS Fixer 3.13.1 Oliva by Fabien Potencier and Dariusz Ruminski.
PHP runtime: 8.1.13
Loaded config CodeIgniter4 Coding Standards from "/.../codeigniter4login/.php-cs-fixer.dist.php".
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.
Legend: .-no changes, F-fixed, S-skipped (cached or empty file), I-invalid file syntax (file ignored), E-error
   1) /.../codeigniter4login/app/Filters/Noauth.php (single_space_after_construct, braces, void_return, declare_strict_types, blank_line_after_opening_tag, single_line_comment_spacing, single_blank_line_before_namespace, ordered_imports)
      ---------- begin diff ----------
--- /.../codeigniter4login/app/Filters/Noauth.php
+++ /.../codeigniter4login/app/Filters/Noauth.php
@@ -1,8 +1,12 @@
-<?php namespace App\Filters;
+<?php

+declare(strict_types=1);
+
+namespace App\Filters;
+
+use CodeIgniter\Filters\FilterInterface;
 use CodeIgniter\HTTP\RequestInterface;
 use CodeIgniter\HTTP\ResponseInterface;
-use CodeIgniter\Filters\FilterInterface;

 class Noauth implements FilterInterface
 {
@@ -9,15 +13,14 @@
     public function before(RequestInterface $request, $arguments = null)
     {
         // Do something here
-        if(session()->get('isLoggedIn')){
-          return redirect()->to('/dashboard');
+        if (session()->get('isLoggedIn')) {
+            return redirect()->to('/dashboard');
         }
-
     }

-    //--------------------------------------------------------------------
+    // --------------------------------------------------------------------

-    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
+    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null): void
     {
         // Do something here
     }

      ----------- end diff -----------

...

      ----------- end diff -----------


Fixed 98 of 100 files in 2.825 seconds, 16.000 MB memory used

上記のように修正された差分が表示されました。

問題がなければ、Gitでcommitしてください。

試しに実行した結果を GitHub にアップしておきました。

まとめ

  • CodeIgniter DevKit はCodeIgniterのライブラリとプロジェクトのための開発用のツールキットです。
  • DevKit には php-cs-fixer が含まれており、簡単にCodeIgniter4のコーディングスタイルにコードを修正できます。
  • CodeIgniter4のコーディングスタイルは PSR-12 準拠です。

CodeIgniter DevKit で Rector を使いコードを修正する」 へ続く。

この記事は CodeIgniter Advent Calendar 2022 - Qiita の19日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

参考

Tags: codeigniter, codeigniter4

CodeIgniter4の設定クラスについて

この記事は CodeIgniter Advent Calendar 2022 - Qiita の18日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

CodeIgniter4の初期設定については、 「CodeIgniter 4.2の初期設定」 を参照してください。

設定ファイル

CodeIgniter4アプリの設定ファイルは、app/Config/ 以下に配置します。

設定ファイルは CodeIgniter\Config\BaseConfig を継承したクラスです。

名前空間は、App\Config ではなく Config です。

設定ファイルの作成

CodeIgniter\Config\BaseConfig を継承したクラスを app/Config/ に追加するだけです。

プロパティは public に設定します。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class Site extends BaseConfig
{
    public $siteName  = 'My Awesome Site';
    public $siteEmail = 'webmaster@example.jp';
    // ...
}

設定ファイルの読み込み

クラスなので new すればインスタンス化できて値を取得できます。

<?php

$siteConfig = new \Config\Site();

echo $siteConfig->siteName;

また、フレームワークの提供する config() 関数を使うこともできます。

<?php

$siteConfig = config('Site');

echo $siteConfig->siteName;

config() 関数を使った場合は、最初に作成されたインスタンスが共有されます。

なお、モジュールを使用する場合の細かい話(preferAppなど)は、 「CodeIgniter4のモジュール(HMVC)②」 「CodeIgniter4のモジュール(HMVC)内の設定ファイルとヘルパーの呼び出し方」 を参照してください。

環境変数による値の書き換え

設定クラスは、インスタンス化時に環境変数をチェックして、対応するプロパティの値を自動的に書き換えます。

つまり、設定クラスのファイルを見ても、実際のプロパティの値はわかりません。 環境変数の値で変更されている可能性があります。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class Site extends BaseConfig
{
    public $siteName  = 'My Awesome Site';
    public $siteEmail = 'webmaster@example.jp';
    // ...
}

上記の場合、環境変数site.siteNameが存在すれば、その値に変更されます。

区切り文字はドットではなくアンダースコアでもOKです。 つまり、site_siteNameでも構いません。

なお、すでに存在する項目の値を変更することしかできません。

設定値が配列だった場合、既存のキーの値は変更できますが、新たにキーを追加することはできません。 プロパティを追加することもできません。

レジストラのよる値の変更

モジュールからアプリの設定を変更したい場合があります。

例えば、モジュールが提供するフィルターを追加したり、バリデーションルールを追加したい場合です。

そのような場合は、モジュールに Config/Registrar.php を作成することで、既存の設定を変更できます。

例えば、以下のコードは、フィルタとバリデーションルール、デバッグツールバーのコレクターを追加します。

<?php

declare(strict_types=1);

namespace CodeIgniter\Shield\Config;

use CodeIgniter\Shield\Authentication\Passwords\ValidationRules as PasswordRules;
use CodeIgniter\Shield\Collectors\Auth;
use CodeIgniter\Shield\Filters\AuthRates;
use CodeIgniter\Shield\Filters\ChainAuth;
use CodeIgniter\Shield\Filters\GroupFilter;
use CodeIgniter\Shield\Filters\PermissionFilter;
use CodeIgniter\Shield\Filters\SessionAuth;
use CodeIgniter\Shield\Filters\TokenAuth;

class Registrar
{
    /**
     * Registers the Shield filters.
     */
    public static function Filters(): array
    {
        return [
            'aliases' => [
                'session'    => SessionAuth::class,
                'tokens'     => TokenAuth::class,
                'chain'      => ChainAuth::class,
                'auth-rates' => AuthRates::class,
                'group'      => GroupFilter::class,
                'permission' => PermissionFilter::class,
            ],
        ];
    }

    public static function Validation(): array
    {
        return [
            'ruleSets' => [
                PasswordRules::class,
            ],
        ];
    }

    public static function Toolbar(): array
    {
        return [
            'collectors' => [
                Auth::class,
            ],
        ];
    }
}

レジストラは、メソッド名に設定クラス名を指定し、プロパティ名をキーにした配列を返します。

すると、設定クラスのインスタンス化時に、その配列の値がマージされます。

レジストラを使うと、アプリの設定ファイルを変更することなく、そのモジュールの機能を追加できます。

なお、レジストラはオートディスカバリーの設定でオフに変更できます。

この記事は CodeIgniter Advent Calendar 2022 - Qiita の18日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

関連

参考

Tags: codeigniter, codeigniter4

CodeIgniter 4.2の初期設定

この記事は CodeIgniter Advent Calendar 2022 - Qiita の17日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

設定ファイル

CodeIgniter4 の設定ファイルは、プロジェクトのルートに配置する .env ファイルと、app/Config/ 以下の設定ファイルがあります。

.env ファイル

.env はインストール直後には存在せず、サンプルとしての env ファイルが用意されています。env.env にコピーすれば OK です。

  • .env は環境変数を設定するためのものです。開発環境固有の設定値(データベース接続情報など)を設定します。
  • .env は Git などのバージョン管理に含めてはいけません。
  • .env はすでに定義されている環境変数は変更しません。
  • CodeIgniter4 の設定クラスがインスタンス化される時に、対応する環境変数があるとその値がプロパティに自動的に設定されます。

app/Config 設定ファイル

app/Config/ 以下に設定ファイルがあります。

設定ファイルはクラスです。

名前空間は、App\Config ではなく Config です。

.env ファイルでの設定

まず、.env ファイルを作成します。

$ cp env .env

.env ファイルで環境変数を設定します。

まず、開発環境を development に変更します。これで開発モードになります。

-# CI_ENVIRONMENT = production
+CI_ENVIRONMENT = development

baseURL を設定します。

-# app.baseURL = ''
+app.baseURL = 'http://localhost:8080/'

データベース接続設定をします。

-# database.default.hostname = localhost
-# database.default.database = ci4
-# database.default.username = root
-# database.default.password = root
-# database.default.DBDriver = MySQLi
-# database.default.DBPrefix =
+database.default.hostname = localhost
+database.default.database = ci4app
+database.default.username = dbuser
+database.default.password = dbpassword
+database.default.DBDriver = MySQLi
+database.default.DBPrefix =

-# database.tests.hostname = localhost
-# database.tests.database = ci4
-# database.tests.username = root
-# database.tests.password = root
-# database.tests.DBDriver = MySQLi
-# database.tests.DBPrefix =
+database.tests.hostname = localhost
+database.tests.database = ci4app_test
+database.tests.username = dbuser
+database.tests.password = dbpassword
+database.tests.DBDriver = MySQLi
+database.tests.DBPrefix =

開発環境なので、ログを出力するレベルを上げておきます。

-# logger.threshold = 4
+logger.threshold = 9

app/Config 設定ファイルでの設定

app/Config 以下の設定ファイルを設定します。

多くのプロジェクトで変更するであろう設定は以下になります。

app/Config/App.php

CodeIgniter が生成するリンクのURLから index.php を削除します。

--- a/app/Config/App.php
+++ b/app/Config/App.php
@@ -37,7 +37,7 @@ class App extends BaseConfig
      *
      * @var string
      */
-    public $indexPage = 'index.php';
+    public $indexPage = '';

     /**
      * --------------------------------------------------------------------------

ロケールを ja に変更します。

@@ -70,7 +70,7 @@ class App extends BaseConfig
      *
      * @var string
      */
-    public $defaultLocale = 'en';
+    public $defaultLocale = 'ja';

     /**
      * --------------------------------------------------------------------------

なお、システムメッセージを日本語にするには、別途、codeigniter4/translations のインストールが必要です。 CodeIgniter4のシステムメッセージを日本語にする を参照してください。

サポートするロケールに ja を追加します。

@@ -97,7 +97 ,7 @@ class App extends BaseConfig
      *
      * @var string[]
      */
-    public $supportedLocales = ['en'];
+    public $supportedLocales = ['en', 'ja'];

     /**
      * --------------------------------------------------------------------------

タイムゾーンを日本時間に変更します。

@@ -109,7 +109,7 @@ class App extends BaseConfig
      *
      * @var string
      */
-    public $appTimezone = 'America/Chicago';
+    public $appTimezone = 'Asia/Tokyo';

     /**
      * --------------------------------------------------------------------------

app/Config/CURLRequest.php

CURLRequest は同じインスタンスでリクエストを複数回送信すると、前回のリクエスト時の option が全て共有されるという仕様がデフォルトなので、これを無効にします。

--- a/app/Config/CURLRequest.php
+++ b/app/Config/CURLRequest.php
@@ -18,5 +18,5 @@ class CURLRequest extends BaseConfig
      *
      * @var bool
      */
-    public $shareOptions = true;
+    public $shareOptions = false;
 }

app/Config/Security.php

デフォルトの CSRF 保護は Cookie ベースなので、これをより安全な Session ベースに変更します。

--- a/app/Config/Security.php
+++ b/app/Config/Security.php
@@ -15,7 +15,7 @@ class Security extends BaseConfig
      *
      * @var string 'cookie' or 'session'
      */
-    public $csrfProtection = 'cookie';
+    public $csrfProtection = 'session';

     /**
      * --------------------------------------------------------------------------

CSRFトークンをより安全にするため、ランダム化を有効に変更します。

@@ -26,7 +26,7 @@ class Security extends BaseConfig
      *
      * @var bool
      */
-    public $tokenRandomize = false;
+    public $tokenRandomize = true;

     /**
      * --------------------------------------------------------------------------

app/Config/Filters.php

csrf, invalidchars, secureheaders フィルタを有効にします。

それぞれ、CSRF保護、不正な文字コードのチェック、セキュリティに関連するレスポンスヘッダー付加のためのフィルタです。

不要な URI があれば、除外設定してください。

--- a/app/Config/Filters.php
+++ b/app/Config/Filters.php
@@ -34,13 +34,13 @@ class Filters extends BaseConfig
     public $globals = [
         'before' => [
             // 'honeypot',
-            // 'csrf',
-            // 'invalidchars',
+            'csrf',
+            'invalidchars',
         ],
         'after' => [
             'toolbar',
             // 'honeypot',
-            // 'secureheaders',
+            'secureheaders',
         ],
     ];

app/Config/Validation.php

バリデーションクラスで「厳密なルール」を使うように変更します。なお、従来のルールは基本的にPOST配列の検証を前提としており、暗黙の型変換が行われるため、JSONデータなど bool や null、配列などの値を正しく検証できない場合があります。

--- a/app/Config/Validation.php
+++ b/app/Config/Validation.php
@@ -3,10 +3,10 @@
 namespace Config;

 use CodeIgniter\Config\BaseConfig;
-use CodeIgniter\Validation\CreditCardRules;
-use CodeIgniter\Validation\FileRules;
-use CodeIgniter\Validation\FormatRules;
-use CodeIgniter\Validation\Rules;
+use CodeIgniter\Validation\StrictRules\CreditCardRules;
+use CodeIgniter\Validation\StrictRules\FileRules;
+use CodeIgniter\Validation\StrictRules\FormatRules;
+use CodeIgniter\Validation\StrictRules\Rules;

 class Validation extends BaseConfig
 {

この記事は CodeIgniter Advent Calendar 2022 - Qiita の17日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

参考

Tags: codeigniter, codeigniter4