处理 user_abort 后 DOMDocument->save() 失败

DOMDocument->save() fails after user_abort is handled

PHP 中,可以 register shutdown functions,在我的场景中明确调用了它(但有时会被忽略),见下文。

PHP/libxml 支持 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.

让例子说话:

PHP版本:

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

为了重现 user_abort 我正在 运行 宁 php 通过 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.

php 脚本 user_aborted.php 尝试在关闭函数 XML 中保存 :

<?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--;
}

现在,如果我 运行 此脚本 w/o 用户中止(只需调用 PHP):
php user_aborted.php XML 得到正确保存

但是 当通过 python2.7 调用它时(模拟用户通过退出父进程中止),python2.7 run.py 最奇怪的事情发生了:

带有 python 的输出看起来是: 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,
  ))
) 

很抱歉 post,但我整天都在查看 PHP/libxml2 代码 w/o 是否成功。 :/

原因:

事实证明这是由于修复了之前的错误。

链接:

链接的 php_libxml_streams_IO_write 函数是 writecallback(设置 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,其中缓冲区是 NULL,因此为 ->save(*) 提供的文件不会得到写了。

解决方法:

使用->saveXML()获取字符串中的XML表示并使用"hand":

使用file_put_contents(*)编写
$xml_as_str = $xml->saveXML();
file_put_contents('/tmp/my.xml', $xml_as_str);