I have encountered a very strange problem, and I thought I would record it so as to potentially uncover a possible bug. I have an acceptable workaround for now.
My project is a PHP-based deployment tool that uses phpseclib to make SSH and SFTP connections to a remote server. I have had more luck with this than the SSH2 extension, so I've decided to stick with it for now. I am running PHP 7.0.16 inside a Dockerised Alpine 3.5 environment. The problem surfaces when I am running functional tests inside PHPUnit that connect to a real (local) SSH server.
When I prepare a file for transmission, it goes through a process of creating a temporary copy so I can make modifications on a per-case basis, using the search and replace of strings. For testing though I want something less dynamic, so I thought I would add a class-wide closure to define what a date looks like:
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'); };
}
}
So the idea here is the closure is the "real" generator, and I can replace it in test environments only with something static.
So, here is the manifestation of the problem - lots of notices:
~ # ./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
Note that $this->dateGenerator is not actually used anywhere. Now, here is the oddity, I can replace it with an empty closure, and I get the notices again:
$this->dateGenerator = function() {};
However, if I comment that out, the notices disappear.
#$this->dateGenerator = function() {};
I have tried a lot of things around this, and I am entirely sure I am only changing one thing. I wondered if the problem was storing closures in the ctor, so I tried injecting the empty closure in a setter, and I get the notices again.
I have swapped the closure for a class, and the problem goes away again. That is how I will fix it, but the notices problem makes me nervous about the phpseclib library, or indeed the stability of everything I am building! I appreciate that since I am using PHP, Docker, PHPUnit, phpseclib, ssh and sshd, there are a lot of things that could inject an issue.
On the phpseclib ticket list, this notice is mentioned in #1125 and #985, but both seem to have a specific cause, rather than the apparently unrelated property I am demonstrating here.
Any ideas on how I can research this? Currently I am theorising that closures have a side-effect that I am not aware of. I even tried to null the closure in a destructor, in case it needed to be "closed", but this did not stop the notices.