2025/01 CTOへの日報まとめ

日報




2025-1-6

CORSのプリフライトリクエスト(OPTIONSリクエスト)に対するキャッシュの有効期限を秒単位で指定するオプションであるMaxAgeについて理解することができました。

  • ブラウザが一度プリフライトリクエストを送信してサーバーからCORSポリシーを取得すると、そのポリシーを指定した秒数の間キャッシュする
  • MaxAgeで指定された期間内は、同じオリジン・リソース・メソッドに対するリクエストではプリフライトリクエストが不要になり、直接リクエストが送信されるようになる
2025-1-7

SQLのCOALESCE関数について知ることができました。

  • COALESCE関数は、指定した複数の引数の中から最初にNULLでない値を返す便利な関数
  • これにより、デフォルト値を設定したり、複数の列や式を順に評価して値を選ぶことができる

基本構文
COALESCE(expression1, expression2, …, expressionN)

  • expression1 から expressionN までの値を順番に評価する
  • 最初にNULLでない値を見つけたら、それを返す
  • 全ての引数がNULLの場合はNULLを返す
2025-1-8

PHP7.3以降ではsetcookie関数にSameSite属性を付与するためのオプションが存在することを知りました
https://qiita.com/sgmryk/items/3da7b242d505d24d54f8

2025-1-9

複数のコミットを一度にcherry-pickできることを知りました
git cherry-pick <ハッシュ1> <ハッシュ2> <ハッシュ3>

2025-1-14

デメテルの法則(Law of Demeter)というものを知りました。

  • デメテルの法則(Law of Demeter)とは、ソフトウェア開発における設計原則の1つ
  • オブジェクト間の関係を最小限に保つことを目的としている
  • この法則は、プログラムのモジュールが他のモジュールに過度に依存しないように設計するための指針を提供する
  • これにより、コードの保守性や再利用性が向上する

下記記事が簡潔で分かりやすかったです
https://zenn.dev/miya_tech/articles/b59916140347e2

2025-1-17

AWS CLIを使ってAPI Gatewayの情報をgetする件で、同期から質問を受けまして、自分も知見がなかったため色々調べました。その過程で色々なことを知ることができました。知ったことについて分かりやすくまとめるのは難しかったので、読んだ記事をいくつか共有しようと思います。

https://dev.classmethod.jp/articles/serverless-framework-profil-edoes-not-exist/
https://zenn.dev/geek/articles/87f26712255d83
https://qiita.com/kaito_program/items/7b9ba489e44d2295cf6f
https://qiita.com/yuki_0920/items/d78f5bd3c14c4dd12774

2025-1-20

Laravel11 × Goで個人開発しているアプリのローカル環境に、MailHogを使用してメール送信の機構を導入してみました。

下記の記事が参考になりました。
https://zenn.dev/naoki0722/articles/5b8bd8fdc22bb8
https://qiita.com/ucan-lab/items/49e7f4986564f68e95c4
https://qiita.com/tatsukoni/items/0801405afd6e9ec466fe

自分が追加したコードは下記のみです。とても簡単に構築することができました。

(docker-compose.ymlにmailHogコンテナ追加)
mailhog:
image: mailhog/mailhog
container_name: mailhog
ports:
– “8025:8025″
networks:
– applift_network

(.envを下記のように修正し、ローカル環境でのLaravelのメール送信先をMailHogコンテナに向ける)
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=”noreply@example.com”
MAIL_FROM_NAME=”${APP_NAME}”

2025-1-21 ①

PHPにおけるabstract class(抽象クラス)について知ることができました。

  • 抽象クラスとは、他のクラスが継承することを前提として設計されたクラス
  • 抽象クラスそのものは、直接インスタンス化できない
  • PHPのシンタックス上、abstractと付けたクラスには、抽象メソッド(例:abstract public function someMethod(); のような「中身なし」のメソッド)を定義可能。これにより、継承先で必ず実装させることができる
  • インターフェイスと似ているが、インターフェイスには実装が書けない(メソッドシグネチャして定義できない)のに対して、抽象クラスは通常のクラス同様、具象(具体的)なメソッドやプロパティも定義可能
  • 抽象メソッドは、抽象クラスの中でのみ定義可能

