Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] Templator with manifest file check #242

Closed
wants to merge 10 commits into from
161 changes: 161 additions & 0 deletions src/System/View/Manifestor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php

declare(strict_types=1);

namespace System\View;

class Manifestor
{
private string $templateDir;
private string $cache_path;
private string $manifest_name;
/** @var array<string, string[]> */
private static $cache_manifest = [];

public function __construct(string $templateDir, string $cache_path, string $manifest_name = '/manifest.json')
{
$this->templateDir = $templateDir;
$this->cache_path = $cache_path;
$this->manifest_name = $manifest_name;
}

public function manifestFileName(): string
{
return $this->cache_path . $this->manifest_name;
}

/**
* Get manifest data as array paired.
*
* @return array<string, string[]>
*/
public function getManifest()
{
if (false === file_exists($file_name = $this->manifestFileName())) {
throw new \InvalidArgumentException("Manifest file name doest exist `{$file_name}`.");
}

$file = file_get_contents($file_name);

if (false === $file) {
throw new \Exception('Cant load manifet file.');
}

return self::$cache_manifest = (array) json_decode($file, true, 512, JSON_THROW_ON_ERROR);
}

/**
* Get cached manifest data.
*
* @return array<string, string[]>
*/
public static function getCachedManifest(string $template_path, string $cache_path, string $manifest_name = 'manifest.json')
{
if ([] === self::$cache_manifest) {
(new self($template_path, $cache_path, $manifest_name))->getManifest();
}

return self::$cache_manifest;
}

public static function flushCachedManifest(): void
{
self::$cache_manifest = [];
}

/**
* Put manifest as json data.
*
* @param array<string, string[]> $manifest
*/
public function putManifest($manifest): bool
{
if (false === ($raw = json_encode($manifest, JSON_PRETTY_PRINT))) {
return false;
}

if (false === file_put_contents($this->manifestFileName(), $raw)) {
return false;
}

self::$cache_manifest = $manifest;

return true;
}

public function hasManifest(): bool
{
return file_exists($this->manifestFileName());
}

public function init(): void
{
$this->putManifest([]);
}

/**
* Get teplate dependency slot/include.
*
* @return string[]
*/
public function getDependency(string $template_filename)
{
return $this->getManifest()[$template_filename] ?? [];
}

/**
* Remove template file from manifest file.
*/
public function removeDependency(string $template_filename): void
{
$new_manifest = [];
foreach ($this->getManifest() as $template => $dependency) {
if ($template === $template_filename) {
continue;
}

$new_manifest[$template] = $dependency;
}
$this->putManifest($new_manifest);
}

/**
* Replace dependecy to new (overwrite).
*
* @param string[] $new_dependency
*/
public function replaceDependency(string $template_filename, $new_dependency): void
{
$dependency = $this->getManifest();
$dependency[$template_filename] = $new_dependency;
$this->putManifest(self::$cache_manifest = $dependency);
}

/**
* Check template file depency is uptode to newst template.
*
* @throws \Exception when dependency file not exist
*/
public function isDependencyUptodate(string $template_filename, int $template_time = null): bool
{
if (false === \file_exists($this->cache_path . '/' . $template_filename)) {
throw new \Exception("Cache file `{$template_filename}` is not exist.");
}

if (null === $template_time) {
$template_time = \filemtime($this->cache_path . '/' . $template_filename);
}

foreach ($this->getDependency($template_filename) as $file) {
if (false === file_exists($check = $this->templateDir . '/' . $file)) {
return false;
}

if (\filemtime($check) >= $template_time) {
return false;
}
}

return true;
}
}
29 changes: 25 additions & 4 deletions src/System/View/Templator.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,19 @@ class Templator
private $sections = [];
public string $suffix = '';
public int $max_depth = 5;
private Manifestor $manifest;
/**
* Template dependency.
*
* @var string[]
*/
private $dependency = [];

public function __construct(string $templateDir, string $cacheDir)
public function __construct(string $templateDir, string $cacheDir, string $manifest = '/manifest.json')
{
$this->templateDir = $templateDir;
$this->cacheDir = $cacheDir;
$this->manifest = new Manifestor($templateDir, $cacheDir, $manifest);
}

