/src/PEAR2/Text/Markdown/Extra/Table.php

https://github.com/pear2/Text_Markdown · PHP · 222 lines · 105 code · 17 blank · 100 comment · 2 complexity · b7ede0499cd53e9535e1a43e214f123e MD5 · raw file

  1. <?php
  2. /**
  3. *
  4. * Block class to form tables from Markdown syntax.
  5. *
  6. * Syntax is ...
  7. *
  8. * | Header 1 | Header 2 | Header N
  9. * | ---------- | ---------- | ----------
  10. * | data cell | data cell | data cell
  11. * | data cell | data cell | data cell
  12. * | data cell | data cell | data cell
  13. * | data cell | data cell | data cell
  14. *
  15. * You can force columns alignment by putting a colon in the header-
  16. * underline row.
  17. *
  18. * | Left-Aligned | No Align | Right-Aligned
  19. * | :----------- | --------- | -------------:
  20. * | data cell | data cell | data cell
  21. * | data cell | data cell | data cell
  22. * | data cell | data cell | data cell
  23. * | data cell | data cell | data cell
  24. *
  25. * @category Solar
  26. *
  27. * @package Markdown_Extra
  28. *
  29. * @author Michel Fortin <http://www.michelf.com/projects/php-markdown/>
  30. *
  31. * @author Paul M. Jones <pmjones@solarphp.com>
  32. *
  33. * @license http://opensource.org/licenses/bsd-license.php BSD
  34. *
  35. * @version $Id: Table.php 3153 2008-05-05 23:14:16Z pmjones $
  36. *
  37. */
  38. namespace PEAR2\Text;
  39. class Markdown_Extra_Table extends Markdown_Plugin
  40. {
  41. /**
  42. *
  43. * This is a block plugin.
  44. *
  45. * @var bool
  46. *
  47. */
  48. protected $_is_block = true;
  49. /**
  50. *
  51. * Uses these chars for parsing.
  52. *
  53. * @var string
  54. *
  55. */
  56. protected $_chars = '|';
  57. /**
  58. *
  59. * Transforms Markdown syntax to XHTML tables.
  60. *
  61. * @param string $text The source text.
  62. *
  63. * @return string The transformed XHTML.
  64. *
  65. */
  66. public function parse($text)
  67. {
  68. $less_than_tab = $this->_getTabWidth() - 1;
  69. // Find tables with leading pipe.
  70. //
  71. // | Header 1 | Header 2
  72. // | -------- | --------
  73. // | Cell 1 | Cell 2
  74. // | Cell 3 | Cell 4
  75. //
  76. $text = preg_replace_callback('
  77. {
  78. ^ # Start of a line
  79. [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
  80. [|] # Optional leading pipe (present)
  81. (.+) \n # $1: Header row (at least one pipe)
  82. [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
  83. [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline
  84. ( # $3: Cells
  85. (?:
  86. [ ]* # Allowed whitespace.
  87. [|] .* \n # Row content.
  88. )*
  89. )
  90. (?=\n|\Z) # Stop at final double newline.
  91. }xm',
  92. array($this, '_parsePipe'),
  93. $text
  94. );
  95. //
  96. // Find tables without leading pipe.
  97. //
  98. // Header 1 | Header 2
  99. // -------- | --------
  100. // Cell 1 | Cell 2
  101. // Cell 3 | Cell 4
  102. //
  103. $text = preg_replace_callback('
  104. {
  105. ^ # Start of a line
  106. [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
  107. (\S.*[|].*) \n # $1: Header row (at least one pipe)
  108. [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
  109. ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline
  110. ( # $3: Cells
  111. (?:
  112. .* [|] .* \n # Row content
  113. )*
  114. )
  115. (?=\n|\Z) # Stop at final double newline.
  116. }xm',
  117. array($this, '_parsePlain'),
  118. $text
  119. );
  120. return $text;
  121. }
  122. /**
  123. *
  124. * Support callback for leading-pipe syntax.
  125. *
  126. * @param array $matches Matches from preg_replace_callback().
  127. *
  128. * @return string The replacement text.
  129. *
  130. */
  131. protected function _parsePipe($matches)
  132. {
  133. // Remove leading pipe for each row.
  134. $matches[3] = preg_replace('/^ *[|]/m', '', $matches[3]);
  135. return $this->_parsePlain($matches);
  136. }
  137. /**
  138. *
  139. * Support callback for table conversion.
  140. *
  141. * @param array $matches Matches from preg_replace_callback().
  142. *
  143. * @return string The replacement text.
  144. *
  145. */
  146. protected function _parsePlain($matches)
  147. {
  148. $head = $matches[1];
  149. $underline = $matches[2];
  150. $content = $matches[3];
  151. // Remove any tailing pipes for each line.
  152. $head = preg_replace('/[|] *$/m', '', $head);
  153. $underline = preg_replace('/[|] *$/m', '', $underline);
  154. $content = preg_replace('/[|] *$/m', '', $content);
  155. // Reading alignment from header underline.
  156. $separators = preg_split('/ *[|] */', $underline);
  157. $attr = array();
  158. foreach ($separators as $n => $s) {
  159. if (preg_match('/^ *-+: *$/', $s)) {
  160. $attr[$n] = ' align="right"';
  161. } elseif (preg_match('/^ *:-+: *$/', $s)) {
  162. $attr[$n] = ' align="center"';
  163. } elseif (preg_match('/^ *:-+ *$/', $s)) {
  164. $attr[$n] = ' align="left"';
  165. } else {
  166. $attr[$n] = '';
  167. }
  168. }
  169. // handle all spans at once, not just code spans
  170. $head = $this->_processSpans($head);
  171. $headers = preg_split('/ *[|] */', $head);
  172. $col_count = count($headers);
  173. // Write column headers.
  174. $text = "\n<table>\n";
  175. $text .= " <thead>\n";
  176. $text .= " <tr>\n";
  177. foreach ($headers as $n => $header) {
  178. $text .= " <th$attr[$n]>". trim($header) ."</th>\n";
  179. }
  180. $text .= " </tr>\n";
  181. $text .= " </thead>\n";
  182. // Split content by row.
  183. $rows = explode("\n", trim($content, "\n"));
  184. $text .= " <tbody>\n";
  185. foreach ($rows as $row) {
  186. // handle all spans at once, not just code spans
  187. $row = $this->_processSpans($row);
  188. // Split row by cell.
  189. $row_cells = preg_split('/ *[|] */', $row, $col_count);
  190. $row_cells = array_pad($row_cells, $col_count, '');
  191. $text .= " <tr>\n";
  192. foreach ($row_cells as $n => $cell) {
  193. $text .= " <td$attr[$n]>". trim($cell) ."</td>\n";
  194. }
  195. $text .= " </tr>\n";
  196. }
  197. $text .= " </tbody>\n";
  198. $text .= "</table>\n";
  199. return $this->_toHtmlToken($text) . "\n";
  200. }
  201. }