aboutsummaryrefslogtreecommitdiffstats
path: root/includes/language/MessageParser.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/language/MessageParser.php')
-rw-r--r--includes/language/MessageParser.php93
1 files changed, 58 insertions, 35 deletions
diff --git a/includes/language/MessageParser.php b/includes/language/MessageParser.php
index 82bd682fd96b..8f595fbe8f23 100644
--- a/includes/language/MessageParser.php
+++ b/includes/language/MessageParser.php
@@ -21,6 +21,9 @@ use Psr\Log\LoggerInterface;
* @since 1.44
*/
class MessageParser {
+ private const DEPTH_EXCEEDED_MESSAGE =
+ '<span class="error">Message parse depth limit exceeded</span>';
+
private ParserFactory $parserFactory;
private OutputTransformPipeline $outputPipeline;
private LanguageFactory $langFactory;
@@ -29,8 +32,9 @@ class MessageParser {
/** @var ParserOptions|null Lazy-initialised */
private ?ParserOptions $parserOptions = null;
- /** @var Parser[] Lazy-created via self::getParser() */
+ /** @var Parser[] Cached Parser objects */
private array $parsers = [];
+ /** @var int Index into $this->parsers for the active Parser */
private int $curParser = -1;
/**
@@ -109,37 +113,18 @@ class MessageParser {
}
$page ??= $this->getPlaceholderTitle();
+ $parser = $this->acquireParser();
+ if ( !$parser ) {
+ return self::DEPTH_EXCEEDED_MESSAGE;
+ }
try {
- $this->curParser++;
- $parser = $this->getParser();
- if ( !$parser ) {
- return '<span class="error">Message transform depth limit exceeded</span>';
- }
- $message = $parser->transformMsg( $message, $popts, $page );
+ return $parser->transformMsg( $message, $popts, $page );
} finally {
- $this->curParser--;
- }
- if ( $oldUserLang ) {
- $popts->setUserLang( $oldUserLang );
- }
-
- return $message;
- }
-
- /**
- * You should increment $this->curParser before calling this method and decrement it after
- * to support recursive calls to message parsing.
- */
- private function getParser(): ?Parser {
- if ( $this->curParser >= self::MAX_PARSER_DEPTH ) {
- $this->logger->debug( __METHOD__ . ": Refusing to create a new parser with index {$this->curParser}" );
- return null;
- }
- if ( !isset( $this->parsers[ $this->curParser ] ) ) {
- $this->logger->debug( __METHOD__ . ": Creating a new parser with index {$this->curParser}" );
- $this->parsers[ $this->curParser ] = $this->parserFactory->create();
+ $this->releaseParser( $parser );
+ if ( $oldUserLang ) {
+ $popts->setUserLang( $oldUserLang );
+ }
}
- return $this->parsers[ $this->curParser ];
}
/**
@@ -198,15 +183,14 @@ class MessageParser {
$page ??= $this->getPlaceholderTitle();
+ $parser = $this->acquireParser();
+ if ( !$parser ) {
+ return new ParserOutput( self::DEPTH_EXCEEDED_MESSAGE );
+ }
try {
- $this->curParser++;
- $parser = $this->getParser();
- if ( !$parser ) {
- return new ParserOutput( '<span class="error">Message parse depth limit exceeded</span>' );
- }
return $parser->parse( $text, $page, $popts, $lineStart );
} finally {
- $this->curParser--;
+ $this->releaseParser( $parser );
}
}
@@ -217,4 +201,43 @@ class MessageParser {
WikiAwareEntity::LOCAL
);
}
+
+ /**
+ * Attempt to get a free parser from the cache. If none exists, create one,
+ * up to a limit of MAX_PARSER_DEPTH. If the limit is exceeded, return null.
+ *
+ * If a parser is returned, it must be released with releaseParser().
+ *
+ * @return Parser|null
+ */
+ private function acquireParser(): ?Parser {
+ $index = $this->curParser + 1;
+ if ( $index >= self::MAX_PARSER_DEPTH ) {
+ $this->logger->debug( __METHOD__ . ": Refusing to create a new parser with index {$index}" );
+ return null;
+ }
+ $parser = $this->parsers[ $index ] ?? null;
+ if ( !$parser ) {
+ $this->logger->debug( __METHOD__ . ": Creating a new parser with index {$index}" );
+ $parser = $this->parserFactory->create();
+ }
+ $this->parsers[ $index ] = $parser;
+ $this->curParser = $index;
+ return $parser;
+ }
+
+ /**
+ * Release a parser previously acquired by acquireParser().
+ *
+ * @param Parser $parser
+ */
+ private function releaseParser( Parser $parser ) {
+ if ( $this->parsers[$this->curParser] !== $parser ) {
+ throw new \LogicException( 'releaseParser called with the wrong ' .
+ "parser instance: #{$this->curParser} = " .
+ gettype( $this->parsers[$this->curParser] ) );
+ }
+ $this->curParser--;
+ }
+
}