/mod/wiki/parser/parser.php

https://bitbucket.org/kudutest1/moodlegit · PHP · 284 lines · 198 code · 57 blank · 29 comment · 46 complexity · b0a6eca98011241ee91f2c0e65fab2f3 MD5 · raw file

  1. <?php
  2. /**
  3. * Generic parser implementation
  4. *
  5. * @author Josep Arús
  6. *
  7. * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
  8. * @package wiki
  9. */
  10. class wiki_parser_proxy {
  11. private static $parsers = array();
  12. private static $basepath = "";
  13. public static function parse(&$string, $type, $options = array()) {
  14. if(empty(self::$basepath)) {
  15. global $CFG;
  16. self::$basepath = $CFG->dirroot.'/mod/wiki/parser/';
  17. }
  18. $type = strtolower($type);
  19. if(self::create_parser_instance($type)) {
  20. return self::$parsers[$type]->parse($string, $options);
  21. }
  22. else {
  23. return false;
  24. }
  25. }
  26. public static function get_token($name, $type) {
  27. if(self::create_parser_instance($type)) {
  28. return self::$parsers[$type]->get_token($name);
  29. }
  30. else {
  31. return false;
  32. }
  33. }
  34. public static function get_section(&$string, $type, $section, $all_content = false) {
  35. if(self::create_parser_instance($type)) {
  36. $content = self::$parsers[$type]->get_section($section, $string, true);
  37. if($all_content) {
  38. return $content;
  39. }
  40. else {
  41. return $content[1];
  42. }
  43. }
  44. else {
  45. return false;
  46. }
  47. }
  48. private static function create_parser_instance($type) {
  49. if(empty(self::$parsers[$type])) {
  50. if(include(self::$basepath."markups/$type.php")) {
  51. $class = strtolower($type)."_parser";
  52. if(class_exists($class)) {
  53. self::$parsers[$type] = new $class;
  54. return true;
  55. }
  56. else {
  57. return false;
  58. }
  59. }
  60. else {
  61. return false;
  62. }
  63. }
  64. else {
  65. return true;
  66. }
  67. }
  68. }
  69. require_once('utils.php');
  70. abstract class generic_parser {
  71. protected $string;
  72. protected $blockrules = array();
  73. protected $tagrules = array();
  74. private $rulestack = array();
  75. protected $parser_status = 'Before';
  76. /**
  77. * Dynamic return values
  78. */
  79. protected $returnvalues = array();
  80. private $nowikiindex = array();
  81. protected $nowikitoken = "%!";
  82. public function __construct() {}
  83. /**
  84. * Parse function
  85. */
  86. public function parse(&$string, $options = array()) {
  87. if(!is_string($string)) {
  88. return false;
  89. }
  90. $this->string =& $string;
  91. $this->set_options(is_array($options) ? $options : array());
  92. $this->initialize_nowiki_index();
  93. if(method_exists($this, 'before_parsing')) {
  94. $this->before_parsing();
  95. }
  96. $this->parser_status = 'Parsing';
  97. foreach($this->blockrules as $name => $block) {
  98. $this->process_block_rule($name, $block);
  99. }
  100. $this->commit_nowiki_index();
  101. $this->parser_status = 'After';
  102. if(method_exists($this, 'after_parsing')) {
  103. $this->after_parsing();
  104. }
  105. return array('parsed_text' => $this->string) + $this->returnvalues;
  106. }
  107. /**
  108. * Initialize options
  109. */
  110. protected function set_options($options) {}
  111. /**
  112. * Block processing function & callbacks
  113. */
  114. protected function process_block_rule($name, $block) {
  115. $this->rulestack[] = array('callback' => method_exists($this, $name."_block_rule") ? $name."_block_rule" : null, 'rule' => $block);
  116. $this->string = preg_replace_callback($block['expression'], array($this, 'block_callback'), $this->string);
  117. array_pop($this->rulestack);
  118. }
  119. private function block_callback($match) {
  120. $rule = end($this->rulestack);
  121. if(!empty($rule['callback'])) {
  122. $stuff = $this->{$rule['callback']}($match);
  123. }
  124. else {
  125. $stuff = $match[1];
  126. }
  127. if(is_array($stuff) && $rule['rule']['tag']) {
  128. $this->rules($stuff[0], $rule['rule']['tags']);
  129. $stuff = "\n".parser_utils::h($rule['rule']['tag'], $stuff[0], $stuff[1])."\n";
  130. }
  131. else {
  132. if(!isset($rule['rule']['tags'])) {
  133. $rule['rule']['tags'] = null;
  134. }
  135. $this->rules($stuff, $rule['rule']['tags']);
  136. if(isset($rule['rule']['tag']) && is_string($rule['rule']['tag'])) {
  137. $stuff = "\n".parser_utils::h($rule['rule']['tag'], $stuff)."\n";
  138. }
  139. }
  140. return $stuff;
  141. }
  142. /**
  143. * Rules processing function & callback
  144. */
  145. protected final function rules(&$text, $rules = null) {
  146. if($rules === null) {
  147. $rules = array('except' => array());
  148. }
  149. else if(is_array($rules) && count($rules) > 1) {
  150. $rules = array('only' => $rules);
  151. }
  152. if(isset($rules['only']) && is_array($rules['only'])) {
  153. $rules = $rules['only'];
  154. foreach($rules as $r) {
  155. if(!empty($this->tagrules[$r])) {
  156. $this->process_tag_rule($r, $this->tagrules[$r], $text);
  157. }
  158. }
  159. }
  160. else if(isset($rules['except']) && is_array($rules['except'])) {
  161. $rules = $rules['except'];
  162. foreach($this->tagrules as $r => $tr) {
  163. if(!in_array($r, $rules)) {
  164. $this->process_tag_rule($r, $tr, $text);
  165. }
  166. }
  167. }
  168. }
  169. private function process_tag_rule($name, $rule, &$text) {
  170. if(method_exists($this, $name."_tag_rule")) {
  171. $this->rulestack[] = array('callback' => $name."_tag_rule", 'rule' => $rule);
  172. $text = preg_replace_callback($rule['expression'], array($this, 'tag_callback'), $text);
  173. array_pop($this->rulestack);
  174. }
  175. else {
  176. if(isset($rule['simple'])) {
  177. $replace = "<{$rule['tag']} />";
  178. }
  179. else {
  180. $replace = parser_utils::h($rule['tag'], "$1");
  181. }
  182. $text = preg_replace($rule['expression'], $replace, $text);
  183. }
  184. }
  185. private function tag_callback($match) {
  186. $rule = end($this->rulestack);
  187. $stuff = $this->{$rule['callback']}($match);
  188. if(is_array($stuff)) {
  189. return parser_utils::h($rule['rule']['tag'], $stuff[0], $stuff[1]);
  190. }
  191. else {
  192. return $stuff;
  193. }
  194. }
  195. /**
  196. * Special nowiki parser index
  197. */
  198. private function initialize_nowiki_index() {
  199. $token = "\Q".$this->nowikitoken."\E";
  200. $this->string = preg_replace_callback("/".$token."\d+".$token."/", array($this, "initialize_nowiki_index_callback"), $this->string);
  201. }
  202. private function initialize_nowiki_index_callback($match) {
  203. return $this->protect($match[0]);
  204. }
  205. protected function protect($text) {
  206. $this->nowikiindex[] = $text;
  207. return $this->nowikitoken.(count($this->nowikiindex)-1).$this->nowikitoken;
  208. }
  209. private function commit_nowiki_index() {
  210. $token = "\Q".$this->nowikitoken."\E";
  211. $this->string = preg_replace_callback("/".$token."(\d+)".$token."/", array($this, "commit_nowiki_index_callback"), $this->string);
  212. }
  213. private function commit_nowiki_index_callback($match) {
  214. return $this->nowikiindex[intval($match[1])];
  215. }
  216. /**
  217. * Get token of the parsable element $name.
  218. */
  219. public function get_token($name) {
  220. foreach(array_merge($this->blockrules, $this->tagrules) as $n => $v) {
  221. if($name == $n && isset($v['token'])) {
  222. return $v['token'] ? $v['token'] : false;
  223. }
  224. }
  225. return false;
  226. }
  227. }