diff --git a/Parsedown.php b/Parsedown.php index 6ffb4020e..f67524125 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -41,7 +41,7 @@ function text($text) # trim line breaks $markup = trim($markup, "\n"); - return $markup; + return $this->runHooks(__FUNCTION__, $markup); } # @@ -275,7 +275,7 @@ protected function lines(array $lines) # ~ - return $markup; + return $this->runHooks(__FUNCTION__, $markup); } # @@ -283,12 +283,14 @@ protected function lines(array $lines) # protected function isBlockContinuable($Type) { - return method_exists($this, 'block'.$Type.'Continue'); + $return = method_exists($this, 'block'.$Type.'Continue'); + return $this->runHooks(__FUNCTION__, $return); } protected function isBlockCompletable($Type) { - return method_exists($this, 'block'.$Type.'Complete'); + $return = method_exists($this, 'block'.$Type.'Complete'); + return $this->runHooks(__FUNCTION__, $return); } # @@ -316,7 +318,7 @@ protected function blockCode($Line, $Block = null) ), ); - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -337,7 +339,7 @@ protected function blockCodeContinue($Line, $Block) $Block['element']['text']['text'] .= $text; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -349,7 +351,7 @@ protected function blockCodeComplete($Block) $Block['element']['text']['text'] = $text; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } # @@ -373,7 +375,7 @@ protected function blockComment($Line) $Block['closed'] = true; } - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -391,7 +393,7 @@ protected function blockCommentContinue($Line, array $Block) $Block['closed'] = true; } - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } # @@ -424,7 +426,7 @@ protected function blockFencedCode($Line) ), ); - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -448,12 +450,12 @@ protected function blockFencedCodeContinue($Line, $Block) $Block['complete'] = true; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } $Block['element']['text']['text'] .= "\n".$Line['body'];; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } protected function blockFencedCodeComplete($Block) @@ -464,7 +466,7 @@ protected function blockFencedCodeComplete($Block) $Block['element']['text']['text'] = $text; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } # @@ -496,7 +498,7 @@ protected function blockHeader($Line) ), ); - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -528,7 +530,7 @@ protected function blockList($Line) $Block['element']['text'] []= & $Block['li']; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -557,12 +559,12 @@ protected function blockListContinue($Line, array $Block) $Block['element']['text'] []= & $Block['li']; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } if ($Line['text'][0] === '[' and $this->blockReference($Line)) { - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } if ( ! isset($Block['interrupted'])) @@ -571,7 +573,7 @@ protected function blockListContinue($Line, array $Block) $Block['li']['text'] []= $text; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } if ($Line['indent'] > 0) @@ -584,7 +586,7 @@ protected function blockListContinue($Line, array $Block) unset($Block['interrupted']); - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -603,7 +605,7 @@ protected function blockQuote($Line) ), ); - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -620,14 +622,14 @@ protected function blockQuoteContinue($Line, array $Block) $Block['element']['text'] []= $matches[1]; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } if ( ! isset($Block['interrupted'])) { $Block['element']['text'] []= $Line['text']; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -644,7 +646,7 @@ protected function blockRule($Line) ), ); - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -662,7 +664,7 @@ protected function blockSetextHeader($Line, array $Block = null) { $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -717,7 +719,7 @@ protected function blockMarkup($Line) } } - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -754,7 +756,7 @@ protected function blockMarkupContinue($Line, array $Block) $Block['markup'] .= "\n".$Line['body']; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } # @@ -782,7 +784,7 @@ protected function blockReference($Line) 'hidden' => true, ); - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -892,7 +894,7 @@ protected function blockTable($Line, array $Block = null) 'text' => $HeaderElements, ); - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -942,7 +944,7 @@ protected function blockTableContinue($Line, array $Block) $Block['element']['text'][1]['text'] []= $Element; - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } } @@ -960,7 +962,7 @@ protected function paragraph($Line) ), ); - return $Block; + return $this->runHooks(__FUNCTION__, $Block); } # @@ -1053,7 +1055,7 @@ public function line($text) $markup .= $this->unmarkedText($text); - return $markup; + return $this->runHooks(__FUNCTION__, $markup); } # @@ -1070,13 +1072,15 @@ protected function inlineCode($Excerpt) $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); $text = preg_replace("/[ ]*\n/", ' ', $text); - return array( + $return = array( 'extent' => strlen($matches[0]), 'element' => array( 'name' => 'code', 'text' => $text, ), ); + + return $this->runHooks(__FUNCTION__, $return); } } @@ -1091,7 +1095,7 @@ protected function inlineEmailTag($Excerpt) $url = 'mailto:' . $url; } - return array( + $return = array( 'extent' => strlen($matches[0]), 'element' => array( 'name' => 'a', @@ -1101,6 +1105,8 @@ protected function inlineEmailTag($Excerpt) ), ), ); + + return $this->runHooks(__FUNCTION__, $return); } } @@ -1126,7 +1132,7 @@ protected function inlineEmphasis($Excerpt) return; } - return array( + $return = array( 'extent' => strlen($matches[0]), 'element' => array( 'name' => $emphasis, @@ -1134,16 +1140,20 @@ protected function inlineEmphasis($Excerpt) 'text' => $matches[1], ), ); + + return $this->runHooks(__FUNCTION__, $return); } protected function inlineEscapeSequence($Excerpt) { if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) { - return array( + $return = array( 'markup' => $Excerpt['text'][1], 'extent' => 2, ); + + return $this->runHooks(__FUNCTION__, $return); } } @@ -1178,7 +1188,7 @@ protected function inlineImage($Excerpt) unset($Inline['element']['attributes']['href']); - return $Inline; + return $this->runHooks(__FUNCTION__, $Inline); } protected function inlineLink($Excerpt) @@ -1248,10 +1258,12 @@ protected function inlineLink($Excerpt) $Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']); - return array( + $return = array( 'extent' => $extent, 'element' => $Element, ); + + return $this->runHooks(__FUNCTION__, $return); } protected function inlineMarkup($Excerpt) @@ -1263,26 +1275,32 @@ protected function inlineMarkup($Excerpt) if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches)) { - return array( + $return = array( 'markup' => $matches[0], 'extent' => strlen($matches[0]), ); + + return $this->runHooks(__FUNCTION__, $return); } if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) { - return array( + $return = array( 'markup' => $matches[0], 'extent' => strlen($matches[0]), ); + + return $this->runHooks(__FUNCTION__, $return); } if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) { - return array( + $return = array( 'markup' => $matches[0], 'extent' => strlen($matches[0]), ); + + return $this->runHooks(__FUNCTION__, $return); } } @@ -1290,20 +1308,24 @@ protected function inlineSpecialCharacter($Excerpt) { if ($Excerpt['text'][0] === '&' and ! preg_match('/^?\w+;/', $Excerpt['text'])) { - return array( + $return = array( 'markup' => '&', 'extent' => 1, ); + + return $this->runHooks(__FUNCTION__, $return); } $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); if (isset($SpecialCharacter[$Excerpt['text'][0]])) { - return array( + $return = array( 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', 'extent' => 1, ); + + return $this->runHooks(__FUNCTION__, $return); } } @@ -1316,7 +1338,7 @@ protected function inlineStrikethrough($Excerpt) if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) { - return array( + $return = array( 'extent' => strlen($matches[0]), 'element' => array( 'name' => 'del', @@ -1324,6 +1346,8 @@ protected function inlineStrikethrough($Excerpt) 'handler' => 'line', ), ); + + return $this->runHooks(__FUNCTION__, $return); } } @@ -1348,7 +1372,7 @@ protected function inlineUrl($Excerpt) ), ); - return $Inline; + return $this->runHooks(__FUNCTION__, $Inline); } } @@ -1358,7 +1382,7 @@ protected function inlineUrlTag($Excerpt) { $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]); - return array( + $return = array( 'extent' => strlen($matches[0]), 'element' => array( 'name' => 'a', @@ -1368,6 +1392,8 @@ protected function inlineUrlTag($Excerpt) ), ), ); + + return $this->runHooks(__FUNCTION__, $return); } } @@ -1385,7 +1411,7 @@ protected function unmarkedText($text) $text = str_replace(" \n", "\n", $text); } - return $text; + return $this->runHooks(__FUNCTION__, $text); } # @@ -1429,7 +1455,7 @@ protected function element(array $Element) $markup .= ' />'; } - return $markup; + return $this->runHooks(__FUNCTION__, $markup); } protected function elements(array $Elements) @@ -1443,7 +1469,7 @@ protected function elements(array $Elements) $markup .= "\n"; - return $markup; + return $this->runHooks(__FUNCTION__, $markup); } # ~ @@ -1464,7 +1490,24 @@ protected function li($lines) $markup = substr_replace($markup, '', $position, 4); } - return $markup; + return $this->runHooks(__FUNCTION__, $markup); + } + + /** + * Called in each method which supports hooks. + * @param String method name + * @param Mixed return value from original method + * @return Mixed return value passed through the hook method + */ + protected function runHooks($methodName, $return) + { + foreach ($this->hooks as $hook) { + if (method_exists($hook, $methodName)) { + $return = $hook->$methodName($return); + } + } + + return $return; } # @@ -1496,6 +1539,52 @@ static function instance($name = 'default') return $instance; } + /** + * @param Array $blockType + * @return Array $BlockTypes + */ + public function addBlockType(array $blockType) + { + $this->BlockTypes[] = $blockType; + return $this->BlockTypes; + } + + /** + * @param String $unmarkedBlockType + * @return Array $UnmarkedBlockTypes + */ + public function addUnmarkedBlockType($unmarkedBlockType) + { + $this->UnmarkedBlockTypes[] = $unmarkedBlockType; + return $this->UnmarkedBlockTypes; + } + + /** + * @param Array $inlineType + * @return Array $InlineTypes + */ + public function addInlineType(array $inlineType) + { + $this->InlineTypes[] = $inlineType; + return $this->InlineTypes; + } + + /** + * Registers a hook class + * @param String $className + */ + public function registerHook($className = NULL) + { + if (class_exists($className)) { + $this->hooks[] = new $className($this); + return end($this->hooks); + } + + return FALSE; + } + + private $hooks = array(); + private static $instances = array(); # diff --git a/README.md b/README.md index 7e1f10e77..612ee1d5e 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,28 @@ $Parsedown = new Parsedown(); echo $Parsedown->text('Hello _Parsedown_!'); # prints:
Hello Parsedown!
``` - More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [this video tutorial](http://youtu.be/wYZBY8DEikI). + +### Hooks +You can write hooks that extend Parsedown's functionality. To use, write a class that declares static methods with the same name as the methods which +you would like to modify the output. Then register the class with Parsedown. +#### Hook Example +``` php +class HookExample +{ + public function inlineUrl($url) { + $url['element']['attributes']['rel'] = 'nofollow'; + return $url; + } +} + +$Parsedown = new Parsedown; +$Parsedown->registerHook('HookExample'); +echo $Parsedown->text('http://google.com'); +``` + + ### Questions **How does Parsedown work?**