From 70dd67c4d9ada5d00e15ed8e717af46f8d9692f7 Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Thu, 27 Aug 2015 12:45:28 +0300 Subject: [PATCH] Connection reuse refactoring to rid of globals --- phpseclib/Net/SFTP/Stream.php | 10 ++--- phpseclib/Net/SSH2.php | 54 +++++++++++++++++++++++++ tests/Functional/Net/SFTPStreamTest.php | 13 ++++++ tests/Functional/Net/SFTPTestCase.php | 3 ++ tests/Unit/Net/SSH2Test.php | 12 ++++++ 5 files changed, 87 insertions(+), 5 deletions(-) diff --git a/phpseclib/Net/SFTP/Stream.php b/phpseclib/Net/SFTP/Stream.php index 2758e73c..98bac5fe 100644 --- a/phpseclib/Net/SFTP/Stream.php +++ b/phpseclib/Net/SFTP/Stream.php @@ -19,6 +19,7 @@ namespace phpseclib\Net\SFTP; use phpseclib\Crypt\RSA; use phpseclib\Net\SFTP; +use phpseclib\Net\SSH2; /** * SFTP Stream Wrapper @@ -166,13 +167,12 @@ class Stream } } - if ($host[0] == '$') { - $host = substr($host, 1); - global $$host; - if (($$host instanceof SFTP) === false) { + if (preg_match('/^{[a-z0-9]+}$/i', $host)) { + $host = SSH2::getConnectionByResourceId($host); + if ($host === false) { return false; } - $this->sftp = $$host; + $this->sftp = $host; } else { if (isset($this->context)) { $context = stream_context_get_options($this->context); diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 210af478..8cef9a1e 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -866,6 +866,14 @@ class SSH2 */ var $agent; + /** + * Connection storage to replicates ssh2 extension functionality: + * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples} + * + * @var SSH2[] + */ + static $connections; + /** * Default Constructor. * @@ -959,6 +967,8 @@ class SSH2 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY') ); + self::$connections[$this->getResourceId()] = $this; + if (is_resource($host)) { $this->fsock = $host; return; @@ -2836,6 +2846,7 @@ class SSH2 if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { fclose($this->realtime_log_file); } + unset(self::$connections[$this->getResourceId()]); } /** @@ -4152,4 +4163,47 @@ class SSH2 $this->windowColumns = $columns; $this->windowRows = $rows; } + + /** + * @return string + */ + function __toString() + { + return $this->getResourceId(); + } + + /** + * We use {} because that symbols should not be in URL according to + * {@link http://tools.ietf.org/html/rfc3986#section-2 RFC}. + * It will safe us from any conflicts, because otherwise regexp will + * match all alphanumeric domains. + * + * @return string + */ + function getResourceId() + { + return '{' . spl_object_hash($this) . '}'; + } + + /** + * Return existing connection + * + * @param string $id + * + * @return bool|SSH2 will return false if no such connection + */ + static function getConnectionByResourceId($id) + { + return isset(self::$connections[$id]) ? self::$connections[$id] : false; + } + + /** + * Return all excising connections + * + * @return SSH2[] + */ + static function getConnections() + { + return self::$connections; + } } diff --git a/tests/Functional/Net/SFTPStreamTest.php b/tests/Functional/Net/SFTPStreamTest.php index b596c4f8..aab16ec9 100644 --- a/tests/Functional/Net/SFTPStreamTest.php +++ b/tests/Functional/Net/SFTPStreamTest.php @@ -27,6 +27,19 @@ class Functional_Net_SFTPStreamTest extends Functional_Net_SFTPTestCase $this->assertSame(0, $this->sftp->size('fooo.txt')); } + /** + * Tests connection reuse functionality same as ssh2 extension: + * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples} + */ + public function testConnectionReuse() + { + $originalConnectionsCount = count(\phpseclib\Net\SSH2::getConnections()); + $session = $this->sftp; + $dirs = scandir("sftp://$session/"); + $this->assertCount($originalConnectionsCount, \phpseclib\Net\SSH2::getConnections()); + $this->assertEquals(array('.', '..'), array_slice($dirs, 0, 2)); + } + protected function buildUrl($suffix) { return sprintf( diff --git a/tests/Functional/Net/SFTPTestCase.php b/tests/Functional/Net/SFTPTestCase.php index abd5999d..aec75e71 100644 --- a/tests/Functional/Net/SFTPTestCase.php +++ b/tests/Functional/Net/SFTPTestCase.php @@ -13,6 +13,9 @@ use phpseclib\Net\SFTP; */ abstract class Functional_Net_SFTPTestCase extends PhpseclibFunctionalTestCase { + /** + * @var SFTP + */ protected $sftp; protected $scratchDir; diff --git a/tests/Unit/Net/SSH2Test.php b/tests/Unit/Net/SSH2Test.php index df9651af..1d3c6439 100644 --- a/tests/Unit/Net/SSH2Test.php +++ b/tests/Unit/Net/SSH2Test.php @@ -110,6 +110,18 @@ class Unit_Net_SSH2Test extends PhpseclibTestCase $this->assertFalse($ssh->isQuietModeEnabled()); } + public function testGetConnectionByResourceId() + { + $ssh = new \phpseclib\Net\SSH2('localhost'); + $this->assertSame($ssh, \phpseclib\Net\SSH2::getConnectionByResourceId($ssh->getResourceId())); + } + + public function testGetResourceId() + { + $ssh = new \phpseclib\Net\SSH2('localhost'); + $this->assertSame('{' . spl_object_hash($ssh) . '}', $ssh->getResourceId()); + } + /** * @return \phpseclib\Net\SSH2 */