N°8834 - Add compatibility with PHP 8.4 (#819)

* N°8834 - Add compatibility with PHP 8.4

* Rollback of scssphp/scssphp version upgrade due to compilation error
This commit is contained in:
Lenaick
2026-02-26 10:36:32 +01:00
committed by GitHub
parent d4821b7edc
commit fc967c06ce
961 changed files with 12298 additions and 7130 deletions

View File

@@ -43,7 +43,7 @@ final class AssetExtension extends AbstractExtension
* If the package used to generate the path is an instance of
* UrlPackage, you will always get a URL and not a path.
*/
public function getAssetUrl(string $path, string $packageName = null): string
public function getAssetUrl(string $path, ?string $packageName = null): string
{
return $this->packages->getUrl($path, $packageName);
}
@@ -51,7 +51,7 @@ final class AssetExtension extends AbstractExtension
/**
* Returns the version of an asset.
*/
public function getAssetVersion(string $path, string $packageName = null): string
public function getAssetVersion(string $path, ?string $packageName = null): string
{
return $this->packages->getVersion($path, $packageName);
}

View File

@@ -59,18 +59,18 @@ final class CodeExtension extends AbstractExtension
$parts = explode('\\', $class);
$short = array_pop($parts);
return sprintf('<abbr title="%s">%s</abbr>', $class, $short);
return \sprintf('<abbr title="%s">%s</abbr>', $class, $short);
}
public function abbrMethod(string $method): string
{
if (str_contains($method, '::')) {
[$class, $method] = explode('::', $method, 2);
$result = sprintf('%s::%s()', $this->abbrClass($class), $method);
$result = \sprintf('%s::%s()', $this->abbrClass($class), $method);
} elseif ('Closure' === $method) {
$result = sprintf('<abbr title="%s">%1$s</abbr>', $method);
$result = \sprintf('<abbr title="%s">%1$s</abbr>', $method);
} else {
$result = sprintf('<abbr title="%s">%1$s</abbr>()', $method);
$result = \sprintf('<abbr title="%s">%1$s</abbr>()', $method);
}
return $result;
@@ -87,20 +87,22 @@ final class CodeExtension extends AbstractExtension
$item[1] = htmlspecialchars($item[1], \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset);
$parts = explode('\\', $item[1]);
$short = array_pop($parts);
$formattedValue = sprintf('<em>object</em>(<abbr title="%s">%s</abbr>)', $item[1], $short);
$formattedValue = \sprintf('<em>object</em>(<abbr title="%s">%s</abbr>)', $item[1], $short);
} elseif ('array' === $item[0]) {
$formattedValue = sprintf('<em>array</em>(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : htmlspecialchars(var_export($item[1], true), \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset));
$formattedValue = \sprintf('<em>array</em>(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : htmlspecialchars(var_export($item[1], true), \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset));
} elseif ('null' === $item[0]) {
$formattedValue = '<em>null</em>';
} elseif ('boolean' === $item[0]) {
$formattedValue = '<em>'.strtolower(htmlspecialchars(var_export($item[1], true), \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset)).'</em>';
} elseif ('resource' === $item[0]) {
$formattedValue = '<em>resource</em>';
} elseif (preg_match('/[^\x07-\x0D\x1B\x20-\xFF]/', $item[1])) {
$formattedValue = '<em>binary string</em>';
} else {
$formattedValue = str_replace("\n", '', htmlspecialchars(var_export($item[1], true), \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset));
}
$result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", htmlspecialchars($key, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), $formattedValue);
$result[] = \is_int($key) ? $formattedValue : \sprintf("'%s' => %s", htmlspecialchars($key, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), $formattedValue);
}
return implode(', ', $result);
@@ -119,52 +121,98 @@ final class CodeExtension extends AbstractExtension
*/
public function fileExcerpt(string $file, int $line, int $srcContext = 3): ?string
{
if (is_file($file) && is_readable($file)) {
// highlight_file could throw warnings
// see https://bugs.php.net/25725
$code = @highlight_file($file, true);
if (\PHP_VERSION_ID >= 80300) {
// remove main pre/code tags
$code = preg_replace('#^<pre.*?>\s*<code.*?>(.*)</code>\s*</pre>#s', '\\1', $code);
// split multiline code tags
$code = preg_replace_callback('#<code ([^>]++)>((?:[^<]*+\\n)++[^<]*+)</code>#', fn ($m) => "<code $m[1]>".str_replace("\n", "</code>\n<code $m[1]>", $m[2]).'</code>', $code);
// Convert spaces to html entities to preserve indentation when rendered
$code = str_replace(' ', '&nbsp;', $code);
$content = explode("\n", $code);
} else {
// remove main code/span tags
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
// split multiline spans
$code = preg_replace_callback('#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#', fn ($m) => "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>', $code);
$content = explode('<br />', $code);
}
$lines = [];
if (0 > $srcContext) {
$srcContext = \count($content);
}
for ($i = max($line - $srcContext, 1), $max = min($line + $srcContext, \count($content)); $i <= $max; ++$i) {
$lines[] = '<li'.($i == $line ? ' class="selected"' : '').'><a class="anchor" id="line'.$i.'"></a><code>'.self::fixCodeMarkup($content[$i - 1]).'</code></li>';
}
return '<ol start="'.max($line - $srcContext, 1).'">'.implode("\n", $lines).'</ol>';
if (!is_file($file) || !is_readable($file)) {
return null;
}
return null;
$contents = file_get_contents($file);
if (!str_contains($contents, '<?php') && !str_contains($contents, '<?=')) {
$lines = explode("\n", $contents);
if (0 > $srcContext) {
$srcContext = \count($lines);
}
return $this->formatFileExcerpt(
$this->extractExcerptLines($lines, $line, $srcContext),
$line,
$srcContext
);
}
// highlight_string could throw warnings
// see https://bugs.php.net/25725
$code = @highlight_string($contents, true);
if (\PHP_VERSION_ID >= 80300) {
// remove main pre/code tags
$code = preg_replace('#^<pre.*?>\s*<code.*?>(.*)</code>\s*</pre>#s', '\\1', $code);
// split multiline span tags
$code = preg_replace_callback(
'#<span ([^>]++)>((?:[^<\\n]*+\\n)++[^<]*+)</span>#',
static fn (array $m): string => "<span $m[1]>".str_replace("\n", "</span>\n<span $m[1]>", $m[2]).'</span>',
$code
);
$lines = explode("\n", $code);
} else {
// remove main code/span tags
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
// split multiline spans
$code = preg_replace_callback(
'#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#',
static fn (array $m): string => "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>',
$code
);
$lines = explode('<br />', $code);
}
if (0 > $srcContext) {
$srcContext = \count($lines);
}
return $this->formatFileExcerpt(
array_map(
self::fixCodeMarkup(...),
$this->extractExcerptLines($lines, $line, $srcContext),
),
$line,
$srcContext
);
}
private function extractExcerptLines(array $lines, int $selectedLine, int $srcContext): array
{
return \array_slice(
$lines,
max($selectedLine - $srcContext, 0),
min($srcContext * 2 + 1, \count($lines) - $selectedLine + $srcContext),
true
);
}
private function formatFileExcerpt(array $lines, int $selectedLine, int $srcContext): string
{
$start = max($selectedLine - $srcContext, 1);
return "<ol start=\"{$start}\">".implode("\n", array_map(
static fn (string $line, int $num): string => '<li'.(++$num === $selectedLine ? ' class="selected"' : '')."><a class=\"anchor\" id=\"line{$num}\"></a><code>{$line}</code></li>",
$lines,
array_keys($lines),
)).'</ol>';
}
/**
* Formats a file path.
*/
public function formatFile(string $file, int $line, string $text = null): string
public function formatFile(string $file, int $line, ?string $text = null): string
{
$file = trim($file);
if (null === $text) {
if (null !== $rel = $this->getFileRelative($file)) {
$rel = explode('/', htmlspecialchars($rel, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), 2);
$text = sprintf('<abbr title="%s%2$s">%s</abbr>%s', htmlspecialchars($this->projectDir, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), $rel[0], '/'.($rel[1] ?? ''));
$text = \sprintf('<abbr title="%s%2$s">%s</abbr>%s', htmlspecialchars($this->projectDir, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), $rel[0], '/'.($rel[1] ?? ''));
} else {
$text = htmlspecialchars($file, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset);
}
@@ -177,7 +225,7 @@ final class CodeExtension extends AbstractExtension
}
if (false !== $link = $this->getFileLink($file, $line)) {
return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', htmlspecialchars($link, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), $text);
return \sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', htmlspecialchars($link, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), $text);
}
return $text;
@@ -241,7 +289,7 @@ final class CodeExtension extends AbstractExtension
// missing </span> tag at the end of line
$opening = strpos($line, '<span');
$closing = strpos($line, '</span>');
if (false !== $opening && (false === $closing || $closing > $opening)) {
if (false !== $opening && (false === $closing || $closing < $opening)) {
$line .= '</span>';
}

View File

@@ -29,7 +29,7 @@ final class DumpExtension extends AbstractExtension
private ClonerInterface $cloner;
private ?HtmlDumper $dumper;
public function __construct(ClonerInterface $cloner, HtmlDumper $dumper = null)
public function __construct(ClonerInterface $cloner, ?HtmlDumper $dumper = null)
{
$this->cloner = $cloner;
$this->dumper = $dumper;

View File

@@ -19,6 +19,7 @@ use Symfony\Component\Form\ChoiceList\View\ChoiceView;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormRenderer;
use Symfony\Component\Form\FormView;
use Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
@@ -35,7 +36,7 @@ final class FormExtension extends AbstractExtension
{
private ?TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator = null)
public function __construct(?TranslatorInterface $translator = null)
{
$this->translator = $translator;
}
@@ -149,23 +150,26 @@ final class FormExtension extends AbstractExtension
private function createFieldChoicesList(iterable $choices, string|false|null $translationDomain): iterable
{
foreach ($choices as $choice) {
$translatableLabel = $this->createFieldTranslation($choice->label, [], $translationDomain);
if ($choice instanceof ChoiceGroupView) {
$translatableLabel = $this->createFieldTranslation($choice->label, [], $translationDomain);
yield $translatableLabel => $this->createFieldChoicesList($choice, $translationDomain);
continue;
}
/* @var ChoiceView $choice */
/** @var ChoiceView $choice */
$translatableLabel = $this->createFieldTranslation($choice->label, $choice->labelTranslationParameters, $translationDomain);
yield $translatableLabel => $choice->value;
}
}
private function createFieldTranslation(?string $value, array $parameters, string|false|null $domain): ?string
private function createFieldTranslation(TranslatableInterface|string|null $value, array $parameters, string|false|null $domain): ?string
{
if (!$this->translator || !$value || false === $domain) {
return $value;
return null !== $value ? (string) $value : null;
}
if ($value instanceof TranslatableInterface) {
return $value->trans($this->translator);
}
return $this->translator->trans($value, $parameters, $domain);

View File

@@ -33,7 +33,7 @@ final class HtmlSanitizerExtension extends AbstractExtension
];
}
public function sanitize(string $html, string $sanitizer = null): string
public function sanitize(string $html, ?string $sanitizer = null): string
{
return $this->sanitizers->get($sanitizer ?? $this->defaultSanitizer)->sanitize($html);
}

View File

@@ -25,7 +25,7 @@ final class HttpKernelRuntime
private FragmentHandler $handler;
private ?FragmentUriGeneratorInterface $fragmentUriGenerator;
public function __construct(FragmentHandler $handler, FragmentUriGeneratorInterface $fragmentUriGenerator = null)
public function __construct(FragmentHandler $handler, ?FragmentUriGeneratorInterface $fragmentUriGenerator = null)
{
$this->handler = $handler;
$this->fragmentUriGenerator = $fragmentUriGenerator;
@@ -57,7 +57,7 @@ final class HttpKernelRuntime
public function generateFragmentUri(ControllerReference $controller, bool $absolute = false, bool $strict = true, bool $sign = true): string
{
if (null === $this->fragmentUriGenerator) {
throw new \LogicException(sprintf('An instance of "%s" must be provided to use "%s()".', FragmentUriGeneratorInterface::class, __METHOD__));
throw new \LogicException(\sprintf('An instance of "%s" must be provided to use "%s()".', FragmentUriGeneratorInterface::class, __METHOD__));
}
return $this->fragmentUriGenerator->generate($controller, null, $absolute, $strict, $sign);

View File

@@ -42,7 +42,7 @@ final class LogoutUrlExtension extends AbstractExtension
*
* @param string|null $key The firewall key or null to use the current firewall key
*/
public function getLogoutPath(string $key = null): string
public function getLogoutPath(?string $key = null): string
{
return $this->generator->getLogoutPath($key);
}
@@ -52,7 +52,7 @@ final class LogoutUrlExtension extends AbstractExtension
*
* @param string|null $key The firewall key or null to use the current firewall key
*/
public function getLogoutUrl(string $key = null): string
public function getLogoutUrl(?string $key = null): string
{
return $this->generator->getLogoutUrl($key);
}

View File

@@ -28,7 +28,7 @@ final class ProfilerExtension extends BaseProfilerExtension
*/
private \SplObjectStorage $events;
public function __construct(Profile $profile, Stopwatch $stopwatch = null)
public function __construct(Profile $profile, ?Stopwatch $stopwatch = null)
{
parent::__construct($profile);

View File

@@ -28,13 +28,13 @@ final class SecurityExtension extends AbstractExtension
private ?AuthorizationCheckerInterface $securityChecker;
private ?ImpersonateUrlGenerator $impersonateUrlGenerator;
public function __construct(AuthorizationCheckerInterface $securityChecker = null, ImpersonateUrlGenerator $impersonateUrlGenerator = null)
public function __construct(?AuthorizationCheckerInterface $securityChecker = null, ?ImpersonateUrlGenerator $impersonateUrlGenerator = null)
{
$this->securityChecker = $securityChecker;
$this->impersonateUrlGenerator = $impersonateUrlGenerator;
}
public function isGranted(mixed $role, mixed $object = null, string $field = null): bool
public function isGranted(mixed $role, mixed $object = null, ?string $field = null): bool
{
if (null === $this->securityChecker) {
return false;
@@ -51,7 +51,7 @@ final class SecurityExtension extends AbstractExtension
}
}
public function getImpersonateExitUrl(string $exitTo = null): string
public function getImpersonateExitUrl(?string $exitTo = null): string
{
if (null === $this->impersonateUrlGenerator) {
return '';
@@ -60,7 +60,7 @@ final class SecurityExtension extends AbstractExtension
return $this->impersonateUrlGenerator->generateExitUrl($exitTo);
}
public function getImpersonateExitPath(string $exitTo = null): string
public function getImpersonateExitPath(?string $exitTo = null): string
{
if (null === $this->impersonateUrlGenerator) {
return '';

View File

@@ -26,7 +26,7 @@ final class StopwatchExtension extends AbstractExtension
private ?Stopwatch $stopwatch;
private bool $enabled;
public function __construct(Stopwatch $stopwatch = null, bool $enabled = true)
public function __construct(?Stopwatch $stopwatch = null, bool $enabled = true)
{
$this->stopwatch = $stopwatch;
$this->enabled = $enabled;

View File

@@ -37,7 +37,7 @@ final class TranslationExtension extends AbstractExtension
private ?TranslatorInterface $translator;
private ?TranslationNodeVisitor $translationNodeVisitor;
public function __construct(TranslatorInterface $translator = null, TranslationNodeVisitor $translationNodeVisitor = null)
public function __construct(?TranslatorInterface $translator = null, ?TranslationNodeVisitor $translationNodeVisitor = null)
{
$this->translator = $translator;
$this->translationNodeVisitor = $translationNodeVisitor;
@@ -47,10 +47,10 @@ final class TranslationExtension extends AbstractExtension
{
if (null === $this->translator) {
if (!interface_exists(TranslatorInterface::class)) {
throw new \LogicException(sprintf('You cannot use the "%s" if the Translation Contracts are not available. Try running "composer require symfony/translation".', __CLASS__));
throw new \LogicException(\sprintf('You cannot use the "%s" if the Translation Contracts are not available. Try running "composer require symfony/translation".', __CLASS__));
}
$this->translator = new class() implements TranslatorInterface {
$this->translator = new class implements TranslatorInterface {
use TranslatorTrait;
};
}
@@ -96,11 +96,11 @@ final class TranslationExtension extends AbstractExtension
/**
* @param array|string $arguments Can be the locale as a string when $message is a TranslatableInterface
*/
public function trans(string|\Stringable|TranslatableInterface|null $message, array|string $arguments = [], string $domain = null, string $locale = null, int $count = null): string
public function trans(string|\Stringable|TranslatableInterface|null $message, array|string $arguments = [], ?string $domain = null, ?string $locale = null, ?int $count = null): string
{
if ($message instanceof TranslatableInterface) {
if ([] !== $arguments && !\is_string($arguments)) {
throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be a locale passed as a string when the message is a "%s", "%s" given.', __METHOD__, TranslatableInterface::class, get_debug_type($arguments)));
throw new \TypeError(\sprintf('Argument 2 passed to "%s()" must be a locale passed as a string when the message is a "%s", "%s" given.', __METHOD__, TranslatableInterface::class, get_debug_type($arguments)));
}
if ($message instanceof TranslatableMessage && '' === $message->getMessage()) {
@@ -111,7 +111,7 @@ final class TranslationExtension extends AbstractExtension
}
if (!\is_array($arguments)) {
throw new \TypeError(sprintf('Unless the message is a "%s", argument 2 passed to "%s()" must be an array of parameters, "%s" given.', TranslatableInterface::class, __METHOD__, get_debug_type($arguments)));
throw new \TypeError(\sprintf('Unless the message is a "%s", argument 2 passed to "%s()" must be an array of parameters, "%s" given.', TranslatableInterface::class, __METHOD__, get_debug_type($arguments)));
}
if ('' === $message = (string) $message) {
@@ -125,10 +125,10 @@ final class TranslationExtension extends AbstractExtension
return $this->getTranslator()->trans($message, $arguments, $domain, $locale);
}
public function createTranslatable(string $message, array $parameters = [], string $domain = null): TranslatableMessage
public function createTranslatable(string $message, array $parameters = [], ?string $domain = null): TranslatableMessage
{
if (!class_exists(TranslatableMessage::class)) {
throw new \LogicException(sprintf('You cannot use the "%s" as the Translation Component is not installed. Try running "composer require symfony/translation".', __CLASS__));
throw new \LogicException(\sprintf('You cannot use the "%s" as the Translation Component is not installed. Try running "composer require symfony/translation".', __CLASS__));
}
return new TranslatableMessage($message, $parameters, $domain);

View File

@@ -46,7 +46,7 @@ final class WebLinkExtension extends AbstractExtension
/**
* Adds a "Link" HTTP header.
*
* @param string $rel The relation type (e.g. "preload", "prefetch", "prerender" or "dns-prefetch")
* @param string $rel The relation type (e.g. "preload", "prefetch", or "dns-prefetch")
* @param array $attributes The attributes of this link (e.g. "['as' => true]", "['pr' => 0.5]")
*
* @return string The relation URI
@@ -117,7 +117,11 @@ final class WebLinkExtension extends AbstractExtension
}
/**
* Indicates to the client that it should prerender this resource .
* Indicates to the client that it should prerender this resource.
*
* This feature is deprecated and superseded by the Speculation Rules API.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/prerender
*
* @param array $attributes The attributes of this link (e.g. "['as' => true]", "['pr' => 0.5]")
*

View File

@@ -48,7 +48,7 @@ final class WorkflowExtension extends AbstractExtension
/**
* Returns true if the transition is enabled.
*/
public function canTransition(object $subject, string $transitionName, string $name = null): bool
public function canTransition(object $subject, string $transitionName, ?string $name = null): bool
{
return $this->workflowRegistry->get($subject, $name)->can($subject, $transitionName);
}
@@ -58,12 +58,12 @@ final class WorkflowExtension extends AbstractExtension
*
* @return Transition[]
*/
public function getEnabledTransitions(object $subject, string $name = null): array
public function getEnabledTransitions(object $subject, ?string $name = null): array
{
return $this->workflowRegistry->get($subject, $name)->getEnabledTransitions($subject);
}
public function getEnabledTransition(object $subject, string $transition, string $name = null): ?Transition
public function getEnabledTransition(object $subject, string $transition, ?string $name = null): ?Transition
{
return $this->workflowRegistry->get($subject, $name)->getEnabledTransition($subject, $transition);
}
@@ -71,7 +71,7 @@ final class WorkflowExtension extends AbstractExtension
/**
* Returns true if the place is marked.
*/
public function hasMarkedPlace(object $subject, string $placeName, string $name = null): bool
public function hasMarkedPlace(object $subject, string $placeName, ?string $name = null): bool
{
return $this->workflowRegistry->get($subject, $name)->getMarking($subject)->has($placeName);
}
@@ -81,7 +81,7 @@ final class WorkflowExtension extends AbstractExtension
*
* @return string[]|int[]
*/
public function getMarkedPlaces(object $subject, bool $placesNameOnly = true, string $name = null): array
public function getMarkedPlaces(object $subject, bool $placesNameOnly = true, ?string $name = null): array
{
$places = $this->workflowRegistry->get($subject, $name)->getMarking($subject)->getPlaces();
@@ -99,7 +99,7 @@ final class WorkflowExtension extends AbstractExtension
* Use a string (the place name) to get place metadata
* Use a Transition instance to get transition metadata
*/
public function getMetadata(object $subject, string $key, string|Transition $metadataSubject = null, string $name = null): mixed
public function getMetadata(object $subject, string $key, string|Transition|null $metadataSubject = null, ?string $name = null): mixed
{
return $this
->workflowRegistry
@@ -109,7 +109,7 @@ final class WorkflowExtension extends AbstractExtension
;
}
public function buildTransitionBlockerList(object $subject, string $transitionName, string $name = null): TransitionBlockerList
public function buildTransitionBlockerList(object $subject, string $transitionName, ?string $name = null): TransitionBlockerList
{
$workflow = $this->workflowRegistry->get($subject, $name);