X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;ds=sidebyside;f=includes%2Fresourceloader%2FResourceLoader.php;h=fe9ba74a4f1d55e58f4348c7e32e46247844b081;hb=b495d51657d5b488c23f82650615443215339232;hp=8f5d083ab5e2d3f865b7f2ec352a8f8ba9249dd9;hpb=7f6bd0c6fec48474f2225bd26979815c0229813d;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/resourceloader/ResourceLoader.php b/includes/resourceloader/ResourceLoader.php index 8f5d083ab5..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 ); } } @@ -1223,7 +1224,7 @@ MESSAGE; if ( self::inDebugMode() ) { $scripts = new XmlJsCode( "function ( $, jQuery, require, module ) {\n{$scripts->value}\n}" ); } else { - $scripts = new XmlJsCode( 'function($,jQuery,require,module){'. $scripts->value . '}' ); + $scripts = new XmlJsCode( 'function($,jQuery,require,module){' . $scripts->value . '}' ); } } elseif ( !is_string( $scripts ) && !is_array( $scripts ) ) { throw new MWException( 'Invalid scripts error. Array of URLs or string of code expected.' ); @@ -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: @@ -1317,31 +1347,6 @@ MESSAGE; ); } - /** - * Returns JS code which calls the script given by $script. The script will - * be called with local variables name, version, dependencies and group, - * which will have values corresponding to $name, $version, $dependencies - * and $group as supplied. - * - * @param string $name Module name - * @param string $version Module version hash - * @param array $dependencies List of module names on which this module depends - * @param string $group Group which the module is in. - * @param string $source Source of the module, or 'local' if not foreign. - * @param string $script JavaScript code - * @return string JavaScript code - */ - public static function makeCustomLoaderScript( $name, $version, $dependencies, - $group, $source, $script - ) { - $script = str_replace( "\n", "\n\t", trim( $script ) ); - return Xml::encodeJsCall( - "( function ( name, version, dependencies, group, source ) {\n\t$script\n} )", - [ $name, $version, $dependencies, $group, $source ], - self::inDebugMode() - ); - } - private static function isEmptyObject( stdClass $obj ) { foreach ( $obj as $key => $value ) { return false; @@ -1378,69 +1383,56 @@ MESSAGE; /** * Returns JS code which calls mw.loader.register with the given - * parameters. Has three calling conventions: - * - * - ResourceLoader::makeLoaderRegisterScript( $name, $version, - * $dependencies, $group, $source, $skip - * ): - * Register a single module. + * parameter. * - * - ResourceLoader::makeLoaderRegisterScript( [ $name1, $name2 ] ): - * Register modules with the given names. + * @par Example + * @code * - * - 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() + ); } /** @@ -1453,24 +1445,19 @@ MESSAGE; * - ResourceLoader::makeLoaderSourcesScript( [ $id1 => $loadUrl, $id2 => $loadUrl, ... ] ); * Register sources with the given IDs and properties. * - * @param string $id Source ID + * @param string|array $sources Source ID * @param string|null $loadUrl load.php url * @return string JavaScript code */ - public static function makeLoaderSourcesScript( $id, $loadUrl = null ) { - if ( is_array( $id ) ) { - return Xml::encodeJsCall( - 'mw.loader.addSource', - [ $id ], - self::inDebugMode() - ); - } else { - return Xml::encodeJsCall( - 'mw.loader.addSource', - [ $id, $loadUrl ], - self::inDebugMode() - ); + public static function makeLoaderSourcesScript( $sources, $loadUrl = null ) { + if ( !is_array( $sources ) ) { + $sources = [ $sources => $loadUrl ]; } + return Xml::encodeJsCall( + 'mw.loader.addSource', + [ $sources ], + self::inDebugMode() + ); } /** @@ -1496,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 ) . '}' . ']);'; } @@ -1597,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;