抽象クラスを使う場面

  • 継承ベースの設計を取りたいときに、抽象クラスとして枠組みや共通処理をまとめておき、サブクラス(継承先)で具体的な実装を強制・統一させるために使われる
  • メリットとして「共通の実装 + サブクラスに強制する実装」の両方をひとまとめにできる

「継承先で必ず実装させることができる」とは、つまりどういうことか

  • 抽象クラス内にある「抽象メソッド」は、サブクラス(継承先)で必ずオーバーライドし、中身(実装)を用意しなければならない
  • もし継承先でも抽象メソッドをそのままにして実装しなかった場合、その継承先自体も抽象クラスとして扱われ、やはりインスタンス化できなくなってしまう
  • つまり、サブクラスとして具象クラス(通常のクラス)を完成させたいなら、抽象メソッドの実装を書かないとエラーになる(結果的に継承先で必ず実装されるという仕組みになっている)

abstract class Animal
{
// 抽象メソッド: サブクラスで必ず実装する
abstract public function makeSound();

public function walk()
{
// これは抽象ではないので、中身のあるメソッド
echo “The animal is walking\n”;
}
}

// Dog は抽象クラス Animal を継承しているので、makeSound() を実装しないといけない
class Dog extends Animal
{
// 抽象メソッドの実装
public function makeSound()
{
echo “Woof!\n”;
}
}

// 実際に使う
$dog = new Dog();
$dog->walk(); // “The animal is walking”
$dog->makeSound(); // “Woof!”

2025-1-21 ②

2つのコミット間の変更内容だけを比較するコマンドを知ることができました

git diff <コミットID A> <コミットID B> — <ファイルパス>

コミットID の代わりにブランチ名やタグ名を指定することも可能

2025-1-22 ①

final public function foo() { … } というように、finalキーワードを使用して定義されているメソッドがどのようなメソッドなのか、理解することができました

  • finalキーワードは、PHP において 「サブクラスからのオーバーライドを禁止する」 ためのもの
  • final public function foo() { … } と定義してあるメソッドは、継承先のクラスで再定義(オーバーライド)することができない

finalキーワードの役割

1. サブクラスでのオーバーライド禁止

  • 親クラスが final を付けたメソッドは、継承先クラス(子クラス)で同名メソッドを再度定義しようとするとエラーになる
  • これにより「このメソッドの挙動は親クラスに固定しておきたい」という要件を強制できる

2. クラス自体を final にできる

  • final class SomeClass { … } と書くと、そのクラスは継承自体を禁止できる
  • つまり「このクラスは継承させたくない」「サブクラスを作られたくない」という設計上の意図がある場合に使う

class ParentClass
{
final public function cannotOverride()
{
echo “I cannot be overridden!\n”;
}

public function canOverride()
{
echo “I can be overridden.\n”;
}
}

class ChildClass extends ParentClass
{
// ここで cannotOverride() を再定義しようとするとエラーになる
public function canOverride()
{
echo “I’m overriding the parent method.\n”;
}
}

PHPUnitを使ってテストコードを書いてみようと思い、その際にPHPUnitのコードがどんな感じなのか気になって色々見ていたところ、final public functionで定義されているメソッドに遭遇し、調べてみました。

PHPUnit での final メソッドの意味としては、下記のようです

  • テストフレームワークのコアな動作を変更されないようにするために、特定のメソッドをfinal指定している
  • つまり、「ユーザーがここを勝手にオーバーライドして挙動を変えると、フレームワークとしてのテスト実行プロセスが破壊される可能性がある」という箇所に対して、finalキーワードを使ってオーバーライドを禁止することで、安全を確保している
2025-1-22 ②

PHP7.4で追加された「null合体代入演算子 ??=」について知ることができました。

$a ??= $b;

