From 1e453e4b111ada214368f8ec40f9459500f1da2d Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 30 Mar 2022 10:17:43 +0200 Subject: [PATCH 01/12] Create the compatible parser --- src/YamlFrontMatter.php | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/YamlFrontMatter.php b/src/YamlFrontMatter.php index 3678413..4b07905 100644 --- a/src/YamlFrontMatter.php +++ b/src/YamlFrontMatter.php @@ -23,6 +23,45 @@ public static function parse(string $content): Document return new Document($matter, $body); } + /** + * A parser that can handle Markdown that contains Markdown. + * + * Fixes https://github.com/spatie/yaml-front-matter/discussions/30. + * Original code by https://github.com/eklausme + * + * @param string $content + * @return Document + */ + public static function markdownCompatibleParse(string $content): Document + { + $n3dash = 0; // count number of triple dashes + $pos1 = 0; + $pos2 = 0; + $len = strlen($content); + + for ($pos = 0; ; $pos += 3) { + $pos = strpos($content, '---', $pos); + if ($pos === false) return new Document([], $content); // no pair of triple dashes at all + // Are we at end or is next character white space? + if ($pos + 3 == $len || ctype_space(substr($content, $pos + 3, 1))) { + if ($n3dash == 0 && ($pos == 0 || $pos > 0 && substr($content, $pos - 1, 1) == "\n")) { + $n3dash = 1; // found first triple dash + $pos1 = $pos + 3; + } else if ($n3dash == 1 && substr($content, $pos - 1, 1) == "\n") { + // found 2nd properly enclosed triple dash + $n3dash = 2; + $pos2 = $pos + 3; + break; + } + } + } + $matter = substr($content, $pos1, $pos2 - 3 - $pos1); + $body = substr($content, $pos2); + $matter = Yaml::parse($matter); + + return new Document($matter, $body); + } + public static function parseFile(string $path): Document { return static::parse( From 1ff16043c262f95955d737dccb2a8f2509193f1f Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 30 Mar 2022 10:17:50 +0200 Subject: [PATCH 02/12] Add the test --- ...FrontMatterMarkdownCompatibleParseTest.php | 34 +++++++++++++++++++ tests/meta-document.md | 17 ++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/YamlFrontMatterMarkdownCompatibleParseTest.php create mode 100644 tests/meta-document.md diff --git a/tests/YamlFrontMatterMarkdownCompatibleParseTest.php b/tests/YamlFrontMatterMarkdownCompatibleParseTest.php new file mode 100644 index 0000000..916b116 --- /dev/null +++ b/tests/YamlFrontMatterMarkdownCompatibleParseTest.php @@ -0,0 +1,34 @@ +assertInstanceOf(Document::class, $document); + $this->assertEquals(['foo' => 'bar'], $document->matter()); + $this->assertStringContainsString('Lorem ipsum.', $document->body()); + } + + /** @test */ + public function it_can_parse_complex_front_matter_from_a_file() + { + $document = YamlFrontMatter::markdownCompatibleParse( + file_get_contents(__DIR__.'/meta-document.md') + ); + + $this->assertInstanceOf(Document::class, $document); + $this->assertEquals(['foo' => 'bar'], $document->matter()); + $this->assertStringContainsString('Lorem ipsum.', $document->body()); + } +} diff --git a/tests/meta-document.md b/tests/meta-document.md new file mode 100644 index 0000000..852d173 --- /dev/null +++ b/tests/meta-document.md @@ -0,0 +1,17 @@ +--- +foo: bar +--- + +Lorem ipsum. + +A paragraph in a Markdown Post. +This file contains a Markdown code block that the original parser does not handle. +See https://github.com/spatie/yaml-front-matter/discussions/30. + +``` +--- +title: The Title of Markdown Code +--- + +A paragraph in Markdown Code +``` From 853b4780b51ecf6088b19b0dffd3436a81b899d3 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Fri, 1 Apr 2022 12:01:37 +0200 Subject: [PATCH 03/12] Refactor the code to be more readable --- src/YamlFrontMatter.php | 74 ++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/src/YamlFrontMatter.php b/src/YamlFrontMatter.php index 4b07905..017ff04 100644 --- a/src/YamlFrontMatter.php +++ b/src/YamlFrontMatter.php @@ -26,39 +26,73 @@ public static function parse(string $content): Document /** * A parser that can handle Markdown that contains Markdown. * + * Attempts to follow the practices defined in https://jekyllrb.com/docs/front-matter/. + * * Fixes https://github.com/spatie/yaml-front-matter/discussions/30. - * Original code by https://github.com/eklausme * * @param string $content * @return Document */ public static function markdownCompatibleParse(string $content): Document { - $n3dash = 0; // count number of triple dashes - $pos1 = 0; - $pos2 = 0; - $len = strlen($content); - - for ($pos = 0; ; $pos += 3) { - $pos = strpos($content, '---', $pos); - if ($pos === false) return new Document([], $content); // no pair of triple dashes at all - // Are we at end or is next character white space? - if ($pos + 3 == $len || ctype_space(substr($content, $pos + 3, 1))) { - if ($n3dash == 0 && ($pos == 0 || $pos > 0 && substr($content, $pos - 1, 1) == "\n")) { - $n3dash = 1; // found first triple dash - $pos1 = $pos + 3; - } else if ($n3dash == 1 && substr($content, $pos - 1, 1) == "\n") { - // found 2nd properly enclosed triple dash - $n3dash = 2; - $pos2 = $pos + 3; + // Turn the string into an array of lines, making the code easier to understand + $lines = explode("\n", $content); + + // Find the line numbers of the front matter start and end blocks + $frontMatterControlBlockIndex = []; + foreach ($lines as $lineNumber => $lineContents) { + // If the line starts with three dashes, it's a front matter control block + if (substr($lineContents, 0, 3) === '---') { + // The line contains the front matter control block + if (sizeof($frontMatterControlBlockIndex) === 0) { + // This is the first control block + $frontMatterControlBlockIndex['start'] = $lineNumber; + } elseif (sizeof($frontMatterControlBlockIndex) === 1) { + // This is the second control block + $frontMatterControlBlockIndex['end'] = $lineNumber; + // We can now break the loop because we found the end of the actual front matter break; } } } - $matter = substr($content, $pos1, $pos2 - 3 - $pos1); - $body = substr($content, $pos2); + + // If there are no front matter blocks, we just return the content as is + if (sizeof($frontMatterControlBlockIndex) < 2) { + return new Document([], $content); + } + + // Construct the new line arrays + $matter = []; + $body = []; + + // Loop through the original line array + foreach ($lines as $lineNumber => $lineContents) { + // Compare the line number to the one of the closing front matter block + // to determine if we're in the front matter or the body + if ($lineNumber <= $frontMatterControlBlockIndex['end']) { + $matter[] = $lineContents; + } else { + $body[] = $lineContents; + } + } + + // Remove the dashes + unset($matter[$frontMatterControlBlockIndex['start']]); + unset($matter[$frontMatterControlBlockIndex['end']]); + + // If the first line of the body is empty, remove it + if (trim($body[0]) === '') { + unset($body[0]); + } + + // Convert the lines back into strings + $matter = implode("\n", $matter); + $body = implode("\n", $body); + + // Parse the front matter $matter = Yaml::parse($matter); + // Return the document return new Document($matter, $body); } From 93f454564d95ec8e430f3a374a571e525e409e86 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Fri, 1 Apr 2022 12:01:48 +0200 Subject: [PATCH 04/12] Add more tests --- ...FrontMatterMarkdownCompatibleParseTest.php | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/YamlFrontMatterMarkdownCompatibleParseTest.php b/tests/YamlFrontMatterMarkdownCompatibleParseTest.php index 916b116..dbeaba5 100644 --- a/tests/YamlFrontMatterMarkdownCompatibleParseTest.php +++ b/tests/YamlFrontMatterMarkdownCompatibleParseTest.php @@ -31,4 +31,46 @@ public function it_can_parse_complex_front_matter_from_a_file() $this->assertEquals(['foo' => 'bar'], $document->matter()); $this->assertStringContainsString('Lorem ipsum.', $document->body()); } + + /** @test */ + public function it_separates_the_front_matter_from_the_body() + { + $document = YamlFrontMatter::markdownCompatibleParse( + "---\ntitle: Front Matter\n---\n\nLorem ipsum." + ); + + $this->assertInstanceOf(Document::class, $document); + + // This implicitly asserts that the front matter does not contain any markdown + $this->assertEquals(['title' => 'Front Matter'], $document->matter()); + // This implicitly asserts that the body does not contain any front matter remnants + $this->assertEquals('Lorem ipsum.', $document->body()); + } + + /** @test */ + public function it_leaves_string_without_front_matter_intact() + { + $document = YamlFrontMatter::markdownCompatibleParse( + "Lorem ipsum." + ); + + $this->assertInstanceOf(Document::class, $document); + $this->assertEmpty($document->matter()); + $this->assertEquals('Lorem ipsum.', $document->body()); + } + + /** @test */ + public function it_can_parse_a_file_partial_front_matter() + { + // If there is only one YAML control block, (---) the front matter is invalid + // and the document should be interpreted as having no front matter. + + $document = YamlFrontMatter::markdownCompatibleParse( + "---\ntitle: Front Matter\n\nLorem ipsum." + ); + + $this->assertInstanceOf(Document::class, $document); + $this->assertEmpty($document->matter()); + $this->assertEquals("---\ntitle: Front Matter\n\nLorem ipsum.", $document->body()); + } } From 3616b8034dc160036245636a61f2d5976d3a8e4a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 4 Apr 2022 22:21:13 +0200 Subject: [PATCH 05/12] Start refactoring complex method to its own class --- src/ComplexMarkdownParser.php | 139 ++++++++++++++++++++++++++++++++++ src/YamlFrontMatter.php | 60 +-------------- 2 files changed, 140 insertions(+), 59 deletions(-) create mode 100644 src/ComplexMarkdownParser.php diff --git a/src/ComplexMarkdownParser.php b/src/ComplexMarkdownParser.php new file mode 100644 index 0000000..f9c6440 --- /dev/null +++ b/src/ComplexMarkdownParser.php @@ -0,0 +1,139 @@ +content = $content; + // Turn the string into an array of lines, making the code easier to understand + $this->lines = explode("\n", $this->content); + } + + /** + * A parser that can handle Markdown that contains Markdown. + * @return Document + */ + public function parse(): Document + { + // Find the line numbers of the front matter start and end blocks + $this->findFrontMatterStartAndEndLineNumbers(); + + // If there are no front matter blocks, we just return the content as is + if (!$this->hasFrontMatter()) { + return new Document([], $this->content); + } + + // Construct the new line arrays + $matter = []; + $body = []; + + // Loop through the original line array + foreach ($this->lines as $lineNumber => $lineContents) { + // Compare the line number to the one of the closing front matter block + // to determine if we're in the front matter or the body + if ($lineNumber <= $this->frontMatterEndLine) { + $matter[] = $lineContents; + } else { + $body[] = $lineContents; + } + } + + // Remove the dashes + unset($matter[$this->frontMatterStartLine]); + unset($matter[$this->frontMatterEndLine]); + + // If the first line of the body is empty, remove it + if (trim($body[0]) === '') { + unset($body[0]); + } + + // Convert the lines back into strings + $matter = implode("\n", $matter); + $body = implode("\n", $body); + + // Parse the front matter + $matter = Yaml::parse($matter); + + // Return the document + return new Document($matter, $body); + } + + /** + * Is a given line a front matter control block? + * + * A control block is a line that starts with three dashes. + * + * @param string $line + * @return bool + */ + private function isFrontMatterControlBlock(string $line): bool + { + return substr($line, 0, 3) === '---'; + } + + /** + * Does the current document have front matter? + * @return bool + */ + private function hasFrontMatter(): bool + { + return $this->frontMatterStartLine !== null && $this->frontMatterEndLine !== null; + } + + private function findFrontMatterStartAndEndLineNumbers() + { + foreach ($this->lines as $lineNumber => $lineContents) { + // If the line starts with three dashes, it's a front matter control block + if ($this->isFrontMatterControlBlock($lineContents)) { + // The line contains the front matter control block + if (!isset($this->frontMatterStartLine)) { + // This is the first control block + $this->frontMatterStartLine = $lineNumber; + } elseif (!isset($this->frontMatterEndLine)) { + // This is the second control block + $this->frontMatterEndLine = $lineNumber; + // We can now break the loop because we found the end of the actual front matter + break; + } + } + } + } +} diff --git a/src/YamlFrontMatter.php b/src/YamlFrontMatter.php index 017ff04..4ab5f5b 100644 --- a/src/YamlFrontMatter.php +++ b/src/YamlFrontMatter.php @@ -35,65 +35,7 @@ public static function parse(string $content): Document */ public static function markdownCompatibleParse(string $content): Document { - // Turn the string into an array of lines, making the code easier to understand - $lines = explode("\n", $content); - - // Find the line numbers of the front matter start and end blocks - $frontMatterControlBlockIndex = []; - foreach ($lines as $lineNumber => $lineContents) { - // If the line starts with three dashes, it's a front matter control block - if (substr($lineContents, 0, 3) === '---') { - // The line contains the front matter control block - if (sizeof($frontMatterControlBlockIndex) === 0) { - // This is the first control block - $frontMatterControlBlockIndex['start'] = $lineNumber; - } elseif (sizeof($frontMatterControlBlockIndex) === 1) { - // This is the second control block - $frontMatterControlBlockIndex['end'] = $lineNumber; - // We can now break the loop because we found the end of the actual front matter - break; - } - } - } - - // If there are no front matter blocks, we just return the content as is - if (sizeof($frontMatterControlBlockIndex) < 2) { - return new Document([], $content); - } - - // Construct the new line arrays - $matter = []; - $body = []; - - // Loop through the original line array - foreach ($lines as $lineNumber => $lineContents) { - // Compare the line number to the one of the closing front matter block - // to determine if we're in the front matter or the body - if ($lineNumber <= $frontMatterControlBlockIndex['end']) { - $matter[] = $lineContents; - } else { - $body[] = $lineContents; - } - } - - // Remove the dashes - unset($matter[$frontMatterControlBlockIndex['start']]); - unset($matter[$frontMatterControlBlockIndex['end']]); - - // If the first line of the body is empty, remove it - if (trim($body[0]) === '') { - unset($body[0]); - } - - // Convert the lines back into strings - $matter = implode("\n", $matter); - $body = implode("\n", $body); - - // Parse the front matter - $matter = Yaml::parse($matter); - - // Return the document - return new Document($matter, $body); + return (new ComplexMarkdownParser($content))->parse(); } public static function parseFile(string $path): Document From 3b528d94c192f3ac59e19eeaa0f8b1143642313b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 4 Apr 2022 22:52:15 +0200 Subject: [PATCH 06/12] Extract the complex logical parts --- src/ComplexMarkdownParser.php | 124 +++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 46 deletions(-) diff --git a/src/ComplexMarkdownParser.php b/src/ComplexMarkdownParser.php index f9c6440..50cc5ea 100644 --- a/src/ComplexMarkdownParser.php +++ b/src/ComplexMarkdownParser.php @@ -23,7 +23,8 @@ class ComplexMarkdownParser protected $content; /** - * The document string as an array of lines + * The document string as an array of lines, + * making it easier to understand and work with. * @var array */ protected $lines; @@ -43,7 +44,6 @@ class ComplexMarkdownParser public function __construct(string $content) { $this->content = $content; - // Turn the string into an array of lines, making the code easier to understand $this->lines = explode("\n", $this->content); } @@ -53,46 +53,17 @@ public function __construct(string $content) */ public function parse(): Document { - // Find the line numbers of the front matter start and end blocks $this->findFrontMatterStartAndEndLineNumbers(); - // If there are no front matter blocks, we just return the content as is if (!$this->hasFrontMatter()) { return new Document([], $this->content); } - // Construct the new line arrays - $matter = []; - $body = []; - - // Loop through the original line array - foreach ($this->lines as $lineNumber => $lineContents) { - // Compare the line number to the one of the closing front matter block - // to determine if we're in the front matter or the body - if ($lineNumber <= $this->frontMatterEndLine) { - $matter[] = $lineContents; - } else { - $body[] = $lineContents; - } - } + $matter = $this->getFrontMatter(); + $body = $this->getBody(); - // Remove the dashes - unset($matter[$this->frontMatterStartLine]); - unset($matter[$this->frontMatterEndLine]); - - // If the first line of the body is empty, remove it - if (trim($body[0]) === '') { - unset($body[0]); - } - - // Convert the lines back into strings - $matter = implode("\n", $matter); - $body = implode("\n", $body); - - // Parse the front matter $matter = Yaml::parse($matter); - // Return the document return new Document($matter, $body); } @@ -100,7 +71,6 @@ public function parse(): Document * Is a given line a front matter control block? * * A control block is a line that starts with three dashes. - * * @param string $line * @return bool */ @@ -109,6 +79,37 @@ private function isFrontMatterControlBlock(string $line): bool return substr($line, 0, 3) === '---'; } + + /** + * Find the line numbers of the front matter start and end blocks. + */ + private function findFrontMatterStartAndEndLineNumbers() + { + foreach ($this->lines as $lineNumber => $lineContents) { + if ($this->isFrontMatterControlBlock($lineContents)) { + $this->setFrontMatterLineNumber($lineNumber); + } + } + } + + /** + * Find and set the line numbers of the front matter start and end blocks. + * + * Ignores any additional front matter blocks found later in the document. + * @param $lineNumber + * @return void + */ + private function setFrontMatterLineNumber($lineNumber) + { + if (!isset($this->frontMatterStartLine)) { + $this->frontMatterStartLine = $lineNumber; + return; + } + if (!isset($this->frontMatterEndLine)) { + $this->frontMatterEndLine = $lineNumber; + } + } + /** * Does the current document have front matter? * @return bool @@ -118,22 +119,53 @@ private function hasFrontMatter(): bool return $this->frontMatterStartLine !== null && $this->frontMatterEndLine !== null; } - private function findFrontMatterStartAndEndLineNumbers() + /** + * Get the front matter from the document. + * + * Works by collecting all the lines before the end block, + * while skipping over the actual control blocks. + * @return string + */ + private function getFrontMatter(): string { + $matter = []; foreach ($this->lines as $lineNumber => $lineContents) { - // If the line starts with three dashes, it's a front matter control block - if ($this->isFrontMatterControlBlock($lineContents)) { - // The line contains the front matter control block - if (!isset($this->frontMatterStartLine)) { - // This is the first control block - $this->frontMatterStartLine = $lineNumber; - } elseif (!isset($this->frontMatterEndLine)) { - // This is the second control block - $this->frontMatterEndLine = $lineNumber; - // We can now break the loop because we found the end of the actual front matter - break; + if ($lineNumber <= $this->frontMatterEndLine) { + if (!$this->isFrontMatterControlBlock($lineContents)) { + $matter[] = $lineContents; } } } + return implode("\n", $matter); + } + + /** + * Get the body of the document. + * + * Works by collecting all the lines after the end block. + * @return string + */ + private function getBody(): string + { + $body = []; + foreach ($this->lines as $lineNumber => $lineContents) { + if ($lineNumber > $this->frontMatterEndLine) { + $body[] = $lineContents; + } + } + return implode("\n", $this->trimBody($body)); + } + + /** + * If the first line of the body is empty, remove it + * @param array $body + * @return array + */ + private function trimBody(array $body): array + { + if (trim($body[0]) === '') { + unset($body[0]); + } + return $body; } } From 2176038f1dadefc219d4fc29eca034024f222b3f Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 5 Apr 2022 10:23:43 +0200 Subject: [PATCH 07/12] Update the PHPDocs --- src/ComplexMarkdownParser.php | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/ComplexMarkdownParser.php b/src/ComplexMarkdownParser.php index 50cc5ea..f3c3864 100644 --- a/src/ComplexMarkdownParser.php +++ b/src/ComplexMarkdownParser.php @@ -8,16 +8,11 @@ * A parser that can handle Markdown that contains Markdown. * * Attempts to follow the practices defined in https://jekyllrb.com/docs/front-matter/. - * - * Fixes https://github.com/spatie/yaml-front-matter/discussions/30. - * - * @param string $content - * @return Document */ class ComplexMarkdownParser { /** - * The Markdown content + * The Markdown content. * @var string */ protected $content; @@ -30,17 +25,21 @@ class ComplexMarkdownParser protected $lines; /** - * The line number of the starting front matter control block + * The line number of the starting front matter control block. * @var int|null */ public $frontMatterStartLine; /** - * The line number of the ending front matter control block + * The line number of the ending front matter control block. * @var int|null */ public $frontMatterEndLine; + /** + * Construct the parser. + * @param string $content The Markdown content to parse. + */ public function __construct(string $content) { $this->content = $content; @@ -48,7 +47,7 @@ public function __construct(string $content) } /** - * A parser that can handle Markdown that contains Markdown. + * Parse the Markdown content. * @return Document */ public function parse(): Document @@ -79,9 +78,8 @@ private function isFrontMatterControlBlock(string $line): bool return substr($line, 0, 3) === '---'; } - /** - * Find the line numbers of the front matter start and end blocks. + * Find and set the line numbers of the front matter start and end blocks. */ private function findFrontMatterStartAndEndLineNumbers() { @@ -93,13 +91,12 @@ private function findFrontMatterStartAndEndLineNumbers() } /** - * Find and set the line numbers of the front matter start and end blocks. + * Set the appropriate line number for the front matter start or end block. * - * Ignores any additional front matter blocks found later in the document. - * @param $lineNumber + * @param $lineNumber int the current line number of the search. * @return void */ - private function setFrontMatterLineNumber($lineNumber) + private function setFrontMatterLineNumber(int $lineNumber) { if (!isset($this->frontMatterStartLine)) { $this->frontMatterStartLine = $lineNumber; @@ -116,7 +113,7 @@ private function setFrontMatterLineNumber($lineNumber) */ private function hasFrontMatter(): bool { - return $this->frontMatterStartLine !== null && $this->frontMatterEndLine !== null; + return ($this->frontMatterStartLine !== null) && ($this->frontMatterEndLine !== null); } /** @@ -157,7 +154,7 @@ private function getBody(): string } /** - * If the first line of the body is empty, remove it + * If the first line of the body is empty, remove it. * @param array $body * @return array */ From e35568532030928aadaabda7925877f7d6c8d745 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 6 Apr 2022 12:28:57 +0200 Subject: [PATCH 08/12] Remove PHPDocs --- src/ComplexMarkdownParser.php | 72 +---------------------------------- 1 file changed, 2 insertions(+), 70 deletions(-) diff --git a/src/ComplexMarkdownParser.php b/src/ComplexMarkdownParser.php index f3c3864..07ab99e 100644 --- a/src/ComplexMarkdownParser.php +++ b/src/ComplexMarkdownParser.php @@ -4,52 +4,21 @@ use Symfony\Component\Yaml\Yaml; -/** - * A parser that can handle Markdown that contains Markdown. - * - * Attempts to follow the practices defined in https://jekyllrb.com/docs/front-matter/. - */ class ComplexMarkdownParser { - /** - * The Markdown content. - * @var string - */ protected $content; - - /** - * The document string as an array of lines, - * making it easier to understand and work with. - * @var array - */ protected $lines; - /** - * The line number of the starting front matter control block. - * @var int|null - */ public $frontMatterStartLine; - - /** - * The line number of the ending front matter control block. - * @var int|null - */ public $frontMatterEndLine; - /** - * Construct the parser. - * @param string $content The Markdown content to parse. - */ public function __construct(string $content) { $this->content = $content; $this->lines = explode("\n", $this->content); } - /** - * Parse the Markdown content. - * @return Document - */ + public function parse(): Document { $this->findFrontMatterStartAndEndLineNumbers(); @@ -66,21 +35,11 @@ public function parse(): Document return new Document($matter, $body); } - /** - * Is a given line a front matter control block? - * - * A control block is a line that starts with three dashes. - * @param string $line - * @return bool - */ private function isFrontMatterControlBlock(string $line): bool { return substr($line, 0, 3) === '---'; } - /** - * Find and set the line numbers of the front matter start and end blocks. - */ private function findFrontMatterStartAndEndLineNumbers() { foreach ($this->lines as $lineNumber => $lineContents) { @@ -90,12 +49,6 @@ private function findFrontMatterStartAndEndLineNumbers() } } - /** - * Set the appropriate line number for the front matter start or end block. - * - * @param $lineNumber int the current line number of the search. - * @return void - */ private function setFrontMatterLineNumber(int $lineNumber) { if (!isset($this->frontMatterStartLine)) { @@ -107,22 +60,12 @@ private function setFrontMatterLineNumber(int $lineNumber) } } - /** - * Does the current document have front matter? - * @return bool - */ private function hasFrontMatter(): bool { return ($this->frontMatterStartLine !== null) && ($this->frontMatterEndLine !== null); } - /** - * Get the front matter from the document. - * - * Works by collecting all the lines before the end block, - * while skipping over the actual control blocks. - * @return string - */ + private function getFrontMatter(): string { $matter = []; @@ -136,12 +79,6 @@ private function getFrontMatter(): string return implode("\n", $matter); } - /** - * Get the body of the document. - * - * Works by collecting all the lines after the end block. - * @return string - */ private function getBody(): string { $body = []; @@ -153,11 +90,6 @@ private function getBody(): string return implode("\n", $this->trimBody($body)); } - /** - * If the first line of the body is empty, remove it. - * @param array $body - * @return array - */ private function trimBody(array $body): array { if (trim($body[0]) === '') { From 7e4f8737019ccde781efd46222ed2ef150d7419b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 6 Apr 2022 12:51:22 +0200 Subject: [PATCH 09/12] Replace private helper function scope to protected --- src/ComplexMarkdownParser.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/ComplexMarkdownParser.php b/src/ComplexMarkdownParser.php index 07ab99e..e343408 100644 --- a/src/ComplexMarkdownParser.php +++ b/src/ComplexMarkdownParser.php @@ -18,7 +18,6 @@ public function __construct(string $content) $this->lines = explode("\n", $this->content); } - public function parse(): Document { $this->findFrontMatterStartAndEndLineNumbers(); @@ -35,12 +34,12 @@ public function parse(): Document return new Document($matter, $body); } - private function isFrontMatterControlBlock(string $line): bool + protected function isFrontMatterControlBlock(string $line): bool { return substr($line, 0, 3) === '---'; } - private function findFrontMatterStartAndEndLineNumbers() + protected function findFrontMatterStartAndEndLineNumbers() { foreach ($this->lines as $lineNumber => $lineContents) { if ($this->isFrontMatterControlBlock($lineContents)) { @@ -49,7 +48,7 @@ private function findFrontMatterStartAndEndLineNumbers() } } - private function setFrontMatterLineNumber(int $lineNumber) + protected function setFrontMatterLineNumber(int $lineNumber) { if (!isset($this->frontMatterStartLine)) { $this->frontMatterStartLine = $lineNumber; @@ -60,13 +59,13 @@ private function setFrontMatterLineNumber(int $lineNumber) } } - private function hasFrontMatter(): bool + protected function hasFrontMatter(): bool { return ($this->frontMatterStartLine !== null) && ($this->frontMatterEndLine !== null); } - - private function getFrontMatter(): string + + protected function getFrontMatter(): string { $matter = []; foreach ($this->lines as $lineNumber => $lineContents) { @@ -79,7 +78,7 @@ private function getFrontMatter(): string return implode("\n", $matter); } - private function getBody(): string + protected function getBody(): string { $body = []; foreach ($this->lines as $lineNumber => $lineContents) { @@ -90,7 +89,7 @@ private function getBody(): string return implode("\n", $this->trimBody($body)); } - private function trimBody(array $body): array + protected function trimBody(array $body): array { if (trim($body[0]) === '') { unset($body[0]); From 7572d355d77327a8039d5d93d02b7091ad3e14c4 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 6 Apr 2022 12:59:41 +0200 Subject: [PATCH 10/12] Move helpers to end of document --- src/ComplexMarkdownParser.php | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/ComplexMarkdownParser.php b/src/ComplexMarkdownParser.php index e343408..135e8cc 100644 --- a/src/ComplexMarkdownParser.php +++ b/src/ComplexMarkdownParser.php @@ -34,11 +34,6 @@ public function parse(): Document return new Document($matter, $body); } - protected function isFrontMatterControlBlock(string $line): bool - { - return substr($line, 0, 3) === '---'; - } - protected function findFrontMatterStartAndEndLineNumbers() { foreach ($this->lines as $lineNumber => $lineContents) { @@ -54,17 +49,12 @@ protected function setFrontMatterLineNumber(int $lineNumber) $this->frontMatterStartLine = $lineNumber; return; } + if (!isset($this->frontMatterEndLine)) { $this->frontMatterEndLine = $lineNumber; } } - protected function hasFrontMatter(): bool - { - return ($this->frontMatterStartLine !== null) && ($this->frontMatterEndLine !== null); - } - - protected function getFrontMatter(): string { $matter = []; @@ -96,4 +86,15 @@ protected function trimBody(array $body): array } return $body; } + + protected function hasFrontMatter(): bool + { + return ($this->frontMatterStartLine !== null) && ($this->frontMatterEndLine !== null); + } + + protected function isFrontMatterControlBlock(string $line): bool + { + return substr($line, 0, 3) === '---'; + } + } From b664489e596775c69d9c7712dfbd4dca674b5ae0 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 6 Apr 2022 13:01:26 +0200 Subject: [PATCH 11/12] Add test for differing end of line characters --- ...FrontMatterMarkdownCompatibleParseTest.php | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/tests/YamlFrontMatterMarkdownCompatibleParseTest.php b/tests/YamlFrontMatterMarkdownCompatibleParseTest.php index dbeaba5..9702c56 100644 --- a/tests/YamlFrontMatterMarkdownCompatibleParseTest.php +++ b/tests/YamlFrontMatterMarkdownCompatibleParseTest.php @@ -31,7 +31,7 @@ public function it_can_parse_complex_front_matter_from_a_file() $this->assertEquals(['foo' => 'bar'], $document->matter()); $this->assertStringContainsString('Lorem ipsum.', $document->body()); } - + /** @test */ public function it_separates_the_front_matter_from_the_body() { @@ -46,13 +46,13 @@ public function it_separates_the_front_matter_from_the_body() // This implicitly asserts that the body does not contain any front matter remnants $this->assertEquals('Lorem ipsum.', $document->body()); } - + /** @test */ public function it_leaves_string_without_front_matter_intact() { $document = YamlFrontMatter::markdownCompatibleParse( "Lorem ipsum." - ); + ); $this->assertInstanceOf(Document::class, $document); $this->assertEmpty($document->matter()); @@ -73,4 +73,28 @@ public function it_can_parse_a_file_partial_front_matter() $this->assertEmpty($document->matter()); $this->assertEquals("---\ntitle: Front Matter\n\nLorem ipsum.", $document->body()); } + + /** @test */ + public function it_can_parse_a_string_with_unix_line_endings() + { + $document = YamlFrontMatter::markdownCompatibleParse( + "---\nfoo: bar\n---\n\nLorem ipsum." + ); + + $this->assertInstanceOf(Document::class, $document); + $this->assertEquals(['foo' => 'bar'], $document->matter()); + $this->assertStringContainsString('Lorem ipsum.', $document->body()); + } + + /** @test */ + public function it_can_parse_a_string_with_windows_line_endings() + { + $document = YamlFrontMatter::markdownCompatibleParse( + "---\r\nfoo: bar\r\n---\r\n\r\nLorem ipsum." + ); + + $this->assertInstanceOf(Document::class, $document); + $this->assertEquals(['foo' => 'bar'], $document->matter()); + $this->assertStringContainsString('Lorem ipsum.', $document->body()); + } } From 716efeaabb15141a5e92f735eaeea660a4b169e5 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 6 Apr 2022 13:04:28 +0200 Subject: [PATCH 12/12] Format the code --- src/ComplexMarkdownParser.php | 3 +-- src/YamlFrontMatter.php | 7 ------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/ComplexMarkdownParser.php b/src/ComplexMarkdownParser.php index 135e8cc..365b5aa 100644 --- a/src/ComplexMarkdownParser.php +++ b/src/ComplexMarkdownParser.php @@ -49,7 +49,7 @@ protected function setFrontMatterLineNumber(int $lineNumber) $this->frontMatterStartLine = $lineNumber; return; } - + if (!isset($this->frontMatterEndLine)) { $this->frontMatterEndLine = $lineNumber; } @@ -96,5 +96,4 @@ protected function isFrontMatterControlBlock(string $line): bool { return substr($line, 0, 3) === '---'; } - } diff --git a/src/YamlFrontMatter.php b/src/YamlFrontMatter.php index 4ab5f5b..a1e1b68 100644 --- a/src/YamlFrontMatter.php +++ b/src/YamlFrontMatter.php @@ -25,13 +25,6 @@ public static function parse(string $content): Document /** * A parser that can handle Markdown that contains Markdown. - * - * Attempts to follow the practices defined in https://jekyllrb.com/docs/front-matter/. - * - * Fixes https://github.com/spatie/yaml-front-matter/discussions/30. - * - * @param string $content - * @return Document */ public static function markdownCompatibleParse(string $content): Document {