# アプリケーションスケルトン

[pieceofcake2/app](https://github.com/pieceofcake2/app) は、[pieceofcake2/cakephp](https://github.com/pieceofcake2/cakephp)
で動作する CakePHP 2 アプリケーションのためのアプリケーションスケルトンです。CakePHP 5 と互換性のあるディレクトリ構成
(`config/`、`src/`、`templates/`、`tests/`、`webroot/`) を使い、CakePHP とすべての依存パッケージを Composer で
読み込みます。CakePHP 5 と同じく、アプリケーションのクラスは `App` 名前空間にあり、テンプレートは CakePHP 5 の
ディレクトリ (`layout/`、`element/`、`email/`、`Error/`) にある `.php` ファイルです。

グローバルなクラスと `.ctp` のテンプレートを使う場合は、代わりに
[pieceofcake2/app-legacy](https://pieceofcake2.github.io/docs/ja/installation/application-skeleton-legacy.md) を使ってください。既存のアプリケーションをこの構成と
名前空間付きのクラスに移すには、[モダン構成への移行](https://pieceofcake2.github.io/docs/ja/appendices/migrating-to-the-modern-layout.md) をご覧ください。
[pieceofcake2/upgrade](https://pieceofcake2.github.io/upgrade/ja/) の `layout` コマンドと `namespace` コマンドで
移行できます。

> [!WARNING]
> CakePHP 2.x は既存アプリケーションのメンテナンスのためのものです。新しいプロジェクトには、
> CakePHP の現在のメジャーバージョンを使ってください。

## 動作要件 {#requirements}

- PHP 8.0、8.1、8.2、8.3、8.4 または 8.5
- [Composer](https://getcomposer.org/)
- データベース: MySQL 5.6 以上、PostgreSQL 9.4 以上、SQLite 3 または SQL Server 2022 以上
- PHP 拡張: `mbstring`、`intl`、`openssl` と、使用するデータベースの PDO ドライバー

詳しくは [システム要件](https://pieceofcake2.github.io/docs/ja/installation.md#requirements) をご覧ください。

## インストール {#installation}

Composer の `create-project` コマンドで新しいアプリケーションを作成します。

```bash
composer create-project pieceofcake2/app my_app
```

これにより `my_app` ディレクトリが作成され、CakePHP (`pieceofcake2/cakephp`) が `vendor/` にインストールされます。
[Bake](https://pieceofcake2.github.io/bake/ja/) プラグインと [DebugKit](https://pieceofcake2.github.io/debug_kit/ja/)
プラグイン、そして開発ツール (PHPUnit、[pieceofcake2/phpstan-cakephp2](https://pieceofcake2.github.io/phpstan-cakephp2/ja/)
を使った PHPStan、CakePHP コーディング規約) は、開発用の依存パッケージとしてインストールされます。AI コーディングツール
向けに、このクックブックの Markdown (`pieceofcake2/docs`、`vendor/pieceofcake2/docs/docs/` に入ります) も開発用の
依存パッケージとしてインストールされます。

続けて設定を仕上げます。

1. `config/database.php.default` を `config/database.php` にコピーし、`config/.env.local` の `DATABASE_URL` で
   接続を設定するか ([環境変数 (.env)](#environment-variables-env) と
   [データベースとメーラーの DSN](#database-and-mailer-dsns) をご覧ください)、ファイルに入力します。テスト用
   データベースは `config/.env.test.local` で設定します ([テストの実行](#running-tests) を参照してください)。
2. `config/core.php` の `Security.salt` と `Security.cipherSeed` を変更するか、`config/.env.local` で
   `SECURITY_SALT` と `SECURITY_CIPHER_SEED` を設定します。スケルトンには広く知られたサンプル値が
   入っており、実際のアプリケーションで使ってはいけません。
   [セキュリティ用のソルトの生成](#generating-the-security-salts) をご覧ください。
3. メールを送信する場合は、`config/email.php.default` を `config/email.php` にコピーし、
   `MAILER_DSN` でトランスポートを設定するか、ファイルで設定します。
4. Web サーバーが `tmp/` と `logs/` に書き込めることを確認します。

### バージョン管理 {#version-control}

スケルトンの `.gitignore` ファイルはアプリケーション用のものなので、`create-project` の直後に
`git init` を実行できます。無視されるのは次のものです。

- `vendor/` と、Composer が `plugins/` にインストールするプラグイン (`plugins/Bake/` と
  `plugins/DebugKit/`)。Composer でさらにプラグインをインストールした場合は、それらを `.gitignore` に
  追加してください。`plugins/` にあるアプリケーション独自のプラグインはコミットされます。
- `tmp/` と `logs/` の中身。ディレクトリを残すための `empty` ファイルはコミットされます。
- 各マシンの設定や認証情報を含む `config/database.php`、`config/email.php` と、`.env` の `.local` のファイル
  (`config/.env.local`、`config/.env.*.local`)。`config/.env` と `config/.env.test` は
  Symfony と同じくコミットします。本当の秘密の値は `.local` のファイルか実際の環境変数に置いてください。
- `phpunit.xml` (`phpunit.xml.dist` のローカルコピー) と `.phpunit.result.cache`。

すべての環境で同じバージョンの依存パッケージがインストールされるように、`composer.lock` はコミットしてください。

### 開発用プラグイン {#development-plugins}

`config/bootstrap.php` は、[Bake](https://pieceofcake2.github.io/bake/ja/) プラグインと
[DebugKit](https://pieceofcake2.github.io/debug_kit/ja/) プラグインがインストールされていれば読み込みます。

```php
use Cake\Core\CakePlugin;
use Cake\Error\MissingPluginException;

foreach (['Bake', 'DebugKit'] as $plugin) {
    try {
        CakePlugin::load($plugin);
    } catch (MissingPluginException) {
        // The plugin is not installed.
    }
}
```

これらは開発用の依存パッケージなので、`composer install --no-dev` でアプリケーションをインストールした場合は
読み込まれません。Bake は次のように実行します。

```bash
bin/cake bake
```

DebugKit のツールバーを表示するには、`AppController` に `DebugKit.Toolbar` コンポーネントを追加します
([DebugKit のドキュメント](https://pieceofcake2.github.io/debug_kit/ja/) を参照)。開発用の依存パッケージが
なくてもアプリケーションが動作するように、プラグインが読み込まれている場合にだけ追加してください。

```php
// src/Controller/AppController.php
namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Core\CakePlugin;

class AppController extends Controller
{
    /**
     * Constructor. Adds the DebugKit toolbar when the plugin is loaded.
     *
     * @param \Cake\Network\CakeRequest|null $request Request object.
     * @param \Cake\Network\CakeResponse|null $response Response object.
     */
    public function __construct($request = null, $response = null)
    {
        if (CakePlugin::loaded('DebugKit')) {
            $this->components[] = 'DebugKit.Toolbar';
        }
        parent::__construct($request, $response);
    }
}
```

ツールバーが表示されるのは、`debug` が 0 より大きい場合だけです。

その他のプラグインは、`config/bootstrap.php` で `CakePlugin::load()` (または `CakePlugin::loadAll()`)
を使って読み込みます。`CakePlugin::load()` はプラグインがインストールされていないと
`MissingPluginException` を投げるので、開発用の依存パッケージであるプラグインは上の例のように囲んでください。

## ディレクトリ構成 {#directory-layout}

```text
my_app/
├── bin/
│   └── cake            the console
├── config/             configuration (core.php, bootstrap.php, routes.php, database.php, ...)
├── logs/               log files
├── plugins/            plugins installed by Composer
├── resources/
│   └── locales/        translation files
├── src/                PHP code (the App namespace)
│   ├── Console/        shells, tasks and bake templates
│   ├── Controller/
│   ├── Model/
│   └── View/           helpers
├── templates/          view templates (.php)
│   ├── Error/          error pages
│   ├── Pages/          pages of PagesController
│   ├── element/        elements (Flash/ for the flash messages)
│   ├── email/          email templates (html/, text/)
│   └── layout/         layouts (email/ for the email layouts)
├── tests/              tests (the App\Test namespace)
│   ├── Fixture/
│   └── TestCase/
├── tmp/                cache, sessions and test files
├── vendor/             Composer packages, including CakePHP
└── webroot/            document root (index.php, test.php, css/, js/, img/)
```

`webroot/index.php`、`webroot/test.php` と `bin/cake` は `config/define.php` を読み込みます。
このファイルは、ルートディレクトリ (`ROOT`)、アプリケーションディレクトリ (`APP_DIR`、`src`)、
ドキュメントルート (`WWW_ROOT`) と Composer の vendor ディレクトリ (`VENDORS`) を設定します。
その他のディレクトリは CakePHP が自分で見つけます。`config/`、`tests/`、`tmp/`、`logs/`、`templates/` と
`resources/locales/` は、存在すれば使われます。[CakePHP のフォルダ構造](https://pieceofcake2.github.io/docs/ja/getting-started/cakephp-folder-structure.md)
もご覧ください。

`app/` ディレクトリを使うレガシー構成 (`app/Config/`、`app/Controller/`、`app/View/`、...) も
サポートされていますが、スケルトンに含まれるのはモダン構成だけです。

### 名前空間 {#namespace}

`src/` のクラスは `App` 名前空間にあり、名前空間はディレクトリに対応します。
たとえば `src/Controller/PostsController.php` は `App\Controller\PostsController`、
`src/Model/Post.php` は `App\Model\Post` です。Composer が PSR-4 で `src/` から `App\` を、`tests/` から
`App\Test\` を読み込むので、クラスのファイルを追加しても `composer dump-autoload` は必要ありません。

```json
"autoload": {
    "psr-4": {
        "App\\": "src/"
    }
},
"autoload-dev": {
    "psr-4": {
        "App\\Test\\": "tests/"
    }
},
```

`config/core.php` で CakePHP に名前空間を設定しているので、CakePHP はアプリケーションのコントローラー、モデル、
コンポーネント、ヘルパー、ビヘイビア、シェルをこの名前空間から探します
([App.namespace](https://pieceofcake2.github.io/docs/ja/development/namespaced-classes.md#app-namespace) を参照)。

```php
Configure::write('App.namespace', 'App');
```

CakePHP のクラスは `Cake` 名前空間にあり、`use` 文でインポートします。名前空間を持たない `config/` と
テンプレートでも同じです。

```php
// src/Controller/AppController.php
namespace App\Controller;

use Cake\Controller\Controller;

class AppController extends Controller
{
}
```

クラスを指定する設定には `::class` を使います (例: `config/core.php` の
`'renderer' => ExceptionRenderer::class`)。CakePHP 2 の名前 (`'File'`、`'Auth'`、`'DebugKit.Toolbar'`) も
引き続き使えます。[名前空間のあるクラス](https://pieceofcake2.github.io/docs/ja/development/namespaced-classes.md) をご覧ください。

## 設定 {#configuration}

| ファイル | 内容 |
| --- | --- |
| `config/core.php` | デバッグレベル、エラーと例外の処理、アプリケーションの名前空間、キャッシュ、セッション、セキュリティ用のソルト。 |
| `config/bootstrap.php` | キャッシュの設定、プラグインの読み込み、ログ、その他のアプリケーションの初期設定。 |
| `config/routes.php` | URL のルーティング。 |
| `config/database.php` | データベース接続 (`default`。`APP_ENV=test` では `test` も)。`database.php.default` からコピーします。 |
| `config/email.php` | メールのトランスポートとプロファイル。`email.php.default` からコピーします。 |
| `config/acl.php`、`config/acl.ini.php` | `IniAcl` と `PhpAcl` の設定。 |
| `config/define.php` | モダン構成のパス定数。 |
| `config/Schema/` | DbAcl、i18n、セッションのテーブルのスキーマファイル。 |

各設定については [設定](https://pieceofcake2.github.io/docs/ja/development/configuration.md) をご覧ください。

## 環境変数 (.env) {#environment-variables-env}

デバッグレベル、セキュリティ用のソルト、データベースの認証情報などの設定は、環境変数から読み込みます。
Symfony と同じく、`config/core.php` が [symfony/dotenv](https://symfony.com/doc/current/configuration.html#configuring-environment-variables-in-env-files)
で `config/` の `.env` ファイルから読み込むので、Web リクエスト、`bin/cake`、テストのすべてに適用されます。

```php
// config/core.php

use Symfony\Component\Dotenv\Dotenv;

(new Dotenv())->usePutenv()->loadEnv(CONFIG . '.env');
```

ファイルは次の順に読み込まれてマージされ、後のファイルが前のファイルより優先されます。

| ファイル | 読み込まれるとき | Git |
| --- | --- | --- |
| `config/.env` | 常に (必須) | コミット: アプリケーションのデフォルト |
| `config/.env.local` | `APP_ENV` が `test` のとき以外 | 無視: 自分のマシン |
| `config/.env.{APP_ENV}` (例: `config/.env.test`) | 常に | コミット: 環境ごとのデフォルト |
| `config/.env.{APP_ENV}.local` | 常に | 無視: その環境での自分のマシン |

- 実際の環境変数 (Web サーバー、シェル、コンテナ、CI) で設定されている変数は上書きされないので、実際の
  環境変数はすべてのファイルより優先されます。値は `$_SERVER`、`$_ENV`、`putenv()` に書き込まれるので、
  `env()`、`getenv()`、`$_SERVER`、`$_ENV` で参照できます。
- `APP_ENV` は実際の環境変数から、無ければファイルから読み込まれます。`config/.env` は `APP_ENV=dev` を
  設定します。`vendor/bin/phpunit`、`bin/cake test`、`webroot/test.php` は、`APP_ENV` が設定されていなければ
  `APP_ENV=test` を設定します ([テストの実行](#running-tests) をご覧ください)。本番環境では `APP_ENV=prod` を
  実際の環境変数として設定してください。
- `config/.env` は必須で、スケルトンに含まれています。`config/.env.local` などの `.local` ファイルには、
  `DATABASE_URL` やソルトなど、自分のマシンで異なる値だけを書きます。本番環境の秘密の値をコミットする
  ファイルに書かないでください。
- 空白を含まない値は引用符で囲む必要はありません。ダブルクォートの中では `$NAME` や `${NAME}` が変数の値に
  置き換えられるので、`$` を含む値はシングルクォートで囲んでください。

| 変数 | 設定 | デフォルト |
| --- | --- | --- |
| `APP_ENV` | 環境: `dev`、`test`、`prod`。`config/.env.{APP_ENV}` を選びます | `dev` (`config/.env` の値) |
| `APP_DEBUG` | `debug` (`0`、`1`、`2`) | `0` |
| `SECURITY_SALT` | `Security.salt` | `config/core.php` の値 |
| `SECURITY_CIPHER_SEED` | `Security.cipherSeed` (1〜4294967295。レガシーな `'cipher'` Cookie でのみ使用) | `config/core.php` の値 |
| `COOKIE_KEY` | `Cookie.key`。`'aes'` タイプでの `CookieComponent` のキー | `null` (`Security.salt` が使われます) |
| `DATABASE_URL` | `config/database.php` の `default` 接続 (`APP_ENV=test` では `test` 接続も) (DSN) | `config/database.php` の値 |
| `MAILER_DSN` | `config/email.php` の `default` プロファイルのトランスポート (Symfony Mailer の DSN) | `config/email.php` の値 |

- 設定されていない変数や空の変数ではデフォルトが使われます。
- 本番環境では `APP_ENV=prod` とほかの設定を実際の環境変数として設定してください。
- `DATABASE_URL` は `config/database.php.default` が、`MAILER_DSN` は `config/email.php.default` が
  読み込みます。以前のスケルトンから `config/database.php` や `config/email.php` を作成した場合は、
  `__construct()` メソッドをそこにコピーしてください (以前の `DB_*` 変数用の `applyEnv()` メソッドと以前の
  `APP_ENV` の分岐は削除します)。`tests/bootstrap.php`、`bin/cake`、`webroot/test.php` の `APP_ENV` の
  デフォルトもスケルトンからコピーしてください。

ファイルの書式や、スケルトンから作成していないアプリケーションについては、
[環境変数](https://pieceofcake2.github.io/docs/ja/development/configuration.md#environment-variables) をご覧ください。

### 以前のスケルトンからの移行 {#migrating-from-an-earlier-skeleton}

pieceofcake2/app 2.4.x から作成したアプリケーションは、josegonzalez/dotenv で `.env` ファイルを読み込んでいます。
上の読み込み方に切り替えるには、次のようにします。

1. `composer remove josegonzalez/dotenv` と `composer require symfony/dotenv` を実行します。
2. `config/core.php` の先頭の `.env` の読み込み処理を上の 2 行に置き換え、`Configure::write('debug', ...)` の行を
   `Configure::write('debug', env('APP_DEBUG') ?? 0);` に置き換えます。
3. プロジェクトルートの `.env` は `config/` に移します。ルートの `.env` ファイルは読み込まれなくなりました。
   デフォルトと `APP_ENV=dev` を書いた `config/.env` をコミットし (スケルトンのファイルをご覧ください)、自分の
   マシンの値と秘密の値は `config/.env.local` に移します。
4. `config/.env.test` にはテストで変える値だけを、`config/.env.test.local` には自分のマシンで異なる値だけを
   書きます。ファイルはマージされるようになりました。
5. `.gitignore` では、`config/.env` とルートの `.env` ファイルの代わりに `/config/.env.local`、
   `/config/.env.*.local` を無視します。
6. 本番環境では、`APP_ENV=prod` (とほかの設定) を実際の環境変数として設定します。

### データベースとメーラーの DSN {#database-and-mailer-dsns}

DSN は、CakePHP 3.x 以降や Symfony と同じように、1 つの文字列で接続全体を設定します。`config/database.php`
の接続と `config/email.php` のプロファイルの `'dsn'` キーが DSN を受け取り、スケルトンはそれを Symfony
の環境変数 (`DATABASE_URL`、`MAILER_DSN`) から設定します。

```php
// config/database.php
$this->default['dsn'] = env('DATABASE_URL');
if (env('APP_ENV') === 'test') {
    $this->test = $this->default;
}

// config/email.php
$this->default['dsn'] = env('MAILER_DSN');
```

Symfony と同じように、データベースの変数は 1 つです。`DATABASE_URL` は現在の環境のデータベースで、
両方の接続がこれを使います。テスト (`APP_ENV=test`) では `config/.env.test` のテスト用データベースになります。

```dotenv
# MySQL
DATABASE_URL="mysql://user:password@localhost:3306/my_app?encoding=utf8mb4"
# PostgreSQL
DATABASE_URL="postgres://user:password@localhost:5432/my_app?schema=public"
# SQLite: three slashes for a path relative to the working directory, four for an absolute path
DATABASE_URL="sqlite:////var/db/my_app.sqlite"
# SQLite in memory (e.g. in config/.env.test.local)
DATABASE_URL="sqlite:///:memory:"
# SQL Server
DATABASE_URL="sqlserver://user:password@localhost:1433/my_app?encoding=utf8"

# SMTP with STARTTLS on port 587
MAILER_DSN="smtp://user:password@smtp.example.com:587"
# SMTP over SSL/TLS (port 465)
MAILER_DSN="smtps://user:password@smtp.example.com"
# Local SMTP server for development (e.g. Mailpit)
MAILER_DSN="smtp://localhost:1025"
# PHP mail()
MAILER_DSN="sendmail://default"
# Do not send (Debug transport)
MAILER_DSN="null://null"
```

- スキームでデータソース (`mysql`、`postgres`、`sqlite`、`sqlserver`) やトランスポート (`Smtp` には
  `smtp` / `smtps`、`Mail` には `sendmail` / `native`、`Debug` には `null`) が決まります。
- クエリパラメーターでほかのキーを設定します。データベースでは `encoding`、`schema`、`prefix`、`persistent=true`、
  `flags[1002]=...`、SMTP では `timeout`、`client`、`tls=1` などです。
- パスワードの予約文字はエンコードしてください。`@` は `%40`、`?` は `%3F`、`#` は `%23`、`%` は `%25` です。
  たとえば `p@ss?` は `p%40ss%3F` になります。
- DSN にあるキーは `config/database.php` や `config/email.php` の同じキーを置き換え、ほかのキーはそのまま残ります。
  スケルトンのファイルのキーはデフォルト値です。パスワードのない DSN (`mysql://root@localhost/my_app`) では
  ファイルの `'password' => 'password'` が残るので、空のパスワードには `mysql://root:@localhost/my_app` と書きます。
- 設定されていない変数や空の変数は無視され、ファイルのキーが使われます。
- プラグインは、ブートストラップで `CakeEmail::setDsnClassMap()` や `ConnectionManager::setDsnClassMap()` を使って
  スキーム (たとえば Amazon SES プラグインの `ses+api://`) を追加できます。
- すべての規則は [データベースの DSN](https://pieceofcake2.github.io/docs/ja/development/configuration.md#database-url) と
  [メーラーの DSN](https://pieceofcake2.github.io/docs/ja/core-utility-libraries/email.md#mailer-dsn) をご覧ください。

#### Docker と CI {#docker-and-ci}

DSN は環境変数として渡します。Docker Compose では次のようにします。

```yaml
services:
  app:
    environment:
      DATABASE_URL: "mysql://my_app:secret@db:3306/my_app?encoding=utf8mb4"
      MAILER_DSN: "smtp://mailpit:1025"
  db:
    image: mysql:8.4
    environment:
      MYSQL_DATABASE: my_app
      MYSQL_USER: my_app
      MYSQL_PASSWORD: secret
      MYSQL_RANDOM_ROOT_PASSWORD: "yes"
  mailpit:
    image: axllent/mailpit
```

GitHub Actions では、テストのステップで `DATABASE_URL` にテスト用データベースを設定します。実際の環境変数は
`config/.env.test` より優先されます。

```yaml
      - name: Run tests
        env:
          DATABASE_URL: mysql://root:root@127.0.0.1:3306/test_app?encoding=utf8mb4
        run: |
          cp config/database.php.default config/database.php
          vendor/bin/phpunit
```

ワークフロー全体の例は [継続的インテグレーション](https://pieceofcake2.github.io/docs/ja/development/continuous-integration.md) をご覧ください。

### セキュリティ用のソルトの生成 {#generating-the-security-salts}

`Security.salt` と `Security.cipherSeed` は、暗号論的に安全な乱数生成器で生成してください。
以下のコマンドは、Linux と macOS のどの POSIX シェルでも動作します (cipher seed はレガシーな `'cipher'`
Cookie のタイプでしか使いません。[cipherSeed と 'cipher' Cookie タイプ](#cipherseed-and-the-cipher-cookie-type)
をご覧ください)。

```bash
# SECURITY_SALT: 64 hexadecimal characters
php -r 'echo bin2hex(random_bytes(32)), PHP_EOL;'
openssl rand -hex 32                               # without PHP

# SECURITY_CIPHER_SEED: a number from 1 to 4294967295
php -r 'echo random_int(1, 4294967295), PHP_EOL;'
od -An -N4 -tu4 /dev/urandom | tr -dc 0-9; echo    # without PHP
```

両方を Git で無視される `config/.env.local` に追記するには次のようにします
(同じ変数が 2 回設定されている場合は、後の行が有効になります)。

```bash
php -r 'printf("SECURITY_SALT=\"%s\"\nSECURITY_CIPHER_SEED=\"%d\"\n", bin2hex(random_bytes(32)), random_int(1, 4294967295));' >> config/.env.local
```

PHP を使わない場合は次のとおりです。

```bash
printf 'SECURITY_SALT="%s"\nSECURITY_CIPHER_SEED="%s"\n' "$(openssl rand -hex 32)" "$(od -An -N4 -tu4 /dev/urandom | tr -dc 0-9)" >> config/.env.local
```

- ソルトは 32 文字以上にしてください。ソルトは `CookieComponent` のデフォルトのキーで、`'aes'` Cookie の
  タイプはそれより短いキーを受け付けません。
- これより長い cipher seed は使わないでください。意味があるのは 1〜4294967295 だけです (後述)。
- 環境ごとに異なる値を使い、コミットしないでください。本番環境では、実際の環境変数を設定してください。
- 後から `Security.salt` を変更すると、ユーザーはログアウトされ、開いているフォームは無効になり、
  暗号化された Cookie は読めなくなります。また、HMAC ソルトを明示せずに `Security::encrypt()` で暗号化した
  データと、`SimplePasswordHasher` でハッシュ化したパスワードも使えなくなります (bcrypt のハッシュは影響を
  受けません)。`Security.cipherSeed` を変更して使えなくなるのは、デフォルトの `'cipher'` タイプの Cookie など、
  `Security::cipher()` で暗号化した値だけです。

詳しい内容と、値を安全に入れ替える方法については、
[セキュリティ用のソルトの生成](https://pieceofcake2.github.io/docs/ja/development/configuration.md#security-salts) をご覧ください。

#### cipherSeed と 'cipher' Cookie タイプ {#cipherseed-and-the-cipher-cookie-type}

`Security.cipherSeed` を使うのは `Security::cipher()`、つまり `CookieComponent` の暗号化タイプ `'cipher'` だけです。
これはデフォルトのタイプで、意味のある範囲のシードを使っても安全ではありません。`config/core.php` の
フォールバックは範囲内のサンプル値ですが、それでも置き換えてください。

代わりに、32 バイト以上のランダムな専用のキーと `'aes'` タイプを使ってください。スケルトンは、PHP 8 で `'aes'` の
Cookie に必要な `Security.useOpenSsl` を `true` にし
([OpenSSL を有効にする](https://pieceofcake2.github.io/docs/ja/appendices/migrating-from-cakephp-2-10.md#enable-openssl) を参照)、ソルトの隣で
`Cookie.type` と `Cookie.key` を `config/core.php` に設定しているので、コントローラーを変更する必要はありません。

```php
Configure::write('Cookie.type', 'aes');
Configure::write('Cookie.key', env('COOKIE_KEY') ?: null);
```

`CookieComponent` は、明示的な `'type'`/`'key'` の設定 (または `type()` と `$this->Cookie->key`) を最初に使い、
次に `Cookie.type`/`Cookie.key`、最後に `'cipher'` と `Security.salt` を使います。`COOKIE_KEY` が設定されていないと、
`Cookie.key` は `null` になり、キーには `Security.salt` が使われます。ソルトが 32 バイトより短いと、暗号化された
Cookie の書き込みや読み込みで `CakeException` が投げられます。

キーは `php -r 'echo bin2hex(random_bytes(32)), PHP_EOL;'` または `openssl rand -hex 32` で生成し、
`config/.env.local` の `COOKIE_KEY` に設定してください (本番環境では実際の環境変数として設定します)。
既存のアプリケーションを `'cipher'` から `'aes'` に切り替えると、既存の Cookie の値 (たとえば、ログイン状態を保持する
remember-me) は一度失われます。シードの使われ方、影響、アプリケーションに合った選択肢、移行の手順については、
[cipherSeed と Cookie の 'cipher' タイプ](https://pieceofcake2.github.io/docs/ja/development/configuration.md#cipher-seed) をご覧ください。

## アプリケーションの実行 {#running-the-application}

開発時は、次のコマンドで PHP のビルトイン Web サーバーを起動します。

```bash
bin/cake server
```

そして `http://localhost:8765/` を開きます。別のポートを使うには `--port` (`-p`) を指定します。

本番環境では、Web サーバーのドキュメントルートを `webroot/` に向けてください。`config/`、`logs/`、`tmp/`、`vendor/`
と `.env` ファイルを含むプロジェクトディレクトリに向けてはいけません。`APP_ENV=prod` とほかの設定は実際の
環境変数で指定してください ([環境変数 (.env)](#environment-variables-env) を参照してください)。`webroot/.htaccess`
ファイルは、`mod_rewrite` を使う Apache のためにリクエストを `index.php` に書き換えます。Apache と nginx の例は
[Web サーバーの設定](https://pieceofcake2.github.io/docs/ja/installation/url-rewriting.md) を、本番環境に公開する前には [デプロイ](https://pieceofcake2.github.io/docs/ja/deployment.md) をご覧ください。

## ページとスタイル {#pages-and-styles}

レイアウト、ホームページ、エラーページ、フラッシュメッセージは
[CakePHP 5 のアプリケーションスケルトン](https://github.com/cakephp/app) から移植したもので、
`webroot/css/` にあるそのスタイルシートを使います。

| ファイル | 説明 |
| --- | --- |
| `normalize.min.css` | [normalize.css](https://github.com/necolas/normalize.css) 8.0.1 |
| `milligram.min.css` | [Milligram](https://milligram.io) 1.4.1 |
| `fonts.css` | `webroot/font/` から配信する Raleway フォント (フォントサービスへのリクエストはありません) |
| `cake.css` | アプリケーションのスタイル: レイアウト、フラッシュメッセージ、フォーム、テーブル、ページネーター、エラーページ、デバッグ出力 |
| `home.css` | ホームページだけのスタイル |
| `cake.generic.css` | 以前のスケルトンのスタイルシート。テンプレートはもう読み込みませんが、そのクラスを使っているビュー (たとえば `legacy` bake テーマで bake したビュー) のために残してあり、コアのフォールバック用レイアウトが読み込みます |

テンプレート:

- `templates/layout/default.php`: 上部のナビゲーションバーとコンテンツのコンテナ。`debug` が 2 の間は、
  コンテンツの下に SQL ログが表示されます。
- `templates/layout/error.php`: 本番環境のエラーページ用の最小限のレイアウト。
- `templates/layout/flash.php`: `Controller::flash()` が描画するページ。
- `templates/element/Flash/`: `default`、`success`、`error`、`warning`、`info` のフラッシュメッセージ
  (`$this->Flash->success('Saved.')` など)。メッセージはエスケープされます。HTML を出力するには
  `['params' => ['escape' => false]]` を渡します。メッセージをクリックすると非表示になります。

[pieceofcake2/bake](https://pieceofcake2.github.io/bake/ja/) で bake したビューも同じスタイルを使います。

### ホームページ {#home-page}

`templates/Pages/home.php` は、`debug` が有効な間 `/` に表示されます。このページはアプリケーションの環境をチェックします。
PHP のバージョンと拡張 (mbstring、intl、openssl、PDO と `default` データソースのドライバー)、`tmp/` と `logs/`
ディレクトリ、キャッシュ、データベース接続 (失敗した場合はエラーメッセージも)、セキュリティ用のソルトと
cipher seed、そして DebugKit プラグインと Bake プラグインです。本番環境では `NotFoundException` を投げるので、
`debug` を無効にする前に独自のページに置き換えてください。[ページコントローラー](https://pieceofcake2.github.io/docs/ja/controllers/pages-controller.md)
もご覧ください。

![ホームページ](https://pieceofcake2.github.io/docs/skeleton-home.png)

スマートフォンでの表示:

![スマートフォンでのホームページ](https://pieceofcake2.github.io/docs/skeleton-home-mobile.png)

### エラーページ {#error-pages}

`debug` が 0 のとき、`templates/Error/error400.php` と `error500.php` は `error` レイアウトで短いメッセージを
表示します。

![本番環境のエラー 400](https://pieceofcake2.github.io/docs/skeleton-error400.png)

![本番環境のエラー 500](https://pieceofcake2.github.io/docs/skeleton-error500.png)

デバッグモードでは、CakePHP の他のエラーページと同じように、デフォルトのレイアウトを使ってスタックトレースを表示します。

### フラッシュメッセージ {#flash-messages}

![フラッシュメッセージ](https://pieceofcake2.github.io/docs/skeleton-flash-messages.png)

### 既存のアプリケーション {#existing-applications}

以前のバージョンのスケルトンから作成したアプリケーションは影響を受けません。テンプレートと `webroot/css/` は
アプリケーション自身のものだからです。新しい見た目を使うには、`templates/layout/`、`templates/element/Flash/`、
`templates/Error/`、`webroot/css/`、`webroot/font/` と `webroot/img/cake.logo.svg` をアプリケーションに
コピーしてください (`.ctp` のテンプレートが必要なら、`Layouts/`、`Elements/`、`Errors/` にある
[pieceofcake2/app-legacy](https://pieceofcake2.github.io/docs/ja/installation/application-skeleton-legacy.md) のものを使います)。
`cake.generic.css` 向けに bake したビューは、bake し直す (またはスタイルを直す) 必要があります。
[bake したビューと legacy テーマ](https://pieceofcake2.github.io/bake/ja/#baked-views-and-the-legacy-theme)
をご覧ください。

## 開発 {#development}

### テストの実行 {#running-tests}

テストケースは `tests/TestCase/` に、フィクスチャは `tests/Fixture/` に置きます。名前空間はそれぞれ
`App\Test\TestCase` と `App\Test\Fixture` です (例: `tests/TestCase/Controller/PostsControllerTest.php` の
`App\Test\TestCase\Controller\PostsControllerTest`)。

`vendor/bin/phpunit` と `bin/cake test` は、デフォルトで `APP_ENV=test` で動きます。`APP_ENV` が設定されていなければ、
CakePHP が `.env` ファイルを読み込む前に `tests/bootstrap.php` と `bin/cake` がこれを設定します (ブラウザーのテスト
ランナー `webroot/test.php` も同じです)。そのため、テストでは `config/.env` の後に、コミットされている
`config/.env.test` が読み込まれ、`config/.env.local` は読み込まれません。`config/.env.test` はテストで変える値だけを
設定します。`DATABASE_URL` はテスト用データベースで (`default` 接続です。`APP_ENV=test` では `config/database.php` が
これを `test` 接続にコピーします)、`MAILER_DSN` は `null://null` です。自分のテスト用データベースは、Git で無視される
`config/.env.test.local` か、CI と同じようにファイルより優先される実際の環境変数で設定してください。

```dotenv
# config/.env.test.local
DATABASE_URL="mysql://my_app:secret@localhost:3306/test_my_app?encoding=utf8mb4"
```

開発用の `DATABASE_URL` は `config/.env.local` に置いたままにします。テストでは `config/.env.local` は読み込まれません。
実際の環境変数はファイルより優先されるので、テストを実行するシェルで開発用の `DATABASE_URL` やほかの `APP_ENV` を
export しないでください。フィクスチャはテーブルを削除して作成します。

それから PHPUnit でテストを実行します。

```bash
vendor/bin/phpunit
```

テストシェルで 1 つのテストケースだけを実行することもできます。

```bash
bin/cake test app Controller/PostsController
```

`phpunit.xml.dist` には 2 つのテストスイートがあります。

| スイート | ディレクトリ | 実行方法 |
| --- | --- | --- |
| `App` | `tests/TestCase/` (`EndToEnd/` を除く) | `vendor/bin/phpunit` (デフォルトのスイート) |
| `EndToEnd` | `tests/TestCase/EndToEnd/` | `vendor/bin/phpunit --testsuite EndToEnd` |

`vendor/bin/phpunit --testsuite App,EndToEnd` は両方を実行します。`phpunit.xml.dist` の `defaultTestSuite="App"`
により、`vendor/bin/phpunit` は速く、ブラウザーなしで実行できます。Symfony アプリケーションのように
**`vendor/bin/phpunit` ですべてを実行するには、この属性を 1 つ削除してください**。

テストの書き方は [テスト](https://pieceofcake2.github.io/docs/ja/development/testing.md) をご覧ください。

#### End-to-End テスト {#end-to-end-tests}

`tests/TestCase/EndToEnd/` は、[pieceofcake2/panther](https://pieceofcake2.github.io/panther/ja/) を使ったブラウザーの
End-to-End テストのためのディレクトリです。pieceofcake2/panther は Symfony Panther と同じ方法でテストを実行します。
アプリケーションを PHP のビルトイン Web サーバーで起動し、ヘッドレス Chrome で操作します。使い方は次のとおりです。

1. パッケージと、使っている Chrome に合った chromedriver をインストールします。

   ```bash
   composer require --dev "pieceofcake2/panther:^1.0.1" dbrekelmans/bdi
   vendor/bin/bdi detect drivers
   ```

2. `phpunit.xml.dist` の `ServerExtension` のコメントを外します。この拡張は、実行全体を通して Web サーバーと
   ブラウザーを動かし続けます。End-to-End テストが実行されるまでは何も起動しません。
3. `tests/TestCase/EndToEnd/` に `PieceOfCake2\Panther\TestSuite\PantherTestCase` を継承したテストを書き、
   `vendor/bin/phpunit --testsuite EndToEnd` で実行します。

Panther が起動する Web サーバーは、`APP_ENV=test` を含む PHPUnit の環境を引き継ぎます。そのため Web サーバーも
`config/.env.test` (と `config/.env.test.local`) を読み込み、ページが使う `default` 接続 (`DATABASE_URL`) がテスト用
データベースになるので、ページはテストのフィクスチャを読み込みます。ほかの設定は必要ありません。Panther の
`PANTHER_APP_ENV` が必要になるのは、Web サーバーにテストとは異なる `APP_ENV` を渡すときだけです。

これらのテストは Panther の `PANTHER_*` 変数で設定します。`phpunit.xml.dist` は、失敗したテストの
スクリーンショット用に `PANTHER_ERROR_SCREENSHOT_DIR=./tmp/error-screenshots` を設定しています。

`PANTHER_NO_HEADLESS=1` (ブラウザーの動きを見るため) のようなマシンごとの値は、シェルか、`phpunit.xml.dist` を
コピーした `phpunit.xml` で設定してください。CI では、End-to-End テストを別のジョブで実行してください。変数、
テスト用データベース、GitHub Actions の例については、[End-to-End テスト](https://pieceofcake2.github.io/docs/ja/development/e2e-testing.md) と
panther のドキュメントをご覧ください。

### コーディング規約 {#code-standards}

CakePHP のコーディング規約に沿っているかコードをチェックします。

```bash
vendor/bin/phpcs
```

`phpcs.xml` は [pieceofcake2/phpcs-cakephp2](https://pieceofcake2.github.io/phpcs-cakephp2/ja/) の
`CakePHP2Namespace` 規約を使います。クラスは名前空間の中で、クラス名と同じ名前のファイルに宣言し、使うクラスは
`use` 文でインポートします。`config/` のクラス (`DATABASE_CONFIG`、`EmailConfig` とスキーマのクラス) は
グローバルのままです。[CakePHP コーディング規約](https://pieceofcake2.github.io/docs/ja/contributing/cakephp-coding-conventions.md) もご覧ください。

### 静的解析 {#static-analysis}

`src/` と `config/` に対して PHPStan (レベル 5) を実行します。

```bash
vendor/bin/phpstan analyse
```

設定は `phpstan.neon` にあり、[pieceofcake2/phpstan-cakephp2](https://pieceofcake2.github.io/phpstan-cakephp2/ja/)
拡張を使います。[静的解析](https://pieceofcake2.github.io/docs/ja/development/static-analysis.md) もご覧ください。

## 由来 {#origin}

このパッケージは [cakephp/cakephp](https://github.com/cakephp/cakephp) (Packagist では
`cakephp/cakephp`、アップストリームの最終バージョンは 2.10.24) の `app/` ディレクトリをもとに別のパッケージとして
切り出したもので、元の作者は CakePHP コミュニティ (Cake Software Foundation, Inc.) です。
[pieceofcake2](https://github.com/pieceofcake2) が、モダンな PHP で動く CakePHP 2 向けにメンテナンスしています。
オリジナルからの変更点は [コミット履歴](https://github.com/pieceofcake2/app/commits/2.x) で確認できます。
