Skip to content

Commit 3b288a2

Browse files
Merge branch '6.4' into 7.0
* 6.4: (32 commits) [Validator] Fix registering "is_valid()" for `#[Expression]` [Scheduler] Trigger unique messages at runtime [Scheduler] Fix CHANGELOG [Clock] Add `DatePoint`: an immutable DateTime implementation with stricter error handling and return types [Scheduler] Allow modifying the schedule at runtime and recalculate heap [Cache] Fix Redis6Proxy [Finder] Disable failing test about open_basedir Fix merge Fix merge Minor CS fixes Deprecate `Kernel::stripComments()` Remove setAccessible reflection call in tests [Notifier] Telegram Bridge add escaping for \ [Component][AssertMapper] add type hint of an argument in asset mapper command [Translation] [Phrase] Refacto ReadConfig and WriteConfig into arrays [Routing] Fix routing collection defaults when adding a new route to a collection [Messenger] Fix cloned TraceableStack not unstacking the stack independently [Translation] Add `--as-tree` option to `translation:pull` command [Mime] Allow to add some headers as a strings [Translation] Give current locale to locale switcher runWithLocale callback ...
2 parents 423e6d2 + e6947d1 commit 3b288a2

File tree

6 files changed

+201
-11
lines changed

6 files changed

+201
-11
lines changed

Container.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -359,13 +359,9 @@ protected function getEnv(string $name): mixed
359359
$localName = $name;
360360
}
361361

362-
if ($processors->has($prefix)) {
363-
$processor = $processors->get($prefix);
364-
} else {
365-
$processor = new EnvVarProcessor($this);
366-
if (false === $i) {
367-
$prefix = '';
368-
}
362+
$processor = $processors->has($prefix) ? $processors->get($prefix) : new EnvVarProcessor($this);
363+
if (false === $i) {
364+
$prefix = '';
369365
}
370366

371367
$this->resolving[$envName] = true;

Dumper/PhpDumper.php

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
use Symfony\Component\DependencyInjection\Variable;
4545
use Symfony\Component\ErrorHandler\DebugClassLoader;
4646
use Symfony\Component\ExpressionLanguage\Expression;
47-
use Symfony\Component\HttpKernel\Kernel;
4847

4948
/**
5049
* PhpDumper dumps a service container as a PHP class.
@@ -556,7 +555,7 @@ private function generateProxyClasses(): array
556555
$proxyClasses = [];
557556
$alreadyGenerated = [];
558557
$definitions = $this->container->getDefinitions();
559-
$strip = '' === $this->docStar && method_exists(Kernel::class, 'stripComments');
558+
$strip = '' === $this->docStar;
560559
$proxyDumper = $this->getProxyDumper();
561560
ksort($definitions);
562561
foreach ($definitions as $id => $definition) {
@@ -605,7 +604,7 @@ private function generateProxyClasses(): array
605604

606605
if ($strip) {
607606
$proxyCode = "<?php\n".$proxyCode;
608-
$proxyCode = substr(Kernel::stripComments($proxyCode), 5);
607+
$proxyCode = substr(self::stripComments($proxyCode), 5);
609608
}
610609

611610
$proxyClass = explode(' ', $this->inlineRequires ? substr($proxyCode, \strlen($code)) : $proxyCode, 3)[1];
@@ -2324,4 +2323,65 @@ private function isProxyCandidate(Definition $definition, ?bool &$asGhostObject,
23242323

23252324
return $this->getProxyDumper()->isProxyCandidate($definition, $asGhostObject, $id) ? $definition : null;
23262325
}
2326+
2327+
/**
2328+
* Removes comments from a PHP source string.
2329+
*
2330+
* We don't use the PHP php_strip_whitespace() function
2331+
* as we want the content to be readable and well-formatted.
2332+
*/
2333+
private static function stripComments(string $source): string
2334+
{
2335+
if (!\function_exists('token_get_all')) {
2336+
return $source;
2337+
}
2338+
2339+
$rawChunk = '';
2340+
$output = '';
2341+
$tokens = token_get_all($source);
2342+
$ignoreSpace = false;
2343+
for ($i = 0; isset($tokens[$i]); ++$i) {
2344+
$token = $tokens[$i];
2345+
if (!isset($token[1]) || 'b"' === $token) {
2346+
$rawChunk .= $token;
2347+
} elseif (\T_START_HEREDOC === $token[0]) {
2348+
$output .= $rawChunk.$token[1];
2349+
do {
2350+
$token = $tokens[++$i];
2351+
$output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token;
2352+
} while (\T_END_HEREDOC !== $token[0]);
2353+
$rawChunk = '';
2354+
} elseif (\T_WHITESPACE === $token[0]) {
2355+
if ($ignoreSpace) {
2356+
$ignoreSpace = false;
2357+
2358+
continue;
2359+
}
2360+
2361+
// replace multiple new lines with a single newline
2362+
$rawChunk .= preg_replace(['/\n{2,}/S'], "\n", $token[1]);
2363+
} elseif (\in_array($token[0], [\T_COMMENT, \T_DOC_COMMENT])) {
2364+
if (!\in_array($rawChunk[\strlen($rawChunk) - 1], [' ', "\n", "\r", "\t"], true)) {
2365+
$rawChunk .= ' ';
2366+
}
2367+
$ignoreSpace = true;
2368+
} else {
2369+
$rawChunk .= $token[1];
2370+
2371+
// The PHP-open tag already has a new-line
2372+
if (\T_OPEN_TAG === $token[0]) {
2373+
$ignoreSpace = true;
2374+
} else {
2375+
$ignoreSpace = false;
2376+
}
2377+
}
2378+
}
2379+
2380+
$output .= $rawChunk;
2381+
2382+
unset($tokens, $rawChunk);
2383+
gc_mem_caches();
2384+
2385+
return $output;
2386+
}
23272387
}

