$options[$member] = $this->{$member};
}
+ $packageFiles = $this->expandPackageFiles( $context );
+ if ( $packageFiles ) {
+ // Extract the minimum needed:
+ // - The 'main' pointer (included as-is).
+ // - The 'files' array, simplied to only which files exist (the keys of
+ // this array), and something that represents their non-file content.
+ // For packaged files that reflect files directly from disk, the
+ // 'getFileHashes' method tracks this already.
+ // It is important that the keys of the 'files' array are preserved,
+ // as they affect the module output.
+ $packageFiles['files'] = array_map( function ( $fileInfo ) {
+ return $fileInfo['definitionSummary'] ?? ( $fileInfo['content'] ?? null );
+ }, $packageFiles['files'] );
+ }
+
$summary[] = [
'options' => $options,
- 'packageFiles' => $this->expandPackageFiles( $context ),
+ 'packageFiles' => $packageFiles,
'fileHashes' => $this->getFileHashes( $context ),
'messageBlob' => $this->getMessageBlob( $context ),
];
}
/**
- * Expand the packageFiles definition into something that's (almost) the right format for
- * getPackageFiles() to return. This expands shorthands, resolves config vars and callbacks,
- * but does not expand file paths or read the actual contents of files. Those things are done
- * by getPackageFiles().
+ * Internal helper for use by getPackageFiles(), getFileHashes() and getDefinitionSummary().
+ *
+ * This expands the 'packageFiles' definition into something that's (almost) the right format
+ * for getPackageFiles() to return. It expands shorthands, resolves config vars, and handles
+ * summarising any non-file data for getVersionHash(). For file-based data, getFileHashes()
+ * handles it instead, which also ends up in getDefinitionSummary().
*
- * This is split up in this way so that getFileHashes() can get a list of file names, and
- * getDefinitionSummary() can get config vars and callback results in their expanded form.
+ * What it does not do is reading the actual contents of any specified files, nor invoking
+ * the computation callbacks. Those things are done by getPackageFiles() instead to improve
+ * backend performance by only doing this work when the module response is needed, and not
+ * when merely computing the version hash for StartupModule, or when checking
+ * If-None-Match headers for a HTTP 304 response.
*
* @param ResourceLoaderContext $context
* @return array|null
+ * @throws MWException If the 'packageFiles' definition is invalid.
*/
private function expandPackageFiles( ResourceLoaderContext $context ) {
$hash = $context->getHash();
}
}
+ // Perform expansions (except 'file' and 'callback'), creating one of these keys:
+ // - 'content': literal value.
+ // - 'filePath': content to be read from a file.
+ // - 'callback': content computed by a callable.
if ( isset( $fileInfo['content'] ) ) {
$expanded['content'] = $fileInfo['content'];
} elseif ( isset( $fileInfo['file'] ) ) {
$expanded['filePath'] = $fileInfo['file'];
} elseif ( isset( $fileInfo['callback'] ) ) {
- if ( is_callable( $fileInfo['callback'] ) ) {
- $expanded['content'] = $fileInfo['callback']( $context );
- } else {
+ if ( !is_callable( $fileInfo['callback'] ) ) {
$msg = __METHOD__ . ": invalid callback for package file \"{$fileInfo['name']}\"" .
" in module \"{$this->getName()}\"";
wfDebugLog( 'resourceloader', $msg );
throw new MWException( $msg );
}
+ if ( isset( $fileInfo['versionCallback'] ) ) {
+ if ( !is_callable( $fileInfo['versionCallback'] ) ) {
+ throw new MWException( __METHOD__ . ": invalid versionCallback for file" .
+ " \"{$fileInfo['name']}\" in module \"{$this->getName()}\"" );
+ }
+ $expanded['definitionSummary'] = ( $fileInfo['versionCallback'] )( $context );
+ // Don't invoke 'callback' here as it may be expensive (T223260).
+ $expanded['callback'] = $fileInfo['callback'];
+ } else {
+ $expanded['content'] = ( $fileInfo['callback'] )( $context );
+ }
} elseif ( isset( $fileInfo['config'] ) ) {
if ( $type !== 'data' ) {
$msg = __METHOD__ . ": invalid use of \"config\" for package file \"{$fileInfo['name']}\" " .
// Expand file contents
foreach ( $expandedPackageFiles['files'] as &$fileInfo ) {
+ // Turn any 'filePath' or 'callback' key into actual 'content',
+ // and remove the key after that.
if ( isset( $fileInfo['filePath'] ) ) {
$localPath = $this->getLocalPath( $fileInfo['filePath'] );
if ( !file_exists( $localPath ) ) {
}
$fileInfo['content'] = $content;
unset( $fileInfo['filePath'] );
+ } elseif ( isset( $fileInfo['callback'] ) ) {
+ $fileInfo['content'] = ( $fileInfo['callback'] )( $context );
+ unset( $fileInfo['callback'] );
}
+
+ // Not needed for client response, exists for getDefinitionSummary().
+ unset( $fileInfo['definitionSummary'] );
}
return $expandedPackageFiles;