MediaWiki master
ParsoidOutputAccess.php
Go to the documentation of this file.
1<?php
21
36use Wikimedia\Parsoid\Core\ClientError;
37use Wikimedia\Parsoid\Core\ResourceLimitExceededException;
38
54 private ParsoidParserFactory $parsoidParserFactory;
55 private PageLookup $pageLookup;
56 private RevisionLookup $revisionLookup;
57 private ParserOutputAccess $parserOutputAccess;
58 private SiteConfig $siteConfig;
59 private IContentHandlerFactory $contentHandlerFactory;
60
69 public function __construct(
70 ParsoidParserFactory $parsoidParserFactory,
71 ParserOutputAccess $parserOutputAccess,
72 PageLookup $pageLookup,
73 RevisionLookup $revisionLookup,
74 SiteConfig $siteConfig,
75 IContentHandlerFactory $contentHandlerFactory
76 ) {
77 $this->parsoidParserFactory = $parsoidParserFactory;
78 $this->parserOutputAccess = $parserOutputAccess;
79 $this->pageLookup = $pageLookup;
80 $this->revisionLookup = $revisionLookup;
81 $this->siteConfig = $siteConfig;
82 $this->contentHandlerFactory = $contentHandlerFactory;
83 }
84
95 public function getParserOutput(
96 PageIdentity $page,
97 ParserOptions $parserOpts,
98 $revision = null,
99 int $options = 0,
100 bool $lenientRevHandling = false
101 ): Status {
102 wfDeprecated( __METHOD__, '1.43' );
103 [ $page, $revision, $uncacheable ] = $this->resolveRevision( $page, $revision, $lenientRevHandling );
104
105 try {
106 if ( $uncacheable ) {
107 $options |= ParserOutputAccess::OPT_NO_UPDATE_CACHE;
108 }
109
110 $this->adjustParserOptions( $revision, $parserOpts );
111 $status = $this->parserOutputAccess->getParserOutput(
112 $page, $parserOpts, $revision, $options
113 );
114 } catch ( ClientError $e ) {
115 $status = Status::newFatal( 'parsoid-client-error', $e->getMessage() );
116 } catch ( ResourceLimitExceededException $e ) {
117 $status = Status::newFatal( 'parsoid-resource-limit-exceeded', $e->getMessage() );
118 }
119 return $status;
120 }
121
131 public function getCachedParserOutput(
132 PageIdentity $page,
133 ParserOptions $parserOpts,
134 $revision = null,
135 bool $lenientRevHandling = false
136 ): ?ParserOutput {
137 wfDeprecated( __METHOD__, '1.43' );
138 [ $page, $revision, $ignored ] = $this->resolveRevision( $page, $revision, $lenientRevHandling );
139
140 $this->adjustParserOptions( $revision, $parserOpts );
141 return $this->parserOutputAccess->getCachedParserOutput( $page, $parserOpts, $revision );
142 }
143
156 public function parseUncacheable(
157 PageIdentity $page,
158 ParserOptions $parserOpts,
159 $revision,
160 bool $lenientRevHandling = false
161 ): Status {
162 wfDeprecated( __METHOD__, '1.43' );
163 // NOTE: If we have a RevisionRecord already, just use it, there is no need to resolve $page to
164 // a PageRecord (and it may not be possible if the page doesn't exist).
165 if ( !$revision instanceof RevisionRecord ) {
166 [ $page, $revision, $ignored ] = $this->resolveRevision( $page, $revision, $lenientRevHandling );
167 }
168
169 // Enforce caller expectation
170 $revId = $revision->getId();
171 if ( $revId !== 0 && $revId !== null ) {
172 return Status::newFatal( 'parsoid-revision-access',
173 "parseUncacheable should not be called for a real revision" );
174 }
175
176 try {
177 // Since we aren't caching this output, there is no need to
178 // call setUseParsoid() here.
179 $parser = $this->parsoidParserFactory->create();
180 $parserOutput = $this->parsoidParserFactory->create()->parseFakeRevision(
181 $revision, $page, $parserOpts );
182 $parserOutput->updateCacheExpiry( 0 ); // Ensure this isn't accidentally cached
183 // set up (fake) render id and other properties
184 $globalIdGenerator = MediaWikiServices::getInstance()->getGlobalIdGenerator();
185 $parserOutput->setRenderId( $globalIdGenerator->newUUIDv1() );
186 $parserOutput->setCacheRevisionId( $revision->getId() );
187 $parserOutput->setRevisionTimestamp( $revision->getTimestamp() );
188 $parserOutput->setCacheTime( wfTimestampNow() );
189
190 $status = Status::newGood( $parserOutput );
191 } catch ( RevisionAccessException $e ) {
192 return Status::newFatal( 'parsoid-revision-access', $e->getMessage() );
193 } catch ( ClientError $e ) {
194 $status = Status::newFatal( 'parsoid-client-error', $e->getMessage() );
195 } catch ( ResourceLimitExceededException $e ) {
196 $status = Status::newFatal( 'parsoid-resource-limit-exceeded', $e->getMessage() );
197 }
198 return $status;
199 }
200
208 private function resolveRevision( PageIdentity $page, $revision, bool $lenientRevHandling = false ): array {
209 $uncacheable = false;
210 if ( !$page instanceof PageRecord ) {
211 $name = "$page";
212 $page = $this->pageLookup->getPageByReference( $page );
213 if ( !$page ) {
214 throw new RevisionAccessException(
215 'Page {name} not found',
216 [ 'name' => $name ]
217 );
218 }
219 }
220
221 $revision ??= $page->getLatest();
222
223 if ( is_int( $revision ) ) {
224 $revId = $revision;
225 $revision = $this->revisionLookup->getRevisionById( $revId );
226
227 if ( !$revision ) {
228 throw new RevisionAccessException(
229 'Revision {revId} not found',
230 [ 'revId' => $revId ]
231 );
232 }
233 }
234
235 if ( $page->getId() !== $revision->getPageId() ) {
236 if ( $lenientRevHandling ) {
237 $page = $this->pageLookup->getPageById( $revision->getPageId() );
238 if ( !$page ) {
239 // This should ideally never trigger!
240 throw new \RuntimeException(
241 "Unexpected NULL page for pageid " . $revision->getPageId() .
242 " from revision " . $revision->getId()
243 );
244 }
245 // Don't cache this!
246 $uncacheable = true;
247 } else {
248 throw new RevisionAccessException(
249 'Revision {revId} does not belong to page {name}',
250 [ 'name' => $page->getDBkey(), 'revId' => $revision->getId() ]
251 );
252 }
253 }
254
255 return [ $page, $revision, $uncacheable ];
256 }
257
258 private function adjustParserOptions( RevisionRecord $revision, ParserOptions $parserOpts ): void {
259 $mainSlot = $revision->getSlot( SlotRecord::MAIN );
260 $contentModel = $mainSlot->getModel();
261 if ( $this->siteConfig->supportsContentModel( $contentModel ) ) {
262 // Since we know Parsoid supports this content model, explicitly
263 // call ParserOptions::setUseParsoid. This ensures that when
264 // we query the parser-cache, the right cache key is called.
265 // This is an optional transition step to using ParserOutputAccess.
266 $parserOpts->setUseParsoid();
267 }
268 }
269}
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
Service locator for MediaWiki core services.
Service for getting rendered output of a given page.
ParserOutput is a rendering of a Content object or a message.
Site-level configuration for Parsoid.
MediaWiki service for getting rendered page content.
getCachedParserOutput(PageIdentity $page, ParserOptions $parserOpts, $revision=null, bool $lenientRevHandling=false)
parseUncacheable(PageIdentity $page, ParserOptions $parserOpts, $revision, bool $lenientRevHandling=false)
This is to be called only for parsing posted wikitext that is actually not part of any real revision.
getParserOutput(PageIdentity $page, ParserOptions $parserOpts, $revision=null, int $options=0, bool $lenientRevHandling=false)
__construct(ParsoidParserFactory $parsoidParserFactory, ParserOutputAccess $parserOutputAccess, PageLookup $pageLookup, RevisionLookup $revisionLookup, SiteConfig $siteConfig, IContentHandlerFactory $contentHandlerFactory)
Exception representing a failure to look up a revision.
Page revision base class.
Value object representing a content slot associated with a page revision.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
Set options of the Parser.
setUseParsoid()
Request Parsoid-format HTML output.
Interface for objects (potentially) representing an editable wiki page.
Service for looking up information about wiki pages.
Data record representing a page that is (or used to be, or could be) an editable page on a wiki.
Service for looking up page revisions.
Copyright (C) 2011-2022 Wikimedia Foundation and others.