$aがnullかどうかを確認する。もしnullなら、$bを$aに代入する。もしnullでなければ、代入しない(既存の$aを維持)

下記の記述と同等
$a = $a ?? $b;

2025-1-23 ①

PHP5.6以降では、クラスだけでなく、関数や定数を use キーワードでインポートすることが可能であることを知りました。

use function Some\Namespace\someFunction;
use const Some\Namespace\SOME_CONST;

2025-1-23 ②

PHPのdeclare関数を使用して、型の厳格モードを有効にする方法について、下記記事とドキュメントで理解することができました

https://qiita.com/hinako_n/items/f1e827255dfd6cda4f52

2025-1-24

CIワークフローにて、DB接続を必要とするテストコードを実行する際に、テスト用DBとしてサーバー型DB(MySQL/PostgreSQL等)かインメモリDB(SQLite)かどちらを選ぶべきか、その判断基準やそれぞれのメリット,デメリットを知ることができました。

結論、アプリの規模や使っているDB機能、必要な精度、テスト速度などを総合的に考えて決定すると良いことを知りました

インメモリDB(SQLite)を使う場合

メリット

1. 高速

  • ディスクに書き込みを行わず、メモリ上のみで完結するため非常に速い
  • CI上でもテスト時間短縮に大いに貢献する

セットアップが楽

  • .env.testing を設定して、DB_CONNECTION=sqlite と DB_DATABASE=:memory: にするだけで完了(Laravelの場合)
  • テスト開始時に migrate すればメモリ上にテーブルが作られる

依存サービスが不要

  • MySQL/PostgreSQL のように別のサービスやコンテナを立ち上げる必要がないため、構築がシンプル

デメリット / 注意点

DB機能の差異

  • SQLite と MySQL / PostgreSQL / SQLServer 等では、サポートしている機能やSQLの細かい挙動に差がある
  • 例えば、JSONカラム、トリガー、外部キー制約の挙動、SQL構文の一部差異など、本番環境とSQLiteで挙動が異なる可能性がある

大規模・高度なDB機能を使う場合に不向き

  • もし本番で MySQL のパーティショニングや特定のプラグイン、PostgreSQL の特殊機能などをフルに使っていると、SQLite では再現できないこともある
  • 大規模アプリで DB スキーマが複雑な場合、SQLite でのテストは本番との乖離が大きくなる

CI環境での動き

  • CIでは基本的に “一時的な仮想環境・コンテナ” が立ち上がる
  • そこに composer install → php artisan test を実行すると、同じ仮想環境のメモリを使って SQLite が動く
  • テストが終われば環境ごと破棄されるため、データが残る心配は不要
  • メモリは、CIの仮想マシンやコンテナのメモリを使用する

実際のDB(MySQL/PostgreSQL等)を使う場合

(メリット)

1. 本番環境と同じDBエンジンでテストできる

  • DBの機能・クエリ挙動・制約・インデックスなどが本番と同一のため、実運用に近いテスト精度を確保できる
  • SQLite と違い「本番だと動くSQLがテストでは失敗する(またはその逆)」が起きにくい

2. 拡張機能・外部キー制約も同じ挙動

  • 外部キー制約のON/OFFや、MySQL だと InnoDB 特有の挙動、PostgreSQL の独自機能(JSONB、ARRAYなど)をテストに含めたい場合にも対応できる

(デメリット / 注意点)

1. セットアップがやや複雑

  • CI上でDBを使うには、DBコンテナ(MySQL/PostgreSQLなど)の起動設定が必要になる
  • GitHub Actions なら services: mysql: …、GitLab CIなら .gitlab-ci.yml で services: と記述するなど、CI上でDBを立ち上げて接続する形を整える必要がある

2. 速度面でSQLiteより遅くなる可能性

  • 特にI/Oの多い大規模テストでは、ディスクへの書き込みやネットワーク越しの通信が発生するぶん、インメモリより遅い

CIのリソースに依存

  • DBコンテナが立ち上がるまでの時間や初期化に時間がかかる
  • 大量のテストデータを入れる場合はそれなりに時間を要する

