MediaWiki REL1_30
MediaWikiGadgetsDefinitionRepo.php
Go to the documentation of this file.
1<?php
4
9 const CACHE_VERSION = 2;
10
12
13 public function getGadget( $id ) {
14 $gadgets = $this->loadGadgets();
15 if ( !isset( $gadgets[$id] ) ) {
16 throw new InvalidArgumentException( "No gadget registered for '$id'" );
17 }
18
19 return $gadgets[$id];
20 }
21
22 public function getGadgetIds() {
23 $gadgets = $this->loadGadgets();
24 if ( $gadgets ) {
25 return array_keys( $gadgets );
26 } else {
27 return [];
28 }
29 }
30
35 public function purgeDefinitionCache() {
36 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
37 $cache->touchCheckKey( $this->getCheckKey() );
38 }
39
40 private function getCheckKey() {
41 return wfMemcKey( 'gadgets-definition', Gadget::GADGET_CLASS_VERSION, self::CACHE_VERSION );
42 }
43
50 protected function loadGadgets() {
51 if ( $this->definitionCache !== null ) {
52 return $this->definitionCache; // process cache hit
53 }
54
55 // Ideally $t1Cache is APC, and $wanCache is memcached
56 $t1Cache = ObjectCache::getLocalServerInstance( 'hash' );
57 $wanCache = MediaWikiServices::getInstance()->getMainWANObjectCache();
58
59 $key = $this->getCheckKey();
60
61 // (a) Check the tier 1 cache
62 $value = $t1Cache->get( $key );
63 // Check if it passes a blind TTL check (avoids I/O)
64 if ( $value && ( microtime( true ) - $value['time'] ) < 10 ) {
65 $this->definitionCache = $value['gadgets']; // process cache
67 }
68 // Cache generated after the "check" time should be up-to-date
69 $ckTime = $wanCache->getCheckKeyTime( $key ) + WANObjectCache::HOLDOFF_TTL;
70 if ( $value && $value['time'] > $ckTime ) {
71 $this->definitionCache = $value['gadgets']; // process cache
73 }
74
75 // (b) Fetch value from WAN cache or regenerate if needed.
76 // This is hit occasionally and more so when the list changes.
77 $us = $this;
78 $value = $wanCache->getWithSetCallback(
79 $key,
81 function ( $old, &$ttl, &$setOpts ) use ( $us ) {
82 $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
83
84 $now = microtime( true );
85 $gadgets = $us->fetchStructuredList();
86 if ( $gadgets === false ) {
87 $ttl = WANObjectCache::TTL_UNCACHEABLE;
88 }
89
90 return [ 'gadgets' => $gadgets, 'time' => $now ];
91 },
92 [ 'checkKeys' => [ $key ], 'lockTSE' => 300 ]
93 );
94
95 // Update the tier 1 cache as needed
96 if ( $value['gadgets'] !== false && $value['time'] > $ckTime ) {
97 // Set a modest TTL to keep the WAN key in cache
98 $t1Cache->set( $key, $value, mt_rand( 300, 600 ) );
99 }
100
101 $this->definitionCache = $value['gadgets'];
102
104 }
105
112 public function fetchStructuredList( $forceNewText = null ) {
113 if ( $forceNewText === null ) {
114 // T157210: avoid using wfMessage() to avoid staleness due to cache layering
115 $title = Title::makeTitle( NS_MEDIAWIKI, 'Gadgets-definition' );
116 $rev = Revision::newFromTitle( $title );
117 if ( !$rev || !$rev->getContent() || $rev->getContent()->isEmpty() ) {
118 return false; // don't cache
119 }
120
121 $g = $rev->getContent()->getNativeData();
122 } else {
123 $g = $forceNewText;
124 }
125
126 $gadgets = $this->listFromDefinition( $g );
127 if ( !count( $gadgets ) ) {
128 return false; // don't cache; Bug 37228
129 }
130
131 $source = $forceNewText !== null ? 'input text' : 'MediaWiki:Gadgets-definition';
132 wfDebug( __METHOD__ . ": $source parsed, cache entry should be updated\n" );
133
134 return $gadgets;
135 }
136
143 private function listFromDefinition( $definition ) {
144 $definition = preg_replace( '/<!--.*?-->/s', '', $definition );
145 $lines = preg_split( '/(\r\n|\r|\n)+/', $definition );
146
147 $gadgets = [];
148 $section = '';
149
150 foreach ( $lines as $line ) {
151 $m = [];
152 if ( preg_match( '/^==+ *([^*:\s|]+?)\s*==+\s*$/', $line, $m ) ) {
153 $section = $m[1];
154 } else {
155 $gadget = $this->newFromDefinition( $line, $section );
156 if ( $gadget ) {
157 $gadgets[$gadget->getName()] = $gadget;
158 }
159 }
160 }
161
162 return $gadgets;
163 }
164
171 public function newFromDefinition( $definition, $category ) {
172 $m = [];
173 if ( !preg_match(
174 '/^\*+ *([a-zA-Z](?:[-_:.\w\d ]*[a-zA-Z0-9])?)(\s*\[.*?\])?\s*((\|[^|]*)+)\s*$/',
175 $definition,
176 $m
177 ) ) {
178 return false;
179 }
180 // NOTE: the gadget name is used as part of the name of a form field,
181 // and must follow the rules defined in https://www.w3.org/TR/html4/types.html#type-cdata
182 // Also, title-normalization applies.
183 $info = [ 'category' => $category ];
184 $info['name'] = trim( str_replace( ' ', '_', $m[1] ) );
185 // If the name is too long, then RL will throw an MWException when
186 // we try to register the module
187 if ( !Gadget::isValidGadgetID( $info['name'] ) ) {
188 return false;
189 }
190 $info['definition'] = $definition;
191 $options = trim( $m[2], ' []' );
192
193 foreach ( preg_split( '/\s*\|\s*/', $options, -1, PREG_SPLIT_NO_EMPTY ) as $option ) {
194 $arr = preg_split( '/\s*=\s*/', $option, 2 );
195 $option = $arr[0];
196 if ( isset( $arr[1] ) ) {
197 $params = explode( ',', $arr[1] );
198 $params = array_map( 'trim', $params );
199 } else {
200 $params = [];
201 }
202
203 switch ( $option ) {
204 case 'ResourceLoader':
205 $info['resourceLoaded'] = true;
206 break;
207 case 'dependencies':
208 $info['dependencies'] = $params;
209 break;
210 case 'peers':
211 $info['peers'] = $params;
212 break;
213 case 'rights':
214 $info['requiredRights'] = $params;
215 break;
216 case 'hidden':
217 $info['hidden'] = true;
218 break;
219 case 'skins':
220 $info['requiredSkins'] = $params;
221 break;
222 case 'default':
223 $info['onByDefault'] = true;
224 break;
225 case 'targets':
226 $info['targets'] = $params;
227 break;
228 case 'type':
229 // Single value, not a list
230 $info['type'] = isset( $params[0] ) ? $params[0] : '';
231 break;
232 }
233 }
234
235 foreach ( preg_split( '/\s*\|\s*/', $m[3], -1, PREG_SPLIT_NO_EMPTY ) as $page ) {
236 $page = "MediaWiki:Gadget-$page";
237
238 if ( preg_match( '/\.js/', $page ) ) {
239 $info['scripts'][] = $page;
240 } elseif ( preg_match( '/\.css/', $page ) ) {
241 $info['styles'][] = $page;
242 }
243 }
244
245 return new Gadget( $info );
246 }
247}
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.
wfMemcKey()
Make a cache key for the local wiki.
$line
Definition cdb.php:58
Wrapper for one gadget.
const GADGET_CLASS_VERSION
Increment this when changing class structure.
const CACHE_TTL
static isValidGadgetID( $id)
Whether the provided gadget id is valid.
Gadgets repo powered by MediaWiki:Gadgets-definition.
getGadget( $id)
Get the Gadget object for a given gadget id.
newFromDefinition( $definition, $category)
Creates an instance of this class from definition in MediaWiki:Gadgets-definition.
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:134
Relational database abstraction object.
Definition Database.php:45
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:1971
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:2990
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:1760
$cache
Definition mcc.php:33
$source
const DB_REPLICA
Definition defines.php:25
$lines
Definition router.php:61
$params