迁移到 PHP 8.0:使用 $this 时取消绑定 $this
Migrate to PHP 8.0: Unbinding $this when $this is used
一步一步,我正在将我的项目从 PHP 7.1 迁移到 PHP 8.0.
在官方 PHP 手册中,“已弃用的功能” 一章的子章节 “从 PHP 7 迁移。 3.x to PHP 7.4.x",我试着理解了下面的deprecation description:
Unbinding $this
when $this
is used
Unbinding $this
of a non-static closure that uses $this
is deprecated.
虽然,没有成功。
这就是为什么我很感激,如果有人能详细解释我的意思,这是什么意思。也许代码片段也有帮助。
非常感谢您的宝贵时间!
也许这对解释也有帮助:
在我的项目中,我认为只有一种情况可以适用此弃用通知:下面介绍的 RouteCollection
class 的方法 executeGroupHandler
.我更愿意粘贴更多 class 的代码,以帮助您理解我使用方法 executeGroupHandler
.
的上下文
路线集合class:
<?php
namespace Packages\Router;
//...
use Packages\Router\RouteCollectionInterface;
/**
* Route collection.
*/
class RouteCollection implements RouteCollectionInterface {
//...
/**
* Group patterns list. Indexed array.
*
* Each time a group handler is executed its pattern is saved in this list.
* All addRoute operations taken place inside the scope of a group handler
* prefix the pattern of the corresponding route with the saved group pattern.
*
* @var array
*/
private $groupPatterns = [];
//...
/**
* Add a group (helper method).
*
* @param string $pattern Group pattern.
* @param \Closure $handler Group handler.
* @return $this
*/
public function group(string $pattern, \Closure $handler) {
$this->addGroup($pattern, $handler);
return $this;
}
/**
* Add a group.
*
* @param string $pattern Group pattern.
* @param \Closure $handler Group handler.
* @return $this
*/
private function addGroup(string $pattern, \Closure $handler) {
$this->saveGroupPattern($pattern);
$this->executeGroupHandler($handler);
/*
* Remove the last group pattern from the group patterns list. This step
* is performed only after all calls for adding groups/routes inside the
* scope of the current group handler have finished their processing.
*/
$this->popLastGroupPattern();
return $this;
}
/**
* Save a group pattern.
*
* @param string $pattern Group pattern.
* @return $this
*/
private function saveGroupPattern(string $pattern) {
$this->groupPatterns[] = $pattern;
return $this;
}
/**
* Execute a group handler.
*
* Temporarily bind the group handler to the route collection
* object - defined by the argument in Closure::call - and
* execute it. Inside the scope of the group handler, the route
* collection will be accessed using the keyword "$this".
*
* @link https://www.php.net/manual/en/closure.call.php Closure::call
*
* @param \Closure $handler Group handler.
* @return mixed The return value of calling the handler.
*/
private function executeGroupHandler(\Closure $handler) {
return $handler->call($this);
}
/**
* Pop the group pattern off the end of group patterns list.
*
* @return string The popped group pattern.
*/
private function popLastGroupPattern() {
return array_pop($this->groupPatterns);
}
}
RouteCollection的使用class:
定义了 RouteCollection
class 后,我使用它类似于以下内容:
<?php
use Packages\Router\RouteCollection;
use SampleMvc\App\View\Template\Users\AddUser as AddUserView;
use SampleMvc\App\Controller\Users\AddUser as AddUserController;
$routeCollection = new RouteCollection();
// Add a group of routes to the route collection.
$routeCollection->group('/users/add', function() {
$this->get('', [AddUserView::class, 'index']);
$this->post('', [
'controller' => AddUserController::class,
'view' => [AddUserView::class, 'addUser'],
]);
});
//...
问得好,我也不明白 PHP 中的“闭包的解除绑定变量”是什么意思。
我写了一个简单的测试来检查绑定另一个 $this
是否可以被视为“解除绑定 $this
”:
class ClosureTest extends TestCase
{
private $x = 1;
public function testClosure(): void
{
var_dump(\PHP_VERSION);
$callable = function (): int {
return $this->x;
};
self::assertSame(1, \call_user_func($callable));
$closure = \Closure::fromCallable($callable);
$obj = new class {
private $x = 2;
};
self::assertSame(2, $closure->call($obj));
}
}
我已经 运行 它在 7.3 和 8.0 下,但没有收到任何通知,所以可能您的代码在 8.0 下 运行 也是安全的。但我会进一步调查这个问题。
弃用是 proposed (and accepted) in this RFC,它提供了有关弃用内容和原因的更多详细信息。
最后一句解释了哪些闭包受到影响:
In particular this applies to non-static closures declared inside non-static methods. A $this
binding can be avoided in the first place by marking the closure as static.
这后来进一步缩小 in this commit,因此它仅适用于闭包中实际提到 $this
的闭包。
同时,第一句更清楚地说明了 什么 被弃用:
Currently it is possible to unbind the $this variable from a closure that originally had one by using $closure->bindTo(null)
.
关键词是unbind而不是rebind,例子中的null
尼基塔有更多背景资料 in this comment:
The reason we're interested in this deprecation is exclusively to enable some performance improvements in PHP 8 related to $this accesses, which is enabled by the removal of static calls to non-static methods. $this accesses can be split into two categories: Those where we know $this to be non-null and those where we don't. Method calls (will) fall into the former category. With this deprecation closures will also fall into the former category.
换句话说,在 PHP 8 中,引擎会盲目地假设闭包中对 $this
的任何引用实际上都是一个对象,而不是 null。
所以被弃用的特定场景是你有一个提到 $this
的闭包,然后你 取消绑定 它,所以 $this
不是'根本没有设置任何东西。只要您为 $this
提供新值,您应该不会受到影响,因为 $this
永远不会是 null
.
一步一步,我正在将我的项目从 PHP 7.1 迁移到 PHP 8.0.
在官方 PHP 手册中,“已弃用的功能” 一章的子章节 “从 PHP 7 迁移。 3.x to PHP 7.4.x",我试着理解了下面的deprecation description:
Unbinding
$this
when$this
is usedUnbinding
$this
of a non-static closure that uses$this
is deprecated.
虽然,没有成功。
这就是为什么我很感激,如果有人能详细解释我的意思,这是什么意思。也许代码片段也有帮助。
非常感谢您的宝贵时间!
也许这对解释也有帮助:
在我的项目中,我认为只有一种情况可以适用此弃用通知:下面介绍的 RouteCollection
class 的方法 executeGroupHandler
.我更愿意粘贴更多 class 的代码,以帮助您理解我使用方法 executeGroupHandler
.
路线集合class:
<?php
namespace Packages\Router;
//...
use Packages\Router\RouteCollectionInterface;
/**
* Route collection.
*/
class RouteCollection implements RouteCollectionInterface {
//...
/**
* Group patterns list. Indexed array.
*
* Each time a group handler is executed its pattern is saved in this list.
* All addRoute operations taken place inside the scope of a group handler
* prefix the pattern of the corresponding route with the saved group pattern.
*
* @var array
*/
private $groupPatterns = [];
//...
/**
* Add a group (helper method).
*
* @param string $pattern Group pattern.
* @param \Closure $handler Group handler.
* @return $this
*/
public function group(string $pattern, \Closure $handler) {
$this->addGroup($pattern, $handler);
return $this;
}
/**
* Add a group.
*
* @param string $pattern Group pattern.
* @param \Closure $handler Group handler.
* @return $this
*/
private function addGroup(string $pattern, \Closure $handler) {
$this->saveGroupPattern($pattern);
$this->executeGroupHandler($handler);
/*
* Remove the last group pattern from the group patterns list. This step
* is performed only after all calls for adding groups/routes inside the
* scope of the current group handler have finished their processing.
*/
$this->popLastGroupPattern();
return $this;
}
/**
* Save a group pattern.
*
* @param string $pattern Group pattern.
* @return $this
*/
private function saveGroupPattern(string $pattern) {
$this->groupPatterns[] = $pattern;
return $this;
}
/**
* Execute a group handler.
*
* Temporarily bind the group handler to the route collection
* object - defined by the argument in Closure::call - and
* execute it. Inside the scope of the group handler, the route
* collection will be accessed using the keyword "$this".
*
* @link https://www.php.net/manual/en/closure.call.php Closure::call
*
* @param \Closure $handler Group handler.
* @return mixed The return value of calling the handler.
*/
private function executeGroupHandler(\Closure $handler) {
return $handler->call($this);
}
/**
* Pop the group pattern off the end of group patterns list.
*
* @return string The popped group pattern.
*/
private function popLastGroupPattern() {
return array_pop($this->groupPatterns);
}
}
RouteCollection的使用class:
定义了 RouteCollection
class 后,我使用它类似于以下内容:
<?php
use Packages\Router\RouteCollection;
use SampleMvc\App\View\Template\Users\AddUser as AddUserView;
use SampleMvc\App\Controller\Users\AddUser as AddUserController;
$routeCollection = new RouteCollection();
// Add a group of routes to the route collection.
$routeCollection->group('/users/add', function() {
$this->get('', [AddUserView::class, 'index']);
$this->post('', [
'controller' => AddUserController::class,
'view' => [AddUserView::class, 'addUser'],
]);
});
//...
问得好,我也不明白 PHP 中的“闭包的解除绑定变量”是什么意思。
我写了一个简单的测试来检查绑定另一个 $this
是否可以被视为“解除绑定 $this
”:
class ClosureTest extends TestCase
{
private $x = 1;
public function testClosure(): void
{
var_dump(\PHP_VERSION);
$callable = function (): int {
return $this->x;
};
self::assertSame(1, \call_user_func($callable));
$closure = \Closure::fromCallable($callable);
$obj = new class {
private $x = 2;
};
self::assertSame(2, $closure->call($obj));
}
}
我已经 运行 它在 7.3 和 8.0 下,但没有收到任何通知,所以可能您的代码在 8.0 下 运行 也是安全的。但我会进一步调查这个问题。
弃用是 proposed (and accepted) in this RFC,它提供了有关弃用内容和原因的更多详细信息。
最后一句解释了哪些闭包受到影响:
In particular this applies to non-static closures declared inside non-static methods. A
$this
binding can be avoided in the first place by marking the closure as static.
这后来进一步缩小 in this commit,因此它仅适用于闭包中实际提到 $this
的闭包。
同时,第一句更清楚地说明了 什么 被弃用:
Currently it is possible to unbind the $this variable from a closure that originally had one by using
$closure->bindTo(null)
.
关键词是unbind而不是rebind,例子中的null
尼基塔有更多背景资料 in this comment:
The reason we're interested in this deprecation is exclusively to enable some performance improvements in PHP 8 related to $this accesses, which is enabled by the removal of static calls to non-static methods. $this accesses can be split into two categories: Those where we know $this to be non-null and those where we don't. Method calls (will) fall into the former category. With this deprecation closures will also fall into the former category.
换句话说,在 PHP 8 中,引擎会盲目地假设闭包中对 $this
的任何引用实际上都是一个对象,而不是 null。
所以被弃用的特定场景是你有一个提到 $this
的闭包,然后你 取消绑定 它,所以 $this
不是'根本没有设置任何东西。只要您为 $this
提供新值,您应该不会受到影响,因为 $this
永远不会是 null
.