/classes/template/parser.php

https://github.com/Yacodo/atoum · PHP · 190 lines · 150 code · 40 blank · 0 comment · 21 complexity · 43e1501ff68c368c2225f7717b2c8bb3 MD5 · raw file

  1. <?php
  2. namespace mageekguy\atoum\template;
  3. use
  4. mageekguy\atoum,
  5. mageekguy\atoum\exceptions,
  6. mageekguy\atoum\template\parser
  7. ;
  8. class parser implements atoum\adapter\aggregator
  9. {
  10. const eol = "\n";
  11. const defaultNamespace = 'tpl';
  12. protected $namespace = '';
  13. protected $adapter = null;
  14. protected $errorLine = null;
  15. protected $errorOffset = null;
  16. protected $errorMessage = null;
  17. public function __construct($namespace = null, atoum\adapter $adapter = null)
  18. {
  19. $this
  20. ->setNamespace($namespace ?: self::defaultNamespace)
  21. ->setAdapter($adapter ?: new atoum\adapter())
  22. ;
  23. }
  24. public function setNamespace($namespace)
  25. {
  26. $this->namespace = (string) $namespace;
  27. return $this;
  28. }
  29. public function getNamespace()
  30. {
  31. return $this->namespace;
  32. }
  33. public function setAdapter(atoum\adapter $adapter)
  34. {
  35. $this->adapter = $adapter;
  36. return $this;
  37. }
  38. public function getAdapter()
  39. {
  40. return $this->adapter;
  41. }
  42. public function checkString($string)
  43. {
  44. return $this->parse($string);
  45. }
  46. public function checkFile($path)
  47. {
  48. return $this->checkString($this->getFileContents($path));
  49. }
  50. public function parseString($string, atoum\template $root = null)
  51. {
  52. $this->parse((string) $string, $root);
  53. return $root;
  54. }
  55. public function parseFile($path, atoum\template $root = null)
  56. {
  57. $this->parse($this->getfileContents($path), $root);
  58. return $root;
  59. }
  60. protected function parse($string, & $root = null)
  61. {
  62. if ($root === null)
  63. {
  64. $root = new atoum\template();
  65. }
  66. $currentTag = $root;
  67. $stack = array();
  68. $line = 1;
  69. $offset = 1;
  70. while (preg_match('%<(/)?' . $this->namespace . ':([^\s/>]+)(?(1)\s*|((?:\s+\w+="[^"]*")*)\s*(/?))(>)%', $string, $tag, PREG_OFFSET_CAPTURE) == true)
  71. {
  72. if ($tag[0][1] != 0)
  73. {
  74. $data = substr($string, 0, $tag[0][1]);
  75. $lastEol = strrpos($data, self::eol);
  76. if ($lastEol === false)
  77. {
  78. $offset += strlen($data);
  79. }
  80. else
  81. {
  82. $line += substr_count($data, self::eol);
  83. $offset = strlen(substr($data, $lastEol));
  84. }
  85. $currentTag->addChild(new data($data));
  86. }
  87. $string = substr($string, $tag[5][1] + 1);
  88. if ($tag[1][0] == '') # < /> or < > tag
  89. {
  90. $child = new tag($tag[2][0], null, $line, $offset);
  91. $currentTag->addChild($child);
  92. if (preg_match_all('%(\w+)="([^"]*)"%', $tag[3][0], $attributes) == true)
  93. {
  94. foreach ($attributes[1] as $index => $attribute)
  95. {
  96. try
  97. {
  98. $child->setAttribute($attribute, $attributes[2][$index]);
  99. }
  100. catch (\exception $exception)
  101. {
  102. throw new parser\exception($exception->getMessage(), $line, $offset, $exception);
  103. }
  104. }
  105. }
  106. if ($tag[4][0] == '') # < >
  107. {
  108. $stack[] = $child;
  109. $currentTag = $child;
  110. }
  111. }
  112. else # </ >
  113. {
  114. $stackedTemplateTag = array_pop($stack);
  115. if ($stackedTemplateTag === null || $stackedTemplateTag->getTag() != $tag[2][0])
  116. {
  117. throw new parser\exception('Tag \'' . $tag[2][0] . '\' is not open', $line, $offset);
  118. }
  119. else
  120. {
  121. $currentTag = end($stack);
  122. if ($currentTag === false)
  123. {
  124. $currentTag = $root;
  125. }
  126. }
  127. }
  128. $offset += ($tag[5][1] - $tag[0][1]) + 1;
  129. }
  130. if ($string != '')
  131. {
  132. $currentTag->addChild(new data($string));
  133. }
  134. if (sizeof($stack) > 0)
  135. {
  136. throw new parser\exception('Tag \'' . $currentTag->getTag() . '\' must be closed', $line, $offset + strlen($string));
  137. }
  138. return $this;
  139. }
  140. protected function getFileContents($path)
  141. {
  142. $fileContents = $this->adapter->file_get_contents($path);
  143. if ($fileContents === false)
  144. {
  145. throw new exceptions\runtime('Unable to get contents from file \'' . $path . '\'');
  146. }
  147. return $fileContents;
  148. }
  149. }
  150. ?>