diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index de9f4e7e02..18ef0239ae 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -130,6 +130,13 @@ // alias once Drupal is running Symfony 5.3 or higher. class_alias('Drupal\Core\Http\KernelEvent', 'Symfony\Component\HttpKernel\Event\KernelEvent', TRUE); +// PHP 8.1 workarounds not final fixes. +// @see https://www.drupal.org/project/drupal/issues/3220021 +class_alias('Drupal\Core\PhpFixes\StaticReflectionClass', 'Doctrine\Common\Reflection\StaticReflectionClass', TRUE); +class_alias('Drupal\Core\PhpFixes\AbstractSessionHandler', 'Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler', TRUE); +class_alias('Drupal\Core\PhpFixes\StrictSessionHandler', 'Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler', TRUE); +class_alias('Drupal\Core\PhpFixes\SessionHandlerProxy', 'Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', TRUE); + /** * Returns and optionally sets the filename for a system resource. * diff --git a/core/lib/Drupal/Component/Render/FormattableMarkup.php b/core/lib/Drupal/Component/Render/FormattableMarkup.php index e26f90e9b8..a350347d29 100644 --- a/core/lib/Drupal/Component/Render/FormattableMarkup.php +++ b/core/lib/Drupal/Component/Render/FormattableMarkup.php @@ -115,7 +115,7 @@ public function count() { * @return string * The safe string content. */ - public function jsonSerialize() { + public function jsonSerialize(): string { return $this->__toString(); } diff --git a/core/lib/Drupal/Component/Render/HtmlEscapedText.php b/core/lib/Drupal/Component/Render/HtmlEscapedText.php index 0ddc4fa4c5..de53bc1f4b 100644 --- a/core/lib/Drupal/Component/Render/HtmlEscapedText.php +++ b/core/lib/Drupal/Component/Render/HtmlEscapedText.php @@ -48,7 +48,7 @@ public function count() { /** * {@inheritdoc} */ - public function jsonSerialize() { + public function jsonSerialize(): string { return $this->__toString(); } diff --git a/core/lib/Drupal/Component/Render/MarkupTrait.php b/core/lib/Drupal/Component/Render/MarkupTrait.php index c7faa81aba..3ef4fb096a 100644 --- a/core/lib/Drupal/Component/Render/MarkupTrait.php +++ b/core/lib/Drupal/Component/Render/MarkupTrait.php @@ -68,7 +68,7 @@ public function count() { * @return string * The safe string content. */ - public function jsonSerialize() { + public function jsonSerialize(): string { return $this->__toString(); } diff --git a/core/lib/Drupal/Component/Utility/UrlHelper.php b/core/lib/Drupal/Component/Utility/UrlHelper.php index 137b4118ce..99b889de4c 100644 --- a/core/lib/Drupal/Component/Utility/UrlHelper.php +++ b/core/lib/Drupal/Component/Utility/UrlHelper.php @@ -211,6 +211,7 @@ public static function encodePath($path) { * TRUE or FALSE, where TRUE indicates an external path. */ public static function isExternal($path) { + $path = $path ?? ''; $colonpos = strpos($path, ':'); // Some browsers treat \ as / so normalize to forward slashes. $path = str_replace('\\', '/', $path); diff --git a/core/lib/Drupal/Core/GeneratedLink.php b/core/lib/Drupal/Core/GeneratedLink.php index 62f10a5df7..3645657835 100644 --- a/core/lib/Drupal/Core/GeneratedLink.php +++ b/core/lib/Drupal/Core/GeneratedLink.php @@ -57,7 +57,7 @@ public function __toString() { /** * {@inheritdoc} */ - public function jsonSerialize() { + public function jsonSerialize(): string { return $this->__toString(); } diff --git a/core/lib/Drupal/Core/Menu/MenuTreeParameters.php b/core/lib/Drupal/Core/Menu/MenuTreeParameters.php index f7c6148801..7c0526fb3b 100644 --- a/core/lib/Drupal/Core/Menu/MenuTreeParameters.php +++ b/core/lib/Drupal/Core/Menu/MenuTreeParameters.php @@ -243,4 +243,37 @@ public function unserialize($serialized) { return $this; } + /** + * {@inheritdoc} + */ + public function __unserialize(array $data): void { + foreach ($data as $key => $value) { + $this->{$key} = $value; + } + } + + /** + * {@inheritdoc} + */ + public function __serialize(): array { + // Enforce type consistency for all the internal properties of this object. + $this->root = (string) $this->root; + $this->minDepth = $this->minDepth !== NULL ? (int) $this->minDepth : NULL; + $this->maxDepth = $this->maxDepth !== NULL ? (int) $this->maxDepth : NULL; + $this->activeTrail = array_values(array_filter($this->activeTrail)); + + // Sort 'expanded' and 'conditions' to prevent duplicate cache items. + sort($this->expandedParents); + asort($this->conditions); + + return [ + 'root' => $this->root, + 'minDepth' => $this->minDepth, + 'maxDepth' => $this->maxDepth, + 'expandedParents' => $this->expandedParents, + 'activeTrail' => $this->activeTrail, + 'conditions' => $this->conditions, + ]; + } + } diff --git a/core/lib/Drupal/Core/PhpFixes/AbstractSessionHandler.php b/core/lib/Drupal/Core/PhpFixes/AbstractSessionHandler.php new file mode 100644 index 0000000000..d5cbe39f61 --- /dev/null +++ b/core/lib/Drupal/Core/PhpFixes/AbstractSessionHandler.php @@ -0,0 +1,150 @@ + + */ +abstract class AbstractSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface +{ + private $sessionName; + private $prefetchId; + private $prefetchData; + private $newSessionId; + private $igbinaryEmptyData; + + /** + * @return bool + */ + public function open($savePath, $sessionName) : bool + { + $this->sessionName = $sessionName; + if (!headers_sent() && !ini_get('session.cache_limiter') && '0' !== ini_get('session.cache_limiter')) { + header(sprintf('Cache-Control: max-age=%d, private, must-revalidate', 60 * (int) ini_get('session.cache_expire'))); + } + + return true; + } + + /** + * @param string $sessionId + * + * @return string + */ + abstract protected function doRead($sessionId); + + /** + * @param string $sessionId + * @param string $data + * + * @return bool + */ + abstract protected function doWrite($sessionId, $data); + + /** + * @param string $sessionId + * + * @return bool + */ + abstract protected function doDestroy($sessionId); + + /** + * @return bool + */ + public function validateId($sessionId) : bool + { + $this->prefetchData = $this->read($sessionId); + $this->prefetchId = $sessionId; + + if (\PHP_VERSION_ID < 70317 || (70400 <= \PHP_VERSION_ID && \PHP_VERSION_ID < 70405)) { + // work around https://bugs.php.net/79413 + foreach (debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) { + if (!isset($frame['class']) && isset($frame['function']) && \in_array($frame['function'], ['session_regenerate_id', 'session_create_id'], true)) { + return '' === $this->prefetchData; + } + } + } + + return '' !== $this->prefetchData; + } + + /** + * @return string + */ + public function read($sessionId) : string|false + { + if (null !== $this->prefetchId) { + $prefetchId = $this->prefetchId; + $prefetchData = $this->prefetchData; + $this->prefetchId = $this->prefetchData = null; + + if ($prefetchId === $sessionId || '' === $prefetchData) { + $this->newSessionId = '' === $prefetchData ? $sessionId : null; + + return $prefetchData; + } + } + + $data = $this->doRead($sessionId); + $this->newSessionId = '' === $data ? $sessionId : null; + + return $data; + } + + /** + * @return bool + */ + public function write($sessionId, $data) : bool + { + if (null === $this->igbinaryEmptyData) { + // see https://github.com/igbinary/igbinary/issues/146 + $this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize([]) : ''; + } + if ('' === $data || $this->igbinaryEmptyData === $data) { + return $this->destroy($sessionId); + } + $this->newSessionId = null; + + return $this->doWrite($sessionId, $data); + } + + /** + * @return bool + */ + public function destroy($sessionId) : bool + { + if (!headers_sent() && filter_var(ini_get('session.use_cookies'), \FILTER_VALIDATE_BOOLEAN)) { + if (!$this->sessionName) { + throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', static::class)); + } + $cookie = SessionUtils::popSessionCookie($this->sessionName, $sessionId); + + /* + * We send an invalidation Set-Cookie header (zero lifetime) + * when either the session was started or a cookie with + * the session name was sent by the client (in which case + * we know it's invalid as a valid session cookie would've + * started the session). + */ + if (null === $cookie || isset($_COOKIE[$this->sessionName])) { + if (\PHP_VERSION_ID < 70300) { + setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), filter_var(ini_get('session.cookie_secure'), \FILTER_VALIDATE_BOOLEAN), filter_var(ini_get('session.cookie_httponly'), \FILTER_VALIDATE_BOOLEAN)); + } else { + $params = session_get_cookie_params(); + unset($params['lifetime']); + setcookie($this->sessionName, '', $params); + } + } + } + + return $this->newSessionId === $sessionId || $this->doDestroy($sessionId); + } +} diff --git a/core/lib/Drupal/Core/PhpFixes/SessionHandlerProxy.php b/core/lib/Drupal/Core/PhpFixes/SessionHandlerProxy.php new file mode 100644 index 0000000000..ffbc30117d --- /dev/null +++ b/core/lib/Drupal/Core/PhpFixes/SessionHandlerProxy.php @@ -0,0 +1,97 @@ + + */ +class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface +{ + protected $handler; + + public function __construct(\SessionHandlerInterface $handler) + { + $this->handler = $handler; + $this->wrapper = ($handler instanceof \SessionHandler); + $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user'; + } + + /** + * @return \SessionHandlerInterface + */ + public function getHandler() + { + return $this->handler; + } + + // \SessionHandlerInterface + + /** + * @return bool + */ + public function open($savePath, $sessionName) : bool + { + return (bool) $this->handler->open($savePath, $sessionName); + } + + /** + * @return bool + */ + public function close() : bool + { + return (bool) $this->handler->close(); + } + + /** + * @return string + */ + public function read($sessionId) : string + { + return (string) $this->handler->read($sessionId); + } + + /** + * @return bool + */ + public function write($sessionId, $data): bool + { + return (bool) $this->handler->write($sessionId, $data); + } + + /** + * @return bool + */ + public function destroy($sessionId) : bool + { + return (bool) $this->handler->destroy($sessionId); + } + + /** + * @return bool + */ + public function gc($maxlifetime) : int + { + return (bool) $this->handler->gc($maxlifetime); + } + + /** + * @return bool + */ + public function validateId($sessionId): bool + { + return !$this->handler instanceof \SessionUpdateTimestampHandlerInterface || $this->handler->validateId($sessionId); + } + + /** + * @return bool + */ + public function updateTimestamp($sessionId, $data) : bool + { + return $this->handler instanceof \SessionUpdateTimestampHandlerInterface ? $this->handler->updateTimestamp($sessionId, $data) : $this->write($sessionId, $data); + } +} diff --git a/core/lib/Drupal/Core/PhpFixes/StaticReflectionClass.php b/core/lib/Drupal/Core/PhpFixes/StaticReflectionClass.php new file mode 100644 index 0000000000..23e08af77d --- /dev/null +++ b/core/lib/Drupal/Core/PhpFixes/StaticReflectionClass.php @@ -0,0 +1,417 @@ +staticReflectionParser = $staticReflectionParser; + } + + /** + * {@inheritDoc} + */ + public function getName() : string + { + return $this->staticReflectionParser->getClassName(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment(): string|false + { + return $this->staticReflectionParser->getDocComment(); + } + + /** + * {@inheritDoc} + */ + public function getNamespaceName(): string + { + return $this->staticReflectionParser->getNamespaceName(); + } + + /** + * @return string[] + */ + public function getUseStatements() + { + return $this->staticReflectionParser->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public function getMethod(string $name): \ReflectionMethod + { + return $this->staticReflectionParser->getReflectionMethod($name); + } + + /** + * {@inheritDoc} + */ + public function getProperty(string $name): \ReflectionProperty + { + return $this->staticReflectionParser->getReflectionProperty($name); + } + + /** + * {@inheritDoc} + */ + public static function export($argument, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstant(string $name) : mixed + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstructor(): ?\ReflectionMethod + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getDefaultProperties() : array + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getEndLine() : int|false + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtension() : ?\ReflectionExtension + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtensionName() : string|false + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getFileName() : string|false + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getInterfaceNames(): array + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getInterfaces(): array + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getMethods(?int $filter = null): array + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers(): int + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getParentClass(): ReflectionClass|false + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getProperties($filter = null) : array + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getShortName() : string + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStartLine() : int|false + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticProperties() : array + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticPropertyValue(string $name, mixed $default = '') : mixed + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraitAliases() : array + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraitNames() : array + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraits() : array + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasConstant($name) : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasMethod($name) : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasProperty($name) : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function implementsInterface($interface) : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function inNamespace() : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isAbstract() : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isCloneable() : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isFinal() : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInstance($object) : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInstantiable() : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInterface() : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInternal() : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isIterateable() : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isSubclassOf($class) : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isTrait() : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isUserDefined() : bool + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstanceArgs(array $args = []): ?object + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstanceWithoutConstructor() : object + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setStaticPropertyValue(string $name, mixed $value): void + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/core/lib/Drupal/Core/PhpFixes/StrictSessionHandler.php b/core/lib/Drupal/Core/PhpFixes/StrictSessionHandler.php new file mode 100644 index 0000000000..32415b41dc --- /dev/null +++ b/core/lib/Drupal/Core/PhpFixes/StrictSessionHandler.php @@ -0,0 +1,99 @@ + + */ +class StrictSessionHandler extends AbstractSessionHandler +{ + private $handler; + private $doDestroy; + + public function __construct(\SessionHandlerInterface $handler) + { + if ($handler instanceof \SessionUpdateTimestampHandlerInterface) { + throw new \LogicException(sprintf('"%s" is already an instance of "SessionUpdateTimestampHandlerInterface", you cannot wrap it with "%s".', \get_class($handler), self::class)); + } + + $this->handler = $handler; + } + + /** + * @return bool + */ + public function open($savePath, $sessionName) : bool + { + parent::open($savePath, $sessionName); + + return $this->handler->open($savePath, $sessionName); + } + + /** + * {@inheritdoc} + */ + protected function doRead($sessionId) + { + return $this->handler->read($sessionId); + } + + /** + * @return bool + */ + public function updateTimestamp($sessionId, $data) : bool + { + return $this->write($sessionId, $data); + } + + /** + * {@inheritdoc} + */ + protected function doWrite($sessionId, $data) + { + return $this->handler->write($sessionId, $data); + } + + /** + * @return bool + */ + public function destroy($sessionId) : bool + { + $this->doDestroy = true; + $destroyed = parent::destroy($sessionId); + + return $this->doDestroy ? $this->doDestroy($sessionId) : $destroyed; + } + + /** + * {@inheritdoc} + */ + protected function doDestroy($sessionId) + { + $this->doDestroy = false; + + return $this->handler->destroy($sessionId); + } + + /** + * @return bool + */ + public function close() : bool + { + return $this->handler->close(); + } + + /** + * @return bool + */ + public function gc($maxlifetime) : int|false + { + return $this->handler->gc($maxlifetime); + } +} diff --git a/core/lib/Drupal/Core/Routing/ContentTypeHeaderMatcher.php b/core/lib/Drupal/Core/Routing/ContentTypeHeaderMatcher.php index b782d9af62..beb988f6eb 100644 --- a/core/lib/Drupal/Core/Routing/ContentTypeHeaderMatcher.php +++ b/core/lib/Drupal/Core/Routing/ContentTypeHeaderMatcher.php @@ -25,7 +25,7 @@ public function filter(RouteCollection $collection, Request $request) { $format = $request->getContentType(); foreach ($collection as $name => $route) { - $supported_formats = array_filter(explode('|', $route->getRequirement('_content_type_format'))); + $supported_formats = array_filter(explode('|', $route->getRequirement('_content_type_format') ?? '')); if (empty($supported_formats)) { // No restriction on the route, so we move the route to the end of the // collection by re-adding it. That way generic routes sink down in the diff --git a/core/lib/Drupal/Core/Routing/RouteProvider.php b/core/lib/Drupal/Core/Routing/RouteProvider.php index 534670f8de..1bd1cc2380 100644 --- a/core/lib/Drupal/Core/Routing/RouteProvider.php +++ b/core/lib/Drupal/Core/Routing/RouteProvider.php @@ -349,7 +349,7 @@ protected function getRoutesByPath($path) { // have a case-insensitive match from the incoming path to the lower case // pattern outlines from \Drupal\Core\Routing\RouteCompiler::compile(). // @see \Drupal\Core\Routing\CompiledRoute::__construct() - $parts = preg_split('@/+@', mb_strtolower($path), NULL, PREG_SPLIT_NO_EMPTY); + $parts = preg_split('@/+@', mb_strtolower($path), -1, PREG_SPLIT_NO_EMPTY); $collection = new RouteCollection(); diff --git a/core/lib/Drupal/Core/Session/SessionHandler.php b/core/lib/Drupal/Core/Session/SessionHandler.php index eb694921af..8c387cca4f 100644 --- a/core/lib/Drupal/Core/Session/SessionHandler.php +++ b/core/lib/Drupal/Core/Session/SessionHandler.php @@ -46,14 +46,14 @@ public function __construct(RequestStack $request_stack, Connection $connection) /** * {@inheritdoc} */ - public function open($save_path, $name) { + public function open($save_path, $name): bool { return TRUE; } /** * {@inheritdoc} */ - public function read($sid) { + public function read($sid): string|FALSE { $data = ''; if (!empty($sid)) { // Read the session data from the database. @@ -67,7 +67,7 @@ public function read($sid) { /** * {@inheritdoc} */ - public function write($sid, $value) { + public function write($sid, $value): bool { // The exception handler is not active at this point, so we need to do it // manually. try { @@ -99,14 +99,14 @@ public function write($sid, $value) { /** * {@inheritdoc} */ - public function close() { + public function close() : bool { return TRUE; } /** * {@inheritdoc} */ - public function destroy($sid) { + public function destroy($sid) : bool { // Delete session data. $this->connection->delete('sessions') ->condition('sid', Crypt::hashBase64($sid)) @@ -118,7 +118,7 @@ public function destroy($sid) { /** * {@inheritdoc} */ - public function gc($lifetime) { + public function gc($lifetime) : int|FALSE { // Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough // value. For example, if you want user sessions to stay in your database // for three weeks before deleting them, you need to set gc_maxlifetime diff --git a/core/lib/Drupal/Core/Session/WriteSafeSessionHandler.php b/core/lib/Drupal/Core/Session/WriteSafeSessionHandler.php index fadf29bffd..bff45da1bf 100644 --- a/core/lib/Drupal/Core/Session/WriteSafeSessionHandler.php +++ b/core/lib/Drupal/Core/Session/WriteSafeSessionHandler.php @@ -43,35 +43,35 @@ public function __construct(\SessionHandlerInterface $wrapped_session_handler, $ /** * {@inheritdoc} */ - public function close() { + public function close(): bool { return $this->wrappedSessionHandler->close(); } /** * {@inheritdoc} */ - public function destroy($session_id) { + public function destroy($session_id) : bool { return $this->wrappedSessionHandler->destroy($session_id); } /** * {@inheritdoc} */ - public function gc($max_lifetime) { + public function gc($max_lifetime) : int|FALSE { return $this->wrappedSessionHandler->gc($max_lifetime); } /** * {@inheritdoc} */ - public function open($save_path, $session_id) { + public function open($save_path, $session_id) : bool { return $this->wrappedSessionHandler->open($save_path, $session_id); } /** * {@inheritdoc} */ - public function read($session_id) { + public function read($session_id) : string|FALSE { $value = $this->wrappedSessionHandler->read($session_id); $this->readSessions[$session_id] = $value; return $value; @@ -80,7 +80,7 @@ public function read($session_id) { /** * {@inheritdoc} */ - public function write($session_id, $session_data) { + public function write($session_id, $session_data): bool { // Only write the session when it has been modified. if (isset($this->readSessions[$session_id]) && $this->readSessions[$session_id] === $session_data) { return TRUE; diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php index cbbe1657f6..6c830e228c 100644 --- a/core/lib/Drupal/Core/Template/Attribute.php +++ b/core/lib/Drupal/Core/Template/Attribute.php @@ -369,7 +369,7 @@ public function storage() { * @return string * The safe string content. */ - public function jsonSerialize() { + public function jsonSerialize(): string { return (string) $this; } diff --git a/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php b/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php index ff13f50aeb..4b62cd8187 100644 --- a/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php +++ b/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php @@ -39,7 +39,7 @@ public function __invoke() { foreach ($header_values as $header_value) { $parameters = unserialize(urldecode($header_value)); if (count($parameters) === 3) { - if ($parameters[1] === 'User deprecated function') { + if ($parameters[1] === 'User deprecated function' || $parameters[1] === 'Deprecated function') { // Fire the same deprecation message to allow it to be // collected by // \Symfony\Bridge\PhpUnit\DeprecationErrorHandler::collectDeprecations(). diff --git a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php index e007e498bc..2611a04c1e 100644 --- a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php +++ b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php @@ -110,26 +110,41 @@ public function query($group_by = FALSE) { $tids = $this->argument; } // Now build the subqueries. - $subquery = Database::getConnection()->select('taxonomy_index', 'tn'); + $connection = Database::getConnection(); + $subquery = $connection->select('taxonomy_index', 'tn'); $subquery->addField('tn', 'nid'); - $where = ($this->view->query->getConnection()->condition('OR'))->condition('tn.tid', $tids, $operator); - $last = "tn"; + $where = $connection->condition('OR')->condition('tn.tid', $tids, $operator); if ($this->options['depth'] > 0) { - $subquery->leftJoin('taxonomy_term__parent', 'th', "[th].[entity_id] = [tn].[tid]"); - $last = "th"; foreach (range(1, abs($this->options['depth'])) as $count) { - $subquery->leftJoin('taxonomy_term__parent', "th$count", "[$last].[parent_target_id] = [th$count].[entity_id]"); - $where->condition("th$count.entity_id", $tids, $operator); - $last = "th$count"; + $last = "th"; + $inner_subquery = $connection->select('taxonomy_term__parent', 'th'); + $inner_subquery->addField("th$count", 'entity_id'); + $inner_where = $connection->condition('OR')->condition("th.entity_id", $tids, $operator); + foreach (range(1, $count) as $inner_count) { + $inner_subquery->leftJoin('taxonomy_term__parent', "th$inner_count", "[$last].[parent_target_id] = [th$inner_count].[entity_id]"); + $inner_where->condition("th$inner_count.entity_id", $tids, $operator); + $last = "th$inner_count"; + } + $inner_subquery->condition($inner_where); + $where->condition('tn.tid', $inner_subquery, 'IN'); } } elseif ($this->options['depth'] < 0) { foreach (range(1, abs($this->options['depth'])) as $count) { - $field = $count == 1 ? 'tid' : 'entity_id'; - $subquery->leftJoin('taxonomy_term__parent', "th$count", "[$last].[$field] = [th$count].[parent_target_id]"); - $where->condition("th$count.entity_id", $tids, $operator); - $last = "th$count"; + $last = "th1"; + $inner_subquery = $connection->select('taxonomy_term__parent', "th1"); + $inner_subquery->addField("th$count", 'parent_target_id'); + $inner_where = $connection->condition('OR')->condition("th1.entity_id", $tids, $operator); + if ($count > 1) { + foreach (range(1, $count) as $inner_count) { + $subquery->leftJoin('taxonomy_term__parent', "th$inner_count", "[$last].[entity_id] = [th$inner_count].[parent_target_id]"); + $where->condition("th$inner_count.entity_id", $tids, $operator); + $last = "th$inner_count"; + } + } + $inner_subquery->condition($inner_where); + $where->condition('tn.tid', $inner_subquery, 'IN'); } } diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php index 3cc63bcc1f..124ddcdb34 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBase.php +++ b/core/tests/Drupal/KernelTests/KernelTestBase.php @@ -346,8 +346,8 @@ private function bootKernel() { // When a module is providing the database driver, then enable that module. $connection_info = Database::getConnectionInfo(); $driver = $connection_info['default']['driver']; - $namespace = $connection_info['default']['namespace'] ?? NULL; - $autoload = $connection_info['default']['autoload'] ?? NULL; + $namespace = $connection_info['default']['namespace'] ?? ''; + $autoload = $connection_info['default']['autoload'] ?? ''; if (strpos($autoload, 'src/Driver/Database/') !== FALSE) { [$first, $second] = explode('\\', $namespace, 3); if ($first === 'Drupal' && strtolower($second) === $second) { diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php index aadbf5a701..28efd59d20 100644 --- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php +++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php @@ -76,6 +76,8 @@ public static function isDeprecationSkipped($message) { // issues and thus were not addressed in time for the 9.0.0 release. '%The entity link url update for the "\w+" view is deprecated in drupal:9.0.0 and is removed from drupal:10.0.0. Module-provided Views configuration should be updated to accommodate the changes described at https://www.drupal.org/node/2857891.%', '%The operator defaults update for the "\w+" view is deprecated in drupal:9.0.0 and is removed from drupal:10.0.0. Module-provided Views configuration should be updated to accommodate the changes described at https://www.drupal.org/node/2869168.%', + // PHP 8.1. + '%Implicit conversion from float \d+\.\d+ to int loses precision%', ]; return (bool) preg_filter($dynamic_skipped_deprecations, '$0', $message); } @@ -125,6 +127,59 @@ public static function getSkippedDeprecations() { 'AssertLegacyTrait::assertNoRaw() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseNotContains() instead. See https://www.drupal.org/node/3129738', // PHPUnit 9. "The \"PHPUnit\TextUI\DefaultResultPrinter\" class is considered internal This class is not covered by the backward compatibility promise for PHPUnit. It may change without further notice. You should not use it from \"Drupal\Tests\Listeners\HtmlOutputPrinter\".", + // PHP 8.1. + 'abs(): Passing null to parameter #1 ($num) of type int|float is deprecated', + 'DateTime::createFromFormat(): Passing null to parameter #2 ($datetime) of type string is deprecated', + 'DateTime::setTimestamp(): Passing null to parameter #1 ($timestamp) of type int is deprecated', + 'Declaration of Twig\Markup::jsonSerialize() should be compatible with JsonSerializable::jsonSerialize(): mixed', + 'DOMElement::setAttribute(): Passing null to parameter #2 ($value) of type string is deprecated', + 'DOMImplementation::createDocument(): Passing null to parameter #2 ($qualifiedName) of type string is deprecated', + 'Exception::__construct(): Passing null to parameter #1 ($message) of type string is deprecated', + 'Exception::__construct(): Passing null to parameter #2 ($code) of type int is deprecated', + 'explode(): Passing null to parameter #2 ($string) of type string is deprecated', + 'hash(): Passing null to parameter #2 ($data) of type string is deprecated', + 'htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated', + 'http_build_query(): Passing null to parameter #2 ($numeric_prefix) of type string is deprecated', + 'imagejpeg(): Passing null to parameter #3 ($quality) of type int is deprecated', + 'ltrim(): Passing null to parameter #1 ($string) of type string is deprecated', + 'mb_strlen(): Passing null to parameter #1 ($string) of type string is deprecated', + 'mb_strtolower(): Passing null to parameter #1 ($string) of type string is deprecated', + 'mb_substr(): Passing null to parameter #1 ($string) of type string is deprecated', + 'mb_substr(): Passing null to parameter #2 ($start) of type int is deprecated', + 'openlog(): Passing null to parameter #1 ($prefix) of type string is deprecated', + 'openlog(): Passing null to parameter #3 ($facility) of type int is deprecated', + 'parse_str(): Passing null to parameter #1 ($string) of type string is deprecated', + 'preg_match_all(): Passing null to parameter #2 ($subject) of type string is deprecated', + 'preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated', + 'preg_quote(): Passing null to parameter #1 ($str) of type string is deprecated', + 'preg_replace_callback(): Passing null to parameter #3 ($subject) of type array|string is deprecated', + 'preg_replace(): Passing null to parameter #2 ($replacement) of type array|string is deprecated', + 'preg_split(): Passing null to parameter #2 ($subject) of type string is deprecated', + 'preg_split(): Passing null to parameter #3 ($limit) of type int is deprecated', + 'realpath(): Passing null to parameter #1 ($path) of type string is deprecated', + 'round(): Passing null to parameter #1 ($num) of type int|float is deprecated', + 'rtrim(): Passing null to parameter #1 ($string) of type string is deprecated', + 'stripos(): Passing null to parameter #1 ($haystack) of type string is deprecated', + 'stripslashes(): Passing null to parameter #1 ($string) of type string is deprecated', + 'strip_tags(): Passing null to parameter #1 ($string) of type string is deprecated', + 'strlen(): Passing null to parameter #1 ($string) of type string is deprecated', + 'strnatcasecmp(): Passing null to parameter #1 ($string1) of type string is deprecated', + 'strnatcasecmp(): Passing null to parameter #2 ($string2) of type string is deprecated', + 'strncmp(): Passing null to parameter #1 ($string1) of type string is deprecated', + 'strpos(): Passing null to parameter #1 ($haystack) of type string is deprecated', + 'str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated', + 'str_replace(): Passing null to parameter #3 ($subject) of type array|string is deprecated', + 'strrpos(): Passing null to parameter #1 ($haystack) of type string is deprecated', + 'strtolower(): Passing null to parameter #1 ($string) of type string is deprecated', + 'strtotime(): Passing null to parameter #1 ($datetime) of type string is deprecated', + 'strtoupper(): Passing null to parameter #1 ($string) of type string is deprecated', + 'strtr(): Passing null to parameter #1 ($string) of type string is deprecated', + 'str_word_count(): Passing null to parameter #1 ($string) of type string is deprecated', + 'substr_count(): Passing null to parameter #1 ($haystack) of type string is deprecated', + 'substr(): Passing null to parameter #1 ($string) of type string is deprecated', + 'trim(): Passing null to parameter #1 ($string) of type string is deprecated', + 'unserialize(): Passing null to parameter #1 ($data) of type string is deprecated', + 'urlencode(): Passing null to parameter #1 ($string) of type string is deprecated', ]; } diff --git a/core/tests/bootstrap.php b/core/tests/bootstrap.php index 4e25592f71..19d3bb43ee 100644 --- a/core/tests/bootstrap.php +++ b/core/tests/bootstrap.php @@ -63,9 +63,11 @@ function drupal_phpunit_contrib_extension_directory_roots($root = NULL) { continue; } $path = "$sites_path/$site"; - $paths[] = is_dir("$path/modules") ? realpath("$path/modules") : NULL; - $paths[] = is_dir("$path/profiles") ? realpath("$path/profiles") : NULL; - $paths[] = is_dir("$path/themes") ? realpath("$path/themes") : NULL; + foreach (['modules', 'profiles', 'themes'] as $type) { + if (is_dir("$path/$type")) { + $paths[] = realpath("$path/$type"); + } + } } return array_filter($paths, 'file_exists'); }