25use InvalidArgumentException;
27use Liuggio\StatsdClient\Factory\StatsdDataFactory;
44use UnexpectedValueException;
45use Wikimedia\Parsoid\Config\SiteConfig;
46use Wikimedia\Parsoid\Core\ClientError;
47use Wikimedia\Parsoid\Core\PageBundle;
48use Wikimedia\Parsoid\Core\ResourceLimitExceededException;
49use Wikimedia\Parsoid\Parsoid;
62 public const PARSOID_PARSER_CACHE_NAME =
'parsoid';
67 private const RENDER_ID_KEY =
'parsoid-render-id';
72 private const PARSOID_PAGE_BUNDLE_KEY =
'parsoid-page-bundle';
75 public const OPT_FORCE_PARSE = 1;
82 private $revisionOutputCache;
88 private $globalIdGenerator;
94 private $parsoidCacheConfig;
100 private $parsoidPageConfigFactory;
103 private $revisionLookup;
131 $this->revisionOutputCache = $parserCacheFactory
133 $this->parserCache = $parserCacheFactory->
getParserCache( self::PARSOID_PARSER_CACHE_NAME );
134 $this->revisionLookup = $revisionLookup;
135 $this->globalIdGenerator = $globalIdGenerator;
136 $this->stats = $stats;
137 $this->parsoid = $parsoid;
138 $this->siteConfig = $siteConfig;
139 $this->parsoidPageConfigFactory = $parsoidPageConfigFactory;
152 return $this->siteConfig->getContentModelHandler( $model ) !==
null;
169 $revId = $revision ? $revision->getId() : $page->getLatest();
171 $revision = $this->revisionLookup->getRevisionById( $revId );
175 'Revision {revId} not found',
176 [
'revId' => $revId ]
182 $statsKey = $isOld ?
'ParsoidOutputAccess.Cache.revision' :
'ParsoidOutputAccess.Cache.parser';
184 $mainSlot = $revision->
getSlot( SlotRecord::MAIN );
185 if ( !$this->supportsContentModel( $mainSlot->getModel() ) ) {
186 throw new UnexpectedValueException(
'Parsoid does not support content model ' . $mainSlot->getModel() );
189 if ( !( $options & self::OPT_FORCE_PARSE ) ) {
190 $parserOutput = $this->getCachedParserOutput(
198 if ( $parserOutput ) {
199 return Status::newGood( $parserOutput );
203 $startTime = microtime(
true );
204 $status = $this->parse( $page, $parserOpts, $revision );
205 $time = microtime(
true ) - $startTime;
207 if ( $status->isOK() ) {
208 if ( $time > $this->parsoidCacheConfig->get(
'CacheThresholdTime' ) ) {
209 $parserOutput = $status->getValue();
213 $this->revisionOutputCache->save( $parserOutput, $revision, $parserOpts, $now );
215 $this->parserCache->save( $parserOutput, $page, $parserOpts, $now );
217 $this->stats->increment( $statsKey .
'.save.ok' );
219 $this->stats->increment( $statsKey .
'.save.skipfast' );
222 $this->stats->increment( $statsKey .
'.save.notok' );
235 private function parseInternal(
237 ?RevisionRecord $revision =
null,
241 $langCode = $languageOverride ? $languageOverride->getCode() : null;
242 $pageConfig = $this->parsoidPageConfigFactory->create(
249 $startTime = microtime(
true );
250 $pageBundle = $this->parsoid->wikitext2html(
252 [
'pageBundle' =>
true ]
254 $parserOutput = $this->createParserOutputFromPageBundle( $pageBundle );
255 $time = microtime(
true ) - $startTime;
257 LoggerFactory::getInstance(
'slow-parsoid' )
258 ->info(
'Parsing {title} was slow, took {time} seconds', [
259 'time' => number_format( $time, 2 ),
260 'title' => (
string)$page,
264 }
catch ( ClientError $e ) {
266 }
catch ( ResourceLimitExceededException $e ) {
267 return Status::newFatal(
'parsoid-resource-limit-exceeded', $e->getMessage() );
283 private function createParserOutputFromPageBundle( PageBundle $pageBundle ):
ParserOutput {
286 self::PARSOID_PAGE_BUNDLE_KEY,
288 'parsoid' => $pageBundle->parsoid,
289 'mw' => $pageBundle->mw
293 return $parserOutput;
309 throw new InvalidArgumentException(
'ParserOutput does not have a render ID' );
312 return ParsoidRenderID::newFromKey( $renderId );
324 return new PageBundle(
326 $pbData[
'parsoid'] ?? [],
348 $parserOutput = $this->revisionOutputCache->get( $revision, $parserOpts );
350 $parserOutput = $this->parserCache->get( $page, $parserOpts );
353 if ( $parserOutput ) {
359 $parserOutput =
null;
363 if ( $parserOutput ) {
364 $this->stats->increment( $statsKey .
'.get.hit' );
365 return $parserOutput;
367 $this->stats->increment( $statsKey .
'.get.miss' );
379 $revId = $revision ? $revision->getId() : $page->getLatest();
381 $status = $this->parseInternal( $page, $revision, $parserOpts->
getTargetLanguage() );
383 if ( !$status->isOK() ) {
387 $parserOutput = $status->getValue();
392 $parsoidRenderId =
new ParsoidRenderID( $revId, $this->globalIdGenerator->newUUIDv1() );
393 $parserOutput->
setExtensionData( self::RENDER_ID_KEY, $parsoidRenderId->getKey() );
const CONTENT_MODEL_WIKITEXT
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
getExtensionData()
Get the extension data as: augmentor name => data.
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
setCacheTime( $t)
setCacheTime() sets the timestamp expressing when the page has been rendered.
A Config instance which stores all settings as a member variable.
Base class for language-specific code.
A class containing constants representing the names of configuration variables.
const ParsoidCacheConfig
Name constant for the ParsoidCacheConfig setting, for use with Config::get()
Helper class used by MediaWiki to create Parsoid PageConfig objects.
Cache for ParserOutput objects corresponding to the latest page revisions.
Set options of the Parser.
getTargetLanguage()
Target language for the parse.
getExtensionData( $key)
Gets extensions data previously attached to this ParserOutput using setExtensionData().
getRawText()
Get the cacheable text with <mw:editsection> markers still in it.
setExtensionData( $key, $value)
Attaches arbitrary data to this ParserObject.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
static newGood( $value=null)
Factory function for good results.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Interface for configuration instances.
MediaWiki adaptation of StatsdDataFactory that provides buffering functionality.
Interface for objects (potentially) representing an editable wiki page.