MediaWiki  1.29.1
MediaWikiGadgetsDefinitionRepo.php
Go to the documentation of this file.
1 <?php
3 
8  const CACHE_VERSION = 2;
9 
11 
12  public function getGadget( $id ) {
13  $gadgets = $this->loadGadgets();
14  if ( !isset( $gadgets[$id] ) ) {
15  throw new InvalidArgumentException( "No gadget registered for '$id'" );
16  }
17 
18  return $gadgets[$id];
19  }
20 
21  public function getGadgetIds() {
22  $gadgets = $this->loadGadgets();
23  if ( $gadgets ) {
24  return array_keys( $gadgets );
25  } else {
26  return [];
27  }
28  }
29 
34  public function purgeDefinitionCache() {
35  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
36  $cache->touchCheckKey( $this->getCheckKey() );
37  }
38 
39  private function getCheckKey() {
40  return wfMemcKey( 'gadgets-definition', Gadget::GADGET_CLASS_VERSION, self::CACHE_VERSION );
41  }
42 
49  protected function loadGadgets() {
50  if ( $this->definitionCache !== null ) {
51  return $this->definitionCache; // process cache hit
52  }
53 
54  // Ideally $t1Cache is APC, and $wanCache is memcached
55  $t1Cache = ObjectCache::getLocalServerInstance( 'hash' );
56  $wanCache = MediaWikiServices::getInstance()->getMainWANObjectCache();
57 
58  $key = $this->getCheckKey();
59 
60  // (a) Check the tier 1 cache
61  $value = $t1Cache->get( $key );
62  // Check if it passes a blind TTL check (avoids I/O)
63  if ( $value && ( microtime( true ) - $value['time'] ) < 10 ) {
64  $this->definitionCache = $value['gadgets']; // process cache
66  }
67  // Cache generated after the "check" time should be up-to-date
68  $ckTime = $wanCache->getCheckKeyTime( $key ) + WANObjectCache::HOLDOFF_TTL;
69  if ( $value && $value['time'] > $ckTime ) {
70  $this->definitionCache = $value['gadgets']; // process cache
72  }
73 
74  // (b) Fetch value from WAN cache or regenerate if needed.
75  // This is hit occasionally and more so when the list changes.
76  $us = $this;
77  $value = $wanCache->getWithSetCallback(
78  $key,
80  function ( $old, &$ttl ) use ( $us ) {
81  $now = microtime( true );
82  $gadgets = $us->fetchStructuredList();
83  if ( $gadgets === false ) {
85  }
86 
87  return [ 'gadgets' => $gadgets, 'time' => $now ];
88  },
89  [ 'checkKeys' => [ $key ], 'lockTSE' => 300 ]
90  );
91 
92  // Update the tier 1 cache as needed
93  if ( $value['gadgets'] !== false && $value['time'] > $ckTime ) {
94  // Set a modest TTL to keep the WAN key in cache
95  $t1Cache->set( $key, $value, mt_rand( 300, 600 ) );
96  }
97 
98  $this->definitionCache = $value['gadgets'];
99 
100  return $this->definitionCache;
101  }
102 
109  public function fetchStructuredList( $forceNewText = null ) {
110  if ( $forceNewText === null ) {
111  // T157210: avoid using wfMessage() to avoid staleness due to cache layering
112  $title = Title::makeTitle( NS_MEDIAWIKI, 'Gadgets-definition' );
114  if ( !$rev || !$rev->getContent() || $rev->getContent()->isEmpty() ) {
115  return false; // don't cache
116  }
117 
118  $g = $rev->getContent()->getNativeData();
119  } else {
120  $g = $forceNewText;
121  }
122 
123  $gadgets = $this->listFromDefinition( $g );
124  if ( !count( $gadgets ) ) {
125  return false; // don't cache; Bug 37228
126  }
127 
128  $source = $forceNewText !== null ? 'input text' : 'MediaWiki:Gadgets-definition';
129  wfDebug( __METHOD__ . ": $source parsed, cache entry should be updated\n" );
130 
131  return $gadgets;
132  }
133 
140  private function listFromDefinition( $definition ) {
141  $definition = preg_replace( '/<!--.*?-->/s', '', $definition );
142  $lines = preg_split( '/(\r\n|\r|\n)+/', $definition );
143 
144  $gadgets = [];
145  $section = '';
146 
147  foreach ( $lines as $line ) {
148  $m = [];
149  if ( preg_match( '/^==+ *([^*:\s|]+?)\s*==+\s*$/', $line, $m ) ) {
150  $section = $m[1];
151  } else {
152  $gadget = $this->newFromDefinition( $line, $section );
153  if ( $gadget ) {
154  $gadgets[$gadget->getName()] = $gadget;
155  }
156  }
157  }
158 
159  return $gadgets;
160  }
161 
168  public function newFromDefinition( $definition, $category ) {
169  $m = [];
170  if ( !preg_match(
171  '/^\*+ *([a-zA-Z](?:[-_:.\w\d ]*[a-zA-Z0-9])?)(\s*\[.*?\])?\s*((\|[^|]*)+)\s*$/',
172  $definition,
173  $m
174  ) ) {
175  return false;
176  }
177  // NOTE: the gadget name is used as part of the name of a form field,
178  // and must follow the rules defined in https://www.w3.org/TR/html4/types.html#type-cdata
179  // Also, title-normalization applies.
180  $info = [ 'category' => $category ];
181  $info['name'] = trim( str_replace( ' ', '_', $m[1] ) );
182  // If the name is too long, then RL will throw an MWException when
183  // we try to register the module
184  if ( !Gadget::isValidGadgetID( $info['name'] ) ) {
185  return false;
186  }
187  $info['definition'] = $definition;
188  $options = trim( $m[2], ' []' );
189 
190  foreach ( preg_split( '/\s*\|\s*/', $options, -1, PREG_SPLIT_NO_EMPTY ) as $option ) {
191  $arr = preg_split( '/\s*=\s*/', $option, 2 );
192  $option = $arr[0];
193  if ( isset( $arr[1] ) ) {
194  $params = explode( ',', $arr[1] );
195  $params = array_map( 'trim', $params );
196  } else {
197  $params = [];
198  }
199 
200  switch ( $option ) {
201  case 'ResourceLoader':
202  $info['resourceLoaded'] = true;
203  break;
204  case 'dependencies':
205  $info['dependencies'] = $params;
206  break;
207  case 'peers':
208  $info['peers'] = $params;
209  break;
210  case 'rights':
211  $info['requiredRights'] = $params;
212  break;
213  case 'hidden':
214  $info['hidden'] = true;
215  break;
216  case 'skins':
217  $info['requiredSkins'] = $params;
218  break;
219  case 'default':
220  $info['onByDefault'] = true;
221  break;
222  case 'targets':
223  $info['targets'] = $params;
224  break;
225  case 'type':
226  // Single value, not a list
227  $info['type'] = isset( $params[0] ) ? $params[0] : '';
228  break;
229  }
230  }
231 
232  foreach ( preg_split( '/\s*\|\s*/', $m[3], -1, PREG_SPLIT_NO_EMPTY ) as $page ) {
233  $page = "MediaWiki:Gadget-$page";
234 
235  if ( preg_match( '/\.js/', $page ) ) {
236  $info['scripts'][] = $page;
237  } elseif ( preg_match( '/\.css/', $page ) ) {
238  $info['styles'][] = $page;
239  }
240  }
241 
242  return new Gadget( $info );
243  }
244 }
WANObjectCache\TTL_UNCACHEABLE
const TTL_UNCACHEABLE
Idiom for getWithSetCallback() callbacks to avoid calling set()
Definition: WANObjectCache.php:127
Gadget\CACHE_TTL
const CACHE_TTL
Definition: Gadgets_body.php:24
captcha-old.count
count
Definition: captcha-old.py:225
MediaWikiGadgetsDefinitionRepo\purgeDefinitionCache
purgeDefinitionCache()
Purge the definitions cache, for example if MediaWiki:Gadgets-definition was edited.
Definition: MediaWikiGadgetsDefinitionRepo.php:34
MediaWikiGadgetsDefinitionRepo\getGadgetIds
getGadgetIds()
Get the ids of the gadgets provided by this repository.
Definition: MediaWikiGadgetsDefinitionRepo.php:21
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:7
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:2961
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:934
$page
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached $page
Definition: hooks.txt:2536
Gadget
Wrapper for one gadget.
Definition: Gadgets_body.php:18
$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:514
GadgetRepo
Definition: GadgetRepo.php:3
WANObjectCache\HOLDOFF_TTL
const HOLDOFF_TTL
Seconds to tombstone keys on delete()
Definition: WANObjectCache.php:106
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:999
MediaWikiGadgetsDefinitionRepo\listFromDefinition
listFromDefinition( $definition)
Generates a structured list of Gadget objects from a definition.
Definition: MediaWikiGadgetsDefinitionRepo.php:140
$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:116
MediaWikiGadgetsDefinitionRepo\newFromDefinition
newFromDefinition( $definition, $category)
Creates an instance of this class from definition in MediaWiki:Gadgets-definition.
Definition: MediaWikiGadgetsDefinitionRepo.php:168
MediaWikiGadgetsDefinitionRepo\getCheckKey
getCheckKey()
Definition: MediaWikiGadgetsDefinitionRepo.php:39
MediaWikiGadgetsDefinitionRepo\fetchStructuredList
fetchStructuredList( $forceNewText=null)
Fetch list of gadgets and returns it as associative array of sections with gadgets e....
Definition: MediaWikiGadgetsDefinitionRepo.php:109
$cache
$cache
Definition: mcc.php:33
$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:2929
$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:1741
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:45
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:70
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:8
MediaWikiGadgetsDefinitionRepo\getGadget
getGadget( $id)
Get the Gadget object for a given gadget id.
Definition: MediaWikiGadgetsDefinitionRepo.php:12
MediaWikiGadgetsDefinitionRepo\loadGadgets
loadGadgets()
Loads list of gadgets and returns it as associative array of sections with gadgets e....
Definition: MediaWikiGadgetsDefinitionRepo.php:49
MediaWikiGadgetsDefinitionRepo\$definitionCache
$definitionCache
Definition: MediaWikiGadgetsDefinitionRepo.php:10
$options
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1049
Gadget\GADGET_CLASS_VERSION
const GADGET_CLASS_VERSION
Increment this when changing class structure.
Definition: Gadgets_body.php:22
ObjectCache\getLocalServerInstance
static getLocalServerInstance( $fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
Definition: ObjectCache.php:288