Napotkałem bardzo dziwny problem i pomyślałem, że go nagrałem, aby potencjalnie wykryć możliwy błąd. Na razie mam akceptowalne obejście.Czy są jakieś znane efekty uboczne z przechowywaniem zamknięć PHP we właściwościach obiektu?
Mój projekt to oparte na PHP narzędzie do wdrażania, które wykorzystuje phpseclib do tworzenia połączeń SSH i SFTP ze zdalnym serwerem. Miałem z tym więcej szczęścia niż rozszerzenie SSH2, więc zdecydowałem się na to teraz. Używam wersji 7.0.16 w środowisku Dockerised Alpine 3.5. Problem pojawia się, gdy uruchamiam testy funkcjonalne wewnątrz PHPUnit, które łączą się z rzeczywistym (lokalnym) serwerem SSH.
Kiedy przygotowuję plik do przesłania, przechodzi proces tworzenia tymczasowej kopii, dzięki czemu mogę wprowadzać modyfikacje dla poszczególnych przypadków, używając wyszukiwania i zamiany ciągów. Do testowania chociaż chcę coś mniej dynamiczny, więc myślałem, że chciałbym dodać zamknięcie klasie szeroko zdefiniować czym data wygląda następująco:
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'); };
}
}
Więc pomysł jest tutaj zamknięcie jest „prawdziwy” generator, i Mogę zastąpić go w środowiskach testowych tylko czymś statycznym.
Więc tutaj jest przejawem problemu - wiele ogłoszeń:
~ # ./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
Zauważ, że $this->dateGenerator
nie jest faktycznie wykorzystywany w dowolnym miejscu. Teraz tutaj jest kuriozum, mogę zastąpić go z pustym zamknięcia, a dostaję powiadomień ponownie:
$this->dateGenerator = function() {};
Jednak gdybym skomentować, że obecnie, zawiadomienia znikają.
#$this->dateGenerator = function() {};
Próbowałem wiele rzeczy wokół tego i jestem całkowicie pewien, że zmieniam tylko jedną rzecz. Zastanawiałem się, czy problem polega na przechowywaniu zamknięć w ctorcie, więc spróbowałem wstrzyknąć puste zamknięcie w setera i otrzymuję powiadomienia ponownie.
Wymieniłem zamknięcie na zajęcia, a problem znowu zniknie. Tak to naprawię, ale problem z powiadomieniami denerwuje mnie z powodu biblioteki phpseclib, a nawet stabilności wszystkiego, co buduję! Doceniam to, że od kiedy używam PHP, Docker, PHPUnit, phpseclib, ssh i sshd, istnieje wiele rzeczy, które mogą wstrzyknąć problem.
Na liście biletów phpseclib ta wzmianka jest wymieniona w #1125 i #985, ale obie wydają się mieć konkretną przyczynę, a nie pozornie niepowiązaną właściwość, którą tu demonstruję.
Jakieś pomysły na to, jak mogę to zbadać? Obecnie uważam, że zamknięcie ma efekt uboczny, którego nie jestem świadomy. Próbowałem nawet anulować zamknięcie w destruktorze, na wypadek gdyby trzeba było "zamknąć", ale to nie powstrzymało zawiadomień.
Jedyny problem, który wiem o zamknięciach, to że nie można ich serializować/deserializować bez użycia niektórych bibliotek/hacków. Ale wtedy powinien rzucić "Serializacja" zamknięcia "nie jest dozwolone" błąd –
Tak, to może być warte odkrywania, @Michal - dzięki. Wiem, że jest kilka opcji w PHPUnit dotyczących serializacji zasobów między testami, więc to może być coś, co warto spróbować. Nie miałem już żadnych problemów związanych z tym, więc będzie musiał poczekać, kiedy będę w stanie zrobić kilka spokojnych badań ":-)". – halfer