Skip to content

Commit ae1ceaa

Browse files
committed
Merge branch '7.2' into 7.3
* 7.2: [Serializer] Handle invalid mapping type property type [Config] Do not generate unreachable configuration paths [WebProfilerBundle] Fix missing indent on non php files opended in the profiler
2 parents e7551d5 + ac9f913 commit ae1ceaa

File tree

18 files changed

+593
-46
lines changed

18 files changed

+593
-46
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[
2+
"Hello",
3+
"World!"
4+
]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
echo 'Hello';
4+
echo 'World!';

src/Symfony/Bundle/WebProfilerBundle/Profiler/CodeExtension.php

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -119,39 +119,85 @@ public function formatArgsAsText(array $args): string
119119
*/
120120
public function fileExcerpt(string $file, int $line, int $srcContext = 3): ?string
121121
{
122-
if (is_file($file) && is_readable($file)) {
123-
// highlight_file could throw warnings
124-
// see https://bugs.php.net/25725
125-
$code = @highlight_file($file, true);
126-
if (\PHP_VERSION_ID >= 80300) {
127-
// remove main pre/code tags
128-
$code = preg_replace('#^<pre.*?>\s*<code.*?>(.*)</code>\s*</pre>#s', '\\1', $code);
129-
// split multiline span tags
130-
$code = preg_replace_callback('#<span ([^>]++)>((?:[^<\\n]*+\\n)++[^<]*+)</span>#', function ($m) {
131-
return "<span $m[1]>".str_replace("\n", "</span>\n<span $m[1]>", $m[2]).'</span>';
132-
}, $code);
133-
$content = explode("\n", $code);
134-
} else {
135-
// remove main code/span tags
136-
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
137-
// split multiline spans
138-
$code = preg_replace_callback('#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#', fn ($m) => "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>', $code);
139-
$content = explode('<br />', $code);
140-
}
122+
if (!is_file($file) || !is_readable($file)) {
123+
return null;
124+
}
125+
126+
$contents = file_get_contents($file);
127+
128+
if (!str_contains($contents, '<?php') && !str_contains($contents, '<?=')) {
129+
$lines = explode("\n", $contents);
141130

142-
$lines = [];
143131
if (0 > $srcContext) {
144-
$srcContext = \count($content);
132+
$srcContext = \count($lines);
145133
}
146134

147-
for ($i = max($line - $srcContext, 1), $max = min($line + $srcContext, \count($content)); $i <= $max; ++$i) {
148-
$lines[] = '<li'.($i == $line ? ' class="selected"' : '').'><a class="anchor" id="line'.$i.'"></a><code>'.self::fixCodeMarkup($content[$i - 1]).'</code></li>';
149-
}
135+
return $this->formatFileExcerpt(
136+
$this->extractExcerptLines($lines, $line, $srcContext),
137+
$line,
138+
$srcContext
139+
);
140+
}
150141

151-
return '<ol start="'.max($line - $srcContext, 1).'">'.implode("\n", $lines).'</ol>';
142+
// highlight_string could throw warnings
143+
// see https://bugs.php.net/25725
144+
$code = @highlight_string($contents, true);
145+
146+
if (\PHP_VERSION_ID >= 80300) {
147+
// remove main pre/code tags
148+
$code = preg_replace('#^<pre.*?>\s*<code.*?>(.*)</code>\s*</pre>#s', '\\1', $code);
149+
// split multiline span tags
150+
$code = preg_replace_callback(
151+
'#<span ([^>]++)>((?:[^<\\n]*+\\n)++[^<]*+)</span>#',
152+
static fn (array $m): string => "<span $m[1]>".str_replace("\n", "</span>\n<span $m[1]>", $m[2]).'</span>',
153+
$code
154+
);
155+
$lines = explode("\n", $code);
156+
} else {
157+
// remove main code/span tags
158+
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
159+
// split multiline spans
160+
$code = preg_replace_callback(
161+
'#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#',
162+
static fn (array $m): string => "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>',
163+
$code
164+
);
165+
$lines = explode('<br />', $code);
152166
}
153167

154-
return null;
168+
if (0 > $srcContext) {
169+
$srcContext = \count($lines);
170+
}
171+
172+
return $this->formatFileExcerpt(
173+
array_map(
174+
self::fixCodeMarkup(...),
175+
$this->extractExcerptLines($lines, $line, $srcContext),
176+
),
177+
$line,
178+
$srcContext
179+
);
180+
}
181+
182+
private function extractExcerptLines(array $lines, int $selectedLine, int $srcContext): array
183+
{
184+
return \array_slice(
185+
$lines,
186+
max($selectedLine - $srcContext, 0),
187+
min($srcContext * 2 + 1, \count($lines) - $selectedLine + $srcContext),
188+
true
189+
);
190+
}
191+
192+
private function formatFileExcerpt(array $lines, int $selectedLine, int $srcContext): string
193+
{
194+
$start = max($selectedLine - $srcContext, 1);
195+
196+
return "<ol start=\"{$start}\">".implode("\n", array_map(
197+
static fn (string $line, int $num): string => '<li'.(++$num === $selectedLine ? ' class="selected"' : '')."><a class=\"anchor\" id=\"line{$num}\"></a><code>{$line}</code></li>",
198+
$lines,
199+
array_keys($lines),
200+
)).'</ol>';
155201
}
156202

157203
/**
@@ -241,7 +287,7 @@ protected static function fixCodeMarkup(string $line): string
241287
// missing </span> tag at the end of line
242288
$opening = strpos($line, '<span');
243289
$closing = strpos($line, '</span>');
244-
if (false !== $opening && (false === $closing || $closing > $opening)) {
290+
if (false !== $opening && (false === $closing || $closing < $opening)) {
245291
$line .= '</span>';
246292
}
247293

src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/CodeExtensionTest.php

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,101 @@ public function testFormatFileIntegration()
129129
$this->assertEquals($expected, $this->render($template));
130130
}
131131

132+
/**
133+
* @dataProvider fileExcerptIntegrationProvider
134+
*/
135+
public function testFileExcerptIntegration(string $expected, array $data)
136+
{
137+
$template = <<<'TWIG'
138+
{{ file_path|file_excerpt(line, src_context) }}
139+
TWIG;
140+
$html = $this->render($template, $data);
141+
142+
// highlight_file function output changed sing PHP 8.3
143+
// see https://github.com/php/php-src/blob/e2667f17bc24e3cd200bb3eda457f566f1f77f8f/UPGRADING#L239-L242
144+
if (\PHP_VERSION_ID < 80300) {
145+
$html = str_replace('&nbsp;', ' ', $html);
146+
}
147+
148+
$html = html_entity_decode($html);
149+
150+
$this->assertEquals($expected, $html);
151+
}
152+
153+
public static function fileExcerptIntegrationProvider()
154+
{
155+
$fixturesPath = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures';
156+
157+
yield 'php file' => [
158+
'expected' => <<<'HTML'
159+
<ol start="1"><li><a class="anchor" id="line1"></a><code><span style="color: #0000BB"><?php</span></code></li>
160+
<li><a class="anchor" id="line2"></a><code><span style="color: #0000BB"></span></code></li>
161+
<li><a class="anchor" id="line3"></a><code><span style="color: #0000BB"></span><span style="color: #007700">echo </span><span style="color: #DD0000">'Hello'</span><span style="color: #007700">;</span></code></li>
162+
<li><a class="anchor" id="line4"></a><code><span style="color: #007700">echo </span><span style="color: #DD0000">'World!'</span><span style="color: #007700">;</span></code></li>
163+
<li><a class="anchor" id="line5"></a><code><span style="color: #007700"></span></code></li></ol>
164+
HTML,
165+
'data' => [
166+
'file_path' => $fixturesPath.\DIRECTORY_SEPARATOR.'hello_world.php',
167+
'line' => 0,
168+
'src_context' => 3,
169+
],
170+
];
171+
172+
yield 'php file with selected line and no source context' => [
173+
'expected' => <<<'HTML'
174+
<ol start="1"><li class="selected"><a class="anchor" id="line1"></a><code><span style="color: #0000BB"><?php</span></code></li>
175+
<li><a class="anchor" id="line2"></a><code><span style="color: #0000BB"></span></code></li>
176+
<li><a class="anchor" id="line3"></a><code><span style="color: #0000BB"></span><span style="color: #007700">echo </span><span style="color: #DD0000">'Hello'</span><span style="color: #007700">;</span></code></li>
177+
<li><a class="anchor" id="line4"></a><code><span style="color: #007700">echo </span><span style="color: #DD0000">'World!'</span><span style="color: #007700">;</span></code></li>
178+
<li><a class="anchor" id="line5"></a><code><span style="color: #007700"></span></code></li></ol>
179+
HTML,
180+
'data' => [
181+
'file_path' => $fixturesPath.\DIRECTORY_SEPARATOR.'hello_world.php',
182+
'line' => 1,
183+
'src_context' => -1,
184+
],
185+
];
186+
187+
yield 'php file excerpt with selected line and custom source context' => [
188+
'expected' => <<<'HTML'
189+
<ol start="2"><li class="selected"><a class="anchor" id="line3"></a><code><span style="color: #0000BB"></span><span style="color: #007700">echo </span><span style="color: #DD0000">'Hello'</span><span style="color: #007700">;</span></code></li>
190+
<li><a class="anchor" id="line4"></a><code><span style="color: #007700">echo </span><span style="color: #DD0000">'World!'</span><span style="color: #007700">;</span></code></li>
191+
<li><a class="anchor" id="line5"></a><code><span style="color: #007700"></span></code></li></ol>
192+
HTML,
193+
'data' => [
194+
'file_path' => $fixturesPath.\DIRECTORY_SEPARATOR.'hello_world.php',
195+
'line' => 3,
196+
'src_context' => 1,
197+
],
198+
];
199+
200+
yield 'php file excerpt with out of bound selected line' => [
201+
'expected' => <<<'HTML'
202+
<ol start="99"></ol>
203+
HTML,
204+
'data' => [
205+
'file_path' => $fixturesPath.\DIRECTORY_SEPARATOR.'hello_world.php',
206+
'line' => 100,
207+
'src_context' => 1,
208+
],
209+
];
210+
211+
yield 'json file' => [
212+
'expected' => <<<'HTML'
213+
<ol start="1"><li><a class="anchor" id="line1"></a><code>[</code></li>
214+
<li><a class="anchor" id="line2"></a><code> "Hello",</code></li>
215+
<li><a class="anchor" id="line3"></a><code> "World!"</code></li>
216+
<li><a class="anchor" id="line4"></a><code>]</code></li>
217+
<li><a class="anchor" id="line5"></a><code></code></li></ol>
218+
HTML,
219+
'data' => [
220+
'file_path' => $fixturesPath.\DIRECTORY_SEPARATOR.'hello_world.json',
221+
'line' => 0,
222+
'src_context' => 3,
223+
],
224+
];
225+
}
226+
132227
public function testFormatFileFromTextIntegration()
133228
{
134229
$template = <<<'TWIG'

src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,13 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n
126126
$class->addRequire($childClass);
127127
$this->classes[] = $childClass;
128128

129+
$nodeTypes = $this->getParameterTypes($node);
130+
$paramType = $this->getParamType($nodeTypes);
131+
129132
$hasNormalizationClosures = $this->hasNormalizationClosures($node);
130133
$comment = $this->getComment($node);
131-
if ($hasNormalizationClosures) {
132-
$comment = \sprintf(" * @template TValue\n * @param TValue \$value\n%s", $comment);
134+
if ($hasNormalizationClosures && 'array' !== $paramType) {
135+
$comment = \sprintf(" * @template TValue of %s\n * @param TValue \$value\n%s", $paramType, $comment);
133136
$comment .= \sprintf(' * @return %s|$this'."\n", $childClass->getFqcn());
134137
$comment .= \sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn());
135138
}
@@ -141,8 +144,7 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n
141144
$node->getName(),
142145
$this->getType($childClass->getFqcn(), $hasNormalizationClosures)
143146
);
144-
$nodeTypes = $this->getParameterTypes($node);
145-
$body = $hasNormalizationClosures ? '
147+
$body = $hasNormalizationClosures && 'array' !== $paramType ? '
146148
COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static
147149
{
148150
if (!\is_array($value)) {
@@ -177,7 +179,7 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n
177179
'COMMENT' => $comment,
178180
'PROPERTY' => $property->getName(),
179181
'CLASS' => $childClass->getFqcn(),
180-
'PARAM_TYPE' => \in_array('mixed', $nodeTypes, true) ? 'mixed' : implode('|', $nodeTypes),
182+
'PARAM_TYPE' => $paramType,
181183
]);
182184

183185
$this->buildNode($node, $childClass, $this->getSubNamespace($childClass));
@@ -217,10 +219,11 @@ private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuild
217219

218220
$nodeParameterTypes = $this->getParameterTypes($node);
219221
$prototypeParameterTypes = $this->getParameterTypes($prototype);
222+
$noKey = null === $key = $node->getKeyAttribute();
220223
if (!$prototype instanceof ArrayNode || ($prototype instanceof PrototypedArrayNode && $prototype->getPrototype() instanceof ScalarNode)) {
221224
$class->addUse(ParamConfigurator::class);
222225
$property = $class->addProperty($node->getName());
223-
if (null === $key = $node->getKeyAttribute()) {
226+
if ($noKey) {
224227
// This is an array of values; don't use singular name
225228
$nodeTypesWithoutArray = array_filter($nodeParameterTypes, static fn ($type) => 'array' !== $type);
226229
$body = '
@@ -241,7 +244,7 @@ public function NAME(PARAM_TYPE $value): static
241244
'PROPERTY' => $property->getName(),
242245
'PROTOTYPE_TYPE' => implode('|', $prototypeParameterTypes),
243246
'EXTRA_TYPE' => $nodeTypesWithoutArray ? '|'.implode('|', $nodeTypesWithoutArray) : '',
244-
'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $nodeParameterTypes),
247+
'PARAM_TYPE' => $this->getParamType($nodeParameterTypes, true),
245248
]);
246249
} else {
247250
$body = '
@@ -258,7 +261,7 @@ public function NAME(string $VAR, TYPE $VALUE): static
258261

259262
$class->addMethod($methodName, $body, [
260263
'PROPERTY' => $property->getName(),
261-
'TYPE' => \in_array('mixed', $prototypeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $prototypeParameterTypes),
264+
'TYPE' => $this->getParamType($prototypeParameterTypes, true),
262265
'VAR' => '' === $key ? 'key' : $key,
263266
'VALUE' => 'value' === $key ? 'data' : 'value',
264267
]);
@@ -279,18 +282,27 @@ public function NAME(string $VAR, TYPE $VALUE): static
279282
$this->getType($childClass->getFqcn().'[]', $hasNormalizationClosures)
280283
);
281284

285+
$paramType = $this->getParamType($noKey ? $nodeParameterTypes : $prototypeParameterTypes);
286+
282287
$comment = $this->getComment($node);
288+
<<<<<<< HEAD
283289
if ($hasNormalizationClosures) {
284290
$comment = \sprintf(" * @template TValue\n * @param TValue \$value\n%s", $comment);
285291
$comment .= \sprintf(' * @return %s|$this'."\n", $childClass->getFqcn());
286292
$comment .= \sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn());
293+
=======
294+
if ($hasNormalizationClosures && 'array' !== $paramType) {
295+
$comment = sprintf(" * @template TValue of %s\n * @param TValue \$value\n%s", $paramType, $comment);
296+
$comment .= sprintf(' * @return %s|$this'."\n", $childClass->getFqcn());
297+
$comment .= sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn());
298+
>>>>>>> 100c683018d ([Config] Do not generate unreachable configuration paths)
287299
}
288300
if ('' !== $comment) {
289301
$comment = "/**\n$comment*/\n";
290302
}
291303

292-
if (null === $key = $node->getKeyAttribute()) {
293-
$body = $hasNormalizationClosures ? '
304+
if ($noKey) {
305+
$body = $hasNormalizationClosures && 'array' !== $paramType ? '
294306
COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static
295307
{
296308
$this->_usedProperties[\'PROPERTY\'] = true;
@@ -312,10 +324,10 @@ public function NAME(string $VAR, TYPE $VALUE): static
312324
'COMMENT' => $comment,
313325
'PROPERTY' => $property->getName(),
314326
'CLASS' => $childClass->getFqcn(),
315-
'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : implode('|', $nodeParameterTypes),
327+
'PARAM_TYPE' => $paramType,
316328
]);
317329
} else {
318-
$body = $hasNormalizationClosures ? '
330+
$body = $hasNormalizationClosures && 'array' !== $paramType ? '
319331
COMMENTpublic function NAME(string $VAR, PARAM_TYPE $VALUE = []): CLASS|static
320332
{
321333
if (!\is_array($VALUE)) {
@@ -351,7 +363,7 @@ public function NAME(string $VAR, TYPE $VALUE): static
351363
'CLASS' => $childClass->getFqcn(),
352364
'VAR' => '' === $key ? 'key' : $key,
353365
'VALUE' => 'value' === $key ? 'data' : 'value',
354-
'PARAM_TYPE' => \in_array('mixed', $prototypeParameterTypes, true) ? 'mixed' : implode('|', $prototypeParameterTypes),
366+
'PARAM_TYPE' => $paramType,
355367
]);
356368
}
357369

@@ -596,4 +608,9 @@ private function getType(string $classType, bool $hasNormalizationClosures): str
596608
{
597609
return $classType.($hasNormalizationClosures ? '|scalar' : '');
598610
}
611+
612+
private function getParamType(array $types, bool $withParamConfigurator = false): string
613+
{
614+
return \in_array('mixed', $types, true) ? 'mixed' : ($withParamConfigurator ? 'ParamConfigurator|' : '').implode('|', $types);
615+
}
599616
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
use Symfony\Config\ArrayValuesConfig;
13+
14+
return static function (ArrayValuesConfig $config) {
15+
$config->transports('foo')->dsn('bar');
16+
$config->transports('bar', ['dsn' => 'foobar']);
17+
18+
$config->errorPages()->withTrace(false);
19+
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy