PageRenderTime 52ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/mod/wiki/parser/parser.php

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