diff --git a/src/Symfony/Component/BrowserKit/Cookie.php b/src/Symfony/Component/BrowserKit/Cookie.php index 8ea50bf46e..79ca794310 100644 --- a/src/Symfony/Component/BrowserKit/Cookie.php +++ b/src/Symfony/Component/BrowserKit/Cookie.php @@ -306,7 +306,7 @@ class Cookie */ public function isExpired() { - return null !== $this->expires && 0 != $this->expires && $this->expires < time(); + return null !== $this->expires && 0 != $this->expires && $this->expires <= time(); } /** diff --git a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php index 42aba12a2d..747db80d80 100644 --- a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php +++ b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php @@ -13,6 +13,7 @@ namespace Symfony\Component\ErrorHandler; use Doctrine\Common\Persistence\Proxy as LegacyProxy; use Doctrine\Persistence\Proxy; +use Mockery\MockInterface; use PHPUnit\Framework\MockObject\Matcher\StatelessInvocation; use PHPUnit\Framework\MockObject\MockObject; use Prophecy\Prophecy\ProphecySubjectInterface; @@ -306,6 +307,7 @@ class DebugClassLoader && !is_subclass_of($symbols[$i], Proxy::class) && !is_subclass_of($symbols[$i], ProxyInterface::class) && !is_subclass_of($symbols[$i], LegacyProxy::class) + && !is_subclass_of($symbols[$i], MockInterface::class) ) { $loader->checkClass($symbols[$i]); } diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index f74e621bb8..93e7c36cfe 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -171,7 +171,7 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac $this->logger && $this->logger->info(sprintf('Request: "%s %s"', $method, implode('', $url))); - [$host, $port, $url['authority']] = self::dnsResolve($url, $this->multi, $info, $onProgress); + [$host, $port] = self::parseHostPort($url, $info); if (!isset($options['normalized_headers']['host'])) { $options['headers'][] = 'Host: '.$host.$port; @@ -198,7 +198,6 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac 'follow_location' => false, // We follow redirects ourselves - the native logic is too limited ], 'ssl' => array_filter([ - 'peer_name' => $host, 'verify_peer' => $options['verify_peer'], 'verify_peer_name' => $options['verify_host'], 'cafile' => $options['cafile'], @@ -222,7 +221,11 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac $proxy = self::getProxy($options['proxy'], $url, $options['no_proxy']); $resolveRedirect = self::createRedirectResolver($options, $host, $proxy, $info, $onProgress); $context = stream_context_create($context, ['notification' => $notification]); - self::configureHeadersAndProxy($context, $host, $options['headers'], $proxy); + + if (!self::configureHeadersAndProxy($context, $host, $options['headers'], $proxy, 'https:' === $url['scheme'])) { + $ip = self::dnsResolve($host, $this->multi, $info, $onProgress); + $url['authority'] = substr_replace($url['authority'], $ip, -\strlen($host) - \strlen($port), \strlen($host)); + } return new NativeResponse($this->multi, $context, implode('', $url), $options, $info, $resolveRedirect, $onProgress, $this->logger); } @@ -265,9 +268,9 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac } /** - * Resolves the IP of the host using the local DNS cache if possible. + * Extracts the host and the port from the URL. */ - private static function dnsResolve(array $url, NativeClientState $multi, array &$info, ?\Closure $onProgress): array + private static function parseHostPort(array $url, array &$info): array { if ($port = parse_url($url['authority'], \PHP_URL_PORT) ?: '') { $info['primary_port'] = $port; @@ -276,8 +279,14 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac $info['primary_port'] = 'http:' === $url['scheme'] ? 80 : 443; } - $host = parse_url($url['authority'], \PHP_URL_HOST); + return [parse_url($url['authority'], \PHP_URL_HOST), $port]; + } + /** + * Resolves the IP of the host using the local DNS cache if possible. + */ + private static function dnsResolve($host, NativeClientState $multi, array &$info, ?\Closure $onProgress): string + { if (null === $ip = $multi->dnsCache[$host] ?? null) { $info['debug'] .= "* Hostname was NOT found in DNS cache\n"; $now = microtime(true); @@ -300,7 +309,7 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac $onProgress(); } - return [$host, $port, substr_replace($url['authority'], $ip, -\strlen($host) - \strlen($port), \strlen($host))]; + return $ip; } /** @@ -363,24 +372,33 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac } } - [$host, $port, $url['authority']] = self::dnsResolve($url, $multi, $info, $onProgress); - stream_context_set_option($context, 'ssl', 'peer_name', $host); + [$host, $port] = self::parseHostPort($url, $info); if (false !== (parse_url($location, \PHP_URL_HOST) ?? false)) { // Authorization and Cookie headers MUST NOT follow except for the initial host name $requestHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth']; $requestHeaders[] = 'Host: '.$host.$port; - self::configureHeadersAndProxy($context, $host, $requestHeaders, $proxy); + $dnsResolve = !self::configureHeadersAndProxy($context, $host, $requestHeaders, $proxy, 'https:' === $url['scheme']); + } else { + $dnsResolve = isset(stream_context_get_options($context)['ssl']['peer_name']); + } + + if ($dnsResolve) { + $ip = self::dnsResolve($host, $multi, $info, $onProgress); + $url['authority'] = substr_replace($url['authority'], $ip, -\strlen($host) - \strlen($port), \strlen($host)); } return implode('', $url); }; } - private static function configureHeadersAndProxy($context, string $host, array $requestHeaders, ?array $proxy): bool + private static function configureHeadersAndProxy($context, string $host, array $requestHeaders, ?array $proxy, bool $isSsl): bool { if (null === $proxy) { - return stream_context_set_option($context, 'http', 'header', $requestHeaders); + stream_context_set_option($context, 'http', 'header', $requestHeaders); + stream_context_set_option($context, 'ssl', 'peer_name', $host); + + return false; } // Matching "no_proxy" should follow the behavior of curl @@ -389,17 +407,24 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac $dotRule = '.'.ltrim($rule, '.'); if ('*' === $rule || $host === $rule || substr($host, -\strlen($dotRule)) === $dotRule) { - return stream_context_set_option($context, 'http', 'header', $requestHeaders); + stream_context_set_option($context, 'http', 'proxy', null); + stream_context_set_option($context, 'http', 'request_fulluri', false); + stream_context_set_option($context, 'http', 'header', $requestHeaders); + stream_context_set_option($context, 'ssl', 'peer_name', $host); + + return false; } } - stream_context_set_option($context, 'http', 'proxy', $proxy['url']); - stream_context_set_option($context, 'http', 'request_fulluri', true); - if (null !== $proxy['auth']) { $requestHeaders[] = 'Proxy-Authorization: '.$proxy['auth']; } - return stream_context_set_option($context, 'http', 'header', $requestHeaders); + stream_context_set_option($context, 'http', 'proxy', $proxy['url']); + stream_context_set_option($context, 'http', 'request_fulluri', !$isSsl); + stream_context_set_option($context, 'http', 'header', $requestHeaders); + stream_context_set_option($context, 'ssl', 'peer_name', null); + + return true; } } diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php index b0a4279ea1..bdde01e496 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php @@ -69,11 +69,11 @@ class Connection } if (null !== $auth && !$this->connection->auth($auth)) { - throw new InvalidArgumentException('Redis connection failed: '.$redis->getLastError()); + throw new InvalidArgumentException('Redis connection failed: '.$this->connection->getLastError()); } if (($dbIndex = $configuration['dbindex'] ?? self::DEFAULT_OPTIONS['dbindex']) && !$this->connection->select($dbIndex)) { - throw new InvalidArgumentException('Redis connection failed: '.$redis->getLastError()); + throw new InvalidArgumentException('Redis connection failed: '.$this->connection->getLastError()); } foreach (['stream', 'group', 'consumer'] as $key) { diff --git a/src/Symfony/Component/Routing/Route.php b/src/Symfony/Component/Routing/Route.php index 7ed8d2b146..2ef2a1dd79 100644 --- a/src/Symfony/Component/Routing/Route.php +++ b/src/Symfony/Component/Routing/Route.php @@ -131,7 +131,7 @@ class Route implements \Serializable public function setPath(string $pattern) { if (false !== strpbrk($pattern, '?<')) { - $pattern = preg_replace_callback('#\{(\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) { + $pattern = preg_replace_callback('#\{(!?\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) { if (isset($m[3][0])) { $this->setDefault($m[1], '?' !== $m[3] ? substr($m[3], 1) : null); } diff --git a/src/Symfony/Component/Routing/Tests/RouteTest.php b/src/Symfony/Component/Routing/Tests/RouteTest.php index 43c59cb697..b67ccd270c 100644 --- a/src/Symfony/Component/Routing/Tests/RouteTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteTest.php @@ -207,6 +207,7 @@ class RouteTest extends TestCase $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', null), new Route('/foo/{bar?}')); $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?baz}')); $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?baz}')); + $this->assertEquals((new Route('/foo/{!bar}'))->setDefault('!bar', 'baz'), new Route('/foo/{!bar?baz}')); $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?}', ['bar' => 'baz'])); $this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '.*'), new Route('/foo/{bar<.*>}'));