選択の基準

1. 本番環境に近い挙動をどこまでテストで担保したいか

  • もし MySQL や Postgres の特殊な機能を利用しているなら、本番と同じエンジンでテストするのが安全
  • 単純なCRUDが中心で、SQLite でも問題ない(互換性のあるSQLしか使ってない)なら、インメモリで高速化を狙うのもアリ

2. テストの実行速度をどこまで重視するか

  • テストが多く、CIを頻繁に回すなら、SQLiteメモリDBは非常に速い
  • 大規模なプロジェクトであっても、基本的な CRUD, Eloquent の機能テスト程度なら SQLite で十分の場合も多い
  • ただし一部の機能(外部キーなど)挙動が違う点には注意

3. チーム構成・プロジェクト規模

  • 小〜中規模:SQLite を選ぶチームが多い。セットアップ簡単&高速がメリット
  • 大規模 or 本番と全く同じ挙動必須:MySQL/Postgres をCI上に立ち上げてテスト。DBのセットアップをTerraformやDocker Composeなどで自動化し、CIでも同じ環境を再現する仕組みを作るケースが一般的

4. ステージングやE2Eテストの存在

  • もし別途、ステージング環境で MySQL/Postgres を使ったより本番に近い E2Eテストを走らせるフローがあるなら、ユニット/フィーチャーテストでは SQLite で高速に回す、という組み合わせもよくある

CI環境での実際のDB使用例

  • GitHub Actions であれば、services: というセクションに MySQL/Postgres コンテナのイメージと環境変数を指定
  • テスト用の .env.testing を DB_HOST=mysql / DB_DATABASE=testing / DB_USERNAME=root … のように設定
  • CI が起動時に自動で MySQL/Postgres コンテナを立ち上げ、テスト時にはそこへ接続し php artisan migrate → php artisan test という流れが回る

(yamlコード例)
jobs:
test:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8
env:
MYSQL_DATABASE: testing
MYSQL_ROOT_PASSWORD: secret
ports:
– 3306:3306
# …
steps:
– uses: actions/checkout@v2
– name: Install dependencies
run: composer install
– name: Run tests
run: php artisan test

これで毎回クリーンなMySQLコンテナが立ち上がり、テスト後に破棄される

まとめ

SQLite in-memory

  • メリット:高速・セットアップが簡単・別途サービスが不要
  • デメリット:DB機能の差異があり、本番と挙動が100%一致しない可能性

(適用例)

  • 小〜中規模アプリ
  • 基本的なCRUD中心かつ SQLite との互換性が十分
  • とにかくテストを高速化したい

本番と同じDB (MySQL/Postgres)

  • メリット:本番との挙動差異が少なく、DBに依存した機能も実際どおりに検証可能
  • デメリット:CIでのセットアップが複雑になり、速度的にもインメモリより遅め

適用例

  • 大規模でDB機能をフル活用している
  • 外部キー制約や特有の拡張機能などの挙動を正確にテストする必要がある
  • 本番に極力近い環境でのフィーチャーテストが必須

総合的に、まずはインメモリSQLiteを導入してみて問題なければそのまま運用、もし本番DB特有の機能が使われていてテスト結果に誤差が出るようなら、CIでもMySQL/Postgresを立ち上げる という二段構えが多いようでした。

2025-1-27 ①

PHP 5.4 から導入された「Traitのasエイリアス機能」について知ることができました。

  • トレイト(以降、元のトレイト)をuseしたトレイト(以降、自作トレイト)内で、元のトレイトに存在するメソッドと同名のメソッドを定義しオーバーライドする際に、元のトレイトのメソッドを呼ぶために使用される
  • 自作トレイト内にて、単に同名のメソッドを定義してしまうと、元の実装にアクセスできなくなる
  • そこで、元のメソッドをエイリアスし、その上でオーバーライドしたメソッドの中から「必要に応じて元の処理を呼び出す」というパターンがよく使われる

