diff --git a/phpseclib/Crypt/Common/AsymmetricKey.php b/phpseclib/Crypt/Common/AsymmetricKey.php index 30d1b4a0..06c28022 100644 --- a/phpseclib/Crypt/Common/AsymmetricKey.php +++ b/phpseclib/Crypt/Common/AsymmetricKey.php @@ -130,6 +130,11 @@ abstract class AsymmetricKey { self::initialize_static_variables(); + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('load() should not be called from final classes (' . static::class . ')'); + } + $components = false; foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) { if (isset(self::$invisiblePlugins[static::ALGORITHM]) && in_array($format, self::$invisiblePlugins[static::ALGORITHM])) { diff --git a/phpseclib/Crypt/DH.php b/phpseclib/Crypt/DH.php index a387f26c..0cf0c428 100644 --- a/phpseclib/Crypt/DH.php +++ b/phpseclib/Crypt/DH.php @@ -82,6 +82,11 @@ abstract class DH extends AsymmetricKey */ public static function createParameters(...$args): Parameters { + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')'); + } + $params = new Parameters(); if (count($args) == 2 && $args[0] instanceof BigInteger && $args[1] instanceof BigInteger) { //if (!$args[0]->isPrime()) { @@ -242,6 +247,11 @@ abstract class DH extends AsymmetricKey */ public static function createKey(Parameters $params, int $length = 0): PrivateKey { + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); + } + $one = new BigInteger(1); if ($length) { $max = $one->bitwise_leftShift($length); @@ -380,9 +390,9 @@ abstract class DH extends AsymmetricKey */ public function getParameters(): AsymmetricKey { - $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters'); + $type = DH::validatePlugin('Keys', 'PKCS1', 'saveParameters'); $key = $type::saveParameters($this->prime, $this->base); - return self::load($key, 'PKCS1'); + return DH::load($key, 'PKCS1'); } } diff --git a/phpseclib/Crypt/DH/Parameters.php b/phpseclib/Crypt/DH/Parameters.php index 958bb8f3..7e8106ed 100644 --- a/phpseclib/Crypt/DH/Parameters.php +++ b/phpseclib/Crypt/DH/Parameters.php @@ -20,7 +20,7 @@ use phpseclib3\Crypt\DH; * * @author Jim Wigginton */ -class Parameters extends DH +final class Parameters extends DH { /** * Returns the parameters diff --git a/phpseclib/Crypt/DH/PrivateKey.php b/phpseclib/Crypt/DH/PrivateKey.php index 608d22cb..0d3089f5 100644 --- a/phpseclib/Crypt/DH/PrivateKey.php +++ b/phpseclib/Crypt/DH/PrivateKey.php @@ -22,7 +22,7 @@ use phpseclib3\Math\BigInteger; * * @author Jim Wigginton */ -class PrivateKey extends DH +final class PrivateKey extends DH { use Common\Traits\PasswordProtected; diff --git a/phpseclib/Crypt/DH/PublicKey.php b/phpseclib/Crypt/DH/PublicKey.php index 7ffd6fab..134b39a1 100644 --- a/phpseclib/Crypt/DH/PublicKey.php +++ b/phpseclib/Crypt/DH/PublicKey.php @@ -22,7 +22,7 @@ use phpseclib3\Math\BigInteger; * * @author Jim Wigginton */ -class PublicKey extends DH +final class PublicKey extends DH { use Common\Traits\Fingerprint; diff --git a/phpseclib/Crypt/DSA.php b/phpseclib/Crypt/DSA.php index 0094bc56..a5974718 100644 --- a/phpseclib/Crypt/DSA.php +++ b/phpseclib/Crypt/DSA.php @@ -106,6 +106,11 @@ abstract class DSA extends AsymmetricKey { self::initialize_static_variables(); + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')'); + } + if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } @@ -181,6 +186,11 @@ abstract class DSA extends AsymmetricKey { self::initialize_static_variables(); + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); + } + if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } diff --git a/phpseclib/Crypt/DSA/Parameters.php b/phpseclib/Crypt/DSA/Parameters.php index 5dc8a718..243f0de5 100644 --- a/phpseclib/Crypt/DSA/Parameters.php +++ b/phpseclib/Crypt/DSA/Parameters.php @@ -20,7 +20,7 @@ use phpseclib3\Crypt\DSA; * * @author Jim Wigginton */ -class Parameters extends DSA +final class Parameters extends DSA { /** * Returns the parameters diff --git a/phpseclib/Crypt/DSA/PrivateKey.php b/phpseclib/Crypt/DSA/PrivateKey.php index 43a5a747..e781963a 100644 --- a/phpseclib/Crypt/DSA/PrivateKey.php +++ b/phpseclib/Crypt/DSA/PrivateKey.php @@ -23,7 +23,7 @@ use phpseclib3\Math\BigInteger; * * @author Jim Wigginton */ -class PrivateKey extends DSA implements Common\PrivateKey +final class PrivateKey extends DSA implements Common\PrivateKey { use Common\Traits\PasswordProtected; diff --git a/phpseclib/Crypt/DSA/PublicKey.php b/phpseclib/Crypt/DSA/PublicKey.php index 5a0af509..77dc8f4a 100644 --- a/phpseclib/Crypt/DSA/PublicKey.php +++ b/phpseclib/Crypt/DSA/PublicKey.php @@ -22,7 +22,7 @@ use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature; * * @author Jim Wigginton */ -class PublicKey extends DSA implements Common\PublicKey +final class PublicKey extends DSA implements Common\PublicKey { use Common\Traits\Fingerprint; diff --git a/phpseclib/Crypt/EC.php b/phpseclib/Crypt/EC.php index 23d59241..81ff6577 100644 --- a/phpseclib/Crypt/EC.php +++ b/phpseclib/Crypt/EC.php @@ -142,6 +142,11 @@ abstract class EC extends AsymmetricKey { self::initialize_static_variables(); + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); + } + if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } diff --git a/phpseclib/Crypt/EC/Parameters.php b/phpseclib/Crypt/EC/Parameters.php index 96782865..256177bb 100644 --- a/phpseclib/Crypt/EC/Parameters.php +++ b/phpseclib/Crypt/EC/Parameters.php @@ -20,7 +20,7 @@ use phpseclib3\Crypt\EC; * * @author Jim Wigginton */ -class Parameters extends EC +final class Parameters extends EC { /** * Returns the parameters diff --git a/phpseclib/Crypt/EC/PrivateKey.php b/phpseclib/Crypt/EC/PrivateKey.php index a104106a..99f1c05f 100644 --- a/phpseclib/Crypt/EC/PrivateKey.php +++ b/phpseclib/Crypt/EC/PrivateKey.php @@ -32,7 +32,7 @@ use phpseclib3\Math\BigInteger; * * @author Jim Wigginton */ -class PrivateKey extends EC implements Common\PrivateKey +final class PrivateKey extends EC implements Common\PrivateKey { use Common\Traits\PasswordProtected; diff --git a/phpseclib/Crypt/EC/PublicKey.php b/phpseclib/Crypt/EC/PublicKey.php index ef394360..b6e619e4 100644 --- a/phpseclib/Crypt/EC/PublicKey.php +++ b/phpseclib/Crypt/EC/PublicKey.php @@ -30,7 +30,7 @@ use phpseclib3\Math\BigInteger; * * @author Jim Wigginton */ -class PublicKey extends EC implements Common\PublicKey +final class PublicKey extends EC implements Common\PublicKey { use Common\Traits\Fingerprint; diff --git a/phpseclib/Crypt/RSA.php b/phpseclib/Crypt/RSA.php index dbeb352d..b38e9af3 100644 --- a/phpseclib/Crypt/RSA.php +++ b/phpseclib/Crypt/RSA.php @@ -299,6 +299,11 @@ abstract class RSA extends AsymmetricKey { self::initialize_static_variables(); + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); + } + $regSize = $bits >> 1; // divide by two to see how many bits P and Q would be if ($regSize > self::$smallestPrime) { $num_primes = floor($bits / self::$smallestPrime); diff --git a/phpseclib/Crypt/RSA/PrivateKey.php b/phpseclib/Crypt/RSA/PrivateKey.php index c113b247..08bcf45e 100644 --- a/phpseclib/Crypt/RSA/PrivateKey.php +++ b/phpseclib/Crypt/RSA/PrivateKey.php @@ -28,7 +28,7 @@ use phpseclib3\Math\BigInteger; * * @author Jim Wigginton */ -class PrivateKey extends RSA implements Common\PrivateKey +final class PrivateKey extends RSA implements Common\PrivateKey { use Common\Traits\PasswordProtected; diff --git a/phpseclib/Crypt/RSA/PublicKey.php b/phpseclib/Crypt/RSA/PublicKey.php index 90d1bd4b..ef9e1247 100644 --- a/phpseclib/Crypt/RSA/PublicKey.php +++ b/phpseclib/Crypt/RSA/PublicKey.php @@ -32,7 +32,7 @@ use phpseclib3\Math\BigInteger; * * @author Jim Wigginton */ -class PublicKey extends RSA implements Common\PublicKey +final class PublicKey extends RSA implements Common\PublicKey { use Common\Traits\Fingerprint; diff --git a/tests/Functional/Net/SSH2Test.php b/tests/Functional/Net/SSH2Test.php index 38e615e7..614af984 100644 --- a/tests/Functional/Net/SSH2Test.php +++ b/tests/Functional/Net/SSH2Test.php @@ -110,6 +110,7 @@ class SSH2Test extends PhpseclibFunctionalTestCase /** * @depends testPasswordLogin * @group github280 + * @requires PHPUnit < 10 */ public function testExecWithMethodCallback(SSH2 $ssh): SSH2 { diff --git a/tests/PhpseclibTestCase.php b/tests/PhpseclibTestCase.php index 0418c7d2..3418736c 100644 --- a/tests/PhpseclibTestCase.php +++ b/tests/PhpseclibTestCase.php @@ -99,66 +99,36 @@ abstract class PhpseclibTestCase extends TestCase // assertIsArray was not introduced until PHPUnit 8 public static function assertIsArray($actual, string $message = ''): void { - if (method_exists(parent::class, 'assertIsArray')) { - parent::assertIsArray($actual, $message); - return; - } - parent::assertInternalType('array', $actual, $message); } // assertIsString was not introduced until PHPUnit 8 public static function assertIsString($actual, string $message = ''): void { - if (method_exists(parent::class, 'assertIsString')) { - parent::assertIsString($actual, $message); - return; - } - parent::assertInternalType('string', $actual, $message); } // assertIsResource was not introduced until PHPUnit 8 public static function assertIsResource($actual, string $message = ''): void { - if (method_exists(parent::class, 'assertIsResource')) { - parent::assertIsResource($actual, $message); - return; - } - parent::assertInternalType('resource', $actual, $message); } // assertIsObject was not introduced until PHPUnit 8 public static function assertIsObject($actual, string $message = ''): void { - if (method_exists(parent::class, 'assertIsObject')) { - parent::assertIsObject($actual, $message); - return; - } - parent::assertInternalType('object', $actual, $message); } // assertContains is deprecated for strings in PHPUnit 8 public static function assertStringContainsString(string $needle, string $haystack, string $message = ''): void { - if (method_exists(parent::class, 'assertStringContainsString')) { - parent::assertStringContainsString($needle, $haystack, $message); - return; - } - parent::assertContains($needle, $haystack, $message); } // assertNotContains is deprecated for strings in PHPUnit 8 public static function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void { - if (method_exists(parent::class, 'assertStringContainsString')) { - parent::assertStringNotContainsString($needle, $haystack, $message); - return; - } - parent::assertNotContains($needle, $haystack, $message); } @@ -167,10 +137,6 @@ abstract class PhpseclibTestCase extends TestCase */ public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void { - if (method_exists(parent::class, 'assertMatchesRegularExpression')) { - parent::assertMatchesRegularExpression($pattern, $string, $message); - } else { - parent::assertRegExp($pattern, $string, $message); - } + parent::assertRegExp($pattern, $string, $message); } } diff --git a/tests/Unit/Crypt/HashTest.php b/tests/Unit/Crypt/HashTest.php index e4a5fb45..821f7ca3 100644 --- a/tests/Unit/Crypt/HashTest.php +++ b/tests/Unit/Crypt/HashTest.php @@ -171,7 +171,7 @@ class HashTest extends PhpseclibTestCase } /** - * @dataProvider hmacData() + * @dataProvider hmacData */ public function testHMAC($hash, $key, $message, $result): void { @@ -179,7 +179,7 @@ class HashTest extends PhpseclibTestCase } /** - * @dataProvider hmacData() + * @dataProvider hmacData */ public function testHMAC96($hash, $key, $message, $result): void { @@ -372,7 +372,7 @@ class HashTest extends PhpseclibTestCase } /** - * @dataProvider hashData() + * @dataProvider hashData */ public function testHash($hash, $message, $result): void { @@ -380,7 +380,7 @@ class HashTest extends PhpseclibTestCase } /** - * @dataProvider hashData() + * @dataProvider hashData */ public function testHash96($hash, $message, $result): void { diff --git a/tests/Unit/Net/SSH2UnitTest.php b/tests/Unit/Net/SSH2UnitTest.php index cd87b4e6..59c206fe 100644 --- a/tests/Unit/Net/SSH2UnitTest.php +++ b/tests/Unit/Net/SSH2UnitTest.php @@ -34,6 +34,7 @@ class SSH2UnitTest extends PhpseclibTestCase /** * @dataProvider formatLogDataProvider + * @requires PHPUnit < 10 */ public function testFormatLog(array $message_log, array $message_number_log, $expected): void { @@ -43,6 +44,9 @@ class SSH2UnitTest extends PhpseclibTestCase $this->assertEquals($expected, $result); } + /** + * @requires PHPUnit < 10 + */ public function testGenerateIdentifier(): void { $identifier = self::callFunc($this->createSSHMock(), 'generate_identifier'); @@ -70,6 +74,9 @@ class SSH2UnitTest extends PhpseclibTestCase } } + /** + * @requires PHPUnit < 10 + */ public function testGetExitStatusIfNotConnected(): void { $ssh = $this->createSSHMock(); @@ -77,12 +84,18 @@ class SSH2UnitTest extends PhpseclibTestCase $this->assertFalse($ssh->getExitStatus()); } + /** + * @requires PHPUnit < 10 + */ public function testPTYIDefaultValue(): void { $ssh = $this->createSSHMock(); $this->assertFalse($ssh->isPTYEnabled()); } + /** + * @requires PHPUnit < 10 + */ public function testEnablePTY(): void { $ssh = $this->createSSHMock(); @@ -94,6 +107,9 @@ class SSH2UnitTest extends PhpseclibTestCase $this->assertFalse($ssh->isPTYEnabled()); } + /** + * @requires PHPUnit < 10 + */ public function testQuietModeDefaultValue(): void { $ssh = $this->createSSHMock(); @@ -101,6 +117,9 @@ class SSH2UnitTest extends PhpseclibTestCase $this->assertFalse($ssh->isQuietModeEnabled()); } + /** + * @requires PHPUnit < 10 + */ public function testEnableQuietMode(): void { $ssh = $this->createSSHMock(); diff --git a/tests/make_compatible_with_phpunit7.php b/tests/make_compatible_with_phpunit7.php new file mode 100644 index 00000000..deb68aca --- /dev/null +++ b/tests/make_compatible_with_phpunit7.php @@ -0,0 +1,31 @@ + $files */ +$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(__DIR__)); +foreach ($files as $file) { + if ($file->getExtension() === 'php' && $file->getPathname() !== __FILE__) { + $fileContents = file_get_contents($file->getPathname()); + if ($fileContents === false) { + throw new \RuntimeException('file_get_contents() failed: ' . $file->getPathname()); + } + $patternToReplacementMap = [ + '~ function setUpBeforeClass\(\)~' => ' function setUpBeforeClass(): void', + '~ function setUp\(\)~' => ' function setUp(): void', + '~ function tearDown\(\)~' => ' function tearDown(): void', + '~ function assertIsArray\(\$actual, \$message = \'\'\)~' => ' function _assertIsArray($actual, string $message = \'\')', + '~ function assertIsResource\(\$actual, \$message = \'\'\)~' => ' function _assertIsResource($actual, string $message = \'\')', + '~ function assertIsObject\(\$actual, \$message = \'\'\)~' => ' function _assertIsObject($actual, string $message = \'\')', + '~ function assertIsString\(\$actual, \$message = \'\'\)~' => ' function _assertIsString($actual, string $message = \'\')', + '~ function assertStringContainsString\(\$needle, \$haystack, \$message = \'\'\)~' => ' function _assertStringContainsString(string $needle, string $haystack, string $message = \'\')', + '~ function assertStringNotContainsString\(\$needle, \$haystack, \$message = \'\'\)~' => ' function _assertStringNotContainsString(string $needle, string $haystack, string $message = \'\')' + ]; + $updatedFileContents = preg_replace( + array_keys($patternToReplacementMap), + array_values($patternToReplacementMap), + $fileContents + ); + if (file_put_contents($file->getPathname(), $updatedFileContents) === false) { + throw new \RuntimeException('file_put_contents() failed: ' . $file->getPathname()); + } + } +} diff --git a/tests/make_compatible_with_phpunit9.php b/tests/make_compatible_with_phpunit9.php new file mode 100644 index 00000000..db04795d --- /dev/null +++ b/tests/make_compatible_with_phpunit9.php @@ -0,0 +1,23 @@ + $files */ +$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(__DIR__)); +foreach ($files as $file) { + if ($file->getExtension() === 'php' && $file->getPathname() !== __FILE__) { + $fileContents = file_get_contents($file->getPathname()); + if ($fileContents === false) { + throw new \RuntimeException('file_get_contents() failed: ' . $file->getPathname()); + } + $patternToReplacementMap = [ + '~ function assertMatchesRegularExpression\(\$pattern, \$string, \$message = \'\'\)~' => ' function _assertMatchesRegularExpression(string $pattern, string $string, string $message = \'\')', + ]; + $updatedFileContents = preg_replace( + array_keys($patternToReplacementMap), + array_values($patternToReplacementMap), + $fileContents + ); + if (file_put_contents($file->getPathname(), $updatedFileContents) === false) { + throw new \RuntimeException('file_put_contents() failed: ' . $file->getPathname()); + } + } +}