Skip to content

Commit

Permalink
Optimizer (#23)
Browse files Browse the repository at this point in the history
* Start optimizer tool

* Provide more options for the rendering

* Fix facade option and add documentation comments

* Add unit tests for the optimizer

* Reset after cache tests

* Add unit tests

* Cover with unit tests

* Add missing files

* Add blank lines

* Rename option for consistency
  • Loading branch information
kylekatarnls authored Feb 4, 2018
1 parent 863476b commit c223468
Show file tree
Hide file tree
Showing 8 changed files with 534 additions and 1 deletion.
229 changes: 229 additions & 0 deletions src/Phug/Optimizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
<?php

namespace Phug;

use Phug\Compiler\Locator\FileLocator;

class Optimizer
{
/**
* Facade for rendering fallback.
*
* @const string
*/
const FACADE = Phug::class;

/**
* Rendering options.
*
* @var array
*/
private $options;

/**
* Templates directories.
*
* @var array
*/
private $paths;

/**
* Cache directory.
*
* @var string
*/
private $cacheDirectory;

/**
* Locator to resolve template file paths.
*
* @var FileLocator
*/
private $locator;

/**
* Return a hashed print from input file or content.
*
* @param string $input
*
* @return string
*/
private function hashPrint($input)
{
// Get the stronger hashing algorithm available to minimize collision risks
$algorithms = hash_algos();
$algorithm = $algorithms[0];
$number = 0;
foreach ($algorithms as $hashAlgorithm) {
foreach (['md', 'sha'] as $type) {
if (strpos($hashAlgorithm, $type) === 0) {
$hashNumber = substr($hashAlgorithm, strlen($type));
if ($hashNumber > $number) {
$number = $hashNumber;
$algorithm = $hashAlgorithm;
}
continue 2;
}
}
}

return rtrim(strtr(base64_encode(hash($algorithm, $input, true)), '+/', '-_'), '=');
}

/**
* Returns true if the path has an expired imports linked.
*
* @param $path
*
* @return bool
*/
private function hasExpiredImport($sourcePath, $cachePath)
{
$importsMap = $cachePath.'.imports.serialize.txt';

if (!file_exists($importsMap)) {
return true;
}

$importPaths = unserialize(file_get_contents($importsMap)) ?: [];
$importPaths[] = $sourcePath;
$time = filemtime($cachePath);
foreach ($importPaths as $importPath) {
if (!file_exists($importPath) || filemtime($importPath) >= $time) {
// If only one file has changed, expires
return true;
}
}

// If only no files changed, it's up to date
return false;
}

public function __construct(array $options = [])
{
$this->locator = new FileLocator();
$this->paths = isset($options['paths']) ? $options['paths'] : [];
if (isset($options['base_dir'])) {
$this->paths[] = $options['base_dir'];
unset($options['base_dir']);
$options['paths'] = $this->paths;
}
if (isset($options['basedir'])) {
$this->paths[] = $options['basedir'];
unset($options['basedir']);
$options['paths'] = $this->paths;
}
if (isset($options['cache']) && !isset($options['cache_dir'])) {
$options['cache_dir'] = $options['cache'];
}
$this->options = $options;
$this->cacheDirectory = isset($options['cache_dir']) ? $options['cache_dir'] : '';
}

/**
* Resolve a template file path.
*
* @param string $file
*
* @return bool|null|string
*/
public function resolve($file)
{
return $this->locator->locate(
$file,
$this->paths,
isset($this->options['extensions'])
? $this->options['extensions']
: ['', '.pug', '.jade']
);
}

/**
* Returns true is a template file is expired, false else.
* $cachePath will be set with the template cache file path.
*
* @param string $file
* @param string &$cachePath
*
* @return bool
*/
public function isExpired($file, &$cachePath = null)
{
if (isset($this->options['up_to_date_check']) && !$this->options['up_to_date_check']) {
return false;
}

if (!$this->cacheDirectory) {
return true;
}

$sourcePath = $this->resolve($file);
$cachePath = rtrim($this->cacheDirectory, '\\/').DIRECTORY_SEPARATOR.$this->hashPrint($sourcePath).'.php';

if (!file_exists($cachePath)) {
return true;
}

return $this->hasExpiredImport($sourcePath, $cachePath);
}

/**
* Display a template.
*
* @param string $__pug_file
* @param array $__pug_parameters
*/
public function displayFile($__pug_file, array $__pug_parameters = [])
{
if ($this->isExpired($__pug_file, $__pug_cache_file)) {
if (isset($this->options['render'])) {
call_user_func($this->options['render'], $__pug_file, $__pug_parameters, $this->options);

return;
}
if (isset($this->options['renderer'])) {
$this->options['renderer']->displayFile($__pug_file, $__pug_parameters);

return;
}
if (isset($this->options['renderer_class_name'])) {
$className = $this->options['renderer_class_name'];
$renderer = new $className($this->options);
$renderer->displayFile($__pug_file, $__pug_parameters);

return;
}
$facade = isset($this->options['facade'])
? $this->options['facade']
: static::FACADE;
if (is_callable([$facade, 'displayFile'])) {
$facade::displayFile($__pug_file, $__pug_parameters, $this->options);

return;
}

throw new \RuntimeException(
'No valid render method, renderer engine, renderer class or facade provided.'
);
}

extract($__pug_parameters);
include $__pug_cache_file;
}

/**
* Returns a rendered template file.
*
* @param string $file
* @param array $parameters
*
* @return string
*/
public function renderFile($file, array $parameters = [])
{
ob_start();
$this->displayFile($file, $parameters);

return ob_get_clean();
}
}
17 changes: 17 additions & 0 deletions tests/Phug/AbstractPhugTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@ abstract class AbstractPhugTest extends TestCase
*/
protected $verbatim;

protected static function emptyDirectory($dir)
{
if (!is_dir($dir)) {
return;
}
foreach (scandir($dir) as $file) {
if ($file !== '.' && $file !== '..') {
$path = $dir.'/'.$file;
if (is_dir($path)) {
static::emptyDirectory($path);
} else {
unlink($path);
}
}
}
}

public function setUp()
{
include_once __DIR__.'/VerbatimExtension.php';
Expand Down
2 changes: 1 addition & 1 deletion tests/Phug/CliTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public function testCacheDirectory()
rmdir($expected);
}

self::assertSame("1 templates cached.\n0 templates failed to be cached.\n", $text);
self::assertSame("3 templates cached.\n0 templates failed to be cached.\n", $text);

$expected = __DIR__.'/../errorTemplates/cache';
ob_start();
Expand Down
Loading

0 comments on commit c223468

Please sign in to comment.