makeEmpty/make silently loses property values for PHP 8.4+ hooked properties
まだ誰も着手していません。
評価
調査の方向性
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 はありません
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
Codeception/Stub のほかの issue
-
難易度 3/5 1〜2日 初心者へのやさしさ 42/100
Codeception/Stub#45 · コメント 1 件 ·
-
難易度 3/5 1〜2日 初心者へのやさしさ 35/100
Codeception/Stub#36 ·
-
難易度 3/5 1〜2日 初心者へのやさしさ 35/100
Codeception/Stub#30 · リアクション 1 件 ·
-
難易度 3/5 1〜2日 初心者へのやさしさ 48/100
Codeception/Stub#7 · リアクション 5 件 ·
-
難易度 1/5 1時間未満 初心者へのやさしさ 48/100
Codeception/Stub#31 · コメント 3 件 ·
Codeception/Stub の issue をすべて見る
似ている issue
-
jira-created
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 70/100
nunomaduro/phpinsights#745 ·
-
status/awaiting_triage
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
WordPress/plugin-check#1486 ·
-
enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
az-digital/az_quickstart#6019 ·