MediaWiki REL1_34
Hooks.php
Go to the documentation of this file.
1<?php
24use UtfNormal\Validator;
25use Wikimedia\PSquare;
26
31
35 public static function onRegistration() {
36 define( 'CONTENT_MODEL_SCRIBUNTO', 'Scribunto' );
37 }
38
45 public static function getSoftwareInfo( array &$software ) {
47 $engine->setTitle( Title::makeTitle( NS_SPECIAL, 'Version' ) );
48 $engine->getSoftwareInfo( $software );
49 return true;
50 }
51
58 public static function setupParserHook( Parser &$parser ) {
59 $parser->setFunctionHook( 'invoke', 'ScribuntoHooks::invokeHook', Parser::SFH_OBJECT_ARGS );
60 return true;
61 }
62
69 public static function clearState( Parser &$parser ) {
71 return true;
72 }
73
80 public static function parserCloned( Parser $parser ) {
81 $parser->scribunto_engine = null;
82 return true;
83 }
84
95 public static function invokeHook( Parser &$parser, PPFrame $frame, array $args ) {
96 global $wgScribuntoGatherFunctionStats;
97
98 try {
99 if ( count( $args ) < 2 ) {
100 throw new ScribuntoException( 'scribunto-common-nofunction' );
101 }
102 $moduleName = trim( $frame->expand( $args[0] ) );
103 $engine = Scribunto::getParserEngine( $parser );
104
105 $title = Title::makeTitleSafe( NS_MODULE, $moduleName );
106 if ( !$title || !$title->hasContentModel( CONTENT_MODEL_SCRIBUNTO ) ) {
107 throw new ScribuntoException( 'scribunto-common-nosuchmodule',
108 [ 'args' => [ $moduleName ] ] );
109 }
110 $module = $engine->fetchModuleFromParser( $title );
111 if ( !$module ) {
112 throw new ScribuntoException( 'scribunto-common-nosuchmodule',
113 [ 'args' => [ $moduleName ] ] );
114 }
115 $functionName = trim( $frame->expand( $args[1] ) );
116
117 $bits = $args[1]->splitArg();
118 unset( $args[0] );
119 unset( $args[1] );
120
121 // If $bits['index'] is empty, then the function name was parsed as a
122 // key=value pair (because of an equals sign in it), and since it didn't
123 // have an index, we don't need the index offset.
124 $childFrame = $frame->newChild( $args, $title, $bits['index'] === '' ? 0 : 1 );
125
126 if ( $wgScribuntoGatherFunctionStats ) {
127 $u0 = $engine->getResourceUsage( $engine::CPU_SECONDS );
128 $result = $module->invoke( $functionName, $childFrame );
129 $u1 = $engine->getResourceUsage( $engine::CPU_SECONDS );
130
131 if ( $u1 > $u0 ) {
132 $timingMs = (int)( 1000 * ( $u1 - $u0 ) );
133 // Since the overhead of stats is worst when when #invoke
134 // calls are very short, don't process measurements <= 20ms.
135 if ( $timingMs > 20 ) {
136 self::reportTiming( $moduleName, $functionName, $timingMs );
137 }
138 }
139 } else {
140 $result = $module->invoke( $functionName, $childFrame );
141 }
142
143 return Validator::cleanUp( strval( $result ) );
144 } catch ( ScribuntoException $e ) {
145 $trace = $e->getScriptTraceHtml( [ 'msgOptions' => [ 'content' ] ] );
146 $html = Html::element( 'p', [], $e->getMessage() );
147 if ( $trace !== false ) {
148 $html .= Html::element( 'p',
149 [],
150 wfMessage( 'scribunto-common-backtrace' )->inContentLanguage()->text()
151 ) . $trace;
152 } else {
153 $html .= Html::element( 'p',
154 [],
155 wfMessage( 'scribunto-common-no-details' )->inContentLanguage()->text()
156 );
157 }
158 $out = $parser->getOutput();
159 $errors = $out->getExtensionData( 'ScribuntoErrors' );
160 if ( $errors === null ) {
161 // On first hook use, set up error array and output
162 $errors = [];
163 $parser->addTrackingCategory( 'scribunto-common-error-category' );
164 $out->addModules( 'ext.scribunto.errors' );
165 }
166 $errors[] = $html;
167 $out->setExtensionData( 'ScribuntoErrors', $errors );
168 $out->addJsConfigVars( 'ScribuntoErrors', $errors );
169 $id = 'mw-scribunto-error-' . ( count( $errors ) - 1 );
170 $parserError = htmlspecialchars( $e->getMessage() );
171
172 // #iferror-compatible error element
173 return "<strong class=\"error\"><span class=\"scribunto-error\" id=\"$id\">" .
174 $parserError . "</span></strong>";
175 }
176 }
177
185 public static function reportTiming( $moduleName, $functionName, $timing ) {
186 global $wgScribuntoGatherFunctionStats, $wgScribuntoSlowFunctionThreshold;
187
188 if ( !$wgScribuntoGatherFunctionStats ) {
189 return;
190 }
191
192 $threshold = $wgScribuntoSlowFunctionThreshold;
193 if ( !( is_float( $threshold ) && $threshold > 0 && $threshold < 1 ) ) {
194 return;
195 }
196
197 static $cache;
198
199 if ( !$cache ) {
200 $cache = ObjectCache::getLocalServerInstance( CACHE_NONE );
201
202 }
203
204 // To control the sampling rate, we keep a compact histogram of
205 // observations in APC, and extract the Nth percentile (specified
206 // via $wgScribuntoSlowFunctionThreshold; defaults to 0.90).
207 // We need APC and \Wikimedia\PSquare to do that.
208 if ( !class_exists( PSquare::class ) || $cache instanceof EmptyBagOStuff ) {
209 return;
210 }
211
212 $cacheVersion = '1';
213 $key = $cache->makeGlobalKey( __METHOD__, $cacheVersion, $threshold );
214
215 // This is a classic "read-update-write" critical section with no
216 // mutual exclusion, but the only consequence is that some samples
217 // will be dropped. We only need enough samples to estimate the
218 // the shape of the data, so that's fine.
219 $ps = $cache->get( $key ) ?: new PSquare( $threshold );
220 $ps->addObservation( $timing );
221 $cache->set( $key, $ps, 60 );
222
223 if ( $ps->getCount() < 1000 || $timing < $ps->getValue() ) {
224 return;
225 }
226
227 static $stats;
228
229 if ( !$stats ) {
230 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
231 }
232
233 $metricKey = sprintf( 'scribunto.traces.%s__%s__%s', wfWikiId(), $moduleName, $functionName );
234 $stats->timing( $metricKey, $timing );
235 }
236
242 public static function getCodeLanguage( Title $title, &$languageCode ) {
243 global $wgScribuntoUseCodeEditor;
244 if ( $wgScribuntoUseCodeEditor && $title->hasContentModel( CONTENT_MODEL_SCRIBUNTO )
245 ) {
246 $engine = Scribunto::newDefaultEngine();
247 if ( $engine->getCodeEditorLanguage() ) {
248 $languageCode = $engine->getCodeEditorLanguage();
249 return false;
250 }
251 }
252
253 return true;
254 }
255
263 public static function contentHandlerDefaultModelFor( Title $title, &$model ) {
264 if ( $model === 'sanitized-css' ) {
265 // Let TemplateStyles override Scribunto
266 return true;
267 }
268 if ( $title->getNamespace() == NS_MODULE && !Scribunto::isDocPage( $title ) ) {
269 $model = CONTENT_MODEL_SCRIBUNTO;
270 return true;
271 }
272 return true;
273 }
274
282 public static function reportLimitData( Parser $parser, ParserOutput $output ) {
283 if ( Scribunto::isParserEnginePresent( $parser ) ) {
284 $engine = Scribunto::getParserEngine( $parser );
285 $engine->reportLimitData( $output );
286 }
287 return true;
288 }
289
300 public static function formatLimitData( $key, &$value, &$report, $isHTML, $localize ) {
301 $engine = Scribunto::newDefaultEngine();
302 return $engine->formatLimitData( $key, $value, $report, $isHTML, $localize );
303 }
304
313 public static function showStandardInputsOptions( EditPage $editor, OutputPage $output, &$tab ) {
314 if ( $editor->getTitle()->hasContentModel( CONTENT_MODEL_SCRIBUNTO ) ) {
315 $output->addModules( 'ext.scribunto.edit' );
316 $editor->editFormTextAfterTools .= '<div id="mw-scribunto-console"></div>';
317 }
318 return true;
319 }
320
328 public static function showReadOnlyFormInitial( EditPage $editor, OutputPage $output ) {
329 if ( $editor->getTitle()->hasContentModel( CONTENT_MODEL_SCRIBUNTO ) ) {
330 $output->addModules( 'ext.scribunto.edit' );
331 $editor->editFormTextAfterContent .= '<div id="mw-scribunto-console"></div>';
332 }
333 return true;
334 }
335
344 public static function beforeEditButtons( EditPage &$editor, array &$buttons, &$tabindex ) {
345 if ( $editor->getTitle()->hasContentModel( CONTENT_MODEL_SCRIBUNTO ) ) {
346 unset( $buttons['preview'] );
347 }
348 return true;
349 }
350
358 Status $status
359 ) {
360 $title = $context->getTitle();
361
362 if ( !$content instanceof ScribuntoContent ) {
363 return true;
364 }
365
366 $validateStatus = $content->validate( $title );
367 if ( $validateStatus->isOK() ) {
368 return true;
369 }
370
371 $status->merge( $validateStatus );
372
373 if ( isset( $validateStatus->scribunto_error->params['module'] ) ) {
374 $module = $validateStatus->scribunto_error->params['module'];
375 $line = $validateStatus->scribunto_error->params['line'];
376 if ( $module === $title->getPrefixedDBkey() && preg_match( '/^\d+$/', $line ) ) {
377 $out = $context->getOutput();
378 $out->addInlineScript( 'window.location.hash = ' . Xml::encodeJsVar( "#mw-ce-l$line" ) );
379 }
380 }
381
382 return true;
383 }
384
391 public static function showDocPageHeader( Article &$article, &$outputDone, &$pcache ) {
392 $title = $article->getTitle();
393 if ( Scribunto::isDocPage( $title, $forModule ) ) {
394 $article->getContext()->getOutput()->addHTML(
395 wfMessage( 'scribunto-doc-page-header', $forModule->getPrefixedText() )->parseAsBlock()
396 );
397 }
398 return true;
399 }
400}
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
const NS_MODULE
$line
Definition cdb.php:59
if( $line===false) $args
Definition cdb.php:64
Class for viewing MediaWiki article and history.
Definition Article.php:38
getContext()
Gets the context this Article is executed in.
Definition Article.php:2267
getTitle()
Get the title object of the article.
Definition Article.php:221
The edit page/HTML interface (split from Article) The actual database and text munging is still in Ar...
Definition EditPage.php:46
A BagOStuff object with no objects in it.
MediaWikiServices is the service locator for the application scope of MediaWiki.
This is one of the Core classes and should be read at least once by any new developers.
addModules( $modules)
Load one or more ResourceLoader modules on this page.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:74
addTrackingCategory( $msg)
Definition Parser.php:4446
setFunctionHook( $id, callable $callback, $flags=0)
Create a function, e.g.
Definition Parser.php:5283
getOutput()
Get the ParserOutput object.
Definition Parser.php:983
Represents the content of a Scribunto script page.
An exception class which represents an error in the script.
Definition Common.php:136
getScriptTraceHtml( $options=[])
Get the backtrace as HTML, or false if there is none available.
Definition Common.php:206
Hooks for the Scribunto extension.
Definition Hooks.php:30
static showStandardInputsOptions(EditPage $editor, OutputPage $output, &$tab)
EditPage::showStandardInputs:options hook.
Definition Hooks.php:313
static contentHandlerDefaultModelFor(Title $title, &$model)
Set the Scribunto content handler for modules.
Definition Hooks.php:263
static beforeEditButtons(EditPage &$editor, array &$buttons, &$tabindex)
EditPageBeforeEditButtons hook.
Definition Hooks.php:344
static showReadOnlyFormInitial(EditPage $editor, OutputPage $output)
EditPage::showReadOnlyForm:initial hook.
Definition Hooks.php:328
static formatLimitData( $key, &$value, &$report, $isHTML, $localize)
Formats the limit report data.
Definition Hooks.php:300
static validateScript(IContextSource $context, Content $content, Status $status)
Definition Hooks.php:357
static getCodeLanguage(Title $title, &$languageCode)
Definition Hooks.php:242
static onRegistration()
Define content handler constant upon extension registration.
Definition Hooks.php:35
static parserCloned(Parser $parser)
Called when the parser is cloned.
Definition Hooks.php:80
static reportLimitData(Parser $parser, ParserOutput $output)
Adds report of number of evaluations by the single wikitext page.
Definition Hooks.php:282
static showDocPageHeader(Article &$article, &$outputDone, &$pcache)
Definition Hooks.php:391
static setupParserHook(Parser &$parser)
Register parser hooks.
Definition Hooks.php:58
static invokeHook(Parser &$parser, PPFrame $frame, array $args)
Hook function for {{#invoke:module|func}}.
Definition Hooks.php:95
static clearState(Parser &$parser)
Called when the interpreter is to be reset.
Definition Hooks.php:69
static getSoftwareInfo(array &$software)
Get software information for Special:Version.
Definition Hooks.php:45
static reportTiming( $moduleName, $functionName, $timing)
Record stats on slow function calls.
Definition Hooks.php:185
static newDefaultEngine( $extraOptions=[])
Create a new engine object with default parameters.
Definition Common.php:32
static isParserEnginePresent(Parser $parser)
Check if an engine instance is present in the given parser.
Definition Common.php:70
static isDocPage(Title $title, Title &$forModule=null)
Test whether the page should be considered a documentation page.
Definition Common.php:92
static getParserEngine(Parser $parser)
Get an engine instance for the given parser, and cache it in the parser so that subsequent calls to t...
Definition Common.php:56
static resetParserEngine(Parser $parser)
Remove the current engine instance from the parser.
Definition Common.php:78
merge( $other, $overwriteValue=false)
Merge another status object into this one.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:40
Represents a title within MediaWiki.
Definition Title.php:42
const CACHE_NONE
Definition Defines.php:91
const NS_SPECIAL
Definition Defines.php:58
Base interface for content objects.
Definition Content.php:34
Interface for objects which can provide a MediaWiki context on request.
expand( $root, $flags=0)
Expand a document tree node.
newChild( $args=false, $title=false, $indexOffset=0)
Create a child frame.
$context
Definition load.php:45
$cache
Definition mcc.php:33
$content
Definition router.php:78