From 162e830f30200a9562da4b71c9c720a44aeb7d6b Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Tue, 30 Mar 2021 17:14:16 +0800
Subject: [PATCH 01/29] fix empty list line

---
 Parser.php | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/Parser.php b/Parser.php
index 515c27c..71b2269 100644
--- a/Parser.php
+++ b/Parser.php
@@ -695,8 +695,8 @@ private function parseBlockList($block, $key, $line, &$state)
     {
         if ($this->isBlock('list') && !preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line)) {
             if ($state['empty'] <= 1
-                && preg_match("/^(\s+)/", $line, $matches)
-                && strlen($matches[1]) > $block[3]) {
+                && preg_match("/^(\s*)\S+/", $line, $matches)
+                && strlen($matches[1]) >= ($block[3] + $state['empty'])) {
 
                 $state['empty'] = 0;
                 $this->setBlock($key);
@@ -1583,7 +1583,7 @@ private function parseHr($lines, $value, $start)
      * @param int $start
      * @return string
      */
-    private function parseNormal(array $lines, $inline = false, $start)
+    private function parseNormal(array $lines, $inline, $start)
     {
         foreach ($lines as $key => &$line) {
             $line = $this->parseInline($line);
@@ -1594,7 +1594,10 @@ private function parseNormal(array $lines, $inline = false, $start)
         }
 
         $str = trim(implode("\n", $lines));
-        $str = preg_replace("/(\n\s*){2,}/", "</p><p>", $str);
+        $str = preg_replace_callback("/(\n\s*){2,}/", function () use (&$inline) {
+            $inline = false;
+            return "</p><p>";
+        }, $str);
         $str = preg_replace("/\n/", "<br>", $str);
 
         return preg_match("/^\s*$/", $str) ? '' : ($inline ? $str : "<p>{$str}</p>");

From e3fb37cb290c021ee277bbc613e3136e41be6d59 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Thu, 1 Apr 2021 18:51:39 +0800
Subject: [PATCH 02/29] redesign list parser

---
 Parser.php | 110 +++++++++++++++--------------------------------------
 1 file changed, 31 insertions(+), 79 deletions(-)

diff --git a/Parser.php b/Parser.php
index 9611382..c3828ba 100644
--- a/Parser.php
+++ b/Parser.php
@@ -146,7 +146,7 @@ public function makeHtml($text)
         });
 
         foreach ($this->blockParsers as $parser) {
-            list ($name) = $parser;
+            [$name] = $parser;
 
             if (isset($parser[2])) {
                 $this->_parsers[$name] = $parser[2];
@@ -258,7 +258,7 @@ private function parse($text, $inline = false, $offset = 0)
         }
 
         foreach ($blocks as $block) {
-            list ($type, $start, $end, $value) = $block;
+            [$type, $start, $end, $value] = $block;
             $extract = array_slice($lines, $start, $end - $start + 1);
             $method = 'parse' . ucfirst($type);
 
@@ -542,7 +542,7 @@ function ($matches) use ($self) {
                 "/(^|[^\"])((https?):[\p{L}_a-z0-9-:\.\*\/%#;!@\?\+=~\|\,&\(\)\[\]]+)/iu",
                 function ($matches) use ($self) {
                     $link = $self->call('parseLink', $matches[2]);
-                    return "{$matches[1]}<a href=\"{$matches[2]}\">{$link}</a>{$matches[4]}";
+                    return "{$matches[1]}<a href=\"{$matches[2]}\">{$link}</a>";
                 },
                 $text
             );
@@ -696,7 +696,7 @@ private function parseBlockList($block, $key, $line, &$state)
         if ($this->isBlock('list') && !preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line)) {
             if ($state['empty'] <= 1
                 && preg_match("/^(\s*)\S+/", $line, $matches)
-                && strlen($matches[1]) >= ($block[3] + $state['empty'])) {
+                && strlen($matches[1]) >= ($block[3][0] + $state['empty'])) {
 
                 $state['empty'] = 0;
                 $this->setBlock($key);
@@ -711,12 +711,17 @@ private function parseBlockList($block, $key, $line, &$state)
         if (preg_match("/^(\s*)((?:[0-9]+\.)|\-|\+|\*)\s+/i", $line, $matches)) {
             $space = strlen($matches[1]);
             $state['empty'] = 0;
+            $type = false !== strpos('+-*', $matches[2]) ? 'ul' : 'ol';
 
             // opened
             if ($this->isBlock('list')) {
-                $this->setBlock($key, $space);
+                if ($space < $block[3][0] || ($space == $block[3][0] && $type != $block[3][1])) {
+                    $this->startBlock('list', $key, [$space, $type]);
+                } else {
+                    $this->setBlock($key);
+                }
             } else {
-                $this->startBlock('list', $key, $space);
+                $this->startBlock('list', $key, [$space, $type]);
             }
 
             return false;
@@ -1181,7 +1186,7 @@ private function optimizeBlocks(array $blocks, array $lines)
             $prevBlock = isset($blocks[$key - 1]) ? $blocks[$key - 1] : NULL;
             $nextBlock = isset($blocks[$key + 1]) ? $blocks[$key + 1] : NULL;
 
-            list ($type, $from, $to) = $block;
+            [$type, $from, $to] = $block;
 
             if ('pre' == $type) {
                 $isEmpty = array_reduce(
@@ -1203,10 +1208,12 @@ function ($result, $line) {
 
                 if ($from == $to && preg_match("/^\s*$/", $lines[$from])
                     && !empty($prevBlock) && !empty($nextBlock)) {
-                    if ($prevBlock[0] == $nextBlock[0] && in_array($prevBlock[0], $types)) {
+                    if ($prevBlock[0] == $nextBlock[0] && in_array($prevBlock[0], $types)
+                        && ($prevBlock[0] != 'list'
+                            || ($prevBlock[3][0] == $nextBlock[3][0] && $prevBlock[3][1] == $nextBlock[3][1]))) {
                         // combine 3 blocks
                         $blocks[$key - 1] = array(
-                            $prevBlock[0],  $prevBlock[1],  $nextBlock[2],  NULL
+                            $prevBlock[0],  $prevBlock[1],  $nextBlock[2], $prevBlock[3] ?? null
                         );
                         array_splice($blocks, $key, 2);
 
@@ -1234,7 +1241,7 @@ function ($result, $line) {
      */
     private function parseCode(array $lines, array $parts, $start)
     {
-        list ($blank, $lang) = $parts;
+        [$blank, $lang] = $parts;
         $lang = trim($lang);
         $count = strlen($blank);
 
@@ -1243,7 +1250,7 @@ private function parseCode(array $lines, array $parts, $start)
         } else {
             $parts = explode(':', $lang);
             if (count($parts) > 1) {
-                list ($lang, $rel) = $parts;
+                [$lang, $rel] = $parts;
                 $lang = trim($lang);
                 $rel = trim($rel);
             }
@@ -1383,80 +1390,25 @@ private function parseQuote(array $lines, $value, $start)
     private function parseList(array $lines, $value, $start)
     {
         $html = '';
-        $minSpace = 99999;
-        $secondMinSpace = 99999;
-        $found = false;
-        $secondFound = false;
+        [$space, $type] = $value;
         $rows = array();
+        $last = 0;
 
-        // count levels
         foreach ($lines as $key => $line) {
-            if (preg_match("/^(\s*)((?:[0-9]+\.?)|\-|\+|\*)(\s+)(.*)$/i", $line, $matches)) {
-                $space = strlen($matches[1]);
-                $type = false !== strpos('+-*', $matches[2]) ? 'ul' : 'ol';
-                $minSpace = min($space, $minSpace);
-                $found = true;
-
-                if ($space > 0) {
-                    $secondMinSpace = min($space, $secondMinSpace);
-                    $secondFound = true;
-                }
-
-                $rows[] = array($space, $type, $line, $matches[4]);
-            } else {
-                $rows[] = $line;
-
-                if (preg_match("/^(\s*)/", $line, $matches)) {
-                    $space = strlen($matches[1]);
-
-                    if ($space > 0) {
-                        $secondMinSpace = min($space, $secondMinSpace);
-                        $secondFound = true;
-                    }
-                }
-            }
-        }
-
-        $minSpace = $found ? $minSpace : 0;
-        $secondMinSpace = $secondFound ? $secondMinSpace : $minSpace;
-
-        $lastType = '';
-        $leftLines = array();
-        $leftStart = 0;
-
-        foreach ($rows as $key => $row) {
-            if (is_array($row)) {
-                list ($space, $type, $line, $text) = $row;
-
-                if ($space != $minSpace) {
-                    $leftLines[] = preg_replace("/^\s{" . $secondMinSpace . "}/", '', $line);
-                } else {
-                    if (!empty($leftLines)) {
-                        $html .= "<li>" . $this->parse(implode("\n", $leftLines), true, $start + $leftStart) . "</li>";
-                    }
-
-                    if ($lastType != $type) {
-                        if (!empty($lastType)) {
-                            $html .= "</{$lastType}>";
-                        }
-
-                        $html .= "<{$type}>";
-                    }
-
-                    $leftStart = $key;
-                    $leftLines = array($text);
-                    $lastType = $type;
-                }
+            if (preg_match("/^(\s{" . $space . "})((?:[0-9]+\.?)|\-|\+|\*)(\s+)(.*)$/i", $line, $matches)) {
+                $rows[] = [$matches[4]];
+                $last = count($rows) - 1;
             } else {
-                $leftLines[] = preg_replace("/^\s{" . $secondMinSpace . "}/", '', $row);
+                $rows[$last][] = preg_replace("/^\s{" . $space . "}/", '', $line);
             }
         }
 
-        if (!empty($leftLines)) {
-            $html .= "<li>" . $this->parse(implode("\n", $leftLines), true, $start + $leftStart) . "</li></{$lastType}>";
+        foreach ($rows as $row) {
+            $html .= "<li>" . $this->parse(implode("\n", $row), true, $start) . "</li>";
+            $start += count($row);
         }
 
-        return $html;
+        return "<{$type}>" . $html . "</{$type}>";
     }
 
     /**
@@ -1467,7 +1419,7 @@ private function parseList(array $lines, $value, $start)
      */
     private function parseTable(array $lines, array $value, $start)
     {
-        list ($ignores, $aligns) = $value;
+        [$ignores, $aligns] = $value;
         $head = count($ignores) > 0 && array_sum($ignores) > 0;
 
         $html = '<table>';
@@ -1530,7 +1482,7 @@ private function parseTable(array $lines, array $value, $start)
                     . '" data-id="' . $this->_uniqid . '"' : '') . '>';
 
             foreach ($columns as $key => $column) {
-                list ($num, $text) = $column;
+                [$num, $text] = $column;
                 $tag = $head ? 'th' : 'td';
 
                 $html .= "<{$tag}";
@@ -1612,7 +1564,7 @@ private function parseNormal(array $lines, $inline, $start)
      */
     private function parseFootnote(array $lines, array $value)
     {
-        list($space, $note) = $value;
+        [$space, $note] = $value;
         $index = array_search($note, $this->_footnotes);
 
         if (false !== $index) {

From 9c3d8516032a45f16bc8ecd8ef919cebcf0a3903 Mon Sep 17 00:00:00 2001
From: joyqi <magike.net@gmail.com>
Date: Thu, 1 Apr 2021 23:08:14 +0800
Subject: [PATCH 03/29] update

---
 Parser.php    | 14 +++++++-------
 test/test.php |  8 ++++++--
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/Parser.php b/Parser.php
index c3828ba..43d41ac 100644
--- a/Parser.php
+++ b/Parser.php
@@ -311,7 +311,7 @@ public function markLine($start, $end = -1)
     /**
      * @param array $lines
      * @param $start
-     * @return string
+     * @return string[]
      */
     public function markLines(array $lines, $start)
     {
@@ -539,10 +539,10 @@ function ($matches) use ($self) {
         // autolink url
         if ($enableAutoLink) {
             $text = preg_replace_callback(
-                "/(^|[^\"])((https?):[\p{L}_a-z0-9-:\.\*\/%#;!@\?\+=~\|\,&\(\)\[\]]+)/iu",
+                "/(^|[^\"])((https?):\S+)($|[^\"])/i",
                 function ($matches) use ($self) {
                     $link = $self->call('parseLink', $matches[2]);
-                    return "{$matches[1]}<a href=\"{$matches[2]}\">{$link}</a>";
+                    return "{$matches[1]}<a href=\"{$matches[2]}\">{$link}</a>{$matches[4]}";
                 },
                 $text
             );
@@ -1394,7 +1394,7 @@ private function parseList(array $lines, $value, $start)
         $rows = array();
         $last = 0;
 
-        foreach ($lines as $key => $line) {
+        foreach ($lines as $line) {
             if (preg_match("/^(\s{" . $space . "})((?:[0-9]+\.?)|\-|\+|\*)(\s+)(.*)$/i", $line, $matches)) {
                 $rows[] = [$matches[4]];
                 $last = count($rows) - 1;
@@ -1408,7 +1408,7 @@ private function parseList(array $lines, $value, $start)
             $start += count($row);
         }
 
-        return "<{$type}>" . $html . "</{$type}>";
+        return "<{$type}>{$html}</{$type}>";
     }
 
     /**
@@ -1609,9 +1609,9 @@ private function parseHtml(array $lines, $type, $start)
      */
     public function cleanUrl($url)
     {
-        if (preg_match("/^\s*((http|https|ftp|mailto):[\p{L}_a-z0-9-:\.\*\/%#;!@\?\+=~\|\,&\(\)\[\]]+)/iu", $url, $matches)) {
+        if (preg_match("/^\s*((http|https|ftp|mailto):\S+)/i", $url, $matches)) {
             return $matches[1];
-        } else if (preg_match("/^\s*([\p{L}_a-z0-9-:\.\*\/%#!@\?\+=~\|\,&]+)/iu", $url, $matches)) {
+        } else if (preg_match("/^\s*(\S+)/i", $url, $matches)) {
             return $matches[1];
         } else {
             return '#';
diff --git a/test/test.php b/test/test.php
index 8580031..79dc95b 100644
--- a/test/test.php
+++ b/test/test.php
@@ -86,6 +86,10 @@ function test($title, $specs) {
         'ol'    => [
             '1. list',
             '<ol><li>list</li></ol>'
+        ],
+        'mix'   => [
+            "1. list 1\n2. list 2\n * aaa\n * bbb\n3. list 3\n- dddd\n- cccc",
+            ''
         ]
     ],
     'bugfix' => [
@@ -101,8 +105,8 @@ function test($title, $specs) {
     ],
     'url'=>[
         'exclamatory'=>[ // 感叹号
-            'http://sqlfiddle.com/#!9/ca126b/1',
-            '<p><a href="http://sqlfiddle.com/#!9/ca126b/1">http://sqlfiddle.com/#!9/ca126b/1</a></p>'
+            'http://sqlfiddle.com/#!9/ca126b/1中文 break',
+            '<p><a href="http://sqlfiddle.com/#!9/ca126b/1中文">http://sqlfiddle.com/#!9/ca126b/1中文</a> break</p>'
         ]
     ]
 ]);

From 2c96b51042dc90fd4c6f2b90c8602a70f5e43936 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Fri, 2 Apr 2021 11:26:18 +0800
Subject: [PATCH 04/29] add cli

---
 cli.php | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 cli.php

diff --git a/cli.php b/cli.php
new file mode 100644
index 0000000..2e6cbbc
--- /dev/null
+++ b/cli.php
@@ -0,0 +1,42 @@
+<?php
+
+$read = false;
+$html = false;
+$line = false;
+$file = null;
+
+foreach ($argv as $val) {
+    if ($val == '-h') {
+        $html = true;
+        continue;
+    } elseif ($val == '-l') {
+        $line = true;
+        continue;
+    }
+
+    if ($read) {
+        $file = $val;
+        $read = false;
+    } elseif ($val == '-f') {
+        $read = true;
+    }
+}
+
+if (empty($file)) {
+    exit(1);
+}
+
+require_once __DIR__ . '/Parser.php';
+
+$parser = new \HyperDown\Parser();
+
+if ($html) {
+    $parser->enableHtml(true);
+}
+
+if ($line) {
+    $parser->enableLine(true);
+}
+
+$buff = file_get_contents($file);
+echo $parser->makeHtml($buff);
\ No newline at end of file

From 2934f69a6ed07ad52db0a08eaa916655fdbaa530 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Wed, 14 Apr 2021 14:17:44 +0800
Subject: [PATCH 05/29] fix code in list

---
 Parser.php | 71 ++++++++++++++++++++++++++++--------------------------
 1 file changed, 37 insertions(+), 34 deletions(-)

diff --git a/Parser.php b/Parser.php
index 43d41ac..74d3810 100644
--- a/Parser.php
+++ b/Parser.php
@@ -146,7 +146,7 @@ public function makeHtml($text)
         });
 
         foreach ($this->blockParsers as $parser) {
-            [$name] = $parser;
+            list($name) = $parser;
 
             if (isset($parser[2])) {
                 $this->_parsers[$name] = $parser[2];
@@ -258,7 +258,7 @@ private function parse($text, $inline = false, $offset = 0)
         }
 
         foreach ($blocks as $block) {
-            [$type, $start, $end, $value] = $block;
+            list($type, $start, $end, $value) = $block;
             $extract = array_slice($lines, $start, $end - $start + 1);
             $method = 'parse' . ucfirst($type);
 
@@ -694,14 +694,17 @@ private function parseBlock($text, &$lines)
     private function parseBlockList($block, $key, $line, &$state)
     {
         if ($this->isBlock('list') && !preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line)) {
-            if ($state['empty'] <= 1
+            if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line)) {
+                // ignore code
+                return true;
+            } elseif ($state['empty'] <= 1
                 && preg_match("/^(\s*)\S+/", $line, $matches)
                 && strlen($matches[1]) >= ($block[3][0] + $state['empty'])) {
 
                 $state['empty'] = 0;
                 $this->setBlock($key);
                 return false;
-            } else if (preg_match("/^(\s*)$/", $line) && $state['empty'] == 0) {
+            } elseif (preg_match("/^(\s*)$/", $line) && $state['empty'] == 0) {
                 $state['empty'] ++;
                 $this->setBlock($key);
                 return false;
@@ -734,9 +737,10 @@ private function parseBlockList($block, $key, $line, &$state)
      * @param $block
      * @param $key
      * @param $line
+     * @param $state
      * @return bool
      */
-    private function parseBlockCode($block, $key, $line)
+    private function parseBlockCode($block, $key, $line, $state)
     {
         if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line, $matches)) {
             if ($this->isBlock('code')) {
@@ -753,10 +757,9 @@ private function parseBlockCode($block, $key, $line)
                 $isAfterList = false;
 
                 if ($this->isBlock('list')) {
-                    $space = $block[3];
+                    $space = $block[3][0];
 
-                    $isAfterList = ($space > 0 && strlen($matches[1]) >= $space)
-                        || strlen($matches[1]) > $space;
+                    $isAfterList = strlen($matches[1]) >= $space + $state['empty'];
                 }
 
                 $this->startBlock('code', $key, array(
@@ -765,7 +768,7 @@ private function parseBlockCode($block, $key, $line)
             }
 
             return false;
-        } else if ($this->isBlock('code')) {
+        } elseif ($this->isBlock('code')) {
             $this->setBlock($key);
             return false;
         }
@@ -791,7 +794,7 @@ private function parseBlockShtml($block, $key, $line, &$state)
                 }
 
                 return false;
-            } else if ($this->isBlock('shtml')) {
+            } elseif ($this->isBlock('shtml')) {
                 $this->setBlock($key);
                 return false;
             }
@@ -814,7 +817,7 @@ private function parseBlockAhtml($block, $key, $line, &$state)
                 if ($this->isBlock('ahtml')) {
                     $this->setBlock($key);
                     return false;
-                } else if (empty($matches[2]) || $matches[2] != '/') {
+                } elseif (empty($matches[2]) || $matches[2] != '/') {
                     $this->startBlock('ahtml', $key);
                     preg_match_all("/<({$this->_blockHtmlTags})(\s+[^>]*)?>/i", $line, $allMatches);
                     $lastMatch = $allMatches[1][count($allMatches[0]) - 1];
@@ -826,14 +829,14 @@ private function parseBlockAhtml($block, $key, $line, &$state)
                     }
                     return false;
                 }
-            } else if (!!$state['html'] && strpos($line, "</{$state['html']}>") !== false) {
+            } elseif (!!$state['html'] && strpos($line, "</{$state['html']}>") !== false) {
                 $this->setBlock($key)->endBlock();
                 $state['html'] = false;
                 return false;
-            } else if ($this->isBlock('ahtml')) {
+            } elseif ($this->isBlock('ahtml')) {
                 $this->setBlock($key);
                 return false;
-            } else if (preg_match("/^\s*<!\-\-(.*?)\-\->\s*$/", $line, $matches)) {
+            } elseif (preg_match("/^\s*<!\-\-(.*?)\-\->\s*$/", $line, $matches)) {
                 $this->startBlock('ahtml', $key)->endBlock();
                 return false;
             }
@@ -858,7 +861,7 @@ private function parseBlockMath($block, $key, $line)
             }
 
             return false;
-        } else if ($this->isBlock('math')) {
+        } elseif ($this->isBlock('math')) {
             $this->setBlock($key);
             return false;
         }
@@ -883,7 +886,7 @@ private function parseBlockPre($block, $key, $line, &$state)
             }
 
             return false;
-        } else if ($this->isBlock('pre') && preg_match("/^\s*$/", $line)) {
+        } elseif ($this->isBlock('pre') && preg_match("/^\s*$/", $line)) {
             $this->setBlock($key);
             return false;
         }
@@ -907,7 +910,7 @@ private function parseBlockHtml($block, $key, $line, &$state)
             }
 
             return false;
-        } else if (preg_match("/<\/({$state['special']})>\s*$/i", $line, $matches)) {
+        } elseif (preg_match("/<\/({$state['special']})>\s*$/i", $line, $matches)) {
             $tag = strtolower($matches[1]);
 
             if ($this->isBlock('html', $tag)) {
@@ -916,7 +919,7 @@ private function parseBlockHtml($block, $key, $line, &$state)
             }
 
             return false;
-        } else if ($this->isBlock('html')) {
+        } elseif ($this->isBlock('html')) {
             $this->setBlock($key);
             return false;
         }
@@ -974,7 +977,7 @@ private function parseBlockQuote($block, $key, $line)
         if (preg_match("/^(\s*)>/", $line, $matches)) {
             if ($this->isBlock('list') && strlen($matches[1]) > 0) {
                 $this->setBlock($key);
-            } else if ($this->isBlock('quote')) {
+            } elseif ($this->isBlock('quote')) {
                 $this->setBlock($key);
             } else {
                 $this->startBlock('quote', $key);
@@ -1029,9 +1032,9 @@ private function parseBlockTable($block, $key, $line, &$state, $lines)
                     if (preg_match("/^\s*(:?)\-+(:?)\s*$/", $row, $matches)) {
                         if (!empty($matches[1]) && !empty($matches[2])) {
                             $align = 'center';
-                        } else if (!empty($matches[1])) {
+                        } elseif (!empty($matches[1])) {
                             $align = 'left';
-                        } else if (!empty($matches[2])) {
+                        } elseif (!empty($matches[2])) {
                             $align = 'right';
                         }
                     }
@@ -1145,14 +1148,14 @@ private function parseBlockDefault($block, $key, $line, &$state)
             } else {
                 $this->startBlock('normal', $key);
             }
-        } else if ($this->isBlock('table')) {
+        } elseif ($this->isBlock('table')) {
             if (false !== strpos($line, '|')) {
                 $block[3][2] ++;
                 $this->setBlock($key, $block[3]);
             } else {
                 $this->startBlock('normal', $key);
             }
-        } else if ($this->isBlock('quote')) {
+        } elseif ($this->isBlock('quote')) {
             if (!preg_match("/^(\s*)$/", $line)) { // empty line
                 $this->setBlock($key);
             } else {
@@ -1186,7 +1189,7 @@ private function optimizeBlocks(array $blocks, array $lines)
             $prevBlock = isset($blocks[$key - 1]) ? $blocks[$key - 1] : NULL;
             $nextBlock = isset($blocks[$key + 1]) ? $blocks[$key + 1] : NULL;
 
-            [$type, $from, $to] = $block;
+            list($type, $from, $to) = $block;
 
             if ('pre' == $type) {
                 $isEmpty = array_reduce(
@@ -1241,7 +1244,7 @@ function ($result, $line) {
      */
     private function parseCode(array $lines, array $parts, $start)
     {
-        [$blank, $lang] = $parts;
+        list($blank, $lang) = $parts;
         $lang = trim($lang);
         $count = strlen($blank);
 
@@ -1250,7 +1253,7 @@ private function parseCode(array $lines, array $parts, $start)
         } else {
             $parts = explode(':', $lang);
             if (count($parts) > 1) {
-                [$lang, $rel] = $parts;
+                list($lang, $rel) = $parts;
                 $lang = trim($lang);
                 $rel = trim($rel);
             }
@@ -1390,7 +1393,7 @@ private function parseQuote(array $lines, $value, $start)
     private function parseList(array $lines, $value, $start)
     {
         $html = '';
-        [$space, $type] = $value;
+        list($space, $type) = $value;
         $rows = array();
         $last = 0;
 
@@ -1419,7 +1422,7 @@ private function parseList(array $lines, $value, $start)
      */
     private function parseTable(array $lines, array $value, $start)
     {
-        [$ignores, $aligns] = $value;
+        list($ignores, $aligns) = $value;
         $head = count($ignores) > 0 && array_sum($ignores) > 0;
 
         $html = '<table>';
@@ -1464,7 +1467,7 @@ private function parseTable(array $lines, array $value, $start)
                     $columns[$last] = array(
                         isset($columns[$last]) ? $columns[$last][0] + 1 : 1,  $row
                     );
-                } else if (isset($columns[$last])) {
+                } elseif (isset($columns[$last])) {
                     $columns[$last][0] ++;
                 } else {
                     $columns[0] = array(1, $row);
@@ -1473,7 +1476,7 @@ private function parseTable(array $lines, array $value, $start)
 
             if ($head) {
                 $html .= '<thead>';
-            } else if ($body) {
+            } elseif ($body) {
                 $html .= '<tbody>';
             }
 
@@ -1482,7 +1485,7 @@ private function parseTable(array $lines, array $value, $start)
                     . '" data-id="' . $this->_uniqid . '"' : '') . '>';
 
             foreach ($columns as $key => $column) {
-                [$num, $text] = $column;
+                list($num, $text) = $column;
                 $tag = $head ? 'th' : 'td';
 
                 $html .= "<{$tag}";
@@ -1501,7 +1504,7 @@ private function parseTable(array $lines, array $value, $start)
 
             if ($head) {
                 $html .= '</thead>';
-            } else if ($body) {
+            } elseif ($body) {
                 $body = false;
             }
         }
@@ -1564,7 +1567,7 @@ private function parseNormal(array $lines, $inline, $start)
      */
     private function parseFootnote(array $lines, array $value)
     {
-        [$space, $note] = $value;
+        list($space, $note) = $value;
         $index = array_search($note, $this->_footnotes);
 
         if (false !== $index) {
@@ -1611,7 +1614,7 @@ public function cleanUrl($url)
     {
         if (preg_match("/^\s*((http|https|ftp|mailto):\S+)/i", $url, $matches)) {
             return $matches[1];
-        } else if (preg_match("/^\s*(\S+)/i", $url, $matches)) {
+        } elseif (preg_match("/^\s*(\S+)/i", $url, $matches)) {
             return $matches[1];
         } else {
             return '#';

From 79e6e4a0e72f074b1260ec4046013e239c5be11b Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Tue, 20 Apr 2021 11:47:37 +0800
Subject: [PATCH 06/29] fix ol start number

---
 Parser.php | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/Parser.php b/Parser.php
index 74d3810..2c58c78 100644
--- a/Parser.php
+++ b/Parser.php
@@ -1395,10 +1395,19 @@ private function parseList(array $lines, $value, $start)
         $html = '';
         list($space, $type) = $value;
         $rows = array();
+        $suffix = '';
         $last = 0;
 
-        foreach ($lines as $line) {
+        foreach ($lines as $key => $line) {
             if (preg_match("/^(\s{" . $space . "})((?:[0-9]+\.?)|\-|\+|\*)(\s+)(.*)$/i", $line, $matches)) {
+                if ($type == 'ol' && $key == 0) {
+                    $start = intval($matches[2]);
+
+                    if ($start != 1) {
+                        $suffix = ' start="' . $start . '"';
+                    }
+                }
+
                 $rows[] = [$matches[4]];
                 $last = count($rows) - 1;
             } else {
@@ -1411,7 +1420,7 @@ private function parseList(array $lines, $value, $start)
             $start += count($row);
         }
 
-        return "<{$type}>{$html}</{$type}>";
+        return "<{$type}{$suffix}>{$html}</{$type}>";
     }
 
     /**

From c583acb6b7667c18ade4a5be6951f0f5956e6f0f Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Tue, 20 Apr 2021 17:28:51 +0800
Subject: [PATCH 07/29] fix list + pre

---
 Parser.php | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/Parser.php b/Parser.php
index 2c58c78..857d3f2 100644
--- a/Parser.php
+++ b/Parser.php
@@ -713,18 +713,19 @@ private function parseBlockList($block, $key, $line, &$state)
 
         if (preg_match("/^(\s*)((?:[0-9]+\.)|\-|\+|\*)\s+/i", $line, $matches)) {
             $space = strlen($matches[1]);
+            $tab = strlen($matches[0]) - $space;
             $state['empty'] = 0;
             $type = false !== strpos('+-*', $matches[2]) ? 'ul' : 'ol';
 
             // opened
             if ($this->isBlock('list')) {
                 if ($space < $block[3][0] || ($space == $block[3][0] && $type != $block[3][1])) {
-                    $this->startBlock('list', $key, [$space, $type]);
+                    $this->startBlock('list', $key, [$space, $type, $tab]);
                 } else {
                     $this->setBlock($key);
                 }
             } else {
-                $this->startBlock('list', $key, [$space, $type]);
+                $this->startBlock('list', $key, [$space, $type, $tab]);
             }
 
             return false;
@@ -1393,7 +1394,7 @@ private function parseQuote(array $lines, $value, $start)
     private function parseList(array $lines, $value, $start)
     {
         $html = '';
-        list($space, $type) = $value;
+        list($space, $type, $tab) = $value;
         $rows = array();
         $suffix = '';
         $last = 0;
@@ -1411,7 +1412,7 @@ private function parseList(array $lines, $value, $start)
                 $rows[] = [$matches[4]];
                 $last = count($rows) - 1;
             } else {
-                $rows[$last][] = preg_replace("/^\s{" . $space . "}/", '', $line);
+                $rows[$last][] = preg_replace("/^\s{" . ($tab + $space) . "}/", '', $line);
             }
         }
 

From 7b1d0dc093b7654adeaeef6caa5ae5c0221a8778 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Wed, 21 Apr 2021 16:41:42 +0800
Subject: [PATCH 08/29] fix recursion code block

---
 Parser.php | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/Parser.php b/Parser.php
index 857d3f2..0484c81 100644
--- a/Parser.php
+++ b/Parser.php
@@ -741,10 +741,15 @@ private function parseBlockList($block, $key, $line, &$state)
      * @param $state
      * @return bool
      */
-    private function parseBlockCode($block, $key, $line, $state)
+    private function parseBlockCode($block, $key, $line, &$state)
     {
         if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line, $matches)) {
             if ($this->isBlock('code')) {
+                if ($state['code'] != $matches[2]) {
+                    $this->setBlock($key);
+                    return false;
+                }
+
                 $isAfterList = $block[3][2];
 
                 if ($isAfterList) {
@@ -763,6 +768,8 @@ private function parseBlockCode($block, $key, $line, $state)
                     $isAfterList = strlen($matches[1]) >= $space + $state['empty'];
                 }
 
+                $state['code'] = $matches[2];
+
                 $this->startBlock('code', $key, array(
                     $matches[1],  $matches[3],  $isAfterList
                 ));

From 6f68dbb77a389c3056a5f0dd29423684d4af3db1 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Tue, 15 Jun 2021 11:14:26 +0800
Subject: [PATCH 09/29] fix autolink

---
 Parser.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Parser.php b/Parser.php
index 0484c81..8e4e46e 100644
--- a/Parser.php
+++ b/Parser.php
@@ -539,10 +539,10 @@ function ($matches) use ($self) {
         // autolink url
         if ($enableAutoLink) {
             $text = preg_replace_callback(
-                "/(^|[^\"])((https?):\S+)($|[^\"])/i",
+                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*))($|[^\"])/i",
                 function ($matches) use ($self) {
                     $link = $self->call('parseLink', $matches[2]);
-                    return "{$matches[1]}<a href=\"{$matches[2]}\">{$link}</a>{$matches[4]}";
+                    return "{$matches[1]}<a href=\"{$matches[2]}\">{$link}</a>{$matches[5]}";
                 },
                 $text
             );
@@ -1629,9 +1629,9 @@ private function parseHtml(array $lines, $type, $start)
      */
     public function cleanUrl($url)
     {
-        if (preg_match("/^\s*((http|https|ftp|mailto):\S+)/i", $url, $matches)) {
+        if (preg_match("/^\s*(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*))/", $url, $matches)) {
             return $matches[1];
-        } elseif (preg_match("/^\s*(\S+)/i", $url, $matches)) {
+        } elseif (preg_match("/^\s*([-a-zA-Z0-9()@:%_\+.~#?&\/=]+)/i", $url, $matches)) {
             return $matches[1];
         } else {
             return '#';

From d28f3ece1afdf084cb27a0a95b8575ed87dd851c Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Tue, 15 Jun 2021 11:23:55 +0800
Subject: [PATCH 10/29] fix case ignore

---
 Parser.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Parser.php b/Parser.php
index 8e4e46e..ba30e0c 100644
--- a/Parser.php
+++ b/Parser.php
@@ -539,7 +539,7 @@ function ($matches) use ($self) {
         // autolink url
         if ($enableAutoLink) {
             $text = preg_replace_callback(
-                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*))($|[^\"])/i",
+                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*))($|[^\"])/",
                 function ($matches) use ($self) {
                     $link = $self->call('parseLink', $matches[2]);
                     return "{$matches[1]}<a href=\"{$matches[2]}\">{$link}</a>{$matches[5]}";
@@ -1631,7 +1631,7 @@ public function cleanUrl($url)
     {
         if (preg_match("/^\s*(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*))/", $url, $matches)) {
             return $matches[1];
-        } elseif (preg_match("/^\s*([-a-zA-Z0-9()@:%_\+.~#?&\/=]+)/i", $url, $matches)) {
+        } elseif (preg_match("/^\s*([-a-zA-Z0-9()@:%_\+.~#?&\/=]+)/", $url, $matches)) {
             return $matches[1];
         } else {
             return '#';

From b300f70716a7d2939807caa5f37ecb9c7d0b67f6 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Tue, 22 Jun 2021 12:00:20 +0800
Subject: [PATCH 11/29] fix clean url

---
 Parser.php | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/Parser.php b/Parser.php
index ba30e0c..5ddf786 100644
--- a/Parser.php
+++ b/Parser.php
@@ -1629,13 +1629,13 @@ private function parseHtml(array $lines, $type, $start)
      */
     public function cleanUrl($url)
     {
-        if (preg_match("/^\s*(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*))/", $url, $matches)) {
-            return $matches[1];
-        } elseif (preg_match("/^\s*([-a-zA-Z0-9()@:%_\+.~#?&\/=]+)/", $url, $matches)) {
-            return $matches[1];
-        } else {
+        $url = preg_replace("/[\"'<>\s]/", '', $url);
+
+        if (preg_match("/^\w+:/i", $url) && !preg_match("/^https?:/i", $url)) {
             return '#';
         }
+
+        return $url;
     }
 
     /**

From d0be55e966c1f54bda809119344bb771937326fd Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Tue, 22 Jun 2021 17:25:45 +0800
Subject: [PATCH 12/29] fix mailto

---
 Parser.php | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/Parser.php b/Parser.php
index 5ddf786..ded6a8f 100644
--- a/Parser.php
+++ b/Parser.php
@@ -418,10 +418,10 @@ function ($matches) use ($self) {
 
         // link
         $text = preg_replace_callback(
-            "/<(https?:\/\/.+)>/i",
+            "/<(https?:\/\/.+|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,})>/i",
             function ($matches) use ($self) {
                 $url = $self->cleanUrl($matches[1]);
-                $link = $self->call('parseLink', $matches[1]);
+                $link = $self->call('parseLink', $url);
 
                 return $self->makeHolder(
                     "<a href=\"{$url}\">{$link}</a>"
@@ -539,10 +539,11 @@ function ($matches) use ($self) {
         // autolink url
         if ($enableAutoLink) {
             $text = preg_replace_callback(
-                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*))($|[^\"])/",
+                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,})($|[^\"])/",
                 function ($matches) use ($self) {
-                    $link = $self->call('parseLink', $matches[2]);
-                    return "{$matches[1]}<a href=\"{$matches[2]}\">{$link}</a>{$matches[5]}";
+                    $url = $self->cleanUrl($matches[2]);
+                    $link = $self->call('parseLink', $url);
+                    return "{$matches[1]}<a href=\"{$link}\">{$matches[2]}</a>{$matches[5]}";
                 },
                 $text
             );
@@ -1631,7 +1632,13 @@ public function cleanUrl($url)
     {
         $url = preg_replace("/[\"'<>\s]/", '', $url);
 
-        if (preg_match("/^\w+:/i", $url) && !preg_match("/^https?:/i", $url)) {
+        if (preg_match("/^(mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,}$/i", $url, $matches)) {
+            if (empty($matches[1])) {
+                $url = 'mailto:' . $url;
+            }
+        }
+
+        if (preg_match("/^\w+:/i", $url) && !preg_match("/^(https?|mailto):/i", $url)) {
             return '#';
         }
 

From 4e09d1a644b2c318baf1977f9169e035d324d896 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Fri, 25 Jun 2021 18:21:08 +0800
Subject: [PATCH 13/29] fix url

---
 Parser.php | 31 ++++++++++++++++++++++++-------
 1 file changed, 24 insertions(+), 7 deletions(-)

diff --git a/Parser.php b/Parser.php
index ded6a8f..551293d 100644
--- a/Parser.php
+++ b/Parser.php
@@ -477,9 +477,11 @@ function ($matches) use ($self) {
             function ($matches) use ($self) {
                 $escaped = htmlspecialchars($self->escapeBracket($matches[1]));
                 $url = $self->escapeBracket($matches[2]);
-                $url = $self->cleanUrl($url);
+                list ($url, $title) = $self->cleanUrl($url, true);
+                $title = empty($title)? $escaped : " title=\"{$title}\"";
+
                 return $self->makeHolder(
-                    "<img src=\"{$url}\" alt=\"{$escaped}\" title=\"{$escaped}\">"
+                    "<img src=\"{$url}\" alt=\"{$title}\" title=\"{$title}\">"
                 );
             },
             $text
@@ -507,8 +509,10 @@ function ($matches) use ($self) {
                     $self->escapeBracket($matches[1]),  '',  false, false
                 );
                 $url = $self->escapeBracket($matches[2]);
-                $url = $self->cleanUrl($url);
-                return $self->makeHolder("<a href=\"{$url}\">{$escaped}</a>");
+                list ($url, $title) = $self->cleanUrl($url, true);
+                $title = empty($title) ? '' : " title=\"{$title}\"";
+
+                return $self->makeHolder("<a href=\"{$url}\"{$title}>{$escaped}</a>");
             },
             $text
         );
@@ -1626,10 +1630,23 @@ private function parseHtml(array $lines, $type, $start)
 
     /**
      * @param $url
-     * @return string
+     * @param bool $parseTitle
+     *
+     * @return mixed
      */
-    public function cleanUrl($url)
+    public function cleanUrl($url, $parseTitle = false)
     {
+        $title = null;
+
+        if ($parseTitle) {
+            $pos = strpos($url, ' ');
+
+            if ($pos !== false) {
+                $title = htmlspecialchars(trim(substr($url, $pos + 1), ' "\''));
+                $url = substr($url, 0, $pos);
+            }
+        }
+
         $url = preg_replace("/[\"'<>\s]/", '', $url);
 
         if (preg_match("/^(mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,}$/i", $url, $matches)) {
@@ -1642,7 +1659,7 @@ public function cleanUrl($url)
             return '#';
         }
 
-        return $url;
+        return $parseTitle ? [$url, $title] : $url;
     }
 
     /**

From 90754e6162ba7eb6daa7ef4e8fb6121255eb9522 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Wed, 30 Jun 2021 16:18:07 +0800
Subject: [PATCH 14/29] fix parseLink

---
 Parser.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Parser.php b/Parser.php
index 551293d..ebaa983 100644
--- a/Parser.php
+++ b/Parser.php
@@ -547,7 +547,7 @@ function ($matches) use ($self) {
                 function ($matches) use ($self) {
                     $url = $self->cleanUrl($matches[2]);
                     $link = $self->call('parseLink', $url);
-                    return "{$matches[1]}<a href=\"{$link}\">{$matches[2]}</a>{$matches[5]}";
+                    return "{$matches[1]}<a href=\"{$url}\">{$link}</a>{$matches[5]}";
                 },
                 $text
             );

From f3be59d68da0095e145123411f2b7c1fd8c2aded Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Mon, 5 Jul 2021 15:34:58 +0800
Subject: [PATCH 15/29] fix HyperDown

---
 Parser.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Parser.php b/Parser.php
index ebaa983..da7a835 100644
--- a/Parser.php
+++ b/Parser.php
@@ -1637,6 +1637,7 @@ private function parseHtml(array $lines, $type, $start)
     public function cleanUrl($url, $parseTitle = false)
     {
         $title = null;
+        $url = trim($url);
 
         if ($parseTitle) {
             $pos = strpos($url, ' ');

From 747641f2a2b99e7360d495f03a20df5c4696cac8 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Mon, 12 Jul 2021 10:26:06 +0800
Subject: [PATCH 16/29] fix hyperdown

---
 Parser.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Parser.php b/Parser.php
index da7a835..73179ae 100644
--- a/Parser.php
+++ b/Parser.php
@@ -701,6 +701,7 @@ private function parseBlockList($block, $key, $line, &$state)
         if ($this->isBlock('list') && !preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line)) {
             if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line)) {
                 // ignore code
+                $state['empty'] = 0;
                 return true;
             } elseif ($state['empty'] <= 1
                 && preg_match("/^(\s*)\S+/", $line, $matches)

From d58406a85ecb205aee1686fb2bb82183b1f9ef7c Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Mon, 12 Jul 2021 17:54:07 +0800
Subject: [PATCH 17/29] fix list spacing

---
 Parser.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Parser.php b/Parser.php
index 73179ae..a50b4cb 100644
--- a/Parser.php
+++ b/Parser.php
@@ -701,7 +701,6 @@ private function parseBlockList($block, $key, $line, &$state)
         if ($this->isBlock('list') && !preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line)) {
             if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line)) {
                 // ignore code
-                $state['empty'] = 0;
                 return true;
             } elseif ($state['empty'] <= 1
                 && preg_match("/^(\s*)\S+/", $line, $matches)
@@ -759,6 +758,7 @@ private function parseBlockCode($block, $key, $line, &$state)
                 $isAfterList = $block[3][2];
 
                 if ($isAfterList) {
+                    $state['empty'] = 0;
                     $this->combineBlock()
                         ->setBlock($key);
                 } else {

From dfadb9211cd087dfb1b2d662225b000b3c47802b Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Tue, 20 Jul 2021 16:08:55 +0800
Subject: [PATCH 18/29] fix autolink

---
 Parser.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Parser.php b/Parser.php
index a50b4cb..67761d1 100644
--- a/Parser.php
+++ b/Parser.php
@@ -546,7 +546,7 @@ function ($matches) use ($self) {
                 "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,})($|[^\"])/",
                 function ($matches) use ($self) {
                     $url = $self->cleanUrl($matches[2]);
-                    $link = $self->call('parseLink', $url);
+                    $link = $self->call('parseLink', $matches[2]);
                     return "{$matches[1]}<a href=\"{$url}\">{$link}</a>{$matches[5]}";
                 },
                 $text

From 6d3c80c4448847a0ea823bb910fef329ce90767c Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Wed, 21 Jul 2021 14:40:32 +0800
Subject: [PATCH 19/29] fix autolink protocol

---
 Parser.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Parser.php b/Parser.php
index 67761d1..bee367f 100644
--- a/Parser.php
+++ b/Parser.php
@@ -543,7 +543,7 @@ function ($matches) use ($self) {
         // autolink url
         if ($enableAutoLink) {
             $text = preg_replace_callback(
-                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,})($|[^\"])/",
+                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,})($|[^\"])/",
                 function ($matches) use ($self) {
                     $url = $self->cleanUrl($matches[2]);
                     $link = $self->call('parseLink', $matches[2]);

From a8024055032d2b935b2521fddb1c5564a85b7dac Mon Sep 17 00:00:00 2001
From: joyqi <magike.net@gmail.com>
Date: Sun, 8 Aug 2021 12:33:34 +0800
Subject: [PATCH 20/29] fix email

---
 Parser.php | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Parser.php b/Parser.php
index bee367f..fa3108e 100644
--- a/Parser.php
+++ b/Parser.php
@@ -418,7 +418,7 @@ function ($matches) use ($self) {
 
         // link
         $text = preg_replace_callback(
-            "/<(https?:\/\/.+|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,})>/i",
+            "/<(https?:\/\/.+|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+(?:\.[a-z]{2,})+)>/i",
             function ($matches) use ($self) {
                 $url = $self->cleanUrl($matches[1]);
                 $link = $self->call('parseLink', $url);
@@ -543,7 +543,7 @@ function ($matches) use ($self) {
         // autolink url
         if ($enableAutoLink) {
             $text = preg_replace_callback(
-                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,})($|[^\"])/",
+                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+(?:\.[a-z]{2,})+)($|[^\"])/",
                 function ($matches) use ($self) {
                     $url = $self->cleanUrl($matches[2]);
                     $link = $self->call('parseLink', $matches[2]);
@@ -1651,7 +1651,7 @@ public function cleanUrl($url, $parseTitle = false)
 
         $url = preg_replace("/[\"'<>\s]/", '', $url);
 
-        if (preg_match("/^(mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,}$/i", $url, $matches)) {
+        if (preg_match("/^(mailto:)?[_a-z0-9-\.\+]+@[_\w-]+(?:\.[a-z]{2,})+$/i", $url, $matches)) {
             if (empty($matches[1])) {
                 $url = 'mailto:' . $url;
             }

From dbcdaf1eb58a49c0d5805785329def95403d932b Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Thu, 19 Aug 2021 15:07:08 +0800
Subject: [PATCH 21/29] reformat code

---
 Parser.php | 415 +++++++++++++++++++++++++++++------------------------
 1 file changed, 230 insertions(+), 185 deletions(-)

diff --git a/Parser.php b/Parser.php
index bee367f..0f579a2 100644
--- a/Parser.php
+++ b/Parser.php
@@ -16,14 +16,14 @@ class Parser
      *
      * @var string
      */
-    public $_commonWhiteList = 'kbd|b|i|strong|em|sup|sub|br|code|del|a|hr|small';
+    private $_commonWhiteList = 'kbd|b|i|strong|em|sup|sub|br|code|del|a|hr|small';
 
     /**
      * html tags
      *
      * @var string
      */
-    public $_blockHtmlTags = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption|svg|script|noscript';
+    private $_blockHtmlTags = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption|svg|script|noscript';
 
     /**
      * _specialWhiteList
@@ -31,48 +31,48 @@ class Parser
      * @var mixed
      * @access private
      */
-    public $_specialWhiteList = array(
-        'table'  =>  'table|tbody|thead|tfoot|tr|td|th'
-    );
+    private $_specialWhiteList = [
+        'table' => 'table|tbody|thead|tfoot|tr|td|th'
+    ];
 
     /**
      * _footnotes
      *
      * @var array
      */
-    public $_footnotes;
+    private $_footnotes;
 
     /**
      * @var bool
      */
-    public $_html = false;
+    private $_html = false;
 
     /**
      * @var bool
      */
-    public $_line = false;
+    private $_line = false;
 
     /**
      * @var array
      */
-    public $blockParsers = array(
-        array('code', 10),
-        array('shtml', 20),
-        array('pre', 30),
-        array('ahtml', 40),
-        array('shr', 50),
-        array('list', 60),
-        array('math', 70),
-        array('html', 80),
-        array('footnote', 90),
-        array('definition', 100),
-        array('quote', 110),
-        array('table', 120),
-        array('sh', 130),
-        array('mh', 140),
-        array('dhr', 150),
-        array('default', 9999)
-    );
+    private $blockParsers = [
+        ['code', 10],
+        ['shtml', 20],
+        ['pre', 30],
+        ['ahtml', 40],
+        ['shr', 50],
+        ['list', 60],
+        ['math', 70],
+        ['html', 80],
+        ['footnote', 90],
+        ['definition', 100],
+        ['quote', 110],
+        ['table', 120],
+        ['sh', 130],
+        ['mh', 140],
+        ['dhr', 150],
+        ['default', 9999]
+    ];
 
     /**
      * _blocks
@@ -100,12 +100,12 @@ class Parser
      *
      * @var array
      */
-    public $_definitions;
+    private $_definitions;
 
     /**
      * @var array
      */
-    private $_hooks = array();
+    private $_hooks = [];
 
     /**
      * @var array
@@ -125,33 +125,34 @@ class Parser
     /**
      * @var array
      */
-    private $_parsers = array();
+    private $_parsers = [];
 
     /**
      * makeHtml
      *
      * @param mixed $text
+     *
      * @return string
      */
     public function makeHtml($text)
     {
-        $this->_footnotes = array();
-        $this->_definitions = array();
-        $this->_holders = array();
+        $this->_footnotes = [];
+        $this->_definitions = [];
+        $this->_holders = [];
         $this->_uniqid = md5(uniqid());
         $this->_id = 0;
 
         usort($this->blockParsers, function ($a, $b) {
-            return $a[1] < $b[1] ? -1 : 1;
+            return $a[1] < $b[1] ? - 1 : 1;
         });
 
         foreach ($this->blockParsers as $parser) {
-            list($name) = $parser;
+            [$name] = $parser;
 
             if (isset($parser[2])) {
                 $this->_parsers[$name] = $parser[2];
             } else {
-                $this->_parsers[$name] = array($this, 'parseBlock' . ucfirst($name));
+                $this->_parsers[$name] = [$this, 'parseBlock' . ucfirst($name)];
             }
         }
 
@@ -190,6 +191,7 @@ public function hook($type, $callback)
 
     /**
      * @param $str
+     *
      * @return string
      */
     public function makeHolder($str)
@@ -203,16 +205,18 @@ public function makeHolder($str)
 
     /**
      * @param $text
+     *
      * @return mixed
      */
     private function initText($text)
     {
-        $text = str_replace(array("\t", "\r"),  array('    ', ''),  $text);
+        $text = str_replace(["\t", "\r"], ['    ', ''], $text);
         return $text;
     }
 
     /**
      * @param $html
+     *
      * @return string
      */
     private function makeFootnotes($html)
@@ -245,6 +249,7 @@ private function makeFootnotes($html)
      * @param string $text
      * @param bool $inline
      * @param int $offset
+     *
      * @return string
      */
     private function parse($text, $inline = false, $offset = 0)
@@ -258,7 +263,7 @@ private function parse($text, $inline = false, $offset = 0)
         }
 
         foreach ($blocks as $block) {
-            list($type, $start, $end, $value) = $block;
+            [$type, $start, $end, $value] = $block;
             $extract = array_slice($lines, $start, $end - $start + 1);
             $method = 'parse' . ucfirst($type);
 
@@ -275,6 +280,7 @@ private function parse($text, $inline = false, $offset = 0)
     /**
      * @param $text
      * @param $clearHolders
+     *
      * @return string
      */
     private function releaseHolder($text, $clearHolders = true)
@@ -286,7 +292,7 @@ private function releaseHolder($text, $clearHolders = true)
         }
 
         if ($clearHolders) {
-            $this->_holders = array();
+            $this->_holders = [];
         }
 
         return $text;
@@ -295,9 +301,10 @@ private function releaseHolder($text, $clearHolders = true)
     /**
      * @param $start
      * @param int $end
+     *
      * @return string
      */
-    public function markLine($start, $end = -1)
+    private function markLine($start, $end = - 1)
     {
         if ($this->_line) {
             $end = $end < 0 ? $start : $end;
@@ -311,24 +318,25 @@ public function markLine($start, $end = -1)
     /**
      * @param array $lines
      * @param $start
+     *
      * @return string[]
      */
-    public function markLines(array $lines, $start)
+    private function markLines(array $lines, $start)
     {
-        $i = -1;
-        $self = $this;
+        $i = - 1;
 
-        return $this->_line ? array_map(function ($line) use ($self, $start, &$i) {
+        return $this->_line ? array_map(function ($line) use ($start, &$i) {
             $i ++;
-            return $self->markLine($start + $i) . $line;
+            return $this->markLine($start + $i) . $line;
         }, $lines) : $lines;
     }
 
     /**
      * @param $html
+     *
      * @return string
      */
-    public function optimizeLines($html)
+    private function optimizeLines($html)
     {
         $last = 0;
 
@@ -349,9 +357,10 @@ function ($matches) use (&$last) {
     /**
      * @param $type
      * @param $value
+     *
      * @return mixed
      */
-    public function call($type, $value)
+    private function call($type, $value)
     {
         if (empty($this->_hooks[$type])) {
             return $value;
@@ -375,20 +384,20 @@ public function call($type, $value)
      * @param string $whiteList
      * @param bool $clearHolders
      * @param bool $enableAutoLink
+     *
      * @return string
      */
-    public function parseInline($text, $whiteList = '', $clearHolders = true, $enableAutoLink = true)
+    private function parseInline($text, $whiteList = '', $clearHolders = true, $enableAutoLink = true)
     {
-        $self = $this;
         $text = $this->call('beforeParseInline', $text);
 
         // code
         $text = preg_replace_callback(
             "/(^|[^\\\])(`+)(.+?)\\2/",
-            function ($matches) use ($self) {
-                return  $matches[1] . $self->makeHolder(
-                    '<code>' . htmlspecialchars($matches[3]) . '</code>'
-                );
+            function ($matches) {
+                return $matches[1] . $this->makeHolder(
+                        '<code>' . htmlspecialchars($matches[3]) . '</code>'
+                    );
             },
             $text
         );
@@ -396,10 +405,10 @@ function ($matches) use ($self) {
         // mathjax
         $text = preg_replace_callback(
             "/(^|[^\\\])(\\$+)(.+?)\\2/",
-            function ($matches) use ($self) {
-                return  $matches[1] . $self->makeHolder(
-                    $matches[2] . htmlspecialchars($matches[3]) . $matches[2]
-                );
+            function ($matches) {
+                return $matches[1] . $this->makeHolder(
+                        $matches[2] . htmlspecialchars($matches[3]) . $matches[2]
+                    );
             },
             $text
         );
@@ -407,11 +416,11 @@ function ($matches) use ($self) {
         // escape
         $text = preg_replace_callback(
             "/\\\(.)/u",
-            function ($matches) use ($self) {
+            function ($matches) {
                 $prefix = preg_match("/^[-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]$/", $matches[1]) ? '' : '\\';
                 $escaped = htmlspecialchars($matches[1]);
                 $escaped = str_replace('$', '&dollar;', $escaped);
-                return  $self->makeHolder($prefix . $escaped);
+                return $this->makeHolder($prefix . $escaped);
             },
             $text
         );
@@ -419,11 +428,11 @@ function ($matches) use ($self) {
         // link
         $text = preg_replace_callback(
             "/<(https?:\/\/.+|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,})>/i",
-            function ($matches) use ($self) {
-                $url = $self->cleanUrl($matches[1]);
-                $link = $self->call('parseLink', $url);
+            function ($matches) {
+                $url = $this->cleanUrl($matches[1]);
+                $link = $this->call('parseLink', $url);
 
-                return $self->makeHolder(
+                return $this->makeHolder(
                     "<a href=\"{$url}\">{$link}</a>"
                 );
             },
@@ -433,38 +442,38 @@ function ($matches) use ($self) {
         // encode unsafe tags
         $text = preg_replace_callback(
             "/<(\/?)([a-z0-9-]+)(\s+[^>]*)?>/i",
-            function ($matches) use ($self, $whiteList) {
-                if ($self->_html || false !== stripos(
-                    '|' . $self->_commonWhiteList . '|' . $whiteList . '|', '|' . $matches[2] . '|'
-                )) {
-                    return $self->makeHolder($matches[0]);
+            function ($matches) use ($whiteList) {
+                if ($this->_html || false !== stripos(
+                        '|' . $this->_commonWhiteList . '|' . $whiteList . '|', '|' . $matches[2] . '|'
+                    )) {
+                    return $this->makeHolder($matches[0]);
                 } else {
-                    return $self->makeHolder(htmlspecialchars($matches[0]));
+                    return $this->makeHolder(htmlspecialchars($matches[0]));
                 }
             },
             $text
         );
 
         if ($this->_html) {
-            $text = preg_replace_callback("/<!\-\-(.*?)\-\->/", function ($matches) use ($self) {
-                return $self->makeHolder($matches[0]);
+            $text = preg_replace_callback("/<!\-\-(.*?)\-\->/", function ($matches) {
+                return $this->makeHolder($matches[0]);
             }, $text);
         }
 
-        $text = str_replace(array('<', '>'),  array('&lt;', '&gt;'),  $text);
+        $text = str_replace(['<', '>'], ['&lt;', '&gt;'], $text);
 
         // footnote
         $text = preg_replace_callback(
             "/\[\^((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
-            function ($matches) use ($self) {
-                $id = array_search($matches[1], $self->_footnotes);
+            function ($matches) {
+                $id = array_search($matches[1], $this->_footnotes);
 
                 if (false === $id) {
-                    $id = count($self->_footnotes) + 1;
-                    $self->_footnotes[$id] = $self->parseInline($matches[1], '', false);
+                    $id = count($this->_footnotes) + 1;
+                    $this->_footnotes[$id] = $this->parseInline($matches[1], '', false);
                 }
 
-                return $self->makeHolder(
+                return $this->makeHolder(
                     "<sup id=\"fnref-{$id}\"><a href=\"#fn-{$id}\" class=\"footnote-ref\">{$id}</a></sup>"
                 );
             },
@@ -474,13 +483,13 @@ function ($matches) use ($self) {
         // image
         $text = preg_replace_callback(
             "/!\[((?:[^\]]|\\\\\]|\\\\\[)*?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/",
-            function ($matches) use ($self) {
-                $escaped = htmlspecialchars($self->escapeBracket($matches[1]));
-                $url = $self->escapeBracket($matches[2]);
-                list ($url, $title) = $self->cleanUrl($url, true);
-                $title = empty($title)? $escaped : " title=\"{$title}\"";
+            function ($matches) {
+                $escaped = htmlspecialchars($this->escapeBracket($matches[1]));
+                $url = $this->escapeBracket($matches[2]);
+                [$url, $title] = $this->cleanUrl($url, true);
+                $title = empty($title) ? $escaped : " title=\"{$title}\"";
 
-                return $self->makeHolder(
+                return $this->makeHolder(
                     "<img src=\"{$url}\" alt=\"{$title}\" title=\"{$title}\">"
                 );
             },
@@ -489,14 +498,14 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/!\[((?:[^\]]|\\\\\]|\\\\\[)*?)\]\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
-            function ($matches) use ($self) {
-                $escaped = htmlspecialchars($self->escapeBracket($matches[1]));
+            function ($matches) {
+                $escaped = htmlspecialchars($this->escapeBracket($matches[1]));
 
-                $result = isset( $self->_definitions[$matches[2]] ) ?
-                    "<img src=\"{$self->_definitions[$matches[2]]}\" alt=\"{$escaped}\" title=\"{$escaped}\">"
+                $result = isset($this->_definitions[$matches[2]]) ?
+                    "<img src=\"{$this->_definitions[$matches[2]]}\" alt=\"{$escaped}\" title=\"{$escaped}\">"
                     : $escaped;
 
-                return $self->makeHolder($result);
+                return $this->makeHolder($result);
             },
             $text
         );
@@ -504,30 +513,30 @@ function ($matches) use ($self) {
         // link
         $text = preg_replace_callback(
             "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/",
-            function ($matches) use ($self) {
-                $escaped = $self->parseInline(
-                    $self->escapeBracket($matches[1]),  '',  false, false
+            function ($matches) {
+                $escaped = $this->parseInline(
+                    $this->escapeBracket($matches[1]), '', false, false
                 );
-                $url = $self->escapeBracket($matches[2]);
-                list ($url, $title) = $self->cleanUrl($url, true);
+                $url = $this->escapeBracket($matches[2]);
+                [$url, $title] = $this->cleanUrl($url, true);
                 $title = empty($title) ? '' : " title=\"{$title}\"";
 
-                return $self->makeHolder("<a href=\"{$url}\"{$title}>{$escaped}</a>");
+                return $this->makeHolder("<a href=\"{$url}\"{$title}>{$escaped}</a>");
             },
             $text
         );
 
         $text = preg_replace_callback(
             "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
-            function ($matches) use ($self) {
-                $escaped = $self->parseInline(
-                    $self->escapeBracket($matches[1]),  '',  false
+            function ($matches) {
+                $escaped = $this->parseInline(
+                    $this->escapeBracket($matches[1]), '', false
                 );
-                $result = isset( $self->_definitions[$matches[2]] ) ?
-                    "<a href=\"{$self->_definitions[$matches[2]]}\">{$escaped}</a>"
+                $result = isset($this->_definitions[$matches[2]]) ?
+                    "<a href=\"{$this->_definitions[$matches[2]]}\">{$escaped}</a>"
                     : $escaped;
 
-                return $self->makeHolder($result);
+                return $this->makeHolder($result);
             },
             $text
         );
@@ -544,9 +553,9 @@ function ($matches) use ($self) {
         if ($enableAutoLink) {
             $text = preg_replace_callback(
                 "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+\.[a-z]{2,})($|[^\"])/",
-                function ($matches) use ($self) {
-                    $url = $self->cleanUrl($matches[2]);
-                    $link = $self->call('parseLink', $matches[2]);
+                function ($matches) {
+                    $url = $this->cleanUrl($matches[2]);
+                    $link = $this->call('parseLink', $matches[2]);
                     return "{$matches[1]}<a href=\"{$url}\">{$link}</a>{$matches[5]}";
                 },
                 $text
@@ -563,17 +572,16 @@ function ($matches) use ($self) {
 
     /**
      * @param $text
+     *
      * @return mixed
      */
-    public function parseInlineCallback($text)
+    private function parseInlineCallback($text)
     {
-        $self = $this;
-
         $text = preg_replace_callback(
             "/(\*{3})(.+?)\\1/",
-            function ($matches) use ($self) {
-                return  '<strong><em>' .
-                    $self->parseInlineCallback($matches[2]) .
+            function ($matches) {
+                return '<strong><em>' .
+                    $this->parseInlineCallback($matches[2]) .
                     '</em></strong>';
             },
             $text
@@ -581,9 +589,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(\*{2})(.+?)\\1/",
-            function ($matches) use ($self) {
-                return  '<strong>' .
-                    $self->parseInlineCallback($matches[2]) .
+            function ($matches) {
+                return '<strong>' .
+                    $this->parseInlineCallback($matches[2]) .
                     '</strong>';
             },
             $text
@@ -591,9 +599,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(\*)(.+?)\\1/",
-            function ($matches) use ($self) {
-                return  '<em>' .
-                    $self->parseInlineCallback($matches[2]) .
+            function ($matches) {
+                return '<em>' .
+                    $this->parseInlineCallback($matches[2]) .
                     '</em>';
             },
             $text
@@ -601,9 +609,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(\s+|^)(_{3})(.+?)\\2(\s+|$)/",
-            function ($matches) use ($self) {
-                return  $matches[1] . '<strong><em>' .
-                    $self->parseInlineCallback($matches[3]) .
+            function ($matches) {
+                return $matches[1] . '<strong><em>' .
+                    $this->parseInlineCallback($matches[3]) .
                     '</em></strong>' . $matches[4];
             },
             $text
@@ -611,9 +619,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(\s+|^)(_{2})(.+?)\\2(\s+|$)/",
-            function ($matches) use ($self) {
-                return  $matches[1] . '<strong>' .
-                    $self->parseInlineCallback($matches[3]) .
+            function ($matches) {
+                return $matches[1] . '<strong>' .
+                    $this->parseInlineCallback($matches[3]) .
                     '</strong>' . $matches[4];
             },
             $text
@@ -621,9 +629,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(\s+|^)(_)(.+?)\\2(\s+|$)/",
-            function ($matches) use ($self) {
-                return  $matches[1] . '<em>' .
-                    $self->parseInlineCallback($matches[3]) .
+            function ($matches) {
+                return $matches[1] . '<em>' .
+                    $this->parseInlineCallback($matches[3]) .
                     '</em>' . $matches[4];
             },
             $text
@@ -631,9 +639,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(~{2})(.+?)\\1/",
-            function ($matches) use ($self) {
-                return  '<del>' .
-                    $self->parseInlineCallback($matches[2]) .
+            function ($matches) {
+                return '<del>' .
+                    $this->parseInlineCallback($matches[2]) .
                     '</del>';
             },
             $text
@@ -647,25 +655,26 @@ function ($matches) use ($self) {
      *
      * @param string $text
      * @param array $lines
+     *
      * @return array
      */
     private function parseBlock($text, &$lines)
     {
         $lines = explode("\n", $text);
-        $this->_blocks = array();
+        $this->_blocks = [];
         $this->_current = 'normal';
-        $this->_pos = -1;
+        $this->_pos = - 1;
 
-        $state = array(
-            'special'   =>  implode("|", array_keys($this->_specialWhiteList)),
-            'empty'     =>  0,
-            'html'      =>  false
-        );
+        $state = [
+            'special' => implode("|", array_keys($this->_specialWhiteList)),
+            'empty'   => 0,
+            'html'    => false
+        ];
 
         // analyze by line
         foreach ($lines as $key => $line) {
             $block = $this->getBlock();
-            $args = array($block, $key, $line, &$state, $lines);
+            $args = [$block, $key, $line, &$state, $lines];
 
             if ($this->_current != 'normal') {
                 $pass = call_user_func_array($this->_parsers[$this->_current], $args);
@@ -694,6 +703,7 @@ private function parseBlock($text, &$lines)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockList($block, $key, $line, &$state)
@@ -744,6 +754,7 @@ private function parseBlockList($block, $key, $line, &$state)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockCode($block, $key, $line, &$state)
@@ -776,9 +787,9 @@ private function parseBlockCode($block, $key, $line, &$state)
 
                 $state['code'] = $matches[2];
 
-                $this->startBlock('code', $key, array(
-                    $matches[1],  $matches[3],  $isAfterList
-                ));
+                $this->startBlock('code', $key, [
+                    $matches[1], $matches[3], $isAfterList
+                ]);
             }
 
             return false;
@@ -795,6 +806,7 @@ private function parseBlockCode($block, $key, $line, &$state)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockShtml($block, $key, $line, &$state)
@@ -822,6 +834,7 @@ private function parseBlockShtml($block, $key, $line, &$state)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockAhtml($block, $key, $line, &$state)
@@ -863,6 +876,7 @@ private function parseBlockAhtml($block, $key, $line, &$state)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockMath($block, $key, $line)
@@ -888,6 +902,7 @@ private function parseBlockMath($block, $key, $line)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockPre($block, $key, $line, &$state)
@@ -913,6 +928,7 @@ private function parseBlockPre($block, $key, $line, &$state)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockHtml($block, $key, $line, &$state)
@@ -945,15 +961,16 @@ private function parseBlockHtml($block, $key, $line, &$state)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockFootnote($block, $key, $line)
     {
         if (preg_match("/^\[\^((?:[^\]]|\\]|\\[)+?)\]:/", $line, $matches)) {
             $space = strlen($matches[0]) - 1;
-            $this->startBlock('footnote', $key, array(
+            $this->startBlock('footnote', $key, [
                 $space, $matches[1]
-            ));
+            ]);
 
             return false;
         }
@@ -965,6 +982,7 @@ private function parseBlockFootnote($block, $key, $line)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockDefinition($block, $key, $line)
@@ -984,6 +1002,7 @@ private function parseBlockDefinition($block, $key, $line)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockQuote($block, $key, $line)
@@ -1009,6 +1028,7 @@ private function parseBlockQuote($block, $key, $line)
      * @param $line
      * @param $state
      * @param $lines
+     *
      * @return bool
      */
     private function parseBlockTable($block, $key, $line, &$state, $lines)
@@ -1016,7 +1036,7 @@ private function parseBlockTable($block, $key, $line, &$state, $lines)
         if (preg_match("/^((?:(?:(?:\||\+)(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:[ :]*\-+[ :]*)(?:\||\+)(?:[ :]*\-+[ :]*))|(?:(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:\||\+)(?:[ :]*\-+[ :]*)))+)$/", $line, $matches)) {
             if ($this->isBlock('table')) {
                 $block[3][0][] = $block[3][2];
-                $block[3][2]++;
+                $block[3][2] ++;
                 $this->setBlock($key, $block[3]);
             } else {
                 $head = 0;
@@ -1034,12 +1054,12 @@ private function parseBlockTable($block, $key, $line, &$state, $lines)
                     $matches[1] = substr($matches[1], 1);
 
                     if ($matches[1][strlen($matches[1]) - 1] == '|') {
-                        $matches[1] = substr($matches[1], 0, -1);
+                        $matches[1] = substr($matches[1], 0, - 1);
                     }
                 }
 
                 $rows = preg_split("/(\+|\|)/", $matches[1]);
-                $aligns = array();
+                $aligns = [];
                 foreach ($rows as $row) {
                     $align = 'none';
 
@@ -1056,7 +1076,7 @@ private function parseBlockTable($block, $key, $line, &$state, $lines)
                     $aligns[] = $align;
                 }
 
-                $this->setBlock($key, array(array($head), $aligns, $head + 1));
+                $this->setBlock($key, [[$head], $aligns, $head + 1]);
             }
 
             return false;
@@ -1069,6 +1089,7 @@ private function parseBlockTable($block, $key, $line, &$state, $lines)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockSh($block, $key, $line)
@@ -1090,12 +1111,13 @@ private function parseBlockSh($block, $key, $line)
      * @param $line
      * @param $state
      * @param $lines
+     *
      * @return bool
      */
     private function parseBlockMh($block, $key, $line, &$state, $lines)
     {
         if (preg_match("/^\s*((=|-){2,})\s*$/", $line, $matches)
-                    && ($block && $block[0] == "normal" && !preg_match("/^\s*$/", $lines[$block[2]]))) {    // check if last line isn't empty
+            && ($block && $block[0] == "normal" && !preg_match("/^\s*$/", $lines[$block[2]]))) {    // check if last line isn't empty
             if ($this->isBlock('normal')) {
                 $this->backBlock(1, 'mh', $matches[1][0] == '=' ? 1 : 2)
                     ->setBlock($key)
@@ -1114,6 +1136,7 @@ private function parseBlockMh($block, $key, $line, &$state, $lines)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockShr($block, $key, $line)
@@ -1132,6 +1155,7 @@ private function parseBlockShr($block, $key, $line)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockDhr($block, $key, $line)
@@ -1151,6 +1175,7 @@ private function parseBlockDhr($block, $key, $line)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockDefault($block, $key, $line, &$state)
@@ -1189,6 +1214,7 @@ private function parseBlockDefault($block, $key, $line, &$state)
     /**
      * @param array $blocks
      * @param array $lines
+     *
      * @return array
      */
     private function optimizeBlocks(array $blocks, array $lines)
@@ -1200,10 +1226,10 @@ private function optimizeBlocks(array $blocks, array $lines)
             $moved = false;
 
             $block = &$blocks[$key];
-            $prevBlock = isset($blocks[$key - 1]) ? $blocks[$key - 1] : NULL;
-            $nextBlock = isset($blocks[$key + 1]) ? $blocks[$key + 1] : NULL;
+            $prevBlock = isset($blocks[$key - 1]) ? $blocks[$key - 1] : null;
+            $nextBlock = isset($blocks[$key + 1]) ? $blocks[$key + 1] : null;
 
-            list($type, $from, $to) = $block;
+            [$type, $from, $to] = $block;
 
             if ('pre' == $type) {
                 $isEmpty = array_reduce(
@@ -1221,7 +1247,7 @@ function ($result, $line) {
 
             if ('normal' == $type) {
                 // combine two blocks
-                $types = array('list', 'quote');
+                $types = ['list', 'quote'];
 
                 if ($from == $to && preg_match("/^\s*$/", $lines[$from])
                     && !empty($prevBlock) && !empty($nextBlock)) {
@@ -1229,9 +1255,9 @@ function ($result, $line) {
                         && ($prevBlock[0] != 'list'
                             || ($prevBlock[3][0] == $nextBlock[3][0] && $prevBlock[3][1] == $nextBlock[3][1]))) {
                         // combine 3 blocks
-                        $blocks[$key - 1] = array(
-                            $prevBlock[0],  $prevBlock[1],  $nextBlock[2], $prevBlock[3] ?? null
-                        );
+                        $blocks[$key - 1] = [
+                            $prevBlock[0], $prevBlock[1], $nextBlock[2], $prevBlock[3] ?? null
+                        ];
                         array_splice($blocks, $key, 2);
 
                         // do not move
@@ -1254,20 +1280,21 @@ function ($result, $line) {
      * @param array $lines
      * @param array $parts
      * @param int $start
+     *
      * @return string
      */
     private function parseCode(array $lines, array $parts, $start)
     {
-        list($blank, $lang) = $parts;
+        [$blank, $lang] = $parts;
         $lang = trim($lang);
         $count = strlen($blank);
 
         if (!preg_match("/^[_a-z0-9-\+\#\:\.]+$/i", $lang)) {
-            $lang = NULL;
+            $lang = null;
         } else {
             $parts = explode(':', $lang);
             if (count($parts) > 1) {
-                list($lang, $rel) = $parts;
+                [$lang, $rel] = $parts;
                 $lang = trim($lang);
                 $rel = trim($rel);
             }
@@ -1282,7 +1309,7 @@ private function parseCode(array $lines, array $parts, $start)
             }
 
             return htmlspecialchars($line);
-        }, array_slice($lines, 1, -1));
+        }, array_slice($lines, 1, - 1));
         $str = implode("\n", $this->markLines($lines, $start + 1));
 
         return $isEmpty ? '' :
@@ -1297,6 +1324,7 @@ private function parseCode(array $lines, array $parts, $start)
      * @param array $lines
      * @param mixed $value
      * @param int $start
+     *
      * @return string
      */
     private function parsePre(array $lines, $value, $start)
@@ -1315,6 +1343,7 @@ private function parsePre(array $lines, $value, $start)
      * @param array $lines
      * @param mixed $value
      * @param int $start
+     *
      * @return string
      */
     private function parseAhtml(array $lines, $value, $start)
@@ -1328,11 +1357,12 @@ private function parseAhtml(array $lines, $value, $start)
      * @param array $lines
      * @param mixed $value
      * @param int $start
+     *
      * @return string
      */
     private function parseShtml(array $lines, $value, $start)
     {
-        return trim(implode("\n", $this->markLines(array_slice($lines, 1, -1), $start + 1)));
+        return trim(implode("\n", $this->markLines(array_slice($lines, 1, - 1), $start + 1)));
     }
 
     /**
@@ -1342,6 +1372,7 @@ private function parseShtml(array $lines, $value, $start)
      * @param mixed $value
      * @param int $start
      * @param int $end
+     *
      * @return string
      */
     private function parseMath(array $lines, $value, $start, $end)
@@ -1356,6 +1387,7 @@ private function parseMath(array $lines, $value, $start, $end)
      * @param int $num
      * @param int $start
      * @param int $end
+     *
      * @return string
      */
     private function parseSh(array $lines, $num, $start, $end)
@@ -1371,6 +1403,7 @@ private function parseSh(array $lines, $num, $start, $end)
      * @param int $num
      * @param int $start
      * @param int $end
+     *
      * @return string
      */
     private function parseMh(array $lines, $num, $start, $end)
@@ -1384,6 +1417,7 @@ private function parseMh(array $lines, $num, $start, $end)
      * @param array $lines
      * @param mixed $value
      * @param int $start
+     *
      * @return string
      */
     private function parseQuote(array $lines, $value, $start)
@@ -1402,13 +1436,14 @@ private function parseQuote(array $lines, $value, $start)
      * @param array $lines
      * @param mixed $value
      * @param int $start
+     *
      * @return string
      */
     private function parseList(array $lines, $value, $start)
     {
         $html = '';
-        list($space, $type, $tab) = $value;
-        $rows = array();
+        [$space, $type, $tab] = $value;
+        $rows = [];
         $suffix = '';
         $last = 0;
 
@@ -1441,15 +1476,16 @@ private function parseList(array $lines, $value, $start)
      * @param array $lines
      * @param array $value
      * @param int $start
+     *
      * @return string
      */
     private function parseTable(array $lines, array $value, $start)
     {
-        list($ignores, $aligns) = $value;
+        [$ignores, $aligns] = $value;
         $head = count($ignores) > 0 && array_sum($ignores) > 0;
 
         $html = '<table>';
-        $body = $head ? NULL : true;
+        $body = $head ? null : true;
         $output = false;
 
         foreach ($lines as $key => $line) {
@@ -1469,7 +1505,7 @@ private function parseTable(array $lines, array $value, $start)
                 $line = substr($line, 1);
 
                 if ($line[strlen($line) - 1] == '|') {
-                    $line = substr($line, 0, -1);
+                    $line = substr($line, 0, - 1);
                 }
             }
 
@@ -1481,19 +1517,19 @@ private function parseTable(array $lines, array $value, $start)
                     return trim($row);
                 }
             }, explode('|', $line));
-            $columns = array();
-            $last = -1;
+            $columns = [];
+            $last = - 1;
 
             foreach ($rows as $row) {
                 if (strlen($row) > 0) {
                     $last ++;
-                    $columns[$last] = array(
-                        isset($columns[$last]) ? $columns[$last][0] + 1 : 1,  $row
-                    );
+                    $columns[$last] = [
+                        isset($columns[$last]) ? $columns[$last][0] + 1 : 1, $row
+                    ];
                 } elseif (isset($columns[$last])) {
                     $columns[$last][0] ++;
                 } else {
-                    $columns[0] = array(1, $row);
+                    $columns[0] = [1, $row];
                 }
             }
 
@@ -1508,7 +1544,7 @@ private function parseTable(array $lines, array $value, $start)
                     . '" data-id="' . $this->_uniqid . '"' : '') . '>';
 
             foreach ($columns as $key => $column) {
-                list($num, $text) = $column;
+                [$num, $text] = $column;
                 $tag = $head ? 'th' : 'td';
 
                 $html .= "<{$tag}";
@@ -1532,7 +1568,7 @@ private function parseTable(array $lines, array $value, $start)
             }
         }
 
-        if ($body !== NULL) {
+        if ($body !== null) {
             $html .= '</tbody>';
         }
 
@@ -1546,6 +1582,7 @@ private function parseTable(array $lines, array $value, $start)
      * @param array $lines
      * @param array $value
      * @param int $start
+     *
      * @return string
      */
     private function parseHr($lines, $value, $start)
@@ -1559,6 +1596,7 @@ private function parseHr($lines, $value, $start)
      * @param array $lines
      * @param bool $inline
      * @param int $start
+     *
      * @return string
      */
     private function parseNormal(array $lines, $inline, $start)
@@ -1586,11 +1624,12 @@ private function parseNormal(array $lines, $inline, $start)
      *
      * @param array $lines
      * @param array $value
+     *
      * @return string
      */
     private function parseFootnote(array $lines, array $value)
     {
-        list($space, $note) = $value;
+        [$space, $note] = $value;
         $index = array_search($note, $this->_footnotes);
 
         if (false !== $index) {
@@ -1617,6 +1656,7 @@ private function parseDefinition()
      * @param array $lines
      * @param string $type
      * @param int $start
+     *
      * @return string
      */
     private function parseHtml(array $lines, $type, $start)
@@ -1635,7 +1675,7 @@ private function parseHtml(array $lines, $type, $start)
      *
      * @return mixed
      */
-    public function cleanUrl($url, $parseTitle = false)
+    private function cleanUrl($url, $parseTitle = false)
     {
         $title = null;
         $url = trim($url);
@@ -1666,12 +1706,13 @@ public function cleanUrl($url, $parseTitle = false)
 
     /**
      * @param $str
+     *
      * @return mixed
      */
-    public function escapeBracket($str)
+    private function escapeBracket($str)
     {
         return str_replace(
-            array('\[', '\]', '\(', '\)'),  array('[', ']', '(', ')'),  $str
+            ['\[', '\]', '\(', '\)'], ['[', ']', '(', ')'], $str
         );
     }
 
@@ -1681,14 +1722,15 @@ public function escapeBracket($str)
      * @param mixed $type
      * @param mixed $start
      * @param mixed $value
+     *
      * @return $this
      */
-    private function startBlock($type, $start, $value = NULL)
+    private function startBlock($type, $start, $value = null)
     {
         $this->_pos ++;
         $this->_current = $type;
 
-        $this->_blocks[$this->_pos] = array($type, $start, $start, $value);
+        $this->_blocks[$this->_pos] = [$type, $start, $start, $value];
 
         return $this;
     }
@@ -1709,12 +1751,13 @@ private function endBlock()
      *
      * @param mixed $type
      * @param mixed $value
+     *
      * @return bool
      */
-    private function isBlock($type, $value = NULL)
+    private function isBlock($type, $value = null)
     {
         return $this->_current == $type
-            && (NULL === $value ? true : $this->_blocks[$this->_pos][3] == $value);
+            && (null === $value ? true : $this->_blocks[$this->_pos][3] == $value);
     }
 
     /**
@@ -1724,7 +1767,7 @@ private function isBlock($type, $value = NULL)
      */
     private function getBlock()
     {
-        return isset($this->_blocks[$this->_pos]) ? $this->_blocks[$this->_pos] : NULL;
+        return isset($this->_blocks[$this->_pos]) ? $this->_blocks[$this->_pos] : null;
     }
 
     /**
@@ -1732,15 +1775,16 @@ private function getBlock()
      *
      * @param mixed $to
      * @param mixed $value
+     *
      * @return $this
      */
-    private function setBlock($to = NULL, $value = NULL)
+    private function setBlock($to = null, $value = null)
     {
-        if (NULL !== $to) {
+        if (null !== $to) {
             $this->_blocks[$this->_pos][2] = $to;
         }
 
-        if (NULL !== $value) {
+        if (null !== $value) {
             $this->_blocks[$this->_pos][3] = $value;
         }
 
@@ -1753,9 +1797,10 @@ private function setBlock($to = NULL, $value = NULL)
      * @param mixed $step
      * @param mixed $type
      * @param mixed $value
+     *
      * @return $this
      */
-    private function backBlock($step, $type, $value = NULL)
+    private function backBlock($step, $type, $value = null)
     {
         if ($this->_pos < 0) {
             return $this->startBlock($type, 0, $value);
@@ -1769,9 +1814,9 @@ private function backBlock($step, $type, $value = NULL)
         }
 
         $this->_current = $type;
-        $this->_blocks[$this->_pos] = array(
-            $type,  $last - $step + 1,  $last,  $value
-        );
+        $this->_blocks[$this->_pos] = [
+            $type, $last - $step + 1, $last, $value
+        ];
 
         return $this;
     }

From ab2ba0712f2f409eddd54d27935b6ff1c08d3167 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Thu, 19 Aug 2021 15:13:21 +0800
Subject: [PATCH 22/29] fix autolink

---
 Parser.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Parser.php b/Parser.php
index fa3108e..11307c4 100644
--- a/Parser.php
+++ b/Parser.php
@@ -543,7 +543,7 @@ function ($matches) use ($self) {
         // autolink url
         if ($enableAutoLink) {
             $text = preg_replace_callback(
-                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+(?:\.[a-z]{2,})+)($|[^\"])/",
+                "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\b([-a-zA-Z0-9@:%_\+.~#?&\/=]*)|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+(?:\.[a-z]{2,})+)($|[^\"])/",
                 function ($matches) use ($self) {
                     $url = $self->cleanUrl($matches[2]);
                     $link = $self->call('parseLink', $matches[2]);

From f540cf88c00402c62fd4ad88d13a445be00af37a Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Thu, 19 Aug 2021 15:20:58 +0800
Subject: [PATCH 23/29] remove public

---
 Parser.php | 162 ++++++++++++++++++++++++++---------------------------
 1 file changed, 79 insertions(+), 83 deletions(-)

diff --git a/Parser.php b/Parser.php
index 11307c4..932b467 100644
--- a/Parser.php
+++ b/Parser.php
@@ -16,14 +16,14 @@ class Parser
      *
      * @var string
      */
-    public $_commonWhiteList = 'kbd|b|i|strong|em|sup|sub|br|code|del|a|hr|small';
+    private $_commonWhiteList = 'kbd|b|i|strong|em|sup|sub|br|code|del|a|hr|small';
 
     /**
      * html tags
      *
      * @var string
      */
-    public $_blockHtmlTags = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption|svg|script|noscript';
+    private $_blockHtmlTags = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption|svg|script|noscript';
 
     /**
      * _specialWhiteList
@@ -31,7 +31,7 @@ class Parser
      * @var mixed
      * @access private
      */
-    public $_specialWhiteList = array(
+    private $_specialWhiteList = array(
         'table'  =>  'table|tbody|thead|tfoot|tr|td|th'
     );
 
@@ -40,22 +40,22 @@ class Parser
      *
      * @var array
      */
-    public $_footnotes;
+    private $_footnotes;
 
     /**
      * @var bool
      */
-    public $_html = false;
+    private $_html = false;
 
     /**
      * @var bool
      */
-    public $_line = false;
+    private $_line = false;
 
     /**
      * @var array
      */
-    public $blockParsers = array(
+    private $blockParsers = array(
         array('code', 10),
         array('shtml', 20),
         array('pre', 30),
@@ -100,7 +100,7 @@ class Parser
      *
      * @var array
      */
-    public $_definitions;
+    private $_definitions;
 
     /**
      * @var array
@@ -297,7 +297,7 @@ private function releaseHolder($text, $clearHolders = true)
      * @param int $end
      * @return string
      */
-    public function markLine($start, $end = -1)
+    private function markLine($start, $end = -1)
     {
         if ($this->_line) {
             $end = $end < 0 ? $start : $end;
@@ -313,14 +313,13 @@ public function markLine($start, $end = -1)
      * @param $start
      * @return string[]
      */
-    public function markLines(array $lines, $start)
+    private function markLines(array $lines, $start)
     {
         $i = -1;
-        $self = $this;
 
-        return $this->_line ? array_map(function ($line) use ($self, $start, &$i) {
+        return $this->_line ? array_map(function ($line) use ($start, &$i) {
             $i ++;
-            return $self->markLine($start + $i) . $line;
+            return $this->markLine($start + $i) . $line;
         }, $lines) : $lines;
     }
 
@@ -328,7 +327,7 @@ public function markLines(array $lines, $start)
      * @param $html
      * @return string
      */
-    public function optimizeLines($html)
+    private function optimizeLines($html)
     {
         $last = 0;
 
@@ -351,7 +350,7 @@ function ($matches) use (&$last) {
      * @param $value
      * @return mixed
      */
-    public function call($type, $value)
+    private function call($type, $value)
     {
         if (empty($this->_hooks[$type])) {
             return $value;
@@ -377,16 +376,15 @@ public function call($type, $value)
      * @param bool $enableAutoLink
      * @return string
      */
-    public function parseInline($text, $whiteList = '', $clearHolders = true, $enableAutoLink = true)
+    private function parseInline($text, $whiteList = '', $clearHolders = true, $enableAutoLink = true)
     {
-        $self = $this;
         $text = $this->call('beforeParseInline', $text);
 
         // code
         $text = preg_replace_callback(
             "/(^|[^\\\])(`+)(.+?)\\2/",
-            function ($matches) use ($self) {
-                return  $matches[1] . $self->makeHolder(
+            function ($matches) {
+                return  $matches[1] . $this->makeHolder(
                     '<code>' . htmlspecialchars($matches[3]) . '</code>'
                 );
             },
@@ -396,8 +394,8 @@ function ($matches) use ($self) {
         // mathjax
         $text = preg_replace_callback(
             "/(^|[^\\\])(\\$+)(.+?)\\2/",
-            function ($matches) use ($self) {
-                return  $matches[1] . $self->makeHolder(
+            function ($matches) {
+                return  $matches[1] . $this->makeHolder(
                     $matches[2] . htmlspecialchars($matches[3]) . $matches[2]
                 );
             },
@@ -407,11 +405,11 @@ function ($matches) use ($self) {
         // escape
         $text = preg_replace_callback(
             "/\\\(.)/u",
-            function ($matches) use ($self) {
+            function ($matches) {
                 $prefix = preg_match("/^[-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]$/", $matches[1]) ? '' : '\\';
                 $escaped = htmlspecialchars($matches[1]);
                 $escaped = str_replace('$', '&dollar;', $escaped);
-                return  $self->makeHolder($prefix . $escaped);
+                return  $this->makeHolder($prefix . $escaped);
             },
             $text
         );
@@ -419,11 +417,11 @@ function ($matches) use ($self) {
         // link
         $text = preg_replace_callback(
             "/<(https?:\/\/.+|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+(?:\.[a-z]{2,})+)>/i",
-            function ($matches) use ($self) {
-                $url = $self->cleanUrl($matches[1]);
-                $link = $self->call('parseLink', $url);
+            function ($matches) {
+                $url = $this->cleanUrl($matches[1]);
+                $link = $this->call('parseLink', $url);
 
-                return $self->makeHolder(
+                return $this->makeHolder(
                     "<a href=\"{$url}\">{$link}</a>"
                 );
             },
@@ -433,21 +431,21 @@ function ($matches) use ($self) {
         // encode unsafe tags
         $text = preg_replace_callback(
             "/<(\/?)([a-z0-9-]+)(\s+[^>]*)?>/i",
-            function ($matches) use ($self, $whiteList) {
-                if ($self->_html || false !== stripos(
-                    '|' . $self->_commonWhiteList . '|' . $whiteList . '|', '|' . $matches[2] . '|'
+            function ($matches) use ($whiteList) {
+                if ($this->_html || false !== stripos(
+                    '|' . $this->_commonWhiteList . '|' . $whiteList . '|', '|' . $matches[2] . '|'
                 )) {
-                    return $self->makeHolder($matches[0]);
+                    return $this->makeHolder($matches[0]);
                 } else {
-                    return $self->makeHolder(htmlspecialchars($matches[0]));
+                    return $this->makeHolder(htmlspecialchars($matches[0]));
                 }
             },
             $text
         );
 
         if ($this->_html) {
-            $text = preg_replace_callback("/<!\-\-(.*?)\-\->/", function ($matches) use ($self) {
-                return $self->makeHolder($matches[0]);
+            $text = preg_replace_callback("/<!\-\-(.*?)\-\->/", function ($matches) {
+                return $this->makeHolder($matches[0]);
             }, $text);
         }
 
@@ -456,15 +454,15 @@ function ($matches) use ($self, $whiteList) {
         // footnote
         $text = preg_replace_callback(
             "/\[\^((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
-            function ($matches) use ($self) {
-                $id = array_search($matches[1], $self->_footnotes);
+            function ($matches) {
+                $id = array_search($matches[1], $this->_footnotes);
 
                 if (false === $id) {
-                    $id = count($self->_footnotes) + 1;
-                    $self->_footnotes[$id] = $self->parseInline($matches[1], '', false);
+                    $id = count($this->_footnotes) + 1;
+                    $this->_footnotes[$id] = $this->parseInline($matches[1], '', false);
                 }
 
-                return $self->makeHolder(
+                return $this->makeHolder(
                     "<sup id=\"fnref-{$id}\"><a href=\"#fn-{$id}\" class=\"footnote-ref\">{$id}</a></sup>"
                 );
             },
@@ -474,13 +472,13 @@ function ($matches) use ($self) {
         // image
         $text = preg_replace_callback(
             "/!\[((?:[^\]]|\\\\\]|\\\\\[)*?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/",
-            function ($matches) use ($self) {
-                $escaped = htmlspecialchars($self->escapeBracket($matches[1]));
-                $url = $self->escapeBracket($matches[2]);
-                list ($url, $title) = $self->cleanUrl($url, true);
+            function ($matches) {
+                $escaped = htmlspecialchars($this->escapeBracket($matches[1]));
+                $url = $this->escapeBracket($matches[2]);
+                list ($url, $title) = $this->cleanUrl($url, true);
                 $title = empty($title)? $escaped : " title=\"{$title}\"";
 
-                return $self->makeHolder(
+                return $this->makeHolder(
                     "<img src=\"{$url}\" alt=\"{$title}\" title=\"{$title}\">"
                 );
             },
@@ -489,14 +487,14 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/!\[((?:[^\]]|\\\\\]|\\\\\[)*?)\]\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
-            function ($matches) use ($self) {
-                $escaped = htmlspecialchars($self->escapeBracket($matches[1]));
+            function ($matches) {
+                $escaped = htmlspecialchars($this->escapeBracket($matches[1]));
 
-                $result = isset( $self->_definitions[$matches[2]] ) ?
-                    "<img src=\"{$self->_definitions[$matches[2]]}\" alt=\"{$escaped}\" title=\"{$escaped}\">"
+                $result = isset( $this->_definitions[$matches[2]] ) ?
+                    "<img src=\"{$this->_definitions[$matches[2]]}\" alt=\"{$escaped}\" title=\"{$escaped}\">"
                     : $escaped;
 
-                return $self->makeHolder($result);
+                return $this->makeHolder($result);
             },
             $text
         );
@@ -504,30 +502,30 @@ function ($matches) use ($self) {
         // link
         $text = preg_replace_callback(
             "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/",
-            function ($matches) use ($self) {
-                $escaped = $self->parseInline(
-                    $self->escapeBracket($matches[1]),  '',  false, false
+            function ($matches) {
+                $escaped = $this->parseInline(
+                    $this->escapeBracket($matches[1]),  '',  false, false
                 );
-                $url = $self->escapeBracket($matches[2]);
-                list ($url, $title) = $self->cleanUrl($url, true);
+                $url = $this->escapeBracket($matches[2]);
+                list ($url, $title) = $this->cleanUrl($url, true);
                 $title = empty($title) ? '' : " title=\"{$title}\"";
 
-                return $self->makeHolder("<a href=\"{$url}\"{$title}>{$escaped}</a>");
+                return $this->makeHolder("<a href=\"{$url}\"{$title}>{$escaped}</a>");
             },
             $text
         );
 
         $text = preg_replace_callback(
             "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
-            function ($matches) use ($self) {
-                $escaped = $self->parseInline(
-                    $self->escapeBracket($matches[1]),  '',  false
+            function ($matches) {
+                $escaped = $this->parseInline(
+                    $this->escapeBracket($matches[1]),  '',  false
                 );
-                $result = isset( $self->_definitions[$matches[2]] ) ?
-                    "<a href=\"{$self->_definitions[$matches[2]]}\">{$escaped}</a>"
+                $result = isset( $this->_definitions[$matches[2]] ) ?
+                    "<a href=\"{$this->_definitions[$matches[2]]}\">{$escaped}</a>"
                     : $escaped;
 
-                return $self->makeHolder($result);
+                return $this->makeHolder($result);
             },
             $text
         );
@@ -544,9 +542,9 @@ function ($matches) use ($self) {
         if ($enableAutoLink) {
             $text = preg_replace_callback(
                 "/(^|[^\"])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\b([-a-zA-Z0-9@:%_\+.~#?&\/=]*)|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+(?:\.[a-z]{2,})+)($|[^\"])/",
-                function ($matches) use ($self) {
-                    $url = $self->cleanUrl($matches[2]);
-                    $link = $self->call('parseLink', $matches[2]);
+                function ($matches) {
+                    $url = $this->cleanUrl($matches[2]);
+                    $link = $this->call('parseLink', $matches[2]);
                     return "{$matches[1]}<a href=\"{$url}\">{$link}</a>{$matches[5]}";
                 },
                 $text
@@ -565,15 +563,13 @@ function ($matches) use ($self) {
      * @param $text
      * @return mixed
      */
-    public function parseInlineCallback($text)
+    private function parseInlineCallback($text)
     {
-        $self = $this;
-
         $text = preg_replace_callback(
             "/(\*{3})(.+?)\\1/",
-            function ($matches) use ($self) {
+            function ($matches) {
                 return  '<strong><em>' .
-                    $self->parseInlineCallback($matches[2]) .
+                    $this->parseInlineCallback($matches[2]) .
                     '</em></strong>';
             },
             $text
@@ -581,9 +577,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(\*{2})(.+?)\\1/",
-            function ($matches) use ($self) {
+            function ($matches) {
                 return  '<strong>' .
-                    $self->parseInlineCallback($matches[2]) .
+                    $this->parseInlineCallback($matches[2]) .
                     '</strong>';
             },
             $text
@@ -591,9 +587,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(\*)(.+?)\\1/",
-            function ($matches) use ($self) {
+            function ($matches) {
                 return  '<em>' .
-                    $self->parseInlineCallback($matches[2]) .
+                    $this->parseInlineCallback($matches[2]) .
                     '</em>';
             },
             $text
@@ -601,9 +597,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(\s+|^)(_{3})(.+?)\\2(\s+|$)/",
-            function ($matches) use ($self) {
+            function ($matches) {
                 return  $matches[1] . '<strong><em>' .
-                    $self->parseInlineCallback($matches[3]) .
+                    $this->parseInlineCallback($matches[3]) .
                     '</em></strong>' . $matches[4];
             },
             $text
@@ -611,9 +607,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(\s+|^)(_{2})(.+?)\\2(\s+|$)/",
-            function ($matches) use ($self) {
+            function ($matches) {
                 return  $matches[1] . '<strong>' .
-                    $self->parseInlineCallback($matches[3]) .
+                    $this->parseInlineCallback($matches[3]) .
                     '</strong>' . $matches[4];
             },
             $text
@@ -621,9 +617,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(\s+|^)(_)(.+?)\\2(\s+|$)/",
-            function ($matches) use ($self) {
+            function ($matches) {
                 return  $matches[1] . '<em>' .
-                    $self->parseInlineCallback($matches[3]) .
+                    $this->parseInlineCallback($matches[3]) .
                     '</em>' . $matches[4];
             },
             $text
@@ -631,9 +627,9 @@ function ($matches) use ($self) {
 
         $text = preg_replace_callback(
             "/(~{2})(.+?)\\1/",
-            function ($matches) use ($self) {
+            function ($matches) {
                 return  '<del>' .
-                    $self->parseInlineCallback($matches[2]) .
+                    $this->parseInlineCallback($matches[2]) .
                     '</del>';
             },
             $text
@@ -1635,7 +1631,7 @@ private function parseHtml(array $lines, $type, $start)
      *
      * @return mixed
      */
-    public function cleanUrl($url, $parseTitle = false)
+    private function cleanUrl($url, $parseTitle = false)
     {
         $title = null;
         $url = trim($url);
@@ -1668,7 +1664,7 @@ public function cleanUrl($url, $parseTitle = false)
      * @param $str
      * @return mixed
      */
-    public function escapeBracket($str)
+    private function escapeBracket($str)
     {
         return str_replace(
             array('\[', '\]', '\(', '\)'),  array('[', ']', '(', ')'),  $str

From d457db5aa6e3e85b0b6530c4a6957b069d2a9956 Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Thu, 19 Aug 2021 15:23:01 +0800
Subject: [PATCH 24/29] reformat code

---
 Parser.php | 279 +++++++++++++++++++++++++++++++----------------------
 1 file changed, 164 insertions(+), 115 deletions(-)

diff --git a/Parser.php b/Parser.php
index 932b467..4b19c21 100644
--- a/Parser.php
+++ b/Parser.php
@@ -31,9 +31,9 @@ class Parser
      * @var mixed
      * @access private
      */
-    private $_specialWhiteList = array(
-        'table'  =>  'table|tbody|thead|tfoot|tr|td|th'
-    );
+    private $_specialWhiteList = [
+        'table' => 'table|tbody|thead|tfoot|tr|td|th'
+    ];
 
     /**
      * _footnotes
@@ -55,24 +55,24 @@ class Parser
     /**
      * @var array
      */
-    private $blockParsers = array(
-        array('code', 10),
-        array('shtml', 20),
-        array('pre', 30),
-        array('ahtml', 40),
-        array('shr', 50),
-        array('list', 60),
-        array('math', 70),
-        array('html', 80),
-        array('footnote', 90),
-        array('definition', 100),
-        array('quote', 110),
-        array('table', 120),
-        array('sh', 130),
-        array('mh', 140),
-        array('dhr', 150),
-        array('default', 9999)
-    );
+    private $blockParsers = [
+        ['code', 10],
+        ['shtml', 20],
+        ['pre', 30],
+        ['ahtml', 40],
+        ['shr', 50],
+        ['list', 60],
+        ['math', 70],
+        ['html', 80],
+        ['footnote', 90],
+        ['definition', 100],
+        ['quote', 110],
+        ['table', 120],
+        ['sh', 130],
+        ['mh', 140],
+        ['dhr', 150],
+        ['default', 9999]
+    ];
 
     /**
      * _blocks
@@ -105,7 +105,7 @@ class Parser
     /**
      * @var array
      */
-    private $_hooks = array();
+    private $_hooks = [];
 
     /**
      * @var array
@@ -125,33 +125,34 @@ class Parser
     /**
      * @var array
      */
-    private $_parsers = array();
+    private $_parsers = [];
 
     /**
      * makeHtml
      *
      * @param mixed $text
+     *
      * @return string
      */
     public function makeHtml($text)
     {
-        $this->_footnotes = array();
-        $this->_definitions = array();
-        $this->_holders = array();
+        $this->_footnotes = [];
+        $this->_definitions = [];
+        $this->_holders = [];
         $this->_uniqid = md5(uniqid());
         $this->_id = 0;
 
         usort($this->blockParsers, function ($a, $b) {
-            return $a[1] < $b[1] ? -1 : 1;
+            return $a[1] < $b[1] ? - 1 : 1;
         });
 
         foreach ($this->blockParsers as $parser) {
-            list($name) = $parser;
+            [$name] = $parser;
 
             if (isset($parser[2])) {
                 $this->_parsers[$name] = $parser[2];
             } else {
-                $this->_parsers[$name] = array($this, 'parseBlock' . ucfirst($name));
+                $this->_parsers[$name] = [$this, 'parseBlock' . ucfirst($name)];
             }
         }
 
@@ -190,6 +191,7 @@ public function hook($type, $callback)
 
     /**
      * @param $str
+     *
      * @return string
      */
     public function makeHolder($str)
@@ -203,16 +205,18 @@ public function makeHolder($str)
 
     /**
      * @param $text
+     *
      * @return mixed
      */
     private function initText($text)
     {
-        $text = str_replace(array("\t", "\r"),  array('    ', ''),  $text);
+        $text = str_replace(["\t", "\r"], ['    ', ''], $text);
         return $text;
     }
 
     /**
      * @param $html
+     *
      * @return string
      */
     private function makeFootnotes($html)
@@ -245,6 +249,7 @@ private function makeFootnotes($html)
      * @param string $text
      * @param bool $inline
      * @param int $offset
+     *
      * @return string
      */
     private function parse($text, $inline = false, $offset = 0)
@@ -258,7 +263,7 @@ private function parse($text, $inline = false, $offset = 0)
         }
 
         foreach ($blocks as $block) {
-            list($type, $start, $end, $value) = $block;
+            [$type, $start, $end, $value] = $block;
             $extract = array_slice($lines, $start, $end - $start + 1);
             $method = 'parse' . ucfirst($type);
 
@@ -275,6 +280,7 @@ private function parse($text, $inline = false, $offset = 0)
     /**
      * @param $text
      * @param $clearHolders
+     *
      * @return string
      */
     private function releaseHolder($text, $clearHolders = true)
@@ -286,7 +292,7 @@ private function releaseHolder($text, $clearHolders = true)
         }
 
         if ($clearHolders) {
-            $this->_holders = array();
+            $this->_holders = [];
         }
 
         return $text;
@@ -295,9 +301,10 @@ private function releaseHolder($text, $clearHolders = true)
     /**
      * @param $start
      * @param int $end
+     *
      * @return string
      */
-    private function markLine($start, $end = -1)
+    private function markLine($start, $end = - 1)
     {
         if ($this->_line) {
             $end = $end < 0 ? $start : $end;
@@ -311,11 +318,12 @@ private function markLine($start, $end = -1)
     /**
      * @param array $lines
      * @param $start
+     *
      * @return string[]
      */
     private function markLines(array $lines, $start)
     {
-        $i = -1;
+        $i = - 1;
 
         return $this->_line ? array_map(function ($line) use ($start, &$i) {
             $i ++;
@@ -325,6 +333,7 @@ private function markLines(array $lines, $start)
 
     /**
      * @param $html
+     *
      * @return string
      */
     private function optimizeLines($html)
@@ -348,6 +357,7 @@ function ($matches) use (&$last) {
     /**
      * @param $type
      * @param $value
+     *
      * @return mixed
      */
     private function call($type, $value)
@@ -374,6 +384,7 @@ private function call($type, $value)
      * @param string $whiteList
      * @param bool $clearHolders
      * @param bool $enableAutoLink
+     *
      * @return string
      */
     private function parseInline($text, $whiteList = '', $clearHolders = true, $enableAutoLink = true)
@@ -384,9 +395,9 @@ private function parseInline($text, $whiteList = '', $clearHolders = true, $enab
         $text = preg_replace_callback(
             "/(^|[^\\\])(`+)(.+?)\\2/",
             function ($matches) {
-                return  $matches[1] . $this->makeHolder(
-                    '<code>' . htmlspecialchars($matches[3]) . '</code>'
-                );
+                return $matches[1] . $this->makeHolder(
+                        '<code>' . htmlspecialchars($matches[3]) . '</code>'
+                    );
             },
             $text
         );
@@ -395,9 +406,9 @@ function ($matches) {
         $text = preg_replace_callback(
             "/(^|[^\\\])(\\$+)(.+?)\\2/",
             function ($matches) {
-                return  $matches[1] . $this->makeHolder(
-                    $matches[2] . htmlspecialchars($matches[3]) . $matches[2]
-                );
+                return $matches[1] . $this->makeHolder(
+                        $matches[2] . htmlspecialchars($matches[3]) . $matches[2]
+                    );
             },
             $text
         );
@@ -409,7 +420,7 @@ function ($matches) {
                 $prefix = preg_match("/^[-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]$/", $matches[1]) ? '' : '\\';
                 $escaped = htmlspecialchars($matches[1]);
                 $escaped = str_replace('$', '&dollar;', $escaped);
-                return  $this->makeHolder($prefix . $escaped);
+                return $this->makeHolder($prefix . $escaped);
             },
             $text
         );
@@ -433,8 +444,8 @@ function ($matches) {
             "/<(\/?)([a-z0-9-]+)(\s+[^>]*)?>/i",
             function ($matches) use ($whiteList) {
                 if ($this->_html || false !== stripos(
-                    '|' . $this->_commonWhiteList . '|' . $whiteList . '|', '|' . $matches[2] . '|'
-                )) {
+                        '|' . $this->_commonWhiteList . '|' . $whiteList . '|', '|' . $matches[2] . '|'
+                    )) {
                     return $this->makeHolder($matches[0]);
                 } else {
                     return $this->makeHolder(htmlspecialchars($matches[0]));
@@ -449,7 +460,7 @@ function ($matches) use ($whiteList) {
             }, $text);
         }
 
-        $text = str_replace(array('<', '>'),  array('&lt;', '&gt;'),  $text);
+        $text = str_replace(['<', '>'], ['&lt;', '&gt;'], $text);
 
         // footnote
         $text = preg_replace_callback(
@@ -475,8 +486,8 @@ function ($matches) {
             function ($matches) {
                 $escaped = htmlspecialchars($this->escapeBracket($matches[1]));
                 $url = $this->escapeBracket($matches[2]);
-                list ($url, $title) = $this->cleanUrl($url, true);
-                $title = empty($title)? $escaped : " title=\"{$title}\"";
+                [$url, $title] = $this->cleanUrl($url, true);
+                $title = empty($title) ? $escaped : " title=\"{$title}\"";
 
                 return $this->makeHolder(
                     "<img src=\"{$url}\" alt=\"{$title}\" title=\"{$title}\">"
@@ -490,7 +501,7 @@ function ($matches) {
             function ($matches) {
                 $escaped = htmlspecialchars($this->escapeBracket($matches[1]));
 
-                $result = isset( $this->_definitions[$matches[2]] ) ?
+                $result = isset($this->_definitions[$matches[2]]) ?
                     "<img src=\"{$this->_definitions[$matches[2]]}\" alt=\"{$escaped}\" title=\"{$escaped}\">"
                     : $escaped;
 
@@ -504,10 +515,10 @@ function ($matches) {
             "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/",
             function ($matches) {
                 $escaped = $this->parseInline(
-                    $this->escapeBracket($matches[1]),  '',  false, false
+                    $this->escapeBracket($matches[1]), '', false, false
                 );
                 $url = $this->escapeBracket($matches[2]);
-                list ($url, $title) = $this->cleanUrl($url, true);
+                [$url, $title] = $this->cleanUrl($url, true);
                 $title = empty($title) ? '' : " title=\"{$title}\"";
 
                 return $this->makeHolder("<a href=\"{$url}\"{$title}>{$escaped}</a>");
@@ -519,9 +530,9 @@ function ($matches) {
             "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
             function ($matches) {
                 $escaped = $this->parseInline(
-                    $this->escapeBracket($matches[1]),  '',  false
+                    $this->escapeBracket($matches[1]), '', false
                 );
-                $result = isset( $this->_definitions[$matches[2]] ) ?
+                $result = isset($this->_definitions[$matches[2]]) ?
                     "<a href=\"{$this->_definitions[$matches[2]]}\">{$escaped}</a>"
                     : $escaped;
 
@@ -561,6 +572,7 @@ function ($matches) {
 
     /**
      * @param $text
+     *
      * @return mixed
      */
     private function parseInlineCallback($text)
@@ -568,7 +580,7 @@ private function parseInlineCallback($text)
         $text = preg_replace_callback(
             "/(\*{3})(.+?)\\1/",
             function ($matches) {
-                return  '<strong><em>' .
+                return '<strong><em>' .
                     $this->parseInlineCallback($matches[2]) .
                     '</em></strong>';
             },
@@ -578,7 +590,7 @@ function ($matches) {
         $text = preg_replace_callback(
             "/(\*{2})(.+?)\\1/",
             function ($matches) {
-                return  '<strong>' .
+                return '<strong>' .
                     $this->parseInlineCallback($matches[2]) .
                     '</strong>';
             },
@@ -588,7 +600,7 @@ function ($matches) {
         $text = preg_replace_callback(
             "/(\*)(.+?)\\1/",
             function ($matches) {
-                return  '<em>' .
+                return '<em>' .
                     $this->parseInlineCallback($matches[2]) .
                     '</em>';
             },
@@ -598,7 +610,7 @@ function ($matches) {
         $text = preg_replace_callback(
             "/(\s+|^)(_{3})(.+?)\\2(\s+|$)/",
             function ($matches) {
-                return  $matches[1] . '<strong><em>' .
+                return $matches[1] . '<strong><em>' .
                     $this->parseInlineCallback($matches[3]) .
                     '</em></strong>' . $matches[4];
             },
@@ -608,7 +620,7 @@ function ($matches) {
         $text = preg_replace_callback(
             "/(\s+|^)(_{2})(.+?)\\2(\s+|$)/",
             function ($matches) {
-                return  $matches[1] . '<strong>' .
+                return $matches[1] . '<strong>' .
                     $this->parseInlineCallback($matches[3]) .
                     '</strong>' . $matches[4];
             },
@@ -618,7 +630,7 @@ function ($matches) {
         $text = preg_replace_callback(
             "/(\s+|^)(_)(.+?)\\2(\s+|$)/",
             function ($matches) {
-                return  $matches[1] . '<em>' .
+                return $matches[1] . '<em>' .
                     $this->parseInlineCallback($matches[3]) .
                     '</em>' . $matches[4];
             },
@@ -628,7 +640,7 @@ function ($matches) {
         $text = preg_replace_callback(
             "/(~{2})(.+?)\\1/",
             function ($matches) {
-                return  '<del>' .
+                return '<del>' .
                     $this->parseInlineCallback($matches[2]) .
                     '</del>';
             },
@@ -643,25 +655,26 @@ function ($matches) {
      *
      * @param string $text
      * @param array $lines
+     *
      * @return array
      */
     private function parseBlock($text, &$lines)
     {
         $lines = explode("\n", $text);
-        $this->_blocks = array();
+        $this->_blocks = [];
         $this->_current = 'normal';
-        $this->_pos = -1;
+        $this->_pos = - 1;
 
-        $state = array(
-            'special'   =>  implode("|", array_keys($this->_specialWhiteList)),
-            'empty'     =>  0,
-            'html'      =>  false
-        );
+        $state = [
+            'special' => implode("|", array_keys($this->_specialWhiteList)),
+            'empty'   => 0,
+            'html'    => false
+        ];
 
         // analyze by line
         foreach ($lines as $key => $line) {
             $block = $this->getBlock();
-            $args = array($block, $key, $line, &$state, $lines);
+            $args = [$block, $key, $line, &$state, $lines];
 
             if ($this->_current != 'normal') {
                 $pass = call_user_func_array($this->_parsers[$this->_current], $args);
@@ -690,6 +703,7 @@ private function parseBlock($text, &$lines)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockList($block, $key, $line, &$state)
@@ -740,6 +754,7 @@ private function parseBlockList($block, $key, $line, &$state)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockCode($block, $key, $line, &$state)
@@ -772,9 +787,9 @@ private function parseBlockCode($block, $key, $line, &$state)
 
                 $state['code'] = $matches[2];
 
-                $this->startBlock('code', $key, array(
-                    $matches[1],  $matches[3],  $isAfterList
-                ));
+                $this->startBlock('code', $key, [
+                    $matches[1], $matches[3], $isAfterList
+                ]);
             }
 
             return false;
@@ -791,6 +806,7 @@ private function parseBlockCode($block, $key, $line, &$state)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockShtml($block, $key, $line, &$state)
@@ -818,6 +834,7 @@ private function parseBlockShtml($block, $key, $line, &$state)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockAhtml($block, $key, $line, &$state)
@@ -859,6 +876,7 @@ private function parseBlockAhtml($block, $key, $line, &$state)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockMath($block, $key, $line)
@@ -884,6 +902,7 @@ private function parseBlockMath($block, $key, $line)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockPre($block, $key, $line, &$state)
@@ -909,6 +928,7 @@ private function parseBlockPre($block, $key, $line, &$state)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockHtml($block, $key, $line, &$state)
@@ -941,15 +961,16 @@ private function parseBlockHtml($block, $key, $line, &$state)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockFootnote($block, $key, $line)
     {
         if (preg_match("/^\[\^((?:[^\]]|\\]|\\[)+?)\]:/", $line, $matches)) {
             $space = strlen($matches[0]) - 1;
-            $this->startBlock('footnote', $key, array(
+            $this->startBlock('footnote', $key, [
                 $space, $matches[1]
-            ));
+            ]);
 
             return false;
         }
@@ -961,6 +982,7 @@ private function parseBlockFootnote($block, $key, $line)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockDefinition($block, $key, $line)
@@ -980,6 +1002,7 @@ private function parseBlockDefinition($block, $key, $line)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockQuote($block, $key, $line)
@@ -1005,6 +1028,7 @@ private function parseBlockQuote($block, $key, $line)
      * @param $line
      * @param $state
      * @param $lines
+     *
      * @return bool
      */
     private function parseBlockTable($block, $key, $line, &$state, $lines)
@@ -1012,7 +1036,7 @@ private function parseBlockTable($block, $key, $line, &$state, $lines)
         if (preg_match("/^((?:(?:(?:\||\+)(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:[ :]*\-+[ :]*)(?:\||\+)(?:[ :]*\-+[ :]*))|(?:(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:\||\+)(?:[ :]*\-+[ :]*)))+)$/", $line, $matches)) {
             if ($this->isBlock('table')) {
                 $block[3][0][] = $block[3][2];
-                $block[3][2]++;
+                $block[3][2] ++;
                 $this->setBlock($key, $block[3]);
             } else {
                 $head = 0;
@@ -1030,12 +1054,12 @@ private function parseBlockTable($block, $key, $line, &$state, $lines)
                     $matches[1] = substr($matches[1], 1);
 
                     if ($matches[1][strlen($matches[1]) - 1] == '|') {
-                        $matches[1] = substr($matches[1], 0, -1);
+                        $matches[1] = substr($matches[1], 0, - 1);
                     }
                 }
 
                 $rows = preg_split("/(\+|\|)/", $matches[1]);
-                $aligns = array();
+                $aligns = [];
                 foreach ($rows as $row) {
                     $align = 'none';
 
@@ -1052,7 +1076,7 @@ private function parseBlockTable($block, $key, $line, &$state, $lines)
                     $aligns[] = $align;
                 }
 
-                $this->setBlock($key, array(array($head), $aligns, $head + 1));
+                $this->setBlock($key, [[$head], $aligns, $head + 1]);
             }
 
             return false;
@@ -1065,6 +1089,7 @@ private function parseBlockTable($block, $key, $line, &$state, $lines)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockSh($block, $key, $line)
@@ -1086,12 +1111,13 @@ private function parseBlockSh($block, $key, $line)
      * @param $line
      * @param $state
      * @param $lines
+     *
      * @return bool
      */
     private function parseBlockMh($block, $key, $line, &$state, $lines)
     {
         if (preg_match("/^\s*((=|-){2,})\s*$/", $line, $matches)
-                    && ($block && $block[0] == "normal" && !preg_match("/^\s*$/", $lines[$block[2]]))) {    // check if last line isn't empty
+            && ($block && $block[0] == "normal" && !preg_match("/^\s*$/", $lines[$block[2]]))) {    // check if last line isn't empty
             if ($this->isBlock('normal')) {
                 $this->backBlock(1, 'mh', $matches[1][0] == '=' ? 1 : 2)
                     ->setBlock($key)
@@ -1110,6 +1136,7 @@ private function parseBlockMh($block, $key, $line, &$state, $lines)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockShr($block, $key, $line)
@@ -1128,6 +1155,7 @@ private function parseBlockShr($block, $key, $line)
      * @param $block
      * @param $key
      * @param $line
+     *
      * @return bool
      */
     private function parseBlockDhr($block, $key, $line)
@@ -1147,6 +1175,7 @@ private function parseBlockDhr($block, $key, $line)
      * @param $key
      * @param $line
      * @param $state
+     *
      * @return bool
      */
     private function parseBlockDefault($block, $key, $line, &$state)
@@ -1185,6 +1214,7 @@ private function parseBlockDefault($block, $key, $line, &$state)
     /**
      * @param array $blocks
      * @param array $lines
+     *
      * @return array
      */
     private function optimizeBlocks(array $blocks, array $lines)
@@ -1196,10 +1226,10 @@ private function optimizeBlocks(array $blocks, array $lines)
             $moved = false;
 
             $block = &$blocks[$key];
-            $prevBlock = isset($blocks[$key - 1]) ? $blocks[$key - 1] : NULL;
-            $nextBlock = isset($blocks[$key + 1]) ? $blocks[$key + 1] : NULL;
+            $prevBlock = $blocks[$key - 1] ?? null;
+            $nextBlock = $blocks[$key + 1] ?? null;
 
-            list($type, $from, $to) = $block;
+            [$type, $from, $to] = $block;
 
             if ('pre' == $type) {
                 $isEmpty = array_reduce(
@@ -1217,7 +1247,7 @@ function ($result, $line) {
 
             if ('normal' == $type) {
                 // combine two blocks
-                $types = array('list', 'quote');
+                $types = ['list', 'quote'];
 
                 if ($from == $to && preg_match("/^\s*$/", $lines[$from])
                     && !empty($prevBlock) && !empty($nextBlock)) {
@@ -1225,9 +1255,9 @@ function ($result, $line) {
                         && ($prevBlock[0] != 'list'
                             || ($prevBlock[3][0] == $nextBlock[3][0] && $prevBlock[3][1] == $nextBlock[3][1]))) {
                         // combine 3 blocks
-                        $blocks[$key - 1] = array(
-                            $prevBlock[0],  $prevBlock[1],  $nextBlock[2], $prevBlock[3] ?? null
-                        );
+                        $blocks[$key - 1] = [
+                            $prevBlock[0], $prevBlock[1], $nextBlock[2], $prevBlock[3] ?? null
+                        ];
                         array_splice($blocks, $key, 2);
 
                         // do not move
@@ -1250,20 +1280,21 @@ function ($result, $line) {
      * @param array $lines
      * @param array $parts
      * @param int $start
+     *
      * @return string
      */
     private function parseCode(array $lines, array $parts, $start)
     {
-        list($blank, $lang) = $parts;
+        [$blank, $lang] = $parts;
         $lang = trim($lang);
         $count = strlen($blank);
 
         if (!preg_match("/^[_a-z0-9-\+\#\:\.]+$/i", $lang)) {
-            $lang = NULL;
+            $lang = null;
         } else {
             $parts = explode(':', $lang);
             if (count($parts) > 1) {
-                list($lang, $rel) = $parts;
+                [$lang, $rel] = $parts;
                 $lang = trim($lang);
                 $rel = trim($rel);
             }
@@ -1278,7 +1309,7 @@ private function parseCode(array $lines, array $parts, $start)
             }
 
             return htmlspecialchars($line);
-        }, array_slice($lines, 1, -1));
+        }, array_slice($lines, 1, - 1));
         $str = implode("\n", $this->markLines($lines, $start + 1));
 
         return $isEmpty ? '' :
@@ -1293,6 +1324,7 @@ private function parseCode(array $lines, array $parts, $start)
      * @param array $lines
      * @param mixed $value
      * @param int $start
+     *
      * @return string
      */
     private function parsePre(array $lines, $value, $start)
@@ -1311,6 +1343,7 @@ private function parsePre(array $lines, $value, $start)
      * @param array $lines
      * @param mixed $value
      * @param int $start
+     *
      * @return string
      */
     private function parseAhtml(array $lines, $value, $start)
@@ -1324,11 +1357,12 @@ private function parseAhtml(array $lines, $value, $start)
      * @param array $lines
      * @param mixed $value
      * @param int $start
+     *
      * @return string
      */
     private function parseShtml(array $lines, $value, $start)
     {
-        return trim(implode("\n", $this->markLines(array_slice($lines, 1, -1), $start + 1)));
+        return trim(implode("\n", $this->markLines(array_slice($lines, 1, - 1), $start + 1)));
     }
 
     /**
@@ -1338,6 +1372,7 @@ private function parseShtml(array $lines, $value, $start)
      * @param mixed $value
      * @param int $start
      * @param int $end
+     *
      * @return string
      */
     private function parseMath(array $lines, $value, $start, $end)
@@ -1352,6 +1387,7 @@ private function parseMath(array $lines, $value, $start, $end)
      * @param int $num
      * @param int $start
      * @param int $end
+     *
      * @return string
      */
     private function parseSh(array $lines, $num, $start, $end)
@@ -1367,6 +1403,7 @@ private function parseSh(array $lines, $num, $start, $end)
      * @param int $num
      * @param int $start
      * @param int $end
+     *
      * @return string
      */
     private function parseMh(array $lines, $num, $start, $end)
@@ -1380,6 +1417,7 @@ private function parseMh(array $lines, $num, $start, $end)
      * @param array $lines
      * @param mixed $value
      * @param int $start
+     *
      * @return string
      */
     private function parseQuote(array $lines, $value, $start)
@@ -1398,13 +1436,14 @@ private function parseQuote(array $lines, $value, $start)
      * @param array $lines
      * @param mixed $value
      * @param int $start
+     *
      * @return string
      */
     private function parseList(array $lines, $value, $start)
     {
         $html = '';
-        list($space, $type, $tab) = $value;
-        $rows = array();
+        [$space, $type, $tab] = $value;
+        $rows = [];
         $suffix = '';
         $last = 0;
 
@@ -1437,15 +1476,16 @@ private function parseList(array $lines, $value, $start)
      * @param array $lines
      * @param array $value
      * @param int $start
+     *
      * @return string
      */
     private function parseTable(array $lines, array $value, $start)
     {
-        list($ignores, $aligns) = $value;
+        [$ignores, $aligns] = $value;
         $head = count($ignores) > 0 && array_sum($ignores) > 0;
 
         $html = '<table>';
-        $body = $head ? NULL : true;
+        $body = $head ? null : true;
         $output = false;
 
         foreach ($lines as $key => $line) {
@@ -1465,7 +1505,7 @@ private function parseTable(array $lines, array $value, $start)
                 $line = substr($line, 1);
 
                 if ($line[strlen($line) - 1] == '|') {
-                    $line = substr($line, 0, -1);
+                    $line = substr($line, 0, - 1);
                 }
             }
 
@@ -1477,19 +1517,19 @@ private function parseTable(array $lines, array $value, $start)
                     return trim($row);
                 }
             }, explode('|', $line));
-            $columns = array();
-            $last = -1;
+            $columns = [];
+            $last = - 1;
 
             foreach ($rows as $row) {
                 if (strlen($row) > 0) {
                     $last ++;
-                    $columns[$last] = array(
-                        isset($columns[$last]) ? $columns[$last][0] + 1 : 1,  $row
-                    );
+                    $columns[$last] = [
+                        isset($columns[$last]) ? $columns[$last][0] + 1 : 1, $row
+                    ];
                 } elseif (isset($columns[$last])) {
                     $columns[$last][0] ++;
                 } else {
-                    $columns[0] = array(1, $row);
+                    $columns[0] = [1, $row];
                 }
             }
 
@@ -1504,7 +1544,7 @@ private function parseTable(array $lines, array $value, $start)
                     . '" data-id="' . $this->_uniqid . '"' : '') . '>';
 
             foreach ($columns as $key => $column) {
-                list($num, $text) = $column;
+                [$num, $text] = $column;
                 $tag = $head ? 'th' : 'td';
 
                 $html .= "<{$tag}";
@@ -1528,7 +1568,7 @@ private function parseTable(array $lines, array $value, $start)
             }
         }
 
-        if ($body !== NULL) {
+        if ($body !== null) {
             $html .= '</tbody>';
         }
 
@@ -1542,6 +1582,7 @@ private function parseTable(array $lines, array $value, $start)
      * @param array $lines
      * @param array $value
      * @param int $start
+     *
      * @return string
      */
     private function parseHr($lines, $value, $start)
@@ -1555,6 +1596,7 @@ private function parseHr($lines, $value, $start)
      * @param array $lines
      * @param bool $inline
      * @param int $start
+     *
      * @return string
      */
     private function parseNormal(array $lines, $inline, $start)
@@ -1582,11 +1624,12 @@ private function parseNormal(array $lines, $inline, $start)
      *
      * @param array $lines
      * @param array $value
+     *
      * @return string
      */
     private function parseFootnote(array $lines, array $value)
     {
-        list($space, $note) = $value;
+        [$space, $note] = $value;
         $index = array_search($note, $this->_footnotes);
 
         if (false !== $index) {
@@ -1613,6 +1656,7 @@ private function parseDefinition()
      * @param array $lines
      * @param string $type
      * @param int $start
+     *
      * @return string
      */
     private function parseHtml(array $lines, $type, $start)
@@ -1662,12 +1706,13 @@ private function cleanUrl($url, $parseTitle = false)
 
     /**
      * @param $str
+     *
      * @return mixed
      */
     private function escapeBracket($str)
     {
         return str_replace(
-            array('\[', '\]', '\(', '\)'),  array('[', ']', '(', ')'),  $str
+            ['\[', '\]', '\(', '\)'], ['[', ']', '(', ')'], $str
         );
     }
 
@@ -1677,14 +1722,15 @@ private function escapeBracket($str)
      * @param mixed $type
      * @param mixed $start
      * @param mixed $value
+     *
      * @return $this
      */
-    private function startBlock($type, $start, $value = NULL)
+    private function startBlock($type, $start, $value = null)
     {
         $this->_pos ++;
         $this->_current = $type;
 
-        $this->_blocks[$this->_pos] = array($type, $start, $start, $value);
+        $this->_blocks[$this->_pos] = [$type, $start, $start, $value];
 
         return $this;
     }
@@ -1705,12 +1751,13 @@ private function endBlock()
      *
      * @param mixed $type
      * @param mixed $value
+     *
      * @return bool
      */
-    private function isBlock($type, $value = NULL)
+    private function isBlock($type, $value = null)
     {
         return $this->_current == $type
-            && (NULL === $value ? true : $this->_blocks[$this->_pos][3] == $value);
+            && (null === $value ? true : $this->_blocks[$this->_pos][3] == $value);
     }
 
     /**
@@ -1720,7 +1767,7 @@ private function isBlock($type, $value = NULL)
      */
     private function getBlock()
     {
-        return isset($this->_blocks[$this->_pos]) ? $this->_blocks[$this->_pos] : NULL;
+        return isset($this->_blocks[$this->_pos]) ? $this->_blocks[$this->_pos] : null;
     }
 
     /**
@@ -1728,15 +1775,16 @@ private function getBlock()
      *
      * @param mixed $to
      * @param mixed $value
+     *
      * @return $this
      */
-    private function setBlock($to = NULL, $value = NULL)
+    private function setBlock($to = null, $value = null)
     {
-        if (NULL !== $to) {
+        if (null !== $to) {
             $this->_blocks[$this->_pos][2] = $to;
         }
 
-        if (NULL !== $value) {
+        if (null !== $value) {
             $this->_blocks[$this->_pos][3] = $value;
         }
 
@@ -1749,9 +1797,10 @@ private function setBlock($to = NULL, $value = NULL)
      * @param mixed $step
      * @param mixed $type
      * @param mixed $value
+     *
      * @return $this
      */
-    private function backBlock($step, $type, $value = NULL)
+    private function backBlock($step, $type, $value = null)
     {
         if ($this->_pos < 0) {
             return $this->startBlock($type, 0, $value);
@@ -1765,9 +1814,9 @@ private function backBlock($step, $type, $value = NULL)
         }
 
         $this->_current = $type;
-        $this->_blocks[$this->_pos] = array(
-            $type,  $last - $step + 1,  $last,  $value
-        );
+        $this->_blocks[$this->_pos] = [
+            $type, $last - $step + 1, $last, $value
+        ];
 
         return $this;
     }

From a341db8cda86c5ab334657e6f3167428dcc518bf Mon Sep 17 00:00:00 2001
From: joyqi <joyqi@segmentfault.com>
Date: Thu, 19 Aug 2021 15:46:14 +0800
Subject: [PATCH 25/29] reformat code

---
 Parser.php | 291 +++++++++++++++++++++++++++--------------------------
 1 file changed, 147 insertions(+), 144 deletions(-)

diff --git a/Parser.php b/Parser.php
index 4b19c21..602f410 100644
--- a/Parser.php
+++ b/Parser.php
@@ -134,7 +134,7 @@ class Parser
      *
      * @return string
      */
-    public function makeHtml($text)
+    public function makeHtml($text): string
     {
         $this->_footnotes = [];
         $this->_definitions = [];
@@ -165,9 +165,9 @@ public function makeHtml($text)
     }
 
     /**
-     * @param $html
+     * @param bool $html
      */
-    public function enableHtml($html = true)
+    public function enableHtml(bool $html = true)
     {
         $this->_html = $html;
     }
@@ -175,26 +175,26 @@ public function enableHtml($html = true)
     /**
      * @param bool $line
      */
-    public function enableLine($line = true)
+    public function enableLine(bool $line = true)
     {
         $this->_line = $line;
     }
 
     /**
-     * @param $type
-     * @param $callback
+     * @param string $type
+     * @param callable $callback
      */
-    public function hook($type, $callback)
+    public function hook(string $type, callable $callback)
     {
         $this->_hooks[$type][] = $callback;
     }
 
     /**
-     * @param $str
+     * @param string $str
      *
      * @return string
      */
-    public function makeHolder($str)
+    public function makeHolder(string $str): string
     {
         $key = "\r" . $this->_uniqid . $this->_id . "\r";
         $this->_id ++;
@@ -204,22 +204,22 @@ public function makeHolder($str)
     }
 
     /**
-     * @param $text
+     * @param string $text
      *
-     * @return mixed
+     * @return string
      */
-    private function initText($text)
+    private function initText(string $text): string
     {
         $text = str_replace(["\t", "\r"], ['    ', ''], $text);
         return $text;
     }
 
     /**
-     * @param $html
+     * @param string $html
      *
      * @return string
      */
-    private function makeFootnotes($html)
+    private function makeFootnotes(string $html): string
     {
         if (count($this->_footnotes) > 0) {
             $html .= '<div class="footnotes"><hr><ol>';
@@ -252,7 +252,7 @@ private function makeFootnotes($html)
      *
      * @return string
      */
-    private function parse($text, $inline = false, $offset = 0)
+    private function parse(string $text, bool $inline = false, int $offset = 0): string
     {
         $blocks = $this->parseBlock($text, $lines);
         $html = '';
@@ -278,12 +278,12 @@ private function parse($text, $inline = false, $offset = 0)
     }
 
     /**
-     * @param $text
-     * @param $clearHolders
+     * @param string $text
+     * @param bool $clearHolders
      *
      * @return string
      */
-    private function releaseHolder($text, $clearHolders = true)
+    private function releaseHolder(string $text, bool $clearHolders = true): string
     {
         $deep = 0;
         while (strpos($text, "\r") !== false && $deep < 10) {
@@ -299,12 +299,12 @@ private function releaseHolder($text, $clearHolders = true)
     }
 
     /**
-     * @param $start
+     * @param int $start
      * @param int $end
      *
      * @return string
      */
-    private function markLine($start, $end = - 1)
+    private function markLine(int $start, int $end = - 1): string
     {
         if ($this->_line) {
             $end = $end < 0 ? $start : $end;
@@ -317,11 +317,11 @@ private function markLine($start, $end = - 1)
 
     /**
      * @param array $lines
-     * @param $start
+     * @param int $start
      *
      * @return string[]
      */
-    private function markLines(array $lines, $start)
+    private function markLines(array $lines, int $start): array
     {
         $i = - 1;
 
@@ -332,11 +332,11 @@ private function markLines(array $lines, $start)
     }
 
     /**
-     * @param $html
+     * @param string $html
      *
      * @return string
      */
-    private function optimizeLines($html)
+    private function optimizeLines(string $html): string
     {
         $last = 0;
 
@@ -355,20 +355,19 @@ function ($matches) use (&$last) {
     }
 
     /**
-     * @param $type
-     * @param $value
+     * @param string $type
+     * @param ...$args
      *
      * @return mixed
      */
-    private function call($type, $value)
+    private function call(string $type, ...$args)
     {
+        $value = $args[0];
+
         if (empty($this->_hooks[$type])) {
             return $value;
         }
 
-        $args = func_get_args();
-        $args = array_slice($args, 1);
-
         foreach ($this->_hooks[$type] as $callback) {
             $value = call_user_func_array($callback, $args);
             $args[0] = $value;
@@ -387,8 +386,12 @@ private function call($type, $value)
      *
      * @return string
      */
-    private function parseInline($text, $whiteList = '', $clearHolders = true, $enableAutoLink = true)
-    {
+    private function parseInline(
+        string $text,
+        string $whiteList = '',
+        bool $clearHolders = true,
+        bool $enableAutoLink = true
+    ): string {
         $text = $this->call('beforeParseInline', $text);
 
         // code
@@ -571,11 +574,11 @@ function ($matches) {
     }
 
     /**
-     * @param $text
+     * @param string $text
      *
-     * @return mixed
+     * @return string
      */
-    private function parseInlineCallback($text)
+    private function parseInlineCallback(string $text): string
     {
         $text = preg_replace_callback(
             "/(\*{3})(.+?)\\1/",
@@ -654,11 +657,11 @@ function ($matches) {
      * parseBlock
      *
      * @param string $text
-     * @param array $lines
+     * @param array|null $lines
      *
      * @return array
      */
-    private function parseBlock($text, &$lines)
+    private function parseBlock(string $text, ?array &$lines): array
     {
         $lines = explode("\n", $text);
         $this->_blocks = [];
@@ -699,14 +702,14 @@ private function parseBlock($text, &$lines)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
-     * @param $state
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
+     * @param array|null $state
      *
      * @return bool
      */
-    private function parseBlockList($block, $key, $line, &$state)
+    private function parseBlockList(?array $block, int $key, string $line, ?array &$state): bool
     {
         if ($this->isBlock('list') && !preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line)) {
             if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line)) {
@@ -750,14 +753,14 @@ private function parseBlockList($block, $key, $line, &$state)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
-     * @param $state
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
+     * @param array|null $state
      *
      * @return bool
      */
-    private function parseBlockCode($block, $key, $line, &$state)
+    private function parseBlockCode(?array $block, int $key, string $line, ?array &$state): bool
     {
         if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line, $matches)) {
             if ($this->isBlock('code')) {
@@ -802,14 +805,14 @@ private function parseBlockCode($block, $key, $line, &$state)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
-     * @param $state
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
+     * @param array|null $state
      *
      * @return bool
      */
-    private function parseBlockShtml($block, $key, $line, &$state)
+    private function parseBlockShtml(?array $block, int $key, string $line, ?array &$state): bool
     {
         if ($this->_html) {
             if (preg_match("/^(\s*)!!!(\s*)$/", $line, $matches)) {
@@ -830,14 +833,14 @@ private function parseBlockShtml($block, $key, $line, &$state)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
-     * @param $state
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
+     * @param array|null $state
      *
      * @return bool
      */
-    private function parseBlockAhtml($block, $key, $line, &$state)
+    private function parseBlockAhtml(?array $block, int $key, string $line, ?array &$state): bool
     {
         if ($this->_html) {
             if (preg_match("/^\s*<({$this->_blockHtmlTags})(\s+[^>]*)?>/i", $line, $matches)) {
@@ -873,13 +876,13 @@ private function parseBlockAhtml($block, $key, $line, &$state)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
      *
      * @return bool
      */
-    private function parseBlockMath($block, $key, $line)
+    private function parseBlockMath(?array $block, int $key, string $line): bool
     {
         if (preg_match("/^(\s*)\\$\\$(\s*)$/", $line, $matches)) {
             if ($this->isBlock('math')) {
@@ -898,14 +901,14 @@ private function parseBlockMath($block, $key, $line)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
-     * @param $state
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
+     * @param array|null $state
      *
      * @return bool
      */
-    private function parseBlockPre($block, $key, $line, &$state)
+    private function parseBlockPre(?array $block, int $key, string $line, ?array &$state): bool
     {
         if (preg_match("/^ {4}/", $line)) {
             if ($this->isBlock('pre')) {
@@ -924,14 +927,14 @@ private function parseBlockPre($block, $key, $line, &$state)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
-     * @param $state
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
+     * @param array|null $state
      *
      * @return bool
      */
-    private function parseBlockHtml($block, $key, $line, &$state)
+    private function parseBlockHtml(?array $block, int $key, string $line, ?array &$state): bool
     {
         if (preg_match("/^\s*<({$state['special']})(\s+[^>]*)?>/i", $line, $matches)) {
             $tag = strtolower($matches[1]);
@@ -958,13 +961,13 @@ private function parseBlockHtml($block, $key, $line, &$state)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
      *
      * @return bool
      */
-    private function parseBlockFootnote($block, $key, $line)
+    private function parseBlockFootnote(?array $block, int $key, string $line): bool
     {
         if (preg_match("/^\[\^((?:[^\]]|\\]|\\[)+?)\]:/", $line, $matches)) {
             $space = strlen($matches[0]) - 1;
@@ -979,13 +982,13 @@ private function parseBlockFootnote($block, $key, $line)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
      *
      * @return bool
      */
-    private function parseBlockDefinition($block, $key, $line)
+    private function parseBlockDefinition(?array $block, int $key, string $line): bool
     {
         if (preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line, $matches)) {
             $this->_definitions[$matches[1]] = $this->cleanUrl($matches[2]);
@@ -999,13 +1002,13 @@ private function parseBlockDefinition($block, $key, $line)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
      *
      * @return bool
      */
-    private function parseBlockQuote($block, $key, $line)
+    private function parseBlockQuote(?array $block, int $key, string $line): bool
     {
         if (preg_match("/^(\s*)>/", $line, $matches)) {
             if ($this->isBlock('list') && strlen($matches[1]) > 0) {
@@ -1023,15 +1026,15 @@ private function parseBlockQuote($block, $key, $line)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
-     * @param $state
-     * @param $lines
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
+     * @param array|null $state
+     * @param array|null $lines
      *
      * @return bool
      */
-    private function parseBlockTable($block, $key, $line, &$state, $lines)
+    private function parseBlockTable(?array $block, int $key, string $line, ?array &$state, array $lines): bool
     {
         if (preg_match("/^((?:(?:(?:\||\+)(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:[ :]*\-+[ :]*)(?:\||\+)(?:[ :]*\-+[ :]*))|(?:(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:\||\+)(?:[ :]*\-+[ :]*)))+)$/", $line, $matches)) {
             if ($this->isBlock('table')) {
@@ -1086,13 +1089,13 @@ private function parseBlockTable($block, $key, $line, &$state, $lines)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
      *
      * @return bool
      */
-    private function parseBlockSh($block, $key, $line)
+    private function parseBlockSh(?array $block, int $key, string $line): bool
     {
         if (preg_match("/^(#+)(.*)$/", $line, $matches)) {
             $num = min(strlen($matches[1]), 6);
@@ -1106,15 +1109,15 @@ private function parseBlockSh($block, $key, $line)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
-     * @param $state
-     * @param $lines
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
+     * @param array|null $state
+     * @param array $lines
      *
      * @return bool
      */
-    private function parseBlockMh($block, $key, $line, &$state, $lines)
+    private function parseBlockMh(?array $block, int $key, string $line, ?array &$state, array $lines): bool
     {
         if (preg_match("/^\s*((=|-){2,})\s*$/", $line, $matches)
             && ($block && $block[0] == "normal" && !preg_match("/^\s*$/", $lines[$block[2]]))) {    // check if last line isn't empty
@@ -1133,13 +1136,13 @@ private function parseBlockMh($block, $key, $line, &$state, $lines)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
      *
      * @return bool
      */
-    private function parseBlockShr($block, $key, $line)
+    private function parseBlockShr(?array $block, int $key, string $line): bool
     {
         if (preg_match("/^(\* *){3,}\s*$/", $line)) {
             $this->startBlock('hr', $key)
@@ -1152,13 +1155,13 @@ private function parseBlockShr($block, $key, $line)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
      *
      * @return bool
      */
-    private function parseBlockDhr($block, $key, $line)
+    private function parseBlockDhr(?array $block, int $key, string $line): bool
     {
         if (preg_match("/^(- *){3,}\s*$/", $line)) {
             $this->startBlock('hr', $key)
@@ -1171,14 +1174,14 @@ private function parseBlockDhr($block, $key, $line)
     }
 
     /**
-     * @param $block
-     * @param $key
-     * @param $line
-     * @param $state
+     * @param array|null $block
+     * @param int $key
+     * @param string $line
+     * @param array|null $state
      *
      * @return bool
      */
-    private function parseBlockDefault($block, $key, $line, &$state)
+    private function parseBlockDefault(?array $block, int $key, string $line, ?array &$state): bool
     {
         if ($this->isBlock('footnote')) {
             preg_match("/^(\s*)/", $line, $matches);
@@ -1217,7 +1220,7 @@ private function parseBlockDefault($block, $key, $line, &$state)
      *
      * @return array
      */
-    private function optimizeBlocks(array $blocks, array $lines)
+    private function optimizeBlocks(array $blocks, array $lines): array
     {
         $blocks = $this->call('beforeOptimizeBlocks', $blocks, $lines);
 
@@ -1283,7 +1286,7 @@ function ($result, $line) {
      *
      * @return string
      */
-    private function parseCode(array $lines, array $parts, $start)
+    private function parseCode(array $lines, array $parts, int $start): string
     {
         [$blank, $lang] = $parts;
         $lang = trim($lang);
@@ -1327,7 +1330,7 @@ private function parseCode(array $lines, array $parts, $start)
      *
      * @return string
      */
-    private function parsePre(array $lines, $value, $start)
+    private function parsePre(array $lines, $value, int $start): string
     {
         foreach ($lines as &$line) {
             $line = htmlspecialchars(substr($line, 4));
@@ -1346,7 +1349,7 @@ private function parsePre(array $lines, $value, $start)
      *
      * @return string
      */
-    private function parseAhtml(array $lines, $value, $start)
+    private function parseAhtml(array $lines, $value, int $start): string
     {
         return trim(implode("\n", $this->markLines($lines, $start)));
     }
@@ -1360,7 +1363,7 @@ private function parseAhtml(array $lines, $value, $start)
      *
      * @return string
      */
-    private function parseShtml(array $lines, $value, $start)
+    private function parseShtml(array $lines, $value, int $start): string
     {
         return trim(implode("\n", $this->markLines(array_slice($lines, 1, - 1), $start + 1)));
     }
@@ -1375,7 +1378,7 @@ private function parseShtml(array $lines, $value, $start)
      *
      * @return string
      */
-    private function parseMath(array $lines, $value, $start, $end)
+    private function parseMath(array $lines, $value, int $start, int $end): string
     {
         return '<p>' . $this->markLine($start, $end) . htmlspecialchars(implode("\n", $lines)) . '</p>';
     }
@@ -1390,7 +1393,7 @@ private function parseMath(array $lines, $value, $start, $end)
      *
      * @return string
      */
-    private function parseSh(array $lines, $num, $start, $end)
+    private function parseSh(array $lines, int $num, int $start, int $end): string
     {
         $line = $this->markLine($start, $end) . $this->parseInline(trim($lines[0], '# '));
         return preg_match("/^\s*$/", $line) ? '' : "<h{$num}>{$line}</h{$num}>";
@@ -1406,7 +1409,7 @@ private function parseSh(array $lines, $num, $start, $end)
      *
      * @return string
      */
-    private function parseMh(array $lines, $num, $start, $end)
+    private function parseMh(array $lines, int $num, int $start, int $end): string
     {
         return $this->parseSh($lines, $num, $start, $end);
     }
@@ -1420,7 +1423,7 @@ private function parseMh(array $lines, $num, $start, $end)
      *
      * @return string
      */
-    private function parseQuote(array $lines, $value, $start)
+    private function parseQuote(array $lines, $value, int $start): string
     {
         foreach ($lines as &$line) {
             $line = preg_replace("/^\s*> ?/", '', $line);
@@ -1439,7 +1442,7 @@ private function parseQuote(array $lines, $value, $start)
      *
      * @return string
      */
-    private function parseList(array $lines, $value, $start)
+    private function parseList(array $lines, $value, int $start): string
     {
         $html = '';
         [$space, $type, $tab] = $value;
@@ -1474,12 +1477,12 @@ private function parseList(array $lines, $value, $start)
 
     /**
      * @param array $lines
-     * @param array $value
+     * @param mixed $value
      * @param int $start
      *
      * @return string
      */
-    private function parseTable(array $lines, array $value, $start)
+    private function parseTable(array $lines, $value, int $start): string
     {
         [$ignores, $aligns] = $value;
         $head = count($ignores) > 0 && array_sum($ignores) > 0;
@@ -1580,12 +1583,12 @@ private function parseTable(array $lines, array $value, $start)
      * parseHr
      *
      * @param array $lines
-     * @param array $value
+     * @param mixed $value
      * @param int $start
      *
      * @return string
      */
-    private function parseHr($lines, $value, $start)
+    private function parseHr(array $lines, $value, int $start): string
     {
         return $this->_line ? '<hr class="line" data-start="' . $start . '" data-end="' . $start . '">' : '<hr>';
     }
@@ -1594,12 +1597,12 @@ private function parseHr($lines, $value, $start)
      * parseNormal
      *
      * @param array $lines
-     * @param bool $inline
+     * @param mixed $inline
      * @param int $start
      *
      * @return string
      */
-    private function parseNormal(array $lines, $inline, $start)
+    private function parseNormal(array $lines, $inline, int $start): string
     {
         foreach ($lines as $key => &$line) {
             $line = $this->parseInline($line);
@@ -1627,7 +1630,7 @@ private function parseNormal(array $lines, $inline, $start)
      *
      * @return string
      */
-    private function parseFootnote(array $lines, array $value)
+    private function parseFootnote(array $lines, array $value): string
     {
         [$space, $note] = $value;
         $index = array_search($note, $this->_footnotes);
@@ -1645,7 +1648,7 @@ private function parseFootnote(array $lines, array $value)
      *
      * @return string
      */
-    private function parseDefinition()
+    private function parseDefinition(): string
     {
         return '';
     }
@@ -1659,11 +1662,11 @@ private function parseDefinition()
      *
      * @return string
      */
-    private function parseHtml(array $lines, $type, $start)
+    private function parseHtml(array $lines, string $type, int $start): string
     {
         foreach ($lines as &$line) {
             $line = $this->parseInline($line,
-                isset($this->_specialWhiteList[$type]) ? $this->_specialWhiteList[$type] : '');
+                $this->_specialWhiteList[$type] ?? '');
         }
 
         return implode("\n", $this->markLines($lines, $start));
@@ -1675,7 +1678,7 @@ private function parseHtml(array $lines, $type, $start)
      *
      * @return mixed
      */
-    private function cleanUrl($url, $parseTitle = false)
+    private function cleanUrl($url, bool $parseTitle = false)
     {
         $title = null;
         $url = trim($url);
@@ -1707,9 +1710,9 @@ private function cleanUrl($url, $parseTitle = false)
     /**
      * @param $str
      *
-     * @return mixed
+     * @return string
      */
-    private function escapeBracket($str)
+    private function escapeBracket($str): string
     {
         return str_replace(
             ['\[', '\]', '\(', '\)'], ['[', ']', '(', ')'], $str
@@ -1725,7 +1728,7 @@ private function escapeBracket($str)
      *
      * @return $this
      */
-    private function startBlock($type, $start, $value = null)
+    private function startBlock($type, $start, $value = null): Parser
     {
         $this->_pos ++;
         $this->_current = $type;
@@ -1740,7 +1743,7 @@ private function startBlock($type, $start, $value = null)
      *
      * @return $this
      */
-    private function endBlock()
+    private function endBlock(): Parser
     {
         $this->_current = 'normal';
         return $this;
@@ -1754,10 +1757,10 @@ private function endBlock()
      *
      * @return bool
      */
-    private function isBlock($type, $value = null)
+    private function isBlock($type, $value = null): bool
     {
         return $this->_current == $type
-            && (null === $value ? true : $this->_blocks[$this->_pos][3] == $value);
+            && (null === $value || $this->_blocks[$this->_pos][3] == $value);
     }
 
     /**
@@ -1765,9 +1768,9 @@ private function isBlock($type, $value = null)
      *
      * @return array
      */
-    private function getBlock()
+    private function getBlock(): ?array
     {
-        return isset($this->_blocks[$this->_pos]) ? $this->_blocks[$this->_pos] : null;
+        return $this->_blocks[$this->_pos] ?? null;
     }
 
     /**
@@ -1778,7 +1781,7 @@ private function getBlock()
      *
      * @return $this
      */
-    private function setBlock($to = null, $value = null)
+    private function setBlock($to = null, $value = null): Parser
     {
         if (null !== $to) {
             $this->_blocks[$this->_pos][2] = $to;
@@ -1800,7 +1803,7 @@ private function setBlock($to = null, $value = null)
      *
      * @return $this
      */
-    private function backBlock($step, $type, $value = null)
+    private function backBlock($step, $type, $value = null): Parser
     {
         if ($this->_pos < 0) {
             return $this->startBlock($type, 0, $value);
@@ -1824,7 +1827,7 @@ private function backBlock($step, $type, $value = null)
     /**
      * @return $this
      */
-    private function combineBlock()
+    private function combineBlock(): Parser
     {
         if ($this->_pos < 1) {
             return $this;

From e263c77bc5600fe9c5f939fe9081c159c5f8b6d3 Mon Sep 17 00:00:00 2001
From: joyqi <magike.net@gmail.com>
Date: Thu, 30 Sep 2021 00:58:39 +0800
Subject: [PATCH 26/29] fix start

---
 Parser.php | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Parser.php b/Parser.php
index 602f410..9c8ce75 100644
--- a/Parser.php
+++ b/Parser.php
@@ -1453,10 +1453,10 @@ private function parseList(array $lines, $value, int $start): string
         foreach ($lines as $key => $line) {
             if (preg_match("/^(\s{" . $space . "})((?:[0-9]+\.?)|\-|\+|\*)(\s+)(.*)$/i", $line, $matches)) {
                 if ($type == 'ol' && $key == 0) {
-                    $start = intval($matches[2]);
+                    $olStart = intval($matches[2]);
 
-                    if ($start != 1) {
-                        $suffix = ' start="' . $start . '"';
+                    if ($olStart != 1) {
+                        $suffix = ' start="' . $olStart . '"';
                     }
                 }
 

From 0feb6827d91fcd767d30e300aa7e386b437a341e Mon Sep 17 00:00:00 2001
From: kumfo <kumfo@sifou.com>
Date: Wed, 25 Jun 2025 14:33:32 +0800
Subject: [PATCH 27/29] feat: https://github.com/typecho/typecho/issues/1882

---
 Parser.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Parser.php b/Parser.php
index 9c8ce75..3d264be 100644
--- a/Parser.php
+++ b/Parser.php
@@ -1036,7 +1036,7 @@ private function parseBlockQuote(?array $block, int $key, string $line): bool
      */
     private function parseBlockTable(?array $block, int $key, string $line, ?array &$state, array $lines): bool
     {
-        if (preg_match("/^((?:(?:(?:\||\+)(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:[ :]*\-+[ :]*)(?:\||\+)(?:[ :]*\-+[ :]*))|(?:(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:\||\+)(?:[ :]*\-+[ :]*)))+)$/", $line, $matches)) {
+        if (preg_match("/^\s*(\|?[ :]*-+[ :]*(?:\|[ :]*-+[ :]*)*\|?)\s*$/", $line, $matches)) {
             if ($this->isBlock('table')) {
                 $block[3][0][] = $block[3][2];
                 $block[3][2] ++;

From 16ed4066b4709c542f2d0319001a556c8ba08752 Mon Sep 17 00:00:00 2001
From: kumfo <kumfo@sifou.com>
Date: Wed, 25 Jun 2025 18:11:25 +0800
Subject: [PATCH 28/29] =?UTF-8?q?fix:=20=E5=A4=84=E7=90=86=E8=A7=A3?=
 =?UTF-8?q?=E6=9E=90=E8=BF=87=E9=95=BF=E7=9A=84=E9=93=BE=E6=8E=A5=E5=AF=BC?=
 =?UTF-8?q?=E8=87=B4JIT=E6=A0=88=E6=BA=A2=E5=87=BA(preg=5Fmatch=20error:?=
 =?UTF-8?q?=20JIT=20stack=20limit=20exhausted)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Parser.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Parser.php b/Parser.php
index 3d264be..ac83201 100644
--- a/Parser.php
+++ b/Parser.php
@@ -515,7 +515,7 @@ function ($matches) {
 
         // link
         $text = preg_replace_callback(
-            "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/",
+            "/\[([^\]]*(?:\\\\.[^\]]*)*)\]\(([^)]*(?:\\\\.[^)]*)*)\)/",
             function ($matches) {
                 $escaped = $this->parseInline(
                     $this->escapeBracket($matches[1]), '', false, false

From 8be45e0673b53d8ad3a065f7a86e093a5c4e47d0 Mon Sep 17 00:00:00 2001
From: kumfo <kumfo@sifou.com>
Date: Fri, 4 Jul 2025 14:29:27 +0800
Subject: [PATCH 29/29] =?UTF-8?q?fix:=20=E5=A4=84=E7=90=86=E8=A1=A8?=
 =?UTF-8?q?=E6=A0=BC=E8=A7=A3=E6=9E=90=E5=8F=AA=E6=9C=89=E8=A1=A8=E6=A0=BC?=
 =?UTF-8?q?=E5=A4=B4=E9=83=A8=EF=BC=8C=E5=88=86=E5=89=B2=E7=BA=BF=E8=A7=A3?=
 =?UTF-8?q?=E6=9E=90=E4=BC=9A=E5=87=BA=E9=97=AE=E9=A2=98=E7=9A=84bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Parser.php | 128 +++++++++++++++++++++++++++--------------------------
 1 file changed, 65 insertions(+), 63 deletions(-)

diff --git a/Parser.php b/Parser.php
index 3d264be..dce3226 100644
--- a/Parser.php
+++ b/Parser.php
@@ -143,7 +143,7 @@ public function makeHtml($text): string
         $this->_id = 0;
 
         usort($this->blockParsers, function ($a, $b) {
-            return $a[1] < $b[1] ? - 1 : 1;
+            return $a[1] < $b[1] ? -1 : 1;
         });
 
         foreach ($this->blockParsers as $parser) {
@@ -197,7 +197,7 @@ public function hook(string $type, callable $callback)
     public function makeHolder(string $str): string
     {
         $key = "\r" . $this->_uniqid . $this->_id . "\r";
-        $this->_id ++;
+        $this->_id++;
         $this->_holders[$key] = $str;
 
         return $key;
@@ -234,7 +234,7 @@ private function makeFootnotes(string $html): string
                 }
 
                 $html .= "<li id=\"fn-{$index}\">{$val}</li>";
-                $index ++;
+                $index++;
             }
 
             $html .= '</ol></div>';
@@ -288,7 +288,7 @@ private function releaseHolder(string $text, bool $clearHolders = true): string
         $deep = 0;
         while (strpos($text, "\r") !== false && $deep < 10) {
             $text = str_replace(array_keys($this->_holders), array_values($this->_holders), $text);
-            $deep ++;
+            $deep++;
         }
 
         if ($clearHolders) {
@@ -304,7 +304,7 @@ private function releaseHolder(string $text, bool $clearHolders = true): string
      *
      * @return string
      */
-    private function markLine(int $start, int $end = - 1): string
+    private function markLine(int $start, int $end = -1): string
     {
         if ($this->_line) {
             $end = $end < 0 ? $start : $end;
@@ -323,10 +323,10 @@ private function markLine(int $start, int $end = - 1): string
      */
     private function markLines(array $lines, int $start): array
     {
-        $i = - 1;
+        $i = -1;
 
         return $this->_line ? array_map(function ($line) use ($start, &$i) {
-            $i ++;
+            $i++;
             return $this->markLine($start + $i) . $line;
         }, $lines) : $lines;
     }
@@ -389,9 +389,10 @@ private function call(string $type, ...$args)
     private function parseInline(
         string $text,
         string $whiteList = '',
-        bool $clearHolders = true,
-        bool $enableAutoLink = true
-    ): string {
+        bool   $clearHolders = true,
+        bool   $enableAutoLink = true
+    ): string
+    {
         $text = $this->call('beforeParseInline', $text);
 
         // code
@@ -515,7 +516,7 @@ function ($matches) {
 
         // link
         $text = preg_replace_callback(
-            "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/",
+            "/\[([^\]]*(?:\\\\.[^\]]*)*)\]\(([^)]*(?:\\\\.[^)]*)*)\)/",
             function ($matches) {
                 $escaped = $this->parseInline(
                     $this->escapeBracket($matches[1]), '', false, false
@@ -666,7 +667,7 @@ private function parseBlock(string $text, ?array &$lines): array
         $lines = explode("\n", $text);
         $this->_blocks = [];
         $this->_current = 'normal';
-        $this->_pos = - 1;
+        $this->_pos = -1;
 
         $state = [
             'special' => implode("|", array_keys($this->_specialWhiteList)),
@@ -723,7 +724,7 @@ private function parseBlockList(?array $block, int $key, string $line, ?array &$
                 $this->setBlock($key);
                 return false;
             } elseif (preg_match("/^(\s*)$/", $line) && $state['empty'] == 0) {
-                $state['empty'] ++;
+                $state['empty']++;
                 $this->setBlock($key);
                 return false;
             }
@@ -1036,53 +1037,54 @@ private function parseBlockQuote(?array $block, int $key, string $line): bool
      */
     private function parseBlockTable(?array $block, int $key, string $line, ?array &$state, array $lines): bool
     {
-        if (preg_match("/^\s*(\|?[ :]*-+[ :]*(?:\|[ :]*-+[ :]*)*\|?)\s*$/", $line, $matches)) {
-            if ($this->isBlock('table')) {
-                $block[3][0][] = $block[3][2];
-                $block[3][2] ++;
-                $this->setBlock($key, $block[3]);
-            } else {
-                $head = 0;
-
-                if (empty($block) ||
-                    $block[0] != 'normal' ||
-                    preg_match("/^\s*$/", $lines[$block[2]])) {
-                    $this->startBlock('table', $key);
+        if (preg_match("/^\s*(\|?[ :]*-{2,}[ :]*(?:[\|\+][ :]*-{2,}[ :]*)*\|?)\s*$/", $line, $matches)) {
+            if (strpos($matches[1], '|') !== false || strpos($matches[1], '+') !== false) {
+                if ($this->isBlock('table')) {
+                    $block[3][0][] = $block[3][2];
+                    $block[3][2]++;
+                    $this->setBlock($key, $block[3]);
                 } else {
-                    $head = 1;
-                    $this->backBlock(1, 'table');
-                }
+                    $head = 0;
+
+                    if (empty($block) ||
+                        $block[0] != 'normal' ||
+                        preg_match("/^\s*$/", $lines[$block[2]])) {
+                        $this->startBlock('table', $key);
+                    } else {
+                        $head = 1;
+                        $this->backBlock(1, 'table');
+                    }
 
-                if ($matches[1][0] == '|') {
-                    $matches[1] = substr($matches[1], 1);
+                    if ($matches[1][0] == '|') {
+                        $matches[1] = substr($matches[1], 1);
 
-                    if ($matches[1][strlen($matches[1]) - 1] == '|') {
-                        $matches[1] = substr($matches[1], 0, - 1);
+                        if ($matches[1][strlen($matches[1]) - 1] == '|') {
+                            $matches[1] = substr($matches[1], 0, -1);
+                        }
                     }
-                }
 
-                $rows = preg_split("/(\+|\|)/", $matches[1]);
-                $aligns = [];
-                foreach ($rows as $row) {
-                    $align = 'none';
-
-                    if (preg_match("/^\s*(:?)\-+(:?)\s*$/", $row, $matches)) {
-                        if (!empty($matches[1]) && !empty($matches[2])) {
-                            $align = 'center';
-                        } elseif (!empty($matches[1])) {
-                            $align = 'left';
-                        } elseif (!empty($matches[2])) {
-                            $align = 'right';
+                    $rows = preg_split("/(\+|\|)/", $matches[1]);
+                    $aligns = [];
+                    foreach ($rows as $row) {
+                        $align = 'none';
+
+                        if (preg_match("/^\s*(:?)\-+(:?)\s*$/", $row, $matches)) {
+                            if (!empty($matches[1]) && !empty($matches[2])) {
+                                $align = 'center';
+                            } elseif (!empty($matches[1])) {
+                                $align = 'left';
+                            } elseif (!empty($matches[2])) {
+                                $align = 'right';
+                            }
                         }
+
+                        $aligns[] = $align;
                     }
 
-                    $aligns[] = $align;
+                    $this->setBlock($key, [[$head], $aligns, $head + 1]);
                 }
-
-                $this->setBlock($key, [[$head], $aligns, $head + 1]);
+                return false;
             }
-
-            return false;
         }
 
         return true;
@@ -1144,7 +1146,7 @@ private function parseBlockMh(?array $block, int $key, string $line, ?array &$st
      */
     private function parseBlockShr(?array $block, int $key, string $line): bool
     {
-        if (preg_match("/^(\* *){3,}\s*$/", $line)) {
+        if (preg_match("/^\*{3,}\s*$/", $line)) {
             $this->startBlock('hr', $key)
                 ->endBlock();
 
@@ -1163,7 +1165,7 @@ private function parseBlockShr(?array $block, int $key, string $line): bool
      */
     private function parseBlockDhr(?array $block, int $key, string $line): bool
     {
-        if (preg_match("/^(- *){3,}\s*$/", $line)) {
+        if (preg_match("/^-{3,}\s*$/", $line)) {
             $this->startBlock('hr', $key)
                 ->endBlock();
 
@@ -1192,7 +1194,7 @@ private function parseBlockDefault(?array $block, int $key, string $line, ?array
             }
         } elseif ($this->isBlock('table')) {
             if (false !== strpos($line, '|')) {
-                $block[3][2] ++;
+                $block[3][2]++;
                 $this->setBlock($key, $block[3]);
             } else {
                 $this->startBlock('normal', $key);
@@ -1270,7 +1272,7 @@ function ($result, $line) {
             }
 
             if (!$moved) {
-                $key ++;
+                $key++;
             }
         }
 
@@ -1312,7 +1314,7 @@ private function parseCode(array $lines, array $parts, int $start): string
             }
 
             return htmlspecialchars($line);
-        }, array_slice($lines, 1, - 1));
+        }, array_slice($lines, 1, -1));
         $str = implode("\n", $this->markLines($lines, $start + 1));
 
         return $isEmpty ? '' :
@@ -1365,7 +1367,7 @@ private function parseAhtml(array $lines, $value, int $start): string
      */
     private function parseShtml(array $lines, $value, int $start): string
     {
-        return trim(implode("\n", $this->markLines(array_slice($lines, 1, - 1), $start + 1)));
+        return trim(implode("\n", $this->markLines(array_slice($lines, 1, -1), $start + 1)));
     }
 
     /**
@@ -1508,7 +1510,7 @@ private function parseTable(array $lines, $value, int $start): string
                 $line = substr($line, 1);
 
                 if ($line[strlen($line) - 1] == '|') {
-                    $line = substr($line, 0, - 1);
+                    $line = substr($line, 0, -1);
                 }
             }
 
@@ -1521,16 +1523,16 @@ private function parseTable(array $lines, $value, int $start): string
                 }
             }, explode('|', $line));
             $columns = [];
-            $last = - 1;
+            $last = -1;
 
             foreach ($rows as $row) {
                 if (strlen($row) > 0) {
-                    $last ++;
+                    $last++;
                     $columns[$last] = [
                         isset($columns[$last]) ? $columns[$last][0] + 1 : 1, $row
                     ];
                 } elseif (isset($columns[$last])) {
-                    $columns[$last][0] ++;
+                    $columns[$last][0]++;
                 } else {
                     $columns[0] = [1, $row];
                 }
@@ -1730,7 +1732,7 @@ private function escapeBracket($str): string
      */
     private function startBlock($type, $start, $value = null): Parser
     {
-        $this->_pos ++;
+        $this->_pos++;
         $this->_current = $type;
 
         $this->_blocks[$this->_pos] = [$type, $start, $start, $value];
@@ -1813,7 +1815,7 @@ private function backBlock($step, $type, $value = null): Parser
         $this->_blocks[$this->_pos][2] = $last - $step;
 
         if ($this->_blocks[$this->_pos][1] <= $this->_blocks[$this->_pos][2]) {
-            $this->_pos ++;
+            $this->_pos++;
         }
 
         $this->_current = $type;
@@ -1840,7 +1842,7 @@ private function combineBlock(): Parser
         $this->_blocks[$this->_pos - 1] = $prev;
         $this->_current = $prev[0];
         unset($this->_blocks[$this->_pos]);
-        $this->_pos --;
+        $this->_pos--;
 
         return $this;
     }
