PageRenderTime 58ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/wp-all-import-pro/libraries/XmlImportTemplateParser.php

https://gitlab.com/najomie/fit-hippie
PHP | 444 lines | 320 code | 31 blank | 93 comment | 85 complexity | 951c37b2fd8529f711336a0dee9ec3db MD5 | raw file
  1. <?php
  2. /**
  3. * @author Olexandr Zanichkovsky <olexandr.zanichkovsky@zophiatech.com>
  4. * @package General
  5. */
  6. require_once dirname(__FILE__) . '/ast/XmlImportAstSequence.php';
  7. require_once dirname(__FILE__) . '/ast/XmlImportAstPrint.php';
  8. require_once dirname(__FILE__) . '/ast/XmlImportAstText.php';
  9. require_once dirname(__FILE__) . '/ast/XmlImportAstWith.php';
  10. require_once dirname(__FILE__) . '/ast/XmlImportAstForeach.php';
  11. require_once dirname(__FILE__) . '/ast/XmlImportAstIf.php';
  12. require_once dirname(__FILE__) . '/ast/XmlImportAstMath.php';
  13. require_once dirname(__FILE__) . '/ast/XmlImportAstSpintax.php';
  14. require_once dirname(__FILE__) . '/ast/XmlImportAstXPath.php';
  15. require_once dirname(__FILE__) . '/ast/XmlImportAstString.php';
  16. require_once dirname(__FILE__) . '/ast/XmlImportAstInteger.php';
  17. require_once dirname(__FILE__) . '/ast/XmlImportAstFloat.php';
  18. require_once dirname(__FILE__) . '/ast/XmlImportAstFunction.php';
  19. /**
  20. * Parses a list of nodes into AST (Abstract Syntax Tree)
  21. */
  22. class XmlImportTemplateParser
  23. {
  24. /**
  25. * List of tokens
  26. *
  27. * @var array
  28. */
  29. private $tokens;
  30. /**
  31. * Current index
  32. *
  33. * @var int
  34. */
  35. private $index = -1;
  36. /**
  37. * Stack that stores possible block endings
  38. *
  39. * @var array
  40. */
  41. private $clauseStack = array();
  42. /**
  43. * Stack of sequences
  44. *
  45. * @var array
  46. */
  47. private $sequenceStack = array();
  48. /**
  49. * Whether else subclause is allowed
  50. *
  51. * @var bool
  52. */
  53. private $elseAllowed = false;
  54. /**
  55. * Creates new instance
  56. *
  57. * @param array $tokens
  58. */
  59. public function __construct(array $tokens)
  60. {
  61. $this->tokens = $tokens;
  62. }
  63. /**
  64. * Parses the list of tokens into AST tree
  65. *
  66. * @return XmlImportAstSequence
  67. */
  68. public function parse()
  69. {
  70. $result = $this->parseSequence();
  71. if (count($this->clauseStack) > 0)
  72. throw new XmlImportException("Unexpected end of template.");
  73. return $result;
  74. }
  75. /**
  76. * Parses sequence
  77. *
  78. * @return XmlImportAstSequence
  79. */
  80. private function parseSequence()
  81. {
  82. if (($this->index + 1) == count($this->tokens))
  83. throw new XmlImportException("Reached end of template but statement sequence expected");
  84. $sequence = new XmlImportAstSequence();
  85. array_push($this->sequenceStack, $sequence);
  86. if (count($this->clauseStack) == 0)
  87. {
  88. while (($this->index + 1) < count($this->tokens))
  89. {
  90. $sequence->addStatement($this->parseStatement());
  91. }
  92. }
  93. else
  94. {
  95. while (($this->index + 1) < count($this->tokens))
  96. {
  97. if ($this->tokens[$this->index + 1]->getKind() == $this->clauseStack[count($this->clauseStack) - 1])
  98. {
  99. $this->index++;
  100. array_pop($this->clauseStack);
  101. break;
  102. }
  103. $statement = $this->parseStatement();
  104. if (is_null($statement)){
  105. array_pop($this->sequenceStack);
  106. return $sequence;
  107. }
  108. $sequence->addStatement($statement);
  109. }
  110. }
  111. array_pop($this->sequenceStack);
  112. return $sequence;
  113. }
  114. /**
  115. * Parses statement
  116. *
  117. * @return XmlImportAstText
  118. */
  119. private function parseStatement()
  120. {
  121. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_TEXT)
  122. {
  123. return new XmlImportAstText($this->tokens[++$this->index]->getValue());
  124. }
  125. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_PRINT)
  126. {
  127. $this->index++;
  128. return new XmlImportAstPrint($this->parseExpression());
  129. }
  130. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_WITH)
  131. {
  132. return $this->parseWith();
  133. }
  134. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_FOREACH)
  135. {
  136. return $this->parseForeach();
  137. }
  138. elseif($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_MATH)
  139. {
  140. return new XmlImportAstPrint($this->parseExpression());
  141. }
  142. elseif($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_SPINTAX)
  143. {
  144. return new XmlImportAstPrint($this->parseExpression());
  145. }
  146. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_IF)
  147. {
  148. return $this->parseIf();
  149. }
  150. elseif($this->clauseStack[count($this->clauseStack) - 1] == XmlImportToken::KIND_ENDIF &&
  151. in_array($this->tokens[$this->index + 1]->getKind(), array(XmlImportToken::KIND_ELSE, XmlImportToken::KIND_ELSEIF)))
  152. {
  153. if ($this->elseAllowed)
  154. {
  155. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_ELSE)
  156. $this->elseAllowed = false;
  157. }
  158. else
  159. {
  160. throw new XmlImportException("ELSEIF or ELSE is not allowed again after ELSE");
  161. }
  162. return null;
  163. }
  164. else
  165. throw new XmlImportException ("Unexpected token {$this->tokens[$this->index + 1]->getKind()}, statement was expected.");
  166. }
  167. /**
  168. * Parses expression
  169. *
  170. * @return XmlImportAstXPath
  171. */
  172. private function parseExpression()
  173. {
  174. if ($this->index + 1 == count($this->tokens))
  175. throw new XmlImportException("Reached end of template but expression was expected");
  176. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_FUNCTION)
  177. {
  178. return $this->parseFunction();
  179. }
  180. elseif($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_MATH)
  181. {
  182. return $this->parseMath();
  183. }
  184. elseif($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_SPINTAX)
  185. {
  186. return $this->parseSpintax();
  187. }
  188. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_XPATH)
  189. {
  190. $xpath = new XmlImportAstXPath($this->tokens[++$this->index]->getValue());
  191. $this->sequenceStack[count($this->sequenceStack) - 1]->addVariable($xpath);
  192. return $xpath;
  193. }
  194. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_STRING || $this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_OPERATION)
  195. {
  196. return new XmlImportAstString($this->tokens[++$this->index]->getValue());
  197. }
  198. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_INT)
  199. {
  200. return new XmlImportAstInteger($this->tokens[++$this->index]->getValue());
  201. }
  202. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_FLOAT)
  203. {
  204. return new XmlImportAstFloat($this->tokens[++$this->index]->getValue());
  205. }
  206. else
  207. throw new XmlImportException("Unexpected token " . $this->tokens[$this->index + 1]->getKind());
  208. }
  209. /**
  210. * Parses function
  211. *
  212. * @return XmlImportAstFunction
  213. */
  214. private function parseFunction()
  215. {
  216. $function = new XmlImportAstFunction($this->tokens[++$this->index]->getValue());
  217. if ($this->tokens[$this->index + 1]->getKind() != XmlImportToken::KIND_OPEN)
  218. throw new XmlImportException ("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  219. $this->index++;
  220. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE)
  221. {
  222. $this->index++;
  223. return $function;
  224. }
  225. else
  226. {
  227. while ($this->index < count($this->tokens) - 2)
  228. {
  229. $function->addArgument($this->parseExpression());
  230. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE)
  231. {
  232. $this->index++;
  233. return $function;
  234. break;
  235. }
  236. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_COMMA)
  237. $this->index++;
  238. else
  239. throw new XmlImportException("Comma or closing brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  240. }
  241. throw new XmlImportException("Unexpected end of {$function->getName()} function argument list");
  242. }
  243. }
  244. /**
  245. * Parses function
  246. *
  247. * @return XmlImportAstFunction
  248. */
  249. private function parseMath()
  250. {
  251. $math = new XmlImportAstMath($this->tokens[++$this->index]->getValue());
  252. if ($this->tokens[$this->index + 1]->getKind() != XmlImportToken::KIND_OPEN)
  253. throw new XmlImportException ("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  254. $this->index++;
  255. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE)
  256. {
  257. $this->index++;
  258. return $math;
  259. }
  260. else
  261. {
  262. while ($this->index < count($this->tokens) - 2)
  263. {
  264. $math->addArgument($this->parseExpression());
  265. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE)
  266. {
  267. $this->index++;
  268. return $math;
  269. break;
  270. }
  271. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_COMMA)
  272. $this->index++;
  273. else
  274. throw new XmlImportException("Comma or closing brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  275. }
  276. throw new XmlImportException("Unexpected end of MATH argument list");
  277. }
  278. }
  279. /**
  280. * Parses function
  281. *
  282. * @return XmlImportSpintaxFunction
  283. */
  284. private function parseSpintax()
  285. {
  286. $spintax = new XmlImportAstSpintax($this->tokens[++$this->index]->getValue());
  287. if ($this->tokens[$this->index + 1]->getKind() != XmlImportToken::KIND_OPEN)
  288. throw new XmlImportException ("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  289. $this->index++;
  290. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE)
  291. {
  292. $this->index++;
  293. return $spintax;
  294. }
  295. else
  296. {
  297. while ($this->index < count($this->tokens) - 2)
  298. {
  299. $spintax->addArgument($this->parseExpression());
  300. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE)
  301. {
  302. $this->index++;
  303. return $spintax;
  304. break;
  305. }
  306. elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_COMMA)
  307. $this->index++;
  308. else
  309. throw new XmlImportException("Comma or closing brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  310. }
  311. throw new XmlImportException("Unexpected end of {$function->getName()} function argument list");
  312. }
  313. }
  314. /**
  315. * Parses clause that uses XPath and returns XPath
  316. *
  317. * @return XmlImportAstXPath
  318. */
  319. private function parseXPathDependant()
  320. {
  321. $this->index++;
  322. if ($this->index + 1 == count($this->tokens))
  323. throw new XmlImportException("Reached end of template but expression was expected");
  324. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_OPEN)
  325. $this->index++;
  326. else
  327. throw new XmlImportException("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  328. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_XPATH)
  329. {
  330. $xpath = new XmlImportAstXPath($this->tokens[++$this->index]->getValue());
  331. $this->sequenceStack[count($this->sequenceStack) - 1]->addVariable($xpath);
  332. }
  333. else
  334. throw new XmlImportException("XPath expression expected instead of " . $this->tokens[$this->index + 1]->getKind());
  335. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE)
  336. $this->index++;
  337. else
  338. throw new XmlImportException("Close brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  339. return $xpath;
  340. }
  341. /**
  342. * Parses WITH clause
  343. *
  344. * @return XmlImportAstWith
  345. */
  346. private function parseWith()
  347. {
  348. $xpath = $this->parseXPathDependant();
  349. //store sequence exit
  350. array_push($this->clauseStack, XmlImportToken::KIND_ENDWITH);
  351. return new XmlImportAstWith($xpath, $this->parseSequence());
  352. }
  353. /**
  354. * Parses FOREACH clause
  355. *
  356. * @return XmlImportAstForeach
  357. */
  358. private function parseForeach()
  359. {
  360. $xpath = $this->parseXPathDependant();
  361. array_push($this->clauseStack, XmlImportToken::KIND_ENDFOREACH);
  362. return new XmlImportAstForeach($xpath, $this->parseSequence());
  363. }
  364. /**
  365. * Parses IF clause
  366. *
  367. * @return XmlImportAstIf
  368. */
  369. private function parseIf()
  370. {
  371. $this->index++;
  372. $this->elseAllowed = true;
  373. array_push($this->clauseStack, XmlImportToken::KIND_ENDIF);
  374. if ($this->index + 1 == count($this->tokens))
  375. throw new XmlImportException("Reached end of template but expression was expected");
  376. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_OPEN)
  377. $this->index++;
  378. else
  379. throw new XmlImportException("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  380. $if = new XmlImportAstIf($this->parseExpression());
  381. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE)
  382. $this->index++;
  383. else
  384. throw new XmlImportException("Close brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  385. $if->addIfBody($this->parseSequence());
  386. if ($this->index + 1 != count($this->tokens))
  387. {
  388. while ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_ELSEIF)
  389. {
  390. $this->index++;
  391. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_OPEN)
  392. $this->index++;
  393. else
  394. throw new XmlImportException("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  395. $condition = $this->parseExpression();
  396. if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE)
  397. $this->index++;
  398. else
  399. throw new XmlImportException("Close brace expected instead of " . $this->tokens[$this->index + 1]->getKind());
  400. $elseif = new XmlImportAstElseif($condition, $this->parseSequence());
  401. $if->addElseif($elseif);
  402. if ($this->index + 1 == count($this->tokens))
  403. break;
  404. }
  405. if ($this->index + 1 < count($this->tokens) && $this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_ELSE)
  406. {
  407. $this->index++;
  408. $if->addElseBody($this->parseSequence());
  409. }
  410. }
  411. return $if;
  412. }
  413. }