Skip to content

Typed property DOMDocument::$documentElement must not be accessed before initialization #12215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
TimWolla opened this issue Sep 15, 2023 · 3 comments

Comments

@TimWolla
Copy link
Member

Description

The following code:

<?php

try {
	$xml = new \DOMDocument('1.0', 'UTF-8');
	$xml->loadXML(
		<<<'EOT'
		<?xml version="1.0" encoding="UTF-8"?>
		<dummy>
		</dummy>
		EOT
	);
	var_dump($xml->documentElement);
} catch (Error $e) {
	echo '<pre>'.$e.'</pre>';
}

Resulted in this output:

Error: Typed property DOMDocument::$documentElement must not be accessed before initialization in /var/www/html/xml.php:12
Stack trace:
#0 {main}

But I expected this output instead:

object(DOMElement)#2 (23) { ["schemaTypeInfo"]=> NULL ["tagName"]=> string(5) "dummy" ["firstElementChild"]=> NULL ["lastElementChild"]=> NULL ["childElementCount"]=> int(0) ["previousElementSibling"]=> NULL ["nextElementSibling"]=> NULL ["nodeName"]=> string(5) "dummy" ["nodeValue"]=> string(1) " " ["nodeType"]=> int(1) ["parentNode"]=> string(22) "(object value omitted)" ["childNodes"]=> string(22) "(object value omitted)" ["firstChild"]=> string(22) "(object value omitted)" ["lastChild"]=> string(22) "(object value omitted)" ["previousSibling"]=> NULL ["nextSibling"]=> NULL ["attributes"]=> string(22) "(object value omitted)" ["ownerDocument"]=> string(22) "(object value omitted)" ["namespaceURI"]=> NULL ["prefix"]=> string(0) "" ["localName"]=> string(5) "dummy" ["baseURI"]=> string(14) "/var/www/html/" ["textContent"]=> string(1) " " }

Exact reproduction steps on a fresh Ubuntu 22.04 VM:

  1. apt install php8.1 php-xml
  2. Open the script in the browser: It works fine.
  3. sed -i '/Dynamic Extensions/a extension=dom.so' /etc/php/8.1/apache2/php.ini (i.e. adding extension=dom.so to the dynamic extensions section of php.ini).
  4. systemctl restart apache2
  5. Open the script in the browser: It emits the error.

Note: The cause of the issue is a configuration error on the users end, but the error message is very non-descript and thus hard to debug. This issue is intended to ask for an improvement in error reporting (e.g. emitting an error because the extension is laoded twice or because a necessary dependency is not loaded in the right order).

PHP Version

PHP 8.1.2

Operating System

Ubuntu 22.04

nielsdos added a commit to nielsdos/php-src that referenced this issue Sep 15, 2023
… ext/dom

When we try to load an extension multiple times, we still overwrite the
type, module number, and handle. If the module number is used to
indicate module boundaries (e.g. in reflection and in dom, see e.g.
dom_objects_set_class_ex), then all sorts of error can happen.

In the case of ext/dom, OP's error happens because the following
happens:
- The property handler is set up incorrectly in
  dom_objects_set_class_ex() because the wrong module number is
  specified. The class highest in the hierarchy is DOMNode, so the
  property handler is incorrectly set to that of DOMNode instead of
  DOMDocument.
- The documentElement property doesn't exist on DOMNode, it only exists
  on DOMDocument, so it tries to read using zend_std_read_property().
  As there is no user property called documentElement, that read
  operation returns an undef value.
  However, the type is still checked, resulting in the strange exception.
nielsdos added a commit to nielsdos/php-src that referenced this issue Sep 19, 2023
… ext/dom

When we try to load an extension multiple times, we still overwrite the
type, module number, and handle. If the module number is used to
indicate module boundaries (e.g. in reflection and in dom, see e.g.
dom_objects_set_class_ex), then all sorts of errors can happen.

In the case of ext/dom, OP's error happens because the following
happens:
- The property handler is set up incorrectly in
  dom_objects_set_class_ex() because the wrong module number is
  specified. The class highest in the hierarchy is DOMNode, so the
  property handler is incorrectly set to that of DOMNode instead of
  DOMDocument.
