wpaeString = $wpaeString; } public function process($xml) { $this->step = 0; $xml = $this->preprocessXml($xml); $xml = $this->handleSimpleSnippets($xml); // Add a snippet to trigger a process $snippetCount = count($this->parseSnippetsInString($xml)); if($snippetCount == 0 ) { $xml .="[str_replace('a','b','c')]"; } // While we have snippets if ($snippetCount = count($this->parseSnippetsInString($xml))) { // $this->step++; $xml = '' . $xml . ''; $this->initVariables($xml); $root = $this->dom->getElementsByTagName("root"); $this->parseElement($root->item(0)); $response = $this->dom->saveXML($this->dom); $xml = $this->cleanResponse($response); // if ($this->step > 8) { // throw new WpaeTooMuchRecursionException('Too much recursion'); // } } $xml = $this->postProcessXml($xml); $xml = $this->decodeSpecialCharacters($xml); $xml = $this->encodeSpecialCharsInAttributes($xml); $xml = str_replace('**OPENSHORTCODE**', '[', $xml); $xml = str_replace('**CLOSESHORTCODE**', ']', $xml); return $this->pretify($xml); } /** * @param $xml * @return mixed */ public function pretify($xml) { $xml = '' . $xml . ''; $this->initVariables($xml); // $root = $this->dom->getElementsByTagName("root"); // $this->preprocess_attributes($root->item(0)); return "\n ".$this->cleanResponse($this->dom->saveXML($this->dom)); } private function preprocess_attributes(DOMNode $element){ if($element->hasAttributes()){ for ($i = 0; $i < $element->attributes->length; $i++) { $element->attributes->item($i)->nodeValue = $this->sanitizeAttribute($element->attributes->item($i)->nodeValue); } } if ($element->hasChildNodes()) { for ($i = 0; $i < $element->childNodes->length; $i++) { $this->preprocess_attributes($element->childNodes->item($i)); } } } private function parseElement(DOMNode $element) { if($element->hasAttributes() && $element->nodeValue == '') { if ($element->hasChildNodes()) { $has_text_elements = false; for ($i = 0; $i < $element->childNodes->length; $i++) { if ( $element->childNodes->item($i)->nodeType == XML_TEXT_NODE ){ $has_text_elements = true; break; } } if ( ! $has_text_elements ){ $nodeAttributes = $this->getNodeAttributes($element); $snippets = $this->parseSnippetsInString($nodeAttributes); if (!empty($snippets)){ $tagValues = array(); foreach ($snippets as $snippet) { $wholeValue = str_replace("\n", '', $nodeAttributes); $isInFunction = $this->wpaeString->isBetween($wholeValue, $snippet, '[',']'); $snippetValue = $this->processSnippet($snippet,$isInFunction); $tagValues[$snippet] = $snippetValue; } // Doing this to replace multiple snippet in the same tag (not to treat them as array and // replace the snippet with the first letter of the string foreach ($snippets as $snippet) { if(isset($tagValues[$snippet])){ //$element->nodeValue = str_replace($snippet, $tagValues[$snippet], $element->nodeValue); $this->replaceSnippetInAttributes($element, $snippet, $tagValues[$snippet]); } } } } for ($i = 0; $i < $element->childNodes->length; $i++) { $this->parseElement($element->childNodes->item($i)); } } $textNode = new DOMText('##FILLER##'); $element->appendChild($textNode); return $this->parseElement($textNode); } if ($element->nodeType === XML_TEXT_NODE) { $nodeAttributes = $this->getNodeAttributes($element->parentNode); $snippets = $this->parseSnippetsInString($element->nodeValue . $nodeAttributes); $maxTagValues = 0; $tagValues = array(); if (count($snippets) > 0) { if (count($snippets) == 1) { $snippet = $snippets[0]; $isInFunction = $this->wpaeString->isBetween($nodeAttributes.$element->nodeValue, $snippet, '[',']'); $snippetValues = $this->processSnippet($snippet, $isInFunction); if (!is_array($snippetValues)) { $element->nodeValue = str_replace( $snippet, $snippetValues, $element->nodeValue ); $nodeXML = $this->cloneNode($element->parentNode, $snippet, $snippetValues); $f = $this->dom->createDocumentFragment(); $f->appendXML($nodeXML); $this->parseElement($f); $element->parentNode->parentNode->replaceChild($f, $element->parentNode); } else { foreach ($snippetValues as $snippetValue) { $newValueNode = $element->parentNode->cloneNode(true); $newValueNode->nodeValue = str_replace($snippet, $snippetValue, $newValueNode->nodeValue); $this->replaceSnippetInAttributes($newValueNode, $snippet, $snippetValue); $this->elementCdata($newValueNode); $element->parentNode->parentNode->insertBefore($newValueNode, $element->parentNode); } $element->parentNode->parentNode->removeChild($element->parentNode); } } else if (count($snippets) > 1) { foreach ($snippets as $snippet) { $wholeValue = $nodeAttributes.$element->nodeValue; $wholeValue = str_replace("\n", '', $wholeValue); $isInFunction = $this->wpaeString->isBetween($wholeValue, $snippet, '[',']'); $snippetValue = $this->processSnippet($snippet,$isInFunction); $tagValues[$snippet] = $snippetValue; if (count($tagValues[$snippet]) > $maxTagValues) { $maxTagValues = count($tagValues[$snippet]); } } //We have arrays if ($maxTagValues > 1) { for ($i = 0; $i < $maxTagValues; $i++) { $elementClone = $element->parentNode->cloneNode(true); $elementValue = $elementClone->nodeValue; foreach ($snippets as $snippet) { // We might have the case that // there are arrays but also implodes in the same tag if(is_array($tagValues[$snippet])) { if (isset($tagValues[$snippet][$i])) { $elementValue = str_replace($snippet, $tagValues[$snippet][$i], $elementValue); $this->replaceSnippetInAttributes($elementClone, $snippet, $tagValues[$snippet][$i]); } else { $elementValue = str_replace($snippet, "", $elementValue); $this->replaceSnippetInAttributes($elementClone, $snippet, ""); } } else { $elementValue = str_replace($snippet, $tagValues[$snippet], $elementValue); $this->replaceSnippetInAttributes($elementClone, $snippet, $tagValues[$snippet]); } } $elementClone->nodeValue = $elementValue; $this->elementCdata($elementClone); $element->parentNode->parentNode->insertBefore($elementClone, $element->parentNode); } $element->parentNode->parentNode->removeChild($element->parentNode); } else { // Doing this to replace multiple snippet in the same tag (not to treat them as array and // replace the snippet with the first letter of the string foreach ($snippets as $snippet) { if(isset($tagValues[$snippet])){ $element->nodeValue = str_replace($snippet, $tagValues[$snippet], $element->nodeValue); $this->replaceSnippetInAttributes($element->parentNode, $snippet, $tagValues[$snippet]); } } } } } $this->elementCdata($element); } else { if ($element->hasChildNodes()) { $has_text_elements = false; for ($i = 0; $i < $element->childNodes->length; $i++) { if ( $element->childNodes->item($i)->nodeType == XML_TEXT_NODE ){ $has_text_elements = true; break; } } if ( ! $has_text_elements ){ $nodeAttributes = $this->getNodeAttributes($element); $snippets = $this->parseSnippetsInString($nodeAttributes); if (!empty($snippets)){ $tagValues = array(); foreach ($snippets as $snippet) { $wholeValue = str_replace("\n", '', $nodeAttributes); $isInFunction = $this->wpaeString->isBetween($wholeValue, $snippet, '[',']'); $snippetValue = $this->processSnippet($snippet,$isInFunction); $tagValues[$snippet] = $snippetValue; } // Doing this to replace multiple snippet in the same tag (not to treat them as array and // replace the snippet with the first letter of the string foreach ($snippets as $snippet) { if(isset($tagValues[$snippet])){ //$element->nodeValue = str_replace($snippet, $tagValues[$snippet], $element->nodeValue); $this->replaceSnippetInAttributes($element, $snippet, $tagValues[$snippet]); } } } } for ($i = 0; $i < $element->childNodes->length; $i++) { $this->parseElement($element->childNodes->item($i)); } } } } /** * @param $filtered * @return mixed */ private function sanitizeFunctionName($filtered) { $functionName = str_replace('array(','(', substr($filtered, 0, strpos($filtered, "("))); return $functionName; } /** * @param $originalTag * @return array */ private function parseSnippetsInString($originalTag) { $results = array(); $matches = array(); preg_match_all("%(\[[^\]\[]*\])%", $originalTag, $matches); $snippets = empty($matches) ? array() : array_unique($matches[0]); foreach ($snippets as $snippet) { $isCdataString = 'xml, $isCdataString) === false) { $results[] = $snippet; } } return $results; } /** * @param $v * @return string */ private function maybe_cdata($v, $hasSnippets = false) { if (XmlExportEngine::$is_preview) { $v = str_replace('&', '&', $v); $v = htmlspecialchars($v); $v = str_replace('##lt##','<', $v); $v = str_replace('##gt##','>', $v); } if (XmlExportEngine::$is_preview && !XmlExportEngine::$exportOptions['show_cdata_in_preview']) { return $v; } $cdataStrategyFactory = new CdataStrategyFactory(); if (!isset(XmlExportEngine::$exportOptions['custom_xml_cdata_logic'])) { XmlExportEngine::$exportOptions['custom_xml_cdata_logic'] = 'auto'; } $cdataStrategy = $cdataStrategyFactory->create_strategy(XmlExportEngine::$exportOptions['custom_xml_cdata_logic']); $is_wrap_into_cdata = $cdataStrategy->should_cdata_be_applied($this->decodeSpecialCharacters($v), $hasSnippets); if ($is_wrap_into_cdata === false) { return $v; } else { return 'CDATABEGIN' . $v . 'CDATACLOSE'; } } /** * @param $filtered * @param $functionName * @throws WpaeInvalidStringException */ private function checkCorrectNumberOfQuotes($filtered, $functionName) { $numberOfSingleQuotes = substr_count($filtered, "'"); $numberOfDoubleQuotes = substr_count($filtered, "\""); if ($numberOfSingleQuotes % 2 || $numberOfDoubleQuotes % 2) { throw new WpaeInvalidStringException($functionName); } } /** * @param $filtered * @return mixed */ private function sanitizeAttribute($filtered) { $filtered = str_replace('&', '&', $filtered); $filtered = str_replace('&', '&', $filtered); $filtered = str_replace("'", ''', $filtered); $filtered = str_replace('"', '"', $filtered); $filtered = str_replace('<', '<', $filtered); $filtered = str_replace('>', '>', $filtered); return $filtered; } /** * @param $functionName * @throws WpaeMethodNotFoundException */ private function checkIfFunctionExists($functionName) { if (!function_exists($functionName) && $functionName != 'array') { throw new WpaeMethodNotFoundException($functionName); } } /** * @param $snippet * @return mixed */ private function sanitizeSnippet($snippet) { $sanitizedSnippet = str_replace(array('[', ']'), '', $snippet); $sanitizedSnippet = str_replace('\'', '"', $sanitizedSnippet); return $sanitizedSnippet; } /** * @param $xml * * @return mixed */ private function handleSimpleSnippets($xml) { preg_match_all("%(\[[^\]\[]*\])%", $xml, $matches); $snippets = empty($matches) ? array() : array_unique($matches[0]); $simple_snipets = array(); preg_match_all("%(\{[^\}\{]*\})%", $xml, $matches); $xpaths = array_unique($matches[0]); if (!empty($xpaths)) { foreach ($xpaths as $xpath) { if (!in_array($xpath, $snippets)) $simple_snipets[] = $xpath; } } if (!empty($simple_snipets)) { foreach ($simple_snipets as $snippet) { $filtered = preg_replace("%[\{\}]%", "", $snippet); //Encode data in attributes if (strpos($xml, "\"$snippet\"") !== false || strpos($xml, "'$snippet'") !== false) { $attributeValue = str_replace('&', '&', $filtered); $attributeValue = str_replace('&', '&', $attributeValue); $attributeValue = str_replace('\'', ''', $attributeValue); $attributeValue = str_replace('"', '"', $attributeValue); $attributeValue = str_replace('<', '<', $attributeValue); $attributeValue = str_replace('>', '>', $attributeValue); $xml = str_replace("\"".$snippet."\"", "\"".$attributeValue."\"", $xml); $xml = str_replace("'".$snippet."'", "\"".$attributeValue."\"", $xml); } $filteredEncoded = $this->encodeSpecialCharacters($filtered); $xml = str_replace($snippet, self::SNIPPET_DELIMITER.$filteredEncoded.self::SNIPPET_DELIMITER, $xml); } } return $xml; } /** * @param $xml * * @return mixed */ public function encodeSpecialCharsInAttributes($xml) { preg_match_all('/<.*?=["\'](.*?)["\'].*?>/', $xml, $attributes); $attributes = $attributes[1]; foreach ($attributes as $attribute) { $attribute = trim($attribute, "'\""); if (!$this->wpaeString->isBetween($xml, $attribute, '')) { $xml = str_replace(array('\'' . $attribute . '\'', '"' . $attribute . '"'), '"' . $attribute . '"', $xml); } } return $xml; } /** * @param $xml * @return DOMDocument */ private function initVariables($xml) { $this->xml = $xml; $dom = new DOMDocument(); $dom->recover = true; $dom->preserveWhiteSpace = false; $dom->substituteEntities = false; $dom->resolveExternals = false; $dom->formatOutput = true; $dom->loadXML($xml); $this->dom = $dom; } /** * @param $snippet * @param bool $isInFunction * * @return mixed * @throws WpaeInvalidStringException * @throws WpaeMethodNotFoundException */ private function processSnippet($snippet, $isInFunction = false) { $sanitizedSnippet = $this->sanitizeSnippet($snippet); $sanitizedSnippet = str_replace(WpaeXmlProcessor::SNIPPET_DELIMITER, '"', $sanitizedSnippet); $functionName = $this->sanitizeFunctionName($sanitizedSnippet); $this->checkCorrectNumberOfQuotes($sanitizedSnippet, $functionName); $this->checkIfFunctionExists($functionName); $argsStr = preg_replace("%^".$functionName."\((.*)\)$%", "$1", $sanitizedSnippet); preg_match_all("%(\"[^\"]*\")%", $argsStr, $matches); if (!empty($matches[0])){ $args = $matches[0]; foreach ($args as $k => $arg){ $sanitizedSnippet = str_replace($arg, 'apply_filters("wp_all_export_post_process_xml", '. $arg .')' ,$sanitizedSnippet); } } // Clean empty strings $sanitizedSnippet = str_replace(array(', ,',',,'), ',"",', $sanitizedSnippet); $snippetValue = eval('return ' . $sanitizedSnippet . ';'); $snippetValue = $this->encodeSpecialCharacters($snippetValue); if(strpos($snippet, 'explode') !== false && $isInFunction) { $snippetValue = 'array('."'" . implode("','", $snippetValue) . "'".')'; } return $snippetValue; } public function wp_all_export_post_process_xml($value){ return $this->postProcessXml($this->decodeSpecialCharacters(str_replace('"','', $value))); } public function getNodeAttributes(DOMNode $dom) { $result = ""; if ($dom->hasAttributes()) { for ($i = 0; $i < $dom->attributes->length; $i++) $result .= $dom->attributes->item($i)->nodeValue; } return $result; } /** * @param DOMNode $newValueNode * @param $snippet * @param $snippetValue * @internal param $snippetValues */ private function replaceSnippetInAttributes(DOMNode $newValueNode, $snippet, $snippetValue) { $snippetValue = $this->sanitizeAttribute($snippetValue); if ($newValueNode->hasAttributes()) { for ($i = 0; $i < $newValueNode->attributes->length; $i++) { $newValueNode->attributes->item($i)->nodeValue = str_replace( $snippet, $snippetValue, $newValueNode->attributes->item($i)->nodeValue ); } } } /** * @param DOMNode $element */ private function elementCdata(DOMNode $element) { $hasSnippets = $this->parseSnippetsInString($element->nodeValue); if (strpos($element->nodeValue, 'nodeValue, 'CDATABEGIN') === false) { $element->nodeValue = $this->maybe_cdata($element->nodeValue, $hasSnippets); } } private function encodeSpecialCharacters($text) { $text = str_replace('&', '&', $text); $text = str_replace('&', '##amp##', $text); $text = str_replace("'", '##x27##', $text); $text = str_replace('"', '##quot##', $text); $text = str_replace('<', '##lt##', $text); $text = str_replace('>', '##gt##', $text); return $text; } private function decodeSpecialCharacters($text) { $text = str_replace('##amp##', '&', $text); $text = str_replace('##x27##', "'", $text); $text = str_replace('##quot##', '"', $text); $text = str_replace('##lt##', '<', $text); $text = str_replace('##gt##', '>', $text); return $text; } /** * @param $xml * @return mixed */ private function postProcessXml($xml) { $xml = str_replace('CDATABEGIN', '', $xml); $xml = str_replace('CLOSEBRAKET', ']', str_replace('OPENBRAKET', '[', $xml)); $xml = str_replace('CLOSECURVE', '}', str_replace('OPENCURVE', '{', $xml)); $xml = str_replace('CLOSECIRCLE', ')', str_replace('OPENCIRCLE', '(', $xml)); $xml = str_replace('**SINGLEQUOT**', "'", $xml); $xml = str_replace('**DOUBLEQUOT**', "\"", $xml); $xml = str_replace('**GT**', ">", $xml); $xml = str_replace('**LT**', "<", $xml); $xml = str_replace('##FILLER##', '', $xml); $xml = str_replace('c', '', $xml); $xml = str_replace('', '', $xml); $xml = str_replace('CDATABEGINcCDATACLOSE', '', $xml); $xml = str_replace('', '', $xml); $xml = str_replace(self::SNIPPET_DELIMITER, '', $xml); $xml = trim($xml); return $xml; } /** * @param $xml * @return mixed */ private function preprocessXml($xml) { $xml = str_replace('', '', $xml); $xml = str_replace("\"{}\"", '""', $xml); $xml = str_replace("{}", '""', $xml); $xml = str_replace(">\"\"<", '><', $xml); $xml = str_replace("[implode(',',{})]", "", $xml); return $xml; } /** * @param $xml * @param $response * @return mixed */ private function cleanResponse($response) { $response = str_replace('', '', $response); $response = str_replace('', '', $response); $response = str_replace('', '', $response); $xml = str_replace("", '', $response); $xml = str_replace("", "", $xml); return trim($xml); } /** * * Cloning DOMNode with including child DOMNode elements * * @param $node * @return \DOMNode* */ private function cloneNode(DOMNode $node, $snippet, $snippetValues){ $Document = new DOMDocument('1.0', 'UTF-8'); $Document->preserveWhiteSpace = false; $Document->formatOutput = true; $newElement = $Document->importNode($node, true); $this->replaceSnippetInAttributes($newElement, $snippet, $snippetValues); foreach ($newElement->childNodes as $child){ if ($child->nodeType === XML_TEXT_NODE) { $this->elementCdata($child); } } $Document->appendChild($newElement); return trim(str_replace("","",$Document->saveXML())); } }