MDL-67438 wiki: Formating changes (file doc block + whitespace fixes)
[moodle.git] / mod / wiki / parser / parser.php
CommitLineData
00710f4c 1<?php
a5e3c4e5
EL
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Library of functions and constants for module wiki
19 *
20 * It contains the great majority of functions defined by Moodle
21 * that are mandatory to develop a module.
22 *
23 * @package mod_wiki
24 * @copyright 2009 Marc Alier, Jordi Piguillem marc.alier@upc.edu
25 * @copyright 2009 Universitat Politecnica de Catalunya http://www.upc.edu
26 *
27 * @author Jordi Piguillem
28 * @author Marc Alier
29 * @author David Jimenez
30 * @author Josep Arus
31 * @author Kenneth Riba
32 *
33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34 */
35
36defined('MOODLE_INTERNAL') || die();
00710f4c
DC
37
38/**
39 * Generic parser implementation
40 *
41 * @author Josep ArĂºs
42 *
43 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
593b8385 44 * @package mod_wiki
00710f4c 45 */
00710f4c
DC
46class wiki_parser_proxy {
47 private static $parsers = array();
48 private static $basepath = "";
49
50 public static function parse(&$string, $type, $options = array()) {
51
a5e3c4e5 52 if (empty(self::$basepath)) {
00710f4c 53 global $CFG;
a5e3c4e5 54 self::$basepath = $CFG->dirroot . '/mod/wiki/parser/';
00710f4c
DC
55 }
56
57 $type = strtolower($type);
20dff4b9 58 self::$parsers[$type] = null; // Reset the current parser because it may have other options.
a5e3c4e5 59 if (self::create_parser_instance($type)) {
00710f4c 60 return self::$parsers[$type]->parse($string, $options);
a5e3c4e5 61 } else {
00710f4c
DC
62 return false;
63 }
64 }
65
66 public static function get_token($name, $type) {
a5e3c4e5 67 if (self::create_parser_instance($type)) {
00710f4c 68 return self::$parsers[$type]->get_token($name);
a5e3c4e5 69 } else {
00710f4c
DC
70 return false;
71 }
72 }
e3bc4eb7 73
a5e3c4e5
EL
74 public static function get_section(&$string, $type, $section, $allcontent = false) {
75 if (self::create_parser_instance($type)) {
00710f4c 76 $content = self::$parsers[$type]->get_section($section, $string, true);
e3bc4eb7 77
a5e3c4e5 78 if ($allcontent) {
e3bc4eb7 79 return $content;
a5e3c4e5 80 } else {
e3bc4eb7
AN
81 return is_array($content) ? $content[1] : null;
82 }
a5e3c4e5 83 } else {
00710f4c
DC
84 return false;
85 }
86 }
87
88 private static function create_parser_instance($type) {
a5e3c4e5
EL
89 if (empty(self::$parsers[$type])) {
90 include_once(self::$basepath . "markups/$type.php");
91 $class = strtolower($type) . "_parser";
92 if (class_exists($class)) {
20dff4b9
MG
93 self::$parsers[$type] = new $class;
94 return true;
a5e3c4e5 95 } else {
00710f4c
DC
96 return false;
97 }
a5e3c4e5 98 } else {
00710f4c
DC
99 return true;
100 }
101 }
102}
103
104require_once('utils.php');
105
106abstract class generic_parser {
107 protected $string;
108
109 protected $blockrules = array();
110 protected $tagrules = array();
111
112 private $rulestack = array();
113
a5e3c4e5 114 protected $parserstatus = 'Before';
e3bc4eb7 115
00710f4c
DC
116 /**
117 * Dynamic return values
118 */
e3bc4eb7 119
00710f4c
DC
120 protected $returnvalues = array();
121
122 private $nowikiindex = array();
123
124 protected $nowikitoken = "%!";
e3bc4eb7 125
a5e3c4e5
EL
126 public function __construct() {
127 }
00710f4c
DC
128
129 /**
130 * Parse function
131 */
00710f4c 132 public function parse(&$string, $options = array()) {
a5e3c4e5 133 if (!is_string($string)) {
00710f4c
DC
134 return false;
135 }
136
137 $this->string =& $string;
138
139 $this->set_options(is_array($options) ? $options : array());
140
141 $this->initialize_nowiki_index();
142
a5e3c4e5 143 if (method_exists($this, 'before_parsing')) {
00710f4c
DC
144 $this->before_parsing();
145 }
e3bc4eb7 146
a5e3c4e5 147 $this->parserstatus = 'Parsing';
00710f4c 148
a5e3c4e5 149 foreach ($this->blockrules as $name => $block) {
00710f4c
DC
150 $this->process_block_rule($name, $block);
151 }
152
153 $this->commit_nowiki_index();
154
a5e3c4e5 155 $this->parserstatus = 'After';
00710f4c 156
a5e3c4e5 157 if (method_exists($this, 'after_parsing')) {
00710f4c
DC
158 $this->after_parsing();
159 }
e3bc4eb7 160
00710f4c
DC
161 return array('parsed_text' => $this->string) + $this->returnvalues;
162 }
163
164 /**
165 * Initialize options
166 */
a5e3c4e5
EL
167 protected function set_options($options) {
168 }
00710f4c
DC
169
170 /**
171 * Block processing function & callbacks
172 */
00710f4c 173 protected function process_block_rule($name, $block) {
a5e3c4e5
EL
174 $this->rulestack[] = array('callback' => method_exists($this, $name . "_block_rule") ? $name . "_block_rule" : null,
175 'rule' => $block);
00710f4c
DC
176
177 $this->string = preg_replace_callback($block['expression'], array($this, 'block_callback'), $this->string);
178
179 array_pop($this->rulestack);
180 }
181
182 private function block_callback($match) {
183 $rule = end($this->rulestack);
a5e3c4e5 184 if (!empty($rule['callback'])) {
00710f4c 185 $stuff = $this->{$rule['callback']}($match);
a5e3c4e5 186 } else {
00710f4c
DC
187 $stuff = $match[1];
188 }
189
a5e3c4e5 190 if (is_array($stuff) && $rule['rule']['tag']) {
00710f4c 191 $this->rules($stuff[0], $rule['rule']['tags']);
a5e3c4e5
EL
192 $stuff = "\n" . parser_utils::h($rule['rule']['tag'], $stuff[0], $stuff[1]) . "\n";
193 } else {
194 if (!isset($rule['rule']['tags'])) {
00710f4c
DC
195 $rule['rule']['tags'] = null;
196 }
197 $this->rules($stuff, $rule['rule']['tags']);
a5e3c4e5
EL
198 if (isset($rule['rule']['tag']) && is_string($rule['rule']['tag'])) {
199 $stuff = "\n" . parser_utils::h($rule['rule']['tag'], $stuff) . "\n";
00710f4c
DC
200 }
201 }
202
203 return $stuff;
204 }
205
206 /**
207 * Rules processing function & callback
208 */
209
210 protected final function rules(&$text, $rules = null) {
a5e3c4e5 211 if ($rules === null) {
00710f4c 212 $rules = array('except' => array());
a5e3c4e5 213 } else if (is_array($rules) && count($rules) > 1) {
00710f4c
DC
214 $rules = array('only' => $rules);
215 }
216
a5e3c4e5 217 if (isset($rules['only']) && is_array($rules['only'])) {
00710f4c 218 $rules = $rules['only'];
a5e3c4e5
EL
219 foreach ($rules as $r) {
220 if (!empty($this->tagrules[$r])) {
00710f4c
DC
221 $this->process_tag_rule($r, $this->tagrules[$r], $text);
222 }
223 }
a5e3c4e5 224 } else if (isset($rules['except']) && is_array($rules['except'])) {
00710f4c 225 $rules = $rules['except'];
a5e3c4e5
EL
226 foreach ($this->tagrules as $r => $tr) {
227 if (!in_array($r, $rules)) {
00710f4c
DC
228 $this->process_tag_rule($r, $tr, $text);
229 }
230 }
231 }
232 }
233
234 private function process_tag_rule($name, $rule, &$text) {
a5e3c4e5
EL
235 if (method_exists($this, $name . "_tag_rule")) {
236 $this->rulestack[] = array('callback' => $name . "_tag_rule", 'rule' => $rule);
00710f4c
DC
237 $text = preg_replace_callback($rule['expression'], array($this, 'tag_callback'), $text);
238 array_pop($this->rulestack);
a5e3c4e5
EL
239 } else {
240 if (isset($rule['simple'])) {
00710f4c 241 $replace = "<{$rule['tag']} />";
a5e3c4e5 242 } else {
00710f4c
DC
243 $replace = parser_utils::h($rule['tag'], "$1");
244 }
e3bc4eb7 245
00710f4c
DC
246 $text = preg_replace($rule['expression'], $replace, $text);
247 }
248 }
249
250 private function tag_callback($match) {
251 $rule = end($this->rulestack);
252 $stuff = $this->{$rule['callback']}($match);
253
a5e3c4e5 254 if (is_array($stuff)) {
00710f4c 255 return parser_utils::h($rule['rule']['tag'], $stuff[0], $stuff[1]);
a5e3c4e5 256 } else {
00710f4c
DC
257 return $stuff;
258 }
259 }
260
261 /**
262 * Special nowiki parser index
263 */
264
265 private function initialize_nowiki_index() {
a5e3c4e5
EL
266 $token = "\Q" . $this->nowikitoken . "\E";
267 $this->string = preg_replace_callback("/" . $token . "\d+" . $token . "/",
268 array($this, "initialize_nowiki_index_callback"), $this->string);
00710f4c
DC
269 }
270
271 private function initialize_nowiki_index_callback($match) {
272 return $this->protect($match[0]);
273 }
274
275 protected function protect($text) {
276 $this->nowikiindex[] = $text;
277
a5e3c4e5 278 return $this->nowikitoken . (count($this->nowikiindex) - 1) . $this->nowikitoken;
00710f4c
DC
279 }
280
281 private function commit_nowiki_index() {
a5e3c4e5
EL
282 $token = "\Q" . $this->nowikitoken . "\E";
283 $this->string = preg_replace_callback("/" . $token . "(\d+)" . $token . "/",
284 array($this, "commit_nowiki_index_callback"), $this->string);
00710f4c
DC
285 }
286
287 private function commit_nowiki_index_callback($match) {
288 return $this->nowikiindex[intval($match[1])];
289 }
290
291 /**
292 * Get token of the parsable element $name.
293 */
a5e3c4e5
EL
294 public function get_token($name) {
295 foreach (array_merge($this->blockrules, $this->tagrules) as $n => $v) {
296 if ($name == $n && isset($v['token'])) {
00710f4c 297 return $v['token'] ? $v['token'] : false;
a5e3c4e5
EL
298 }
299 }
00710f4c 300
a5e3c4e5
EL
301 return false;
302 }
00710f4c 303}