/**
Expand All @@ -33,16 +41,19 @@ public function render(string $templateName, array $data, bool $cache = true): s
throw new ViewFileNotFound($templatePath);
}

$cachePath = $this->cacheDir . '/' . md5($templateName) . '.php';
$alias = md5($templateName) . '.php';
$cachePath = $this->cacheDir . '/' . $alias;

if ($cache && file_exists($cachePath) && filemtime($cachePath) >= filemtime($templatePath)) {
if ($cache && file_exists($cachePath) && $this->manifest->isDependencyUptodate($alias)) {
return $this->getView($cachePath, $data);
}
$this->addDependency($templateName);

$template = file_get_contents($templatePath);
$template = $this->templates($template);

file_put_contents($cachePath, $template);
$this->manifest->replaceDependency($alias, $this->dependency);

return $this->getView($cachePath, $data);
}
Expand Down Expand Up @@ -117,6 +128,8 @@ function ($matches) use ($matches_layout) {
$layout
);

$this->addDependency($matches_layout[1]);

return $template;
}

Expand All @@ -132,6 +145,7 @@ function ($matches) use ($maks_dept) {
}

$includedTemplate = file_get_contents($templatePath);
$this->addDependency($matches[1]);
if ($maks_dept === 0) {
return $includedTemplate;
}
Expand Down Expand Up @@ -173,8 +187,15 @@ private function templateEach(string $template): string
return preg_replace('/{%\s*foreach\s+([^%]+)\s+as\s+([^%]+)\s*%}(.*?){%\s*endforeach\s*%}/s', '<?php foreach ($$1 as $$2): ?>$3<?php endforeach; ?>', $template);
}

public function templateComment(string $template): string
private function templateComment(string $template): string
{
return preg_replace('/{#\s*(.*?)\s*#}/', '<?php // $1 ?>', $template);
}

private function addDependency(string $templatename): void
{
if (false === in_array($templatename, $this->dependency)) {
$this->dependency[] = $templatename;
}
}
}
3 changes: 3 additions & 0 deletions tests/Integrate/Helper/ViewTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use System\Http\Response;
use System\Integrate\Application;
use System\Text\Str;
use System\View\Manifestor;
use System\View\Templator;

final class ViewTest extends TestCase
Expand All @@ -16,6 +17,8 @@ public function testItCanGetResponeFromeContainer()
{
$app = new Application('/');

(new Manifestor(__DIR__ . '/assets/view/', __DIR__ . '/assets/cache/'))->init();

$app->set(
'view.response',
fn () => fn (string $view_path, array $portal = []): Response => (new Response())
Expand Down
3 changes: 2 additions & 1 deletion tests/Integrate/Helper/assets/cache/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.php
*.php
*.json
137 changes: 137 additions & 0 deletions tests/View/ManifestorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

declare(strict_types=1);

namespace System\Test\View;

use PHPUnit\Framework\TestCase;
use System\View\Manifestor;

class ManifestorTest extends TestCase
{
protected function setUp(): void
{
file_put_contents(__DIR__ . '/caches/manifestor.test.json', '[]');
}

protected function tearDown(): void
{
Manifestor::flushCachedManifest();
if (file_exists(__DIR__ . '/caches/manifestor.test.json')) {
unlink(__DIR__ . '/caches/manifestor.test.json');
}
}

/**
* @test
*/
public function itCanGetManifestData()
{
$manifest = new Manifestor(__DIR__, __DIR__ . '/caches/', 'manifestor.test.json');

$this->assertEquals(__DIR__ . '/caches/manifestor.test.json', $manifest->manifestFileName());
$this->assertEquals([], $manifest->getManifest());
}

/**
* @test
*/
public function itCanGetCachedManifestData()
{
$manifest = new Manifestor(__DIR__, __DIR__ . '/caches/', 'manifestor.test.json');

$manifest->putManifest(['a' => ['b', 'c']]);
$this->assertEquals(['a' => ['b', 'c']], Manifestor::getCachedManifest(__DIR__ . '/caches/', 'manifestor.test.json'));
}

/**
* @test
*/
public function itCanGetCachedManifestDataNoCached()
{
$this->assertEquals([], Manifestor::getCachedManifest(__DIR__, __DIR__ . '/caches/', 'manifestor.test.json'));
}

/**
* @test
*/
public function itCanPutMaifestData()
{
$manifest = new Manifestor(__DIR__, __DIR__ . '/caches/', 'manifestor.test.json');

$manifest->putManifest(['a' => ['b', 'c']]);
$this->assertEquals(['a' => ['b', 'c']], $manifest->getManifest());
}

/**
* @test
*/
public function itCheakHasManifest()
{
$manifest = new Manifestor(__DIR__, __DIR__ . '/caches/', 'manifestor.test.json');

$this->assertTrue($manifest->hasManifest());
}

/**
* @test
*/
public function itGetDependecy()
{
$manifest = new Manifestor(__DIR__, __DIR__ . '/caches/', 'manifestor.test.json');

$manifest->putManifest(['a' => ['b', 'c']]);
$this->assertEquals(['b', 'c'], $manifest->getDependency('a'));
}

/**
* @test
*/
public function itRemoveDependecy()
{
$manifest = new Manifestor(__DIR__, __DIR__ . '/caches/', 'manifestor.test.json');

$manifest->putManifest(['a' => ['b', 'c']]);
$this->assertEquals(['b', 'c'], $manifest->getDependency('a'));
$manifest->removeDependency('a');
$this->assertEquals([], $manifest->getDependency('a'));
}

/**
* @test
*/
public function itReplaceDependecy()
{
$manifest = new Manifestor(__DIR__, __DIR__ . '/caches/', 'manifestor.test.json');

$manifest->putManifest(['a' => ['b', 'c']]);
$this->assertEquals(['b', 'c'], $manifest->getDependency('a'));
$manifest->replaceDependency('a', ['d', 'e']);
$this->assertEquals(['d', 'e'], $manifest->getDependency('a'));
}

/**
* @test
*/
public function itCheckDepencyIsUpdate()
{
$manifest = new Manifestor(__DIR__ . '/caches_fixed', __DIR__ . '/caches/', 'manifestor.test.json');

$manifest->putManifest(['a.php' => ['old2.php', 'old3.php']]);
file_put_contents(__DIR__ . '/caches/a.php', 'a');
$this->assertTrue($manifest->isDependencyUptodate('a.php'));
}

/**
* @test
*/
public function itCheckDepencyIsNotUpdate()
{
$manifest = new Manifestor(__DIR__ . '/caches', __DIR__ . '/caches_fixed/', 'manifestor.test.json');

$manifest->putManifest(['old.php' => ['middle.php', 'newst.php']]);
file_put_contents(__DIR__ . '/caches/middle.php', now()->format('Y-m-d H:i'));
file_put_contents(__DIR__ . '/caches/newst.php', now()->format('Y-m-d H:i'));
$this->assertFalse($manifest->isDependencyUptodate('old.php'));
}
}
Loading
Loading