Description
XmlReaderTask (through CleverAge\ProcessBundle\Filesystem\XmlFile::read()) does not handle bad input files:
- Empty file: the task crashes with a low-level
ValueError: SplFileObject::fread(): Argument #1 ($length) must be greater than 0.
- Invalid XML: the result of
DOMDocument::loadXML() is ignored. Only a PHP warning is raised, and an empty \DOMDocument (documentElement = null, no child nodes) is passed to the next tasks. In debug mode the Symfony error handler turns the warning into an exception, so the process fails; without debug (production), the process goes on silently with an empty document.
Reproduction
clever_age_process:
configurations:
demo.xml_reader_invalid:
tasks:
read:
service: '@CleverAge\ProcessBundle\Task\File\Xml\XmlReaderTask'
options:
file_path: '{{ file_path }}'
outputs: [dump]
dump:
service: '@CleverAge\ProcessBundle\Task\Debug\DebugTask'
printf '' > var/data/empty.xml
printf '<root><a>1</a>' > var/data/invalid.xml
bin/console cleverage:process:execute demo.xml_reader_invalid -c file_path:"'/var/www/var/data/empty.xml'"
bin/console cleverage:process:execute --no-debug demo.xml_reader_invalid -c file_path:"'/var/www/var/data/invalid.xml'"
Empty file:
CRITICAL [cleverage_process_task] SplFileObject::fread(): Argument #1 ($length) must be greater than 0
In XmlFile.php line 33:
Process demo.xml_reader_invalid has failed during process read with message
: 'SplFileObject::fread(): Argument #1 ($length) must be greater than 0'.
Invalid XML (--no-debug): the process does not fail, and the dumped document is empty:
ERROR [php] Warning: DOMDocument::loadXML(): Premature end of data in tag root line 1 in Entity, line: 1
+childNodes: DOMNodeList {
+length: 0
+documentElement: null
Tested on main (c1a2804), PHP 8.5, Symfony 7.4.
Cause
In XmlFile::read():
- the file size is passed to
fread() without a check, and fread(0) throws a ValueError (L33);
- the return value of
$dom->loadXML() is ignored (L35), and libxml errors are only reported as PHP warnings.
Proposed fix
- an empty file throws an explicit
\UnexpectedValueException('XML file "<path>" is empty');
loadXML() runs with libxml_use_internal_errors(true); the collected libxml errors are cleared and the previous setting is restored;
- if the load fails, or libxml reports an error or a fatal error, an
\UnexpectedValueException('Invalid XML in file "<path>": <libxml message> (line L, column C); ...') is thrown; libxml warnings are still tolerated;
- update the
XmlReaderTask reference documentation (Notes), add unit tests for XmlFile.
Requirements
Breaking changes
Bug fix: XmlReaderTask now throws an \UnexpectedValueException when the XML is invalid, instead of passing an empty or partial \DOMDocument to the next tasks with only a PHP warning. This includes XML that parses but has libxml errors, such as an undefined namespace prefix (<root><x:a/></root>), which used to give a partial document. An empty file now throws an \UnexpectedValueException instead of a \ValueError. Processes relying on the silent empty document (e.g. in production, without the Symfony debug error handler) will now fail at the reader task: use error_strategy to handle these files.
Description
XmlReaderTask(throughCleverAge\ProcessBundle\Filesystem\XmlFile::read()) does not handle bad input files:ValueError: SplFileObject::fread(): Argument #1 ($length) must be greater than 0.DOMDocument::loadXML()is ignored. Only a PHP warning is raised, and an empty\DOMDocument(documentElement=null, no child nodes) is passed to the next tasks. In debug mode the Symfony error handler turns the warning into an exception, so the process fails; without debug (production), the process goes on silently with an empty document.Reproduction
Empty file:
Invalid XML (
--no-debug): the process does not fail, and the dumped document is empty:Tested on
main(c1a2804), PHP 8.5, Symfony 7.4.Cause
In
XmlFile::read():fread()without a check, andfread(0)throws aValueError(L33);$dom->loadXML()is ignored (L35), and libxml errors are only reported as PHP warnings.Proposed fix
\UnexpectedValueException('XML file "<path>" is empty');loadXML()runs withlibxml_use_internal_errors(true); the collected libxml errors are cleared and the previous setting is restored;\UnexpectedValueException('Invalid XML in file "<path>": <libxml message> (line L, column C); ...')is thrown; libxml warnings are still tolerated;XmlReaderTaskreference documentation (Notes), add unit tests forXmlFile.Requirements
Breaking changes
Bug fix:
XmlReaderTasknow throws an\UnexpectedValueExceptionwhen the XML is invalid, instead of passing an empty or partial\DOMDocumentto the next tasks with only a PHP warning. This includes XML that parses but has libxml errors, such as an undefined namespace prefix (<root><x:a/></root>), which used to give a partial document. An empty file now throws an\UnexpectedValueExceptioninstead of a\ValueError. Processes relying on the silent empty document (e.g. in production, without the Symfony debug error handler) will now fail at the reader task: useerror_strategyto handle these files.