From 91f7b5588ed0f24ab5dc0d25648ea05894906ac1 Mon Sep 17 00:00:00 2001
From: Abdussamed <46815237+abdussamedulutas@users.noreply.github.com>
Date: Wed, 5 Apr 2023 23:26:44 +0300
Subject: [PATCH] Added blade template
---
Core/Blade.php | 15 +
Core/BladeOne.php | 4297 ++++++++++++++++++++++++++++++++++++++++
Core/BladeOneCache.php | 328 +++
Core/Core.php | 1 +
cache/.gitignore | 0
views/.gitignore | 0
6 files changed, 4641 insertions(+)
create mode 100644 Core/Blade.php
create mode 100644 Core/BladeOne.php
create mode 100644 Core/BladeOneCache.php
create mode 100644 cache/.gitignore
create mode 100644 views/.gitignore
diff --git a/Core/Blade.php b/Core/Blade.php
new file mode 100644
index 0000000..48fb00c
--- /dev/null
+++ b/Core/Blade.php
@@ -0,0 +1,15 @@
+setOptimize(true);
+ return $blade->run($name,$args);
+ }
\ No newline at end of file
diff --git a/Core/BladeOne.php b/Core/BladeOne.php
new file mode 100644
index 0000000..f0d0cce
--- /dev/null
+++ b/Core/BladeOne.php
@@ -0,0 +1,4297 @@
+
+ * @copyright Copyright (c) 2016-2022 Jorge Patricio Castro Castillo MIT License.
+ * Don't delete this comment, its part of the license.
+ * Part of this code is based in the work of Laravel PHP Components.
+ * @version 4.8.2
+ * @link https://github.com/EFTEC/BladeOne
+ */
+class BladeOne
+{
+ //
+ public const VERSION = '4.8.2';
+ /** @var int BladeOne reads if the compiled file has changed. If it has changed,then the file is replaced. */
+ public const MODE_AUTO = 0;
+ /** @var int Then compiled file is always replaced. It's slow and it's useful for development. */
+ public const MODE_SLOW = 1;
+ /** @var int The compiled file is never replaced. It's fast and it's useful for production. */
+ public const MODE_FAST = 2;
+ /** @var int DEBUG MODE, the file is always compiled and the filename is identifiable. */
+ public const MODE_DEBUG = 5;
+ /** @var array Hold dictionary of translations */
+ public static $dictionary = [];
+ /** @var string PHP tag. You could use < ?php or < ? (if shorttag is active in php.ini) */
+ public $phpTag = '
+ * If false (default value), then the variables defined in the "include" as arguments are defined globally.
+ * Example: (includeScope=false)
+ *
+ * @include("template",['a1'=>'abc']) // a1 is equals to abc
+ * @include("template",[]) // a1 is equals to abc
+ *
+ * Example: (includeScope=true)
+ *
+ * @include("template",['a1'=>'abc']) // a1 is equals to abc
+ * @include("template",[]) // a1 is not defined
+ *
+ */
+ public $includeScope = false;
+ /**
+ * @var callable[] It allows to parse the compiled output using a function.
+ * This function doesn't require to return a value
+ * Example: this converts all compiled result in uppercase (note, content is a ref)
+ *
+ */
+ public $compileCallbacks = [];
+ /** @var array All the registered extensions. */
+ protected $extensions = [];
+ /** @var array All the finished, captured sections. */
+ protected $sections = [];
+ /** @var string The template currently being compiled. For example "folder.template" */
+ protected $fileName;
+ protected $currentView;
+ protected $notFoundPath;
+ /** @var string File extension for the template files. */
+ protected $fileExtension = '.blade.php';
+ /** @var array The stack of in-progress sections. */
+ protected $sectionStack = [];
+ /** @var array The stack of in-progress loops. */
+ protected $loopsStack = [];
+ /** @var array Dictionary of variables */
+ protected $variables = [];
+ /** @var array Dictionary of global variables */
+ protected $variablesGlobal = [];
+ /** @var array All the available compiler functions. */
+ protected $compilers = [
+ 'Extensions',
+ 'Statements',
+ 'Comments',
+ 'Echos',
+ ];
+ /** @var string|null it allows to set the stack */
+ protected $viewStack;
+ /** @var array used by $this->composer() */
+ protected $composerStack = [];
+ /** @var array The stack of in-progress push sections. */
+ protected $pushStack = [];
+ /** @var array All the finished, captured push sections. */
+ protected $pushes = [];
+ /** @var int The number of active rendering operations. */
+ protected $renderCount = 0;
+ /** @var string[] Get the template path for the compiled views. */
+ protected $templatePath;
+ /** @var string Get the compiled path for the compiled views. If null then it uses the default path */
+ protected $compiledPath;
+ /** @var string the extension of the compiled file. */
+ protected $compileExtension = '.bladec';
+ /**
+ * @var string=['auto','sha1','md5'][$i] It determines how the compiled filename will be called.
+ * auto (default mode) the mode is "sha1"
+ * sha1 the filename is converted into a sha1 hash
+ * md5 the filename is converted into a md5 hash
+ */
+ protected $compileTypeFileName='auto';
+
+
+ /** @var array Custom "directive" dictionary. Those directives run at compile time. */
+ protected $customDirectives = [];
+ /** @var bool[] Custom directive dictionary. Those directives run at runtime. */
+ protected $customDirectivesRT = [];
+ /** @var callable Function used for resolving injected classes. */
+ protected $injectResolver;
+ /** @var array Used for conditional if. */
+ protected $conditions = [];
+ /** @var int Unique counter. It's used for extends */
+ protected $uidCounter = 0;
+ /** @var string The main url of the system. Don't use raw $_SERVER values unless the value is sanitized */
+ protected $baseUrl = '.';
+ /** @var string|null The base domain of the system */
+ protected $baseDomain;
+ /** @var string|null It stores the current canonical url. */
+ protected $canonicalUrl;
+ /** @var string|null It stores the current url including arguments */
+ protected $currentUrl;
+ /** @var string it is a relative path calculated between baseUrl and the current url. Example ../../ */
+ protected $relativePath = '';
+ /** @var string[] Dictionary of assets */
+ protected $assetDict;
+ /** @var bool if true then it removes tabs and unneeded spaces */
+ protected $optimize = true;
+ /** @var bool if false, then the template is not compiled (but executed on memory). */
+ protected $isCompiled = true;
+ /** @var bool */
+ protected $isRunFast = false; // stored for historical purpose.
+ /** @var array Array of opening and closing tags for raw echos. */
+ protected $rawTags = ['{!!', '!!}'];
+ /** @var array Array of opening and closing tags for regular echos. */
+ protected $contentTags = ['{{', '}}'];
+ /** @var array Array of opening and closing tags for escaped echos. */
+ protected $escapedTags = ['{{{', '}}}'];
+ /** @var string The "regular" / legacy echo string format. */
+ protected $echoFormat = '\htmlentities(%s??\'\', ENT_QUOTES, \'UTF-8\', false)';
+ /** @var string */
+ protected $echoFormatOld = 'static::e(%s)';
+ /** @var array Lines that will be added at the footer of the template */
+ protected $footer = [];
+ /** @var string Placeholder to temporary mark the position of verbatim blocks. */
+ protected $verbatimPlaceholder = '$__verbatim__$';
+ /** @var array Array to temporary store the verbatim blocks found in the template. */
+ protected $verbatimBlocks = [];
+ /** @var int Counter to keep track of nested forelse statements. */
+ protected $forelseCounter = 0;
+ /** @var array The components being rendered. */
+ protected $componentStack = [];
+ /** @var array The original data passed to the component. */
+ protected $componentData = [];
+ /** @var array The slot contents for the component. */
+ protected $slots = [];
+ /** @var array The names of the slots being rendered. */
+ protected $slotStack = [];
+ /** @var string tag unique */
+ protected $PARENTKEY = '@parentXYZABC';
+ /**
+ * Indicates the compile mode.
+ * if the constant BLADEONE_MODE is defined, then it is used instead of this field.
+ *
+ * @var int=[BladeOne::MODE_AUTO,BladeOne::MODE_DEBUG,BladeOne::MODE_SLOW,BladeOne::MODE_FAST][$i]
+ */
+ protected $mode;
+ /** @var int Indicates the number of open switches */
+ protected $switchCount = 0;
+ /** @var bool Indicates if the switch is recently open */
+ protected $firstCaseInSwitch = true;
+
+ //
+
+ //
+
+ /**
+ * Bob the constructor.
+ * The folder at $compiledPath is created in case it doesn't exist.
+ *
+ * @param string|array $templatePath If null then it uses (caller_folder)/views
+ * @param string $compiledPath If null then it uses (caller_folder)/compiles
+ * @param int $mode =[BladeOne::MODE_AUTO,BladeOne::MODE_DEBUG,BladeOne::MODE_FAST,BladeOne::MODE_SLOW][$i]
+ */
+ public function __construct($templatePath = null, $compiledPath = null, $mode = 0)
+ {
+ if ($templatePath === null) {
+ $templatePath = \getcwd() . '/views';
+ }
+ if ($compiledPath === null) {
+ $compiledPath = \getcwd() . '/compiles';
+ }
+ $this->templatePath = (is_array($templatePath)) ? $templatePath : [$templatePath];
+ $this->compiledPath = $compiledPath;
+ $this->setMode($mode);
+ $this->authCallBack = function (
+ $action = null,
+ /** @noinspection PhpUnusedParameterInspection */
+ $subject = null
+ ) {
+ return \in_array($action, $this->currentPermission, true);
+ };
+
+ $this->authAnyCallBack = function ($array = []) {
+ foreach ($array as $permission) {
+ if (\in_array($permission, $this->currentPermission ?? [], true)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ $this->errorCallBack = static function (
+ /** @noinspection PhpUnusedParameterInspection */
+ $key = null
+ ) {
+ return false;
+ };
+
+
+ // If the "traits" has "Constructors", then we call them.
+ // Requisites.
+ // 1- the method must be public or protected
+ // 2- it must don't have arguments
+ // 3- It must have the name of the trait. i.e. trait=MyTrait, method=MyTrait()
+ $traits = get_declared_traits();
+ foreach ($traits as $trait) {
+ $r = explode('\\', $trait);
+ $name = end($r);
+ if (is_callable([$this, $name]) && method_exists($this, $name)) {
+ $this->{$name}();
+ }
+ }
+ }
+ //
+ //
+
+ /**
+ * Show an error in the web.
+ *
+ * @param string $id Title of the error
+ * @param string $text Message of the error
+ * @param bool $critic if true then the compilation is ended, otherwise it continues
+ * @param bool $alwaysThrow if true then it always throws a runtime exception.
+ * @return string
+ * @throws \RuntimeException
+ */
+ public function showError($id, $text, $critic = false, $alwaysThrow = false): string
+ {
+ \ob_get_clean();
+ if ($this->throwOnError || $alwaysThrow || $critic === true) {
+ throw new \RuntimeException("BladeOne Error [$id] $text");
+ }
+
+ $msg = "
+ *
+ * @param string $text
+ * @param string|null $textWithWildcard
+ *
+ * @return bool
+ */
+ protected function wildCardComparison($text, $textWithWildcard): bool
+ {
+ if (($textWithWildcard === null || $textWithWildcard === '')
+ || strpos($textWithWildcard, '*') === false
+ ) {
+ // if the text with wildcard is null or empty, or it contains two ** or it contains no * then..
+ return $text == $textWithWildcard;
+ }
+ if ($textWithWildcard === '*' || $textWithWildcard === '**') {
+ return true;
+ }
+ $c0 = $textWithWildcard[0];
+ $c1 = substr($textWithWildcard, -1);
+ $textWithWildcardClean = str_replace('*', '', $textWithWildcard);
+ $p0 = strpos($text, $textWithWildcardClean);
+ if ($p0 === false) {
+ // no matches.
+ return false;
+ }
+ if ($c0 === '*' && $c1 === '*') {
+ // $textWithWildcard='*asasasas*'
+ return true;
+ }
+ if ($c1 === '*') {
+ // $textWithWildcard='asasasas*'
+ return $p0 === 0;
+ }
+ // $textWithWildcard='*asasasas'
+ $len = strlen($textWithWildcardClean);
+ return (substr($text, -$len) === $textWithWildcardClean);
+ }
+
+ protected function methodExistsStatic($class, $method): bool
+ {
+ try {
+ return (new \ReflectionMethod($class, $method))->isStatic();
+ } catch (\ReflectionException $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Compile the view at the given path.
+ *
+ * @param string $templateName The name of the template. Example folder.template
+ * @param bool $forced If the compilation will be forced (always compile) or not.
+ * @return boolean|string True if the operation was correct, or false (if not exception)
+ * if it fails. It returns a string (the content compiled) if isCompiled=false
+ * @throws Exception
+ */
+ public function compile($templateName = null, $forced = false)
+ {
+ $compiled = $this->getCompiledFile($templateName);
+ $template = $this->getTemplateFile($templateName);
+ if (!$this->isCompiled) {
+ $contents = $this->compileString($this->getFile($template));
+ $this->compileCallBacks($contents, $templateName);
+ return $contents;
+ }
+ if ($forced || $this->isExpired($templateName)) {
+ // compile the original file
+ $contents = $this->compileString($this->getFile($template));
+ $this->compileCallBacks($contents, $templateName);
+ if ($this->optimize) {
+ // removes space and tabs and replaces by a single space
+ $contents = \preg_replace('/^ {2,}/m', ' ', $contents);
+ $contents = \preg_replace('/^\t{2,}/m', ' ', $contents);
+ }
+ $ok = @\file_put_contents($compiled, $contents);
+ if ($ok === false) {
+ $this->showError(
+ 'Compiling',
+ "Unable to save the file [$compiled]. Check the compile folder is defined and has the right permission"
+ );
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the full path of the compiled file.
+ *
+ * @param string $templateName
+ * @return string
+ */
+ public function getCompiledFile($templateName = ''): string
+ {
+ $templateName = (empty($templateName)) ? $this->fileName : $templateName;
+
+ $fullPath = $this->getTemplateFile($templateName);
+ if($fullPath == '') {
+ throw new \RuntimeException('Template not found: ' . $templateName);
+ }
+
+ $style=$this->compileTypeFileName;
+ if ($style==='auto') {
+ $style='sha1';
+ }
+ $hash = $style === 'md5' ? \md5($fullPath) : \sha1($fullPath);
+ return $this->compiledPath . '/' . basename($templateName) . '_' . $hash . $this->compileExtension;
+ }
+
+
+
+ /**
+ * Get the mode of the engine.See BladeOne::MODE_* constants
+ *
+ * @return int=[self::MODE_AUTO,self::MODE_DEBUG,self::MODE_FAST,self::MODE_SLOW][$i]
+ */
+ public function getMode(): int
+ {
+ if (\defined('BLADEONE_MODE')) {
+ $this->mode = BLADEONE_MODE;
+ }
+ return $this->mode;
+ }
+
+ /**
+ * Set the compile mode
+ *
+ * @param $mode int=[self::MODE_AUTO,self::MODE_DEBUG,self::MODE_FAST,self::MODE_SLOW][$i]
+ * @return void
+ */
+ public function setMode($mode): void
+ {
+ $this->mode = $mode;
+ }
+
+ /**
+ * Get the full path of the template file.
+ *
Example: getTemplateFile('.abc.def')
+ *
+ * @param string $templateName template name. If not template is set then it uses the base template.
+ * @return string
+ */
+ public function getTemplateFile($templateName = ''): string
+ {
+ $templateName = (empty($templateName)) ? $this->fileName : $templateName;
+ if (\strpos($templateName, '/') !== false) {
+ return $this->locateTemplate($templateName); // it's a literal
+ }
+ $arr = \explode('.', $templateName);
+ $c = \count($arr);
+ if ($c == 1) {
+ // it's in the root of the template folder.
+ return $this->locateTemplate($templateName . $this->fileExtension);
+ }
+
+ $file = $arr[$c - 1];
+ \array_splice($arr, $c - 1, $c - 1); // delete the last element
+ $path = \implode('/', $arr);
+ return $this->locateTemplate($path . '/' . $file . $this->fileExtension);
+ }
+
+ /**
+ * Find template file with the given name in all template paths in the order the paths were written
+ *
+ * @param string $name Filename of the template (without path)
+ * @return string template file
+ */
+ protected function locateTemplate($name): string
+ {
+ $this->notFoundPath = '';
+ foreach ($this->templatePath as $dir) {
+ $path = $dir . '/' . $name;
+ if (\is_file($path)) {
+ return $path;
+ }
+
+ $this->notFoundPath .= $path . ",";
+ }
+ return '';
+ }
+
+ /**
+ * Get the contents of a file.
+ *
+ * @param string $fullFileName It gets the content of a filename or returns ''.
+ *
+ * @return string
+ */
+ public function getFile($fullFileName): string
+ {
+ if (\is_file($fullFileName)) {
+ return \file_get_contents($fullFileName);
+ }
+ $this->showError('getFile', "File does not exist at paths (separated by comma) [$this->notFoundPath] or permission denied");
+ return '';
+ }
+
+ protected function compileCallBacks(&$contents, $templateName): void
+ {
+ if (!empty($this->compileCallbacks)) {
+ foreach ($this->compileCallbacks as $callback) {
+ if (is_callable($callback)) {
+ $callback($contents, $templateName);
+ }
+ }
+ }
+ }
+
+ /**
+ * Determine if the view has expired.
+ *
+ * @param string|null $fileName
+ * @return bool
+ */
+ public function isExpired($fileName): bool
+ {
+ $compiled = $this->getCompiledFile($fileName);
+ $template = $this->getTemplateFile($fileName);
+ if (!\is_file($template)) {
+ if ($this->mode == self::MODE_DEBUG) {
+ $this->showError('Read file', 'Template not found :' . $this->fileName . " on file: $template", true);
+ } else {
+ $this->showError('Read file', 'Template not found :' . $this->fileName, true);
+ }
+ }
+ // If the compiled file doesn't exist we will indicate that the view is expired
+ // so that it can be re-compiled. Else, we will verify the last modification
+ // of the views is less than the modification times of the compiled views.
+ if (!$this->compiledPath || !\is_file($compiled)) {
+ return true;
+ }
+ return \filemtime($compiled) < \filemtime($template);
+ }
+
+ /**
+ * Evaluates a text (string) using the current variables
+ *
+ * @param string $content
+ * @param array $variables
+ * @return string
+ * @throws Exception
+ */
+ protected function evaluateText($content, $variables): string
+ {
+ \ob_start();
+ \extract($variables);
+ // We'll evaluate the contents of the view inside a try/catch block, so we can
+ // flush out any stray output that might get out before an error occurs or
+ // an exception is thrown. This prevents any partial views from leaking.
+ try {
+ eval(' ?>' . $content . $this->phpTag);
+ } catch (Exception $e) {
+ $this->handleViewException($e);
+ }
+ return \ltrim(\ob_get_clean());
+ }
+
+ /**
+ * Handle a view exception.
+ *
+ * @param Exception $e
+ * @return void
+ * @throws $e
+ */
+ protected function handleViewException($e): void
+ {
+ \ob_get_clean();
+ throw $e;
+ }
+
+ /**
+ * Evaluates a compiled file using the current variables
+ *
+ * @param string $compiledFile full path of the compile file.
+ * @param array $variables
+ * @return string
+ * @throws Exception
+ */
+ protected function evaluatePath($compiledFile, $variables): string
+ {
+ \ob_start();
+ // note, the variables are extracted locally inside this method,
+ // they are not global variables :-3
+ \extract($variables);
+ // We'll evaluate the contents of the view inside a try/catch block, so we can
+ // flush out any stray output that might get out before an error occurs or
+ // an exception is thrown. This prevents any partial views from leaking.
+ try {
+ include $compiledFile;
+ } catch (Exception $e) {
+ $this->handleViewException($e);
+ }
+ return \ltrim(\ob_get_clean());
+ }
+
+ /**
+ * @param array $views array of views
+ * @param array $value
+ * @return string
+ * @throws Exception
+ */
+ public function includeFirst($views = [], $value = []): string
+ {
+ foreach ($views as $view) {
+ if ($this->templateExist($view)) {
+ return $this->runChild($view, $value);
+ }
+ }
+ return '';
+ }
+
+ /**
+ * Returns true if the template exists. Otherwise, it returns false
+ *
+ * @param $templateName
+ * @return bool
+ */
+ protected function templateExist($templateName): bool
+ {
+ $file = $this->getTemplateFile($templateName);
+ return \is_file($file);
+ }
+
+ /**
+ * Convert an array such as ["class1"=>"myclass","style="mystyle"] to class1='myclass' style='mystyle' string
+ *
+ * @param array|string $array array to convert
+ * @return string
+ */
+ public function convertArg($array): string
+ {
+ if (!\is_array($array)) {
+ return $array; // nothing to convert.
+ }
+ return \implode(' ', \array_map('static::convertArgCallBack', \array_keys($array), $array));
+ }
+
+ /**
+ * Returns the current token. if there is not a token then it generates a new one.
+ * It could require an open session.
+ *
+ * @param bool $fullToken It returns a token with the current ip.
+ * @param string $tokenId [optional] Name of the token.
+ *
+ * @return string
+ */
+ public function getCsrfToken($fullToken = false, $tokenId = '_token'): string
+ {
+ if ($this->csrf_token == '') {
+ $this->regenerateToken($tokenId);
+ }
+ if ($fullToken) {
+ return $this->csrf_token . '|' . $this->ipClient();
+ }
+ return $this->csrf_token;
+ }
+
+ /**
+ * Regenerates the csrf token and stores in the session.
+ * It requires an open session.
+ *
+ * @param string $tokenId [optional] Name of the token.
+ */
+ public function regenerateToken($tokenId = '_token'): void
+ {
+ try {
+ $this->csrf_token = \bin2hex(\random_bytes(10));
+ } catch (Exception $e) {
+ $this->csrf_token = '123456789012345678901234567890'; // unable to generates a random token.
+ }
+ @$_SESSION[$tokenId] = $this->csrf_token . '|' . $this->ipClient();
+ }
+
+ public function ipClient()
+ {
+ if (
+ isset($_SERVER['HTTP_X_FORWARDED_FOR'])
+ && \preg_match('/^(d{1,3}).(d{1,3}).(d{1,3}).(d{1,3})$/', $_SERVER['HTTP_X_FORWARDED_FOR'])
+ ) {
+ return $_SERVER['HTTP_X_FORWARDED_FOR'];
+ }
+ return $_SERVER['REMOTE_ADDR'] ?? '';
+ }
+
+ /**
+ * Validates if the csrf token is valid or not.
+ * It requires an open session.
+ *
+ * @param bool $alwaysRegenerate [optional] Default is false.
+ * If true then it will generate a new token regardless
+ * of the method.
+ * If false, then it will generate only if the method is POST.
+ * Note: You must not use true if you want to use csrf with AJAX.
+ *
+ * @param string $tokenId [optional] Name of the token.
+ *
+ * @return bool It returns true if the token is valid, or it is generated. Otherwise, false.
+ */
+ public function csrfIsValid($alwaysRegenerate = false, $tokenId = '_token'): bool
+ {
+ if (@$_SERVER['REQUEST_METHOD'] === 'POST' && $alwaysRegenerate === false) {
+ $this->csrf_token = $_POST[$tokenId] ?? null; // ping pong the token.
+ return $this->csrf_token . '|' . $this->ipClient() === ($_SESSION[$tokenId] ?? null);
+ }
+
+ if ($this->csrf_token == '' || $alwaysRegenerate) {
+ // if not token then we generate a new one
+ $this->regenerateToken($tokenId);
+ }
+ return true;
+ }
+
+ /**
+ * Stop injecting content into a section and return its contents.
+ *
+ * @return string
+ */
+ public function yieldSection(): ?string
+ {
+ $sc = $this->stopSection();
+ return $this->sections[$sc] ?? null;
+ }
+
+ /**
+ * Stop injecting content into a section.
+ *
+ * @param bool $overwrite
+ * @return string
+ */
+ public function stopSection($overwrite = false): string
+ {
+ if (empty($this->sectionStack)) {
+ $this->showError('stopSection', 'Cannot end a section without first starting one.', true, true);
+ }
+ $last = \array_pop($this->sectionStack);
+ if ($overwrite) {
+ $this->sections[$last] = \ob_get_clean();
+ } else {
+ $this->extendSection($last, \ob_get_clean());
+ }
+ return $last;
+ }
+
+ /**
+ * Append content to a given section.
+ *
+ * @param string $section
+ * @param string $content
+ * @return void
+ */
+ protected function extendSection($section, $content): void
+ {
+ if (isset($this->sections[$section])) {
+ $content = \str_replace($this->PARENTKEY, $content, $this->sections[$section]);
+ }
+ $this->sections[$section] = $content;
+ }
+
+ public function dump($object, $jsconsole = false): void
+ {
+ if (!$jsconsole) {
+ echo '
';
+ \var_dump($object);
+ echo '
';
+ } else {
+ /** @noinspection BadExpressionStatementJS */
+ /** @noinspection JSVoidFunctionReturnValueUsed */
+ echo '';
+ }
+ }
+
+ /**
+ * Start injecting content into a section.
+ *
+ * @param string $section
+ * @param string $content
+ * @return void
+ */
+ public function startSection($section, $content = ''): void
+ {
+ if ($content === '') {
+ \ob_start() && $this->sectionStack[] = $section;
+ } else {
+ $this->extendSection($section, $content);
+ }
+ }
+
+ /**
+ * Stop injecting content into a section and append it.
+ *
+ * @return string
+ * @throws InvalidArgumentException
+ */
+ public function appendSection(): string
+ {
+ if (empty($this->sectionStack)) {
+ $this->showError('appendSection', 'Cannot end a section without first starting one.', true, true);
+ }
+ $last = \array_pop($this->sectionStack);
+ if (isset($this->sections[$last])) {
+ $this->sections[$last] .= \ob_get_clean();
+ } else {
+ $this->sections[$last] = \ob_get_clean();
+ }
+ return $last;
+ }
+
+ /**
+ * Adds a global variable. If $varname is an array then it merges all the values.
+ * Example:
+ *
+ * $this->share('variable',10.5);
+ * $this->share('variable2','hello');
+ * // or we could add the two variables as:
+ * $this->share(['variable'=>10.5,'variable2'=>'hello']);
+ *
+ *
+ * @param string|array $varname It is the name of the variable or, it is an associative array
+ * @param mixed $value
+ * @return $this
+ * @see BladeOne::share
+ */
+ public function with($varname, $value = null): BladeOne
+ {
+ return $this->share($varname, $value);
+ }
+
+ /**
+ * Adds a global variable. If $varname is an array then it merges all the values.
+ * Example:
+ *
+ * $this->share('variable',10.5);
+ * $this->share('variable2','hello');
+ * // or we could add the two variables as:
+ * $this->share(['variable'=>10.5,'variable2'=>'hello']);
+ *
+ *
+ * @param string|array $varname It is the name of the variable, or it is an associative array
+ * @param mixed $value
+ * @return $this
+ */
+ public function share($varname, $value = null): BladeOne
+ {
+ if (is_array($varname)) {
+ $this->variablesGlobal = \array_merge($this->variablesGlobal, $varname);
+ } else {
+ $this->variablesGlobal[$varname] = $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Get the string contents of a section.
+ *
+ * @param string $section
+ * @param string $default
+ * @return string
+ */
+ public function yieldContent($section, $default = ''): string
+ {
+ if (isset($this->sections[$section])) {
+ return \str_replace($this->PARENTKEY, $default, $this->sections[$section]);
+ }
+
+ return $default;
+ }
+
+ /**
+ * Register a custom Blade compiler.
+ *
+ * @param callable $compiler
+ * @return void
+ */
+ public function extend(callable $compiler): void
+ {
+ $this->extensions[] = $compiler;
+ }
+
+ /**
+ * Register a handler for custom directives for run at runtime
+ *
+ * @param string $name
+ * @param callable $handler
+ * @return void
+ */
+ public function directiveRT($name, callable $handler): void
+ {
+ $this->customDirectives[$name] = $handler;
+ $this->customDirectivesRT[$name] = true;
+ }
+
+ /**
+ * Sets the escaped content tags used for the compiler.
+ *
+ * @param string $openTag
+ * @param string $closeTag
+ * @return void
+ */
+ public function setEscapedContentTags($openTag, $closeTag): void
+ {
+ $this->setContentTags($openTag, $closeTag, true);
+ }
+
+ /**
+ * Gets the content tags used for the compiler.
+ *
+ * @return array
+ */
+ public function getContentTags(): array
+ {
+ return $this->getTags();
+ }
+
+ /**
+ * Sets the content tags used for the compiler.
+ *
+ * @param string $openTag
+ * @param string $closeTag
+ * @param bool $escaped
+ * @return void
+ */
+ public function setContentTags($openTag, $closeTag, $escaped = false): void
+ {
+ $property = ($escaped === true) ? 'escapedTags' : 'contentTags';
+ $this->{$property} = [\preg_quote($openTag), \preg_quote($closeTag)];
+ }
+
+ /**
+ * Gets the tags used for the compiler.
+ *
+ * @param bool $escaped
+ * @return array
+ */
+ protected function getTags($escaped = false): array
+ {
+ $tags = $escaped ? $this->escapedTags : $this->contentTags;
+ return \array_map('stripcslashes', $tags);
+ }
+
+ /**
+ * Gets the escaped content tags used for the compiler.
+ *
+ * @return array
+ */
+ public function getEscapedContentTags(): array
+ {
+ return $this->getTags(true);
+ }
+
+ /**
+ * Sets the function used for resolving classes with inject.
+ *
+ * @param callable $function
+ */
+ public function setInjectResolver(callable $function): void
+ {
+ $this->injectResolver = $function;
+ }
+
+ /**
+ * Get the file extension for template files.
+ *
+ * @return string
+ */
+ public function getFileExtension(): string
+ {
+ return $this->fileExtension;
+ }
+
+ /**
+ * Set the file extension for the template files.
+ * It must include the leading dot e.g. ".blade.php"
+ *
+ * @param string $fileExtension Example: .prefix.ext
+ */
+ public function setFileExtension($fileExtension): void
+ {
+ $this->fileExtension = $fileExtension;
+ }
+
+ /**
+ * Get the file extension for template files.
+ *
+ * @return string
+ */
+ public function getCompiledExtension(): string
+ {
+ return $this->compileExtension;
+ }
+
+ /**
+ * Set the file extension for the compiled files.
+ * Including the leading dot for the extension is required, e.g. ".bladec"
+ *
+ * @param $fileExtension
+ */
+ public function setCompiledExtension($fileExtension): void
+ {
+ $this->compileExtension = $fileExtension;
+ }
+ /**
+ * @return string
+ * @see BladeOne::setCompileTypeFileName
+ */
+ public function getCompileTypeFileName(): string
+ {
+ return $this->compileTypeFileName;
+ }
+
+ /**
+ * It determines how the compiled filename will be called.
+ * auto (default mode) the mode is "sha1"
+ * sha1 the filename is converted into a sha1 hash (it's the slow method, but it is safest)
+ * md5 the filename is converted into a md5 hash (it's faster than sha1, and it uses less space)
+ * @param string $compileTypeFileName=['auto','sha1','md5'][$i]
+ * @return BladeOne
+ */
+ public function setCompileTypeFileName(string $compileTypeFileName): BladeOne
+ {
+ $this->compileTypeFileName = $compileTypeFileName;
+ return $this;
+ }
+ /**
+ * Add new loop to the stack.
+ *
+ * @param array|Countable $data
+ * @return void
+ */
+ public function addLoop($data): void
+ {
+ $length = \is_array($data) || $data instanceof Countable ? \count($data) : null;
+ $parent = static::last($this->loopsStack);
+ $this->loopsStack[] = [
+ 'index' => -1,
+ 'iteration' => 0,
+ 'remaining' => isset($length) ? $length + 1 : null,
+ 'count' => $length,
+ 'first' => true,
+ 'even' => true,
+ 'odd' => false,
+ 'last' => isset($length) ? $length == 1 : null,
+ 'depth' => \count($this->loopsStack) + 1,
+ 'parent' => $parent ? (object)$parent : null,
+ ];
+ }
+
+ /**
+ * Increment the top loop's indices.
+ *
+ * @return object
+ */
+ public function incrementLoopIndices(): object
+ {
+ $c = \count($this->loopsStack) - 1;
+ $loop = &$this->loopsStack[$c];
+
+ $loop['index']++;
+ $loop['iteration']++;
+ $loop['first'] = $loop['index'] == 0;
+ $loop['even'] = $loop['index'] % 2 == 0;
+ $loop['odd'] = !$loop['even'];
+ if (isset($loop['count'])) {
+ $loop['remaining']--;
+ $loop['last'] = $loop['index'] == $loop['count'] - 1;
+ }
+ return (object)$loop;
+ }
+
+ /**
+ * Pop a loop from the top of the loop stack.
+ *
+ * @return void
+ */
+ public function popLoop(): void
+ {
+ \array_pop($this->loopsStack);
+ }
+
+ /**
+ * Get an instance of the first loop in the stack.
+ *
+ * @return object|null
+ */
+ public function getFirstLoop(): ?object
+ {
+ return ($last = static::last($this->loopsStack)) ? (object)$last : null;
+ }
+
+ /**
+ * Get the rendered contents of a partial from a loop.
+ *
+ * @param string $view
+ * @param array $data
+ * @param string $iterator
+ * @param string $empty
+ * @return string
+ * @throws Exception
+ */
+ public function renderEach($view, $data, $iterator, $empty = 'raw|'): string
+ {
+ $result = '';
+
+ if (\count($data) > 0) {
+ // If is actually data in the array, we will loop through the data and append
+ // an instance of the partial view to the final result HTML passing in the
+ // iterated value of this data array, allowing the views to access them.
+ foreach ($data as $key => $value) {
+ $data = ['key' => $key, $iterator => $value];
+ $result .= $this->runChild($view, $data);
+ }
+ } elseif (static::startsWith($empty, 'raw|')) {
+ $result = \substr($empty, 4);
+ } else {
+ $result = $this->run($empty);
+ }
+ return $result;
+ }
+
+ /**
+ * Run the blade engine. It returns the result of the code.
+ *
+ * @param string|null $view The name of the cache. Ex: "folder.folder.view" ("/folder/folder/view.blade")
+ * @param array $variables An associative arrays with the values to display.
+ * @return string
+ * @throws Exception
+ */
+ public function run($view = null, $variables = []): string
+ {
+ $mode = $this->getMode();
+
+ if ($view === null) {
+ $view = $this->viewStack;
+ }
+ $this->viewStack = null;
+ if ($view === null) {
+ $this->showError('run', 'BladeOne: view not set', true);
+ return '';
+ }
+
+ $forced = ($mode & 1)!==0; // mode=1 forced:it recompiles no matter if the compiled file exists or not.
+ $runFast = ($mode & 2)!==0; // mode=2 runfast: the code is not compiled neither checked, and it runs directly the compiled
+ $this->sections = [];
+ if ($mode == 3) {
+ $this->showError('run', "we can't force and run fast at the same time", true);
+ }
+ return $this->runInternal($view, $variables, $forced, $runFast);
+ }
+
+ /**
+ * It sets the current view
+ * This value is cleared when it is used (method run).
+ * Example:
+ *
+ * $this->setView('folder.view')->share(['var1'=>20])->run(); // or $this->run('folder.view',['var1'=>20]);
+ *
+ *
+ * @param string $view
+ * @return BladeOne
+ */
+ public function setView($view): BladeOne
+ {
+ $this->viewStack = $view;
+ return $this;
+ }
+
+ /**
+ * It injects a function, an instance, or a method class when a view is called.
+ * It could be stacked. If it sets null then it clears all definitions.
+ * Example:
+ *
+ * $this->composer('folder.view',function($bladeOne) { $bladeOne->share('newvalue','hi there'); });
+ * $this->composer('folder.view','namespace1\namespace2\SomeClass'); // SomeClass must exist, and it must have the
+ * // method 'composer'
+ * $this->composer('folder.*',$instance); // $instance must have the method called 'composer'
+ * $this->composer(); // clear all composer.
+ *
+ *
+ * @param string|array|null $view It could contain wildcards (*). Example: 'aa.bb.cc','*.bb.cc','aa.bb.*','*.bb.*'
+ *
+ * @param callable|string|null $functionOrClass
+ * @return BladeOne
+ */
+ public function composer($view = null, $functionOrClass = null): BladeOne
+ {
+ if ($view === null && $functionOrClass === null) {
+ $this->composerStack = [];
+ return $this;
+ }
+ if (is_array($view)) {
+ foreach ($view as $v) {
+ $this->composerStack[$v] = $functionOrClass;
+ }
+ } else {
+ $this->composerStack[$view] = $functionOrClass;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Start a component rendering process.
+ *
+ * @param string $name
+ * @param array $data
+ * @return void
+ */
+ public function startComponent($name, array $data = []): void
+ {
+ if (\ob_start()) {
+ $this->componentStack[] = $name;
+
+ $this->componentData[$this->currentComponent()] = $data;
+
+ $this->slots[$this->currentComponent()] = [];
+ }
+ }
+
+ /**
+ * Get the index for the current component.
+ *
+ * @return int
+ */
+ protected function currentComponent(): int
+ {
+ return \count($this->componentStack) - 1;
+ }
+
+ /**
+ * Render the current component.
+ *
+ * @return string
+ * @throws Exception
+ */
+ public function renderComponent(): string
+ {
+ //echo "render ";
+ $name = \array_pop($this->componentStack);
+ //return $this->runChild($name, $this->componentData());
+ $cd = $this->componentData();
+ $clean = array_keys($cd);
+ $r = $this->runChild($name, $cd);
+ // we clean variables defined inside the component (so they are garbaged when the component is used)
+ foreach ($clean as $key) {
+ unset($this->variables[$key]);
+ }
+ return $r;
+ }
+
+ /**
+ * Get the data for the given component.
+ *
+ * @return array
+ */
+ protected function componentData(): array
+ {
+ $cs = count($this->componentStack);
+ //echo "";
+ //echo " data: ";
+ //var_dump($this->componentData);
+ //echo " datac: ";
+ //var_dump(count($this->componentStack));
+ return array_merge(
+ $this->componentData[$cs],
+ ['slot' => trim(ob_get_clean())],
+ $this->slots[$cs]
+ );
+ }
+
+ /**
+ * Start the slot rendering process.
+ *
+ * @param string $name
+ * @param string|null $content
+ * @return void
+ */
+ public function slot($name, $content = null): void
+ {
+ if (\count(\func_get_args()) === 2) {
+ $this->slots[$this->currentComponent()][$name] = $content;
+ } elseif (\ob_start()) {
+ $this->slots[$this->currentComponent()][$name] = '';
+
+ $this->slotStack[$this->currentComponent()][] = $name;
+ }
+ }
+
+ /**
+ * Save the slot content for rendering.
+ *
+ * @return void
+ */
+ public function endSlot(): void
+ {
+ static::last($this->componentStack);
+
+ $currentSlot = \array_pop(
+ $this->slotStack[$this->currentComponent()]
+ );
+
+ $this->slots[$this->currentComponent()][$currentSlot] = \trim(\ob_get_clean());
+ }
+
+ /**
+ * @return string
+ */
+ public function getPhpTag(): string
+ {
+ return $this->phpTag;
+ }
+
+ /**
+ * @param string $phpTag
+ */
+ public function setPhpTag($phpTag): void
+ {
+ $this->phpTag = $phpTag;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCurrentUser(): string
+ {
+ return $this->currentUser;
+ }
+
+ /**
+ * @param string $currentUser
+ */
+ public function setCurrentUser($currentUser): void
+ {
+ $this->currentUser = $currentUser;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCurrentRole(): string
+ {
+ return $this->currentRole;
+ }
+
+ /**
+ * @param string $currentRole
+ */
+ public function setCurrentRole($currentRole): void
+ {
+ $this->currentRole = $currentRole;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getCurrentPermission(): array
+ {
+ return $this->currentPermission;
+ }
+
+ /**
+ * @param string[] $currentPermission
+ */
+ public function setCurrentPermission($currentPermission): void
+ {
+ $this->currentPermission = $currentPermission;
+ }
+
+ /**
+ * Returns the current base url without trailing slash.
+ *
+ * @return string
+ */
+ public function getBaseUrl(): string
+ {
+ return $this->baseUrl;
+ }
+
+ /**
+ * It sets the base url and, it also calculates the relative path.
+ * The base url defines the "root" of the project, not always the level of the domain, but it could be
+ * any folder.
+ * This value is used to calculate the relativity of the resources, but it is also used to set the domain.
+ * Note: The trailing slash is removed automatically if it's present.
+ * Note: We should not use arguments or name of the script.
+ * Examples:
+ *
+ * Note:The relative path is calculated when we set the base url.
+ *
+ * @return string
+ * @see BladeOne::setBaseUrl
+ */
+ public function getRelativePath(): string
+ {
+ return $this->relativePath;
+ }
+
+ /**
+ * It gets the full current canonical url.
+ * Example: https://www.mysite.com/aaa/bb/php.php?aa=bb
+ *
+ *
It returns the $this->canonicalUrl value if is not null
+ *
Otherwise, it returns the $this->currentUrl if not null
+ *
Otherwise, the url is calculated with the information sends by the user
+ *
+ *
+ * @return string|null
+ */
+ public function getCanonicalUrl(): ?string
+ {
+ return $this->canonicalUrl ?? $this->getCurrentUrl();
+ }
+
+ /**
+ * It sets the full canonical url.
+ * Example: https://www.mysite.com/aaa/bb/php.php?aa=bb
+ *
+ * @param string|null $canonUrl
+ * @return BladeOne
+ */
+ public function setCanonicalUrl($canonUrl = null): BladeOne
+ {
+ $this->canonicalUrl = $canonUrl;
+ return $this;
+ }
+
+ /**
+ * It gets the full current url
+ * Example: https://www.mysite.com/aaa/bb/php.php?aa=bb
+ *
+ *
It returns the $this->currentUrl if not null
+ *
Otherwise, the url is calculated with the information sends by the user
+ *
+ *
+ * @param bool $noArgs if true then it ignores the arguments.
+ * @return string|null
+ */
+ public function getCurrentUrl($noArgs = false): ?string
+ {
+ $link = $this->currentUrl ?? $this->getCurrentUrlCalculated();
+ if ($noArgs) {
+ $link = @explode('?', $link)[0];
+ }
+ return $link;
+ }
+
+ /**
+ * It sets the full current url.
+ * Example: https://www.mysite.com/aaa/bb/php.php?aa=bb
+ * Note: If the current url is not set, then the system could calculate the current url.
+ *
+ * @param string|null $currentUrl
+ * @return BladeOne
+ */
+ public function setCurrentUrl($currentUrl = null): BladeOne
+ {
+ $this->currentUrl = $currentUrl;
+ return $this;
+ }
+
+ /**
+ * If true then it optimizes the result (it removes tab and extra spaces).
+ *
+ * @param bool $bool
+ * @return BladeOne
+ */
+ public function setOptimize($bool = false): BladeOne
+ {
+ $this->optimize = $bool;
+ return $this;
+ }
+
+ /**
+ * It sets the callback function for authentication. It is used by @can and @cannot
+ *
+ * @param callable $fn
+ */
+ public function setCanFunction(callable $fn): void
+ {
+ $this->authCallBack = $fn;
+ }
+
+ /**
+ * It sets the callback function for authentication. It is used by @canany
+ *
+ * @param callable $fn
+ */
+ public function setAnyFunction(callable $fn): void
+ {
+ $this->authAnyCallBack = $fn;
+ }
+
+ /**
+ * It sets the callback function for errors. It is used by @error
+ *
+ * @param callable $fn
+ */
+ public function setErrorFunction(callable $fn): void
+ {
+ $this->errorCallBack = $fn;
+ }
+
+ //
+ //
+
+ /**
+ * Get the entire loop stack.
+ *
+ * @return array
+ */
+ public function getLoopStack(): array
+ {
+ return $this->loopsStack;
+ }
+
+ /**
+ * It adds a string inside a quoted string
+ * example:
+ *