262 lines
8.4 KiB
PHP
262 lines
8.4 KiB
PHP
<?php declare(strict_types=1);
|
|
|
|
namespace Picea;
|
|
|
|
use Closure;
|
|
use Picea\Language\LanguageRegistration,
|
|
Picea\Exception\RenderingError;
|
|
|
|
use Psr\Http\Message\ResponseInterface;
|
|
|
|
class Picea implements LanguageRegistration
|
|
{
|
|
const DEFAULT_BUILDER_TEMPLATE = "/Builder/ClassTemplate.php";
|
|
|
|
public Compiler\Context $context;
|
|
|
|
public Compiler $compiler;
|
|
|
|
public LanguageRegistration $languageRegistration;
|
|
|
|
public string $builderTemplatePath;
|
|
|
|
public Caching\Cache $cache;
|
|
|
|
public FileFetcher $fileFetcher;
|
|
|
|
public bool $debug;
|
|
|
|
public array $loadedTemplateFile = [];
|
|
|
|
public array $globalVariables = [];
|
|
|
|
public array $compiled = [];
|
|
|
|
public function __construct(
|
|
? Compiler\Context $context = null,
|
|
? Caching\Cache $cache = null,
|
|
? Compiler $compiler = null,
|
|
? LanguageRegistration $languageRegistration = null,
|
|
? FileFetcher $fileFetcher = null,
|
|
? string $builderTemplatePath = null,
|
|
bool $debug = false
|
|
){
|
|
$this->cache = $cache ?? new Caching\Memory("");
|
|
$this->context = $context ?? new Compiler\BaseContext();
|
|
$this->languageRegistration = $languageRegistration ?? new Language\DefaultRegistrations([], [], []);
|
|
$this->builderTemplatePath = $builderTemplatePath ?? dirname(__FILE__) . static::DEFAULT_BUILDER_TEMPLATE;
|
|
$this->compiler = $compiler ?? $this->instanciateCompiler();
|
|
$this->fileFetcher = $fileFetcher ?? $this->instanciateFileFetcher();
|
|
$this->debug = $debug;
|
|
|
|
$this->compiler->registerExtensionsFunctions($this->context);
|
|
|
|
$this->renderContext($this->context);
|
|
}
|
|
|
|
public function gatherCompiledObject(string $viewPath, array $variables = [], ? object $proxy = null) : ? object
|
|
{
|
|
if ( null === $object = $this->fetchFromCache($viewPath, $this->globalVariables + $variables, $proxy) ) {
|
|
throw new \RuntimeException("An error occured while trying to save a compiled template.");
|
|
}
|
|
|
|
return $object;
|
|
}
|
|
|
|
public function renderHtml(string $viewPath, array $variables = [], ?object $proxy = null) : ? string
|
|
{
|
|
$object = $this->gatherCompiledObject($viewPath, $variables, $proxy);
|
|
|
|
try {
|
|
return call_user_func($object);
|
|
}
|
|
catch(\Throwable $ex) {
|
|
#if (! $ex instanceof Exception\RenderHtmlException ) {
|
|
# throw new Exception\RenderHtmlException($object, $this, "An error occurred trying to render HTML view `$viewPath` : " . $ex->getMessage(), 911, $ex);
|
|
#}
|
|
#else {
|
|
throw $ex;
|
|
#}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Method used by Block and View tokens
|
|
*/
|
|
public function inlineHtml(? object $proxy, string $viewPath, array $variables) {
|
|
return $this->renderHtml($viewPath, $this->globalVariables + $variables, $proxy);
|
|
}
|
|
|
|
/**
|
|
* Allows block to be called from templates
|
|
*/
|
|
public function inlineBlock(? object $proxy, string $viewPath, ... $variables) {
|
|
return $this->renderHtml($viewPath, [ 'inlineVariables' => $variables, 'globalVariables' => $this->globalVariables ], $proxy);
|
|
}
|
|
|
|
/**
|
|
* Push view content inline
|
|
*/
|
|
public function inlineContent(string $viewPath) {
|
|
return $this->fileFetcher->getFileContent($viewPath);
|
|
}
|
|
|
|
/**
|
|
* Renders a compile context
|
|
*/
|
|
public function renderContext(Compiler\Context $context) : object
|
|
{
|
|
if ( null === $object = $this->contextFromCache($context) ) {
|
|
throw new \RuntimeException("An error occured while trying to save a compiled template.");
|
|
}
|
|
|
|
return $object;
|
|
}
|
|
|
|
public function compileSource(string $source) : array
|
|
{
|
|
$this->compiler->loadSourceCode($source);
|
|
|
|
return [
|
|
'context' => $context = clone $this->context,
|
|
'source' => $this->compiler->compile($context),
|
|
];
|
|
}
|
|
|
|
public function compileFile(string $viewPath) : array
|
|
{
|
|
if (! file_exists($viewPath) ) {
|
|
throw new \InvalidArgumentException("File `$viewPath` cannot be found or there is a permission problem.");
|
|
}
|
|
|
|
if ( false === $fileContent = file_get_contents($viewPath) ) {
|
|
throw new \ErrorException("Given file could not be opened `$viewPath`. This could indicate a permission misconfiguration on your file or folder.");
|
|
}
|
|
|
|
$this->compiler->loadSourceCode($fileContent);
|
|
$context = clone $this->context;
|
|
$context->viewPath = $viewPath;
|
|
$context->source = $this->compiler->compile($context);
|
|
|
|
return $context;
|
|
}
|
|
|
|
public function buildFromSource(string $source) : array
|
|
{
|
|
$tmpFolder = sys_get_temp_dir();
|
|
$builder = $this->instanciateBuilder();
|
|
$compiledSource = $this->compileSource($source);
|
|
list($namespace, $className) = $builder->build($compiledSource['context'], $compiledSource['source']) ;
|
|
|
|
$path = "$tmpFolder/$className.php";
|
|
file_put_contents($path, $compiledSource);
|
|
|
|
return [
|
|
'path' => $path,
|
|
'namespace' => $namespace,
|
|
'className' => $className,
|
|
];
|
|
}
|
|
|
|
public function fetchFromCache(string $viewPath, array $variables = [], ?object $proxy = null) : ?object
|
|
{
|
|
if ( ! isset($this->compiled[$viewPath]) && ( true === $this->debug ) || (! $this->cache->compiled($viewPath))) {
|
|
$context = $this->compileView($viewPath);
|
|
$this->cache->save($context);
|
|
|
|
if ( $context->extendFrom ) {
|
|
$this->fetchFromCache($context->extendFrom, $variables, $proxy);
|
|
}
|
|
|
|
$this->compiled[$viewPath] = true;
|
|
}
|
|
|
|
return $this->cache->load($viewPath, $this, $variables, $proxy);
|
|
}
|
|
|
|
public function contextFromCache(Compiler\Context $context) : ?object
|
|
{
|
|
if ( $this->debug || ! $this->cache->compiled( $context->cacheFilename() ) ) {
|
|
$context = $this->compileContext($context);
|
|
|
|
$this->cache->save($context);
|
|
}
|
|
|
|
return $this->cache->load($context->cacheFilename(), $this);
|
|
}
|
|
|
|
public function instanciateCompiler() : Compiler
|
|
{
|
|
return new Compiler($this->languageRegistration);
|
|
}
|
|
|
|
public function instanciateBuilder() : Builder
|
|
{
|
|
return new Builder($this->builderTemplatePath);
|
|
}
|
|
|
|
public function instanciateFileFetcher() : FileFetcher
|
|
{
|
|
return new FileFetcher();
|
|
}
|
|
|
|
public function registerAll(Compiler $compiler) : void
|
|
{
|
|
$this->registerSyntax($compiler);
|
|
$this->registerControlStructure($compiler);
|
|
$this->registerExtension($compiler);
|
|
}
|
|
|
|
public function registerSyntax(Compiler $compiler) : void
|
|
{
|
|
$this->languageRegistration->registerSyntax($compiler);
|
|
}
|
|
|
|
public function registerControlStructure(Compiler $compiler) : void
|
|
{
|
|
$this->languageRegistration->registerControlStructure($compiler);
|
|
}
|
|
|
|
public function registerExtension(Compiler $compiler) : void
|
|
{
|
|
$this->languageRegistration->registerExtension($compiler);
|
|
}
|
|
|
|
public function declareLoadedTemplate(string $className, string $filePath) : void
|
|
{
|
|
$this->loadedTemplateFile[$className] = $filePath;
|
|
}
|
|
|
|
protected function compileView(string $viewPath) : Compiler\Context
|
|
{
|
|
$tmpFolder = sys_get_temp_dir();
|
|
$builder = $this->instanciateBuilder();
|
|
$compiled = $this->compileSource($this->fileFetcher->getFileContent($viewPath));
|
|
$context = $compiled['context'];
|
|
$context->viewPath = $viewPath;
|
|
$context->filePath = $this->fileFetcher->getFilePath($viewPath);
|
|
$context = $builder->build($compiled['context'], $compiled['source']) ;
|
|
$context->classPath = $tmpFolder . DIRECTORY_SEPARATOR . $context->className . ".php";
|
|
|
|
if ( $context->extendFrom ) {
|
|
$this->compileView($context->extendFrom);
|
|
}
|
|
|
|
return $context;
|
|
}
|
|
|
|
protected function compileContext(Compiler\Context $context) : Compiler\Context
|
|
{
|
|
$tmpFolder = sys_get_temp_dir();
|
|
$builder = $this->instanciateBuilder();
|
|
$compiled = $this->compileSource("");
|
|
$context->viewPath = $context->cacheFilename();
|
|
$builder->build($context, "");
|
|
$context->classPath = $tmpFolder . DIRECTORY_SEPARATOR . $context->cacheFilename() . ".php";
|
|
|
|
return $context;
|
|
}
|
|
}
|