From a57252a1f521344e53e8776c34f925f522f512f3 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Thu, 4 May 2017 18:00:28 +0200 Subject: [PATCH] Allow callback functions for creating jobs $wgJobClasses can now specify a factory function for creating a job, instead of a class to be instantiated directly. This makes it possible to inject services in a job constructor, and register a factory function that calls the constructor with default services. This follows Ieb85493a7765 and Ia2107dc5af78, which introduced factory functions for API modules and special pages. Change-Id: I0461e59da2a8fa6681e3b1fcdfc38bfed7f3ac32 --- RELEASE-NOTES-1.30 | 5 +++ docs/extension.schema.v1.json | 2 +- docs/extension.schema.v2.json | 2 +- includes/DefaultSettings.php | 6 ++-- includes/jobqueue/Job.php | 20 ++++++++--- tests/phpunit/includes/jobqueue/JobTest.php | 37 +++++++++++++++++++++ 6 files changed, 63 insertions(+), 9 deletions(-) diff --git a/RELEASE-NOTES-1.30 b/RELEASE-NOTES-1.30 index cdf8ba4421..5370c6d2ca 100644 --- a/RELEASE-NOTES-1.30 +++ b/RELEASE-NOTES-1.30 @@ -12,6 +12,11 @@ production. case. * $wgShellLocale now affects LC_ALL rather than only LC_CTYPE. See documentation of $wgShellLocale for details. +* $wgJobClasses may now specify callback functions + as an alternative to plain class names. + This is intended for extensions that want control + over the instantiation of their jobs, + to allow for proper dependency injection. === New features in 1.30 === * … diff --git a/docs/extension.schema.v1.json b/docs/extension.schema.v1.json index 3d6eda9ad9..f241cf59e4 100644 --- a/docs/extension.schema.v1.json +++ b/docs/extension.schema.v1.json @@ -576,7 +576,7 @@ }, "JobClasses": { "type": "object", - "description": "Job types this extension implements (mapping of job type to class name)" + "description": "Job types this extension implements (mapping of job type to class name or factory function)" }, "LogTypes": { "type": "array", diff --git a/docs/extension.schema.v2.json b/docs/extension.schema.v2.json index a2fdf65aad..137dd519ca 100644 --- a/docs/extension.schema.v2.json +++ b/docs/extension.schema.v2.json @@ -586,7 +586,7 @@ }, "JobClasses": { "type": "object", - "description": "Job types this extension implements (mapping of job type to class name)" + "description": "Job types this extension implements (mapping of job type to class name or factory function)" }, "LogTypes": { "type": "array", diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 7c18fcc594..a6bc7e54bd 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -7332,8 +7332,10 @@ $wgServiceWiringFiles = [ ]; /** - * Maps jobs to their handling classes; extensions - * can add to this to provide custom jobs + * Maps jobs to their handlers; extensions + * can add to this to provide custom jobs. + * A job handler should either be a class name to be instantiated, + * or (since 1.30) a callback to use for creating the job object. */ $wgJobClasses = [ 'refreshLinks' => 'RefreshLinksJob', diff --git a/includes/jobqueue/Job.php b/includes/jobqueue/Job.php index f814ceeb1b..703e48564b 100644 --- a/includes/jobqueue/Job.php +++ b/includes/jobqueue/Job.php @@ -69,12 +69,22 @@ abstract class Job implements IJobSpecification { global $wgJobClasses; if ( isset( $wgJobClasses[$command] ) ) { - $class = $wgJobClasses[$command]; - - $job = new $class( $title, $params ); - $job->command = $command; + $handler = $wgJobClasses[$command]; + + if ( is_callable( $handler ) ) { + $job = call_user_func( $handler, $title, $params ); + } elseif ( class_exists( $handler ) ) { + $job = new $handler( $title, $params ); + } else { + $job = null; + } - return $job; + if ( $job instanceof Job ) { + $job->command = $command; + return $job; + } else { + throw new InvalidArgumentException( "Cannot instantiate job '$command': bad spec!" ); + } } throw new InvalidArgumentException( "Invalid job command '{$command}'" ); diff --git a/tests/phpunit/includes/jobqueue/JobTest.php b/tests/phpunit/includes/jobqueue/JobTest.php index 1deb7aa35d..00c47c8ae8 100644 --- a/tests/phpunit/includes/jobqueue/JobTest.php +++ b/tests/phpunit/includes/jobqueue/JobTest.php @@ -91,4 +91,41 @@ class JobTest extends MediaWikiTestCase { return $mock; } + /** + * @dataProvider provideTestJobFactory + * + * @param mixed $handler + * + * @covers Job::factory + */ + public function testJobFactory( $handler ) { + $this->mergeMWGlobalArrayValue( 'wgJobClasses', [ 'testdummy' => $handler ] ); + + $job = Job::factory( 'testdummy', Title::newMainPage(), [] ); + $this->assertInstanceOf( NullJob::class, $job ); + + $job2 = Job::factory( 'testdummy', Title::newMainPage(), [] ); + $this->assertInstanceOf( NullJob::class, $job2 ); + $this->assertNotSame( $job, $job2, 'should not reuse instance' ); + } + + public function provideTestJobFactory() { + return [ + 'class name' => [ 'NullJob' ], + 'closure' => [ function( Title $title, array $params ) { + return new NullJob( $title, $params ); + } ], + 'function' => [ [ $this, 'newNullJob' ] ], + 'static function' => [ self::class . '::staticNullJob' ] + ]; + } + + public function newNullJob( Title $title, array $params ) { + return new NullJob( $title, $params ); + } + + public static function staticNullJob( Title $title, array $params ) { + return new NullJob( $title, $params ); + } + } -- 2.20.1