EnvVarProcessorInterface.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ interface EnvVarProcessorInterface
2424
* Returns the value of the given variable as managed by the current instance.
2525
*
2626
* @param string $prefix The namespace of the variable
27+
* @param string $prefix The namespace of the variable; when the empty string is passed, null values should be kept as is
2728
* @param string $name The name of the variable within the namespace
2829
* @param \Closure(string): mixed $getEnv A closure that allows fetching more env vars
2930
*

Tests/ContainerTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\DependencyInjection\Container;
1616
use Symfony\Component\DependencyInjection\ContainerInterface;
17+
use Symfony\Component\DependencyInjection\EnvVarProcessor;
1718
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1819
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
1920
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
2021
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
2122
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
23+
use Symfony\Component\DependencyInjection\ServiceLocator;
2224
use Symfony\Contracts\Service\ResetInterface;
2325

2426
class ContainerTest extends TestCase
@@ -404,6 +406,33 @@ public function testRequestAnInternalSharedPrivateService()
404406
$c->get('internal_dependency');
405407
$c->get('internal');
406408
}
409+
410+
public function testGetEnvDoesNotAutoCastNullWithDefaultEnvVarProcessor()
411+
{
412+
$container = new Container();
413+
$container->setParameter('env(FOO)', null);
414+
$container->compile();
415+
416+
$r = new \ReflectionMethod($container, 'getEnv');
417+
$r->setAccessible(true);
418+
$this->assertNull($r->invoke($container, 'FOO'));
419+
}
420+
421+
public function testGetEnvDoesNotAutoCastNullWithEnvVarProcessorsLocatorReturningDefaultEnvVarProcessor()
422+
{
423+
$container = new Container();
424+
$container->setParameter('env(FOO)', null);
425+
$container->set('container.env_var_processors_locator', new ServiceLocator([
426+
'string' => static function () use ($container): EnvVarProcessor {
427+
return new EnvVarProcessor($container);
428+
},
429+
]));
430+
$container->compile();
431+
432+
$r = new \ReflectionMethod($container, 'getEnv');
433+
$r->setAccessible(true);
434+
$this->assertNull($r->invoke($container, 'FOO'));
435+
}
407436
}
408437

