MediaWiki  master
SessionBackendTest.php
Go to the documentation of this file.
1 <?php
2 
3 namespace MediaWiki\Session;
4 
5 use Config;
7 use User;
9 
16  const SESSIONID = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
17 
19  protected $manager;
20 
22  protected $config;
23 
25  protected $provider;
26 
28  protected $store;
29 
30  protected $onSessionMetadataCalled = false;
31 
38  protected function getBackend( User $user = null, $id = null ) {
39  if ( !$this->config ) {
40  $this->config = new \HashConfig();
41  $this->manager = null;
42  }
43  if ( !$this->store ) {
44  $this->store = new TestBagOStuff();
45  $this->manager = null;
46  }
47 
48  $logger = new \Psr\Log\NullLogger();
49  if ( !$this->manager ) {
50  $this->manager = new SessionManager( [
51  'store' => $this->store,
52  'logger' => $logger,
53  'config' => $this->config,
54  ] );
55  }
56 
57  if ( !$this->provider ) {
58  $this->provider = new \DummySessionProvider();
59  }
60  $this->provider->setLogger( $logger );
61  $this->provider->setConfig( $this->config );
62  $this->provider->setManager( $this->manager );
63 
65  'provider' => $this->provider,
66  'id' => $id ?: self::SESSIONID,
67  'persisted' => true,
68  'userInfo' => UserInfo::newFromUser( $user ?: new User, true ),
69  'idIsSafe' => true,
70  ] );
71  $id = new SessionId( $info->getId() );
72 
73  $backend = new SessionBackend( $id, $info, $this->store, $logger, 10 );
74  $priv = TestingAccessWrapper::newFromObject( $backend );
75  $priv->persist = false;
76  $priv->requests = [ 100 => new \FauxRequest() ];
77  $priv->requests[100]->setSessionId( $id );
78  $priv->usePhpSessionHandling = false;
79 
80  $manager = TestingAccessWrapper::newFromObject( $this->manager );
81  $manager->allSessionBackends = [ $backend->getId() => $backend ] + $manager->allSessionBackends;
82  $manager->allSessionIds = [ $backend->getId() => $id ] + $manager->allSessionIds;
83  $manager->sessionProviders = [ (string)$this->provider => $this->provider ];
84 
85  return $backend;
86  }
87 
88  public function testConstructor() {
89  // Set variables
90  $this->getBackend();
91 
93  'provider' => $this->provider,
94  'id' => self::SESSIONID,
95  'persisted' => true,
96  'userInfo' => UserInfo::newFromName( 'UTSysop', false ),
97  'idIsSafe' => true,
98  ] );
99  $id = new SessionId( $info->getId() );
100  $logger = new \Psr\Log\NullLogger();
101  try {
102  new SessionBackend( $id, $info, $this->store, $logger, 10 );
103  $this->fail( 'Expected exception not thrown' );
104  } catch ( \InvalidArgumentException $ex ) {
105  $this->assertSame(
106  "Refusing to create session for unverified user {$info->getUserInfo()}",
107  $ex->getMessage()
108  );
109  }
110 
111  $info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
112  'id' => self::SESSIONID,
113  'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
114  'idIsSafe' => true,
115  ] );
116  $id = new SessionId( $info->getId() );
117  try {
118  new SessionBackend( $id, $info, $this->store, $logger, 10 );
119  $this->fail( 'Expected exception not thrown' );
120  } catch ( \InvalidArgumentException $ex ) {
121  $this->assertSame( 'Cannot create session without a provider', $ex->getMessage() );
122  }
123 
124  $info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
125  'provider' => $this->provider,
126  'id' => self::SESSIONID,
127  'persisted' => true,
128  'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
129  'idIsSafe' => true,
130  ] );
131  $id = new SessionId( '!' . $info->getId() );
132  try {
133  new SessionBackend( $id, $info, $this->store, $logger, 10 );
134  $this->fail( 'Expected exception not thrown' );
135  } catch ( \InvalidArgumentException $ex ) {
136  $this->assertSame(
137  'SessionId and SessionInfo don\'t match',
138  $ex->getMessage()
139  );
140  }
141 
142  $info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
143  'provider' => $this->provider,
144  'id' => self::SESSIONID,
145  'persisted' => true,
146  'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
147  'idIsSafe' => true,
148  ] );
149  $id = new SessionId( $info->getId() );
150  $backend = new SessionBackend( $id, $info, $this->store, $logger, 10 );
151  $this->assertSame( self::SESSIONID, $backend->getId() );
152  $this->assertSame( $id, $backend->getSessionId() );
153  $this->assertSame( $this->provider, $backend->getProvider() );
154  $this->assertInstanceOf( User::class, $backend->getUser() );
155  $this->assertSame( 'UTSysop', $backend->getUser()->getName() );
156  $this->assertSame( $info->wasPersisted(), $backend->isPersistent() );
157  $this->assertSame( $info->wasRemembered(), $backend->shouldRememberUser() );
158  $this->assertSame( $info->forceHTTPS(), $backend->shouldForceHTTPS() );
159 
160  $expire = time() + 100;
161  $this->store->setSessionMeta( self::SESSIONID, [ 'expires' => $expire ] );
162 
163  $info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
164  'provider' => $this->provider,
165  'id' => self::SESSIONID,
166  'persisted' => true,
167  'forceHTTPS' => true,
168  'metadata' => [ 'foo' ],
169  'idIsSafe' => true,
170  ] );
171  $id = new SessionId( $info->getId() );
172  $backend = new SessionBackend( $id, $info, $this->store, $logger, 10 );
173  $this->assertSame( self::SESSIONID, $backend->getId() );
174  $this->assertSame( $id, $backend->getSessionId() );
175  $this->assertSame( $this->provider, $backend->getProvider() );
176  $this->assertInstanceOf( User::class, $backend->getUser() );
177  $this->assertTrue( $backend->getUser()->isAnon() );
178  $this->assertSame( $info->wasPersisted(), $backend->isPersistent() );
179  $this->assertSame( $info->wasRemembered(), $backend->shouldRememberUser() );
180  $this->assertSame( $info->forceHTTPS(), $backend->shouldForceHTTPS() );
181  $this->assertSame( $expire, TestingAccessWrapper::newFromObject( $backend )->expires );
182  $this->assertSame( [ 'foo' ], $backend->getProviderMetadata() );
183  }
184 
185  public function testSessionStuff() {
186  $backend = $this->getBackend();
187  $priv = TestingAccessWrapper::newFromObject( $backend );
188  $priv->requests = []; // Remove dummy session
189 
190  $manager = TestingAccessWrapper::newFromObject( $this->manager );
191 
192  $request1 = new \FauxRequest();
193  $session1 = $backend->getSession( $request1 );
194  $request2 = new \FauxRequest();
195  $session2 = $backend->getSession( $request2 );
196 
197  $this->assertInstanceOf( Session::class, $session1 );
198  $this->assertInstanceOf( Session::class, $session2 );
199  $this->assertSame( 2, count( $priv->requests ) );
200 
201  $index = TestingAccessWrapper::newFromObject( $session1 )->index;
202 
203  $this->assertSame( $request1, $backend->getRequest( $index ) );
204  $this->assertSame( null, $backend->suggestLoginUsername( $index ) );
205  $request1->setCookie( 'UserName', 'Example' );
206  $this->assertSame( 'Example', $backend->suggestLoginUsername( $index ) );
207 
208  $session1 = null;
209  $this->assertSame( 1, count( $priv->requests ) );
210  $this->assertArrayHasKey( $backend->getId(), $manager->allSessionBackends );
211  $this->assertSame( $backend, $manager->allSessionBackends[$backend->getId()] );
212  try {
213  $backend->getRequest( $index );
214  $this->fail( 'Expected exception not thrown' );
215  } catch ( \InvalidArgumentException $ex ) {
216  $this->assertSame( 'Invalid session index', $ex->getMessage() );
217  }
218  try {
219  $backend->suggestLoginUsername( $index );
220  $this->fail( 'Expected exception not thrown' );
221  } catch ( \InvalidArgumentException $ex ) {
222  $this->assertSame( 'Invalid session index', $ex->getMessage() );
223  }
224 
225  $session2 = null;
226  $this->assertSame( 0, count( $priv->requests ) );
227  $this->assertArrayNotHasKey( $backend->getId(), $manager->allSessionBackends );
228  $this->assertArrayHasKey( $backend->getId(), $manager->allSessionIds );
229  }
230 
231  public function testSetProviderMetadata() {
232  $backend = $this->getBackend();
233  $priv = TestingAccessWrapper::newFromObject( $backend );
234  $priv->providerMetadata = [ 'dummy' ];
235 
236  try {
237  $backend->setProviderMetadata( 'foo' );
238  $this->fail( 'Expected exception not thrown' );
239  } catch ( \InvalidArgumentException $ex ) {
240  $this->assertSame( '$metadata must be an array or null', $ex->getMessage() );
241  }
242 
243  try {
244  $backend->setProviderMetadata( (object)[] );
245  $this->fail( 'Expected exception not thrown' );
246  } catch ( \InvalidArgumentException $ex ) {
247  $this->assertSame( '$metadata must be an array or null', $ex->getMessage() );
248  }
249 
250  $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'sanity check' );
251  $backend->setProviderMetadata( [ 'dummy' ] );
252  $this->assertFalse( $this->store->getSession( self::SESSIONID ) );
253 
254  $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'sanity check' );
255  $backend->setProviderMetadata( [ 'test' ] );
256  $this->assertNotFalse( $this->store->getSession( self::SESSIONID ) );
257  $this->assertSame( [ 'test' ], $backend->getProviderMetadata() );
258  $this->store->deleteSession( self::SESSIONID );
259 
260  $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'sanity check' );
261  $backend->setProviderMetadata( null );
262  $this->assertNotFalse( $this->store->getSession( self::SESSIONID ) );
263  $this->assertSame( null, $backend->getProviderMetadata() );
264  $this->store->deleteSession( self::SESSIONID );
265  }
266 
267  public function testResetId() {
268  $id = session_id();
269 
270  $builder = $this->getMockBuilder( \DummySessionProvider::class )
271  ->setMethods( [ 'persistsSessionId', 'sessionIdWasReset' ] );
272 
273  $this->provider = $builder->getMock();
274  $this->provider->expects( $this->any() )->method( 'persistsSessionId' )
275  ->will( $this->returnValue( false ) );
276  $this->provider->expects( $this->never() )->method( 'sessionIdWasReset' );
277  $backend = $this->getBackend( User::newFromName( 'UTSysop' ) );
278  $manager = TestingAccessWrapper::newFromObject( $this->manager );
279  $sessionId = $backend->getSessionId();
280  $backend->resetId();
281  $this->assertSame( self::SESSIONID, $backend->getId() );
282  $this->assertSame( $backend->getId(), $sessionId->getId() );
283  $this->assertSame( $id, session_id() );
284  $this->assertSame( $backend, $manager->allSessionBackends[self::SESSIONID] );
285 
286  $this->provider = $builder->getMock();
287  $this->provider->expects( $this->any() )->method( 'persistsSessionId' )
288  ->will( $this->returnValue( true ) );
289  $backend = $this->getBackend();
290  $this->provider->expects( $this->once() )->method( 'sessionIdWasReset' )
291  ->with( $this->identicalTo( $backend ), $this->identicalTo( self::SESSIONID ) );
292  $manager = TestingAccessWrapper::newFromObject( $this->manager );
293  $sessionId = $backend->getSessionId();
294  $backend->resetId();
295  $this->assertNotEquals( self::SESSIONID, $backend->getId() );
296  $this->assertSame( $backend->getId(), $sessionId->getId() );
297  $this->assertInternalType( 'array', $this->store->getSession( $backend->getId() ) );
298  $this->assertFalse( $this->store->getSession( self::SESSIONID ) );
299  $this->assertSame( $id, session_id() );
300  $this->assertArrayNotHasKey( self::SESSIONID, $manager->allSessionBackends );
301  $this->assertArrayHasKey( $backend->getId(), $manager->allSessionBackends );
302  $this->assertSame( $backend, $manager->allSessionBackends[$backend->getId()] );
303  }
304 
305  public function testPersist() {
306  $this->provider = $this->getMockBuilder( \DummySessionProvider::class )
307  ->setMethods( [ 'persistSession' ] )->getMock();
308  $this->provider->expects( $this->once() )->method( 'persistSession' );
309  $backend = $this->getBackend();
310  $this->assertFalse( $backend->isPersistent(), 'sanity check' );
311  $backend->save(); // This one shouldn't call $provider->persistSession()
312 
313  $backend->persist();
314  $this->assertTrue( $backend->isPersistent(), 'sanity check' );
315 
316  $this->provider = null;
317  $backend = $this->getBackend();
318  $wrap = TestingAccessWrapper::newFromObject( $backend );
319  $wrap->persist = true;
320  $wrap->expires = 0;
321  $backend->persist();
322  $this->assertNotEquals( 0, $wrap->expires );
323  }
324 
325  public function testUnpersist() {
326  $this->provider = $this->getMockBuilder( \DummySessionProvider::class )
327  ->setMethods( [ 'unpersistSession' ] )->getMock();
328  $this->provider->expects( $this->once() )->method( 'unpersistSession' );
329  $backend = $this->getBackend();
330  $wrap = TestingAccessWrapper::newFromObject( $backend );
331  $wrap->store = new \CachedBagOStuff( $this->store );
332  $wrap->persist = true;
333  $wrap->dataDirty = true;
334 
335  $backend->save(); // This one shouldn't call $provider->persistSession(), but should save
336  $this->assertTrue( $backend->isPersistent(), 'sanity check' );
337  $this->assertNotFalse( $this->store->getSession( self::SESSIONID ), 'sanity check' );
338 
339  $backend->unpersist();
340  $this->assertFalse( $backend->isPersistent() );
341  $this->assertFalse( $this->store->getSession( self::SESSIONID ) );
342  $this->assertNotFalse(
343  $wrap->store->get( $wrap->store->makeKey( 'MWSession', self::SESSIONID ) )
344  );
345  }
346 
347  public function testRememberUser() {
348  $backend = $this->getBackend();
349 
350  $remembered = $backend->shouldRememberUser();
351  $backend->setRememberUser( !$remembered );
352  $this->assertNotEquals( $remembered, $backend->shouldRememberUser() );
353  $backend->setRememberUser( $remembered );
354  $this->assertEquals( $remembered, $backend->shouldRememberUser() );
355  }
356 
357  public function testForceHTTPS() {
358  $backend = $this->getBackend();
359 
360  $force = $backend->shouldForceHTTPS();
361  $backend->setForceHTTPS( !$force );
362  $this->assertNotEquals( $force, $backend->shouldForceHTTPS() );
363  $backend->setForceHTTPS( $force );
364  $this->assertEquals( $force, $backend->shouldForceHTTPS() );
365  }
366 
367  public function testLoggedOutTimestamp() {
368  $backend = $this->getBackend();
369 
370  $backend->setLoggedOutTimestamp( 42 );
371  $this->assertSame( 42, $backend->getLoggedOutTimestamp() );
372  $backend->setLoggedOutTimestamp( '123' );
373  $this->assertSame( 123, $backend->getLoggedOutTimestamp() );
374  }
375 
376  public function testSetUser() {
377  $user = static::getTestSysop()->getUser();
378 
379  $this->provider = $this->getMockBuilder( \DummySessionProvider::class )
380  ->setMethods( [ 'canChangeUser' ] )->getMock();
381  $this->provider->expects( $this->any() )->method( 'canChangeUser' )
382  ->will( $this->returnValue( false ) );
383  $backend = $this->getBackend();
384  $this->assertFalse( $backend->canSetUser() );
385  try {
386  $backend->setUser( $user );
387  $this->fail( 'Expected exception not thrown' );
388  } catch ( \BadMethodCallException $ex ) {
389  $this->assertSame(
390  'Cannot set user on this session; check $session->canSetUser() first',
391  $ex->getMessage()
392  );
393  }
394  $this->assertNotSame( $user, $backend->getUser() );
395 
396  $this->provider = null;
397  $backend = $this->getBackend();
398  $this->assertTrue( $backend->canSetUser() );
399  $this->assertNotSame( $user, $backend->getUser(), 'sanity check' );
400  $backend->setUser( $user );
401  $this->assertSame( $user, $backend->getUser() );
402  }
403 
404  public function testDirty() {
405  $backend = $this->getBackend();
406  $priv = TestingAccessWrapper::newFromObject( $backend );
407  $priv->dataDirty = false;
408  $backend->dirty();
409  $this->assertTrue( $priv->dataDirty );
410  }
411 
412  public function testGetData() {
413  $backend = $this->getBackend();
414  $data = $backend->getData();
415  $this->assertSame( [], $data );
416  $this->assertTrue( TestingAccessWrapper::newFromObject( $backend )->dataDirty );
417  $data['???'] = '!!!';
418  $this->assertSame( [ '???' => '!!!' ], $data );
419 
420  $testData = [ 'foo' => 'foo!', 'bar', [ 'baz', null ] ];
421  $this->store->setSessionData( self::SESSIONID, $testData );
422  $backend = $this->getBackend();
423  $this->assertSame( $testData, $backend->getData() );
424  $this->assertFalse( TestingAccessWrapper::newFromObject( $backend )->dataDirty );
425  }
426 
427  public function testAddData() {
428  $backend = $this->getBackend();
429  $priv = TestingAccessWrapper::newFromObject( $backend );
430 
431  $priv->data = [ 'foo' => 1 ];
432  $priv->dataDirty = false;
433  $backend->addData( [ 'foo' => 1 ] );
434  $this->assertSame( [ 'foo' => 1 ], $priv->data );
435  $this->assertFalse( $priv->dataDirty );
436 
437  $priv->data = [ 'foo' => 1 ];
438  $priv->dataDirty = false;
439  $backend->addData( [ 'foo' => '1' ] );
440  $this->assertSame( [ 'foo' => '1' ], $priv->data );
441  $this->assertTrue( $priv->dataDirty );
442 
443  $priv->data = [ 'foo' => 1 ];
444  $priv->dataDirty = false;
445  $backend->addData( [ 'bar' => 2 ] );
446  $this->assertSame( [ 'foo' => 1, 'bar' => 2 ], $priv->data );
447  $this->assertTrue( $priv->dataDirty );
448  }
449 
450  public function testDelaySave() {
451  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
452  $backend = $this->getBackend();
453  $priv = TestingAccessWrapper::newFromObject( $backend );
454  $priv->persist = true;
455 
456  // Saves happen normally when no delay is in effect
457  $this->onSessionMetadataCalled = false;
458  $priv->metaDirty = true;
459  $backend->save();
460  $this->assertTrue( $this->onSessionMetadataCalled, 'sanity check' );
461 
462  $this->onSessionMetadataCalled = false;
463  $priv->metaDirty = true;
464  $priv->autosave();
465  $this->assertTrue( $this->onSessionMetadataCalled, 'sanity check' );
466 
467  $delay = $backend->delaySave();
468 
469  // Autosave doesn't happen when no delay is in effect
470  $this->onSessionMetadataCalled = false;
471  $priv->metaDirty = true;
472  $priv->autosave();
473  $this->assertFalse( $this->onSessionMetadataCalled );
474 
475  // Save still does happen when no delay is in effect
476  $priv->save();
477  $this->assertTrue( $this->onSessionMetadataCalled );
478 
479  // Save happens when delay is consumed
480  $this->onSessionMetadataCalled = false;
481  $priv->metaDirty = true;
482  \Wikimedia\ScopedCallback::consume( $delay );
483  $this->assertTrue( $this->onSessionMetadataCalled );
484 
485  // Test multiple delays
486  $delay1 = $backend->delaySave();
487  $delay2 = $backend->delaySave();
488  $delay3 = $backend->delaySave();
489  $this->onSessionMetadataCalled = false;
490  $priv->metaDirty = true;
491  $priv->autosave();
492  $this->assertFalse( $this->onSessionMetadataCalled );
493  \Wikimedia\ScopedCallback::consume( $delay3 );
494  $this->assertFalse( $this->onSessionMetadataCalled );
495  \Wikimedia\ScopedCallback::consume( $delay1 );
496  $this->assertFalse( $this->onSessionMetadataCalled );
497  \Wikimedia\ScopedCallback::consume( $delay2 );
498  $this->assertTrue( $this->onSessionMetadataCalled );
499  }
500 
501  public function testSave() {
502  $user = static::getTestSysop()->getUser();
503  $this->store = new TestBagOStuff();
504  $testData = [ 'foo' => 'foo!', 'bar', [ 'baz', null ] ];
505 
506  $neverHook = $this->getMockBuilder( __CLASS__ )
507  ->setMethods( [ 'onSessionMetadata' ] )->getMock();
508  $neverHook->expects( $this->never() )->method( 'onSessionMetadata' );
509 
510  $builder = $this->getMockBuilder( \DummySessionProvider::class )
511  ->setMethods( [ 'persistSession', 'unpersistSession' ] );
512 
513  $neverProvider = $builder->getMock();
514  $neverProvider->expects( $this->never() )->method( 'persistSession' );
515  $neverProvider->expects( $this->never() )->method( 'unpersistSession' );
516 
517  // Not persistent or dirty
518  $this->provider = $neverProvider;
519  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
520  $this->store->setSessionData( self::SESSIONID, $testData );
521  $backend = $this->getBackend( $user );
522  $this->store->deleteSession( self::SESSIONID );
523  $this->assertFalse( $backend->isPersistent(), 'sanity check' );
524  TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
525  TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
526  $backend->save();
527  $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
528 
529  // (but does unpersist if forced)
530  $this->provider = $builder->getMock();
531  $this->provider->expects( $this->never() )->method( 'persistSession' );
532  $this->provider->expects( $this->atLeastOnce() )->method( 'unpersistSession' );
533  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
534  $this->store->setSessionData( self::SESSIONID, $testData );
535  $backend = $this->getBackend( $user );
536  $this->store->deleteSession( self::SESSIONID );
537  TestingAccessWrapper::newFromObject( $backend )->persist = false;
538  TestingAccessWrapper::newFromObject( $backend )->forcePersist = true;
539  $this->assertFalse( $backend->isPersistent(), 'sanity check' );
540  TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
541  TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
542  $backend->save();
543  $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
544 
545  // (but not to a WebRequest associated with a different session)
546  $this->provider = $neverProvider;
547  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
548  $this->store->setSessionData( self::SESSIONID, $testData );
549  $backend = $this->getBackend( $user );
550  TestingAccessWrapper::newFromObject( $backend )->requests[100]
551  ->setSessionId( new SessionId( 'x' ) );
552  $this->store->deleteSession( self::SESSIONID );
553  TestingAccessWrapper::newFromObject( $backend )->persist = false;
554  TestingAccessWrapper::newFromObject( $backend )->forcePersist = true;
555  $this->assertFalse( $backend->isPersistent(), 'sanity check' );
556  TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
557  TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
558  $backend->save();
559  $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
560 
561  // Not persistent, but dirty
562  $this->provider = $neverProvider;
563  $this->onSessionMetadataCalled = false;
564  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
565  $this->store->setSessionData( self::SESSIONID, $testData );
566  $backend = $this->getBackend( $user );
567  $this->store->deleteSession( self::SESSIONID );
568  $this->assertFalse( $backend->isPersistent(), 'sanity check' );
569  TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
570  TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
571  $backend->save();
572  $this->assertTrue( $this->onSessionMetadataCalled );
573  $blob = $this->store->getSession( self::SESSIONID );
574  $this->assertInternalType( 'array', $blob );
575  $this->assertArrayHasKey( 'metadata', $blob );
576  $metadata = $blob['metadata'];
577  $this->assertInternalType( 'array', $metadata );
578  $this->assertArrayHasKey( '???', $metadata );
579  $this->assertSame( '!!!', $metadata['???'] );
580  $this->assertFalse( $this->store->getSessionFromBackend( self::SESSIONID ),
581  'making sure it didn\'t save to backend' );
582 
583  // Persistent, not dirty
584  $this->provider = $neverProvider;
585  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
586  $this->store->setSessionData( self::SESSIONID, $testData );
587  $backend = $this->getBackend( $user );
588  $this->store->deleteSession( self::SESSIONID );
589  TestingAccessWrapper::newFromObject( $backend )->persist = true;
590  $this->assertTrue( $backend->isPersistent(), 'sanity check' );
591  TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
592  TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
593  $backend->save();
594  $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
595 
596  // (but will persist if forced)
597  $this->provider = $builder->getMock();
598  $this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
599  $this->provider->expects( $this->never() )->method( 'unpersistSession' );
600  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
601  $this->store->setSessionData( self::SESSIONID, $testData );
602  $backend = $this->getBackend( $user );
603  $this->store->deleteSession( self::SESSIONID );
604  TestingAccessWrapper::newFromObject( $backend )->persist = true;
605  TestingAccessWrapper::newFromObject( $backend )->forcePersist = true;
606  $this->assertTrue( $backend->isPersistent(), 'sanity check' );
607  TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
608  TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
609  $backend->save();
610  $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
611 
612  // Persistent and dirty
613  $this->provider = $neverProvider;
614  $this->onSessionMetadataCalled = false;
615  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
616  $this->store->setSessionData( self::SESSIONID, $testData );
617  $backend = $this->getBackend( $user );
618  $this->store->deleteSession( self::SESSIONID );
619  TestingAccessWrapper::newFromObject( $backend )->persist = true;
620  $this->assertTrue( $backend->isPersistent(), 'sanity check' );
621  TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
622  TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
623  $backend->save();
624  $this->assertTrue( $this->onSessionMetadataCalled );
625  $blob = $this->store->getSession( self::SESSIONID );
626  $this->assertInternalType( 'array', $blob );
627  $this->assertArrayHasKey( 'metadata', $blob );
628  $metadata = $blob['metadata'];
629  $this->assertInternalType( 'array', $metadata );
630  $this->assertArrayHasKey( '???', $metadata );
631  $this->assertSame( '!!!', $metadata['???'] );
632  $this->assertNotSame( false, $this->store->getSessionFromBackend( self::SESSIONID ),
633  'making sure it did save to backend' );
634 
635  // (also persists if forced)
636  $this->provider = $builder->getMock();
637  $this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
638  $this->provider->expects( $this->never() )->method( 'unpersistSession' );
639  $this->onSessionMetadataCalled = false;
640  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
641  $this->store->setSessionData( self::SESSIONID, $testData );
642  $backend = $this->getBackend( $user );
643  $this->store->deleteSession( self::SESSIONID );
644  TestingAccessWrapper::newFromObject( $backend )->persist = true;
645  TestingAccessWrapper::newFromObject( $backend )->forcePersist = true;
646  $this->assertTrue( $backend->isPersistent(), 'sanity check' );
647  TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
648  TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
649  $backend->save();
650  $this->assertTrue( $this->onSessionMetadataCalled );
651  $blob = $this->store->getSession( self::SESSIONID );
652  $this->assertInternalType( 'array', $blob );
653  $this->assertArrayHasKey( 'metadata', $blob );
654  $metadata = $blob['metadata'];
655  $this->assertInternalType( 'array', $metadata );
656  $this->assertArrayHasKey( '???', $metadata );
657  $this->assertSame( '!!!', $metadata['???'] );
658  $this->assertNotSame( false, $this->store->getSessionFromBackend( self::SESSIONID ),
659  'making sure it did save to backend' );
660 
661  // (also persists if metadata dirty)
662  $this->provider = $builder->getMock();
663  $this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
664  $this->provider->expects( $this->never() )->method( 'unpersistSession' );
665  $this->onSessionMetadataCalled = false;
666  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
667  $this->store->setSessionData( self::SESSIONID, $testData );
668  $backend = $this->getBackend( $user );
669  $this->store->deleteSession( self::SESSIONID );
670  TestingAccessWrapper::newFromObject( $backend )->persist = true;
671  $this->assertTrue( $backend->isPersistent(), 'sanity check' );
672  TestingAccessWrapper::newFromObject( $backend )->metaDirty = true;
673  TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
674  $backend->save();
675  $this->assertTrue( $this->onSessionMetadataCalled );
676  $blob = $this->store->getSession( self::SESSIONID );
677  $this->assertInternalType( 'array', $blob );
678  $this->assertArrayHasKey( 'metadata', $blob );
679  $metadata = $blob['metadata'];
680  $this->assertInternalType( 'array', $metadata );
681  $this->assertArrayHasKey( '???', $metadata );
682  $this->assertSame( '!!!', $metadata['???'] );
683  $this->assertNotSame( false, $this->store->getSessionFromBackend( self::SESSIONID ),
684  'making sure it did save to backend' );
685 
686  // Not marked dirty, but dirty data
687  // (e.g. indirect modification from ArrayAccess::offsetGet)
688  $this->provider = $neverProvider;
689  $this->onSessionMetadataCalled = false;
690  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
691  $this->store->setSessionData( self::SESSIONID, $testData );
692  $backend = $this->getBackend( $user );
693  $this->store->deleteSession( self::SESSIONID );
694  TestingAccessWrapper::newFromObject( $backend )->persist = true;
695  $this->assertTrue( $backend->isPersistent(), 'sanity check' );
696  TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
697  TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
698  TestingAccessWrapper::newFromObject( $backend )->dataHash = 'Doesn\'t match';
699  $backend->save();
700  $this->assertTrue( $this->onSessionMetadataCalled );
701  $blob = $this->store->getSession( self::SESSIONID );
702  $this->assertInternalType( 'array', $blob );
703  $this->assertArrayHasKey( 'metadata', $blob );
704  $metadata = $blob['metadata'];
705  $this->assertInternalType( 'array', $metadata );
706  $this->assertArrayHasKey( '???', $metadata );
707  $this->assertSame( '!!!', $metadata['???'] );
708  $this->assertNotSame( false, $this->store->getSessionFromBackend( self::SESSIONID ),
709  'making sure it did save to backend' );
710 
711  // Bad hook
712  $this->provider = null;
713  $mockHook = $this->getMockBuilder( __CLASS__ )
714  ->setMethods( [ 'onSessionMetadata' ] )->getMock();
715  $mockHook->expects( $this->any() )->method( 'onSessionMetadata' )
716  ->will( $this->returnCallback(
717  function ( SessionBackend $backend, array &$metadata, array $requests ) {
718  $metadata['userId']++;
719  }
720  ) );
721  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $mockHook ] ] );
722  $this->store->setSessionData( self::SESSIONID, $testData );
723  $backend = $this->getBackend( $user );
724  $backend->dirty();
725  try {
726  $backend->save();
727  $this->fail( 'Expected exception not thrown' );
728  } catch ( \UnexpectedValueException $ex ) {
729  $this->assertSame(
730  'SessionMetadata hook changed metadata key "userId"',
731  $ex->getMessage()
732  );
733  }
734 
735  // SessionManager::preventSessionsForUser
736  TestingAccessWrapper::newFromObject( $this->manager )->preventUsers = [
737  $user->getName() => true,
738  ];
739  $this->provider = $neverProvider;
740  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $neverHook ] ] );
741  $this->store->setSessionData( self::SESSIONID, $testData );
742  $backend = $this->getBackend( $user );
743  $this->store->deleteSession( self::SESSIONID );
744  TestingAccessWrapper::newFromObject( $backend )->persist = true;
745  $this->assertTrue( $backend->isPersistent(), 'sanity check' );
746  TestingAccessWrapper::newFromObject( $backend )->metaDirty = true;
747  TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
748  $backend->save();
749  $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
750  }
751 
752  public function testRenew() {
753  $user = static::getTestSysop()->getUser();
754  $this->store = new TestBagOStuff();
755  $testData = [ 'foo' => 'foo!', 'bar', [ 'baz', null ] ];
756 
757  // Not persistent
758  $this->provider = $this->getMockBuilder( \DummySessionProvider::class )
759  ->setMethods( [ 'persistSession' ] )->getMock();
760  $this->provider->expects( $this->never() )->method( 'persistSession' );
761  $this->onSessionMetadataCalled = false;
762  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
763  $this->store->setSessionData( self::SESSIONID, $testData );
764  $backend = $this->getBackend( $user );
765  $this->store->deleteSession( self::SESSIONID );
766  $wrap = TestingAccessWrapper::newFromObject( $backend );
767  $this->assertFalse( $backend->isPersistent(), 'sanity check' );
768  $wrap->metaDirty = false;
769  $wrap->dataDirty = false;
770  $wrap->forcePersist = false;
771  $wrap->expires = 0;
772  $backend->renew();
773  $this->assertTrue( $this->onSessionMetadataCalled );
774  $blob = $this->store->getSession( self::SESSIONID );
775  $this->assertInternalType( 'array', $blob );
776  $this->assertArrayHasKey( 'metadata', $blob );
777  $metadata = $blob['metadata'];
778  $this->assertInternalType( 'array', $metadata );
779  $this->assertArrayHasKey( '???', $metadata );
780  $this->assertSame( '!!!', $metadata['???'] );
781  $this->assertNotEquals( 0, $wrap->expires );
782 
783  // Persistent
784  $this->provider = $this->getMockBuilder( \DummySessionProvider::class )
785  ->setMethods( [ 'persistSession' ] )->getMock();
786  $this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
787  $this->onSessionMetadataCalled = false;
788  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
789  $this->store->setSessionData( self::SESSIONID, $testData );
790  $backend = $this->getBackend( $user );
791  $this->store->deleteSession( self::SESSIONID );
792  $wrap = TestingAccessWrapper::newFromObject( $backend );
793  $wrap->persist = true;
794  $this->assertTrue( $backend->isPersistent(), 'sanity check' );
795  $wrap->metaDirty = false;
796  $wrap->dataDirty = false;
797  $wrap->forcePersist = false;
798  $wrap->expires = 0;
799  $backend->renew();
800  $this->assertTrue( $this->onSessionMetadataCalled );
801  $blob = $this->store->getSession( self::SESSIONID );
802  $this->assertInternalType( 'array', $blob );
803  $this->assertArrayHasKey( 'metadata', $blob );
804  $metadata = $blob['metadata'];
805  $this->assertInternalType( 'array', $metadata );
806  $this->assertArrayHasKey( '???', $metadata );
807  $this->assertSame( '!!!', $metadata['???'] );
808  $this->assertNotEquals( 0, $wrap->expires );
809 
810  // Not persistent, not expiring
811  $this->provider = $this->getMockBuilder( \DummySessionProvider::class )
812  ->setMethods( [ 'persistSession' ] )->getMock();
813  $this->provider->expects( $this->never() )->method( 'persistSession' );
814  $this->onSessionMetadataCalled = false;
815  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionMetadata' => [ $this ] ] );
816  $this->store->setSessionData( self::SESSIONID, $testData );
817  $backend = $this->getBackend( $user );
818  $this->store->deleteSession( self::SESSIONID );
819  $wrap = TestingAccessWrapper::newFromObject( $backend );
820  $this->assertFalse( $backend->isPersistent(), 'sanity check' );
821  $wrap->metaDirty = false;
822  $wrap->dataDirty = false;
823  $wrap->forcePersist = false;
824  $expires = time() + $wrap->lifetime + 100;
825  $wrap->expires = $expires;
826  $backend->renew();
827  $this->assertFalse( $this->onSessionMetadataCalled );
828  $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
829  $this->assertEquals( $expires, $wrap->expires );
830  }
831 
832  public function onSessionMetadata( SessionBackend $backend, array &$metadata, array $requests ) {
833  $this->onSessionMetadataCalled = true;
834  $metadata['???'] = '!!!';
835  }
836 
837  public function testTakeOverGlobalSession() {
840  }
841  if ( !PHPSessionHandler::isEnabled() ) {
842  $rProp = new \ReflectionProperty( PHPSessionHandler::class, 'instance' );
843  $rProp->setAccessible( true );
844  $handler = TestingAccessWrapper::newFromObject( $rProp->getValue() );
845  $resetHandler = new \Wikimedia\ScopedCallback( function () use ( $handler ) {
846  session_write_close();
847  $handler->enable = false;
848  } );
849  $handler->enable = true;
850  }
851 
852  $backend = $this->getBackend( static::getTestSysop()->getUser() );
853  TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = true;
854 
855  $resetSingleton = TestUtils::setSessionManagerSingleton( $this->manager );
856 
857  $manager = TestingAccessWrapper::newFromObject( $this->manager );
858  $request = \RequestContext::getMain()->getRequest();
859  $manager->globalSession = $backend->getSession( $request );
860  $manager->globalSessionRequest = $request;
861 
862  session_id( '' );
863  TestingAccessWrapper::newFromObject( $backend )->checkPHPSession();
864  $this->assertSame( $backend->getId(), session_id() );
865  session_write_close();
866 
867  $backend2 = $this->getBackend(
868  User::newFromName( 'UTSysop' ), 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
869  );
870  TestingAccessWrapper::newFromObject( $backend2 )->usePhpSessionHandling = true;
871 
872  session_id( '' );
873  TestingAccessWrapper::newFromObject( $backend2 )->checkPHPSession();
874  $this->assertSame( '', session_id() );
875  }
876 
877  public function testResetIdOfGlobalSession() {
880  }
881  if ( !PHPSessionHandler::isEnabled() ) {
882  $rProp = new \ReflectionProperty( PHPSessionHandler::class, 'instance' );
883  $rProp->setAccessible( true );
884  $handler = TestingAccessWrapper::newFromObject( $rProp->getValue() );
885  $resetHandler = new \Wikimedia\ScopedCallback( function () use ( $handler ) {
886  session_write_close();
887  $handler->enable = false;
888  } );
889  $handler->enable = true;
890  }
891 
892  $backend = $this->getBackend( User::newFromName( 'UTSysop' ) );
893  TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = true;
894 
895  $resetSingleton = TestUtils::setSessionManagerSingleton( $this->manager );
896 
897  $manager = TestingAccessWrapper::newFromObject( $this->manager );
898  $request = \RequestContext::getMain()->getRequest();
899  $manager->globalSession = $backend->getSession( $request );
900  $manager->globalSessionRequest = $request;
901 
902  session_id( self::SESSIONID );
903  \Wikimedia\quietCall( 'session_start' );
904  $_SESSION['foo'] = __METHOD__;
905  $backend->resetId();
906  $this->assertNotEquals( self::SESSIONID, $backend->getId() );
907  $this->assertSame( $backend->getId(), session_id() );
908  $this->assertArrayHasKey( 'foo', $_SESSION );
909  $this->assertSame( __METHOD__, $_SESSION['foo'] );
910  session_write_close();
911  }
912 
913  public function testUnpersistOfGlobalSession() {
916  }
917  if ( !PHPSessionHandler::isEnabled() ) {
918  $rProp = new \ReflectionProperty( PHPSessionHandler::class, 'instance' );
919  $rProp->setAccessible( true );
920  $handler = TestingAccessWrapper::newFromObject( $rProp->getValue() );
921  $resetHandler = new \Wikimedia\ScopedCallback( function () use ( $handler ) {
922  session_write_close();
923  $handler->enable = false;
924  } );
925  $handler->enable = true;
926  }
927 
928  $backend = $this->getBackend( User::newFromName( 'UTSysop' ) );
929  $wrap = TestingAccessWrapper::newFromObject( $backend );
930  $wrap->usePhpSessionHandling = true;
931  $wrap->persist = true;
932 
933  $resetSingleton = TestUtils::setSessionManagerSingleton( $this->manager );
934 
935  $manager = TestingAccessWrapper::newFromObject( $this->manager );
936  $request = \RequestContext::getMain()->getRequest();
937  $manager->globalSession = $backend->getSession( $request );
938  $manager->globalSessionRequest = $request;
939 
940  session_id( self::SESSIONID . 'x' );
941  \Wikimedia\quietCall( 'session_start' );
942  $backend->unpersist();
943  $this->assertSame( self::SESSIONID . 'x', session_id() );
944  session_write_close();
945 
946  session_id( self::SESSIONID );
947  $wrap->persist = true;
948  $backend->unpersist();
949  $this->assertSame( '', session_id() );
950  }
951 
952  public function testGetAllowedUserRights() {
953  $this->provider = $this->getMockBuilder( \DummySessionProvider::class )
954  ->setMethods( [ 'getAllowedUserRights' ] )
955  ->getMock();
956  $this->provider->expects( $this->any() )->method( 'getAllowedUserRights' )
957  ->will( $this->returnValue( [ 'foo', 'bar' ] ) );
958 
959  $backend = $this->getBackend();
960  $this->assertSame( [ 'foo', 'bar' ], $backend->getAllowedUserRights() );
961  }
962 
963 }
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
const MIN_PRIORITY
Minimum allowed priority.
Definition: SessionInfo.php:36
This is the actual workhorse for Session.
$data
Utility to generate mapping file used in mw.Title (phpCharToUpper.json)
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
static newFromUser(User $user, $verified=false)
Create an instance from an existing User object.
Definition: UserInfo.php:117
static setSessionManagerSingleton(SessionManager $manager=null)
Override the singleton for unit testing.
Definition: TestUtils.php:18
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition: hooks.txt:175
mergeMwGlobalArrayValue( $name, $values)
Merges the given values into a MW global array variable.
save( $closing=false)
Save the session.
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:48
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not it can be in the form of< username >< more info > e g for bot passwords intended to be added to log contexts Fields it might only if the login was with a bot password it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
Definition: hooks.txt:780
BagOStuff with utility functions for MediaWiki\\Session\\* testing.
static getMain()
Get the RequestContext object associated with the main request.
Interface for configuration instances.
Definition: Config.php:28
static install(SessionManager $manager)
Install a session handler for the current web request.
getBackend(User $user=null, $id=null)
Returns a non-persistent backend that thinks it has at least one session active.
isPersistent()
Indicate whether this session is persisted across requests.
Session Database MediaWiki\Session\SessionBackend.
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
Allows to change the fields on the form that will be generated are created Can be used to omit specific feeds from being outputted You must not use this hook to add use OutputPage::addFeedLink() instead. & $feedLinks hooks can tweak the array to change how login etc forms should look $requests
Definition: hooks.txt:273
static isInstalled()
Test whether the handler is installed.
static singleton()
Get the global SessionManager.
onSessionMetadata(SessionBackend $backend, array &$metadata, array $requests)
static isEnabled()
Test whether the handler is installed and enabled.
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
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
static newFromName( $name, $verified=false)
Create an instance for a logged-in user by name.
Definition: UserInfo.php:103
Value object holding the session ID in a manner that can be globally updated.
Definition: SessionId.php:38
This serves as the entry point to the MediaWiki session handling system.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:587
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2633
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1473
Value object returned by SessionProvider.
Definition: SessionInfo.php:34