Skip to content
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

PHP 8.2 Improvements #533

Merged
merged 24 commits into from
Sep 20, 2022
Merged

PHP 8.2 Improvements #533

merged 24 commits into from
Sep 20, 2022

Conversation

swissspidy
Copy link
Contributor

@swissspidy swissspidy commented Aug 24, 2022

Fixes #532

@swissspidy swissspidy marked this pull request as ready for review August 25, 2022 07:15
composer.json Outdated Show resolved Hide resolved
}
} else {
$head = $this->getElementsByTagName(Tag::HEAD)->item(0);
if (!$head) {
$this->head = $this->createElement(Tag::HEAD);
$this->documentElement->insertBefore($this->head, $this->documentElement->firstChild);
$this->properties['head'] = $this->createElement(Tag::HEAD);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not continue to use $this->head and $this->body? Is there a reason to switch to using $this->properties?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for the sake of performance?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran into some issues when I originally tried that, but I might have been doing something wrong. I can give it another try 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just tested again. Even on PHP 8.1 I am getting lots of Undefined property warnings in this case.

Even when doing something seemingly simple like this:

// Throws warning
$this->html = $html;
return $this->html;

// Works
$this->html = $html;
return $html;

// Also works
$this->properties['html'] = $html;
return $this->properties['html'];

I also just analyzed the AmpProject\Dom\DocumentTest::testNormalizeDomStructure() test case for this.

  1. Calling normalizeDomStructure() calls $this->head
  2. This triggers __get()
  3. The getter for head runs, and it sees that it isn't set yet, which causes a call to normalizeDomStructure() again. The property hasn't been set yet.
  4. That one then calls moveInvalidHeadNodesToBody at the end
  5. That one then triggers Undefined property: AmpProject\Dom\Document::$head.

No such issues happen with the current $properties approach.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the issue at question is this note:

PHP will not call an overloaded method from within the same overloaded method. That means, for example, writing return $this->foo inside of __get() will return null and raise an E_WARNING if there is no foo property defined, rather than calling __get() a second time. However, overload methods may invoke other overload methods implicitly (such as __set() triggering __get()).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, if a caller is trying to access $x->foo which invokes a magic getter, the getter must not also try to access $x->foo although it can access $x->bar. So there can't be any recursive reference to the current magic property being gotten.

src/Dom/Document.php Outdated Show resolved Hide resolved
src/Dom/Document.php Outdated Show resolved Hide resolved
Comment on lines +111 to +117
/**
* Associative array for lazily-created, cached properties for the document.
*
* @var array
*/
private $properties = [];

Copy link
Member

@westonruter westonruter Sep 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized a potential large simplification here. Why if each of the properties were instead just declared as null?

Suggested change
/**
* Associative array for lazily-created, cached properties for the document.
*
* @var array
*/
private $properties = [];
public $xpath
public $html;
public $head;
public $body;
public $charset;
public $viewport;
public $ampElements;
public $ampCustomStyle;
public $ampCustomStyleByteCount;
public $inlineStyleByteCount
public $links;

The phpdoc notwithstanding.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you did this in 525e918 but then reverted. Did you try private instead of public? This would model what is done with the $links property already.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the problem with that approach. It's that private access in the class won't cause the getter to be invoked. I think I have a way around that, and that is to manually invoke __get() for methods on Document. I'm going to give it a try, though the commit may not be pushed until Monday.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bah. Doing this makes PHPStan blow up with a lot of "Access to private property" errors.

@westonruter westonruter added this to the 0.11.3 milestone Sep 20, 2022
@westonruter westonruter merged commit e943b0d into ampproject:main Sep 20, 2022
@swissspidy swissspidy deleted the try/532-php82 branch September 20, 2022 05:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PHP 8.2 Compatibility
3 participants