Kaynağa Gözat

Able to parse simple markdown with PHP

main
Rocketsoup 1 yıl önce
ebeveyn
işleme
06e046bef3
1 değiştirilmiş dosya ile 88 ekleme ve 85 silme
  1. 88
    85
      php/markdown.php

+ 88
- 85
php/markdown.php Dosyayı Görüntüle

@@ -69,7 +69,7 @@ class MDUtils {
69 69
 	 * @param string[] $lines
70 70
 	 * @return string[]
71 71
 	 */
72
-	public static function withoutTrailingBlankLines(array &$lines): array {
72
+	public static function withoutTrailingBlankLines(array $lines): array {
73 73
 		$stripped = $lines;
74 74
 		while (sizeof($stripped) > 0 && mb_strlen(trim($stripped[sizeof($stripped) - 1])) == 0) {
75 75
 			array_pop($stripped);
@@ -83,7 +83,7 @@ class MDUtils {
83 83
 	 *
84 84
 	 * @param string[] $lines
85 85
 	 */
86
-	public static function containsBlankLine(array &$lines): bool {
86
+	public static function containsBlankLine(array $lines): bool {
87 87
 		foreach ($lines as $line) {
88 88
 			if (mb_strlen(trim($line)) == 0) return true;
89 89
 		}
@@ -462,6 +462,12 @@ class MDState {
462 462
 	 */
463 463
 	public int $p = 0;
464 464
 
465
+	/**
466
+	 * General storage for anything readers need to track during the parsing
467
+	 * process.
468
+	 */
469
+	public array $userInfo = [];
470
+
465 471
 	private ?MDState $parent = null;
466 472
 
467 473
 	/**
@@ -495,8 +501,6 @@ class MDState {
495 501
 	 */
496 502
 	public MDHTMLFilter $tagFilter;
497 503
 
498
-	private static string $textWhitespaceRegex = '^(\\s*)(?:(\\S|\\S.*\\S)(\\s*?))?$'; // 1=leading WS, 2=text, 3=trailing WS
499
-
500 504
 	/**
501 505
 	 * @param string[] $lines - lines of markdown text
502 506
 	 */
@@ -552,7 +556,7 @@ class MDState {
552 556
 		$lines = MDUtils::withoutTrailingBlankLines(array_slice($this->lines, $this->p));
553 557
 		if (sizeof($lines) == 0) return null;
554 558
 		$this->p = sizeof($this->lines);
555
-		return $this->inlineMarkdownToNode(implode("\n", $lines));
559
+		return new MDBlockNode($this->inlineMarkdownToNode(implode("\n", $lines)));
556 560
 	}
557 561
 
558 562
 	/**
@@ -571,8 +575,8 @@ class MDState {
571 575
 				if ($this->p == $startP) {
572 576
 					$readerClassName = get_class($reader);
573 577
 					$blockClassName = get_class($block);
574
-					throw new Error("{$readerClassName} returned an " +
575
-						"{$blockClassName} without incrementing MDState.p. " +
578
+					throw new Error("{$readerClassName} returned an " .
579
+						"{$blockClassName} without incrementing MDState.p. " .
576 580
 						"This could lead to an infinite loop.");
577 581
 				}
578 582
 				return $block;
@@ -596,27 +600,24 @@ class MDState {
596 600
 		/**
597 601
 		 * Flushes accumulated content in `text` to `tokens`.
598 602
 		 */
599
-		function endText() {
603
+		$endText = function() use (&$tokens, &$text) {
600 604
 			if (mb_strlen($text) == 0) return;
601
-			$textGroups = null;
602
-			if (mb_eregi(MDState::$textWhitespaceRegex, $text, $textGroups)) {
603
-				if (mb_strlen($textGroups[1]) > 0) {
604
-					array_push($tokens, new MDToken($textGroups[1], MDTokenType::Whitespace, $textGroups[1]));
605
-				}
606
-				if ($textGroups[2] && mb_strlen($textGroups[2]) > 0) {
607
-					$tokens.push(new MDToken($textGroups[2], MDTokenType::Text, $textGroups[2]));
608
-				}
609
-				if ($textGroups[3] && mb_strlen($textGroups[3]) > 0) {
610
-					$tokens.push(new MDToken($textGroups[3], MDTokenType::Whitespace, $textGroups[3]));
611
-				}
605
+			$textGroups = [];
606
+			if (mb_eregi('^(\s+)(.*?)$', $text, $textGroups)) {
607
+				array_push($tokens, new MDToken($textGroups[1], MDTokenType::Whitespace, $textGroups[1]));
608
+				$text = $textGroups[2];
609
+			}
610
+			if (mb_eregi('^(.*?)(\s+)$', $text, $textGroups)) {
611
+				array_push($tokens, new MDToken($textGroups[1], MDTokenType::Text, $textGroups[1]));
612
+				array_push($tokens, new MDToken($textGroups[2], MDTokenType::Whitespace, $textGroups[2]));
612 613
 			} else {
613 614
 				array_push($tokens, new MDToken($text, MDTokenType::Text, $text));
614 615
 			}
615 616
 			$text = '';
616
-		}
617
+		};
617 618
 
618
-		for ($p = 0; $p < mb_strlen(line); $p++) {
619
-			$ch = mb_substr($line, p, 1);
619
+		for ($p = 0; $p < mb_strlen($line); $p++) {
620
+			$ch = mb_substr($line, $p, 1);
620 621
 			$remainder = mb_substr($line, $p);
621 622
 			if ($expectLiteral) {
622 623
 				$text .= $ch;
@@ -631,7 +632,7 @@ class MDState {
631 632
 			foreach ($this->root()->readersByTokenPriority as $reader) {
632 633
 				$token = $reader->readToken($this, $remainder);
633 634
 				if ($token === null) continue;
634
-				endText();
635
+				$endText();
635 636
 				array_push($tokens, $token);
636 637
 				if ($token->original == null || mb_strlen($token->original) == 0) {
637 638
 					$readerClassName = get_class($reader);
@@ -642,10 +643,10 @@ class MDState {
642 643
 				break;
643 644
 			}
644 645
 			if (!$found) {
645
-				$text += $ch;
646
+				$text .= $ch;
646 647
 			}
647 648
 		}
648
-		endText();
649
+		$endText();
649 650
 		return $tokens;
650 651
 	}
651 652
 
@@ -686,7 +687,7 @@ class MDState {
686 687
 		$anyChanges = false;
687 688
 		do {
688 689
 			$anyChanges = false;
689
-			foreach ($this->root->readersBySubstitutePriority as $readerTuple) {
690
+			foreach ($this->root()->readersBySubstitutePriority as $readerTuple) {
690 691
 				/** @var int */
691 692
 				$pass = $readerTuple[0];
692 693
 				/** @var MDReader */
@@ -744,7 +745,7 @@ class MDState {
744 745
 	 * Defines a URL by reference symbol.
745 746
 	 */
746 747
 	public function defineURL(string $reference, string $url, ?string $title=null) {
747
-		$this->root->referenceToURL[mb_strtolower($reference)] = $url;
748
+		$this->root()->referenceToURL[mb_strtolower($reference)] = $url;
748 749
 		if ($title !== null) $this->root()->referenceToTitle[mb_strtolower($reference)] = $title;
749 750
 	}
750 751
 
@@ -1012,10 +1013,10 @@ class MDHTMLFilter {
1012 1013
 				case '*':
1013 1014
 					return true;
1014 1015
 				case '{classlist}':
1015
-					if (mb_eregi(self::classListRegex, $value)) return true;
1016
+					if (mb_eregi(self::$classListRegex, $value)) return true;
1016 1017
 					break;
1017 1018
 				case '{int}':
1018
-					if (mb_eregi(self::integerRegex, $value)) return true;
1019
+					if (mb_eregi(self::$integerRegex, $value)) return true;
1019 1020
 					break;
1020 1021
 				case '{none}':
1021 1022
 					if ($value === true) return true;
@@ -1024,7 +1025,7 @@ class MDHTMLFilter {
1024 1025
 					if ($this->isValidStyleDeclaration($value)) return true;
1025 1026
 					break;
1026 1027
 				case '{url}':
1027
-					if (mb_eregi(self::permissiveURLRegex, $value)) return true;
1028
+					if (mb_eregi(self::$permissiveURLRegex, $value)) return true;
1028 1029
 					break;
1029 1030
 				default:
1030 1031
 					if ($value === $option) return true;
@@ -1126,7 +1127,7 @@ class MDHTMLTag {
1126 1127
 			if ($value === true) {
1127 1128
 				$html .= " {$safeName}";
1128 1129
 			} else {
1129
-				$escapedValue = MDUtils::escapeHTML("{$value}");
1130
+				$escapedValue = htmlentities("{$value}");
1130 1131
 				$html .= " {$safeName}=\"{$escapedValue}\"";
1131 1132
 			}
1132 1133
 		}
@@ -1605,7 +1606,7 @@ class MDReader {
1605 1606
 		$valuesById = [];
1606 1607
 
1607 1608
 		// Build the graph and compute in-degrees
1608
-		foreach ($arr as $elem) {
1609
+		foreach ($arr as $index => $elem) {
1609 1610
 			$id = $idFn($elem);
1610 1611
 			$graph[$id] = [];
1611 1612
 			$inDegrees[$id] = 0;
@@ -1621,7 +1622,7 @@ class MDReader {
1621 1622
 				$idB = $idFn($elemB);
1622 1623
 				$comparisonResult = $compareFn($elemA, $elemB);
1623 1624
 				if ($comparisonResult < 0) {
1624
-					array_push($graph[$idA], push($idB));
1625
+					array_push($graph[$idA], $idB);
1625 1626
 					$inDegrees[$idB]++;
1626 1627
 				} elseif ($comparisonResult > 0) {
1627 1628
 					array_push($graph[$idB], $idA);
@@ -1632,8 +1633,8 @@ class MDReader {
1632 1633
 
1633 1634
 		// Initialize the queue with zero-inDegree nodes
1634 1635
 		$queue = [];
1635
-		foreach ($inDegrees as $elemId) {
1636
-			if ($inDegrees[$elemId] === 0) {
1636
+		foreach ($inDegrees as $elemId => $degree) {
1637
+			if ($degree === 0) {
1637 1638
 				array_push($queue, $elemId);
1638 1639
 			}
1639 1640
 		}
@@ -1666,7 +1667,7 @@ class MDReader {
1666 1667
 	 * @param MDReader[] $readers
1667 1668
 	 * @return MDReader[]  sorted readers
1668 1669
 	 */
1669
-	public static function sortReaderForBlocks(array &$readers) {
1670
+	public static function sortReaderForBlocks(array &$readers): array {
1670 1671
 		$sorted = $readers;
1671 1672
 		return self::kahnTopologicalSort($sorted, function(MDReader $a, MDReader $b): int {
1672 1673
 			return $a->compareBlockOrdering($b);
@@ -1679,7 +1680,7 @@ class MDReader {
1679 1680
 	 * @param MDReader[] $readers
1680 1681
 	 * @return MDReader[]  sorted readers
1681 1682
 	 */
1682
-	public static function sortReadersForTokenizing(array $readers): array {
1683
+	public static function sortReadersForTokenizing(array &$readers): array {
1683 1684
 		$sorted = $readers;
1684 1685
 		return self::kahnTopologicalSort($sorted, function(MDReader $a, MDReader $b): int {
1685 1686
 			return $a->compareTokenizeOrdering($b);
@@ -1698,20 +1699,20 @@ class MDReader {
1698 1699
 	 * @return MDReader[]  sorted array of tuples with the pass number and
1699 1700
 	 *   reader instance in each
1700 1701
 	 */
1701
-	public static function sortReadersForSubstitution(array $readers): array {
1702
+	public static function sortReadersForSubstitution(array &$readers): array {
1702 1703
 		$tuples = [];
1703 1704
 		$maxPass = 1;
1704 1705
 		foreach ($readers as $reader) {
1705 1706
 			$passCount = $reader->substitutionPassCount();
1706 1707
 			for ($pass = 1; $pass <= $passCount; $pass++) {
1707
-				array_push($tuples, [ $pass, $reader ]);
1708
+				array_push($tuples, [[ $pass, $reader ]]);
1708 1709
 			}
1709 1710
 			$maxPass = max($maxPass, $pass);
1710 1711
 		}
1711 1712
 		$result = [];
1712 1713
 		for ($pass = 1; $pass <= $maxPass; $pass++) {
1713
-			$readersThisPass = array_filter(tuples, fn($tup) => $tup[0] == $pass);
1714
-			$passResult = self::kahnTopologicalSort($readersThisPass, function(MDReader $a, MDReader $b): int {
1714
+			$readersThisPass = array_filter($tuples, fn($tup) => $tup[0] == $pass);
1715
+			$passResult = self::kahnTopologicalSort($readersThisPass, function(array $a, array $b) use ($pass): int {
1715 1716
 				$aReader = $a[1];
1716 1717
 				$bReader = $b[1];
1717 1718
 				return $aReader->compareSubstituteOrdering($bReader, $pass);
@@ -1733,7 +1734,7 @@ class MDUnderlinedHeadingReader extends MDReader {
1733 1734
 		if (!$state->hasLines(2)) return null;
1734 1735
 		$modifier;
1735 1736
 		$contentLine = trim($state->lines[$p++]);
1736
-		[$contentLine, $modifier] = MDTagModifier.fromLine(contentLine, state);
1737
+		[$contentLine, $modifier] = MDTagModifier::fromLine($contentLine, $state);
1737 1738
 		$underLine = trim($state->lines[$p++]);
1738 1739
 		if ($contentLine == '') return null;
1739 1740
 		if (mb_eregi('^=+$', $underLine)) {
@@ -1766,7 +1767,7 @@ class MDHashHeadingReader extends MDReader {
1766 1767
 		$line = $state->lines[$p++];
1767 1768
 		$modifier;
1768 1769
 		[$line, $modifier] = MDTagModifier::fromLine($line, $state);
1769
-		if (!mb_eregi(self::hashHeadingRegex, $line, $groups)) return null;
1770
+		if (!mb_eregi(self::$hashHeadingRegex, $line, $groups)) return null;
1770 1771
 		$state->p = $p;
1771 1772
 		$level = mb_strlen($groups[1]);
1772 1773
 		$content = $groups[2];
@@ -1790,7 +1791,7 @@ class MDSubtextReader extends MDReader {
1790 1791
 		$line = $state->lines[$p++];
1791 1792
 		$modifier;
1792 1793
 		[$line, $modifier] = MDTagModifier::fromLine($line, $state);
1793
-		if (!mb_eregi(self::subtextRegex, $line, $groups)) return null;
1794
+		if (!mb_eregi(self::$subtextRegex, $line, $groups)) return null;
1794 1795
 		$state->p = $p;
1795 1796
 		$content = $groups[1];
1796 1797
 		$block = new MDSubtextNode($state->inlineMarkdownToNodes($content));
@@ -1882,7 +1883,7 @@ class _MDListReader extends MDReader {
1882 1883
 		// Multiline content with no blank lines. Search for new block
1883 1884
 		// boundaries without the benefit of a blank line to demarcate it.
1884 1885
 		for ($p = 1; $p < sizeof($itemLines); $p++) {
1885
-			$line = $itemLines[p];
1886
+			$line = $itemLines[$p];
1886 1887
 			if (mb_eregi('^(?:\\*|\\-|\\+|\\d+\\.)\\s+', $line)) {
1887 1888
 				// Nested list found
1888 1889
 				$firstBlock = $state->inlineMarkdownToNode(implode("\n", array_slice($itemLines, 0, $p)));
@@ -1901,7 +1902,8 @@ class _MDListReader extends MDReader {
1901 1902
 	}
1902 1903
 
1903 1904
 	public function readBlock(MDState $state): ?MDBlockNode {
1904
-		throw new Error(`Abstract readBlock must be overridden in ${this.constructor.name}`);
1905
+		$className = get_class($this);
1906
+		throw new Error("Abstract readBlock must be overridden in {$className}");
1905 1907
 	}
1906 1908
 }
1907 1909
 
@@ -1955,7 +1957,7 @@ class MDOrderedListReader extends _MDListReader {
1955 1957
 			$item = $this->readOrderedListItem($state);
1956 1958
 			if ($item) array_push($items, $item);
1957 1959
 		} while ($item);
1958
-		if (sizeof($items)) return null;
1960
+		if (sizeof($items) == 0) return null;
1959 1961
 		return new MDOrderedListNode($items, $items[0]->ordinal);
1960 1962
 	}
1961 1963
 }
@@ -1973,7 +1975,7 @@ class MDFencedCodeBlockReader extends MDReader {
1973 1975
 		if (!$state->hasLines(2)) return null;
1974 1976
 		$p = $state->p;
1975 1977
 		$openFenceLine = $state->lines[$p++];
1976
-		[$openFenceLine, $modifier] = MDTagModifier->fromLine($openFenceLine, $state);
1978
+		[$openFenceLine, $modifier] = MDTagModifier::fromLine($openFenceLine, $state);
1977 1979
 		if (!mb_eregi('```\s*([a-z0-9]*)\s*$', $openFenceLine, $groups)) return null;
1978 1980
 		$language = mb_strlen($groups[1]) > 0 ? $groups[1] : null;
1979 1981
 		$codeLines = [];
@@ -2023,7 +2025,7 @@ class MDHorizontalRuleReader extends MDReader {
2023 2025
 		$p = $state->p;
2024 2026
 		$line = $state->lines[$p++];
2025 2027
 		[$line, $modifier] = MDTagModifier::fromLine($line, $state);
2026
-		if (mb_eregi(self::horizontalRuleRegex, $line)) {
2028
+		if (mb_eregi(self::$horizontalRuleRegex, $line)) {
2027 2029
 			$state->p = $p;
2028 2030
 			$block = new MDHorizontalRuleNode();
2029 2031
 			if ($modifier) $modifier->applyTo($block);
@@ -2169,34 +2171,34 @@ class MDFootnoteReader extends MDReader {
2169 2171
 	 * @param MDNode[] $footnote
2170 2172
 	 */
2171 2173
 	private function defineFootnote(MDState $state, string $symbol, array $footnote) {
2172
-		$footnotes = $state->root()['footnotes'] ?? [];
2174
+		$footnotes = $state->root()->userInfo['footnotes'] ?? [];
2173 2175
 		$footnotes[$symbol] = $footnote;
2174
-		$state->root()['footnotes'] = $footnotes;
2176
+		$state->root()->userInfo['footnotes'] = $footnotes;
2175 2177
 	}
2176 2178
 
2177 2179
 	private function registerUniqueInstance(MDState $state, string $symbol, int $unique) {
2178
-		$footnoteInstances = $state->root()['footnoteInstances'];
2180
+		$footnoteInstances = $state->root()->userInfo['footnoteInstances'];
2179 2181
 		$instances = $footnoteInstances[$symbol] ?? [];
2180 2182
 		array_push($instances, $unique);
2181 2183
 		$footnoteInstances[$symbol] = $instances;
2182 2184
 	}
2183 2185
 
2184 2186
 	private function idForFootnoteSymbol(MDState $state, string $symbol): int {
2185
-		$footnoteIds = $state->root()['footnoteIds'];
2187
+		$footnoteIds = $state->root()->userInfo['footnoteIds'];
2186 2188
 		$existing = $footnoteIds[$symbol];
2187 2189
 		if ($existing) return $existing;
2188
-		$nextFootnoteId = $state->root()['nextFootnoteId'];
2190
+		$nextFootnoteId = $state->root()->userInfo['nextFootnoteId'];
2189 2191
 		$id = $nextFootnoteId++;
2190 2192
 		$footnoteIds[$symbol] = $id;
2191
-		$state->root()['nextFootnoteId'] = $nextFootnoteId;
2193
+		$state->root()->userInfo['nextFootnoteId'] = $nextFootnoteId;
2192 2194
 		return $id;
2193 2195
 	}
2194 2196
 
2195 2197
 	public function preProcess(MDState $state) {
2196
-		$state->root()['footnoteInstances'] = [];
2197
-		$state->root()['footnotes'] = [];
2198
-		$state->root()['footnoteIds'] = [];
2199
-		$state->root()['nextFootnoteId'] = 1;
2198
+		$state->root()->userInfo['footnoteInstances'] = [];
2199
+		$state->root()->userInfo['footnotes'] = [];
2200
+		$state->root()->userInfo['footnoteIds'] = [];
2201
+		$state->root()->userInfo['nextFootnoteId'] = 1;
2200 2202
 	}
2201 2203
 
2202 2204
 	public function readBlock(MDState $state): ?MDBlockNode {
@@ -2207,7 +2209,7 @@ class MDFootnoteReader extends MDReader {
2207 2209
 		while ($state->hasLines(1, $p)) {
2208 2210
 			$line = $state->lines[$p++];
2209 2211
 			if (mb_eregi('^\\s+', $line)) {
2210
-				$def += "\n" . $line;
2212
+				$def .= "\n" . $line;
2211 2213
 			} else {
2212 2214
 				$p--;
2213 2215
 				break;
@@ -2224,14 +2226,14 @@ class MDFootnoteReader extends MDReader {
2224 2226
 		if (mb_eregi(self::$footnoteWithTitleRegex, $line, $groups)) {
2225 2227
 			return new MDToken($groups[0], MDTokenType::Footnote, $groups[1], $groups[2]);
2226 2228
 		}
2227
-		if (mb_eregi(MDFootnoteReader::footnoteRegex, $line, $groups)) {
2229
+		if (mb_eregi(self::$footnoteRegex, $line, $groups)) {
2228 2230
 			return new MDToken($groups[0], MDTokenType::Footnote, $groups[1]);
2229 2231
 		}
2230 2232
 		return null;
2231 2233
 	}
2232 2234
 
2233 2235
 	public function substituteTokens(MDState $state, int $pass, array &$tokens): bool {
2234
-		if ($match = MDToken::findFirstTokens($tokens, [ MDTokenType::Footnote ])) {
2236
+		if ($match = self::findFirstTokens($tokens, [ MDTokenType::Footnote ])) {
2235 2237
 			$symbol = $match->tokens[0]->content;
2236 2238
 			array_splice($tokens, $match->index, 1, new MDFootnoteNode($symbol));
2237 2239
 			return true;
@@ -2254,7 +2256,7 @@ class MDFootnoteReader extends MDReader {
2254 2256
 				$this->$registerUniqueInstance($state, $node->symbol, $node->occurrenceId);
2255 2257
 			});
2256 2258
 		}
2257
-		if (sizeof($state->footnotes) == 0) return;
2259
+		if (sizeof($state->userInfo['footnotes']) == 0) return;
2258 2260
 		array_push($blocks, new MDFootnoteListNode());
2259 2261
 	}
2260 2262
 
@@ -2294,8 +2296,8 @@ class MDAbbreviationReader extends MDReader {
2294 2296
 	}
2295 2297
 
2296 2298
 	public function preProcess(MDState $state) {
2297
-		$state->root()['abbreviations'] = [];
2298
-		$state->root()['abbreviationRegexes'] = [];
2299
+		$state->root()->userInfo['abbreviations'] = [];
2300
+		$state->root()->userInfo['abbreviationRegexes'] = [];
2299 2301
 	}
2300 2302
 
2301 2303
 	public function readBlock(MDState $state): ?MDBlockNode {
@@ -2314,14 +2316,14 @@ class MDAbbreviationReader extends MDReader {
2314 2316
 	 * @param MDNode[] $blocks
2315 2317
 	 */
2316 2318
 	public function postProcess(MDState $state, array &$blocks) {
2317
-		$abbreviations = $state->root()['abbreviations'];
2318
-		$regexes = $state->root()['abbreviationRegexes'];
2319
-		MDNode::replaceNodes($state, $blocks, function($original) {
2319
+		$abbreviations = $state->root()->userInfo['abbreviations'];
2320
+		$regexes = $state->root()->userInfo['abbreviationRegexes'];
2321
+		MDNode::replaceNodes($state, $blocks, function($original) use ($abbreviations, $regexes) {
2320 2322
 			if (!($original instanceof MDTextNode)) return null;
2321 2323
 			$changed = false;
2322 2324
 			$elems = [ $original->text ]; // mix of strings and MDNodes
2323 2325
 			for ($i = 0; $i < sizeof($elems); $i++) {
2324
-				$text = $elems[i];
2326
+				$text = $elems[$i];
2325 2327
 				if (!is_string($text)) continue;
2326 2328
 				foreach ($abbreviations as $abbreviation) {
2327 2329
 					$index = strpos($text, $abbreviation);
@@ -2628,7 +2630,7 @@ class MDSuperscriptReader extends MDSimplePairInlineReader {
2628 2630
 class MDLinkReader extends MDReader {
2629 2631
 	public function readToken(MDState $state, string $line): ?MDToken {
2630 2632
 		$simpleEmailRegex = "^<(" . MDUtils::$baseEmailRegex . ")>";
2631
-		$simpleURLRegex = "^<(" . MDUtils::$baseURLRegex + ")>";
2633
+		$simpleURLRegex = "^<(" . MDUtils::$baseURLRegex . ")>";
2632 2634
 		if ($groups = MDToken::tokenizeLabel($line)) {
2633 2635
 			return new MDToken($groups[0], MDTokenType::Label, $groups[1]);
2634 2636
 		}
@@ -2901,7 +2903,7 @@ class MDNode {
2901 2903
 				}
2902 2904
 			}
2903 2905
 			$this->children = $children;
2904
-		} else if ($children instanceof MDNode) {
2906
+		} elseif ($children instanceof MDNode) {
2905 2907
 			$this->children = [ $children ];
2906 2908
 		} else {
2907 2909
 			$thisClassName = get_class($this);
@@ -2971,7 +2973,7 @@ class MDNode {
2971 2973
 			$html .= " class=\"{$classList}\"";
2972 2974
 		}
2973 2975
 		if ($this->cssId !== null && mb_strlen($this->cssId) > 0) {
2974
-			$html += " id=\"{$this->cssId}\"";
2976
+			$html .= " id=\"{$this->cssId}\"";
2975 2977
 		}
2976 2978
 		$styles = [];
2977 2979
 		foreach ($this->cssStyles as $key => $value) {
@@ -3041,7 +3043,7 @@ class MDNode {
3041 3043
 	 * @return string HTML string
3042 3044
 	 */
3043 3045
 	public static function arrayToHTML(array $nodes, MDState $state): string {
3044
-		return implode('', array_map(function($node) {
3046
+		return implode('', array_map(function($node) use ($state) {
3045 3047
 			return $node->toHTML($state) . ($node instanceof MDBlockNode ? "\n" : '');
3046 3048
 		}, $nodes));
3047 3049
 	}
@@ -3218,8 +3220,8 @@ class MDCodeBlockNode extends MDBlockNode {
3218 3220
 
3219 3221
 	public function toHTML(MDState $state): string {
3220 3222
 		$languageModifier = ($this->language !== null) ? " class=\"language-{$this->language}\"" : '';
3221
-		return "<pre" . $this->htmlAttributes() . "><code{$languageModifier}>" +
3222
-			MDUtils.escapeHTML($this->text) . "</code></pre>\n";
3223
+		return "<pre" . $this->htmlAttributes() . "><code{$languageModifier}>" .
3224
+			htmlentities($this->text) . "</code></pre>\n";
3223 3225
 	}
3224 3226
 }
3225 3227
 
@@ -3282,7 +3284,7 @@ class MDTableNode extends MDBlockNode {
3282 3284
 		$html .= $this->headerRow->toHTML($state) . "\n";
3283 3285
 		$html .= "</thead>\n";
3284 3286
 		$html .= "<tbody>\n";
3285
-		$html .= MDNode::toHTML($this->bodyRows, $state) + "\n";
3287
+		$html .= MDNode::toHTML($this->bodyRows, $state) . "\n";
3286 3288
 		$html .= "</tbody>\n";
3287 3289
 		$html .= "</table>\n";
3288 3290
 		return $html;
@@ -3353,13 +3355,13 @@ class MDDefinitionListDefinitionNode extends MDBlockNode {
3353 3355
  */
3354 3356
 class MDFootnoteListNode extends MDBlockNode {
3355 3357
 	private function footnoteId(MDState $state, string $symbol): int {
3356
-		$lookup = $state->root()['footnoteIds'];
3358
+		$lookup = $state->root()->userInfo['footnoteIds'];
3357 3359
 		if (!$lookup) return null;
3358 3360
 		return $lookup[$symbol] ?? null;
3359 3361
 	}
3360 3362
 
3361 3363
 	public function toHTML(MDState $state): string {
3362
-		$footnotes = $state->footnotes;
3364
+		$footnotes = $state->userInfo['footnotes'];
3363 3365
 		$symbolOrder = array_keys($footnotes);
3364 3366
 		if (sizeof($footnotes) == 0) return '';
3365 3367
 		$footnoteUniques = $state->root()->footnoteInstances;
@@ -3375,7 +3377,7 @@ class MDFootnoteListNode extends MDBlockNode {
3375 3377
 			$uniques = $footnoteUniques[$symbol];
3376 3378
 			if ($uniques) {
3377 3379
 				foreach ($uniques as $unique) {
3378
-					$html .= " <a href=\"#{$state->root->elementIdPrefix}footnoteref_{$unique}\" class=\"footnote-backref\">↩︎</a>";
3380
+					$html .= " <a href=\"#{$state->root()->elementIdPrefix}footnoteref_{$unique}\" class=\"footnote-backref\">↩︎</a>";
3379 3381
 				}
3380 3382
 			}
3381 3383
 			$html .= "</li>\n";
@@ -3386,7 +3388,7 @@ class MDFootnoteListNode extends MDBlockNode {
3386 3388
 	}
3387 3389
 
3388 3390
 	public function toPlaintext(MDState $state): string {
3389
-		$footnotes = $state->footnotes;
3391
+		$footnotes = $state->userInfo['footnotes'];
3390 3392
 		$symbolOrder = array_keys($footnotes);
3391 3393
 		if (sizeof($footnotes) == 0) return '';
3392 3394
 		$text = '';
@@ -3412,6 +3414,7 @@ class MDTextNode extends MDInlineNode {
3412 3414
 
3413 3415
 	public function __construct(string $text) {
3414 3416
 		parent::__construct([]);
3417
+		if (!is_string($text) || mb_strlen($text) == 0) throw new Error("Meh!");
3415 3418
 		$this->text = $text;
3416 3419
 	}
3417 3420
 
@@ -3806,11 +3809,11 @@ class Markdown {
3806 3809
 	 *
3807 3810
 	 * @param MDReader[] $readers
3808 3811
 	 */
3809
-	public function __construct(?array $readers) {
3812
+	public function __construct(?array $readers=null) {
3810 3813
 		$this->readers = $readers ?? self::allReaders();
3811
-		$this->readersByBlockPriority = MDReader::sortReaderForBlocks($readers);
3812
-		$this->readersByTokenPriority = MDReader::sortReadersForTokenizing($readers);
3813
-		$this->readersBySubstitutePriority = MDReader::sortReadersForSubstitution($readers);
3814
+		$this->readersByBlockPriority = MDReader::sortReaderForBlocks($this->readers);
3815
+		$this->readersByTokenPriority = MDReader::sortReadersForTokenizing($this->readers);
3816
+		$this->readersBySubstitutePriority = MDReader::sortReadersForSubstitution($this->readers);
3814 3817
 		$this->tagFilter = new MDHTMLFilter();
3815 3818
 	}
3816 3819
 
@@ -3844,7 +3847,7 @@ class Markdown {
3844 3847
 		$state->readersBySubstitutePriority = $this->readersBySubstitutePriority;
3845 3848
 		$state->tagFilter = $this->tagFilter;
3846 3849
 		$state->elementIdPrefix = $elementIdPrefix;
3847
-		foreach ($this.readers as $reader) {
3850
+		foreach ($this->readers as $reader) {
3848 3851
 			$reader->preProcess($state);
3849 3852
 		}
3850 3853
 		$nodes = $state->readBlocks();

Loading…
İptal
Kaydet