/lib/wiki-plugins/wikiplugin_dbreport.php

https://gitlab.com/ElvisAns/tiki · PHP · 1412 lines · 1359 code · 19 blank · 34 comment · 116 complexity · 3a29d056978eed16ecff2e650f565eb8 MD5 · raw file

  1. <?php
  2. // (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
  3. //
  4. // All Rights Reserved. See copyright.txt for details and a complete list of authors.
  5. // Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
  6. // $Id$
  7. use Tiki\File\FileHelper;
  8. $wikiplugin_dbreport_errors;
  9. $wikiplugin_dbreport_fields;
  10. $wikiplugin_dbreport_fields_allowed;
  11. $wikiplugin_dbreport_record;
  12. class WikipluginDBReportToken
  13. {
  14. public $type; // key=keyword, fld=field, str=string, var=variable, sty=style, eof=end of file
  15. public $content;
  16. public $start;
  17. public $after;
  18. public $code;
  19. public function type_name()
  20. {
  21. switch ($this->type) {
  22. case 'key':
  23. return 'Keyword';
  24. case 'txt':
  25. return 'Text';
  26. case 'sty':
  27. return 'Style';
  28. case 'fld':
  29. return 'Field';
  30. case 'var':
  31. return 'Variable';
  32. case 'str':
  33. return 'String';
  34. case 'bra':
  35. return 'Brackets';
  36. case 'eof':
  37. return 'End';
  38. default:
  39. return $token->type;
  40. }
  41. }
  42. public function __construct($content = null)
  43. {
  44. $this->content = $content;
  45. }
  46. }
  47. class WikipluginDBReportField
  48. {
  49. public $name;
  50. public $variable;
  51. public $break;
  52. public $index;
  53. public function __construct($text)
  54. {
  55. global $wikiplugin_dbreport_fields, $wikiplugin_dbreport_fields_allowed;
  56. $this->name = stripcslashes($text);
  57. if ($text[0] == '$') {
  58. $this->variable = substr($this->name, 1);
  59. // print("new variable $this->variable ");
  60. } else {
  61. // add to the list of parsed fields
  62. // if ($wikiplugin_dbreport_fields_allowed)
  63. $wikiplugin_dbreport_fields[] =& $this;
  64. }
  65. }
  66. public function text()
  67. {
  68. global $wikiplugin_dbreport_record;
  69. if (isset($this->index)) {
  70. // indexed field
  71. return (string) ($wikiplugin_dbreport_record[$this->index]);
  72. } elseif (isset($this->variable)) {
  73. // PHP variable
  74. if (isset($GLOBALS[$this->variable])) {
  75. return (string) $GLOBALS[$this->variable];
  76. } elseif (isset($_SESSION[$this->variable])) {
  77. return (string) $_SESSION[$this->variable];
  78. } elseif (isset($_REQUEST[$this->variable])) {
  79. return (string) $_REQUEST[$this->variable];
  80. }
  81. } else {
  82. return "[$this->name]";
  83. }
  84. }
  85. public function code()
  86. {
  87. if (isset($this->variable)) {
  88. return '[$' . addcslashes($this->variable, "\0..\37[]$\\") . ']';
  89. } else {
  90. return '[' . addcslashes($this->name, "\0..\37[]$\\") . ']';
  91. }
  92. }
  93. public function html()
  94. {
  95. return htmlentities($this->text());
  96. }
  97. public function uri()
  98. {
  99. return urlencode($this->text());
  100. }
  101. }
  102. class WikipluginDBReportString
  103. {
  104. public $literal;
  105. public function __construct($text)
  106. {
  107. $this->literal = stripcslashes($text);
  108. }
  109. public function text()
  110. {
  111. return $this->literal;
  112. }
  113. public function code()
  114. {
  115. return addcslashes($this->literal, "\0..\37[]\\");
  116. }
  117. public function html()
  118. {
  119. return htmlentities($this->text());
  120. }
  121. public function uri()
  122. {
  123. return $this->text();
  124. }
  125. }
  126. class WikipluginDBReportContent
  127. {
  128. public $elements;
  129. public function parse_text(&$text)
  130. {
  131. $parse_state = 0;
  132. $parse_text = '';
  133. $pos = 0;
  134. $len = strlen($text);
  135. while ($pos < $len) {
  136. $char = $text[$pos++];
  137. switch ($parse_state) {
  138. case 0: // start of next token
  139. switch ($char) {
  140. case '[':
  141. $parse_state = 3;
  142. break;
  143. case '\\':
  144. $parse_state = 2;
  145. $parse_text .= $char;
  146. break;
  147. default:
  148. $parse_state = 1;
  149. $parse_text .= $char;
  150. }
  151. break;
  152. case 1: // text string
  153. switch ($char) {
  154. case '[':
  155. unset($this->elements);
  156. $this->elements[] = new WikipluginDBReportString($parse_text);
  157. $parse_text = '';
  158. $parse_state = 3;
  159. break;
  160. case '\\':
  161. $parse_state = 2;
  162. $parse_text .= $char;
  163. break;
  164. default:
  165. $parse_text .= $char;
  166. }
  167. break;
  168. case 2: // literal escape
  169. $parse_text .= $char;
  170. $parse_state = 1;
  171. break;
  172. case 3: // field text
  173. switch ($char) {
  174. case '[':
  175. break;
  176. case ']':
  177. unset($this->elements);
  178. $this->elements[] = new WikipluginDBReportField($parse_text);
  179. $parse_text = '';
  180. $parse_state = 0;
  181. break;
  182. case '\\':
  183. $parse_state = 4;
  184. $parse_text .= $char;
  185. break;
  186. default:
  187. $parse_text .= $char;
  188. }
  189. break;
  190. case 4: // field escape
  191. $parse_text .= $char;
  192. $parse_state = 3;
  193. break;
  194. }
  195. }
  196. // hanging text is parsed as a string
  197. if ($parse_state != 0) {
  198. unset($this->elements);
  199. $this->elements[] = new WikipluginDBReportString($parse_text);
  200. }
  201. }
  202. public function append_field($name)
  203. {
  204. unset($this->elements);
  205. $this->elements[] = new WikipluginDBReportField($name);
  206. }
  207. public function append_variable($name)
  208. {
  209. $this->elements[] = new WikipluginDBReportField('$' . $name);
  210. }
  211. public function append_string($text)
  212. {
  213. $this->elements[] = new WikipluginDBReportString($text);
  214. }
  215. public function append($text)
  216. {
  217. $this->parse_text($text);
  218. }
  219. public function __construct(&$token)
  220. {
  221. switch ($token->type) {
  222. case 'txt':
  223. $this->parse_text($token->content);
  224. break;
  225. case 'fld':
  226. $this->append_field($token->content);
  227. break;
  228. case 'var':
  229. $this->append_variable($token->content);
  230. break;
  231. }
  232. }
  233. public function text()
  234. {
  235. $result = '';
  236. if (isset($this->elements)) {
  237. foreach ($this->elements as $element) {
  238. $result .= $element->text();
  239. }
  240. }
  241. return $result;
  242. }
  243. public function code()
  244. {
  245. $result = '';
  246. if (isset($this->elements)) {
  247. foreach ($this->elements as $element) {
  248. $result .= $element->code();
  249. }
  250. }
  251. return $result;
  252. }
  253. public function html()
  254. {
  255. $result = '';
  256. if (isset($this->elements)) {
  257. foreach ($this->elements as $element) {
  258. $result .= $element->html();
  259. }
  260. }
  261. return $result;
  262. }
  263. public function uri()
  264. {
  265. $result = '';
  266. if (isset($this->elements)) {
  267. foreach ($this->elements as $element) {
  268. $result .= $element->uri();
  269. }
  270. }
  271. return $result;
  272. }
  273. }
  274. class WikipluginDBReportText extends WikipluginDBReportContent
  275. {
  276. public $link;
  277. public $style;
  278. public function code()
  279. {
  280. $result = '"' . addcslashes(parent::code(), '"') . '"';
  281. if (isset($this->style)) {
  282. $result .= $this->style->code();
  283. }
  284. if (isset($this->link)) {
  285. $result .= ' ' . $this->link->code();
  286. }
  287. return $result;
  288. }
  289. public function html()
  290. {
  291. $html = parent::html();
  292. if (isset($this->style)) {
  293. $html = $this->style->html_start() . $html . $this->style->html_end();
  294. }
  295. if (isset($this->link)) {
  296. $html = $this->link->html_start() . $html . $this->link->html_end();
  297. }
  298. return $html;
  299. }
  300. }
  301. class WikipluginDBReportStyle
  302. {
  303. public $tag;
  304. public $class;
  305. public $style;
  306. public function __construct(&$token)
  307. {
  308. if (is_object($token)) {
  309. if ($token->content['class']) {
  310. $subtoken =& $token->content['class'];
  311. unset($this->class);
  312. $this->class = new WikipluginDBReportContent($subtoken);
  313. }
  314. if ($token->content['style']) {
  315. $subtoken =& $token->content['style'];
  316. unset($this->style);
  317. $this->style = new WikipluginDBReportContent($subtoken);
  318. }
  319. } elseif (is_string($token)) {
  320. unset($subtoken);
  321. $subtoken = new WikipluginDBReportToken($token);
  322. $subtoken->type = 'txt';
  323. unset($this->class);
  324. $this->class = new WikipluginDBReportContent($subtoken);
  325. }
  326. }
  327. public function code()
  328. {
  329. $code = ':';
  330. if (isset($this->class)) {
  331. $code .= addcslashes($this->class->code(), ' ');
  332. } elseif ($this->tag != 'span') {
  333. $code .= $this->tag;
  334. }
  335. // if (isset($this->class)) $code .= $this->class->code();
  336. if (isset($this->style)) {
  337. $code .= "{" . $this->style->code() . "}";
  338. }
  339. return $code;
  340. }
  341. public function attributes()
  342. {
  343. if (isset($this->class)) {
  344. $html .= ' class="' . $this->class->html() . '"';
  345. }
  346. if (isset($this->style)) {
  347. $html .= ' style="' . $this->style->html() . '"';
  348. }
  349. return $html;
  350. }
  351. public function html_start()
  352. {
  353. if (isset($this->class)) {
  354. $class = $this->class->html();
  355. switch (strtolower($class)) {
  356. case 'u':
  357. case 'b':
  358. case 'i':
  359. case 'h1':
  360. case 'h2':
  361. case 'h3':
  362. case 'h4':
  363. case 'h5':
  364. case 'h6':
  365. $this->tag = $class;
  366. $html = '<' . $class;
  367. break;
  368. default:
  369. $this->tag = 'span';
  370. $html = '<span class="' . $class . '"';
  371. }
  372. } else {
  373. $this->tag = 'span';
  374. $html = '<span';
  375. }
  376. if (isset($this->style)) {
  377. $html .= ' style="' . $this->style->html() . '"';
  378. }
  379. $html .= '>';
  380. return $html;
  381. }
  382. public function html_end()
  383. {
  384. return '</' . $this->tag . '>';
  385. }
  386. }
  387. class WikipluginDBReportLink
  388. {
  389. public $style;
  390. public $contents;
  391. public function code()
  392. {
  393. $result = '<';
  394. if (isset($this->contents)) {
  395. foreach ($this->contents as $content) {
  396. if (is_a($content, 'WikipluginDBReportContent')) {
  397. $result .= '"' . $content->code() . '"';
  398. } elseif (is_a($content, 'WikipluginDBReportField')) {
  399. $result .= $content->code();
  400. }
  401. }
  402. }
  403. if (isset($this->style)) {
  404. $result .= $this->style->code();
  405. }
  406. $result .= '>';
  407. return $result;
  408. }
  409. public function uri()
  410. {
  411. if (isset($this->contents)) {
  412. foreach ($this->contents as $content) {
  413. if (is_a($content, 'WikipluginDBReportContent')) {
  414. $uri .= $content->html();
  415. } elseif (is_a($content, 'WikipluginDBReportField')) {
  416. $uri .= $content->uri();
  417. }
  418. }
  419. }
  420. return $uri;
  421. }
  422. public function html_start()
  423. {
  424. $html = '<a href="' . $this->uri() . '"';
  425. if ($this->style) {
  426. $html .= $this->style->attributes();
  427. }
  428. $html .= '>';
  429. return $html;
  430. }
  431. public function html_end()
  432. {
  433. return '</a>';
  434. }
  435. public function html_onclick()
  436. {
  437. return 'onclick="document.location.href=&quot;' . $this->uri() . '&quot;"';
  438. }
  439. }
  440. class WikipluginDBReportCell
  441. {
  442. public $link;
  443. public $style;
  444. public $colspan;
  445. public $rowspan;
  446. public $contents;
  447. public function code($mode)
  448. {
  449. $result = 'CELL';
  450. if (isset($this->colspan) && isset($this->rowspan)) {
  451. if ($this->rowspan != 1) {
  452. $result .= ' ROWSPAN ' . $this->rowspan;
  453. }
  454. if ($this->colspan != 1) {
  455. $result .= ' COLSPAN ' . $this->colspan;
  456. }
  457. } elseif (isset($this->colspan)) {
  458. if ($this->colspan != 1) {
  459. if ($mode == 'ROW') {
  460. $result .= ' SPAN ' . $this->colspan;
  461. } else {
  462. $result .= ' COLSPAN ' . $this->colspan;
  463. }
  464. }
  465. } elseif (isset($this->rowspan)) {
  466. if ($this->rowspan != 1) {
  467. if ($mode == 'ROW') {
  468. $result .= ' ROWSPAN ' . $this->rowspan;
  469. } else {
  470. $result .= ' SPAN ' . $this->rowspan;
  471. }
  472. }
  473. }
  474. if (isset($this->style)) {
  475. $result .= ' ' . $this->style->code();
  476. }
  477. if (isset($this->link)) {
  478. $result .= ' ' . $this->link->code();
  479. }
  480. if (isset($this->contents)) {
  481. foreach ($this->contents as $content) {
  482. $result .= ' ' . $content->code();
  483. }
  484. }
  485. return $result;
  486. }
  487. public function html($heading = false)
  488. {
  489. if ($heading) {
  490. $html = '<th';
  491. } else {
  492. $html = '<td';
  493. }
  494. if (isset($this->style)) {
  495. $html .= $this->style->attributes();
  496. }
  497. if (isset($this->rowspan)) {
  498. $html .= ' rowspan="' . $this->rowspan . '"';
  499. }
  500. if (isset($this->colspan)) {
  501. $html .= ' colspan="' . $this->colspan . '"';
  502. }
  503. if (isset($this->link)) {
  504. $html .= ' ' . $this->link->html_onclick();
  505. }
  506. $html .= '>';
  507. if (isset($this->contents)) {
  508. foreach ($this->contents as $content) {
  509. $html .= $content->html();
  510. }
  511. }
  512. $html .= '</td>';
  513. return $html;
  514. }
  515. }
  516. class WikipluginDBReportLine
  517. {
  518. public $link;
  519. public $styles;
  520. public $cells;
  521. public function code($indent = '', $rtype = 'ROW', $cellmode = 'ROW')
  522. {
  523. $result = $indent . $rtype;
  524. if (isset($this->styles)) {
  525. foreach ($this->styles as $style) {
  526. $result .= ' ' . $style->code();
  527. }
  528. }
  529. if (isset($this->link)) {
  530. $result .= ' ' . $this->link->code();
  531. }
  532. $result .= "\n";
  533. foreach ($this->cells as $cell) {
  534. $result .= $indent . ' ' . $cell->code($cellmode) . "\n";
  535. }
  536. return $result;
  537. }
  538. public function row_html($data, $style, $heading = false)
  539. {
  540. // set the global report row
  541. global $wikiplugin_dbreport_record;
  542. $wikiplugin_dbreport_record = $data;
  543. // generate HTML
  544. $html = '<tr';
  545. if (isset($style)) {
  546. $html .= $style->attributes();
  547. }
  548. if (isset($this->link)) {
  549. $html .= ' ' . $this->link->html_onclick();
  550. }
  551. $html .= '>';
  552. foreach ($this->cells as $cell) {
  553. $html .= $cell->html($heading);
  554. }
  555. $html .= '</tr>' . "\n";
  556. return $html;
  557. }
  558. }
  559. class WikipluginDBReportTable
  560. {
  561. public $style;
  562. public $headers;
  563. public $rows;
  564. public $footers;
  565. public $style_index;
  566. public function code($indent = '')
  567. {
  568. $result = $indent . 'TABLE';
  569. if (isset($this->style)) {
  570. $result .= ' ' . $this->style->code();
  571. }
  572. $result .= "\n";
  573. if (isset($this->headers)) {
  574. foreach ($this->headers as $line) {
  575. $result .= $line->code($indent . ' ', 'HEADER');
  576. }
  577. }
  578. if (isset($this->rows)) {
  579. foreach ($this->rows as $line) {
  580. $result .= $line->code($indent . ' ', 'ROW');
  581. }
  582. }
  583. if (isset($this->footers)) {
  584. foreach ($this->footers as $line) {
  585. $result .= $line->code($indent . ' ', 'FOOTER');
  586. }
  587. }
  588. return $result;
  589. }
  590. public function line_row_html($list, $data, $heading = false)
  591. {
  592. $html = '';
  593. foreach ($list as $line) {
  594. $style = null;
  595. if (isset($line->styles)) {
  596. $style_count = count($line->styles);
  597. $style = $line->styles[$this->style_index % $style_count];
  598. }
  599. $html .= $line->row_html($data, $style, $heading);
  600. }
  601. return $html;
  602. }
  603. public function header_row_html($data)
  604. {
  605. $html = '';
  606. // generate a new table
  607. if (isset($this->style)) {
  608. $html .= '<table' . $this->style->attributes() . '>' . "\n";
  609. } else {
  610. $html .= '<table>' . "\n";
  611. }
  612. // write headers
  613. $style_index = 0;
  614. if (isset($this->headers)) {
  615. $html .= $this->line_row_html($this->headers, $data, true);
  616. }
  617. return $html;
  618. }
  619. public function record_row_html($data)
  620. {
  621. $html = '';
  622. if (isset($this->rows)) {
  623. $html .= $this->line_row_html($this->rows, $data);
  624. }
  625. $this->style_index++;
  626. return $html;
  627. }
  628. public function footer_row_html($data)
  629. {
  630. $html = '';
  631. // write footers
  632. $this->style_index = 0;
  633. if (isset($this->footers)) {
  634. $html .= $this->line_row_html($this->footers, $data, true);
  635. }
  636. // close the table
  637. $html .= '</table>';
  638. return $html;
  639. }
  640. }
  641. class WikipluginDBReportGroup
  642. {
  643. public $link;
  644. public $style;
  645. public $fields;
  646. public $field_count;
  647. public $contents;
  648. public function __toString()
  649. {
  650. $result = '';
  651. foreach ($this->contents as $entry) {
  652. $result .= $entry;
  653. }
  654. return $result;
  655. }
  656. public function code($indent = '')
  657. {
  658. $result = $indent . 'GROUP';
  659. if (isset($this->style)) {
  660. $result .= ' ' . $this->style->code();
  661. }
  662. if (isset($this->fields)) {
  663. foreach ($this->fields as $field) {
  664. $result .= ' ' . $field->code();
  665. }
  666. }
  667. if (isset($this->link)) {
  668. $result .= ' ' . $this->link->code();
  669. }
  670. if (isset($this->contents)) {
  671. foreach ($this->contents as $content) {
  672. $result .= ' ' . $content->code();
  673. }
  674. }
  675. $result .= "\n";
  676. return $result;
  677. }
  678. public function check_break(&$row)
  679. {
  680. $ret = false;
  681. // compare the field values against the break values
  682. for ($i = 0; $i < $this->field_count; $i++) {
  683. $field =& $this->fields[$i];
  684. $value =& $row[$field->index];
  685. if ($value !== $field->break) {
  686. $ret = true;
  687. $field->break =& $value;
  688. }
  689. }
  690. return $ret;
  691. }
  692. public function start_html($row)
  693. {
  694. global $wikiplugin_dbreport_record;
  695. $wikiplugin_dbreport_record = $row;
  696. $html = '';
  697. // generate a new <div> with the report content at the top
  698. if (isset($this->style)) {
  699. $html .= '<div' . $this->style->attributes() . '>' . "\n";
  700. } else {
  701. $html .= '<div>' . "\n";
  702. }
  703. if (isset($this->contents)) {
  704. foreach ($this->contents as $content) {
  705. $html .= $content->html();
  706. }
  707. }
  708. return $html;
  709. }
  710. public function end_html(&$row)
  711. {
  712. $html = '';
  713. // close the <div>
  714. $html .= '</div>';
  715. return $html;
  716. }
  717. }
  718. class WikipluginDBReportParameter extends WikipluginDBReportContent
  719. {
  720. public $name;
  721. public function code($indent = '')
  722. {
  723. $result = $indent . 'PARAM';
  724. // if (isset($this->name)) $result .= ' :'.$this->name;
  725. if (isset($this->elements)) {
  726. foreach ($this->elements as $element) {
  727. $result .= ' ' . $element->code();
  728. }
  729. }
  730. $result .= "\n";
  731. // $result .= ' "' . parent::code() . "\"\n";
  732. return $result;
  733. }
  734. }
  735. class WikipluginDBReportFail
  736. {
  737. public $link;
  738. public $style;
  739. public $contents;
  740. public function code($mode)
  741. {
  742. $result = 'FAIL';
  743. if (isset($this->style)) {
  744. $result .= ' ' . $this->style->code();
  745. }
  746. if (isset($this->link)) {
  747. $result .= ' ' . $this->link->code();
  748. }
  749. if (isset($this->contents)) {
  750. foreach ($this->contents as $content) {
  751. $result .= ' ' . $content->code();
  752. }
  753. }
  754. return $result;
  755. }
  756. public function html($heading = false)
  757. {
  758. $html = '<div';
  759. if (isset($this->style)) {
  760. $html .= $this->style->attributes();
  761. }
  762. if (isset($this->link)) {
  763. $html .= ' ' . $this->link->html_onclick();
  764. }
  765. $html .= '>';
  766. if (isset($this->contents)) {
  767. foreach ($this->contents as $content) {
  768. $html .= $content->html();
  769. }
  770. }
  771. $html .= '</div>';
  772. return $html;
  773. }
  774. }
  775. class WikipluginDBReport
  776. {
  777. public $sql;
  778. public $params;
  779. public $groups;
  780. public $table;
  781. public $columns;
  782. public $fail;
  783. public function code($indent = '')
  784. {
  785. // write the report in cannonical form.
  786. $result = $indent . 'SQL {' . $this->sql . '}' . "\n";
  787. if (isset($this->params)) {
  788. foreach ($this->params as $param) {
  789. $result .= $param->code($indent . ' ');
  790. }
  791. }
  792. if (isset($this->groups)) {
  793. foreach ($this->groups as $group) {
  794. $result .= $group->code($indent);
  795. }
  796. }
  797. if (isset($this->table)) {
  798. $result .= $this->table->code($indent);
  799. }
  800. if (isset($this->fail)) {
  801. $result .= $this->fail->code($indent);
  802. }
  803. return $result;
  804. }
  805. }
  806. function wikiplugin_dbreport_parse_error(&$token, $msg)
  807. {
  808. global $wikiplugin_dbreport_errors;
  809. // find the line relating to the token in the code
  810. $pos = 0;
  811. $line = 0;
  812. $code =& $token->code;
  813. $len = strlen($code);
  814. while ($pos < $len) {
  815. // find the next line break
  816. $line++;
  817. $break = strpos($code, "\n", $pos);
  818. if ($break === false) {
  819. $break = $len;
  820. }
  821. // was the token in this line?
  822. if ($token->start < $break) {
  823. // format the line with the token highlighted
  824. $err_line = '<i>line ' . $line . '</i>: ';
  825. $err_line .= substr($code, $pos, $token->start - $pos);
  826. $err_line .= '<span style="font-weight:bold;color:DarkRed;">' . substr($code, $token->start, $token->after - $token->start) . '</span>';
  827. if ($token->after < $break) {
  828. $err_line .= substr($code, $token->after, $break - $token->after);
  829. }
  830. $wikiplugin_dbreport_errors[] = $err_line;
  831. $pos = $len;
  832. } else {
  833. // update position and loop
  834. $pos = $break + 1;
  835. }
  836. }
  837. // add the message to the errors
  838. $wikiplugin_dbreport_errors[] = $msg;
  839. }
  840. function wikiplugin_dbreport_next_token(&$code, $len, $pos)
  841. {
  842. global $wikiplugin_dbreport_errors, $wikiplugin_dbreport_fields_allowed;
  843. $whitespace = " \n\r\t\v\f";
  844. $tokenstop = " :<>[$\"\n\r\t\v\f";
  845. // create a token object to return
  846. unset($token);
  847. $token = new WikipluginDBReportToken();
  848. $token->code =& $code;
  849. // find the next non-whitespace character in the code
  850. while (($pos < $len) && (strpos($whitespace, $code[$pos]) !== false)) {
  851. $pos++;
  852. }
  853. if ($pos >= $len) {
  854. $token->type = 'eof';
  855. $token->start = $len - 1;
  856. $token->after = $pos;
  857. return $token;
  858. }
  859. // what did we find?
  860. switch ($code[$pos]) {
  861. case '[':
  862. // field token
  863. $token->start = $pos;
  864. // parse to closing ']'
  865. while (($pos < $len) && ($code[$pos] != ']')) {
  866. if ($code[$pos] == '\\') {
  867. $pos++;
  868. }
  869. $pos++;
  870. }
  871. if ($pos < $len) {
  872. $token->after = ++$pos;
  873. $token->type = 'fld';
  874. $token->content = substr($code, $token->start + 1, $pos - $token->start - 2);
  875. return $token;
  876. } else {
  877. $token->after = ++$pos;
  878. $token->type = 'eof';
  879. wikiplugin_dbreport_parse_error($token, "Unclosed Field. ] expected.");
  880. return $token;
  881. }
  882. break;
  883. case '{':
  884. // brackets token
  885. $token->type = 'bra';
  886. $token->start = $pos;
  887. // parse until we find the closing bracket.
  888. $pos++;
  889. $state = 0;
  890. while (($pos < $len) && ($state < 4)) {
  891. $c = $code[$pos++];
  892. switch ($state) {
  893. case 0: // normal content
  894. switch ($c) {
  895. case '}':
  896. $state = 4;
  897. break;
  898. case '`':
  899. $state = 1;
  900. $token->content .= $c;
  901. break;
  902. case "'":
  903. $state = 2;
  904. $token->content .= $c;
  905. break;
  906. case '"':
  907. $state = 3;
  908. $token->content .= $c;
  909. break;
  910. default:
  911. $token->content .= $c;
  912. }
  913. break;
  914. case 1: // backtick-quoted
  915. switch ($c) {
  916. case '`':
  917. $state = 0;
  918. default:
  919. $token->content .= $c;
  920. }
  921. break;
  922. case 2: // single-quoted
  923. switch ($c) {
  924. case '\\':
  925. $token->content .= $c . $code[$pos++];
  926. break;
  927. case '\'':
  928. $state = 0;
  929. default:
  930. $token->content .= $c;
  931. }
  932. break;
  933. case 3: // double-quoted
  934. switch ($c) {
  935. case '\\':
  936. $token->content .= $c . $code[$pos++];
  937. break;
  938. case '"':
  939. $state = 0;
  940. default:
  941. $token->content .= $c;
  942. }
  943. break;
  944. }
  945. }
  946. $token->after = $pos;
  947. switch ($state) {
  948. case 0: // unclosed brackets
  949. wikiplugin_dbreport_parse_error($token, "Unclosed brackets. } expected");
  950. $token->type = 'eof';
  951. break;
  952. case 1: // unclosed backtick-quoted content
  953. wikiplugin_dbreport_parse_error($token, "Unclosed backtick-quoted content in brackets. ` then } expected");
  954. $token->type = 'eof';
  955. break;
  956. case 2: // unclosed single-quoted content
  957. wikiplugin_dbreport_parse_error($token, "Unclosed single-quoted content in brackets. ' then } expected");
  958. $token->type = 'eof';
  959. break;
  960. case 3: // unclosed double-quoted content
  961. wikiplugin_dbreport_parse_error($token, "Unclosed double-quoted content in brackets. \" then } expected");
  962. $token->type = 'eof';
  963. break;
  964. }
  965. return $token;
  966. break;
  967. case ':':
  968. // style token
  969. $token->type = 'sty';
  970. $token->start = $pos;
  971. $token->content = [];
  972. // create content sub-tokens
  973. unset($class);
  974. $class = new WikipluginDBReportToken();
  975. $class->code =& $code;
  976. $class->type = 'txt';
  977. $class->start = $pos;
  978. unset($style);
  979. $style = new WikipluginDBReportToken();
  980. $style->code =& $code;
  981. $style->type = 'txt';
  982. // parse until we find the closing space.
  983. $pos++;
  984. $state = 0;
  985. while (($pos < $len) && ($state < 6)) {
  986. $c = $code[$pos++];
  987. if ($c == '\\') {
  988. $c .= $code[$pos++];
  989. $tc = $c;
  990. } else {
  991. $tc = strpos($whitespace, $c) !== false ? ' ' : $c;
  992. }
  993. switch ($state) {
  994. case 0: // class content
  995. switch ($tc) {
  996. case '<':
  997. case '>':
  998. case '"':
  999. case ' ':
  1000. $state = 6;
  1001. $class->after = $pos;
  1002. break;
  1003. case '{':
  1004. $state = 1;
  1005. $class->after = $pos;
  1006. $style->start = $pos - 1;
  1007. break;
  1008. case '[':
  1009. $state = 2;
  1010. default:
  1011. $class->content .= $c;
  1012. }
  1013. break;
  1014. case 1: // inline style content
  1015. switch ($tc) {
  1016. case '}':
  1017. $state = 6;
  1018. $style->after = $pos;
  1019. break;
  1020. case '[':
  1021. $state = 3;
  1022. $style->content .= $c;
  1023. break;
  1024. case "'":
  1025. $state = 4;
  1026. $style->content .= $c;
  1027. break;
  1028. case '"':
  1029. $state = 5;
  1030. $style->content .= $c;
  1031. break;
  1032. default:
  1033. $style->content .= $c;
  1034. }
  1035. break;
  1036. case 2: // class field
  1037. switch ($tc) {
  1038. case ']':
  1039. $state = 0;
  1040. default:
  1041. $class->content .= $c;
  1042. }
  1043. break;
  1044. case 3: // inline style field
  1045. switch ($tc) {
  1046. case ']':
  1047. $state = 1;
  1048. default:
  1049. $style->content .= $c;
  1050. }
  1051. break;
  1052. case 4: // single-quoted inline style
  1053. switch ($tc) {
  1054. case '\'':
  1055. $state = 1;
  1056. default:
  1057. $style->content .= $c;
  1058. }
  1059. break;
  1060. case 5: // double-quoted inline style
  1061. switch ($tc) {
  1062. case '"':
  1063. $state = 1;
  1064. default:
  1065. $style->content .= $c;
  1066. }
  1067. break;
  1068. }
  1069. }
  1070. switch ($state) {
  1071. case 0: // end of file
  1072. $class->after = $pos;
  1073. break;
  1074. case 1: // inline style content
  1075. $style->after = $pos;
  1076. wikiplugin_dbreport_parse_error($style, "Unclosed style CSS. } expected");
  1077. $token->type = 'eof';
  1078. break;
  1079. case 2: // class field
  1080. $class->after = $pos;
  1081. wikiplugin_dbreport_parse_error($class, "Unclosed field in style class. ] expected");
  1082. $token->type = 'eof';
  1083. break;
  1084. case 3: // inline style field
  1085. $style->after = $pos;
  1086. wikiplugin_dbreport_parse_error($style, "Unclosed field in style CSS. ] then } expected");
  1087. $token->type = 'eof';
  1088. break;
  1089. case 4: // single-quoted inline style
  1090. $style->after = $pos;
  1091. wikiplugin_dbreport_parse_error($style, "Unclosed single-quoted content in style CSS. ' then } expected");
  1092. $token->type = 'eof';
  1093. break;
  1094. case 5: // double-quoted inline style
  1095. $style->after = $pos;
  1096. wikiplugin_dbreport_parse_error($style, "Unclosed double-quoted content in style CSS. \" then } expected");
  1097. $token->type = 'eof';
  1098. break;
  1099. }
  1100. if ($class->content) {
  1101. $token->content['class'] = $class;
  1102. }
  1103. if ($style->content) {
  1104. $token->content['style'] = $style;
  1105. }
  1106. $token->after = $pos;
  1107. return $token;
  1108. break;
  1109. case '$':
  1110. // variable token
  1111. $token->type = 'var';
  1112. $token->start = $pos;
  1113. $pos++;
  1114. // parse to end of token
  1115. while (($pos < $len) && (strpos($tokenstop, $code[$pos]) === false)) {
  1116. if ($code[$pos] == '\\') {
  1117. $pos++;
  1118. }
  1119. $pos++;
  1120. }
  1121. $token->content = substr($code, $token->start + 1, $pos - $token->start - 1);
  1122. $token->after = $pos;
  1123. return $token;
  1124. break;
  1125. case '"':
  1126. // string token
  1127. $token->type = 'txt';
  1128. $token->start = $pos;
  1129. $token->content = '';
  1130. // parse until we find the closing quote.
  1131. $pos++;
  1132. while ($pos < $len) {
  1133. // what is it?
  1134. $c = $code[$pos++];
  1135. switch ($c) {
  1136. case '"':
  1137. $token->after = $pos;
  1138. return $token;
  1139. break;
  1140. case '\\':
  1141. $token->content .= $c;
  1142. if (($pos < $len)) {
  1143. $c = $code[$pos++];
  1144. $token->content .= $c;
  1145. } else {
  1146. wikiplugin_dbreport_parse_error($token, "Unclosed escaped string. \" expected.");
  1147. $token->type = 'eof';
  1148. return $token;
  1149. }
  1150. break;
  1151. default:
  1152. $token->content .= $c;
  1153. break;
  1154. }
  1155. }
  1156. // didn't find closing quotes
  1157. $token->type = 'txt';
  1158. wikiplugin_dbreport_parse_error($token, "Unterminated string. \" expected.");
  1159. $token->type = 'eof';
  1160. return $token;
  1161. break;
  1162. case '<':
  1163. case '>':
  1164. // link keywords
  1165. $token->type = 'key';
  1166. $token->content = $code[$pos];
  1167. $token->start = $pos;
  1168. $token->after = ++$pos;
  1169. return $token;
  1170. break;
  1171. default:
  1172. // keyword token
  1173. $token->start = $pos;
  1174. // parse to end of token
  1175. while (($pos < $len) && (strpos($tokenstop, $code[$pos]) === false)) {
  1176. $pos++;
  1177. }
  1178. $token->type = 'key';
  1179. $token->content = substr($code, $token->start, $pos - $token->start);
  1180. $token->after = $pos;
  1181. return $token;
  1182. }
  1183. }
  1184. function wikiplugin_dbreport_parse(&$code)
  1185. {
  1186. global $debug, $wikiplugin_dbreport_fields_allowed;
  1187. // code properties
  1188. $len = strlen($code);
  1189. $pos = 0;
  1190. // FSM state
  1191. $parse_state = 0;
  1192. $parse_link_return = 0;
  1193. $parse_line_return = 0;
  1194. $parse_cell_return = 0;
  1195. $parse_object;
  1196. $parse_text;
  1197. $parse_line;
  1198. $parse_cell;
  1199. $span_mode;
  1200. unset($parse_report);
  1201. $parse_report = new WikipluginDBReport();
  1202. // parse the code
  1203. while (true) {
  1204. // get the next token
  1205. $token = wikiplugin_dbreport_next_token($code, $len, $pos);
  1206. $pos = $token->after;
  1207. // repeat while we have an unconsumed token
  1208. while (isset($token)) {
  1209. $next_token = $token;
  1210. switch ($parse_state) {
  1211. case 0: // next keyword
  1212. switch ($token->type) {
  1213. case 'eof':
  1214. if (! isset($parse_report->sql)) {
  1215. return wikiplugin_dbreport_parse_error($token, "Unexpected End.");
  1216. }
  1217. return $parse_report;
  1218. break;
  1219. case 'key':
  1220. switch (TikiLib::strtoupper($token->content)) {
  1221. case 'SQL':
  1222. $parse_state = 1; // switch state
  1223. unset($next_token); // consume the token
  1224. $wikiplugin_dbreport_fields_allowed = false; // no fields in sql
  1225. break;
  1226. case 'PARAM':
  1227. // create the parameter object
  1228. unset($parse_object);
  1229. $parse_object = new WikipluginDBReportParameter($token);
  1230. $parse_report->params[] =& $parse_object;
  1231. $parse_state = 2; // switch state
  1232. unset($next_token); // consume the token
  1233. $wikiplugin_dbreport_fields_allowed = false; // no fields in sql params
  1234. break;
  1235. case 'GROUP':
  1236. // create the group object
  1237. unset($parse_object);
  1238. $parse_object = new WikipluginDBReportGroup();
  1239. $parse_report->groups[] =& $parse_object;
  1240. $parse_state = 3; // switch state
  1241. unset($next_token); // consume the token
  1242. $wikiplugin_dbreport_fields_allowed = true; // we can now parse fields
  1243. break;
  1244. case 'TABLE':
  1245. // create the table object
  1246. unset($parse_object);
  1247. $parse_object = new WikipluginDBReportTable();
  1248. $parse_report->table =& $parse_object;
  1249. $parse_state = 4; // switch state
  1250. unset($next_token); // consume the token
  1251. $wikiplugin_dbreport_fields_allowed = true; // we can now parse fields
  1252. break;
  1253. case 'FAIL':
  1254. // create the fail object
  1255. unset($parse_object);
  1256. $parse_object = new WikipluginDBReportFail();
  1257. $parse_report->fail =& $parse_object;
  1258. $parse_state = 10; // switch state
  1259. unset($next_token); // consume the token
  1260. $wikiplugin_dbreport_fields_allowed = false; // no fields in fail message
  1261. break;
  1262. default:
  1263. return wikiplugin_dbreport_parse_error($token, "Invalid keyword '$token->content'");
  1264. }
  1265. break;
  1266. default:
  1267. return wikiplugin_dbreport_parse_error($token, "Unexpected " . $token->type . " '" . $token->content . "' at " . $token->start);
  1268. }
  1269. break;
  1270. case 1: // SQL content
  1271. switch ($token->type) {
  1272. case 'eof':
  1273. $parse_state = 0; // switch state and reparse the token
  1274. break;
  1275. case 'bra':
  1276. $parse_report->sql .= $token->content;
  1277. unset($next_token); // consume the token
  1278. break;
  1279. case 'txt':
  1280. $parse_report->sql .= stripcslashes($token->content);
  1281. unset($next_token); // consume the token
  1282. break;
  1283. case 'key':
  1284. $parse_state = 0; // switch state and reparse the token
  1285. break;
  1286. default:
  1287. return wikiplugin_dbreport_parse_error($token, "Unexpected " . $token->type_name() . " '$token->content' after 'SQL'. String expected.");
  1288. }
  1289. break;
  1290. case 2: // PARAM content
  1291. switch ($token->type) {
  1292. case 'eof':
  1293. $parse_state = 0; // switch parse state
  1294. break;
  1295. /* case 'sty':
  1296. $parse_object->name = $token->content;
  1297. unset($next_token); // consume the token
  1298. break; */
  1299. case 'fld':
  1300. $parse_object->append_field($token->content);
  1301. unset($next_token); // consume the token
  1302. break;
  1303. case 'var':
  1304. $parse_object->append_variable($token->content);
  1305. unset($next_token); // consume the token
  1306. break;
  1307. case 'txt':
  1308. $parse_object->elements[] = new WikipluginDBReportText($token);
  1309. unset($next_token); // consume the token
  1310. break;
  1311. case 'key':
  1312. unset($parse_object);
  1313. $parse_state = 0; // switch state and reparse the token
  1314. break;
  1315. default:
  1316. return wikiplugin_dbreport_parse_error($token, "Unexpected " . $token->type_name() . " '$token->content' after 'PARAM'. Name, Field, String or Variable expected.");
  1317. }
  1318. break;
  1319. case 3: // GROUP content
  1320. switch ($token->type) {
  1321. case 'eof':
  1322. $parse_state = 0; // switch state and reparse the token
  1323. break;
  1324. case 'fld':
  1325. unset($parse_object->fields);
  1326. $parse_object->fields[] = new WikipluginDBReportField($token->content);
  1327. $parse_object->field_count++;
  1328. unset($next_token); // consume the token
  1329. break;
  1330. case 'txt':
  1331. case 'var':
  1332. unset($parse_text);
  1333. $parse_text = new WikipluginDBReportText($token);
  1334. $parse_object->contents[] =& $parse_text;
  1335. $parse_text_return = $parse_state; // return to this state
  1336. $parse_state = 9; // switch state
  1337. unset($next_token); // consume the token
  1338. break;
  1339. case 'sty':
  1340. unset($parse_object->style);
  1341. $parse_object->style = new WikipluginDBReportStyle($token);
  1342. unset($next_token); // consume the token
  1343. break;
  1344. case 'key':
  1345. switch (TikiLib::strtoupper($token->content)) {
  1346. case '<':
  1347. unset($parse_link);
  1348. $parse_link = new WikipluginDBReportLink($token); // create the link object
  1349. $parse_object->link =& $parse_link;
  1350. $parse_link_return = $parse_state; // return to this state
  1351. $parse_state = 5; // switch state
  1352. unset($next_token); // consume the token
  1353. break;
  1354. default:
  1355. unset($parse_object); // we are finished parsing the group
  1356. $wikiplugin_dbreport_fields_allowed = false; // we cannot parse fields anymore
  1357. $parse_state = 0; // switch state and reparse the token
  1358. break;
  1359. }
  1360. break;
  1361. default:
  1362. return wikiplugin_dbreport_parse_error($token, "Unexpected " . $token->type_name() . " '$token->content' after '<'. Field, String or Style expected.");
  1363. }
  1364. break;
  1365. case 4: // TABLE content
  1366. switch ($token->type) {
  1367. case 'eof':
  1368. $parse_state = 0; // switch state and reparse the token
  1369. break;
  1370. case 'sty':
  1371. unset($parse_object->style);
  1372. $parse_object->style = new WikipluginDBReportStyle($token);
  1373. unset($next_token); // consume the token
  1374. break;
  1375. case 'key':
  1376. switch (TikiLib::strtoupper($token->content)) {
  1377. case 'HEADER':
  1378. unset($parse_line);
  1379. $parse_line = new WikipluginDBReportLine();
  1380. $parse_object->headers[] =& $parse_line;
  1381. $parse_line_return = $parse_state; // return to this state
  1382. $parse_state = 6; // switch state
  1383. unset($next_token); // consume the token
  1384. break;
  1385. case 'FOOTER':
  1386. unset($parse_line);
  1387. $parse_line = new WikipluginDBReportLine();
  1388. $parse_object->footers[] =& $parse_line;
  1389. $parse_line_return = $parse_state; // return to this state
  1390. $parse_state = 6; // switch state
  1391. unset($next_token); // consume the token