X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2Fresourceloader%2FResourceLoader.php;h=fe9ba74a4f1d55e58f4348c7e32e46247844b081;hp=dde72b2f36cf22d70e3123c68edff58d793608c0;hb=b495d51657d5b488c23f82650615443215339232;hpb=42310ac502e3570d565288efd163d087113853e9 diff --git a/includes/resourceloader/ResourceLoader.php b/includes/resourceloader/ResourceLoader.php index dde72b2f36..fe9ba74a4f 100644 --- a/includes/resourceloader/ResourceLoader.php +++ b/includes/resourceloader/ResourceLoader.php @@ -37,7 +37,7 @@ use Wikimedia\WrappedString; */ class ResourceLoader implements LoggerAwareInterface { /** @var int */ - protected static $filterCacheVersion = 8; + const CACHE_VERSION = 8; /** @var bool */ protected static $debugMode = null; @@ -199,7 +199,8 @@ class ResourceLoader implements LoggerAwareInterface { 'resourceloader', 'filter', $filter, - self::$filterCacheVersion, md5( $data ) + self::CACHE_VERSION, + md5( $data ) ); $result = $cache->get( $key ); @@ -1162,8 +1163,8 @@ MESSAGE; } } else { if ( $states ) { - // Keep default escaping of slashes (e.g. "") for ResourceLoaderClientHtml. - $this->errors[] = 'Problematic modules: ' . json_encode( $states, JSON_PRETTY_PRINT ); + $this->errors[] = 'Problematic modules: ' + . self::encodeJsonForScript( $states ); } } @@ -1292,6 +1293,35 @@ MESSAGE; return $out; } + /** + * Wrapper around json_encode that avoids needless escapes, + * and pretty-prints in debug mode. + * + * @internal + * @since 1.32 + * @param bool|string|array $data + * @return string JSON + */ + public static function encodeJsonForScript( $data ) { + // Keep output as small as possible by disabling needless escape modes + // that PHP uses by default. + // However, while most module scripts are only served on HTTP responses + // for JavaScript, some modules can also be embedded in the HTML as inline + // scripts. This, and the fact that we sometimes need to export strings + // containing user-generated content and labels that may genuinely contain + // a sequences like "", we need to encode either '/' or '<'. + // By default PHP escapes '/'. Let's escape '<' instead which is less common + // and allows URLs to mostly remain readable. + $jsonFlags = JSON_UNESCAPED_SLASHES | + JSON_UNESCAPED_UNICODE | + JSON_HEX_TAG | + JSON_HEX_AMP; + if ( self::inDebugMode() ) { + $jsonFlags |= JSON_PRETTY_PRINT; + } + return json_encode( $data, $jsonFlags ); + } + /** * Returns a JS call to mw.loader.state, which sets the state of one * ore more modules to a given value. Has two calling conventions: @@ -1353,69 +1383,56 @@ MESSAGE; /** * Returns JS code which calls mw.loader.register with the given - * parameters. Has three calling conventions: + * parameter. * - * - ResourceLoader::makeLoaderRegisterScript( $name, $version, - * $dependencies, $group, $source, $skip - * ): - * Register a single module. + * @par Example + * @code * - * - ResourceLoader::makeLoaderRegisterScript( [ $name1, $name2 ] ): - * Register modules with the given names. - * - * - ResourceLoader::makeLoaderRegisterScript( [ + * ResourceLoader::makeLoaderRegisterScript( [ * [ $name1, $version1, $dependencies1, $group1, $source1, $skip1 ], * [ $name2, $version2, $dependencies1, $group2, $source2, $skip2 ], * ... * ] ): - * Registers modules with the given names and parameters. + * @endcode * - * @param string $name Module name - * @param string|null $version Module version hash - * @param array|null $dependencies List of module names on which this module depends - * @param string|null $group Group which the module is in - * @param string|null $source Source of the module, or 'local' if not foreign - * @param string|null $skip Script body of the skip function + * @internal + * @since 1.32 + * @param array $modules Array of module registration arrays, each containing + * - string: module name + * - string: module version + * - array|null: List of dependencies (optional) + * - string|null: Module group (optional) + * - string|null: Name of foreign module source, or 'local' (optional) + * - string|null: Script body of a skip function (optional) * @return string JavaScript code */ - public static function makeLoaderRegisterScript( $name, $version = null, - $dependencies = null, $group = null, $source = null, $skip = null - ) { - if ( is_array( $name ) ) { + public static function makeLoaderRegisterScript( array $modules ) { + // Optimisation: Transform dependency names into indexes when possible + // to produce smaller output. They are expanded by mw.loader.register on + // the other end using resolveIndexedDependencies(). + $index = []; + foreach ( $modules as $i => &$module ) { // Build module name index - $index = []; - foreach ( $name as $i => &$module ) { - $index[$module[0]] = $i; - } - - // Transform dependency names into indexes when possible, they will be resolved by - // mw.loader.register on the other end - foreach ( $name as &$module ) { - if ( isset( $module[2] ) ) { - foreach ( $module[2] as &$dependency ) { - if ( isset( $index[$dependency] ) ) { - $dependency = $index[$dependency]; - } + $index[$module[0]] = $i; + } + foreach ( $modules as &$module ) { + if ( isset( $module[2] ) ) { + foreach ( $module[2] as &$dependency ) { + if ( isset( $index[$dependency] ) ) { + // Replace module name in dependency list with index + $dependency = $index[$dependency]; } } } + } - array_walk( $name, [ 'self', 'trimArray' ] ); + array_walk( $modules, [ 'self', 'trimArray' ] ); - return Xml::encodeJsCall( - 'mw.loader.register', - [ $name ], - self::inDebugMode() - ); - } else { - $registration = [ $name, $version, $dependencies, $group, $source, $skip ]; - self::trimArray( $registration ); - return Xml::encodeJsCall( - 'mw.loader.register', - $registration, - self::inDebugMode() - ); - } + return Xml::encodeJsCall( + 'mw.loader.register', + [ $modules ], + self::inDebugMode() + ); } /** @@ -1466,7 +1483,7 @@ MESSAGE; public static function makeInlineCodeWithModule( $modules, $script ) { // Adds an array to lazy-created RLQ return '(window.RLQ=window.RLQ||[]).push([' - . json_encode( $modules ) . ',' + . self::encodeJsonForScript( $modules ) . ',' . 'function(){' . trim( $script ) . '}' . ']);'; } @@ -1567,6 +1584,9 @@ MESSAGE; * Global state and $wgRequest are evil, but we're using it right * now and sometimes we need to be able to force ResourceLoader to * re-evaluate the context because it has changed (e.g. in the test suite). + * + * @internal For use by unit tests + * @codeCoverageIgnore */ public static function clearCache() { self::$debugMode = null;