MediaWiki  1.30.0
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 ) {
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 
103  return $this->definitionCache;
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' );
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 }
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:45
WANObjectCache\TTL_UNCACHEABLE
const TTL_UNCACHEABLE
Idiom for getWithSetCallback() callbacks to avoid calling set()
Definition: WANObjectCache.php:128
Gadget\CACHE_TTL
const CACHE_TTL
Definition: Gadgets_body.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:35
MediaWikiGadgetsDefinitionRepo\getGadgetIds
getGadgetIds()
Get the ids of the gadgets provided by this repository.
Definition: MediaWikiGadgetsDefinitionRepo.php:22
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
$params
$params
Definition: styleTest.css.php:40
MediaWikiGadgetsDefinitionRepo
Gadgets repo powered by MediaWiki:Gadgets-definition.
Definition: MediaWikiGadgetsDefinitionRepo.php:8
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:134
wfMemcKey
wfMemcKey()
Make a cache key for the local wiki.
Definition: GlobalFunctions.php:2756
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:932
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2856
Gadget
Wrapper for one gadget.
Definition: Gadgets_body.php:17
$lines
$lines
Definition: router.php:67
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:529
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
GadgetRepo
Definition: GadgetRepo.php:3
WANObjectCache\HOLDOFF_TTL
const HOLDOFF_TTL
Seconds to tombstone keys on delete()
Definition: WANObjectCache.php:107
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:1047
MediaWikiGadgetsDefinitionRepo\listFromDefinition
listFromDefinition( $definition)
Generates a structured list of Gadget objects from a definition.
Definition: MediaWikiGadgetsDefinitionRepo.php:143
$line
$line
Definition: cdb.php:58
$value
$value
Definition: styleTest.css.php:45
Gadget\isValidGadgetID
static isValidGadgetID( $id)
Whether the provided gadget id is valid.
Definition: Gadgets_body.php:114
MediaWikiGadgetsDefinitionRepo\newFromDefinition
newFromDefinition( $definition, $category)
Creates an instance of this class from definition in MediaWiki:Gadgets-definition.
Definition: MediaWikiGadgetsDefinitionRepo.php:171
MediaWikiGadgetsDefinitionRepo\getCheckKey
getCheckKey()
Definition: MediaWikiGadgetsDefinitionRepo.php:40
MediaWikiGadgetsDefinitionRepo\fetchStructuredList
fetchStructuredList( $forceNewText=null)
Fetch list of gadgets and returns it as associative array of sections with gadgets e....
Definition: MediaWikiGadgetsDefinitionRepo.php:112
$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:1965
$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:2981
$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:1750
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:73
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
MediaWikiGadgetsDefinitionRepo\CACHE_VERSION
const CACHE_VERSION
Definition: MediaWikiGadgetsDefinitionRepo.php:9
MediaWikiGadgetsDefinitionRepo\getGadget
getGadget( $id)
Get the Gadget object for a given gadget id.
Definition: MediaWikiGadgetsDefinitionRepo.php:13
MediaWikiGadgetsDefinitionRepo\loadGadgets
loadGadgets()
Loads list of gadgets and returns it as associative array of sections with gadgets e....
Definition: MediaWikiGadgetsDefinitionRepo.php:50
MediaWikiGadgetsDefinitionRepo\$definitionCache
$definitionCache
Definition: MediaWikiGadgetsDefinitionRepo.php:11
Gadget\GADGET_CLASS_VERSION
const GADGET_CLASS_VERSION
Increment this when changing class structure.
Definition: Gadgets_body.php:21
ObjectCache\getLocalServerInstance
static getLocalServerInstance( $fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
Definition: ObjectCache.php:288