*/
protected $forceRecompile = false;
+ /**
+ * @var int Compilation flags passed to LightnCandy
+ */
+ // Do not add more flags here without discussion.
+ // If you do add more flags, be sure to update unit tests as well.
+ protected $compileFlags = LightnCandy::FLAG_ERROR_EXCEPTION;
+
/**
* @param string $templateDir
* @param bool $forceRecompile
$this->forceRecompile = $forceRecompile;
}
+ /**
+ * Enable/disable the use of recursive partials.
+ * @param bool $enable
+ */
+ public function enableRecursivePartials( $enable ) {
+ if ( $enable ) {
+ $this->compileFlags = $this->compileFlags | LightnCandy::FLAG_RUNTIMEPARTIAL;
+ } else {
+ $this->compileFlags = $this->compileFlags & ~LightnCandy::FLAG_RUNTIMEPARTIAL;
+ }
+ }
+
/**
* Constructs the location of the the source Mustache template
* @param string $templateName The name of the template
* @throws RuntimeException
*/
protected function getTemplate( $templateName ) {
+ $templateKey = $templateName . '|' . $this->compileFlags;
+
// If a renderer has already been defined for this template, reuse it
- if ( isset( $this->renderers[$templateName] ) &&
- is_callable( $this->renderers[$templateName] )
+ if ( isset( $this->renderers[$templateKey] ) &&
+ is_callable( $this->renderers[$templateKey] )
) {
- return $this->renderers[$templateName];
+ return $this->renderers[$templateKey];
}
$filename = $this->getTemplateFilename( $templateName );
$fileContents = file_get_contents( $filename );
// Generate a quick hash for cache invalidation
- $fastHash = md5( $fileContents );
+ $fastHash = md5( $this->compileFlags . '|' . $fileContents );
// Fetch a secret key for building a keyed hash of the PHP code
$config = MediaWikiServices::getInstance()->getMainConfig();
$key = $cache->makeKey( 'template', $templateName, $fastHash );
$code = $this->forceRecompile ? null : $cache->get( $key );
- if ( !$code ) {
- $code = $this->compileForEval( $fileContents, $filename );
-
- // Prefix the cached code with a keyed hash (64 hex chars) as an integrity check
- $cache->set( $key, hash_hmac( 'sha256', $code, $secretKey ) . $code );
- } else {
+ if ( $code ) {
// Verify the integrity of the cached PHP code
$keyedHash = substr( $code, 0, 64 );
$code = substr( $code, 64 );
if ( $keyedHash !== hash_hmac( 'sha256', $code, $secretKey ) ) {
- // Generate a notice if integrity check fails
- trigger_error( "Template failed integrity check: {$filename}" );
+ // If the integrity check fails, don't use the cached code
+ // We'll update the invalid cache below
+ $code = null;
}
}
+ if ( !$code ) {
+ $code = $this->compileForEval( $fileContents, $filename );
+
+ // Prefix the cached code with a keyed hash (64 hex chars) as an integrity check
+ $cache->set( $key, hash_hmac( 'sha256', $code, $secretKey ) . $code );
+ }
// If there is no secret key available, don't use cache
} else {
$code = $this->compileForEval( $fileContents, $filename );
if ( !is_callable( $renderer ) ) {
throw new RuntimeException( "Requested template, {$templateName}, is not callable" );
}
- $this->renderers[$templateName] = $renderer;
+ $this->renderers[$templateKey] = $renderer;
return $renderer;
}
return LightnCandy::compile(
$code,
[
- // Do not add more flags here without discussion.
- // If you do add more flags, be sure to update unit tests as well.
- 'flags' => LightnCandy::FLAG_ERROR_EXCEPTION,
+ 'flags' => $this->compileFlags,
'basedir' => $this->templateDir,
'fileext' => '.mustache',
]