| 1 |  |  | <?php | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  | /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  |  * Created by PhpStorm. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  |  * @author hashashiyyin [email protected] / [email protected] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  |  * Date: 22/04/24 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  |  * Time: 18:48 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  |  * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  |  */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  | namespace Matecat\XmlParser; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | use DOMDocument; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | use Exception; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  | use Matecat\XmlParser\Exception\InvalidXmlException; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  | use Matecat\XmlParser\Exception\XmlParsingException; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | use RuntimeException; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  | /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  |  * This class is copied from Symfony\Component\Config\Util\XmlUtils: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  |  * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  |  * Please see: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |  * https://github.com/symfony/config/blob/v4.0.0/Util/XmlUtils.php | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 23 |  |  |  */ | 
            
                                                                        
                            
            
                                    
            
            
                | 24 |  |  | class XmlDomLoader { | 
            
                                                                        
                            
            
                                    
            
            
                | 25 |  |  |     /** | 
            
                                                                        
                            
            
                                    
            
            
                | 26 |  |  |      * Parses an XML string. | 
            
                                                                        
                            
            
                                    
            
            
                | 27 |  |  |      * | 
            
                                                                        
                            
            
                                    
            
            
                | 28 |  |  |      * @param string      $content An XML string | 
            
                                                                        
                            
            
                                    
            
            
                | 29 |  |  |      * @param Config|null $config | 
            
                                                                        
                            
            
                                    
            
            
                | 30 |  |  |      * | 
            
                                                                        
                            
            
                                    
            
            
                | 31 |  |  |      * @return DOMDocument | 
            
                                                                        
                            
            
                                    
            
            
                | 32 |  |  |      * | 
            
                                                                        
                            
            
                                    
            
            
                | 33 |  |  |      * @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself | 
            
                                                                        
                            
            
                                    
            
            
                | 34 |  |  |      * @throws XmlParsingException When parsing of XML file returns error | 
            
                                                                        
                            
            
                                    
            
            
                | 35 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  |     public static function load( $content, Config $config = null ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  |         if ( !extension_loaded( 'dom' ) ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  |             throw new RuntimeException( 'Extension DOM is required.' ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |         if ( is_null( $config ) ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  |             $config = new Config(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  |         $internalErrors  = libxml_use_internal_errors( true ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |         $disableEntities = libxml_disable_entity_loader(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 |  |  |         libxml_clear_errors(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  |         $dom                  = new DOMDocument( '1.0', 'UTF-8' ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  |         $dom->validateOnParse = true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |         if ( is_string( $config->getSetRootElement() ) && !empty( $config->getSetRootElement() ) ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |             $content = "<{$config->getSetRootElement()}>$content</{$config->getSetRootElement()}>"; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 |  |  |         $res = $dom->loadXML( $content, $config->getXML_OPTIONS() ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  |         if ( !$res ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  |             libxml_disable_entity_loader( $disableEntities ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |             throw new XmlParsingException( implode( "\n", static::getXmlErrors( $internalErrors ) ) ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  |         $dom->normalizeDocument(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  |         libxml_use_internal_errors( $internalErrors ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  |         libxml_disable_entity_loader( $disableEntities ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 |  |  |         foreach ( $dom->childNodes as $child ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  |             if ( XML_DOCUMENT_TYPE_NODE === $child->nodeType && !$config->getAllowDocumentType() ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 |  |  |                 throw new XmlParsingException( 'Document types are not allowed.' ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  |             } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  |         if ( null !== $config->getSchemaOrCallable() ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 76 |  |  |             $internalErrors = libxml_use_internal_errors( true ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 |  |  |             libxml_clear_errors(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 |  |  |             $e = null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  |             if ( is_callable( $config->getSchemaOrCallable() ) ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |                 try { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  |                     $valid = call_user_func( $config->getSchemaOrCallable(), $dom, $internalErrors ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 |  |  |                 } catch ( Exception $e ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  |                     $valid = false; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 |  |  |             } elseif ( !is_array( $config->getSchemaOrCallable() ) && is_file( (string)$config->getSchemaOrCallable() ) ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  |                 $schemaSource = file_get_contents( (string)$config->getSchemaOrCallable() ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 |  |  |                 $valid        = @$dom->schemaValidateSource( $schemaSource ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  |             } else { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |                 libxml_use_internal_errors( $internalErrors ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |                 throw new XmlParsingException( 'The schemaOrCallable argument has to be a valid path to XSD file or callable.' ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  |             } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  |             if ( !$valid ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  |                 $messages = static::getXmlErrors( $internalErrors ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  |                 if ( empty( $messages ) ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |                     throw new InvalidXmlException( 'The XML is not valid.', 0, $e ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  |                 throw new XmlParsingException( implode( "\n", $messages ), 0, $e ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 |  |  |             } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 |  |  |         libxml_clear_errors(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  |         libxml_use_internal_errors( $internalErrors ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 |  |  |  | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 107 |  |  |         return $dom; | 
            
                                                                        
                            
            
                                    
            
            
                | 108 |  |  |     } | 
            
                                                                        
                            
            
                                    
            
            
                | 109 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 110 |  |  |     /** | 
            
                                                                        
                            
            
                                    
            
            
                | 111 |  |  |      * @param $internalErrors | 
            
                                                                        
                            
            
                                    
            
            
                | 112 |  |  |      * | 
            
                                                                        
                            
            
                                    
            
            
                | 113 |  |  |      * @return array | 
            
                                                                        
                            
            
                                    
            
            
                | 114 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |     private static function getXmlErrors( $internalErrors ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  |         $errors = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  |         foreach ( libxml_get_errors() as $error ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 |  |  |             $errors[] = sprintf( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |                     '[%s %s] %s (in %s - line %d, column %d)', | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  |                     LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 |  |  |                     $error->code, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 |  |  |                     trim( $error->message ), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 |  |  |                     $error->file ?: 'n/a', | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  |                     $error->line, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  |                     $error->column | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  |             ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 |  |  |         libxml_clear_errors(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  |         libxml_use_internal_errors( $internalErrors ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 |  |  |  | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 132 |  |  |         return $errors; | 
            
                                                        
            
                                    
            
            
                | 133 |  |  |     } | 
            
                                                        
            
                                    
            
            
                | 134 |  |  | } |