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.
After looking up the line number of the error in phpseclib\Net\SSH2
, it looks to me like it's coming from send_binary_packet()
, which throws that exception when its fsock
is either not a valid resource or has reached an EOF marker.
I would look into where any objects are used that inherit from this Base
class, and see what is done with them. Your dateGenerator
may not be called explicitly, but there could still be something out there seeing it and not liking it for some reason. Might even have to dig through code that's not your own. :/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With