DRY原則とは1つの知識を1箇所で明確に表現すること

DRY原則とは?

『The Pragmatic Programmer: From Journeyman to Master』(1999年)(翻訳『達人プログラマー』2000年)で最初に言及された原則です。

定義としては以下です。

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. https://en.wikipedia.org/wiki/Don%27t_repeat_yourself

知識のあらゆる部分はそのシステムにおいて単一で、曖昧さのない、信頼できる表現でなくてはならない。 https://www.infoq.com/jp/news/2012/05/DRY-code-duplication-coupling/

要するに、「1つの知識は、システム内で1箇所で明確に表現されないといけない」という原則です。

知識とは何でしょうか?コンセプト、概念と説明されることもあります。

DRY原則のよくある誤解

コードの重複ではない

DRY原則は、コードの重複とは説明されていません。コードの重複はDRY原則に違反している可能性が高いですが、違反していないかも知れません。

コードだけに限らない

コードに限られてたものではありません。

『達人プログラマー』の著者の Dave Thomas も以下のように説明しています。

A system's knowledge is far broader than just its code. It refers to database schemas, test plans, the build system, even documentation. https://www.artima.com/intv/dry.html

システムの知識は単なるコードよりずっと大きなものです。データベーススキーマ、テスト計画、ビルドシステム、さらにはドキュメントまでも指しています。 https://www.infoq.com/jp/news/2012/05/DRY-code-duplication-coupling/

コードの重複を取り除く

商品クラス

例えば、商品があるとします。

class 商品
{
    private string $商品名;
    private int $価格;

    public function __construct(string $商品名, int $価格)
    {
        $this->商品名 = $商品名;
        $this->価格 = $価格;
    }

    public function 価格(): int
    {
        return $this->価格;
    }
}

通常値引計算クラス

10%を割り引く通常値引というものがあるとすると、以下のようにクラス化できます。

class 通常値引計算
{
    public function 値引額(商品 $商品): int
    {
        $discount = $商品->価格() * 10 / 100;

        return (int) $discount;
    }
}

消費税計算クラス

また、10%の消費税を課税しないといけないということで、以下のようなクラスも出てきます。 本物の消費税は複数税率とか計算方法もややこしいですが、ここでは単純に全て10%とします。

class 消費税計算
{
    public function 税額(商品 $商品): float
    {
        $tax = $商品->価格() * 10 / 100;

        return $tax;
    }
}

そうすると、以下のコードは重複しています。DRY原則違反でしょうか?

$商品->価格() * 10 / 100;

共通化する

とりあえず、同じ処理なので共通化してみましょう。

class ???
{
    public static function 計算する(商品 $商品): float
    {
        return $商品->価格() * 10 / 100;
    }
}

さて、このクラスは何を表現しているのでしょうか? やっていることは商品価格の10%を計算することです。よって、少しぎこちないですが、 「商品価格の10パーセント計算」とクラス名を付けましょう。

class 商品価格の10パーセント計算
{
    public static function 計算する(商品 $商品): float
    {
        return $商品->価格() * 10 / 100;
    }
}

すると、「通常値引計算」と「消費税計算」は以下のようにリファクタリングされます。

class 通常値引計算
{
    public function 値引額(商品 $商品): int
    {
        $discount = 商品価格の10パーセント計算::計算する($商品);

        return (int) $discount;
    }
}
class 消費税計算
{
    public function 税額(商品 $商品): float
    {
        $tax = 商品価格の10パーセント計算::計算する($商品);

        return $tax;
    }
}

値引率の変更

ここで、通常値引が10%ではなく、20%に変わるとします。 「商品価格の10パーセント計算」クラスを変更しようとする人はいるでしょうか? もし、変更すると以下のようになります。

class 商品価格の10パーセント計算
{
    public static function 計算する(商品 $商品): float
    {
        return $商品->価格() * 20 / 100;
    }
}

「商品価格の10パーセント計算」クラスが20%を返すようになります。 流石にこれをする人はいないでしょう。

通常値引の変更なので「通常値引計算」クラスを以下のように変更します。

class 通常値引計算
{
    public function 値引額(商品 $商品): int
    {
        $discount = $商品->価格() * 20 / 100;

        return $discount;
    }
}

「商品価格の10パーセント計算」クラスが必要だったかどうかは疑問が残りますが、作ったからといって問題は起こりませんでした。

過度な共通化をするには

たまたま同じ10%なので共通化してしまい、その後、片方の率が変更になり、他方にも影響を与えてバグってしまうようにするにはどうすればいいでしょう?