trait ChildTrait
{
use ParentTrait {
// ParentTrait::methodAを baseMethodA という別名で呼べるように
methodA as baseMethodA;
}

protected function methodA() // オーバーライドメソッド
{
// … 処理 …
// もし元のmethodA()の処理を呼びたい場合はこう書ける
// $this->baseMethodA();
// … 処理 …
}
}

2025-1-27 ②

特定のファイルだけを選んで stashする方法を知ることができました

# 例) fileA と fileB の変更だけを stash に保存
git stash push -m “stash only fileA and fileB” — fileA fileB

2025-1-28

最近はPHPでテストコードを書いています。

これまでテストコードを書いた経験がなかったため、どういったメソッドに対してどういったテストメソッドを書くべきなのか知見がありませんでしたが、最近分かってきました

public function test_insert()
{
table1::create([
‘column1 => 1,
‘column1’ => 1,
]);
}

例えば、上記のようなinsertを行うメソッドに対しては、下記のようなテストコードを書けば良いことを知りました

public function test_test_insert(): void
{
$this->assertDatabaseMissing(‘table1’, [ // 該当のレコードがないことを確認
‘column1’ => 1,
‘column2’ => 1,
], ‘DB1’);

$controller = new \App\Http\Controllers\FooController;
$controller->test_insert();

$this->assertDatabaseHas(‘table1’, [ // insertが正しく行われたことを確認
‘column1’ => 1,
‘column2’ => 1,
], ‘DB1’);
}

2025-1-29 ①

composer.lock に記載されている “platform”: {“php”: “^8.2”} が何を意味している記述なのか分からなかったため、調べました

  • これはComposer が依存関係を解決したときに「PHPバージョン 8.2 を想定してパッケージを選定した」という情報の一部
  • PHP 8.2 以上の環境で動くパッケージ構成 になっていると解釈することができる

よくあるケース

  • PHP 8.2 の環境で composer install や composer update を行ったため、dependency resolution(依存パッケージの整合性チェック)が「PHP 8.2 の環境」を前提に行われている
  • その結果として、 composer.lock に “platform”: {“php”: “8.2…”} という情報が書き込まれている、というパターンが多い
2025-1-29 ②

シェルのブレース展開について知ることができました
https://qiita.com/ine1127/items/6e5fe80f4a9c64509558

2025-1-30 ①

GitHub Actions のワークフローで、依存関係やビルド成果物をキャッシュするための公式アクションである「actions/cache」によりキャッシュされたデータがどこに保存されるのか知らなかったため、調べました。

  • 結論:キャッシュは GitHub の専用ストレージに保存される
  • GitHub Actions の実行環境は エフェメラル(使い捨て) であり、ワークフローが終了するとその仮想環境は破棄される
  • しかし、 actions/cache で生成されたデータは、GitHubが管理するキャッシュ用の場所に一時的に保管され、次回のワークフロー実行時にそのキャッシュを取得できるようになっている
  • 例えば、composer.lockのハッシュが同一なら、前回のキャッシュをダウンロードして展開し、composer install を高速化する仕組み
2025-1-30 ②

GitHub Actions のコンテキスト変数の1つである ${{ runner.os }} について知ることができました。

  • ${{ runner.os }} は、OS環境を自動で取得してくれる
  • 具体的には Linux / Windows / macOS といった文字列がこの変数に自動的にセットされる
  • 例えば、runs-on: ubuntu-latest を指定していれば、この変数の値は Linux となる
  • このような「ランナー(ジョブを実行するサーバー)がどのOSか」を示す変数をキャッシュキーなどに埋め込むと「OSが違えば別キャッシュにする」ことができ、混在を防ぎやすくなる
2025-1-31 ①

CIワークフローでテストコードを実行し自動テストを行う場合に、テスト実行環境の構築にdockerを用いるべきかどうかの判断基準について知ることができました。

結論、DockerやDocker Composeを使うかどうかは、テスト実行環境をどこまで本番環境に近い形、かつ依存サービスを含め一貫性を持って扱いたいか、によって判断するとよい。