- The documentElement property doesn't exist on DOMNode, it only exists
  on DOMDocument, so it tries to read using zend_std_read_property().
  As there is no user property called documentElement, that read
  operation returns an undef value.
  However, the type is still checked, resulting in the strange exception.

Solve this by changing the API such that the data is only overwritten if
it's owned data.
nielsdos added a commit that referenced this issue Sep 20, 2023
* PHP-8.1:
  Fix GH-12215: Module entry being overwritten causes type errors in ext/dom (<= PHP 8.3)
  Fix bug #55098: SimpleXML iteration produces infinite loop
nielsdos added a commit that referenced this issue Sep 20, 2023
* PHP-8.2:
  Fix GH-12215: Module entry being overwritten causes type errors in ext/dom (<= PHP 8.3)
  Fix bug #55098: SimpleXML iteration produces infinite loop
nielsdos added a commit that referenced this issue Sep 20, 2023
* PHP-8.3:
  Fix GH-12215: Module entry being overwritten causes type errors in ext/dom (<= PHP 8.3)
  Fix bug #55098: SimpleXML iteration produces infinite loop
nielsdos added a commit that referenced this issue Sep 20, 2023
…t/dom (PHP 8.4)

When we try to load an extension multiple times, we still overwrite the
type, module number, and handle. If the module number is used to
indicate module boundaries (e.g. in reflection and in dom, see e.g.
dom_objects_set_class_ex), then all sorts of errors can happen.

In the case of ext/dom, OP's error happens because the following
happens:
- The property handler is set up incorrectly in
  dom_objects_set_class_ex() because the wrong module number is
  specified. The class highest in the hierarchy is DOMNode, so the
  property handler is incorrectly set to that of DOMNode instead of
  DOMDocument.
- The documentElement property doesn't exist on DOMNode, it only exists
  on DOMDocument, so it tries to read using zend_std_read_property().
  As there is no user property called documentElement, that read
  operation returns an undef value.
  However, the type is still checked, resulting in the strange exception.

Solve this by changing the API such that the data is only overwritten if
it's owned data.

Closes GH-12246.
@vmajor
Copy link

vmajor commented Oct 17, 2023

OK, I have no run into this error as well on my VM with Ubnutu 22.04.

php -v
PHP 8.1.12-1ubuntu4.3 (cli) (built: Aug 17 2023 17:37:48) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.12, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.12-1ubuntu4.3, Copyright (c), by Zend Technologies

Code that triggers this error. I am not a php developer, just a user that is stuck with a presumably functioning plugin that fails to function on my brand new VM. This is a short code snippet that is designed to generate the error that crashes the plugin:

<?php

try {
        $xml = new \DOMDocument('1.0', 'UTF-8');
        $xml_string = <<<'EOT'
                <?xml version="1.0" encoding="UTF-8"?>
                <dummy>
                </dummy>
                EOT;
        if ($xml->loadXML($xml_string)) {
          var_dump($xml->documentElement);
        } else {
          echo "Invalid XML string";
        }
} catch (Error $e) {
        echo '<pre>'.$e.'</pre>';
}

?>

Error:

Error: Typed property DOMDocument::$documentElement must not be accessed before initialization in /var/www/*****/test2.php:11
Stack trace:
#0 {main}

DOM section of the php.ini

DOM/XML enabled
DOM/XML API Version 20031129
libxml Version 2.9.14
HTML Support enabled
XPath Support enabled
XPointer Support enabled
Schema Support enabled
RelaxNG Support enabled

I have no option to use a higher version of php because one is not available in the official lunar channel, and I cannot install ondrej/ppa because no repo is available for my version of ubuntu. Attempting to add the ppa fails with a 404.

Help?

@nielsdos
Copy link
Member

@vmajor you are somehow loading the DOM extension twice, causing it to get confused.
Either the DOM extension is embedded in the php binary, and you don't need to load it in the php.ini again. In that case remove the extension line for DOM in php.ini.
Or you are loading it twice in the php.ini and you have to remove the duplicate line.

I have no experience with ppa.
But in any case, you can always install a newer PHM version by compiling from the git repo yourself.

@vmajor
Copy link

vmajor commented Oct 17, 2023

Oh... thank you for the extremely fast, and correct reply. I had only one instance of DOM in php.ini, but I commented it out and now the test script works. Next, testing the presumably functional plugin.

Thank you so much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants