Skip to content

Commit

Permalink
fixed WordpressParser issue with offsets, fixed RegularParser bug whe…
Browse files Browse the repository at this point in the history
…n nesting just-closed shortcodes, removed internal isRoot variable
  • Loading branch information
thunderer committed Jan 25, 2016
1 parent 6b8bf5d commit 4cda52f
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 17 deletions.
52 changes: 40 additions & 12 deletions src/Parser/RegularParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ public function parse($text)
while($this->position < $this->tokensCount && !$this->lookahead(self::TOKEN_OPEN)) {
$this->position++;
}
foreach($this->shortcode(true) ?: array() as $shortcode) {
$names = array();
$this->beginBacktrack();
foreach($this->shortcode($names) ?: array() as $shortcode) {
$shortcodes[] = $shortcode;
}
}
Expand All @@ -62,15 +64,14 @@ private function getObject($name, $parameters, $bbCode, $offset, $content, $text

/* --- RULES ----------------------------------------------------------- */

private function shortcode($isRoot)
private function shortcode(array &$names)
{
$name = null;
$offset = null;

$setName = function(array $token) use(&$name) { $name = $token[1]; };
$setOffset = function(array $token) use(&$offset) { $offset = $token[2]; };

$isRoot && $this->beginBacktrack();
if(!$this->match(self::TOKEN_OPEN, $setOffset, true)) { return false; }
if(!$this->match(self::TOKEN_STRING, $setName, false)) { return false; }
if($this->lookahead(self::TOKEN_STRING, null)) { return false; }
Expand All @@ -89,22 +90,32 @@ private function shortcode($isRoot)
// just-closed or with-content
if(!$this->match(self::TOKEN_CLOSE)) { return false; }
$this->beginBacktrack();
list($content, $shortcodes) = $this->content($name);
$names[] = $name;
list($content, $shortcodes, $closingName) = $this->content($names);
if(null !== $closingName && $closingName !== $name) {
array_pop($names);
array_pop($this->backtracks);
array_pop($this->backtracks);

return $closingName;
}
if(false === $content) {
$this->backtrack(false);
$text = $this->backtrack(false);

return array_merge(array($this->getObject($name, $parameters, $bbCode, $offset, null, $text)), $shortcodes);
}
array_pop($this->backtracks);
if(!$this->close($name)) { return false; }
$content = $this->getBacktrack();
if(!$this->close($names)) { return false; }

return array($this->getObject($name, $parameters, $bbCode, $offset, $content, $this->getBacktrack()));
}

private function content($name)
private function content(array &$names)
{
$content = null;
$shortcodes = array();
$closingName = null;
$appendContent = function(array $token) use(&$content) { $content .= $token[1]; };

while($this->position < $this->tokensCount) {
Expand All @@ -113,20 +124,25 @@ private function content($name)
}

$this->beginBacktrack();
$matchedShortcodes = $this->shortcode(false);
$matchedShortcodes = $this->shortcode($names);
if(is_string($matchedShortcodes)) {
$closingName = $matchedShortcodes;
break;
}
if(false !== $matchedShortcodes) {
$shortcodes = array_merge($shortcodes, $matchedShortcodes);
continue;
}
$this->backtrack();

$this->beginBacktrack();
if(false !== $this->close($name)) {
if(false !== ($closingName = $this->close($names))) {
if(null === $content) { $content = ''; }
$this->backtrack();
$shortcodes = array();
break;
}
$closingName = null;
$this->backtrack();
if($this->position < $this->tokensCount) {
$shortcodes = array();
Expand All @@ -136,10 +152,10 @@ private function content($name)
$this->match(null, $appendContent);
}

return array($this->position < $this->tokensCount ? $content : false, $shortcodes);
return array($this->position < $this->tokensCount ? $content : false, $shortcodes, $closingName);
}

private function close($openingName)
private function close(array &$names)
{
$closingName = null;
$setName = function(array $token) use(&$closingName) { $closingName = $token[1]; };
Expand All @@ -149,7 +165,7 @@ private function close($openingName)
if(!$this->match(self::TOKEN_STRING, $setName, true)) { return false; }
if(!$this->match(self::TOKEN_CLOSE)) { return false; }

return $openingName === $closingName;
return in_array($closingName, $names) ? $closingName : false;
}

private function bbCode()
Expand Down Expand Up @@ -196,6 +212,18 @@ private function value()

/* --- PARSER ---------------------------------------------------------- */

/**
* This method is used only for debugging purposes. DO NOT DELETE.
*
* @return array
*/
private function listBacktracks()
{
return array_map(function(array $backtrack) {
return implode('', array_column($backtrack, 1));
}, $this->backtracks);
}

private function beginBacktrack()
{
$this->backtracks[] = array();
Expand Down
5 changes: 1 addition & 4 deletions src/Parser/WordpressParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,7 @@ public function parse($text)
$parameters = static::parseParameters($matches[3][$i][0]);
$content = $matches[5][$i][0] ?: null;
$match = $matches[0][$i][0];
// TODO check how WordPress deals with unicode offset problems
// which can be solved by changing the line below to
// $offset = mb_strlen(substr($text, 0, $matches[0][$i][1]), 'utf-8');
$offset = $matches[0][$i][1];
$offset = mb_strlen(substr($text, 0, $matches[0][$i][1]), 'utf-8');

$shortcode = new Shortcode($name, $parameters, $content, null);
$shortcodes[] = new ParsedShortcode($shortcode, $match, $offset);
Expand Down
10 changes: 9 additions & 1 deletion tests/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@ public function provideShortcodes()
array($s, str_repeat('[a]', 20), array_map(function($offset) { // 20
return new ParsedShortcode(new Shortcode('a', array(), null), '[a]', $offset);
}, range(0, 57, 3))),
array($s, '[b][a]x[a][/a][/a][/b] [b][a][a][/a]y[/a][/b]', array(
new ParsedShortcode(new Shortcode('b', array(), '[a]x[a][/a][/a]'), '[b][a]x[a][/a][/a][/b]', 0),
new ParsedShortcode(new Shortcode('b', array(), '[a][a][/a]y[/a]'), '[b][a][a][/a]y[/a][/b]', 23),
)),
array($s, '[b] [a][a][a] [/b] [b] [a][a][a] [/b]', array(
new ParsedShortcode(new Shortcode('b', array(), ' [a][a][a] '), '[b] [a][a][a] [/b]', 0),
new ParsedShortcode(new Shortcode('b', array(), ' [a][a][a] '), '[b] [a][a][a] [/b]', 19),
)),
);

/**
Expand All @@ -178,7 +186,7 @@ public function provideShortcodes()
*
* Tests cases from array above with identifiers in the array below must be skipped.
*/
$wordpressSkip = array(3, 6, 16, 21, 22, 23, 25, 32, 33, 34, 35);
$wordpressSkip = array(3, 6, 16, 21, 22, 23, 25, 32, 33, 34);
$result = array();
foreach($tests as $key => $test) {
$syntax = array_shift($test);
Expand Down

0 comments on commit 4cda52f

Please sign in to comment.