10%という知識を通常値引計算クラス、消費税計算クラスから完全に取り除く必要があります。

「商品価格の10パーセント計算」をもっと抽象的な名前に変更しましょう。 これくらいでどうでしょうか?

class 商品関連計算
{
    public static function 計算する(商品 $商品): float
    {
        return $商品->価格() * 10 / 100;
    }
}

何だかよくわかりませんが、商品に関連した計算をしているクラスができました。

すると、「通常値引計算」と「消費税計算」は以下のようになります。

class 通常値引計算
{
    public function 値引額(商品 $商品): int
    {
        $discount = 商品関連計算::計算する($商品);

        return (int) $discount;
    }
}
class 消費税計算
{
    public function 税額(商品 $商品): float
    {
        $tax = 商品関連計算::計算する($商品);

        return $tax;
    }
}

これで、10%という通常値引計算および消費税計算の知識の一部が両クラスから完全に消えました。

ここで、消費税率が15%に変更になるとすると、何だかよくわからない「商品関連計算」クラスの計算ロジックを15%に変更すると、通常値引も15%になってしまうという状況をやっと作り出せます。

これはDRY原則に従っているか?

さて、「商品関連計算」クラスは 1つの知識を明確に表現しているでしょうか?していませんね。何を表現しているのかよくわからないクラスですから。これはDRY原則違反でしょう。

過度な共通化の要件

「通常値引計算」「消費税計算」という概念(知識)をクラスとして分けている時点で、両者を混同するのはかなり難しいことがわかります。

現実にこれらを混同するためには、違う概念であるということが理解できていない、あるいは、そのようなことを全く考えていない必要があります。

つまり、異なる概念であることを理解できていない場合は、このような過度な共通化をしてしまう可能性があるということです。

1つの知識を1箇所で表現するには

結局、1つの知識を1箇所で表現するには、知識・概念を正しく把握することが必要です。

例えば、色々な種類の値引計算があるとして、それらの関係はどうなのか?ある値引は別の値引に関連・依存するのか?しないのか?それらはビジネスに関するルールであり知識です。

値引Aが値引Bに依存すると思っていれば、値引Aが値引Bに依存するコードを書いてしまいます。本当は両者は完全に独立したものであったとしても。

ビジネスに関する知識が不確かならば、誤って違う概念を混同したり、同じ概念を2箇所以上に書いてしまう可能性が高くなります。

まとめ

  • DRY原則とは「1つの知識は、システム内で1箇所に明確に表現されないといけない」という原則です。
  • コードの重複だけがDRY原則の対象ではありません。
  • 知識が不確かなら、DRY原則を守ることは難しくなるでしょう。

参考

Tags: programming, php

CodeIgniter 4.0を開発版にアップデートする

CodeIgniter 4.0 を開発版に更新する方法です。

CodeIgniter 4.0.4 からかなり開発が進んでいますので、 執筆時点では、CodeIgniter 4.0.4 よりも開発版(develop ブランチ)を使用することをお薦めします。

動作確認環境

  • CodeIgniter 4.0.4
  • Composer 2.0.8
  • PHP 7.4.13
    • Xdebug 3.0.1
  • macOS 10.15.7

PHPUnit のアップデート

PHPUnit を 9 にアップデートします。以下で用いる Liaison Revision を使わない場合は、このアップデートは必須ではありません。