409438
class ProjectServiceContainer extends Container

Tests/Dumper/PhpDumperTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,6 +1945,110 @@ public function testCallableAdapterConsumer()
19451945
$this->assertInstanceOf(SingleMethodInterface::class, $container->get('bar')->foo);
19461946
$this->assertInstanceOf(Foo::class, $container->get('bar')->foo->theMethod());
19471947
}
1948+
1949+
/**
1950+
* @dataProvider getStripCommentsCodes
1951+
*/
1952+
public function testStripComments(string $source, string $expected)
1953+
{
1954+
$reflection = new \ReflectionClass(PhpDumper::class);
1955+
$method = $reflection->getMethod('stripComments');
1956+
1957+
$output = $method->invoke(null, $source);
1958+
1959+
// Heredocs are preserved, making the output mixing Unix and Windows line
1960+
// endings, switching to "\n" everywhere on Windows to avoid failure.
1961+
if ('\\' === \DIRECTORY_SEPARATOR) {
1962+
$expected = str_replace("\r\n", "\n", $expected);
1963+
$output = str_replace("\r\n", "\n", $output);
1964+
}
1965+
1966+
$this->assertEquals($expected, $output);
1967+
}
1968+
1969+
public static function getStripCommentsCodes(): array
1970+
{
1971+
return [
1972+
['<?php echo foo();', '<?php echo foo();'],
1973+
['<?php echo/**/foo();', '<?php echo foo();'],
1974+
['<?php echo/** bar */foo();', '<?php echo foo();'],
1975+
['<?php /**/echo foo();', '<?php echo foo();'],
1976+
['<?php echo \foo();', '<?php echo \foo();'],
1977+
['<?php echo/**/\foo();', '<?php echo \foo();'],
1978+
['<?php echo/** bar */\foo();', '<?php echo \foo();'],
1979+
['<?php /**/echo \foo();', '<?php echo \foo();'],
1980+
[<<<'EOF'
1981+
<?php
1982+
include_once \dirname(__DIR__).'/foo.php';
1983+
1984+
$string = 'string should not be modified';
1985+
1986+
$string = 'string should not be
1987+
1988+
modified';
1989+
1990+
1991+
$heredoc = <<<HD
1992+
1993+
1994+
Heredoc should not be modified {$a[1+$b]}
1995+
1996+
1997+
HD;
1998+
1999+
$nowdoc = <<<'ND'
2000+
2001+
2002+
Nowdoc should not be modified
2003+
2004+
2005+
ND;
2006+
2007+
/**
2008+
* some class comments to strip
2009+
*/
2010+
class TestClass
2011+
{
2012+
/**
2013+
* some method comments to strip
2014+
*/
2015+
public function doStuff()
2016+
{
2017+
// inline comment
2018+
}
2019+
}
2020+
EOF
2021+
, <<<'EOF'
2022+
<?php
2023+
include_once \dirname(__DIR__).'/foo.php';
2024+
$string = 'string should not be modified';
2025+
$string = 'string should not be
2026+
2027+
modified';
2028+
$heredoc = <<<HD
2029+
2030+
2031+
Heredoc should not be modified {$a[1+$b]}
2032+
2033+
2034+
HD;
2035+
$nowdoc = <<<'ND'
2036+
2037+
2038+
Nowdoc should not be modified
2039+
2040+
2041+
ND;
2042+
class TestClass
2043+
{
2044+
public function doStuff()
2045+
{
2046+
}
2047+
}
2048+
EOF
2049+
],
2050+
];
2051+
}
19482052
}
19492053

19502054
class Rot13EnvVarProcessor implements EnvVarProcessorInterface

Tests/Loader/IniFileLoaderTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
class IniFileLoaderTest extends TestCase
2121
{
22-
protected ContainerBuilder$container;
22+
protected ContainerBuilder $container;
2323
protected IniFileLoader $loader;
2424

2525
protected function setUp(): void

0 commit comments

Comments
 (0)