MediaWiki  1.33.1
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 ) {
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 
126  return $this->definitionCache;
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' );
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 }
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:48
MediaWiki\Linker\LinkTarget\getText
getText()
Returns the link in text form, without namespace prefix or fragment.
WANObjectCache\TTL_UNCACHEABLE
const TTL_UNCACHEABLE
Idiom for getWithSetCallback() callbacks to avoid calling set()
Definition: WANObjectCache.php:177
Gadget\CACHE_TTL
const CACHE_TTL
Definition: Gadget.php:23
captcha-old.count
count
Definition: captcha-old.py:249
MediaWikiGadgetsDefinitionRepo\purgeDefinitionCache
purgeDefinitionCache()
Purge the definitions cache, for example if MediaWiki:Gadgets-definition was edited.
Definition: MediaWikiGadgetsDefinitionRepo.php:49
MediaWikiGadgetsDefinitionRepo\getGadgetIds
getGadgetIds()
Get the ids of the gadgets provided by this repository.
Definition: MediaWikiGadgetsDefinitionRepo.php:30
$params
$params
Definition: styleTest.css.php:44
MediaWikiGadgetsDefinitionRepo
Gadgets repo powered by MediaWiki:Gadgets-definition.
Definition: MediaWikiGadgetsDefinitionRepo.php:10
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
Revision\newFromTitle
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
MediaWiki\Linker\LinkTarget\getNamespace
getNamespace()
Get the namespace index.
MediaWikiGadgetsDefinitionRepo\handlePageUpdate
handlePageUpdate(LinkTarget $target)
Given that the provided page was updated, invalidate caches if necessary.
Definition: MediaWikiGadgetsDefinitionRepo.php:39
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:925
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2636
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
Gadget
Wrapper for one gadget.
Definition: Gadget.php:17
$lines
$lines
Definition: router.php:61
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:576
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
GadgetRepo
Definition: GadgetRepo.php:5
WANObjectCache\HOLDOFF_TTL
const HOLDOFF_TTL
Seconds to tombstone keys on delete()
Definition: WANObjectCache.php:153
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:949
MediaWikiGadgetsDefinitionRepo\listFromDefinition
listFromDefinition( $definition)
Generates a structured list of Gadget objects from a definition.
Definition: MediaWikiGadgetsDefinitionRepo.php:166
$line
$line
Definition: cdb.php:59
$value
$value
Definition: styleTest.css.php:49
Gadget\isValidGadgetID
static isValidGadgetID( $id)
Whether the provided gadget id is valid.
Definition: Gadget.php:114
MediaWikiGadgetsDefinitionRepo\newFromDefinition
newFromDefinition( $definition, $category)
Creates an instance of this class from definition in MediaWiki:Gadgets-definition.
Definition: MediaWikiGadgetsDefinitionRepo.php:194
MediaWikiGadgetsDefinitionRepo\fetchStructuredList
fetchStructuredList( $forceNewText=null)
Fetch list of gadgets and returns it as associative array of sections with gadgets e....
Definition: MediaWikiGadgetsDefinitionRepo.php:135
$cache
$cache
Definition: mcc.php:33
$options
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:1993
$section
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:3061
$rev
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:1769
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
$source
$source
Definition: mwdoc-filter.php:46
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:72
MediaWikiServices
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
MediaWikiGadgetsDefinitionRepo\CACHE_VERSION
const CACHE_VERSION
Definition: MediaWikiGadgetsDefinitionRepo.php:11
MediaWikiGadgetsDefinitionRepo\getGadget
getGadget( $id)
Definition: MediaWikiGadgetsDefinitionRepo.php:21
MediaWikiGadgetsDefinitionRepo\loadGadgets
loadGadgets()
Loads list of gadgets and returns it as associative array of sections with gadgets e....
Definition: MediaWikiGadgetsDefinitionRepo.php:70
MediaWikiGadgetsDefinitionRepo\$definitionCache
$definitionCache
Definition: MediaWikiGadgetsDefinitionRepo.php:13
Gadget\GADGET_CLASS_VERSION
const GADGET_CLASS_VERSION
Increment this when changing class structure.
Definition: Gadget.php:21
MediaWikiGadgetsDefinitionRepo\getDefinitionCacheKey
getDefinitionCacheKey()
Definition: MediaWikiGadgetsDefinitionRepo.php:54
ObjectCache\getLocalServerInstance
static getLocalServerInstance( $fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
Definition: ObjectCache.php:279