ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
LangCheck.php
Go to the documentation of this file.
1 <?php
2 require_once __DIR__ . '/../src/geshi.php';
3 
9 class LangCheck
10 {
11 
12  const TYPE_NOTICE = 1;
13  const TYPE_WARNING = 2;
14  const TYPE_ERROR = 3;
15  const TYPE_FATAL = 4;
16 
18  protected $issues = array();
19 
21  protected $file;
22 
24  protected $content;
25 
27  protected $langdata;
28 
30  protected $severityNames = array(
31  self::TYPE_NOTICE => '[NOTICE] ',
32  self::TYPE_WARNING => '[WARNING]',
33  self::TYPE_ERROR => '[ERROR] ',
34  self::TYPE_FATAL => '[FATAL] '
35  );
36 
42  public function __construct($file)
43  {
44  $this->file = $file;
45  }
46 
52  public function runChecks()
53  {
54  $this->issues = array();
55  $this->loadFile();
56  $this->checkGeneral();
57  $this->checkComment();
58  $this->loadLanguageData();
59  $this->checkMainKeys();
60  // additional checks only if no errors before
61  if (!$this->issues) {
62  $this->checkKeyContents();
63  }
64 
65  if ($this->issues) {
66  return false;
67  }
68  return true;
69  }
70 
76  public function getIssues()
77  {
78  return $this->issues;
79  }
80 
86  public function getIssuesAsString()
87  {
88  $string = '';
89  foreach ($this->issues as $issue) {
90  $string .= $this->severityNames[$issue[0]] . ' ' . $issue[1] . "\n";
91  }
92  return $string;
93  }
94 
100  protected function loadFile()
101  {
102  if (!is_file($this->file)) {
103  $this->issue(self::TYPE_FATAL, 'The path "' . $this->file . '" does not ressemble a regular file!');
104  }
105 
106  if (!is_readable($this->file)) {
107  $this->issue(self::TYPE_FATAL, 'Cannot read file "' . $this->file . '"!');
108  }
109 
110  $this->content = file_get_contents($this->file);
111  }
112 
116  protected function checkGeneral()
117  {
118  if (preg_match('/\?>(?:\r?\n|\r(?!\n)){2,}\Z/', $this->content)) {
119  $this->issue(self::TYPE_ERROR, 'Language file contains trailing empty lines at EOF!');
120  }
121  if (preg_match('/\?>(?:\r?\n|\r(?!\n))?\Z/', $this->content)) {
122  $this->issue(self::TYPE_ERROR, 'Language file contains an PHP end marker at EOF!');
123  }
124  if (!preg_match('/(?:\r?\n|\r(?!\n))\Z/', $this->content)) {
125  $this->issue(self::TYPE_ERROR, 'Language file contains no newline at EOF!');
126  }
127  if (preg_match('/(\r?\n|\r(?!\n))\\1\Z/', $this->content)) {
128  $this->issue(self::TYPE_ERROR, 'Language file contains trailing empty line before EOF!');
129  }
130  if (preg_match('/[\x20\t]$/m', $this->content)) {
131  $this->issue(self::TYPE_ERROR, 'Language file contains trailing whitespace at EOL!');
132  }
133  if (preg_match('/\t/', $this->content)) {
134  $this->issue(self::TYPE_NOTICE, 'Language file contains unescaped tabulator chars (probably for indentation)!');
135  }
136  if (preg_match('/^(?: )*(?! )(?! \*) /m', $this->content)) {
137  $this->issue(self::TYPE_NOTICE, 'Language file contains irregular indentation (other than 4 spaces per indentation level)!');
138  }
139  if (preg_match('/\015\012/s', $this->content)) {
140  $this->issue(self::TYPE_ERROR, 'Language file contains DOS line endings!');
141  } else if (preg_match('/\015/s', $this->content)) {
142  $this->issue(self::TYPE_ERROR, 'Language file contains MAC line endings!');
143  }
144  }
145 
149  protected function checkComment()
150  {
151  if (!preg_match('/\/\*\*\**\s(.*?)(?:\s*\*\/)/s', $this->content, $m)) {
152  $this->issue(self::TYPE_ERROR, 'Language file does not have an initial comment block!');
153  return;
154  }
155  $comment = $m[1];
156 
157  if (!preg_match('/Author: +\S+/', $comment)) {
158  $this->issue(self::TYPE_WARNING, 'Language file does not contain a specification of an author!');
159  }
160  if (!preg_match('/Copyright: +\S+/', $comment)) {
161  $this->issue(self::TYPE_WARNING, 'Language file does not contain a specification of the copyright!');
162  }
163  if (!preg_match('/Release Version: +\d+\.\d+\.\d+\.\d+/', $comment)) {
164  $this->issue(self::TYPE_WARNING, 'Language file does not contain a specification of the release version!');
165  }
166  if (!preg_match('/Date Started: +\S+/s', $comment)) {
167  $this->issue(self::TYPE_WARNING, 'Language file does not contain a specification of the date it was started!');
168  }
169  if (!preg_match('/This file is part of GeSHi\./', $comment)) {
170  $this->issue(self::TYPE_WARNING, 'Language file does not state that it belongs to GeSHi!');
171  }
172  if (!preg_match('/\S+ language file for GeSHi\./', $comment)) {
173  $this->issue(self::TYPE_WARNING, 'Language file does not state that it is a language file for GeSHi!');
174  }
175  if (!preg_match('/GNU General Public License/', $comment)) {
176  $this->issue(self::TYPE_WARNING, 'Language file does not state that it is provided under the terms of the GNU GPL!');
177  }
178  }
179 
185  protected function loadLanguageData()
186  {
187  $language_data = array();
188 
189  include $this->file;
190 
191  if (!isset($language_data)) {
192  $this->issue(self::TYPE_FATAL, 'Language file does not contain a $language_data structure to check!');
193  }
194 
195  if (!is_array($language_data)) {
196  $this->issue(self::TYPE_FATAL, 'Language file contains a $language_data structure which is not an array!');
197  }
198 
199  $this->langdata = $language_data;
200  unset($language_data);
201 
202  $defined = get_defined_vars();
203  if ($defined) {
204  $this->issue(self::TYPE_ERROR, 'Language file seems to define other variables than $language_data!');
205  }
206  }
207 
218  protected function ensureKeyType($name, $type = 'array', $optional = false)
219  {
220  $types = explode('|', $type);
221 
222  if (!isset($this->langdata[$name])) {
223  if ($optional) {
224  return false;
225  }
226  $this->issue(self::TYPE_ERROR, "Language file contains no \$language_data['$name'] specification!");
227  return false;
228  }
229 
230  if (!in_array(gettype($this->langdata[$name]), $types)) {
231  $this->issue(self::TYPE_ERROR, "Language file contains a \$language_data['$name'] specification which is not a $type!");
232  return false;
233  }
234 
235  return true;
236  }
237 
241  protected function checkMainKeys()
242  {
243  // these just need a type check:
244  $this->ensureKeyType('LANG_NAME', 'string');
245  $this->ensureKeyType('COMMENT_SINGLE');
246  $this->ensureKeyType('COMMENT_MULTI');
247  $this->ensureKeyType('COMMENT_REGEXP', 'array', true);
248  $this->ensureKeyType('QUOTEMARKS');
249  $this->ensureKeyType('HARDQUOTE', 'array', true);
250  $this->ensureKeyType('SYMBOLS');
251  $this->ensureKeyType('OBJECT_SPLITTERS');
252  $this->ensureKeyType('REGEXPS');
253  $this->ensureKeyType('SCRIPT_DELIMITERS');
254  $this->ensureKeyType('HIGHLIGHT_STRICT_BLOCK');
255  $this->ensureKeyType('PARSER_CONTROL', 'array', true);
256 
257  // these need additional simple checks after the type checks out
258  if ($this->ensureKeyType('ESCAPE_CHAR', 'string')) {
259  if (1 < strlen($this->langdata['ESCAPE_CHAR'])) {
260  $this->issue(self::TYPE_ERROR, 'Language file contains a $language_data[\'ESCAPE_CHAR\'] specification is not empty or exactly one char!');
261  }
262  }
263 
264  if ($this->ensureKeyType('CASE_KEYWORDS', 'integer')) {
265  if (GESHI_CAPS_NO_CHANGE != $this->langdata['CASE_KEYWORDS'] &&
266  GESHI_CAPS_LOWER != $this->langdata['CASE_KEYWORDS'] &&
267  GESHI_CAPS_UPPER != $this->langdata['CASE_KEYWORDS']
268  ) {
269  $this->issue(self::TYPE_ERROR, 'Language file contains a $language_data[\'CASE_KEYWORDS\'] specification which is neither of GESHI_CAPS_NO_CHANGE, GESHI_CAPS_LOWER nor GESHI_CAPS_UPPER!');
270  }
271  }
272 
273  if ($this->ensureKeyType('KEYWORDS')) {
274  foreach ($this->langdata['KEYWORDS'] as $kw_key => $kw_value) {
275  if (!is_integer($kw_key)) {
276  $this->issue(self::TYPE_WARNING, "Language file contains an key '$kw_key' in \$language_data['KEYWORDS'] that is not integer!");
277  } elseif (!is_array($kw_value)) {
278  $this->issue(self::TYPE_ERROR, "Language file contains a \$language_data['KEYWORDS']['$kw_value'] structure which is not an array!");
279  }
280  }
281  }
282 
283  if ($this->ensureKeyType('CASE_SENSITIVE')) {
284  foreach ($this->langdata['CASE_SENSITIVE'] as $cs_key => $cs_value) {
285  if (!is_integer($cs_key)) {
286  $this->issue(self::TYPE_WARNING, "Language file contains an key '$cs_key' in \$language_data['CASE_SENSITIVE'] that is not integer!");
287  } elseif (!is_bool($cs_value)) {
288  $this->issue(self::TYPE_ERROR, "Language file contains a Case Sensitivity specification for \$language_data['CASE_SENSITIVE']['$cs_value'] which is not a boolean!");
289  }
290  }
291  }
292 
293  if ($this->ensureKeyType('URLS')) {
294  foreach ($this->langdata['URLS'] as $url_key => $url_value) {
295  if (!is_integer($url_key)) {
296  $this->issue(self::TYPE_WARNING, "Language file contains an key '$url_key' in \$language_data['URLS'] that is not integer!");
297  } elseif (!is_string($url_value)) {
298  $this->issue(self::TYPE_ERROR, "Language file contains a Documentation URL specification for \$language_data['URLS']['$url_value'] which is not a string!");
299  } elseif (preg_match('#&([^;]*(=|$))#U', $url_value)) {
300  $this->issue(self::TYPE_ERROR, "Language file contains unescaped ampersands (&amp;) in \$language_data['URLS']!");
301  }
302  }
303  }
304 
305  if ($this->ensureKeyType('OOLANG', 'boolean|integer')) {
306  if (false !== $this->langdata['OOLANG'] &&
307  true !== $this->langdata['OOLANG'] &&
308  2 !== $this->langdata['OOLANG']
309  ) {
310  $this->issue(self::TYPE_ERROR, 'Language file contains a $language_data[\'OOLANG\'] specification which is neither of false, true or 2!');
311  }
312  }
313 
314  if ($this->ensureKeyType('STRICT_MODE_APPLIES', 'integer')) {
315  if (GESHI_MAYBE != $this->langdata['STRICT_MODE_APPLIES'] &&
316  GESHI_ALWAYS != $this->langdata['STRICT_MODE_APPLIES'] &&
317  GESHI_NEVER != $this->langdata['STRICT_MODE_APPLIES']
318  ) {
319  $this->issue(self::TYPE_ERROR, 'Language file contains a $language_data[\'STRICT_MODE_APPLIES\'] specification which is neither of GESHI_MAYBE, GESHI_ALWAYS nor GESHI_NEVER!');
320  }
321  }
322 
323  if ($this->ensureKeyType('TAB_WIDTH', 'integer', true)) {
324  if (1 > $this->langdata['TAB_WIDTH']) {
325  $this->issue(self::TYPE_ERROR, 'Language file contains a $language_data[\'TAB_WIDTH\'] specification which is less than 1!');
326  }
327  }
328 
329  if ($this->ensureKeyType('STYLES')) {
330  $style_arrays = array('KEYWORDS', 'COMMENTS', 'ESCAPE_CHAR',
331  'BRACKETS', 'STRINGS', 'NUMBERS', 'METHODS', 'SYMBOLS',
332  'REGEXPS', 'SCRIPT');
333  foreach ($style_arrays as $style_kind) {
334  if (!isset($this->langdata['STYLES'][$style_kind])) {
335  $this->issue(self::TYPE_ERROR, "Language file contains no \$language_data['STYLES']['$style_kind'] structure to check!");
336  } elseif (!is_array($this->langdata['STYLES'][$style_kind])) {
337  $this->issue(self::TYPE_ERROR, "Language file contains a \$language_data['STYLES\']['$style_kind'] structure which is not an array!");
338  } else {
339  foreach ($this->langdata['STYLES'][$style_kind] as $sk_key => $sk_value) {
340  if (!is_int($sk_key) && ('COMMENTS' != $style_kind && 'MULTI' != $sk_key)
341  && !(('STRINGS' == $style_kind || 'ESCAPE_CHAR' == $style_kind) && 'HARD' == $sk_key)
342  ) {
343  $this->issue(self::TYPE_WARNING, "Language file contains an key '$sk_key' in \$language_data['STYLES']['$style_kind'] that is not integer!");
344  } elseif (!is_string($sk_value)) {
345  $this->issue(self::TYPE_WARNING, "Language file contains a CSS specification for \$language_data['STYLES']['$style_kind'][$key] which is not a string!");
346  }
347  }
348  }
349  }
350  }
351  }
352 
358  protected function checkKeyContents()
359  {
360  foreach ($this->langdata['KEYWORDS'] as $key => $keywords) {
361  if (!isset($this->langdata['CASE_SENSITIVE'][$key])) {
362  $this->issue(self::TYPE_ERROR, "Language file contains no \$language_data['CASE_SENSITIVE'] specification for keyword group $key!");
363  }
364  if (!isset($this->langdata['URLS'][$key])) {
365  $this->issue(self::TYPE_ERROR, "Language file contains no \$language_data['URLS'] specification for keyword group $key!");
366  }
367  if (empty($keywords)) {
368  $this->issue(self::TYPE_WARNING, "Language file contains an empty keyword list in \$language_data['KEYWORDS'] for group $key!");
369  }
370  foreach ($keywords as $id => $kw) {
371  if (!is_string($kw)) {
372  $this->issue(self::TYPE_WARNING, "Language file contains an non-string entry at \$language_data['KEYWORDS'][$key][$id]!");
373  } elseif (!strlen($kw)) {
374  $this->issue(self::TYPE_ERROR, "Language file contains an empty string entry at \$language_data['KEYWORDS'][$key][$id]!");
375  } elseif (preg_match('/^([\(\)\{\}\[\]\^=.,:;\-+\*\/%\$\"\'\?]|&[\w#]\w*;)+$/i', $kw)) {
376  $this->issue(self::TYPE_NOTICE, "Language file contains an keyword ('$kw') at \$language_data['KEYWORDS'][$key][$id] which seems to be better suited for the symbols section!");
377  }
378  }
379  if (isset($this->langdata['CASE_SENSITIVE'][$key]) && !$this->langdata['CASE_SENSITIVE'][$key]) {
380  array_walk($keywords, array($this, 'strtolower'));
381  }
382  if (count($keywords) != count(array_unique($keywords))) {
383  $kw_diffs = array_count_values($keywords);
384  foreach ($kw_diffs as $kw => $kw_count) {
385  if ($kw_count > 1) {
386  $this->issue(self::TYPE_WARNING, "Language file contains per-group duplicate keyword '$kw' in \$language_data['KEYWORDS'][$key]!");
387  }
388  }
389  }
390  }
391 
392  $disallowed_before = '(?<![a-zA-Z0-9\$_\|\#;>|^&';
393  $disallowed_after = '(?![a-zA-Z0-9_\|%\\-&;';
394 
395  foreach ($this->langdata['KEYWORDS'] as $key => $keywords) {
396  foreach ($this->langdata['KEYWORDS'] as $key2 => $keywords2) {
397  if ($key2 <= $key) {
398  continue;
399  }
400  $kw_diffs = array_intersect($keywords, $keywords2);
401  foreach ($kw_diffs as $kw) {
402  if (isset($this->langdata['PARSER_CONTROL']['KEYWORDS'])) {
403  //Check the precondition\post-cindition for the involved keyword groups
404  $g1_pre = $disallowed_before;
405  $g2_pre = $disallowed_before;
406  $g1_post = $disallowed_after;
407  $g2_post = $disallowed_after;
408  if (isset($this->langdata['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'])) {
409  $g1_pre = $this->langdata['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'];
410  $g2_pre = $this->langdata['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'];
411  }
412  if (isset($this->langdata['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'])) {
413  $g1_post = $this->langdata['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'];
414  $g2_post = $this->langdata['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'];
415  }
416 
417  if (isset($this->langdata['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_BEFORE'])) {
418  $g1_pre = $this->langdata['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_BEFORE'];
419  }
420  if (isset($this->langdata['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_AFTER'])) {
421  $g1_post = $this->langdata['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_AFTER'];
422  }
423 
424  if (isset($this->langdata['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_BEFORE'])) {
425  $g2_pre = $this->langdata['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_BEFORE'];
426  }
427  if (isset($this->langdata['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_AFTER'])) {
428  $g2_post = $this->langdata['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_AFTER'];
429  }
430 
431  if ($g1_pre != $g2_pre || $g1_post != $g2_post) {
432  continue;
433  }
434  }
435  $this->issue(self::TYPE_WARNING, "Language file contains cross-group duplicate keyword '$kw' in \$language_data['KEYWORDS'][$key] and \$language_data['KEYWORDS'][$key2]!");
436  }
437  }
438  }
439  foreach ($this->langdata['CASE_SENSITIVE'] as $key => $keywords) {
440  if (!isset($this->langdata['KEYWORDS'][$key]) && $key != GESHI_COMMENTS) {
441  $this->issue(self::TYPE_WARNING, "Language file contains an superfluous \$language_data['CASE_SENSITIVE'] specification for non-existing keyword group $key!");
442  }
443  }
444  foreach ($this->langdata['URLS'] as $key => $keywords) {
445  if (!isset($this->langdata['KEYWORDS'][$key])) {
446  $this->issue(self::TYPE_WARNING, "Language file contains an superfluous \$language_data['URLS'] specification for non-existing keyword group $key!");
447  }
448  }
449  foreach ($this->langdata['STYLES']['KEYWORDS'] as $key => $keywords) {
450  if (!isset($this->langdata['KEYWORDS'][$key])) {
451  $this->issue(self::TYPE_WARNING, "Language file contains an superfluous \$language_data['STYLES']['KEYWORDS'] specification for non-existing keyword group $key!");
452  }
453  }
454 
455  foreach ($this->langdata['COMMENT_SINGLE'] as $ck => $cv) {
456  if (!is_int($ck)) {
457  $this->issue(self::TYPE_WARNING, "Language file contains an key '$ck' in \$language_data['COMMENT_SINGLE'] that is not integer!");
458  }
459  if (!is_string($cv)) {
460  $this->issue(self::TYPE_WARNING, "Language file contains an non-string entry at \$language_data['COMMENT_SINGLE'][$ck]!");
461  }
462  if (!isset($this->langdata['STYLES']['COMMENTS'][$ck])) {
463  $this->issue(self::TYPE_WARNING, "Language file contains no \$language_data['STYLES']['COMMENTS'] specification for comment group $ck!");
464  }
465  }
466  if (isset($this->langdata['COMMENT_REGEXP'])) {
467  foreach ($this->langdata['COMMENT_REGEXP'] as $ck => $cv) {
468  if (!is_int($ck)) {
469  $this->issue(self::TYPE_WARNING, "Language file contains an key '$ck' in \$language_data['COMMENT_REGEXP'] that is not integer!");
470  }
471  if (!is_string($cv)) {
472  $this->issue(self::TYPE_WARNING, "Language file contains an non-string entry at \$language_data['COMMENT_REGEXP'][$ck]!");
473  }
474  if (!isset($this->langdata['STYLES']['COMMENTS'][$ck])) {
475  $this->issue(self::TYPE_WARNING, "Language file contains no \$language_data['STYLES']['COMMENTS'] specification for comment group $ck!");
476  }
477  }
478  }
479  foreach ($this->langdata['STYLES']['COMMENTS'] as $ck => $cv) {
480  if ($ck != 'MULTI' && !isset($this->langdata['COMMENT_SINGLE'][$ck]) &&
481  !isset($this->langdata['COMMENT_REGEXP'][$ck])
482  ) {
483  $this->issue(self::TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['COMMENTS'] specification for Single Line or Regular-Expression Comment key $ck!");
484  }
485  }
486  if (isset($this->langdata['STYLES']['STRINGS']['HARD'])) {
487  if (empty($this->langdata['HARDQUOTE'])) {
488  $this->issue(self::TYPE_NOTICE, "Language file contains superfluous \$language_data['STYLES']['STRINGS'] specification for key 'HARD', but no 'HARDQUOTE's are defined!");
489  }
490  unset($this->langdata['STYLES']['STRINGS']['HARD']);
491  }
492  foreach ($this->langdata['STYLES']['STRINGS'] as $sk => $sv) {
493  if ($sk && !isset($this->langdata['QUOTEMARKS'][$sk])) {
494  $this->issue(self::TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['STRINGS'] specification for non-existing quotemark key $sk!");
495  }
496  }
497 
498  foreach ($this->langdata['REGEXPS'] as $rk => $rv) {
499  if (!is_int($rk)) {
500  $this->issue(self::TYPE_WARNING, "Language file contains an key '$rk' in \$language_data['REGEXPS'] that is not integer!");
501  }
502  if (is_string($rv)) {
503  //Check for unmasked / in regular expressions ...
504  if (empty($rv)) {
505  $this->issue(self::TYPE_WARNING, "Language file contains an empty regular expression at \$language_data['REGEXPS'][$rk]!");
506  } else {
507  if (preg_match("/(?<!\\\\)\//s", $rv)) {
508  $this->issue(self::TYPE_WARNING, "Language file contains a regular expression with an unmasked / character at \$language_data['REGEXPS'][$rk]!");
509  } elseif (preg_match("/(?<!<)(\\\\\\\\)*\\\\\|(?!>)/s", $rv)) {
510  $this->issue(self::TYPE_WARNING, "Language file contains a regular expression with an unescaped match for a pipe character '|' which needs escaping as '&lt;PIPE&gt;' instead at \$language_data['REGEXPS'][$rk]!");
511  }
512  }
513  } elseif (is_array($rv)) {
514  if (!isset($rv[GESHI_SEARCH])) {
515  $this->issue(self::TYPE_ERROR, "Language file contains no GESHI_SEARCH entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
516  } elseif (!is_string($rv[GESHI_SEARCH])) {
517  $this->issue(self::TYPE_ERROR, "Language file contains a GESHI_SEARCH entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
518  } else {
519  if (preg_match("/(?<!\\\\)\//s", $rv[GESHI_SEARCH])) {
520  $this->issue(self::TYPE_WARNING, "Language file contains a regular expression with an unmasked / character at \$language_data['REGEXPS'][$rk]!");
521  } elseif (preg_match("/(?<!<)(\\\\\\\\)*\\\\\|(?!>)/s", $rv[GESHI_SEARCH])) {
522  $this->issue(self::TYPE_WARNING, "Language file contains a regular expression with an unescaped match for a pipe character '|' which needs escaping as '&lt;PIPE&gt;' instead at \$language_data['REGEXPS'][$rk]!");
523  }
524  }
525  if (!isset($rv[GESHI_REPLACE])) {
526  $this->issue(self::TYPE_WARNING, "Language file contains no GESHI_REPLACE entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
527  } elseif (!is_string($rv[GESHI_REPLACE])) {
528  $this->issue(self::TYPE_ERROR, "Language file contains a GESHI_REPLACE entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
529  }
530  if (!isset($rv[GESHI_MODIFIERS])) {
531  $this->issue(self::TYPE_WARNING, "Language file contains no GESHI_MODIFIERS entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
532  } elseif (!is_string($rv[GESHI_MODIFIERS])) {
533  $this->issue(self::TYPE_ERROR, "Language file contains a GESHI_MODIFIERS entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
534  }
535  if (!isset($rv[GESHI_BEFORE])) {
536  $this->issue(self::TYPE_WARNING, "Language file contains no GESHI_BEFORE entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
537  } elseif (!is_string($rv[GESHI_BEFORE])) {
538  $this->issue(self::TYPE_ERROR, "Language file contains a GESHI_BEFORE entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
539  }
540  if (!isset($rv[GESHI_AFTER])) {
541  $this->issue(self::TYPE_WARNING, "Language file contains no GESHI_AFTER entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
542  } elseif (!is_string($rv[GESHI_AFTER])) {
543  $this->issue(self::TYPE_ERROR, "Language file contains a GESHI_AFTER entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
544  }
545  } else {
546  $this->issue(self::TYPE_WARNING, "Language file contains an non-string and non-array entry at \$language_data['REGEXPS'][$rk]!");
547  }
548  if (!isset($this->langdata['STYLES']['REGEXPS'][$rk])) {
549  $this->issue(self::TYPE_WARNING, "Language file contains no \$language_data['STYLES']['REGEXPS'] specification for regexp group $rk!");
550  }
551  }
552  foreach ($this->langdata['STYLES']['REGEXPS'] as $rk => $rv) {
553  if (!isset($this->langdata['REGEXPS'][$rk])) {
554  $this->issue(self::TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['REGEXPS'] specification for regexp key $rk!");
555  }
556  }
557  }
558 
559 
567  protected function issue($type, $msg)
568  {
569  $this->issues[] = array($type, $msg);
570 
571  // abort all processing on fatal errors
572  if ($type == self::TYPE_FATAL) {
573  throw new Exception($msg);
574  }
575  }
576 
577  public function strtolower(&$value)
578  {
579  $value = strtolower($value);
580  }
581 }
loadLanguageData()
Load the language data.
Definition: LangCheck.php:185
const TYPE_WARNING
Definition: LangCheck.php:13
const GESHI_MODIFIERS
The key of the regex array defining any modifiers to the regular expression.
Definition: geshi.php:137
$type
getIssuesAsString()
Get all found issues as formatted list.
Definition: LangCheck.php:86
if(!array_key_exists('StateId', $_REQUEST)) $id
const GESHI_BEFORE
The key of the regex array defining what bracket group in a matched search to put before the replacem...
Definition: geshi.php:140
__construct($file)
LangCheck constructor.
Definition: LangCheck.php:42
issue($type, $msg)
log an issue
Definition: LangCheck.php:567
strtolower(&$value)
Definition: LangCheck.php:577
const GESHI_COMMENTS
Used in language files to mark comments.
Definition: geshi.php:149
checkGeneral()
Check some general file properties.
Definition: LangCheck.php:116
const GESHI_REPLACE
The key of the regex array defining what bracket group in a matched search to use as a replacement...
Definition: geshi.php:135
Class LangCheck.
Definition: LangCheck.php:9
const TYPE_ERROR
Definition: LangCheck.php:14
if($format !==null) $name
Definition: metadata.php:146
checkMainKeys()
Check the major keys in the language array.
Definition: LangCheck.php:241
const TYPE_FATAL
Definition: LangCheck.php:15
loadFile()
Load the file content.
Definition: LangCheck.php:100
const GESHI_CAPS_UPPER
Uppercase keywords found.
Definition: geshi.php:96
ensureKeyType($name, $type='array', $optional=false)
Check that the given key exists and has the correct type.
Definition: LangCheck.php:218
getIssues()
Get all found issues as (severity, message) tuple.
Definition: LangCheck.php:76
checkKeyContents()
Check the keywords are sane.
Definition: LangCheck.php:358
Reload workbook from saved file
const GESHI_SEARCH
The key of the regex array defining what to search for.
Definition: geshi.php:132
$comment
Definition: buildRTE.php:83
Create styles array
The data for the language used.
const GESHI_CAPS_LOWER
Leave keywords found as the case that they are.
Definition: geshi.php:98
const GESHI_MAYBE
Strict mode might apply, and can be enabled or disabled by GeSHi->enable_strict_mode().
Definition: geshi.php:126
$language_data
Definition: 4cs.php:40
const TYPE_NOTICE
Definition: LangCheck.php:12
const GESHI_NEVER
#+ private
Definition: geshi.php:123
const GESHI_CAPS_NO_CHANGE
Lowercase keywords found.
Definition: geshi.php:94
$key
Definition: croninfo.php:18
checkComment()
Check that the needed information is in the initial file comment.
Definition: LangCheck.php:149
const GESHI_ALWAYS
Strict mode always applies.
Definition: geshi.php:128
runChecks()
Run all the checks on the file.
Definition: LangCheck.php:52
const GESHI_AFTER
The key of the regex array defining what bracket group in a matched search to put after the replaceme...
Definition: geshi.php:143