| 1 |  |  | <?php declare(strict_types=1); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  | * @package   s9e\SweetDOM | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | * @copyright Copyright (c) The s9e authors | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  | * @license   http://www.opensource.org/licenses/mit-license.php The MIT License | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | namespace s9e\SweetDOM; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  | use DOMDocument; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | use DOMNode; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | use DOMNodeList; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | use DOMXPath; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  | use RuntimeException; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  | use const PHP_VERSION; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | use function func_get_args, libxml_get_last_error, trim, version_compare; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  | /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  | * @method Attr|false createAttribute(string $localName) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  | * @method Attr|false createAttributeNS(?string $namespace, string $qualifiedName) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  | * @method CdataSection|false createCDATASection(string $data) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  | * @method Comment createComment(string $data) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 | 26 |  | * @method DocumentFragment createDocumentFragment() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  | * @method Element|false createElement(string $localName, string $value = '') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 | 26 |  | * @method Element|false createElementNS(?string $namespace, string $qualifiedName, string $value = '') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  | * @method Text createTextNode(string $data) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 | 26 |  | * @method ?Element getElementById(string $elementId) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  | * @property ?DocumentType $doctype | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  | * @property ?Element $documentElement | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  | * @property ?Element $firstElementChild | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  | * @property ?Element $lastElementChild | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  | * @property ?Document $ownerDocument | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  | * @property ?Element $parentElement | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  | */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  | class Document extends DOMDocument | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 | 2 |  | { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  | 	public NodeCreator $nodeCreator; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 | 2 |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 | 2 |  | 	/** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  | 	* @link https://www.php.net/manual/domdocument.construct.php | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 41 | 1 |  | 	*/ | 
            
                                                                        
                            
            
                                    
            
            
                | 42 |  |  | 	public function __construct(string $version = '1.0', string $encoding = '') | 
            
                                                                        
                            
            
                                    
            
            
                | 43 |  |  | 	{ | 
            
                                                                        
                            
            
                                    
            
            
                | 44 | 2 |  | 		parent::__construct($version, $encoding); | 
            
                                                                        
                            
            
                                    
            
            
                | 45 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 46 |  |  | 		$this->nodeCreator = new NodeCreator($this); | 
            
                                                                        
                            
            
                                    
            
            
                | 47 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 48 |  |  | 		$classes   = ['Attr', 'CdataSection', 'Comment', 'DocumentFragment', 'Element', 'Text']; | 
            
                                                                        
                            
            
                                    
            
            
                | 49 |  |  | 		$namespace = $this->getNodesNamespace(); | 
            
                                                                        
                            
            
                                    
            
            
                | 50 |  |  | 		foreach ($classes as $className) | 
            
                                                                        
                            
            
                                    
            
            
                | 51 |  |  | 		{ | 
            
                                                                        
                            
            
                                    
            
            
                | 52 |  |  | 			$this->registerNodeClass('DOM' . $className, $namespace . '\\' . $className); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 | 2 |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 | 2 |  | 	/** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 | 2 |  | 	* Evaluate and return the result of a given XPath expression | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  | 	*/ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 | 2 |  | 	public function evaluate(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  | 	{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  | 		return $this->xpath('evaluate', func_get_args()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  | 	/** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  | 	* Evaluate and return the first element of a given XPath query | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  | 	*/ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 | 1 |  | 	public function firstOf(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): ?DOMNode | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  | 	{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 | 1 |  | 		return $this->query(...func_get_args())->item(0); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  | 	/** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 |  |  | 	* Evaluate and return the result of a given XPath query | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 |  |  | 	*/ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  | 	public function query(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): DOMNodeList | 
            
                                                                                                            
                            
            
                                    
            
            
                | 76 |  |  | 	{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 |  |  | 		$result = $this->xpath('query', func_get_args()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 | 2 |  | 		if ($result === false) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 |  |  | 		{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 | 2 |  | 			$errorMessage = libxml_get_last_error()?->message ?? 'No error message'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  | 			throw new RuntimeException('Invalid XPath query: ' . trim($errorMessage)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  | 		return $result; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 |  |  | 	protected function getNodesNamespace(): string | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 | 1 |  | 	{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  | 		$namespace = __NAMESPACE__; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 | 1 |  | 		if ($this->needsWorkarounds()) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 | 1 |  | 		{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  | 			$namespace .= '\\PatchedNodes'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 | 1 |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  | 		elseif (version_compare(PHP_VERSION, '8.3.0', '<')) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  | 		{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  | 			$namespace .= '\\ForwardCompatibleNodes'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  | 		return $namespace; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  | 	protected function needsWorkarounds(): bool | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 | 2 |  | 	{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  | 		if (version_compare(PHP_VERSION, '8.2.10', '>=')) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 | 2 |  | 		{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 107 | 2 |  | 			// PHP ^8.2.10 needs no workarounds | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 |  |  | 			return false; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 | 2 |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  | 		if (version_compare(PHP_VERSION, '8.1.23', '<')) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  | 		{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  | 			// Anything older than 8.1.23 does | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  | 			return true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  | 		// ~8.1.23 is okay, anything between 8.2.0 and 8.2.9 needs workarounds | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  | 		return version_compare(PHP_VERSION, '8.2.0-dev', '>='); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 | 2 |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 | 2 |  | 	/** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 |  |  | 	* Execute a DOMXPath method and return the result | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 |  |  | 	*/ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 |  |  | 	protected function xpath(string $methodName, array $args): mixed | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  | 	{ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  | 		$xpath = new DOMXPath($this); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  | 		$xpath->registerNamespace('xsl', 'http://www.w3.org/1999/XSL/Transform'); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 |  |  | 		$xpath->registerNodeNamespaces = true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 | 2 |  | 		return $xpath->$methodName(...$args); | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 130 |  |  | 	} | 
            
                                                        
            
                                    
            
            
                | 131 |  |  | } |