diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 7b4cf2133..9171048e9 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -31,9 +31,12 @@ class JsonFile const LAX_SCHEMA = 1; const STRICT_SCHEMA = 2; - const JSON_UNESCAPED_SLASHES = 64; - const JSON_PRETTY_PRINT = 128; - const JSON_UNESCAPED_UNICODE = 256; + /** @deprecated Use \JSON_UNESCAPED_SLASHES */ + public const JSON_UNESCAPED_SLASHES = 64; + /** @deprecated Use \JSON_PRETTY_PRINT */ + public const JSON_PRETTY_PRINT = 128; + /** @deprecated Use \JSON_UNESCAPED_UNICODE */ + public const JSON_UNESCAPED_UNICODE = 256; const COMPOSER_SCHEMA_PATH = '/../../../res/composer-schema.json'; @@ -116,12 +119,12 @@ class JsonFile /** * Writes json file. * - * @param mixed[] $hash writes hash into json file - * @param int $options json_encode options (defaults to JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) + * @param mixed[] $hash writes hash into json file + * @param int $options json_encode options * @throws \UnexpectedValueException|\Exception * @return void */ - public function write(array $hash, $options = 448) + public function write(array $hash, $options = JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) { if ($this->path === 'php://memory') { file_put_contents($this->path, static::encode($hash, $options)); @@ -146,7 +149,7 @@ class JsonFile $retries = 3; while ($retries--) { try { - $this->filePutContentsIfModified($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : '')); + $this->filePutContentsIfModified($this->path, static::encode($hash, $options). ($options & JSON_PRETTY_PRINT ? "\n" : '')); break; } catch (\Exception $e) { if ($retries) { diff --git a/src/Composer/Json/JsonFormatter.php b/src/Composer/Json/JsonFormatter.php index 45f5d3c5f..a85357f87 100644 --- a/src/Composer/Json/JsonFormatter.php +++ b/src/Composer/Json/JsonFormatter.php @@ -22,7 +22,7 @@ use Composer\Pcre\Preg; * @author Konstantin Kudryashiv * @author Jordi Boggiano * - * @abandoned Use json_encode or JsonFile::encode() with modern JSON_* flags to configure formatting - this class will be removed in 3.0 + * @deprecated Use json_encode or JsonFile::encode() with modern JSON_* flags to configure formatting - this class will be removed in 3.0 */ class JsonFormatter { diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index d8ab66dc4..a5c262158 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -403,7 +403,7 @@ class Locker $this->virtualFileWritten = false; } else { $this->virtualFileWritten = true; - $this->lockDataCache = JsonFile::parseJson(JsonFile::encode($lock, 448 & JsonFile::JSON_PRETTY_PRINT)); + $this->lockDataCache = JsonFile::parseJson(JsonFile::encode($lock)); } return true; diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 5dba6ca22..ede56e3ed 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -1514,7 +1514,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $response->collect(); if ($lastModifiedDate) { $data['last-modified'] = $lastModifiedDate; - $json = JsonFile::encode($data, JsonFile::JSON_UNESCAPED_SLASHES | JsonFile::JSON_UNESCAPED_UNICODE); + $json = JsonFile::encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } if (!$cache->isReadOnly()) { $cache->write($cacheKey, $json); diff --git a/src/Composer/Util/HttpDownloader.php b/src/Composer/Util/HttpDownloader.php index d1c03b957..d1c8c1fbd 100644 --- a/src/Composer/Util/HttpDownloader.php +++ b/src/Composer/Util/HttpDownloader.php @@ -110,24 +110,6 @@ class HttpDownloader $response = $this->getResponse($job['id']); - // check for failed curl response (empty body but successful looking response) - if ( - $this->curl - && PHP_VERSION_ID < 70000 - && $response->getBody() === null - && $response->getStatusCode() === 200 - && $response->getHeader('content-length') !== '0' - ) { - $this->io->writeError('cURL downloader failed to return a response, disabling it and proceeding in slow mode.'); - - $this->curl = null; - - list($job) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => null), true); - $this->wait($job['id']); - - $response = $this->getResponse($job['id']); - } - return $response; } diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 1ec560f32..7455ec7a9 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -51,8 +51,6 @@ class RemoteFilesystem private $lastProgress; /** @var mixed[] */ private $options = array(); - /** @var array */ - private $peerCertificateMap = array(); /** @var bool */ private $disableTls = false; /** @var string[] */ @@ -463,41 +461,6 @@ class RemoteFilesystem } } - // Handle SSL cert match issues - if (false === $result && false !== strpos($errorMessage, 'Peer certificate') && PHP_VERSION_ID < 50600) { - // Certificate name error, PHP doesn't support subjectAltName on PHP < 5.6 - // The procedure to handle sAN for older PHP's is: - // - // 1. Open socket to remote server and fetch certificate (disabling peer - // validation because PHP errors without giving up the certificate.) - // - // 2. Verifying the domain in the URL against the names in the sAN field. - // If there is a match record the authority [host/port], certificate - // common name, and certificate fingerprint. - // - // 3. Retry the original request but changing the CN_match parameter to - // the common name extracted from the certificate in step 2. - // - // 4. To prevent any attempt at being hoodwinked by switching the - // certificate between steps 2 and 3 the fingerprint of the certificate - // presented in step 3 is compared against the one recorded in step 2. - if (CaBundle::isOpensslParseSafe()) { - $certDetails = $this->getCertificateCnAndFp($this->fileUrl, $options); - - if ($certDetails) { - $this->peerCertificateMap[$this->getUrlAuthority($this->fileUrl)] = $certDetails; - - $this->retry = true; - } - } else { - $this->io->writeError(''); - $this->io->writeError(sprintf( - 'Your version of PHP, %s, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.', - PHP_VERSION - )); - } - } - if ($this->retry) { $this->retry = false; @@ -651,35 +614,6 @@ class RemoteFilesystem protected function getOptionsForUrl($originUrl, $additionalOptions) { $tlsOptions = array(); - - // Setup remaining TLS options - the matching may need monitoring, esp. www vs none in CN - if ($this->disableTls === false && PHP_VERSION_ID < 50600 && !stream_is_local($this->fileUrl)) { - $host = parse_url($this->fileUrl, PHP_URL_HOST); - - $tlsOptions['ssl']['CN_match'] = $host; - $tlsOptions['ssl']['SNI_server_name'] = $host; - - $urlAuthority = $this->getUrlAuthority($this->fileUrl); - - if (isset($this->peerCertificateMap[$urlAuthority])) { - // Handle subjectAltName on lesser PHP's. - $certMap = $this->peerCertificateMap[$urlAuthority]; - - $this->io->writeError('', true, IOInterface::DEBUG); - $this->io->writeError(sprintf( - 'Using %s as CN for subjectAltName enabled host %s', - $certMap['cn'], - $urlAuthority - ), true, IOInterface::DEBUG); - - $tlsOptions['ssl']['CN_match'] = $certMap['cn']; - $tlsOptions['ssl']['peer_fingerprint'] = $certMap['fp']; - } elseif (!CaBundle::isOpensslParseSafe() && $host === 'repo.packagist.org') { - // handle subjectAltName for packagist.org's repo domain on very old PHPs - $tlsOptions['ssl']['CN_match'] = 'packagist.org'; - } - } - $headers = array(); if (extension_loaded('zlib')) { @@ -759,88 +693,6 @@ class RemoteFilesystem return false; } - /** - * Fetch certificate common name and fingerprint for validation of SAN. - * - * @todo Remove when PHP 5.6 is minimum supported version. - * - * @param string $url - * @param mixed[] $options - * - * @return ?array{cn: string, fp: string} - */ - private function getCertificateCnAndFp($url, $options) - { - if (PHP_VERSION_ID >= 50600) { - throw new \BadMethodCallException(sprintf( - '%s must not be used on PHP >= 5.6', - __METHOD__ - )); - } - - $context = StreamContextFactory::getContext($url, $options, array('options' => array( - 'ssl' => array( - 'capture_peer_cert' => true, - 'verify_peer' => false, // Yes this is fucking insane! But PHP is lame. - ), ), - )); - - // Ideally this would just use stream_socket_client() to avoid sending a - // HTTP request but that does not capture the certificate. - if (false === $handle = @fopen($url, 'rb', false, $context)) { - return null; - } - - // Close non authenticated connection without reading any content. - fclose($handle); - $handle = null; - - $params = stream_context_get_params($context); - - if (!empty($params['options']['ssl']['peer_certificate'])) { - $peerCertificate = $params['options']['ssl']['peer_certificate']; - - if (TlsHelper::checkCertificateHost($peerCertificate, parse_url($url, PHP_URL_HOST), $commonName)) { - return array( - 'cn' => $commonName, - 'fp' => TlsHelper::getCertificateFingerprint($peerCertificate), - ); - } - } - - return null; - } - - /** - * @param string $url - * - * @return string - */ - private function getUrlAuthority($url) - { - $defaultPorts = array( - 'ftp' => 21, - 'http' => 80, - 'https' => 443, - 'ssh2.sftp' => 22, - 'ssh2.scp' => 22, - ); - - $scheme = parse_url($url, PHP_URL_SCHEME); - - if (!isset($defaultPorts[$scheme])) { - throw new \InvalidArgumentException(sprintf( - 'Could not get default port for unknown scheme: %s', - $scheme - )); - } - - $defaultPort = $defaultPorts[$scheme]; - $port = parse_url($url, PHP_URL_PORT) ?: $defaultPort; - - return parse_url($url, PHP_URL_HOST).':'.$port; - } - /** * @param string|false $result * @param string[] $http_response_header diff --git a/src/Composer/Util/TlsHelper.php b/src/Composer/Util/TlsHelper.php index 16ecbc527..3fe329d4a 100644 --- a/src/Composer/Util/TlsHelper.php +++ b/src/Composer/Util/TlsHelper.php @@ -17,6 +17,7 @@ use Composer\Pcre\Preg; /** * @author Chris Smith + * @deprecated Use composer/ca-bundle and composer/composer 2.2 if you still need PHP 5 compatibility, this class will be removed in Composer 3.0 */ final class TlsHelper { diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index 2a9485af1..6316ceb26 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -87,21 +87,18 @@ class ClassMapGeneratorTest extends TestCase array(__DIR__ . '/Fixtures/template', array()), ); - if (PHP_VERSION_ID >= 50400) { - $data[] = array(__DIR__ . '/Fixtures/php5.4', array( - 'TFoo' => __DIR__ . '/Fixtures/php5.4/traits.php', - 'CFoo' => __DIR__ . '/Fixtures/php5.4/traits.php', - 'Foo\\TBar' => __DIR__ . '/Fixtures/php5.4/traits.php', - 'Foo\\IBar' => __DIR__ . '/Fixtures/php5.4/traits.php', - 'Foo\\TFooBar' => __DIR__ . '/Fixtures/php5.4/traits.php', - 'Foo\\CBar' => __DIR__ . '/Fixtures/php5.4/traits.php', - )); - } - if (PHP_VERSION_ID >= 70000) { - $data[] = array(__DIR__ . '/Fixtures/php7.0', array( - 'Dummy\Test\AnonClassHolder' => __DIR__ . '/Fixtures/php7.0/anonclass.php', - )); - } + $data[] = array(__DIR__ . '/Fixtures/php5.4', array( + 'TFoo' => __DIR__ . '/Fixtures/php5.4/traits.php', + 'CFoo' => __DIR__ . '/Fixtures/php5.4/traits.php', + 'Foo\\TBar' => __DIR__ . '/Fixtures/php5.4/traits.php', + 'Foo\\IBar' => __DIR__ . '/Fixtures/php5.4/traits.php', + 'Foo\\TFooBar' => __DIR__ . '/Fixtures/php5.4/traits.php', + 'Foo\\CBar' => __DIR__ . '/Fixtures/php5.4/traits.php', + )); + + $data[] = array(__DIR__ . '/Fixtures/php7.0', array( + 'Dummy\Test\AnonClassHolder' => __DIR__ . '/Fixtures/php7.0/anonclass.php', + )); if (PHP_VERSION_ID >= 80100) { $data[] = array(__DIR__ . '/Fixtures/php8.1', array( diff --git a/tests/Composer/Test/Installer/BinaryInstallerTest.php b/tests/Composer/Test/Installer/BinaryInstallerTest.php index da7a8c477..fd0ec0aac 100644 --- a/tests/Composer/Test/Installer/BinaryInstallerTest.php +++ b/tests/Composer/Test/Installer/BinaryInstallerTest.php @@ -91,7 +91,7 @@ class BinaryInstallerTest extends TestCase public function executableBinaryProvider() { - $tests = array( + return array( 'simple php file' => array(<<<'EOL' array( base64_decode('IyEvdXNyL2Jpbi9lbnYgcGhwCjw/cGhwCgpQaGFyOjptYXBQaGFyKCd0ZXN0LnBoYXInKTsKCnJlcXVpcmUgJ3BoYXI6Ly90ZXN0LnBoYXIvcnVuLnBocCc7CgpfX0hBTFRfQ09NUElMRVIoKTsgPz4NCj4AAAABAAAAEQAAAAEACQAAAHRlc3QucGhhcgAAAAAHAAAAcnVuLnBocCoAAADb9n9hKgAAAMUDDWGkAQAAAAAAADw/cGhwIGVjaG8gInN1Y2Nlc3MgIi4kX1NFUlZFUlsiYXJndiJdWzFdO1SOC0IE3+UN0yzrHIwyspp9slhmAgAAAEdCTUI=') ), - ); - - if (PHP_VERSION_ID >= 70000) { - $tests += array( - 'shebang with strict types declare' => array(<<<'EOL' + 'shebang with strict types declare' => array(<<<'EOL' #!/usr/bin/env php expects($this->any()) ->method('write') ->will($this->returnCallback(function ($value, $options = 0) use (&$lockData) { - $lockData = json_encode($value, JsonFile::JSON_PRETTY_PRINT); + $lockData = json_encode($value, JSON_PRETTY_PRINT); })); $tempLockData = null; @@ -339,7 +339,7 @@ class InstallerTest extends TestCase $repositoryManager->setLocalRepository(new InstalledFilesystemRepositoryMock($jsonMock)); // emulate a writable lock file - $lockData = $lock ? json_encode($lock, JsonFile::JSON_PRETTY_PRINT) : null; + $lockData = $lock ? json_encode($lock, JSON_PRETTY_PRINT) : null; $lockJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock(); $lockJsonMock->expects($this->any()) ->method('read') @@ -354,7 +354,7 @@ class InstallerTest extends TestCase $lockJsonMock->expects($this->any()) ->method('write') ->will($this->returnCallback(function ($value, $options = 0) use (&$lockData) { - $lockData = json_encode($value, JsonFile::JSON_PRETTY_PRINT); + $lockData = json_encode($value, JSON_PRETTY_PRINT); })); if ($expectLock) { diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index 3dde97777..894db2cfe 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -312,7 +312,7 @@ class JsonFileTest extends TestCase $data = "\\/ƌ"; - $this->assertJsonFormat('"\\\\\\/ƌ"', $data, JsonFile::JSON_UNESCAPED_UNICODE); + $this->assertJsonFormat('"\\\\\\/ƌ"', $data, JSON_UNESCAPED_UNICODE); } public function testEscapedSlashes() diff --git a/tests/Composer/Test/Json/JsonFormatterTest.php b/tests/Composer/Test/Json/JsonFormatterTest.php index 94f95871b..d2bd8e0d6 100644 --- a/tests/Composer/Test/Json/JsonFormatterTest.php +++ b/tests/Composer/Test/Json/JsonFormatterTest.php @@ -29,6 +29,7 @@ class JsonFormatterTest extends TestCase $backslash = chr(92); $data = '"' . $backslash . $backslash . $backslash . 'u0119"'; $expected = '"' . $backslash . $backslash . 'ę"'; + /** @phpstan-ignore-next-line */ $this->assertEquals($expected, JsonFormatter::format($data, true, true)); } @@ -43,6 +44,7 @@ class JsonFormatterTest extends TestCase } $escaped = '"\ud83d\ude00"'; + /** @phpstan-ignore-next-line */ $this->assertEquals($escaped, JsonFormatter::format($escaped, true, true)); } } diff --git a/tests/Composer/Test/Util/TlsHelperTest.php b/tests/Composer/Test/Util/TlsHelperTest.php index 84c826178..3b9549951 100644 --- a/tests/Composer/Test/Util/TlsHelperTest.php +++ b/tests/Composer/Test/Util/TlsHelperTest.php @@ -29,6 +29,7 @@ class TlsHelperTest extends TestCase $certificate['subject']['commonName'] = $expectedCn = array_shift($certNames); $certificate['extensions']['subjectAltName'] = $certNames ? 'DNS:'.implode(',DNS:', $certNames) : ''; + // @phpstan-ignore-next-line $result = TlsHelper::checkCertificateHost($certificate, $hostname, $foundCn); if (true === $expectedResult) { @@ -71,6 +72,7 @@ class TlsHelperTest extends TestCase $certificate['subject']['commonName'] = 'example.net'; $certificate['extensions']['subjectAltName'] = 'DNS: example.com, IP: 127.0.0.1, DNS: getcomposer.org, Junk: blah, DNS: composer.example.org'; + // @phpstan-ignore-next-line $names = TlsHelper::getCertificateNames($certificate); $this->assertSame('example.net', $names['cn']);