欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

为什么 PSR-7 规范中 RequestInterface 中用 withX 而不是 setX

程序员文章站 2022-05-13 08:26:09
...
比如 SlimPHP 中的 Request 实现。
public function withAttribute($name, $value)
{
    $clone = clone $this;
    $clone->attributes->set($name, $value);
    return $clone;
}

with 的实现都涉及到 clone $object,clone 就会申请新的变量空间。为什么不用 set 呢?一个请求过来一个 Request 对象就可以了吧。

public function setAttribute($name, $value)
{
    $this->attributes->set($name, $value);
    return $this;
}

回复内容:

比如 SlimPHP 中的 Request 实现。

public function withAttribute($name, $value)
{
    $clone = clone $this;
    $clone->attributes->set($name, $value);
    return $clone;
}

with 的实现都涉及到 clone $object,clone 就会申请新的变量空间。为什么不用 set 呢?一个请求过来一个 Request 对象就可以了吧。

public function setAttribute($name, $value)
{
    $this->attributes->set($name, $value);
    return $this;
}

好问题!

因为PSR-7规定,HTTP Message和URI都是值对象(value object),值对象的一个特征是它的值决定了它的唯一性,也就是说如果两个值对象的所有值都是一样的,那它们也应该是同一个对象;反过来说,如果两个值对象有至少一个值不一样,那它们就应该算两个不同的对象。正因为这个原因,当遵循PSR-7而你要改变Request一个属性时,你应该创建另一个对象,而不能在原来的对象上做修改,所以要用clone而不能简单地赋值。所以,值对象是一种不可变(immutable)的对象。

那么,PSR-7为什么要选用不可变的值对象来规范HTTP Message呢?原因有以下几点:

  1. 因为HTTP Message本身就因该是一个不可变的状态。当一个用户给你的程序发送请求,发送完成后请求的内容就已经固定,不会再变,只会有下一个相同或不同的请求。

  2. 值对象可以保存原始请求的一切状态,任何其他的程序都可以获得这种原始状态。

官方FIG提供了一段示范值对象好处的代码:

$uri = new Uri('http://api.example.com');
$baseRequest = new Request($uri, null, [
    'Authorization' => 'Bearer ' . $token,
    'Accept'        => 'application/json',
]);;
//上面构造基础 Request

$request = $baseRequest->withUri($uri->withPath('/user'))->withMethod('GET');
$response = $client->send($request);
//基于上述基础Request构造一个GET请求,获得user的ID

$body = new StringStream(json_encode(['tasks' => [
    'Code',
    'Coffee',
]]));;
$request = $baseRequest
    ->withUri($uri->withPath('/tasks/user/' . $userId))
    ->withMethod('POST')
    ->withHeader('Content-Type', 'application/json')
    ->withBody($body);
$response = $client->send($request)
//基于基础Request构造另一个POST请求,用刚才拿到的user ID添加task
//如果不是使用值对象模型,我们将要重新从头开始构建这个Request


$request = $baseRequest->withUri($uri->withPath('/tasks'))->withMethod('GET');
$response = $client->send($request);
//依然是基于基础Request构建其他请求

其实这些在PSR-7的Meta Document中都有说明
http://www.php-fig.org/psr/psr-7/meta/

相关标签: psr-7 php