2017-07-04 82 views
7

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ń.

+0

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 –

+0

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

Odpowiedz

1

Po przyjrzeniu się numer linii błędu w phpseclib\Net\SSH2, wygląda mi na to, że nadchodzi z send_binary_packet(), która rzuca ten wyjątek, gdy jej fsock jest albo nie jest ważny zasób lub osiągnął markera EOF.

Sprawdzam, gdzie są używane obiekty dziedziczące po klasie Base i zobacz, co się z nimi dzieje. Twój numer dateGenerator może nie być wywoływany jawnie, ale nadal może być coś, co widzi i nie lubi go z jakiegoś powodu. Może nawet przekopać się przez kod, który nie jest twój.:/

+0

Dzięki za twoje przemyślenia na ten temat. Nie mam nawrotu problemu od lipca (i nadal pracuję nad tym samym kodem). Podejrzewam, że jeśli ponownie dodałem zamknięcie, to wróci, ale wolę moją poprawkę. Najlepszym pomysłem jaki miałem na temat 'dateGenerator' może być (przypadkowo) wykorzystana jest serializacja/deserializacja wykonywana automatycznie przez PHPUnit pomiędzy testami. – halfer