1<?php 2 3namespace MediaWiki\HookContainer { 4 5 use ExtensionRegistry; 6 use MediaWiki\MediaWikiServices; 7 use Wikimedia\ScopedCallback; 8 9 class HookContainerIntegrationTest extends \MediaWikiIntegrationTestCase { 10 11 /** 12 * @covers \MediaWiki\HookContainer\HookContainer::run 13 */ 14 public function testHookRunsWhenExtensionRegistered() { 15 $hookContainer = MediaWikiServices::getInstance()->getHookContainer(); 16 $extensionRegistry = ExtensionRegistry::getInstance(); 17 $numHandlersExecuted = 0; 18 $handlers = [ 'FooHook' => [ [ 19 'handler' => [ 20 'class' => 'FooExtension\\FooExtensionHooks', 21 'name' => 'FooExtension-FooHandler', 22 ] ] ] 23 ]; 24 $reset = $extensionRegistry->setAttributeForTest( 'Hooks', $handlers ); 25 $this->assertSame( 0, $numHandlersExecuted ); 26 $hookContainer->run( 'FooHook', [ &$numHandlersExecuted ] ); 27 $this->assertSame( 1, $numHandlersExecuted ); 28 ScopedCallback::consume( $reset ); 29 } 30 31 /** 32 * @covers \MediaWiki\HookContainer\HookContainer::run 33 * @covers \MediaWiki\HookContainer\HookContainer::scopedRegister 34 */ 35 public function testPreviouslyRegisteredHooksAreReAppliedAfterScopedRegisterRemovesThem() { 36 $hookContainer = MediaWikiServices::getInstance()->getHookContainer(); 37 38 // Some handlers for FooHook have been previously set 39 $reset = $hookContainer->register( 'FooHook', static function () { 40 return true; 41 } ); 42 $reset1 = $hookContainer->register( 'FooHook', static function () { 43 return true; 44 } ); 45 $handlersBeforeScopedRegister = $hookContainer->getLegacyHandlers( 'FooHook' ); 46 $this->assertCount( 2, $handlersBeforeScopedRegister ); 47 48 // Wipe out the 2 existing handlers and add a new scoped handler 49 $reset2 = $hookContainer->scopedRegister( 'FooHook', static function () { 50 return true; 51 }, true ); 52 $handlersAfterScopedRegister = $hookContainer->getLegacyHandlers( 'FooHook' ); 53 $this->assertCount( 1, $handlersAfterScopedRegister ); 54 55 ScopedCallback::consume( $reset2 ); 56 57 // Teardown causes the original handlers to be re-applied 58 $this->mediaWikiTearDown(); 59 60 $handlersAfterTearDown = $hookContainer->getLegacyHandlers( 'FooHook' ); 61 $this->assertCount( 2, $handlersAfterTearDown ); 62 } 63 64 /** 65 * @covers \MediaWiki\HookContainer\HookContainer::run 66 * @covers \MediaWiki\HookContainer\HookContainer::scopedRegister 67 */ 68 public function testHookRunsWithMultipleMixedHandlerTypes() { 69 $hookContainer = MediaWikiServices::getInstance()->getHookContainer(); 70 $numHandlersExecuted = 0; 71 $reset = $hookContainer->scopedRegister( 'FooHook', static function ( &$numHandlersRun ) { 72 $numHandlersRun++; 73 }, false ); 74 $reset2 = $hookContainer->scopedRegister( 'FooHook', static function ( &$numHandlersRun ) { 75 $numHandlersRun++; 76 }, false ); 77 $handlerThree = [ 78 'FooHook' => [ 79 [ 'handler' => [ 80 'class' => 'FooExtension\\FooExtensionHooks', 81 'name' => 'FooExtension-FooHandler', 82 ] 83 ] 84 ] 85 ]; 86 $reset3 = ExtensionRegistry::getInstance()->setAttributeForTest( 'Hooks', $handlerThree ); 87 $hookContainer->run( 'FooHook', [ &$numHandlersExecuted ] ); 88 $this->assertEquals( 3, $numHandlersExecuted ); 89 ScopedCallback::consume( $reset ); 90 ScopedCallback::consume( $reset2 ); 91 ScopedCallback::consume( $reset3 ); 92 } 93 94 /** 95 * @covers \MediaWiki\HookContainer\HookContainer 96 */ 97 public function testValidServiceInjection() { 98 $handler = [ 99 'handler' => [ 100 'name' => 'FooExtension-Mash', 101 'class' => 'FooExtension\\ServiceHooks', 102 'services' => [ 'ReadOnlyMode' ] 103 ], 104 'extensionPath' => '/path/to/extension.json' 105 ]; 106 $hooks = [ 'Mash' => [ $handler ] ]; 107 $hookContainer = MediaWikiServices::getInstance()->getHookContainer(); 108 $reset = ExtensionRegistry::getInstance()->setAttributeForTest( 'Hooks', $hooks ); 109 $arg = 0; 110 $ret = $hookContainer->run( 'Mash', [ &$arg ] ); 111 $this->assertTrue( $ret ); 112 $this->assertSame( 1, $arg ); 113 } 114 115 /** 116 * @covers \MediaWiki\HookContainer\HookContainer 117 */ 118 public function testInvalidServiceInjection() { 119 $handler = [ 120 'handler' => [ 121 'name' => 'FooExtension-Mash', 122 'class' => 'FooExtension\\ServiceHooks', 123 'services' => [ 'ReadOnlyMode' ] 124 ], 125 'extensionPath' => '/path/to/extension.json' 126 ]; 127 $hooks = [ 'Mash' => [ $handler ] ]; 128 $hookContainer = MediaWikiServices::getInstance()->getHookContainer(); 129 $reset = ExtensionRegistry::getInstance()->setAttributeForTest( 'Hooks', $hooks ); 130 $this->expectException( \UnexpectedValueException::class ); 131 $arg = 0; 132 $hookContainer->run( 'Mash', [ &$arg ], [ 'noServices' => true ] ); 133 } 134 } 135} 136 137namespace FooExtension { 138 139 class FooExtensionHooks { 140 141 public function onFooHook( &$numHandlersRun ) { 142 $numHandlersRun++; 143 } 144 } 145 146 class ServiceHooks { 147 public function __construct( \ReadOnlyMode $readOnlyMode ) { 148 } 149 150 public function onMash( &$arg ) { 151 $arg++; 152 return true; 153 } 154 } 155 156} 157