In PHP
, its possible to register shutdown functions, which (sometimes gets ignored, however) is definetely called in my scenario, see below.
PHP/libxml supported by the DOMDocument class in PHP does not play along well w/ my registered shutdown functions, if I want to call ->save()
(->saveXML()
works fine) after user abort (e.g. from registered shutdown function
or a class instance destructor
). Related is also the PHP connection handling.
Let the examples speak:
PHP version:
php --version
PHP 7.1.4 (cli) (built: Apr 25 2017 09:48:36) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
To reproduce user_abort I'm running the php through python2.7 run.py
:
import subprocess
cmd = ["/usr/bin/php", "./user_aborted.php"]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
# As this process exists here and the user_aborted.php
# has sleeps/blocks in for-cycle thus simulating a user-abort
# for the php subprocess.
The php script user_aborted.php
to try saving XML in shutdown function :
<?php
ignore_user_abort(false);
libxml_use_internal_errors(true);
$xml = new DOMDocument();
$xml_track = $xml->createElement( "Track", "Highway Blues" );
$xml->appendChild($xml_track);
function shutdown() {
global $xml;
$out_as_str = $xml->saveXML();
fwrite(STDERR, "\nout_as_str: " . var_export($out_as_str, true) . "\n");
$out_as_file = $xml->save('out.xml');
fwrite(STDERR, "\nout_as_file: >" . var_export($out_as_file, true) . "<\n");
fwrite(STDERR, "\nerrors: \n" . var_export(libxml_get_errors(), true) . "\n");
}
register_shutdown_function('shutdown');
$i = 2;
while ($i > 0) {
fwrite(STDERR, "\n PID: " . getmypid() . " aborted: " . connection_aborted());
echo("\nmaking some output on stdout"); // so user_abort will be checked
sleep(1);
$i--;
}
Now, if I run this script w/o user abort (simply calling PHP) with:
php user_aborted.php
the XML gets saved properly.
However when calling this through python2.7
(which simulates the user abort by exiting the parent process), python2.7 run.py
the weirdest things happen:
out_as_str
value is fine, looks the XML I wantedout.xml
is an empty filelibxml_get_errors
reports FLUSH problemsThe output w/ python looks: python2.7 run.py
PID: 16863 aborted: 0
out_as_str: '<?xml version="1.0"?>
<Track>Highway Blues</Track>
'
out_as_file: >false<
errors:
array (
0 =>
LibXMLError::__set_state(array(
'level' => 2,
'code' => 1545,
'column' => 0,
'message' => 'flush error',
'file' => '',
'line' => 0,
))
)
Sorry for the long post, but I was looking through PHP/libxml2 code the whole day w/o any success. :/
Reason:
It turns out this is due to a fix for a previous bug.
Links:
The linked php_libxml_streams_IO_write
function is the writecallback
(set in ext/libxml/libxml.c) for the buffer of the docp
object, which is handed over for the libxml
call on ext/dom/document.c. Ending up in libxml xmlIO.c where the buffer is NULL
hence the file given over for ->save(*)
does not get written.
Workaround:
Use the ->saveXML()
to get the XML representation in string and write it using file_put_contents(*)
by "hand":
$xml_as_str = $xml->saveXML();
file_put_contents('/tmp/my.xml', $xml_as_str);