--- a/composer.json
+++ b/composer.json
@@ -12,7 +12,7 @@
    "require-dev": {
        "fzaninotto/faker": "^1.9@dev",
        "mikey179/vfsstream": "1.6.*",
-       "phpunit/phpunit": "^8.5"
+       "phpunit/phpunit": "^9.0"
    },
    "autoload-dev": {
        "psr-4": {
$ composer update

PHPUnit 9.5.0 にアップデートされました。

phpunit.xml の書き方が変更されていますので、移行します。

$ vendor/bin/phpunit --migrate-configuration
PHPUnit 9.5.0 by Sebastian Bergmann and contributors.

Created backup:         /Users/kenji/work/codeigniter/ci4app/phpunit.xml.dist.bak
Migrated configuration: /Users/kenji/work/codeigniter/ci4app/phpunit.xml.dist
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,52 +1,38 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<phpunit bootstrap="vendor/codeigniter4/framework/system/Test/bootstrap.php"
-       backupGlobals="false"
-       colors="true"
-       convertErrorsToExceptions="true"
-       convertNoticesToExceptions="true"
-       convertWarningsToExceptions="true"
-       stopOnError="false"
-       stopOnFailure="false"
-       stopOnIncomplete="false"
-       stopOnSkipped="false">
-   <testsuites>
-       <testsuite name="app">
-           <directory>./tests</directory>
-       </testsuite>
-   </testsuites>
-
-   <filter>
-       <whitelist addUncoveredFilesFromWhitelist="true" processUncoveredFilesFromWhitelist="true">
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/codeigniter4/framework/system/Test/bootstrap.php" backupGlobals="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="false" stopOnFailure="false" stopOnIncomplete="false" stopOnSkipped="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
+  <coverage includeUncoveredFiles="true" processUncoveredFiles="true">
+    <include>
       <directory suffix=".php">./app</directory>
+    </include>
     <exclude>
       <directory suffix=".php">./app/Views</directory>
       <file>./app/Config/Routes.php</file>
     </exclude>
-       </whitelist>
-   </filter>
-
+    <report>
+      <clover outputFile="build/logs/clover.xml"/>
+      <html outputDirectory="build/logs/html"/>
+      <php outputFile="build/logs/coverage.serialized"/>
+      <text outputFile="php://stdout" showUncoveredFiles="false"/>
+    </report>
+  </coverage>
+  <testsuites>
+    <testsuite name="app">
+      <directory>./tests</directory>
+    </testsuite>
+  </testsuites>
   <logging>
-       <log type="coverage-html" target="build/logs/html"/>
-       <log type="coverage-clover" target="build/logs/clover.xml"/>
-       <log type="coverage-php" target="build/logs/coverage.serialized"/>
-       <log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
-       <log type="testdox-html" target="build/logs/testdox.html"/>
-       <log type="testdox-text" target="build/logs/testdox.txt"/>
-       <log type="junit" target="build/logs/logfile.xml"/>
+    <testdoxHtml outputFile="build/logs/testdox.html"/>
+    <testdoxText outputFile="build/logs/testdox.txt"/>
+    <junit outputFile="build/logs/logfile.xml"/>
   </logging>
-
   <php>
     <server name="app.baseURL" value="http://example.com"/>
-
     <!-- Directory containing phpunit.xml -->
     <const name="HOMEPATH" value="./"/>
-
     <!-- Directory containing the Paths config file -->
     <const name="CONFIGPATH" value="./app/Config/"/>
-
     <!-- Directory containing the front controller (index.php) -->
     <const name="PUBLICPATH" value="./public/"/>
-
     <!-- Database configuration -->
     <!--   <env name="database.tests.hostname" value="localhost"/>  -->
     <!--   <env name="database.tests.database" value="tests"/>      -->

CodeIgniter4 を開発版に変更

プロジェクトのルートフォルダで builds development コマンドを実行すると、最新の開発版に変更できます。

$ php builds development
The following files were modified:
 * /Users/kenji/work/codeigniter/ci4app/composer.json
 * /Users/kenji/work/codeigniter/ci4app/app/Config/Paths.php
 * /Users/kenji/work/codeigniter/ci4app/phpunit.xml.dist
Run `composer update` to sync changes with your vendor folder

上記ファイル内のパスなどが変更されました。

Composer パッケージを更新します。

$ composer update
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 1 install, 1 update, 1 removal
  - Removing codeigniter4/framework (v4.0.4)
  - Locking codeigniter4/codeigniter4 (dev-develop e5721a4)
  - Downgrading fzaninotto/faker (dev-master 5ffe7db => v1.9.2)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 1 update, 1 removal
  - Syncing codeigniter4/codeigniter4 (dev-develop e5721a4) into cache
  - Syncing fzaninotto/faker (v1.9.2) into cache
  - Removing codeigniter4/framework (v4.0.4)
  - Installing codeigniter4/codeigniter4 (dev-develop e5721a4): Cloning e5721a4bcd from cache
  - Downgrading fzaninotto/faker (dev-master 5ffe7db => v1.9.2): Checking out 848d812523 from cache
Package fzaninotto/faker is abandoned, you should avoid using it. No replacement was suggested.
Generating autoload files
28 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Generating autoload files
Generated autoload files

CodeIgniter v4.0.4 が codeigniter4/codeigniter4 の develop ブランチに変更されました。

Faker のアップデート

Faker のパッケージが変更されていますので、更新します。

--- a/composer.json
+++ b/composer.json
@@ -11,7 +11,7 @@
"liaison/revision": "1.x-dev"
},
"require-dev": {
-        "fzaninotto/faker": "^1.9@dev",
+        "fakerphp/faker": "^1.9",
         "mikey179/vfsstream": "1.6.*",
         "phpunit/phpunit": "^9.0"
  },
$ composer update
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 1 install, 0 updates, 1 removal
  - Removing fzaninotto/faker (v1.9.2)
  - Locking fakerphp/faker (v1.13.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 1 removal
  - Downloading fakerphp/faker (v1.13.0)
  - Removing fzaninotto/faker (v1.9.2)
  - Installing fakerphp/faker (v1.13.0): Extracting archive
Generating autoload files
31 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Generating autoload files
Generated autoload files

system 以外のアップデート

composer updatevendor/ 以下にある CodeIgniter4 の system フォルダは更新されますが、それ以外の app/ フォルダ内のファイルなどは更新されません。

それらを更新してくれるパッケージが Liaison Revision です。

Liaison Revision のインストール

Liaison Revision は PHP 7.3 以上、CodeIgniter 4.0.4 以上が必要です。

$ composer require liaison/revision
Using version 1.x-dev for liaison/revision
./composer.json has been updated
Running composer update liaison/revision
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 4 installs, 0 updates, 0 removals
  - Locking liaison/revision (dev-develop 9bb7912)
  - Locking symfony/filesystem (v5.2.1)
  - Locking symfony/polyfill-php80 (v1.20.0)
  - Locking symfony/process (v5.2.1)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 4 installs, 0 updates, 0 removals
  - Syncing liaison/revision (dev-develop 9bb7912) into cache
  - Installing symfony/polyfill-php80 (v1.20.0): Extracting archive
  - Installing symfony/process (v5.2.1): Extracting archive
  - Installing symfony/filesystem (v5.2.1): Extracting archive
  - Installing liaison/revision (dev-develop 9bb7912): Cloning 9bb791224b from cache
Generating autoload files
31 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Generating autoload files
Generated autoload files

インストールが完了したら、既存のファイルは全て Git で管理しておきましょう。 commit されていない変更は commit してください。

ファイルのアップデート

それでは、system 以外のファイルをアップデートします。

spark revision:update コマンドを実行し、対話型で作業を進めます。

$ php spark revision:update
CodeIgniter v4.0.4 Command Line Tool - Server Time: 2020-12-29 20:04:47 UTC+09:00

Liaison Revision
Version: 1.0.0
Run Date: Tue, 29 December 2020, 20:04:47 UTC+09:00

Loaded configuration settings from: Liaison\Revision\Config\Revision.
+---------------------------+--------------------------------------------------------+
| Setting                   | Value                                                  |
+---------------------------+--------------------------------------------------------+
| Root Path                 | /Users/kenji/work/codeigniter/ci4app/                  |
| Write Path                | /Users/kenji/work/codeigniter/ci4app/writable/         |
| Ignored Directories Count | 0                                                      |
| Ignored Files Count       | 0                                                      |
| Allow Gitignore Entry     | Allowed                                                |
| Fall Through to Project   | Allowed                                                |
| Maximum Retries           | 10                                                     |
| Consolidator              | Liaison\Revision\Consolidation\DefaultConsolidator     |
| Upgrader                  | Liaison\Revision\Upgrade\ComposerUpgrader              |
| Pathfinder                | Liaison\Revision\Paths\DefaultPathfinder               |
| Diff Output Builder       | SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder |
| Log Handlers Count        | 2                                                      |
+---------------------------+--------------------------------------------------------+

Starting software updates...

Loading composer repositories with package information
Updating dependencies                                 
Nothing to modify in lock file
Installing dependencies from lock file (including require-dev)
Nothing to install, update or remove
Generating autoload files
31 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

Found 45 files to consolidate.
[p] Proceed.
[l] List all files to consolidate.
[c] List created files only (2).
[m] List modified files only (43).
[d] List deleted files only (0).
[a] Abort.

What shall I do? [p, l, c, m, d, a]: 

p を選択し、処理を進めます。

What shall I do? [p, l, c, m, d, a]: p

Found 43 files in conflict.
[l] List all files in conflict.
[o] Overwrite all.
[b] Create backup files then safely overwrite all.
[s] Skip all.
[r] Resolve each conflict.
[a] Abort.

What shall I do? [l, o, b, s, r, a]:

ファイルは Git 管理されていますので、o を選択し、上書きします。

What shall I do? [l, o, b, s, r, a]: o


Logs for this run can be found here:
/Users/kenji/work/codeigniter/ci4app/writable/revision/logs/

Terminating: Application update was successful.
Software updates finished in 2.496 minutes.

以下のファイルが変更されました。結構、変わってます。

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   app/Config/App.php
    modified:   app/Config/Autoload.php
    modified:   app/Config/Boot/development.php
    modified:   app/Config/Boot/production.php
    modified:   app/Config/Boot/testing.php
    modified:   app/Config/Cache.php
    modified:   app/Config/Constants.php
    modified:   app/Config/ContentSecurityPolicy.php
    modified:   app/Config/Database.php
    modified:   app/Config/DocTypes.php
    modified:   app/Config/Email.php
    modified:   app/Config/Encryption.php
    modified:   app/Config/Events.php
    modified:   app/Config/Exceptions.php
    modified:   app/Config/Filters.php
    modified:   app/Config/ForeignCharacters.php
    modified:   app/Config/Format.php
    modified:   app/Config/Honeypot.php
    modified:   app/Config/Images.php
    modified:   app/Config/Kint.php
    modified:   app/Config/Logger.php
    modified:   app/Config/Migrations.php
    modified:   app/Config/Mimes.php
    modified:   app/Config/Modules.php
    modified:   app/Config/Pager.php
    modified:   app/Config/Paths.php
    modified:   app/Config/Routes.php
    modified:   app/Config/Services.php
    modified:   app/Config/Toolbar.php
    modified:   app/Config/UserAgents.php
    modified:   app/Config/Validation.php
    modified:   app/Config/View.php
    modified:   app/Controllers/BaseController.php
    modified:   app/Controllers/Home.php
    modified:   app/Views/errors/cli/error_404.php
    modified:   app/Views/errors/cli/error_exception.php
    modified:   app/Views/errors/html/debug.css
    modified:   app/Views/errors/html/debug.js
    modified:   app/Views/errors/html/error_exception.php
    modified:   env
    modified:   public/.htaccess
    modified:   public/index.php
    modified:   spark

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    app/Config/Generators.php
    app/Config/Security.php
    writable/revision/

Git で差分を確認しつつ、必要ない差分は削除します。

app/Config/Filter.phpcsrf フィルタの有効化と app/Config/Paths.php$systemDirectory は元に戻しました。

--- a/app/Config/Paths.php
+++ b/app/Config/Paths.php
@@ -26,7 +26,7 @@ class Paths
     *
     * @var string
     */
-   public $systemDirectory = __DIR__ . '/../../system';
+   public $systemDirectory = __DIR__ . '/../../vendor/codeigniter4/codeigniter4/system';

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

PHPUnit テストの実行

テストが通るか確認しておきましょう。

CodeIgniter プロジェクトのルートフォルダで、以下のコマンドを実行します。

$ composer test
> phpunit
PHPUnit 9.5.0 by Sebastian Bergmann and contributors.

Warning:       XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set

.....                                                               5 / 5 (100%)

Time: 00:00.153, Memory: 10.00 MB

OK (5 tests, 6 assertions)

通りました。

開発版のユーザガイド

開発版のユーザガイドは以下にあります。

リリース版のユーザガイドの誤りも修正されていますので、こちらを参照しましょう。

参考

Tags: codeigniter, codeigniter4

CodeIgniter 4.0のインストール方法【2020年最新版】

CodeIgniter 4.0 のインストール方法と初期設定についてまとめておきます。

CodeIgniter 4.0 のサーバ要件

  • PHP 7.2 以上
    • php-json、php-mysqlnd、php-xml が有効
    • intl、mbstring 機能拡張
    • libcurl(CURLRequest を使う場合)
  • データベース
    • MySQL 5.1 以上(MySQLi ドライバ)
    • PostgreSQL(Postgre ドライバ)
    • SQLite3(SQLite3 ドライバ)

動作確認環境

  • CodeIgniter 4.0.4
  • Composer 2.0.8
  • PHPUnit 8.5.13
  • PHP 7.4.13
    • Xdebug 3.0.1
  • MySQL 5.7.32
  • macOS 10.15.7

Composer のインストール

Composer とは、PHPでの新しいパッケージ(ライブラリ)管理システムです。

Composer はパッケージ管理コマンドとしての composer コマンド、パッケージのリポジトリとしてのPackagist、そしてクラスファイルをオートロードするオートローダを提供します。

Composer では主にパッケージを各プロジェクト配下にインストールして使います。

Homebrew

Composer は Homebrew からインストールできます。

以下のコマンドを実行してください。

$ brew install compoesr

Homebrew を使わない場合は、以下の公式のインストール方法があります。

公式のインストール方法

https://getcomposer.org/download/ に記載されているコマンドを実行します。

執筆時点では以下のようになっています。

$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$ php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
Installer verified
$ php composer-setup.php
All settings correct for using Composer
Downloading...

Composer (version 2.0.8) successfully installed to: /Users/kenji/composer.phar
Use it: php composer.phar
$ php -r "unlink('composer-setup.php');"

これで、カレントディレクトリに composer.phar がインストールされます。

composer.pharcomposer にリネームし、パスの通ったディレクトリに配置してください。

例えば、

$ mv composer.phar /usr/local/bin/composer

composer コマンドの確認

composer コマンドを実行してみましょう。

以下のようにバージョンとヘルプが表示されます。

$ composer
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 2.0.8 2020-12-03 17:20:38

Usage:
  command [options] [arguments]

...

CodeIgniter 4.0 のインストール

CodeIgniter 4 を composer からインストールします。

$ composer create-project codeigniter4/appstarter ci4app
Creating a "codeigniter4/appstarter" project at "./ci4app"
Installing codeigniter4/appstarter (v4.0.4)
  - Installing codeigniter4/appstarter (v4.0.4): Extracting archive
Created project in /Users/kenji/work/codeigniter/ci4app
Loading composer repositories with package information
Updating dependencies
Lock file operations: 36 installs, 0 updates, 0 removals
  - Locking codeigniter4/framework (v4.0.4)
  - Locking doctrine/instantiator (1.4.0)
  - Locking fzaninotto/faker (dev-master 5ffe7db)
  - Locking kint-php/kint (3.3)
  - Locking laminas/laminas-escaper (2.7.0)
  - Locking laminas/laminas-zendframework-bridge (1.1.1)
  - Locking mikey179/vfsstream (v1.6.8)
  - Locking myclabs/deep-copy (1.10.2)
  - Locking phar-io/manifest (2.0.1)
  - Locking phar-io/version (3.0.4)
  - Locking phpdocumentor/reflection-common (2.2.0)
  - Locking phpdocumentor/reflection-docblock (5.2.2)
  - Locking phpdocumentor/type-resolver (1.4.0)
  - Locking phpspec/prophecy (1.12.2)
  - Locking phpunit/php-code-coverage (7.0.14)
  - Locking phpunit/php-file-iterator (2.0.3)
  - Locking phpunit/php-text-template (1.2.1)
  - Locking phpunit/php-timer (2.1.3)
  - Locking phpunit/php-token-stream (4.0.4)
  - Locking phpunit/phpunit (8.5.13)
  - Locking psr/log (1.1.3)
  - Locking sebastian/code-unit-reverse-lookup (1.0.2)
  - Locking sebastian/comparator (3.0.3)
  - Locking sebastian/diff (3.0.3)
  - Locking sebastian/environment (4.2.4)
  - Locking sebastian/exporter (3.1.3)
  - Locking sebastian/global-state (3.0.1)
  - Locking sebastian/object-enumerator (3.0.4)
  - Locking sebastian/object-reflector (1.1.2)
  - Locking sebastian/recursion-context (3.0.1)
  - Locking sebastian/resource-operations (2.0.2)
  - Locking sebastian/type (1.1.4)
  - Locking sebastian/version (2.0.1)
  - Locking symfony/polyfill-ctype (v1.20.0)
  - Locking theseer/tokenizer (1.2.0)
  - Locking webmozart/assert (1.9.1)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 36 installs, 0 updates, 0 removals
  - Syncing fzaninotto/faker (dev-master 5ffe7db) into cache
  - Installing psr/log (1.1.3): Extracting archive
  - Installing laminas/laminas-zendframework-bridge (1.1.1): Extracting archive
  - Installing laminas/laminas-escaper (2.7.0): Extracting archive
  - Installing kint-php/kint (3.3): Extracting archive
  - Installing codeigniter4/framework (v4.0.4): Extracting archive
  - Installing fzaninotto/faker (dev-master 5ffe7db): Cloning 5ffe7db6c8 from cache
  - Installing mikey179/vfsstream (v1.6.8): Extracting archive
  - Installing symfony/polyfill-ctype (v1.20.0): Extracting archive
  - Installing webmozart/assert (1.9.1): Extracting archive
  - Installing phpdocumentor/reflection-common (2.2.0): Extracting archive
  - Installing phpdocumentor/type-resolver (1.4.0): Extracting archive
  - Installing phpdocumentor/reflection-docblock (5.2.2): Extracting archive
  - Installing phpunit/php-token-stream (4.0.4): Extracting archive
  - Installing sebastian/version (2.0.1): Extracting archive
  - Installing sebastian/type (1.1.4): Extracting archive
  - Installing sebastian/resource-operations (2.0.2): Extracting archive
  - Installing sebastian/recursion-context (3.0.1): Extracting archive
  - Installing sebastian/object-reflector (1.1.2): Extracting archive
  - Installing sebastian/object-enumerator (3.0.4): Extracting archive
  - Installing sebastian/global-state (3.0.1): Extracting archive
  - Installing sebastian/exporter (3.1.3): Extracting archive
  - Installing sebastian/environment (4.2.4): Extracting archive
  - Installing sebastian/diff (3.0.3): Extracting archive
  - Installing sebastian/comparator (3.0.3): Extracting archive
  - Installing phpunit/php-timer (2.1.3): Extracting archive
  - Installing phpunit/php-text-template (1.2.1): Extracting archive
  - Installing phpunit/php-file-iterator (2.0.3): Extracting archive
  - Installing theseer/tokenizer (1.2.0): Extracting archive
  - Installing sebastian/code-unit-reverse-lookup (1.0.2): Extracting archive
  - Installing phpunit/php-code-coverage (7.0.14): Extracting archive
  - Installing doctrine/instantiator (1.4.0): Extracting archive
  - Installing phpspec/prophecy (1.12.2): Extracting archive
  - Installing phar-io/version (3.0.4): Extracting archive
  - Installing phar-io/manifest (2.0.1): Extracting archive
  - Installing myclabs/deep-copy (1.10.2): Extracting archive
  - Installing phpunit/phpunit (8.5.13): Extracting archive
6 package suggestions were added by new dependencies, use `composer suggest` to see details.
Package fzaninotto/faker is abandoned, you should avoid using it. No replacement was suggested.
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating autoload files
22 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Generating autoload files
Generated autoload files

ci4app フォルダに CodeIgniter 4.0.4 がインストールされました。

CodeIgniter4 のフォルダ構成

CodeIgniter4 のフォルダ構成は以下のようになっています。

ci4app/
├── app/ ... アプリケーション
│   ├── Common.php
│   ├── Config/      ... 設定
│   ├── Controllers/ ... コントローラ
│   ├── Database/    ... データベース
│   ├── Filters/     ... コントローラフィルタ
│   ├── Helpers/
│   ├── Language/
│   ├── Libraries/
│   ├── Models/      ... モデル
│   ├── ThirdParty/
│   └── Views/       ... ビュー
├── builds*       ... buildsコマンド
├── composer.json
├── composer.lock
├── env           ... 環境変数設定ファイルのサンプル
├── phpunit.xml.dist
├── public/ ... Web公開領域(ドキュメントルート)
│   ├── favicon.ico
│   ├── index.php
│   └── robots.txt
├── spark* ... sparkコマンド
├── tests/ ... テストファイル
│   ├── _support/
│   ├── database/
│   ├── session/
│   └── unit/
├── vendor/   ... Composer管理
└── writable/ ... 書き込み用フォルダ
    ├── cache/
    ├── debugbar/
    ├── logs/
    ├── session/
    └── uploads/

システムメッセージの翻訳のインストール

システムメッセージの翻訳は公式の Composer パッケージがありますので、それをインストールします。

CodeIgniter プロジェクトのルートフォルダで、以下のコマンドを実行します。

$ composer require codeigniter4/translations:dev-develop

注意:現在の codeigniter4/translations のリリースバージョン 4.0.2 にはまだ日本語が含まれていませんので develop ブランチをインストールしました。

環境変数の設定

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

.env ファイルとは?

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

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

.env の設定

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

$ cp env .env

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

まず、開発環境を development に変更します。

--- env 2020-07-16 12:44:28.000000000 +0900
+++ .env    2020-12-29 09:14:27.000000000 +0900
@@ -14,13 +14,13 @@
 # ENVIRONMENT
 #--------------------------------------------------------------------

-# CI_ENVIRONMENT = production
+CI_ENVIRONMENT = development

baseURL を設定します。

 #--------------------------------------------------------------------
 # APP
 #--------------------------------------------------------------------

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

 # app.sessionDriver = 'CodeIgniter\Session\Handlers\FileHandler'

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

@@ -49,17 +49,17 @@
 # DATABASE
 #--------------------------------------------------------------------

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

 #--------------------------------------------------------------------
 # CONTENT SECURITY POLICY

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

@@ -99,3 +99,9 @@
 # honeypot.name = 'honeypot'
 # honeypot.template = '<label>{label}</label><input type="text" name="{name}" value=""/>'
 # honeypot.container = '<div style="display:none">{template}</div>'
+
+#--------------------------------------------------------------------
+# LOGGER
+#--------------------------------------------------------------------
+
+logger.threshold = 9

設定ファイルの設定

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

言語を ja に変更します。

--- a/app/Config/App.php
+++ b/app/Config/App.php
@@ -63,7 +63,7 @@ class App extends BaseConfig
    | should run under for this request.
    |
    */
-   public $defaultLocale = 'en';
+   public $defaultLocale = 'ja';

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

    /*
    |--------------------------------------------------------------------------
@@ -99,7 +99,7 @@ class App extends BaseConfig
    | dates with the date helper, and can be retrieved through app_timezone()
    |
    */
-   public $appTimezone = 'America/Chicago';
+   public $appTimezone = 'Asia/Tokyo';

    /*
    |--------------------------------------------------------------------------

デフォルトで CSRF フィルタを有効にします。

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

不要な URI があれば、除外設定しましょう。

PHP 開発サーバの起動

Welcome ページにアクセスするために、PHP 開発サーバを起動します。

CodeIgniter プロジェクトのルートフォルダで、spark serve コマンドを実行してください。

$ php spark serve
CodeIgniter CLI Tool - Version 4.0.4 - Server-Time: 2020-12-29 09:56:35am

CodeIgniter development server started on http://localhost:8080
Press Control-C to stop.

ブラウザでのアクセス

ブラウザで http://localhost:8080/ にアクセスします。

Welcome ページが表示されます。

スクロールしていくと、環境が development になっていることがわかります。

右下の CodeIgniter の炎のロゴをクリックすると、デバッグツールバーが表示されます。

PHPUnit テストの実行

PHPUnit でのテストを実行します。

CodeIgniter プロジェクトのルートフォルダで、以下のコマンドを実行します。

$ XDEBUG_MODE=coverage composer test
> phpunit
PHPUnit 8.5.13 by Sebastian Bergmann and contributors.

.....                                                               5 / 5 (100%)

Time: 295 ms, Memory: 12.00 MB

OK (5 tests, 6 assertions)

Generating code coverage report in Clover XML format ... done [62 ms]

Generating code coverage report in HTML format ... done [116 ms]

Generating code coverage report in PHP format ... done [1 ms]


Code Coverage Report:   
  2020-12-29 09:37:59   

 Summary:               
  Classes:  0.00% (0/5) 
  Methods:  0.00% (0/6) 
  Lines:    8.57% (6/70)

\Config::Config\Database
  Methods:   0.00% ( 0/ 1)   Lines:  55.56% (  5/  9)

通りました。

データベース接続のテスト

データベースに接続できるか確認しておきましょう。

データベースセッション用のマイグレーションファイルの生成

spark コマンドに用意されているデータベースセッション用テーブルを作成するマイグレーションファイルを生成してみます。

CodeIgniter プロジェクトのルートフォルダで、以下のコマンドを実行します。

$ php spark session:migration
CodeIgniter CLI Tool - Version 4.0.4 - Server-Time: 2020-12-29 09:41:32am

Created file: APPPATH/Database/Migrations/20201229094132_create_ci_sessions_table.php

生成されました。

マイグレーションの実行

作成されたマイグレーションファイルを実行します。

$ php spark migrate
CodeIgniter CLI Tool - Version 4.0.4 - Server-Time: 2020-12-29 09:43:26am

すべての新しいマイグレーションを実行しています...
    実行中: (App) 20201229094132_App\Database\Migrations\Migration_create_ci_sessions_table
Done

完了しました。

テーブルの確認

mysql コマンドでデータベースに接続し、テーブルが作成されたことを確認します。

mysql> use ci4app;
mysql> desc ci_sessions;
+------------+------------------+------+-----+---------+-------+
| Field      | Type             | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+-------+
| id         | varchar(128)     | NO   | PRI | NULL    |       |
| ip_address | varchar(45)      | NO   |     | NULL    |       |
| timestamp  | int(10) unsigned | NO   | MUL | 0       |       |
| data       | text             | NO   |     | NULL    |       |
+------------+------------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

データベースセッションを使わない場合は、マイグレーションファイル、ci_sessionsmigrations テーブルを削除してください。

データベースセッションを使う場合は、app/Config/App.php のセッションの設定をしてください。

関連

参考

Tags: codeigniter, codeigniter4