From 6f13f976745c56a4cfc4c3797ec59aa594368af1 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 8 Apr 2018 17:33:01 +0100 Subject: [PATCH 01/22] Use mutating loop instead of array_map --- Parsedown.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 5baf6ab..153fb85 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1190,14 +1190,11 @@ class Parsedown $InlineText = $this->inlineText($text); $Elements[] = $InlineText['element']; - $Elements = array_map( - function ($Element) { - $Element['autobreak'] = isset($Element['autobreak']) - ? $Element['autobreak'] : false; - return $Element; - }, - $Elements - ); + foreach ($Elements as &$Element) + { + $Element['autobreak'] = isset($Element['autobreak']) + ? $Element['autobreak'] : false; + } return $Elements; } From f2327023c14170047217f2bad9f7fcdf91426f61 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 8 Apr 2018 17:38:09 +0100 Subject: [PATCH 02/22] No need to unset if not set --- Parsedown.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 153fb85..6dbca0e 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1597,9 +1597,9 @@ class Parsedown { $Element = $this->handle($Element); } - } - unset($Element['handler']); + unset($Element['handler']); + } return $Element; } From 90ad738933e3468374545da0fe331620943800af Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 8 Apr 2018 20:37:36 +0100 Subject: [PATCH 03/22] General readability --- Parsedown.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 6dbca0e..f7d0a27 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1105,7 +1105,7 @@ class Parsedown # ~ # - public function line($text, $nonNestables=array()) + public function line($text, $nonNestables = array()) { return $this->elements($this->lineElements($text, $nonNestables)); } @@ -1156,9 +1156,9 @@ class Parsedown # cause the new element to 'inherit' our non nestables - foreach ($nonNestables as $non_nestable) + foreach ($nonNestables as $nonNestable) { - $Inline['element']['nonNestables'][] = $non_nestable; + $Inline['element']['nonNestables'][] = $nonNestable; } # the text that comes before the inline @@ -1192,8 +1192,10 @@ class Parsedown foreach ($Elements as &$Element) { - $Element['autobreak'] = isset($Element['autobreak']) - ? $Element['autobreak'] : false; + if ( ! isset($Element['autobreak'])) + { + $Element['autobreak'] = false; + } } return $Elements; @@ -1763,8 +1765,8 @@ class Parsedown continue; } - $autoBreakNext = (isset($Element['autobreak']) && $Element['autobreak'] - || ! isset($Element['autobreak']) && isset($Element['name']) + $autoBreakNext = (isset($Element['autobreak']) + ? $Element['autobreak'] : isset($Element['name']) ); // (autobreak === false) covers both sides of an element $autoBreak = !$autoBreak ? $autoBreak : $autoBreakNext; From 70f5c02d47818c8ea033984b39d26988771dca45 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 8 Apr 2018 20:38:21 +0100 Subject: [PATCH 04/22] Use non-nestable values as keys for O(1) lookup --- Parsedown.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Parsedown.php b/Parsedown.php index f7d0a27..62cbf5a 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1114,6 +1114,8 @@ class Parsedown { $Elements = array(); + $nonNestables = array_combine($nonNestables, $nonNestables); + # $excerpt is based on the first occurrence of a marker while ($excerpt = strpbrk($text, $this->inlineMarkerList)) @@ -1128,7 +1130,7 @@ class Parsedown { # check to see if the current inline type is nestable in the current context - if ( ! empty($nonNestables) and in_array($inlineType, $nonNestables)) + if (isset($nonNestables[$inlineType])) { continue; } From dc5cf8770b536e2d92747c558666c51569fe2b2a Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 8 Apr 2018 20:39:20 +0100 Subject: [PATCH 05/22] The AST has high complexity here (and so traversal is hard anyway) We gain quite a bit of a speed boost by working with text here since this is a very common function --- Parsedown.php | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 62cbf5a..438e39e 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1211,32 +1211,20 @@ class Parsedown { $Inline = array( 'extent' => strlen($text), - 'element' => array( - 'elements' => array(), - ), + 'element' => array(), ); + $safeText = self::escape($text, true); + if ($this->breaksEnabled) { - $Inline['element']['elements'] = self::pregReplaceElements( - '/[ ]*\n/', - array( - array('name' => 'br'), - array('text' => "\n"), - ), - $text - ); + $Inline['element']['rawHtml'] = preg_replace('/[ ]*\n/', "
\n", $safeText); + $Inline['element']['allowRawHtmlInSafeMode'] = true; } else { - $Inline['element']['elements'] = self::pregReplaceElements( - '/(?:[ ][ ]+|[ ]*\\\\)\n/', - array( - array('name' => 'br'), - array('text' => "\n"), - ), - $text - ); + $Inline['element']['rawHtml'] = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $safeText); + $Inline['element']['allowRawHtmlInSafeMode'] = true; } return $Inline; From d6e306d62044c7731317ec2670a30842d13358d9 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 8 Apr 2018 20:43:14 +0100 Subject: [PATCH 06/22] Optimise commonly used regexes to fail fast --- Parsedown.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 438e39e..2e61f0f 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1218,12 +1218,12 @@ class Parsedown if ($this->breaksEnabled) { - $Inline['element']['rawHtml'] = preg_replace('/[ ]*\n/', "
\n", $safeText); + $Inline['element']['rawHtml'] = preg_replace('/[ ]*+\n/', "
\n", $safeText); $Inline['element']['allowRawHtmlInSafeMode'] = true; } else { - $Inline['element']['rawHtml'] = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $safeText); + $Inline['element']['rawHtml'] = preg_replace('/(?:[ ]*+\\\\|[ ]{2,}+)\n/', "
\n", $safeText); $Inline['element']['allowRawHtmlInSafeMode'] = true; } From d4f1ac465c2f0abc371fd8d200ceae245a181969 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 8 Apr 2018 21:24:45 +0100 Subject: [PATCH 07/22] String interpolation is slightly faster than concat --- Parsedown.php | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 2e61f0f..8fac5cd 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -216,7 +216,7 @@ class Parsedown if (isset($CurrentBlock['continuable'])) { - $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); + $Block = $this->{"block{$CurrentBlock['type']}Continue"}($Line, $CurrentBlock); if (isset($Block)) { @@ -228,7 +228,7 @@ class Parsedown { if ($this->isBlockCompletable($CurrentBlock['type'])) { - $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + $CurrentBlock = $this->{"block{$CurrentBlock['type']}Complete"}($CurrentBlock); } } } @@ -254,7 +254,7 @@ class Parsedown foreach ($blockTypes as $blockType) { - $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); + $Block = $this->{"block$blockType"}($Line, $CurrentBlock); if (isset($Block)) { @@ -309,7 +309,7 @@ class Parsedown if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) { - $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + $CurrentBlock = $this->{"block{$CurrentBlock['type']}Complete"}($CurrentBlock); } # ~ @@ -326,12 +326,12 @@ class Parsedown protected function isBlockContinuable($Type) { - return method_exists($this, 'block'.$Type.'Continue'); + return method_exists($this, "block${Type}Continue"); } protected function isBlockCompletable($Type) { - return method_exists($this, 'block'.$Type.'Complete'); + return method_exists($this, "block${Type}Complete"); } # @@ -427,7 +427,7 @@ class Parsedown return; } - $Block['element']['rawHtml'] .= "\n" . $Line['body']; + $Block['element']['rawHtml'] .= "\n{$Line['body']}"; if (strpos($Line['text'], '-->') !== false) { @@ -451,7 +451,7 @@ class Parsedown if (isset($matches[2])) { - $class = 'language-'.$matches[2]; + $class = "language-{$matches[2]}"; $Element['attributes'] = array( 'class' => $class, @@ -496,7 +496,7 @@ class Parsedown return $Block; } - $Block['element']['element']['text'] .= "\n".$Line['body']; + $Block['element']['element']['text'] .= "\n{$Line['body']}"; return $Block; } @@ -840,7 +840,7 @@ class Parsedown return; } - $Block['element']['rawHtml'] .= "\n".$Line['body']; + $Block['element']['rawHtml'] .= "\n{$Line['body']}"; return $Block; } @@ -960,7 +960,7 @@ class Parsedown $alignment = $alignments[$index]; $HeaderElement['attributes'] = array( - 'style' => 'text-align: '.$alignment.';', + 'style' => "text-align: $alignment;", ); } @@ -1031,7 +1031,7 @@ class Parsedown if (isset($Block['alignments'][$index])) { $Element['attributes'] = array( - 'style' => 'text-align: '.$Block['alignments'][$index].';', + 'style' => "text-align: {$Block['alignments'][$index]};", ); } @@ -1135,7 +1135,7 @@ class Parsedown continue; } - $Inline = $this->{'inline'.$inlineType}($Excerpt); + $Inline = $this->{"inline$inlineType"}($Excerpt); if ( ! isset($Inline)) { @@ -1263,7 +1263,7 @@ class Parsedown if ( ! isset($matches[2])) { - $url = 'mailto:' . $url; + $url = "mailto:$url"; } return array( @@ -1472,7 +1472,7 @@ class Parsedown if (preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches)) { return array( - 'element' => array('rawHtml' => '&'.$matches[1].';'), + 'element' => array('rawHtml' => "&{$matches[1]};"), 'extent' => strlen($matches[0]), ); } @@ -1674,7 +1674,7 @@ class Parsedown if ($hasName) { - $markup .= '<'.$Element['name']; + $markup .= "<{$Element['name']}"; if (isset($Element['attributes'])) { @@ -1685,7 +1685,7 @@ class Parsedown continue; } - $markup .= ' '.$name.'="'.self::escape($value).'"'; + $markup .= " $name=\"".self::escape($value).'"'; } } } @@ -1732,7 +1732,7 @@ class Parsedown } } - $markup .= $hasName ? '' : ''; + $markup .= $hasName ? "" : ''; } elseif ($hasName) { From 107223d3a0f65c89ac0c2afa4ead3fa6a8545004 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 8 Apr 2018 22:32:42 +0100 Subject: [PATCH 08/22] Avoid recomputation --- Parsedown.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Parsedown.php b/Parsedown.php index 8fac5cd..238ec0e 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -572,13 +572,15 @@ class Parsedown $matches[1] .= ' '; } + $markerWithoutWhitespace = strstr($matches[1], ' ', true); + $Block = array( 'indent' => $Line['indent'], 'pattern' => $pattern, 'data' => array( 'type' => $name, 'marker' => $matches[1], - 'markerType' => ($name === 'ul' ? strstr($matches[1], ' ', true) : substr(strstr($matches[1], ' ', true), -1)), + 'markerType' => ($name === 'ul' ? $markerWithoutWhitespace : substr($markerWithoutWhitespace, -1)), ), 'element' => array( 'name' => $name, From b42add3762da22437a9509f67bd5f6e5e8a49656 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 8 Apr 2018 15:41:18 +0100 Subject: [PATCH 09/22] Make some regexes possesive --- Parsedown.php | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 238ec0e..057dd49 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -442,7 +442,7 @@ class Parsedown protected function blockFencedCode($Line) { - if (preg_match('/^(['.$Line['text'][0].']{3,})[ ]*([^`]+)?[ ]*$/', $Line['text'], $matches)) + if (preg_match('/^(['.$Line['text'][0].']{3,}+)[ ]*+([^`]++)?+[ ]*+$/', $Line['text'], $matches)) { $Element = array( 'name' => 'code', @@ -486,7 +486,7 @@ class Parsedown } if ( - preg_match('/^(['.preg_quote($Block['char']).']{3,})[ ]*$/', $Line['text'], $matches) + preg_match('/^(['.preg_quote($Block['char']).']{3,}+)[ ]*+$/', $Line['text'], $matches) and mb_strlen($matches[1]) >= $Block['openerLength'] ) { $Block['element']['element']['text'] = substr($Block['element']['element']['text'], 1); @@ -555,9 +555,9 @@ class Parsedown protected function blockList($Line, array $CurrentBlock = null) { - list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]{1,9}[.\)]'); + list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]{1,9}+[.\)]'); - if (preg_match('/^('.$pattern.'([ ]+|$))(.*)/', $Line['text'], $matches)) + if (preg_match('/^('.$pattern.'([ ]++|$))(.*+)/', $Line['text'], $matches)) { $contentIndent = strlen($matches[2]); @@ -587,6 +587,7 @@ class Parsedown 'elements' => array(), ), ); + $Block['data']['markerTypeRegex'] = preg_quote($Block['data']['markerType'], '/'); if ($name === 'ol') { @@ -634,10 +635,10 @@ class Parsedown and ( ( $Block['data']['type'] === 'ol' - and preg_match('/^[0-9]+'.preg_quote($Block['data']['markerType']).'(?:[ ]+(.*)|$)/', $Line['text'], $matches) + and preg_match('/^[0-9]++'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches) ) or ( $Block['data']['type'] === 'ul' - and preg_match('/^'.preg_quote($Block['data']['markerType']).'(?:[ ]+(.*)|$)/', $Line['text'], $matches) + and preg_match('/^'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches) ) ) ) { @@ -699,7 +700,7 @@ class Parsedown if ( ! isset($Block['interrupted'])) { - $text = preg_replace('/^[ ]{0,'.$requiredIndent.'}/', '', $Line['body']); + $text = preg_replace('/^[ ]{0,'.$requiredIndent.'}+/', '', $Line['body']); $Block['li']['handler']['argument'] []= $text; @@ -728,7 +729,7 @@ class Parsedown protected function blockQuote($Line) { - if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + if (preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches)) { $Block = array( 'element' => array( @@ -752,7 +753,7 @@ class Parsedown return; } - if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + if ($Line['text'][0] === '>' and preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches)) { $Block['element']['handler']['argument'] []= $matches[1]; @@ -772,7 +773,7 @@ class Parsedown protected function blockRule($Line) { - if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) + if (preg_match('/^(['.$Line['text'][0].'])([ ]*+\1){2,}+[ ]*+$/', $Line['text'])) { $Block = array( 'element' => array( @@ -814,7 +815,7 @@ class Parsedown return; } - if (preg_match('/^<[\/]?+(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) + if (preg_match('/^<[\/]?+(\w*)(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+(\/)?>/', $Line['text'], $matches)) { $element = strtolower($matches[1]); @@ -852,7 +853,7 @@ class Parsedown protected function blockReference($Line) { - if (preg_match('/^\[(.+?)\]:[ ]*?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) + if (preg_match('/^\[(.+?)\]:[ ]*+?(?:[ ]+["\'(](.+)["\')])?[ ]*+$/', $Line['text'], $matches)) { $id = strtolower($matches[1]); @@ -1013,7 +1014,7 @@ class Parsedown $row = trim($row); $row = trim($row, '|'); - preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); + preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]++`|`)++/', $row, $matches); $cells = array_slice($matches[0], 0, count($Block['alignments'])); @@ -1236,10 +1237,10 @@ class Parsedown { $marker = $Excerpt['text'][0]; - if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(? strlen($matches[0]), @@ -1395,7 +1396,7 @@ class Parsedown return; } - if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches)) + if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches)) { $Element['attributes']['href'] = $matches[1]; @@ -1444,7 +1445,7 @@ class Parsedown return; } - if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches)) + if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $Excerpt['text'], $matches)) { return array( 'element' => array('rawHtml' => $matches[0]), @@ -1452,7 +1453,7 @@ class Parsedown ); } - if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) + if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) { return array( 'element' => array('rawHtml' => $matches[0]), @@ -1460,7 +1461,7 @@ class Parsedown ); } - if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) + if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*+(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+\/?>/s', $Excerpt['text'], $matches)) { return array( 'element' => array('rawHtml' => $matches[0]), @@ -1512,7 +1513,7 @@ class Parsedown return; } - if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) + if (preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) { $url = $matches[0][0]; @@ -1534,7 +1535,7 @@ class Parsedown protected function inlineUrlTag($Excerpt) { - if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w++:\/{2}[^ >]++)>/i', $Excerpt['text'], $matches)) { $url = $matches[1]; @@ -1939,8 +1940,8 @@ class Parsedown ); protected $StrongRegex = array( - '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', - '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*+[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*+_)+?)__(?!_)/us', ); protected $EmRegex = array( @@ -1948,7 +1949,7 @@ class Parsedown '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', ); - protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*+(?:\s*+=\s*+(?:[^"\'=<>`\s]+|"[^"]*+"|\'[^\']*+\'))?+'; protected $voidElements = array( 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', From 2faba6fef5de5611c0f167e8a6a3bbf7360bc7b5 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 8 Apr 2018 23:55:08 +0100 Subject: [PATCH 10/22] Remove unneeded complete function --- Parsedown.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 057dd49..1dc4f0e 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -383,15 +383,6 @@ class Parsedown } } - protected function blockCodeComplete($Block) - { - $text = $Block['element']['element']['text']; - - $Block['element']['element']['text'] = $text; - - return $Block; - } - # # Comment From c45e41950fc2969b24d41223605a661195636a36 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 9 Apr 2018 00:05:12 +0100 Subject: [PATCH 11/22] Use standard library over while loop --- Parsedown.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 1dc4f0e..0057566 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -199,12 +199,7 @@ class Parsedown } } - $indent = 0; - - while (isset($line[$indent]) and $line[$indent] === ' ') - { - $indent ++; - } + $indent = strspn($line, ' '); $text = $indent > 0 ? substr($line, $indent) : $line; From 3ea08140b62158fbd6d468672afb60bb4c52d9fa Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 9 Apr 2018 00:46:26 +0100 Subject: [PATCH 12/22] Remove use of array --- Parsedown.php | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 0057566..22c2558 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -182,21 +182,17 @@ class Parsedown continue; } - if (strpos($line, "\t") !== false) - { - $parts = explode("\t", $line); + for ( + $beforeTab = strstr($line, "\t", true); + $beforeTab !== false; + $beforeTab = strstr($line, "\t", true) + ) { + $shortage = 4 - mb_strlen($beforeTab, 'utf-8') % 4; - $line = $parts[0]; - - unset($parts[0]); - - foreach ($parts as $part) - { - $shortage = 4 - mb_strlen($line, 'utf-8') % 4; - - $line .= str_repeat(' ', $shortage); - $line .= $part; - } + $line = $beforeTab + . str_repeat(' ', $shortage) + . substr($line, strlen($beforeTab) + 1) + ; } $indent = strspn($line, ' '); From b53aa74a72b43ab3e13d7792a122ac607378e8e8 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 9 Apr 2018 00:55:36 +0100 Subject: [PATCH 13/22] Use standard library function --- Parsedown.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 22c2558..794438a 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -497,12 +497,7 @@ class Parsedown protected function blockHeader($Line) { - $level = 1; - - while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') - { - $level ++; - } + $level = strspn($Line['text'], '#'); if ($level > 6) { From e74a5bd7ed8ac3eaf0e95bca6d1096bdbad6bd4f Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 9 Apr 2018 01:46:36 +0100 Subject: [PATCH 14/22] In theory PHP stores the length of strings, so looking this up should be quick --- Parsedown.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Parsedown.php b/Parsedown.php index 794438a..a973478 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1102,7 +1102,7 @@ class Parsedown { $marker = $excerpt[0]; - $markerPosition = strpos($text, $marker); + $markerPosition = strlen($text) - strlen($excerpt); $Excerpt = array('text' => $excerpt, 'context' => $text); From d2dd736e1b41b65101098c9eeebdd7180dea24e3 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 9 Apr 2018 02:30:22 +0100 Subject: [PATCH 15/22] Remove regex from fenced code block Also remove unused function --- Parsedown.php | 73 +++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index a973478..fc9533f 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -424,33 +424,42 @@ class Parsedown protected function blockFencedCode($Line) { - if (preg_match('/^(['.$Line['text'][0].']{3,}+)[ ]*+([^`]++)?+[ ]*+$/', $Line['text'], $matches)) + $marker = $Line['text'][0]; + + $openerLength = strspn($Line['text'], $marker); + + if ($openerLength < 3) { - $Element = array( - 'name' => 'code', - 'text' => '', - ); - - if (isset($matches[2])) - { - $class = "language-{$matches[2]}"; - - $Element['attributes'] = array( - 'class' => $class, - ); - } - - $Block = array( - 'char' => $Line['text'][0], - 'openerLength' => mb_strlen($matches[1]), - 'element' => array( - 'name' => 'pre', - 'element' => $Element, - ), - ); - - return $Block; + return; } + + $infostring = trim(substr($Line['text'], $openerLength), "\t "); + + if (strpos($infostring, '`') !== false) + { + return; + } + + $Element = array( + 'name' => 'code', + 'text' => '', + ); + + if ($infostring !== '') + { + $Element['attributes'] = array('class' => "language-$infostring"); + } + + $Block = array( + 'char' => $marker, + 'openerLength' => $openerLength, + 'element' => array( + 'name' => 'pre', + 'element' => $Element, + ), + ); + + return $Block; } protected function blockFencedCodeContinue($Line, $Block) @@ -467,9 +476,8 @@ class Parsedown unset($Block['interrupted']); } - if ( - preg_match('/^(['.preg_quote($Block['char']).']{3,}+)[ ]*+$/', $Line['text'], $matches) - and mb_strlen($matches[1]) >= $Block['openerLength'] + if (($len = strspn($Line['text'], $Block['char'])) >= $Block['openerLength'] + and chop(substr($Line['text'], $len), ' ') === '' ) { $Block['element']['element']['text'] = substr($Block['element']['element']['text'], 1); @@ -483,15 +491,6 @@ class Parsedown return $Block; } - protected function blockFencedCodeComplete($Block) - { - $text = $Block['element']['element']['text']; - - $Block['element']['element']['text'] = $text; - - return $Block; - } - # # Header From 7e15d99d90f99066d39c4771bb061a7682ed0cb5 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 9 Apr 2018 02:31:36 +0100 Subject: [PATCH 16/22] Remove regex from block rule --- Parsedown.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Parsedown.php b/Parsedown.php index fc9533f..210e721 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -749,7 +749,9 @@ class Parsedown protected function blockRule($Line) { - if (preg_match('/^(['.$Line['text'][0].'])([ ]*+\1){2,}+[ ]*+$/', $Line['text'])) + $marker = $Line['text'][0]; + + if (substr_count($Line['text'], $marker) >= 3 and chop($Line['text'], " $marker") === '') { $Block = array( 'element' => array( From 450a74fedf2986987512b0f638b971ba2ddd1fb7 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 9 Apr 2018 02:32:01 +0100 Subject: [PATCH 17/22] More expensive statement last --- Parsedown.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 210e721..6b7ff86 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -773,10 +773,8 @@ class Parsedown return; } - if ( - chop(chop($Line['text'], ' '), $Line['text'][0]) === '' - and $Line['indent'] < 4 - ) { + if ($Line['indent'] < 4 and chop(chop($Line['text'], ' '), $Line['text'][0]) === '') + { $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; return $Block; From 726d4ef44a676de2e2af0c3b1861c9429a257196 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 9 Apr 2018 02:32:23 +0100 Subject: [PATCH 18/22] Sanity checks before starting regex engine --- Parsedown.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 6b7ff86..7fa06e3 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -829,8 +829,9 @@ class Parsedown protected function blockReference($Line) { - if (preg_match('/^\[(.+?)\]:[ ]*+?(?:[ ]+["\'(](.+)["\')])?[ ]*+$/', $Line['text'], $matches)) - { + if (strpos($Line['text'], ']') !== false + and preg_match('/^\[(.+?)\]:[ ]*+?(?:[ ]+["\'(](.+)["\')])?[ ]*+$/', $Line['text'], $matches) + ) { $id = strtolower($matches[1]); $Data = array( @@ -1448,8 +1449,9 @@ class Parsedown protected function inlineSpecialCharacter($Excerpt) { - if (preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches)) - { + if ($Excerpt['text'][1] !== ' ' and strpos($Excerpt['text'], ';') !== false + and preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches) + ) { return array( 'element' => array('rawHtml' => "&{$matches[1]};"), 'extent' => strlen($matches[0]), @@ -1489,8 +1491,9 @@ class Parsedown return; } - if (preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) - { + if (strpos($Excerpt['context'], 'http') !== false + and preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE) + ) { $url = $matches[0][0]; $Inline = array( From 88a3f31dd74a8ef3b7c7f2bfb4abd03343d3f3bd Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 12 Apr 2018 19:33:01 +0100 Subject: [PATCH 19/22] Rewrite as one statement --- Parsedown.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 7fa06e3..153dc74 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1196,16 +1196,12 @@ class Parsedown $safeText = self::escape($text, true); - if ($this->breaksEnabled) - { - $Inline['element']['rawHtml'] = preg_replace('/[ ]*+\n/', "
\n", $safeText); - $Inline['element']['allowRawHtmlInSafeMode'] = true; - } - else - { - $Inline['element']['rawHtml'] = preg_replace('/(?:[ ]*+\\\\|[ ]{2,}+)\n/', "
\n", $safeText); - $Inline['element']['allowRawHtmlInSafeMode'] = true; - } + $Inline['element']['rawHtml'] = preg_replace( + $this->breaksEnabled ? '/[ ]*+\n/' : '/(?:[ ]*+\\\\|[ ]{2,}+)\n/', + "
\n", + $safeText + ); + $Inline['element']['allowRawHtmlInSafeMode'] = true; return $Inline; } From 3e70819a20d950623f15ab040b4ef76cacdc95ed Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 12 Apr 2018 22:16:25 +0100 Subject: [PATCH 20/22] Readability improvements, thanks @PhrozenByte --- Parsedown.php | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 153dc74..4ec6a7a 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -182,11 +182,8 @@ class Parsedown continue; } - for ( - $beforeTab = strstr($line, "\t", true); - $beforeTab !== false; - $beforeTab = strstr($line, "\t", true) - ) { + while (($beforeTab = strstr($line, "\t", true)) !== false) + { $shortage = 4 - mb_strlen($beforeTab, 'utf-8') % 4; $line = $beforeTab @@ -207,7 +204,8 @@ class Parsedown if (isset($CurrentBlock['continuable'])) { - $Block = $this->{"block{$CurrentBlock['type']}Continue"}($Line, $CurrentBlock); + $methodName = 'block' . $CurrentBlock['type'] . 'Continue'; + $Block = $this->$methodName($Line, $CurrentBlock); if (isset($Block)) { @@ -219,7 +217,8 @@ class Parsedown { if ($this->isBlockCompletable($CurrentBlock['type'])) { - $CurrentBlock = $this->{"block{$CurrentBlock['type']}Complete"}($CurrentBlock); + $methodName = 'block' . $CurrentBlock['type'] . 'Complete'; + $CurrentBlock = $this->$methodName($CurrentBlock); } } } @@ -300,7 +299,8 @@ class Parsedown if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) { - $CurrentBlock = $this->{"block{$CurrentBlock['type']}Complete"}($CurrentBlock); + $methodName = 'block' . $CurrentBlock['type'] . 'Complete'; + $CurrentBlock = $this->$methodName($CurrentBlock); } # ~ @@ -317,12 +317,12 @@ class Parsedown protected function isBlockContinuable($Type) { - return method_exists($this, "block${Type}Continue"); + return method_exists($this, 'block' . $Type . 'Continue'); } protected function isBlockCompletable($Type) { - return method_exists($this, "block${Type}Complete"); + return method_exists($this, 'block' . $Type . 'Complete'); } # @@ -1138,10 +1138,11 @@ class Parsedown # cause the new element to 'inherit' our non nestables - foreach ($nonNestables as $nonNestable) - { - $Inline['element']['nonNestables'][] = $nonNestable; - } + + $Inline['element']['nonNestables'] = isset($Inline['element']['nonNestables']) + ? array_merge($Inline['element']['nonNestables'], $nonNestables) + : $nonNestables + ; # the text that comes before the inline $unmarkedText = substr($text, 0, $Inline['position']); From 7f4318dbdb5eed781a259ace08b5d5f31ac65208 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 12 Apr 2018 22:22:53 +0100 Subject: [PATCH 21/22] =?UTF-8?q?PHP=205.3=20=3D=3D=20=F0=9F=92=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Parsedown.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Parsedown.php b/Parsedown.php index 4ec6a7a..74f1d0e 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1094,7 +1094,10 @@ class Parsedown { $Elements = array(); - $nonNestables = array_combine($nonNestables, $nonNestables); + $nonNestables = (empty($nonNestables) + ? array() + : array_combine($nonNestables, $nonNestables) + ); # $excerpt is based on the first occurrence of a marker From a9764ec90f40cc440f9afc9b0f69c54b0d478c2d Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sat, 14 Apr 2018 15:27:06 +0100 Subject: [PATCH 22/22] Remove complex string interpolation expressions --- Parsedown.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 74f1d0e..46062df 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -409,7 +409,7 @@ class Parsedown return; } - $Block['element']['rawHtml'] .= "\n{$Line['body']}"; + $Block['element']['rawHtml'] .= "\n" . $Line['body']; if (strpos($Line['text'], '-->') !== false) { @@ -486,7 +486,7 @@ class Parsedown return $Block; } - $Block['element']['element']['text'] .= "\n{$Line['body']}"; + $Block['element']['element']['text'] .= "\n" . $Line['body']; return $Block; } @@ -819,7 +819,7 @@ class Parsedown return; } - $Block['element']['rawHtml'] .= "\n{$Line['body']}"; + $Block['element']['rawHtml'] .= "\n" . $Line['body']; return $Block; } @@ -1011,7 +1011,7 @@ class Parsedown if (isset($Block['alignments'][$index])) { $Element['attributes'] = array( - 'style' => "text-align: {$Block['alignments'][$index]};", + 'style' => 'text-align: ' . $Block['alignments'][$index] . ';', ); } @@ -1453,7 +1453,7 @@ class Parsedown and preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches) ) { return array( - 'element' => array('rawHtml' => "&{$matches[1]};"), + 'element' => array('rawHtml' => '&' . $matches[1] . ';'), 'extent' => strlen($matches[0]), ); } @@ -1656,7 +1656,7 @@ class Parsedown if ($hasName) { - $markup .= "<{$Element['name']}"; + $markup .= '<' . $Element['name']; if (isset($Element['attributes'])) { @@ -1714,7 +1714,7 @@ class Parsedown } } - $markup .= $hasName ? "" : ''; + $markup .= $hasName ? '' : ''; } elseif ($hasName) {