You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony ObjectProphecy数组与对象表现差异:setUp初始化数组无法修改

问题与解答

测试代码

<?php

use Monolog\Test\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophet;

class MyBar {
    public int $bar = 0;
}

class MyFoo {
    public function fooArray(): array {
        return [];
    }

    public function fooBar(): MyBar {
        return new MyBar();
    }

    public function fooInt(): int {
        return 0;
    }
}

final class SimpleTest extends TestCase
{
    private Prophet $prophet;
    private ObjectProphecy $mock;
    private array $myArray;
    private MyBar $myBar;
    private int $myInt;

    public function getProphet(): Prophet {
        return $this->prophet;
    }

    protected function setUp(): void {
        $this->prophet = new Prophet();
        $this->myArray = [0];
        $this->myBar = new MyBar();
        $this->myInt = 0;
        $this->mock = $this->getProphet()->prophesize(MyFoo::class);
        $this->mock->fooArray()->willReturn($this->myArray);
        $this->mock->fooBar()->willReturn($this->myBar);
        $this->mock->fooInt()->willReturn($this->myInt);
    }
    
    public function test(): void {
        $this->myArray = [1];
        $this->assertEquals([0], $this->mock->reveal()->fooArray());

        $this->myBar->bar = 1;
        $this->assertEquals(1, $this->mock->reveal()->fooBar()->bar);

        $this->myInt = 1;
        $this->assertEquals(0, $this->mock->reveal()->fooInt());
    }
}

问题

  1. 为何数组$myArray、整型$myInt修改后不生效,但对象$myBar修改后生效?
  2. 如何让数组像对象一样可以修改?(要求使用Prophecy框架内方案,不使用MockObject)

解答

1. 三者表现不一致的原因

这是PHP中值类型和引用类型的差异导致的:

  • 整型$myInt是值类型,setUp中调用willReturn($this->myInt)时,传递的是变量的值拷贝。后续修改$this->myInt,不会影响Prophecy内部保存的拷贝值。
  • 数组在PHP中默认按值传递,willReturn($this->myArray)传递的是数组的拷贝。后续重新赋值$this->myArray = [1],只是改变了当前变量指向的数组,和Prophecy保存的数组拷贝无关。
  • 对象$myBar是引用类型,willReturn($this->myBar)传递的是对象引用。后续修改对象属性,本质是操作同一个实例,所以Mock返回的对象属性会同步变化。

2. 让数组可修改的Prophecy方案

方式一:用闭包动态返回当前数组

在setUp中,将willReturn替换为will方法,传入闭包。每次调用Mock方法时,闭包会返回当前最新的$this->myArray:

$this->mock->fooArray()->will(function () {
    return $this->myArray;
});

测试方法中修改$this->myArray后,Mock返回的就是更新后的数组。

方式二:用对象包裹数组

把数组封装到对象中,利用对象的引用特性间接实现数组可修改:

  1. 定义数组包裹类:
class ArrayWrapper {
    public array $data = [];
}
  1. 修改测试类的变量和Mock设置:
private ArrayWrapper $myArrayWrapper;

protected function setUp(): void {
    // 其他代码不变
    $this->myArrayWrapper = new ArrayWrapper();
    $this->myArrayWrapper->data = [0];
    $this->mock->fooArray()->willReturn($this->myArrayWrapper->data);
}
  1. 测试方法中修改对象内的数组:
$this->myArrayWrapper->data = [1];
$this->assertEquals([1], $this->mock->reveal()->fooArray());

通过对象引用传递的特性,修改对象内的数组时,Mock返回的内容会同步更新。


内容的提问来源于stack exchange,提问作者user_51

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 17:17:17