MediaWiki REL1_33
MediaWikiGadgetsDefinitionRepo.php
Go to the documentation of this file.
1<?php
2
6
11 const CACHE_VERSION = 2;
12
14
21 public function getGadget( $id ) {
22 $gadgets = $this->loadGadgets();
23 if ( !isset( $gadgets[$id] ) ) {
24 throw new InvalidArgumentException( "No gadget registered for '$id'" );
25 }
26
27 return $gadgets[$id];
28 }
29
30 public function getGadgetIds() {
31 $gadgets = $this->loadGadgets();
32 if ( $gadgets ) {
33 return array_keys( $gadgets );
34 } else {
35 return [];
36 }
37 }
38
39 public function handlePageUpdate( LinkTarget $target ) {
40 if ( $target->getNamespace() == NS_MEDIAWIKI && $target->getText() == 'Gadgets-definition' ) {
41 $this->purgeDefinitionCache();
42 }
43 }
44
49 private function purgeDefinitionCache() {
50 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
51 $cache->touchCheckKey( $this->getDefinitionCacheKey() );
52 }
53
54 private function getDefinitionCacheKey() {
55 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
56
57 return $cache->makeKey(
58 'gadgets-definition',
60 self::CACHE_VERSION
61 );
62 }
63
70 protected function loadGadgets() {
71 if ( $this->definitionCache !== null ) {
72 return $this->definitionCache; // process cache hit
73 }
74
75 // Ideally $t1Cache is APC, and $wanCache is memcached
76 $t1Cache = ObjectCache::getLocalServerInstance( 'hash' );
77 $wanCache = MediaWikiServices::getInstance()->getMainWANObjectCache();
78
79 $key = $this->getDefinitionCacheKey();
80
81 // (a) Check the tier 1 cache
82 $value = $t1Cache->get( $key );
83 // Randomize logical APC expiry to avoid stampedes
84 // somewhere between 7.0 and 15.0 (seconds)
85 $cutoffAge = mt_rand( 7 * 1e6, 15 * 1e6 ) / 1e6;
86 // Check if it passes a blind TTL check (avoids I/O)
87 if ( $value && ( microtime( true ) - $value['time'] ) < $cutoffAge ) {
88 $this->definitionCache = $value['gadgets']; // process cache
90 }
91 // Cache generated after the "check" time should be up-to-date
92 $ckTime = $wanCache->getCheckKeyTime( $key ) + WANObjectCache::HOLDOFF_TTL;
93 if ( $value && $value['time'] > $ckTime ) {
94 $this->definitionCache = $value['gadgets']; // process cache
96 }
97
98 // (b) Fetch value from WAN cache or regenerate if needed.
99 // This is hit occasionally and more so when the list changes.
100 $us = $this;
101 $value = $wanCache->getWithSetCallback(
102 $key,
104 function ( $old, &$ttl, &$setOpts ) use ( $us ) {
105 $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
106
107 $now = microtime( true );
108 $gadgets = $us->fetchStructuredList();
109 if ( $gadgets === false ) {
110 $ttl = WANObjectCache::TTL_UNCACHEABLE;
111 }
112
113 return [ 'gadgets' => $gadgets, 'time' => $now ];
114 },
115 [ 'checkKeys' => [ $key ], 'lockTSE' => 300 ]
116 );
117
118 // Update the tier 1 cache as needed
119 if ( $value['gadgets'] !== false && $value['time'] > $ckTime ) {
120 // Set a modest TTL to keep the WAN key in cache
121 $t1Cache->set( $key, $value, mt_rand( 300, 600 ) );
122 }
123
124 $this->definitionCache = $value['gadgets'];
125
127 }
128
135 public function fetchStructuredList( $forceNewText = null ) {
136 if ( $forceNewText === null ) {
137 // T157210: avoid using wfMessage() to avoid staleness due to cache layering
138 $title = Title::makeTitle( NS_MEDIAWIKI, 'Gadgets-definition' );
139 $rev = Revision::newFromTitle( $title );
140 if ( !$rev || !$rev->getContent() || $rev->getContent()->isEmpty() ) {
141 return false; // don't cache
142 }
143
144 $g = $rev->getContent()->getNativeData();
145 } else {
146 $g = $forceNewText;
147 }
148
149 $gadgets = $this->listFromDefinition( $g );
150 if ( !count( $gadgets ) ) {
151 return false; // don't cache; Bug 37228
152 }
153
154 $source = $forceNewText !== null ? 'input text' : 'MediaWiki:Gadgets-definition';
155 wfDebug( __METHOD__ . ": $source parsed, cache entry should be updated\n" );
156
157 return $gadgets;
158 }
159
166 private function listFromDefinition( $definition ) {
167 $definition = preg_replace( '/<!--.*?-->/s', '', $definition );
168 $lines = preg_split( '/(\r\n|\r|\n)+/', $definition );
169
170 $gadgets = [];
171 $section = '';
172
173 foreach ( $lines as $line ) {
174 $m = [];
175 if ( preg_match( '/^==+ *([^*:\s|]+?)\s*==+\s*$/', $line, $m ) ) {
176 $section = $m[1];
177 } else {
178 $gadget = $this->newFromDefinition( $line, $section );
179 if ( $gadget ) {
180 $gadgets[$gadget->getName()] = $gadget;
181 }
182 }
183 }
184
185 return $gadgets;
186 }
187
194 public function newFromDefinition( $definition, $category ) {
195 $m = [];
196 if ( !preg_match(
197 '/^\*+ *([a-zA-Z](?:[-_:.\w\d ]*[a-zA-Z0-9])?)(\s*\[.*?\])?\s*((\|[^|]*)+)\s*$/',
198 $definition,
199 $m
200 ) ) {
201 return false;
202 }
203 // NOTE: the gadget name is used as part of the name of a form field,
204 // and must follow the rules defined in https://www.w3.org/TR/html4/types.html#type-cdata
205 // Also, title-normalization applies.
206 $info = [ 'category' => $category ];
207 $info['name'] = trim( str_replace( ' ', '_', $m[1] ) );
208 // If the name is too long, then RL will throw an MWException when
209 // we try to register the module
210 if ( !Gadget::isValidGadgetID( $info['name'] ) ) {
211 return false;
212 }
213 $info['definition'] = $definition;
214 $options = trim( $m[2], ' []' );
215
216 foreach ( preg_split( '/\s*\|\s*/', $options, -1, PREG_SPLIT_NO_EMPTY ) as $option ) {
217 $arr = preg_split( '/\s*=\s*/', $option, 2 );
218 $option = $arr[0];
219 if ( isset( $arr[1] ) ) {
220 $params = explode( ',', $arr[1] );
221 $params = array_map( 'trim', $params );
222 } else {
223 $params = [];
224 }
225
226 switch ( $option ) {
227 case 'ResourceLoader':
228 $info['resourceLoaded'] = true;
229 break;
230 case 'dependencies':
231 $info['dependencies'] = $params;
232 break;
233 case 'peers':
234 $info['peers'] = $params;
235 break;
236 case 'rights':
237 $info['requiredRights'] = $params;
238 break;
239 case 'hidden':
240 $info['hidden'] = true;
241 break;
242 case 'skins':
243 $info['requiredSkins'] = $params;
244 break;
245 case 'default':
246 $info['onByDefault'] = true;
247 break;
248 case 'targets':
249 $info['targets'] = $params;
250 break;
251 case 'type':
252 // Single value, not a list
253 $info['type'] = $params[0] ?? '';
254 break;
255 }
256 }
257
258 foreach ( preg_split( '/\s*\|\s*/', $m[3], -1, PREG_SPLIT_NO_EMPTY ) as $page ) {
259 $page = "MediaWiki:Gadget-$page";
260
261 if ( preg_match( '/\.js/', $page ) ) {
262 $info['scripts'][] = $page;
263 } elseif ( preg_match( '/\.css/', $page ) ) {
264 $info['styles'][] = $page;
265 }
266 }
267
268 return new Gadget( $info );
269 }
270}
and that you know you can do these things To protect your we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights These restrictions translate to certain responsibilities for you if you distribute copies of the or if you modify it For if you distribute copies of such a whether gratis or for a you must give the recipients all the rights that you have You must make sure that receive or can get the source code And you must show them these terms so they know their rights We protect your rights with two and(2) offer you this license which gives you legal permission to copy
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
$line
Definition cdb.php:59
Wrapper for one gadget.
Definition Gadget.php:17
const GADGET_CLASS_VERSION
Increment this when changing class structure.
Definition Gadget.php:21
const CACHE_TTL
Definition Gadget.php:23
static isValidGadgetID( $id)
Whether the provided gadget id is valid.
Definition Gadget.php:114
Gadgets repo powered by MediaWiki:Gadgets-definition.
newFromDefinition( $definition, $category)
Creates an instance of this class from definition in MediaWiki:Gadgets-definition.
handlePageUpdate(LinkTarget $target)
Given that the provided page was updated, invalidate caches if necessary.
loadGadgets()
Loads list of gadgets and returns it as associative array of sections with gadgets e....
listFromDefinition( $definition)
Generates a structured list of Gadget objects from a definition.
getGadgetIds()
Get the ids of the gadgets provided by this repository.
purgeDefinitionCache()
Purge the definitions cache, for example if MediaWiki:Gadgets-definition was edited.
fetchStructuredList( $forceNewText=null)
Fetch list of gadgets and returns it as associative array of sections with gadgets e....
MediaWikiServices is the service locator for the application scope of MediaWiki.
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given link target.
Definition Revision.php:137
Relational database abstraction object.
Definition Database.php:49
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition hooks.txt:1999
usually copyright or history_copyright This message must be in HTML not wikitext if the section is included from a template $section
Definition hooks.txt:3070
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition hooks.txt:1779
getNamespace()
Get the namespace index.
getText()
Returns the link in text form, without namespace prefix or fragment.
$cache
Definition mcc.php:33
$source
const DB_REPLICA
Definition defines.php:25
$lines
Definition router.php:61
$params