Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

makeEmpty/make silently loses property values for PHP 8.4+ hooked properties

オープン
#61 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
68/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
静か
技術スタック
php
領域
testing

調査の方向性

Stub.php の523〜526行付近から始めます。ここでは Stub::bindParameters() が PHPUnit のモックオブジェクトに対して親のリフレクションへ切り替えています。PHP 8.4+ の hooked property で問題を再現してください。PHPUnit 13 が '$prop::get' を通じて hooked properties をどのように公開しているかを確認し、hooked properties をどのように検出すべきかを判断してください。makeEmpty() と make() に渡された値が、プロパティの型が final class である場合を含め、引き続き読み取り可能であれば完了です。

索引モデルが issue の本文から書いたものです。

説明

Environment

  • PHP: 8.5.6 (reproduces on PHP 8.4+, the version that introduced property hooks)
  • codeception/stub: 4.3.0
  • phpunit/phpunit: 13.0.6

Problem

When mocking a class that has PHP 8.4 property hooks, values passed to makeEmpty() / make() for hooked properties are silently lost. Depending on the property type, this either returns null / an auto-generated stub, or throws a RuntimeException if the property type is a final class.

A real-world trigger: symfony/http-foundation 8.1 added set hooks with deprecation warnings to all major public bags on Request ($request, $query, $attributes, etc.), causing all tests that mock Request with those properties to break.

Minimal reproduction

class Foo
{
    public string $bar {
        set { $this->bar = $value; }
    }
}

// Value is silently lost
$mock = $this->makeEmpty(Foo::class, ['bar' => 'baz']);
var_dump($mock->bar); // expected: string(3) baz, actual: auto-stub or error

With a final-typed property (e.g. InputBag):

// Throws RuntimeException:
// Return value for MockObject_Request::::get() cannot be generated:
// Class "Symfony\Component\HttpFoundation\InputBag" is declared "final"
$mock = $this->makeEmpty(Request::class, ['request' => new InputBag(['foo' => 'bar'])]);
$mock->request->get('foo'); // boom

Root cause

Stub::bindParameters() deliberately switches to getParentClass() for PHPUnit mock objects (line 523–526 in Stub.php), then calls ReflectionProperty::setValue() on the parent's reflection:

if ($mock instanceof PHPUnitMockObject) {
    $reflectionClass = $reflectionClass->getParentClass(); // Request::class
}
// ...
$reflectionProperty->setValue($mock, $value); // writes to parent's backing store

In PHP 8.4, a class that redeclares a property with hooks (as PHPUnit's mock generator does when the source class has hooks — see phpunit#6549) gets its own separate backing store. The write goes into the parent's backing store; the read goes through the mock's generated get hook, which reads from the mock's backing store — they never meet.

Possible fix direction

For properties that have hooks in the source class, bindParameters() should configure the mock via PHPUnit's mock API instead of ReflectionProperty::setValue():

// PHPUnit 13 exposes hooked properties as mockable methods named '$prop::get'
$mock->method('$bar::get')->willReturn('baz');

This requires detecting hooked properties (via ReflectionProperty::hasHooks() / getHook(), available since PHP 8.4) and branching accordingly.

主要言語
PHP
スター
301
フォーク
19
PR マージ指標
30日以内にマージされた PR はありません

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

Codeception/Stub のほかの issue

Codeception/Stub の issue をすべて見る

似ている issue

PHP の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。