bug #39068 [DependencyInjection][Translator] Silent deprecation triggered by libxml_disable_entity_loader (jderusse)
This PR was squashed before being merged into the 4.4 branch.
Discussion
----------
[DependencyInjection][Translator] Silent deprecation triggered by libxml_disable_entity_loader
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | Fix #39040
| License | MIT
| Doc PR | -
The XML entity loader is disabled by default since libxml 2.9
But, since PHP 8.0, calling the method `libxml_disable_entity_loader` triggers a deprecation which has been solved in symfony by calling `libxml_disable_entity_loader` only for libxml < 2.9
The issue is, some dependencies, enable the entity loader and does not restore the initial state afterward, leading to exceptions triggered by Symfony iteself.
In previous versions symfony was resilient by disabling the flag before working, which is not the case anymore to avoid the deprecation.
This PR restore the resiliency of Symfony for PHP < 8.0, which is not yet deprecated.
But we have no way to check the status of the entity loader without triggering a deprecation with Symfony 8.
Commits
-------
114b7a543a
[DependencyInjection][Translator] Silent deprecation triggered by libxml_disable_entity_loader
This commit is contained in:
commit
6db84b6ae9
@ -634,14 +634,13 @@ $imports
|
|||||||
EOF
|
EOF
|
||||||
;
|
;
|
||||||
|
|
||||||
if (\LIBXML_VERSION < 20900) {
|
if ($this->shouldEnableEntityLoader()) {
|
||||||
$disableEntities = libxml_disable_entity_loader(false);
|
$disableEntities = libxml_disable_entity_loader(false);
|
||||||
$valid = @$dom->schemaValidateSource($source);
|
$valid = @$dom->schemaValidateSource($source);
|
||||||
libxml_disable_entity_loader($disableEntities);
|
libxml_disable_entity_loader($disableEntities);
|
||||||
} else {
|
} else {
|
||||||
$valid = @$dom->schemaValidateSource($source);
|
$valid = @$dom->schemaValidateSource($source);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($tmpfiles as $tmpfile) {
|
foreach ($tmpfiles as $tmpfile) {
|
||||||
@unlink($tmpfile);
|
@unlink($tmpfile);
|
||||||
}
|
}
|
||||||
@ -649,6 +648,36 @@ EOF
|
|||||||
return $valid;
|
return $valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function shouldEnableEntityLoader(): bool
|
||||||
|
{
|
||||||
|
// Version prior to 8.0 can be enabled without deprecation
|
||||||
|
if (\PHP_VERSION_ID < 80000) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static $dom, $schema;
|
||||||
|
if (null === $dom) {
|
||||||
|
$dom = new \DOMDocument();
|
||||||
|
$dom->loadXML('<?xml version="1.0"?><test/>');
|
||||||
|
|
||||||
|
$tmpfile = tempnam(sys_get_temp_dir(), 'symfony');
|
||||||
|
register_shutdown_function(static function () use ($tmpfile) {
|
||||||
|
@unlink($tmpfile);
|
||||||
|
});
|
||||||
|
$schema = '<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xsd:include schemaLocation="file:///'.str_replace('\\', '/', $tmpfile).'" />
|
||||||
|
</xsd:schema>';
|
||||||
|
file_put_contents($tmpfile, '<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xsd:element name="test" type="testType" />
|
||||||
|
<xsd:complexType name="testType"/>
|
||||||
|
</xsd:schema>');
|
||||||
|
}
|
||||||
|
|
||||||
|
return !@$dom->schemaValidateSource($schema);
|
||||||
|
}
|
||||||
|
|
||||||
private function validateAlias(\DOMElement $alias, string $file)
|
private function validateAlias(\DOMElement $alias, string $file)
|
||||||
{
|
{
|
||||||
foreach ($alias->attributes as $name => $node) {
|
foreach ($alias->attributes as $name => $node) {
|
||||||
|
@ -29,6 +29,21 @@ class TranslationFilesTest extends TestCase
|
|||||||
$this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message'))));
|
$this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message'))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideTranslationFiles
|
||||||
|
* @group Legacy
|
||||||
|
*/
|
||||||
|
public function testTranslationFileIsValidWithoutEntityLoader($filePath)
|
||||||
|
{
|
||||||
|
$document = new \DOMDocument();
|
||||||
|
$document->loadXML(file_get_contents($filePath));
|
||||||
|
libxml_disable_entity_loader(true);
|
||||||
|
|
||||||
|
$errors = XliffUtils::validateSchema($document);
|
||||||
|
|
||||||
|
$this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message'))));
|
||||||
|
}
|
||||||
|
|
||||||
public function provideTranslationFiles()
|
public function provideTranslationFiles()
|
||||||
{
|
{
|
||||||
return array_map(
|
return array_map(
|
||||||
|
@ -29,6 +29,20 @@ class TranslationFilesTest extends TestCase
|
|||||||
$this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message'))));
|
$this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message'))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideTranslationFiles
|
||||||
|
*/
|
||||||
|
public function testTranslationFileIsValidWithoutEntityLoader($filePath)
|
||||||
|
{
|
||||||
|
$document = new \DOMDocument();
|
||||||
|
$document->loadXML(file_get_contents($filePath));
|
||||||
|
libxml_disable_entity_loader(true);
|
||||||
|
|
||||||
|
$errors = XliffUtils::validateSchema($document);
|
||||||
|
|
||||||
|
$this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message'))));
|
||||||
|
}
|
||||||
|
|
||||||
public function provideTranslationFiles()
|
public function provideTranslationFiles()
|
||||||
{
|
{
|
||||||
return array_map(
|
return array_map(
|
||||||
|
@ -61,21 +61,18 @@ class XliffUtils
|
|||||||
{
|
{
|
||||||
$xliffVersion = static::getVersionNumber($dom);
|
$xliffVersion = static::getVersionNumber($dom);
|
||||||
$internalErrors = libxml_use_internal_errors(true);
|
$internalErrors = libxml_use_internal_errors(true);
|
||||||
if (\LIBXML_VERSION < 20900) {
|
if ($shouldEnable = self::shouldEnableEntityLoader()) {
|
||||||
$disableEntities = libxml_disable_entity_loader(false);
|
$disableEntities = libxml_disable_entity_loader(false);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
$isValid = @$dom->schemaValidateSource(self::getSchema($xliffVersion));
|
$isValid = @$dom->schemaValidateSource(self::getSchema($xliffVersion));
|
||||||
if (!$isValid) {
|
if (!$isValid) {
|
||||||
if (\LIBXML_VERSION < 20900) {
|
return self::getXmlErrors($internalErrors);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if ($shouldEnable) {
|
||||||
libxml_disable_entity_loader($disableEntities);
|
libxml_disable_entity_loader($disableEntities);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::getXmlErrors($internalErrors);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (\LIBXML_VERSION < 20900) {
|
|
||||||
libxml_disable_entity_loader($disableEntities);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$dom->normalizeDocument();
|
$dom->normalizeDocument();
|
||||||
@ -86,6 +83,36 @@ class XliffUtils
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function shouldEnableEntityLoader(): bool
|
||||||
|
{
|
||||||
|
// Version prior to 8.0 can be enabled without deprecation
|
||||||
|
if (\PHP_VERSION_ID < 80000) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static $dom, $schema;
|
||||||
|
if (null === $dom) {
|
||||||
|
$dom = new \DOMDocument();
|
||||||
|
$dom->loadXML('<?xml version="1.0"?><test/>');
|
||||||
|
|
||||||
|
$tmpfile = tempnam(sys_get_temp_dir(), 'symfony');
|
||||||
|
register_shutdown_function(static function () use ($tmpfile) {
|
||||||
|
@unlink($tmpfile);
|
||||||
|
});
|
||||||
|
$schema = '<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xsd:include schemaLocation="file:///'.str_replace('\\', '/', $tmpfile).'" />
|
||||||
|
</xsd:schema>';
|
||||||
|
file_put_contents($tmpfile, '<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xsd:element name="test" type="testType" />
|
||||||
|
<xsd:complexType name="testType"/>
|
||||||
|
</xsd:schema>');
|
||||||
|
}
|
||||||
|
|
||||||
|
return !@$dom->schemaValidateSource($schema);
|
||||||
|
}
|
||||||
|
|
||||||
public static function getErrorsAsString(array $xmlErrors): string
|
public static function getErrorsAsString(array $xmlErrors): string
|
||||||
{
|
{
|
||||||
$errorsAsString = '';
|
$errorsAsString = '';
|
||||||
|
@ -29,6 +29,20 @@ class TranslationFilesTest extends TestCase
|
|||||||
$this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message'))));
|
$this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message'))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideTranslationFiles
|
||||||
|
*/
|
||||||
|
public function testTranslationFileIsValidWithoutEntityLoader($filePath)
|
||||||
|
{
|
||||||
|
$document = new \DOMDocument();
|
||||||
|
$document->loadXML(file_get_contents($filePath));
|
||||||
|
libxml_disable_entity_loader(true);
|
||||||
|
|
||||||
|
$errors = XliffUtils::validateSchema($document);
|
||||||
|
|
||||||
|
$this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message'))));
|
||||||
|
}
|
||||||
|
|
||||||
public function provideTranslationFiles()
|
public function provideTranslationFiles()
|
||||||
{
|
{
|
||||||
return array_map(
|
return array_map(
|
||||||
|
Reference in New Issue
Block a user