PageRenderTime 45ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/examples/tasks/replaceMarkup.php

http://github.com/ThomasWeinert/FluentDOM
PHP | 156 lines | 102 code | 10 blank | 44 comment | 3 complexity | ef6fa0cc1748e743cdbb9e7ebafce188 MD5 | raw file
  1. <?php
  2. require('../../src/FluentDOM.php');
  3. $markup = new FluentDOMMarkupReplacer();
  4. $html = <<<HTML
  5. <head>
  6. <body>
  7. 'Text can be //italic//, !!bold!!,
  8. and contain ##quotes##, [[with caption|http://links]],
  9. [[with caption and target|http://links|_self]],
  10. and [[http://links/without-caption]].
  11. Escaped literal markup sequences: \/\/, \!\!, \#\#.'
  12. </body>
  13. </head>
  14. HTML;
  15. echo $markup->replace(
  16. FluentDOM($html, 'text/html')->find('//body')
  17. );
  18. class FluentDOMMarkupReplacer {
  19. /**
  20. * Splitting pattern, all parentheises will be captured if not empty.
  21. * so make sure only a siongle one matches each parkup
  22. *
  23. * @var string
  24. */
  25. private $_splitPattern = '(
  26. (//(?:.+?)//)|
  27. (!!(?:.+?)!!)|
  28. (\\#\\#(?:.+?)\\#\\#)|
  29. (\\[\\[.*?\\]\\])
  30. )x';
  31. /**
  32. * List of replacements, first match is used to covert the element
  33. *
  34. * pattern => array(element name, attributes, text content)
  35. *
  36. * If a pattern matches the configuration is used to create the
  37. * replacement node. The configuration constists of an element name
  38. * a list of attributes and the text content of the new element.
  39. *
  40. * Each of the attribute values and the text content is a replacement pattern.
  41. *
  42. * @var array(string=>array(string,array(string=>string),string))
  43. */
  44. private $_replacements = array(
  45. '(^//(.*)//$)D' => array(
  46. 'em', array(), '$1'
  47. ),
  48. '(^!!(.*)!!$)D' => array(
  49. 'strong', array(), '$1'
  50. ),
  51. '(^##(.*)##$)D' => array(
  52. 'span', array('class' => 'quoteblock'), '$1'
  53. ),
  54. '(^\\[\\[(.+)\\|(.+)\\|(.+)\\]\\]$)D' => array(
  55. 'a', array('href' => '$2', 'target' => '$3'), '$1'
  56. ),
  57. '(^\\[\\[(.+)\\|(.+)\\]\\]$)D' => array(
  58. 'a', array('href' => '$2'), '$1'
  59. ),
  60. '(^\\[\\[(.+)\\]\\]$)D' => array(
  61. 'a', array('href' => '$1'), '$1'
  62. )
  63. );
  64. /**
  65. * A list of replacement done on the strings to replace escapin sequences
  66. *
  67. * var array(string=>string)
  68. */
  69. private $_escapings = array(
  70. '\\/\\/' => '//',
  71. '\\!\\!' => '!!',
  72. '\\#\\#' => '##'
  73. );
  74. /**
  75. * Replace markup in all text nodes inside the current selection
  76. *
  77. * @param FluentDOM $fd
  78. * @return FluentDOM
  79. */
  80. public function replace(FluentDOM $fd) {
  81. $fd
  82. ->find('descendant-or-self::text()')
  83. ->each(array($this, 'replaceNode'));
  84. return $fd->spawn();
  85. }
  86. /**
  87. * Callback used to replace a single text node.
  88. *
  89. * @param DOMText $node
  90. */
  91. public function replaceNode(DOMText $node) {
  92. if (preg_match($this->_splitPattern, $node->nodeValue)) {
  93. // split using the pattern, but capture the delimiter strings
  94. $parts = preg_split(
  95. $this->_splitPattern,
  96. $node->nodeValue,
  97. -1,
  98. PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
  99. );
  100. $items = array();
  101. foreach ($parts as $part) {
  102. $items[] = $this->createNodes($node->ownerDocument, $part);
  103. }
  104. // replace the text node
  105. FluentDOM($node)->replaceWith($items);
  106. } else {
  107. FluentDOM($node)
  108. ->text(
  109. strtr(FluentDOM($node)->text(), $this->_escapings
  110. )
  111. );
  112. }
  113. }
  114. /**
  115. * Create nodes for the given string
  116. *
  117. * @param DOMDocument $dom
  118. * @param string $string
  119. * @return DOMElement
  120. */
  121. private function createNodes($dom, $string) {
  122. foreach ($this->_replacements as $pattern => $replacement) {
  123. if (preg_match($pattern, $string)) {
  124. $node = $dom->createElement($replacement[0]);
  125. foreach ($replacement[1] as $attributeName => $attributePattern) {
  126. $node->setAttribute(
  127. $attributeName,
  128. preg_replace(
  129. $pattern, $attributePattern, $string
  130. )
  131. );
  132. }
  133. $text = $dom->createTextNode(
  134. preg_replace(
  135. $pattern, $replacement[2], $string
  136. )
  137. );
  138. $node->appendChild($text);
  139. return $node;
  140. }
  141. }
  142. return $dom->createTextNode(
  143. strtr($string, $this->_escapings)
  144. );
  145. }
  146. }