我遇到了一个很奇怪的问题,我想我会把它记录下来,以便潜在地发现一个可能的错误。我现在有一个可接受的解决方法。
我的项目是一个基于 PHP 的部署工具,它使用 phpseclib 与远程服务器建立 SSH 和 SFTP 连接。与 SSH2 扩展相比,我在这方面的运气更好,所以我决定暂时坚持使用它。我在 Dockerised Alpine 3.5 环境中运行 PHP 7.0.16。当我在连接到真实(本地)SSH 服务器的 PHPUnit 中运行功能测试时,问题就出现了。
当我准备要传输的文件时,它会经历一个创建临时副本的过程,这样我就可以使用字符串的搜索和替换在每个案例的基础上进行修改。为了测试,虽然我想要一些不太动态的东西,所以我想我会添加一个类范围的闭包来定义日期的样子:
abstract class Base
{
// (other properties here)
protected $dateGenerator;
public function __construct(QueueState $queueState, BaseFetcher $fetcher)
{
$this->queueState = $queueState;
$this->fetcher = $fetcher;
// Sets a default date generator
$this->dateGenerator = function() { return date('r'); };
}
}
所以这里的想法是闭包是“真正的”生成器,我只能在测试环境中用静态的东西替换它。
所以,这是问题的表现 - 很多通知:
~ # ./phpunit test/functional/tests/Sftp --stop-on-error
PHPUnit 6.2.2 by Sebastian Bergmann and contributors.
........... 11 / 11 (100%)
Time: 7.56 seconds, Memory: 6.00MB
OK (11 tests, 17 assertions)
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice: Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
请注意,$this->dateGenerator
实际上并未在任何地方使用。现在,奇怪的是,我可以用一个空闭包替换它,然后我再次收到通知:
$this->dateGenerator = function() {};
但是,如果我将其注释掉,通知就会消失。
#$this->dateGenerator = function() {};
围绕这个我已经尝试了很多东西,而且我完全确定我只是改变了一件事。我想知道问题是否出在 ctor 中存储闭包,所以我尝试在 setter 中注入(inject)空闭包,然后我再次收到通知。
我已经将闭包换成一个类,问题又消失了。这就是我修复它的方式,但是通知问题让我对 phpseclib 库或者我正在构建的所有东西的稳定性感到紧张!我很感激,因为我使用的是 PHP、Docker、PHPUnit、phpseclib、ssh 和 sshd,所以有很多东西可能会注入(inject)问题。
在 phpseclib 票单列表中,此通知在 #1125 中提到和 #985 ,但两者似乎都有特定的原因,而不是我在这里展示的明显无关的属性。
关于我如何研究这个的任何想法?目前我认为闭包有一个我不知道的副作用。我什至试图在析构函数中使闭包无效,以防它需要“关闭”,但这并没有停止通知。
最佳答案
在 phpseclib\Net\SSH2
中查找错误的行号后,在我看来它来自 send_binary_packet()
,当它的 fsock
不是有效资源或已达到 EOF 标记。
我会查看从该 Base
类继承的任何对象的使用位置,并查看对它们做了什么。您的 dateGenerator
可能没有被显式调用,但仍然可能有一些东西看到它并且出于某种原因不喜欢它。甚至可能不得不挖掘不属于您自己的代码。 :/
关于php - 在对象属性中存储 PHP 闭包是否有任何已知的副作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44908207/