1<?php 2 3/* 4 * This file is part of the league/commonmark package. 5 * 6 * (c) Colin O'Dell <colinodell@gmail.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace League\CommonMark\Extension\Mention; 13 14use League\CommonMark\Extension\Mention\Generator\CallbackGenerator; 15use League\CommonMark\Extension\Mention\Generator\MentionGeneratorInterface; 16use League\CommonMark\Extension\Mention\Generator\StringTemplateLinkGenerator; 17use League\CommonMark\Inline\Parser\InlineParserInterface; 18use League\CommonMark\InlineParserContext; 19 20final class MentionParser implements InlineParserInterface 21{ 22 /** @var string */ 23 private $symbol; 24 25 /** @var string */ 26 private $mentionRegex; 27 28 /** @var MentionGeneratorInterface */ 29 private $mentionGenerator; 30 31 public function __construct(string $symbol, string $mentionRegex, MentionGeneratorInterface $mentionGenerator) 32 { 33 $this->symbol = $symbol; 34 $this->mentionRegex = $mentionRegex; 35 $this->mentionGenerator = $mentionGenerator; 36 } 37 38 public function getCharacters(): array 39 { 40 return [$this->symbol]; 41 } 42 43 public function parse(InlineParserContext $inlineContext): bool 44 { 45 $cursor = $inlineContext->getCursor(); 46 47 // The symbol must not have any other characters immediately prior 48 $previousChar = $cursor->peek(-1); 49 if ($previousChar !== null && \preg_match('/\w/', $previousChar)) { 50 // peek() doesn't modify the cursor, so no need to restore state first 51 return false; 52 } 53 54 // Save the cursor state in case we need to rewind and bail 55 $previousState = $cursor->saveState(); 56 57 // Advance past the symbol to keep parsing simpler 58 $cursor->advance(); 59 60 // Parse the mention match value 61 $identifier = $cursor->match($this->mentionRegex); 62 if ($identifier === null) { 63 // Regex failed to match; this isn't a valid mention 64 $cursor->restoreState($previousState); 65 66 return false; 67 } 68 69 $mention = $this->mentionGenerator->generateMention(new Mention($this->symbol, $identifier)); 70 71 if ($mention === null) { 72 $cursor->restoreState($previousState); 73 74 return false; 75 } 76 77 $inlineContext->getContainer()->appendChild($mention); 78 79 return true; 80 } 81 82 public static function createWithStringTemplate(string $symbol, string $mentionRegex, string $urlTemplate): MentionParser 83 { 84 return new self($symbol, $mentionRegex, new StringTemplateLinkGenerator($urlTemplate)); 85 } 86 87 public static function createWithCallback(string $symbol, string $mentionRegex, callable $callback): MentionParser 88 { 89 return new self($symbol, $mentionRegex, new CallbackGenerator($callback)); 90 } 91} 92