@@ -974,24 +974,48 @@ private function relativize($xpath)
974
974
// We cannot simply drop
975
975
$ nonMatchingExpression = 'a[name() = "b"] ' ;
976
976
977
- // Split any unions into individual expressions.
978
- foreach ($ this ->splitUnionParts ($ xpath ) as $ expression ) {
979
- $ expression = trim ($ expression );
980
- $ parenthesis = '' ;
981
-
982
- // If the union is inside some braces, we need to preserve the opening braces and apply
983
- // the change only inside it.
984
- if (preg_match ('/^[\(\s*]+/ ' , $ expression , $ matches )) {
985
- $ parenthesis = $ matches [0 ];
986
- $ expression = substr ($ expression , strlen ($ parenthesis ));
977
+ $ xpathLen = strlen ($ xpath );
978
+ $ openedBrackets = 0 ;
979
+ $ lastUnion = strspn ($ xpath , " \t\n\r\0\x0B" );
980
+
981
+ for ($ i = $ lastUnion ; $ i <= $ xpathLen ; ++$ i ) {
982
+ $ i += strcspn ($ xpath , '" \'[]| ' , $ i );
983
+
984
+ if ($ i < $ xpathLen ) {
985
+ switch ($ xpath [$ i ]) {
986
+ case '" ' :
987
+ case "' " :
988
+ if (false === $ i = strpos ($ xpath , $ xpath [$ i ], $ i + 1 )) {
989
+ return $ xpath ; // The XPath expression is invalid
990
+ }
991
+ continue 2 ;
992
+ case '[ ' :
993
+ case '] ' :
994
+ $ openedBrackets += '[ ' === $ xpath [$ i ] ? 1 : -1 ;
995
+ continue 2 ;
996
+ }
997
+ }
998
+ if ($ openedBrackets ) {
999
+ continue ;
987
1000
}
988
1001
1002
+ if ('( ' === $ xpath [$ lastUnion ]) {
1003
+ // If the union is inside some braces, we need to preserve the opening braces and apply
1004
+ // the change only inside it.
1005
+ $ j = 1 + strspn ($ xpath , "( \t\n\r\0\x0B" , $ lastUnion + 1 );
1006
+ $ parenthesis = substr ($ xpath , $ lastUnion , $ j );
1007
+ $ lastUnion += $ j ;
1008
+ } else {
1009
+ $ parenthesis = '' ;
1010
+ }
1011
+ $ expression = rtrim (substr ($ xpath , $ lastUnion , $ i - $ lastUnion ));
1012
+
989
1013
if (0 === strpos ($ expression , 'self::*/ ' )) {
990
1014
$ expression = './ ' .substr ($ expression , 8 );
991
1015
}
992
1016
993
1017
// add prefix before absolute element selector
994
- if (empty ( $ expression) ) {
1018
+ if ('' === $ expression ) {
995
1019
$ expression = $ nonMatchingExpression ;
996
1020
} elseif (0 === strpos ($ expression , '// ' )) {
997
1021
$ expression = 'descendant-or-self:: ' .substr ($ expression , 2 );
@@ -1004,58 +1028,24 @@ private function relativize($xpath)
1004
1028
} elseif ('/ ' === $ expression [0 ] || '. ' === $ expression [0 ] || 0 === strpos ($ expression , 'self:: ' )) {
1005
1029
$ expression = $ nonMatchingExpression ;
1006
1030
} elseif (0 === strpos ($ expression , 'descendant:: ' )) {
1007
- $ expression = 'descendant-or-self:: ' .substr ($ expression , strlen ( ' descendant:: ' ) );
1031
+ $ expression = 'descendant-or-self:: ' .substr ($ expression , 12 );
1008
1032
} elseif (preg_match ('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/ ' , $ expression )) {
1009
1033
// the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes)
1010
1034
$ expression = $ nonMatchingExpression ;
1011
1035
} elseif (0 !== strpos ($ expression , 'descendant-or-self:: ' )) {
1012
1036
$ expression = 'self:: ' .$ expression ;
1013
1037
}
1014
1038
$ expressions [] = $ parenthesis .$ expression ;
1015
- }
1016
-
1017
- return implode (' | ' , $ expressions );
1018
- }
1019
1039
1020
- /**
1021
- * Splits the XPath into parts that are separated by the union operator.
1022
- *
1023
- * @param string $xpath
1024
- *
1025
- * @return string[]
1026
- */
1027
- private function splitUnionParts ($ xpath )
1028
- {
1029
- // Split any unions into individual expressions. We need to iterate
1030
- // through the string to correctly parse opening/closing quotes and
1031
- // braces which is not possible with regular expressions.
1032
- $ unionParts = array ();
1033
- $ inSingleQuotedString = false ;
1034
- $ inDoubleQuotedString = false ;
1035
- $ openedBrackets = 0 ;
1036
- $ lastUnion = 0 ;
1037
- $ xpathLength = strlen ($ xpath );
1038
- for ($ i = 0 ; $ i < $ xpathLength ; ++$ i ) {
1039
- $ char = $ xpath [$ i ];
1040
-
1041
- if ($ char === "' " && !$ inDoubleQuotedString ) {
1042
- $ inSingleQuotedString = !$ inSingleQuotedString ;
1043
- } elseif ($ char === '" ' && !$ inSingleQuotedString ) {
1044
- $ inDoubleQuotedString = !$ inDoubleQuotedString ;
1045
- } elseif (!$ inSingleQuotedString && !$ inDoubleQuotedString ) {
1046
- if ($ char === '[ ' ) {
1047
- ++$ openedBrackets ;
1048
- } elseif ($ char === '] ' ) {
1049
- --$ openedBrackets ;
1050
- } elseif ($ char === '| ' && $ openedBrackets === 0 ) {
1051
- $ unionParts [] = substr ($ xpath , $ lastUnion , $ i - $ lastUnion );
1052
- $ lastUnion = $ i + 1 ;
1053
- }
1040
+ if ($ i === $ xpathLen ) {
1041
+ return implode (' | ' , $ expressions );
1054
1042
}
1043
+
1044
+ $ i += strspn ($ xpath , " \t\n\r\0\x0B" , $ i + 1 );
1045
+ $ lastUnion = $ i + 1 ;
1055
1046
}
1056
- $ unionParts [] = substr ($ xpath , $ lastUnion );
1057
1047
1058
- return $ unionParts ;
1048
+ return $ xpath ; // The XPath expression is invalid
1059
1049
}
1060
1050
1061
1051
/**
0 commit comments