Docker Compose を使うケースの主な理由・メリット

1、外部サービス(DB など)の起動・依存関係管理

  • MySQL、PostgreSQL、Redis、などの依存サービスが必要な場合、それらを本番と同様のバージョン・設定で起動することができる
  • CI環境でも docker-compose up -d 一発でサービスが立ち上がり、テスト開始前のセットアップが明確になる

2、本番環境に近いコンテナ構成で動かす

  • Laravelアプリケーションの場合、それ自体も Docker 化しておけば、本番と同じミドルウェア、同じPHPバージョン、同じライブラリなどでテストを実行できる
  • たとえば「本番はNginx + PHP-FPMのDockerコンテナだが、ローカルではXAMPP」などという差異があると、トラブルの原因になりやすい
  • Docker Compose を使うと、「ローカル環境」「CI環境」「本番環境」の構成をなるべく統一しやすい

3、開発チーム全体での共通化

  • チームが複数人いる場合、環境構築手順に差が出にくい
  • 新しいメンバーが入っても docker compose up だけで開発~テスト実行まで整備できる

4、スケールするテスト環境

  • ある程度大規模になってくると、複数のサービスを並列起動して統合的にテストする需要が出てくる
  • Docker Compose は 複数コンテナの起動や設定を一括で管理 できるため、Microservices構成などでも使いやすい

Docker Composeがいらないケース

  • ファイルベースのSQLiteを使用したテストなど、依存するサービス(MySQLやPostgreSQLなど)が無い場合は、単に「PHPが動く環境」だけあればテストを実行できる
  • GitHub Actions は素の Ubuntu 環境を提供しており、そこで直接「composer install & php artisan test」を叩けば問題なくテストが完結する(php artisan test = Laravelでのテストコード実行コマンド)
  • テストに使う .env や SQLite ファイルを用意するだけで十分
  • そのため、こういった場合は「Dockerを使うほどでもない」「docker-compose.yml をわざわざ書いて管理するメリットが薄い」と感じるケースが多い

どういうときに「Dockerを使わない」方がシンプルか?

  • 依存サービスが無い(例、DBはSQLiteのみで完結する)
  • ローカル/本番/CI で使うPHPバージョンやOSがほぼ同じであり、敢えてDocker化しなくてもズレが生じにくい
  • プロジェクトが小規模で、アプリ以外に必要な依存サービスがほぼない
  • CI/CDでそこまで複雑なセットアップは不要で、Actionsで直接 php artisan test させるだけで事足りる

こうしたケースでは「Docker Compose なし」で十分シンプルに回せる

2025-1-31 ②

GitHub Actionsが提供するキャッシュ機能 (actions/cache@v3)で利用可能なrestore-keys というオプションがどういったオプションなのか知ることができました。

  • actions/cache@v3 には、「完全に一致するキャッシュキーがなかった場合に、 ある程度の部分一致(プレフィックス)があるキーを使ってキャッシュを探す」という仕組みがある
  • その部分一致キーが restore-keys

(具体例)
jobs:
build:
runs-on: ubuntu-latest
steps:
– uses: actions/checkout@v4
– name: Cache dependencies
uses: actions/cache@v3
with:
path: composer.lock
key: ${{ runner.os }}-composer-${{ hashFiles(‘composer.lock) }}
restore-keys: ${{ runner.os }}-composer-
   // 省略

上記の場合、まずLinux-composer- (もし OS がLinuxなら) というキーを探す。もし見つからなければ、 Linux-composer- というプレフィックスまで一致するキャッシュを探す。それも見つからなければミス(キャッシュなし)

こうすることで 「composer.lock が微妙に変わった場合でも、ある程度似通ったバージョンのキャッシュが使えるかもしれない」 と期待して、少しでもインストールの手間を軽減しようとする仕組み。実際には、厳密に一致していないキャッシュ がヒットしても完璧な再利用はできないが、一部パッケージは活かせる場合があり、インストールが少しだけ速くなる可能性がある。