/wp-content/plugins/wp-all-import-pro/libraries/XmlImportTemplateScanner.php
https://gitlab.com/najomie/fit-hippie · PHP · 442 lines · 367 code · 15 blank · 60 comment · 43 complexity · 9d2f23e404ed0f0ebc5abeca257a7441 MD5 · raw file
- <?php
- /**
- * @author Olexandr Zanichkovsky <olexandr.zanichkovsky@zophiatech.com>
- * @package General
- */
- require_once dirname(__FILE__) . '/XmlImportToken.php';
- require_once dirname(__FILE__) . '/XmlImportException.php';
- /**
- * Used to scan string into a list of tokens
- */
- final class XmlImportTemplateScanner
- {
- /**
- * Language keywords
- *
- * @var array
- */
- private $keywords = array(
- 'IF',
- 'ELSEIF',
- 'ELSE',
- 'ENDIF',
- 'FOREACH',
- 'ENDFOREACH',
- 'WITH',
- 'ENDWITH',
- 'MATH',
- 'SPINTAX'
- );
- /**
- * Parsing text
- */
- const STATE_TEXT = 'STATE_TEXT';
- /**
- * Parsing XPath
- */
- const STATE_XPATH = 'STATE_XPATH';
- /**
- * Parsing Language
- */
- const STATE_LANG = 'STATE_LANG';
- /**
- * Whether it is lang block start
- *
- * @var bool
- */
- private $isLangBegin = false;
- private $previous_ch = false;
- /**
- * Current parsing state
- *
- * @var string
- */
- private $currentState = XmlImportTemplateScanner::STATE_TEXT;
- /**
- * Scans template from XmlImportReaderInterface and returns the list of tokens
- *
- * @param XmlImportReaderInterface $input
- * @return array
- */
- public function scan(XmlImportReaderInterface $input)
- {
- $results = array();
-
- while (($ch = $input->peek()) !== false)
- {
- switch ($this->currentState)
- {
- case XmlImportTemplateScanner::STATE_TEXT:
- if ($ch == '[')
- {
- $this->previous_ch = '[';
- $this->currentState = XmlImportTemplateScanner::STATE_LANG;
- $this->isLangBegin = true;
- //omit [
- $input->read();
- }
- elseif ($ch == '{')
- {
- $this->currentState = XmlImportTemplateScanner::STATE_XPATH;
- //omit {
- $input->read();
- }
- else
- {
- $results[] = $this->scanText($input);
- }
- break;
- case XmlImportTemplateScanner::STATE_XPATH:
- $results = array_merge($results, $this->scanXPath($input, false));
- break;
- case XmlImportTemplateScanner::STATE_LANG:
- $ch = $input->peek();
-
- if (preg_match('/\s/', $ch))
- {
- //omit space
- $input->read();
- }
- elseif (preg_match('/[_a-z]/i', $ch))
- {
- $result = $this->scanName($input);
- if (is_array($result))
- $results = array_merge($results, $result);
- else
- $results[] = $result;
- }
- elseif (preg_match('/(\d)/', $ch))
- {
- $result = $this->scanNumber($input);
- if (is_array($result))
- $results = array_merge($results, $result);
- else
- $results[] = $result;
- }
- elseif ($ch == '"')
- {
- //omit "
- $input->read();
- $result = $this->scanString($input);
- if (is_array($result))
- $results = array_merge($results, $result);
- else
- $results[] = $result;
- }
- elseif ($ch == '{')
- {
- $input->read();
- $result = $this->scanXPath($input);
- if (is_array($result))
- $results = array_merge($results, $result);
- else
- $results[] = $result;
- }
- elseif ($ch == '(')
- {
- $this->isLangBegin = false;
- $input->read();
- $results[] = new XmlImportToken(XmlImportToken::KIND_OPEN);
- }
- elseif ($ch == ')')
- {
- $this->isLangBegin = false;
- $input->read();
- $results[] = new XmlImportToken(XmlImportToken::KIND_CLOSE);
- }
- elseif ($ch == ',')
- {
- $this->isLangBegin = false;
- $input->read();
- $results[] = new XmlImportToken(XmlImportToken::KIND_COMMA);
- }
- elseif ($ch == "]")
- {
- $this->isLangBegin = false;
- $this->currentState = XmlImportTemplateScanner::STATE_TEXT;
- //omit ]
- $input->read();
- }
- elseif ($ch == "|" || $ch == "+" || $ch == "-" || $ch == "*" || $ch == "/")
- {
- $results[] = $this->scanText($input);
- }
- else{
- if ($ch == "'"){
- throw new XmlImportException("Unexpected symbol ' - When using shortcodes/PHP functions, use double quotes \", not single quotes '");
- }
- else{
- throw new XmlImportException("Unexpected symbol '$ch'");
- }
- }
-
- break;
- }
- }
-
- return $results;
- }
- /**
- * Scans text
- *
- * @param XmlImportReaderInterface $input
- * @return XmlImportToken
- */
- private function scanText($input)
- {
- $accum = $input->read();
- while (($ch = $input->peek()) !== false)
- {
- if ($ch == '{' && $accum[strlen($accum) - 1] != "\\")
- {
- $this->currentState = XmlImportTemplateScanner::STATE_XPATH;
- //omit {
- $input->read();
- break;
- }
- elseif ($ch == '[' && $accum[strlen($accum) - 1] != "\\")
- {
-
- $this->currentState = XmlImportTemplateScanner::STATE_LANG;
- $this->isLangBegin = true;
- //omit [
- $input->read();
- break;
- }
- elseif ($accum == '/' && $this->previous_ch == "["){
- $accum = "[" . $accum . $input->read();
- //$this->previous_ch = false;
- }
- else
- $accum .= $input->read();
- $this->previous_ch = $ch;
- }
- $accum = str_replace(array("\\[", "\\{"), array('[', '{'), $accum);
- return new XmlImportToken(XmlImportToken::KIND_TEXT, $accum);
- }
- /**
- * Scans XPath
- *
- * @param XmlImportReaderInterface $input
- * @param bool $insideLang
- * @return XmlImportToken
- */
- private function scanXPath($input, $insideLang = true)
- {
- $accum = '';
- while(($ch = $input->peek()) !== false)
- {
- if ($ch == '}' && (strlen($accum) == 0 || $accum[strlen($accum) - 1] != "\\"))
- {
- //skip }
- $input->read();
- $accum = str_replace("\\}", '}', $accum);
- if ($insideLang)
- {
- if ($this->isLangBegin)
- {
- return array(new XmlImportToken(XmlImportToken::KIND_PRINT), new XmlImportToken(XmlImportToken::KIND_XPATH, $accum));
- }
- else
- return new XmlImportToken(XmlImportToken::KIND_XPATH, $accum);
- }
- else
- {
- $this->currentState = XmlImportTemplateScanner::STATE_TEXT;
- return array(new XmlImportToken(XmlImportToken::KIND_PRINT), new XmlImportToken(XmlImportToken::KIND_XPATH, $accum));
- }
- }
- else
- $accum .= $input->read();
- }
- throw new XmlImportException('Unexpected end of XPath expression \'' . $accum . '\'');
- }
- /**
- * Scans name
- *
- * @param XmlImportReaderInterface $input
- * @return XmlImportToken
- */
- private function scanName(XmlImportReaderInterface $input)
- {
- $accum = $input->read();
- $is_function = false;
- while (preg_match('%[/_a-z0-9=\s\-"]%i', $input->peek(), $matches))
- {
- $accum .= $input->read();
- if ($input->peek() === false)
- throw new XmlImportException("Unexpected end of function or keyword name \"$accum\"");
- }
- $ch = $input->peek();
- if ($ch == "(") $is_function = true;
-
- if (in_array(strtoupper(trim($accum)), $this->keywords))
- {
- return new XmlImportToken(strtoupper($accum));
- }
- else
- {
-
- if (strpos($accum, "=") !== false or (shortcode_exists($accum) and !$is_function) or ! $is_function) {
- $this->isLangBegin = false;
- return new XmlImportToken(XmlImportToken::KIND_TEXT, '[' . trim(trim($accum, "["), "]") . ']');
-
- }
- if ($this->isLangBegin)
- {
- $this->isLangBegin = false;
- if ( function_exists($accum) or in_array($accum, array('array')))
- return array(new XmlImportToken(XmlImportToken::KIND_PRINT), new XmlImportToken(XmlImportToken::KIND_FUNCTION, $accum));
- else
- throw new XmlImportException("Call to undefined function \"$accum\"");
-
- }
- else{
- if ( function_exists($accum) or in_array($accum, array('array')))
- return new XmlImportToken(XmlImportToken::KIND_FUNCTION, $accum);
- else
- throw new XmlImportException("Call to undefined function \"$accum\"");
- }
- }
- }
- /**
- * Scans string literal
- *
- * @param XmlImportReaderInterface $input
- * @return XmlImportToken
- */
- private function scanString(XmlImportReaderInterface $input)
- {
- $accum = '';
- while(($ch = $input->peek()) !== false)
- {
- if ($ch == '"' && (strlen($accum) == 0 || $accum[strlen($accum) - 1] != "\\"))
- {
- //skip "
- $input->read();
- $accum = str_replace("\\\"", '"', $accum);
- if ($this->isLangBegin)
- {
- $this->isLangBegin = false;
- return array(new XmlImportToken(XmlImportToken::KIND_PRINT), new XmlImportToken(XmlImportToken::KIND_STRING, $accum));
- }
- else
- {
- return new XmlImportToken(XmlImportToken::KIND_STRING, $accum);
- }
- }
- else
- $accum .= $input->read();
- }
- throw new XmlImportException('Unexpected end of string literal "' . $accum . '"');
- }
- /**
- * Scans number
- *
- * @param XmlImportReaderInterface $input
- * @return XmlImportToken
- */
- private function scanNumber(XmlImportReaderInterface $input)
- {
- $isInt = true;
- $accum = $this->scanInt($input);
- if ($input->peek() == '.')
- {
- $isInt = false;
- $accum .= $input->read();
- $accum .= $this->scanNumberFrac($input);
- }
- if (strtolower($input->peek()) == 'e' )
- {
- $isInt = false;
- $accum .= $input->read();
- $accum .= $this->scanInt($input);
- }
- if ($isInt)
- {
- if ($this->isLangBegin)
- {
- $this->isLangBegin = false;
- return array(new XmlImportToken(XmlImportToken::KIND_PRINT), new XmlImportToken(XmlImportToken::KIND_INT, intval($accum)));
- }
- else
- {
- return new XmlImportToken(XmlImportToken::KIND_INT, intval($accum));
- }
- }
- else
- {
- if ($this->isLangBegin)
- {
- $this->isLangBegin = false;
- return array(new XmlImportToken(XmlImportToken::KIND_PRINT), new XmlImportToken(XmlImportToken::KIND_FLOAT, floatval($accum)));
- }
- else
- {
- return new XmlImportToken(XmlImportToken::KIND_FLOAT, floatval($accum));
- }
- }
- }
- /**
- * Scans integer number
- *
- * @param XmlImportReaderInterface $input
- * @return string
- */
- private function scanInt(XmlImportReaderInterface $input)
- {
- if (preg_match('/(\d)/', $input->peek()))
- {
- $accum = $input->read();
- if ($accum == '-' && !preg_match('/\d/', $input->peek()))
- throw new XmlImportException("Expected digit after a minus");
- while (preg_match('/\d/', $input->peek()))
- {
- $accum .= $input->read();
- }
- return $accum;
- }
- else
- throw new XmlImportException("digit or '-' expected in a number");
- }
- /**
- * Scans fraction part of a number
- *
- * @param XmlImportReaderInterface $input
- * @return string
- */
- private function scanNumberFrac(XmlImportReaderInterface $input)
- {
- $accum = '';
- while (preg_match('/\d/', $input->peek()))
- {
- $accum .= $input->read();
- }
- if (strlen($accum) == 0)
- throw new XmlImportException("Digits are expected after a '.'");
- return $accum;
- }
- }