PageRenderTime 60ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Haml/Parser.php

https://github.com/humansky/qframe
PHP | 219 lines | 83 code | 25 blank | 111 comment | 20 complexity | 83d5683e3a13a6c389e07f0e4ac83409 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-3.0
  1. <?php
  2. /**
  3. * This file is part of the CSI SIG.
  4. *
  5. * The CSI SIG is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * The CSI SIG is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. * @category Haml
  19. * @package Haml
  20. * @copyright Copyright (c) 2007 Collaborative Software Initiative (CSI)
  21. * @license http://www.gnu.org/licenses/ GNU General Public License v3
  22. */
  23. /** Haml_Line */
  24. require_once 'Haml/Line.php';
  25. /** Haml_Element */
  26. require_once 'Haml/Element.php';
  27. /** Haml_Prolog */
  28. require_once 'Haml/Prolog.php';
  29. /**
  30. * @category Haml
  31. * @package Haml
  32. * @copyright Copyright (c) 2007 Collaborative Software Initiative (CSI)
  33. * @license http://www.gnu.org/licenses/ GNU General Public License v3
  34. */
  35. class Haml_Parser {
  36. /**
  37. * String representing one indentation level
  38. * @var string
  39. */
  40. private $indent_string = NULL;
  41. /**
  42. * Current indent level
  43. * @var integer
  44. */
  45. private $indent_level = 0;
  46. /**
  47. * Original template (in Haml form)
  48. * @var string
  49. */
  50. private $template = NULL;
  51. /**
  52. * Character set to use
  53. * @var string
  54. */
  55. private $charset = 'UTF-8';
  56. /**
  57. * Stack of unclosed tags (push on > indent, pop on < indent)
  58. * @var array
  59. */
  60. private $tag_stack = array();
  61. /**
  62. * Number of blank lines since the last non-blank line
  63. * @var integer
  64. */
  65. private $blank_lines = 0;
  66. /**
  67. * If not null, the indent level at which output is no longer hidden
  68. * @var integer
  69. */
  70. private $hide_until = null;
  71. /**
  72. * Class constructor
  73. *
  74. * Simply accepts a template and assigns it to the appropriate member
  75. *
  76. * @param string Haml template to be parsed
  77. * @param string (optional) character set to use (defaults to UTF-8)
  78. */
  79. function __construct($template, $charset = 'UTF-8') {
  80. $this->template = $template;
  81. $this->charset = $charset;
  82. }
  83. /**
  84. * Renders a template
  85. *
  86. * Renders a template by breaking the template up by newline and using {@link render_line}
  87. * to render each line. Does scan ahead to the next line (where a next line exists) so it
  88. * can tell render_line whether this line has children.
  89. *
  90. * @return string
  91. */
  92. function render() {
  93. $output = '';
  94. $lines = explode("\n", $this->template);
  95. foreach($lines as $index => $line) {
  96. $next_level = ($index < count($lines) - 1) ? $this->get_indent_level($lines[$index + 1]) : 0;
  97. $rendered = $this->render_line($line, $next_level);
  98. if(!is_null($rendered)) $output .= $rendered . "\n";
  99. }
  100. for($i = count($this->tag_stack) - 1; $i >= 0; $i--) {
  101. $tag = array_pop($this->tag_stack);
  102. $output .= str_repeat($this->indent_string, $i) . $tag->end() . "\n";
  103. }
  104. /*
  105. * Remove closing ?> and opening <?php when they are adjoined by only whitespace
  106. */
  107. $output = preg_replace('/\?>\s*<\?php/', '', $output);
  108. return $output;
  109. }
  110. /**
  111. * (PRIVATE) Render a line (sans white space)
  112. *
  113. * Processes white space and renders a single line using {@link Line::parse}. Pushes and pops
  114. * to/from the tag stack where necessary.
  115. *
  116. * @param string the line (in Haml) to be rendered
  117. * @param integer the level of the next line
  118. * @return string
  119. * @see Haml_Line
  120. */
  121. private function render_line($line, $next_level) {
  122. $current_indent = $this->get_indent_level($line);
  123. if($this->hide_until !== null && $current_indent > $this->hide_until) return null;
  124. elseif($this->hide_until !== null) $this->hide_until = null;
  125. if(trim($line) == '') {
  126. $this->blank_lines++;
  127. return null;
  128. }
  129. $content = $this->indent($line) . str_repeat($this->indent_string, $this->indent_level);
  130. $parsed = Haml_Line::parse($line, $current_indent < $next_level, $this->charset);
  131. if($parsed instanceof Haml_Element) {
  132. if($parsed->hide_content()) {
  133. $this->hide_until = $current_indent;
  134. return null;
  135. }
  136. $content .= $parsed->start();
  137. if(!$parsed->is_closed()) array_push($this->tag_stack, $parsed);
  138. }
  139. else if($parsed instanceof Haml_Prolog) $content .= $parsed->content();
  140. else $content .= $parsed;
  141. return $content;
  142. }
  143. /**
  144. * (PRIVATE) Determine the indentation for the given line using
  145. *
  146. * Also, if this is the first indented line, sets the indention string for the template
  147. * and removes leading whitespace from the line.
  148. *
  149. * @param string line to process
  150. * @return string
  151. * @see #get_indent_level
  152. */
  153. private function indent(&$line) {
  154. $content = '';
  155. $current_indent_level = $this->get_indent_level($line);
  156. if($current_indent_level < $this->indent_level) {
  157. for($i = count($this->tag_stack) - 1; $i >= $current_indent_level; $i--) {
  158. $tag = array_pop($this->tag_stack);
  159. $content .= str_repeat($this->indent_string, $i) . $tag->end() . "\n";
  160. }
  161. }
  162. for($i = 0; $i < $this->blank_lines; $i++) $content .= "\n";
  163. $this->blank_lines = 0;
  164. $this->indent_level = $current_indent_level;
  165. $line = trim($line);
  166. return $content;
  167. }
  168. /**
  169. * (PRIVATE) Fetches the indent level for a given line
  170. *
  171. * @param string line to process
  172. * @return integer
  173. */
  174. private function get_indent_level($line) {
  175. if(is_null($this->indent_string) && preg_match('/^\s+/', $line, $matches)) {
  176. $this->indent_string = $matches[0];
  177. return 1;
  178. }
  179. else if(!is_null($this->indent_string)) {
  180. $whitespace = self::whitespace($line);
  181. return preg_match_all('/' . $this->indent_string . '/', $whitespace, $matches);
  182. }
  183. else return 0;
  184. }
  185. /**
  186. * (PRIVATE) Returns leading whitespace for a given string
  187. *
  188. * @param string string to process
  189. * @return string
  190. */
  191. private static function whitespace($str) {
  192. return (preg_match('/^\s+/', $str, $matches)) ? $matches[0] : '';
  193. }
  194. }