From: Marius Hoch Date: Sun, 7 Jul 2013 22:51:15 +0000 (+0200) Subject: resourceloader: Optimize module registry sent in the startup module X-Git-Tag: 1.31.0-rc.0~16001^2 X-Git-Url: http://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=commitdiff_plain;h=df8176e8a56ecc380da41f037435ff2a066690ec resourceloader: Optimize module registry sent in the startup module The optimization basically works like this: * Given module A with the dependencies B and C and module B with the dependency C. * Don't tell the client that A depends on C, as that's already included in module B. This way we can reduce the amount of data for module registration sent to the client. The code here isn't polished yet, but it works and should be good enough to demonstrate my idea and implementation. Change-Id: I7732a3b1d879c5eef059e136a5241d6d48046872 --- diff --git a/includes/resourceloader/ResourceLoaderStartUpModule.php b/includes/resourceloader/ResourceLoaderStartUpModule.php index 005081ce8b..0afac059ac 100644 --- a/includes/resourceloader/ResourceLoaderStartUpModule.php +++ b/includes/resourceloader/ResourceLoaderStartUpModule.php @@ -117,6 +117,75 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { return $this->configVars[$hash]; } + /** + * Recursively get all explicit and implicit dependencies for to the given module. + * + * @param array $registryData + * @param string $moduleName + * @return array + */ + protected static function getImplicitDependencies( Array $registryData, $moduleName ) { + static $dependencyCache = array(); + + // The list of implicit dependencies won't be altered, so we can + // cache them without having to worry. + if ( !isset( $dependencyCache[$moduleName] ) ) { + + if ( !isset( $registryData[$moduleName] ) ) { + // Dependencies may not exist + $dependencyCache[$moduleName] = array(); + } else { + $data = $registryData[$moduleName]; + $dependencyCache[$moduleName] = $data['dependencies']; + + foreach ( $data['dependencies'] as $dependency ) { + // Recursively get the dependencies of the dependencies + $dependencyCache[$moduleName] = array_merge( + $dependencyCache[$moduleName], + self::getImplicitDependencies( $registryData, $dependency ) + ); + } + } + } + + return $dependencyCache[$moduleName]; + } + + /** + * Optimize the dependency tree in $this->modules and return it. + * + * The optimization basically works like this: + * Given we have module A with the dependencies B and C + * and module B with the dependency C. + * Now we don't have to tell the client to explicitly fetch module + * C as that's already included in module B. + * + * This way we can reasonably reduce the amout of module registration + * data send to the client. + * + * @param Array &$registryData Modules keyed by name with properties: + * - string 'version' + * - array 'dependencies' + * - string|null 'group' + * - string 'source' + * - string|false 'loader' + */ + public static function compileUnresolvedDependencies( Array &$registryData ) { + foreach ( $registryData as $name => &$data ) { + if ( $data['loader'] !== false ) { + continue; + } + $dependencies = $data['dependencies']; + foreach ( $data['dependencies'] as $dependency ) { + $implicitDependencies = self::getImplicitDependencies( $registryData, $dependency ); + $dependencies = array_diff( $dependencies, $implicitDependencies ); + } + // Rebuild keys + $data['dependencies'] = array_values( $dependencies ); + } + } + + /** * Get registration code for all modules. * @@ -157,6 +226,8 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { ); } + self::compileUnresolvedDependencies( $registryData ); + // Register sources $out .= ResourceLoader::makeLoaderSourcesScript( $resourceLoader->getSources() ); diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php index 5b51ef8937..c4412de6a7 100644 --- a/tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php +++ b/tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php @@ -151,6 +151,7 @@ mw.loader.addSource( { 'test.x.foo', 'test.x.bar', 'test.x.util', + 'test.x.unknown', ), ) ), 'test.group.foo.1' => new ResourceLoaderTestModule( array( @@ -211,7 +212,6 @@ mw.loader.addSource( { "test.x.bar", "1388534400", [ - "test.x.core", "test.x.util" ] ], @@ -221,7 +221,7 @@ mw.loader.addSource( { [ "test.x.foo", "test.x.bar", - "test.x.util" + "test.x.unknown" ] ], [ @@ -256,7 +256,10 @@ mw.loader.addSource( { /** * @dataProvider provideGetModuleRegistrations + * @covers ResourceLoaderStartupModule::optimizeDependencies * @covers ResourceLoaderStartUpModule::getModuleRegistrations + * @covers ResourceLoader::makeLoaderSourcesScript + * @covers ResourceLoader::makeLoaderRegisterScript */ public function testGetModuleRegistrations( $case ) { if ( isset( $case['sources'] ) ) {