19.

PHP static 完全ガイド (Static Method/Late Static Binding)

編集
この記事の要点
  • static method / property はインスタンス化せず ClassName::method() で呼べる
  • self:: は記述したクラスを指す、static:: は呼び出し元クラスを指す (Late Static Binding)
  • 関数内 static $count = 0; は呼び出し間で値を保持
  • static はグローバル状態になりやすく テストが困難。DI コンテナで回避
  • PHP 8+: 戻り値型に static を指定可 (Fluent Builder 等)

static method と static property の基本

class Counter {
    public static int $count = 0;

    public static function increment(): void {
        self::$count++;
    }

    public static function reset(): void {
        self::$count = 0;
    }
}

// インスタンス化せずに使う
Counter::increment();
Counter::increment();
echo Counter::$count;   // 2
Counter::reset();

呼び出し方:

  • ClassName::method() — 通常
  • self::method() — 記述したクラス内から
  • static::method() — 呼び出し元クラス基準 (Late Static Binding)
  • parent::method() — 親クラスの static を呼ぶ

self:: と static:: の違い (Late Static Binding)

class Animal {
    public static function create(): static {
        return new self();    // ← Animal 固定
    }

    public static function createV2(): static {
        return new static();  // ← 呼び出したクラス (子も含む)
    }
}

class Dog extends Animal {}

var_dump(Animal::create());     // Animal
var_dump(Dog::create());        // Animal (self だから)
var_dump(Dog::createV2());      // Dog    (static だから)
キーワード指すもの用途
self::記述したクラス (Early Binding)子クラスでも親の挙動を維持したい
static::呼び出し元クラス (Late Static Binding, PHP 5.3+)子クラスで挙動を上書きしたい
parent::親クラス親の static メソッドを呼ぶ

Static Factory パターン

class User {
    private function __construct(
        public string $name,
        public string $email,
    ) {}

    public static function register(string $name, string $email): static {
        // バリデーション
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException();
        }
        return new static($name, $email);
    }

    public static function fromArray(array $data): static {
        return new static($data['name'], $data['email']);
    }
}

$u = User::register('taro', 'taro@example.com');
$u = User::fromArray(['name' => 'jiro', 'email' => 'j@example.com']);

Singleton (推奨しない例)

class DBConnection {
    private static ?self $instance = null;
    private PDO $pdo;

    private function __construct() {
        $this->pdo = new PDO(/* ... */);
    }

    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

DBConnection::getInstance()->pdo->query('SELECT 1');

// ❌ 問題: テストでモック化不能、グローバル状態
// → DI コンテナ (Laravel Service Container) の使用を推奨

関数内 static 変数 (関数呼出間で値を保持)

function counter(): int {
    static $count = 0;   // 初回のみ初期化
    return ++$count;
}

counter();   // 1
counter();   // 2
counter();   // 3
// → 関数を抜けても値が消えない

// 用途: メモ化
function expensiveCalc(int $n): int {
    static $cache = [];
    if (isset($cache[$n])) return $cache[$n];
    return $cache[$n] = /* 重い計算 */ $n * $n;
}

static の問題点とテスト

// ❌ 静的依存はテストしにくい
class OrderService {
    public function place(Order $o): void {
        $now = DateTime::now();    // ← 直接呼び出し
        Mailer::send($o->email);   // ← 静的呼び出し → モック不能
    }
}

// ✅ DI でインスタンス注入
class OrderService {
    public function __construct(
        private ClockInterface $clock,
        private MailerInterface $mailer,
    ) {}

    public function place(Order $o): void {
        $now = $this->clock->now();
        $this->mailer->send($o->email);
    }
}
// → テストで Clock / Mailer のモックを差し替え可能

戻り値型としての static (PHP 8+)

// Fluent Builder で便利
class QueryBuilder {
    private array $wheres = [];

    public function where(string $col, $val): static {
        $this->wheres[] = [$col, $val];
        return $this;
    }
}

class CustomQueryBuilder extends QueryBuilder {
    public function newMethod(): static { /* ... */ return $this; }
}

// 子クラスで連鎖しても型が CustomQueryBuilder に推論される
(new CustomQueryBuilder())->where('id', 1)->newMethod();

定数 const と static の使い分け

class Config {
    // 定数: 不変
    public const MAX_RETRY = 3;

    // static property: 変更可
    public static int $timeout = 30;
}

Config::MAX_RETRY;       // 3
Config::$timeout = 60;   // 変更可

// 定数は ::CONSTANT、static は ::$variable

よくあるエラー

エラー原因対処
Using $this when not in object contextstatic method 内で $this 使用self / static / new self() を使う
Non-static method ... cannot be called statically通常メソッドを ::method() で呼んだstatic にするか new でインスタンス化
Cannot access static:: when no class scope is activeクラス外で static::クラス内/メソッド内で使う

FAQ

Q: static を使うべきか避けるべきか?
A: ユーティリティ関数 (純粋関数) なら OK。状態を持つもの (DB 接続 / 設定) は DI 推奨。

Q: self と static、どっち書けばいい?
A: 継承を考慮するなら static:: (LSB)、固定なら self::。最近は static 派が増加。

Q: static メソッドはなぜテストしにくい?
A: モックライブラリの多くがインスタンスメソッドにしか対応しないため。回避するには「静的呼び出しをラップするインスタンスメソッドを作る」or「DI で差し替える」。

編集
Post Share
子ページ
  1. static変数
  2. static関数
同階層のページ
  1. 基本事項
  2. HTMLへの埋め込み
  3. 変数
  4. 可変変数
  5. 定数
  6. データ型
  7. キャスト
  8. エスケープ文字
  9. 配列
  10. 演算子
  11. 代入の際の注意点
  12. 条件分岐
  13. 繰り返し処理
  14. クラスとインスタンス
  15. コンストラクタ
  16. 関数
  17. スーパーグローバル変数
  18. スコープ
  19. staticについて
  20. yieldについて
  21. ファイルのアップロード方法
  22. DB接続方法
  23. SQL実行方法
  24. カプセル化の具体例
  25. 継承の構文
  26. オーバーライド
  27. ポリモーフィズム(多様性)の具体例
  28. 抽象クラス・メソッドの構文と具体例
  29. GET通信
  30. try catchで全てのエラーを拾う方法

最近更新/作成されたページ