* @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
* $this->compileCallbacks[]= static function (&$content, $templatename=null) {
* $content=strtoupper($content);
* };
*
*/
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 null 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 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)';
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 */
private $switchCount = 0;
/** @var bool Indicates if the switch is recently open */
private $firstCaseInSwitch = true;
//
* $this->wrapPHP('$hello'); // "< ?php echo $this->e($hello); ? >"
* $this->wrapPHP('$hello',''); // < ?php echo $this->e($hello); ? >
* $this->wrapPHP('$hello','',false); // < ?php echo $hello; ? >
* $this->wrapPHP('"hello"'); // "< ?php echo $this->e("hello"); ? >"
* $this->wrapPHP('hello()'); // "< ?php echo $this->e(hello()); ? >"
*
*
* @param string $input The input value
* @param string $quote The quote used (to quote the result)
* @param bool $parse If the result will be parsed or not. If false then it's returned without $this->e
* @return string
*/
public function wrapPHP($input, $quote = '"', $parse = true)
{
if (strpos($input, '(') !== false && !$this->isQuoted($input)) {
if ($parse) {
return $quote . $this->phpTagEcho . '$this->e(' . $input . ');?>' . $quote;
}
return $quote . $this->phpTagEcho . $input . ';?>' . $quote;
}
if (strpos($input, '$') === false) {
if ($parse) {
return self::enq($input);
}
return $input;
}
if ($parse) {
return $quote . $this->phpTagEcho . '$this->e(' . $input . ');?>' . $quote;
}
return $quote . $this->phpTagEcho . $input . ';?>' . $quote;
}
/**
* Returns true if the text is surrounded by quotes (double or single quote)
*
* @param string|null $text
* @return bool
*/
public function isQuoted($text)
{
if (!$text || strlen($text) < 2) {
return false;
}
if ($text[0] === '"' && substr($text, -1) === '"') {
return true;
}
return ($text[0] === "'" && substr($text, -1) === "'");
}
/**
* Escape HTML entities in a string.
*
* @param string $value
* @return string
*/
public static function enq($value)
{
if (\is_array($value) || \is_object($value)) {
return \htmlentities(\print_r($value, true), ENT_NOQUOTES, 'UTF-8', false);
}
return \htmlentities($value, ENT_NOQUOTES, 'UTF-8', false);
}
/**
* @param string $view example "folder.template"
* @param string|null $alias example "mynewop". If null then it uses the name of the template.
*/
public function addInclude($view, $alias = null)
{
if (!isset($alias)) {
$alias = \explode('.', $view);
$alias = \end($alias);
}
$this->directive($alias, function ($expression) use ($view) {
$expression = $this->stripParentheses($expression) ?: '[]';
return "$this->phpTag echo \$this->runChild('$view', $expression); ?>";
});
}
/**
* Register a handler for custom directives.
*
* @param string $name
* @param callable $handler
* @return void
*/
public function directive($name, callable $handler)
{
$this->customDirectives[$name] = $handler;
$this->customDirectivesRT[$name] = false;
}
/**
* Strip the parentheses from the given expression.
*
* @param string $expression
* @return string
*/
public function stripParentheses($expression)
{
if (static::startsWith($expression, '(')) {
$expression = \substr($expression, 1, -1);
}
return $expression;
}
/**
* Determine if a given string starts with a given substring.
*
* @param string $haystack
* @param string|array $needles
* @return bool
*/
public static function startsWith($haystack, $needles)
{
foreach ((array)$needles as $needle) {
if ($needle != '') {
if (\function_exists('mb_strpos')) {
if (\mb_strpos($haystack, $needle) === 0) {
return true;
}
} elseif (\strpos($haystack, $needle) === 0) {
return true;
}
}
}
return false;
}
/**
* If false then the file is not compiled, and it is executed directly from the memory.Example:setPath("somefolder","otherfolder"); * * @param null|string|string[] $templatePath If null then it uses the current path /views folder * @param null|string $compiledPath If null then it uses the current path /views folder */ public function setPath($templatePath, $compiledPath) { if ($templatePath === null) { $templatePath = \getcwd() . '/views'; } if ($compiledPath === null) { $compiledPath = \getcwd() . '/compiles'; } $this->templatePath = (is_array($templatePath)) ? $templatePath : [$templatePath]; $this->compiledPath = $compiledPath; } /** * @return array */ public function getAliasClasses() { return $this->aliasClasses; } /** * @param array $aliasClasses */ public function setAliasClasses($aliasClasses) { $this->aliasClasses = $aliasClasses; } /** * @param string $aliasName * @param string $classWithNS */ public function addAliasClasses($aliasName, $classWithNS) { $this->aliasClasses[$aliasName] = $classWithNS; } //
* Text::wildCardComparison('abcdef','abc*'); // true
* Text::wildCardComparison('abcdef','*def'); // true
* Text::wildCardComparison('abcdef','*abc*'); // true
* Text::wildCardComparison('abcdef','*cde*'); // true
* Text::wildCardComparison('abcdef','*cde'); // false
*
*
*
* @param string $text
* @param string|null $textWithWildcard
*
* @return bool
*/
protected function wildCardComparison($text, $textWithWildcard)
{
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)
{
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);
$dir = \dirname($compiled);
if (!\is_dir($dir)) {
$ok = @\mkdir($dir, 0777, true);
if ($ok === false) {
$this->showError(
'Compiling',
"Unable to create the compile folder [$dir]. Check the permissions of it's parent folder.",
true
);
return false;
}
}
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 = '')
{
$templateName = (empty($templateName)) ? $this->fileName : $templateName;
if ($this->getMode() == self::MODE_DEBUG) {
return $this->compiledPath . '/' . $templateName . $this->compileExtension;
}
return $this->compiledPath . '/' . \sha1($templateName) . $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()
{
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)
{
$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 = '') { $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 */ private function locateTemplate($name) { $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) { 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) { 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) { $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) { \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) { \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) { \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 = []) { 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 */ private function templateExist($templateName) { $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) { 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') { 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') { 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.';
\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 = '')
{
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()
{
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 \eftec\bladeone\BladeOne::share
*/
public function with($varname, $value = null)
{
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)
{
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 = '')
{
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)
{
$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)
{
$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)
{
$this->setContentTags($openTag, $closeTag, true);
}
/**
* Gets the content tags used for the compiler.
*
* @return array
*/
public function getContentTags()
{
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)
{
$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)
{
$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()
{
return $this->getTags(true);
}
/**
* Sets the function used for resolving classes with inject.
*
* @param callable $function
*/
public function setInjectResolver(callable $function)
{
$this->injectResolver = $function;
}
/**
* Get the file extension for template files.
*
* @return string
*/
public function getFileExtension()
{
return $this->fileExtension;
}
/**
* Set the file extension for the template files.
* It must includes the leading dot e.g. .blade.php
*
* @param string $fileExtension Example: .prefix.ext
*/
public function setFileExtension($fileExtension)
{
$this->fileExtension = $fileExtension;
}
/**
* Get the file extension for template files.
*
* @return string
*/
public function getCompiledExtension()
{
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)
{
$this->compileExtension = $fileExtension;
}
/**
* Add new loop to the stack.
*
* @param array|Countable $data
* @return void
*/
public function addLoop($data)
{
$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()
{
$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()
{
\array_pop($this->loopsStack);
}
/**
* Get an instance of the first loop in the stack.
*
* @return object
*/
public function getFirstLoop()
{
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|')
{
$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 = [])
{
$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; // mode=1 forced:it recompiles no matter if the compiled file exists or not.
$runFast = $mode & 2; // 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, true, $runFast);
}
/**
* It sets the current view
* $this->setView('folder.view')->share(['var1'=>20])->run(); // or $this->run('folder.view',['var1'=>20]);
*
*
* @param string $view
* @return BladeOne
*/
public function setView($view)
{
$this->viewStack = $view;
return $this;
}
/**
* It injects a function, an instance, or a method class when a view is called.
* $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 contains wildcards (*). Example: 'aa.bb.cc','*.bb.cc','aa.bb.*','*.bb.*'
*
* @param callable|string|null $functionOrClass
* @return BladeOne
*/
public function composer($view = null, $functionOrClass = null)
{
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 = [])
{
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()
{
return \count($this->componentStack) - 1;
}
/**
* Render the current component.
*
* @return string
* @throws Exception
*/
public function renderComponent()
{
//echo "
* $this->setBaseUrl('http://domain.dom/myblog');
* $this->setBaseUrl('http://domain.dom/corporate/erp');
* $this->setBaseUrl('http://domain.dom/blog.php?args=20'); // avoid this one.
* $this->setBaseUrl('http://another.dom');
*
*
* @param string $baseUrl Example http://www.web.com/folder https://www.web.com/folder/anotherfolder
* @return BladeOne
*/
public function setBaseUrl($baseUrl)
{
$this->baseUrl = \rtrim($baseUrl, '/'); // base with the url trimmed
$this->baseDomain = @parse_url($this->baseUrl)['host'];
$currentUrl = $this->getCurrentUrlCalculated();
if ($currentUrl === '') {
$this->relativePath = '';
return $this;
}
if (\strpos($currentUrl, $this->baseUrl) === 0) {
$part = \str_replace($this->baseUrl, '', $currentUrl);
$numf = \substr_count($part, '/') - 1;
$numf = ($numf > 10) ? 10 : $numf; // avoid overflow
$this->relativePath = ($numf < 0) ? '' : \str_repeat('../', $numf);
} else {
$this->relativePath = '';
}
return $this;
}
/**
* It gets the full current url calculated with the information sends by the user.
* // current url='http://domain.dom/page/subpage/web.php?aaa=2
* $this->setBaseUrl('http://domain.dom/');
* $this->getRelativePath(); // '../../'
* $this->setBaseUrl('http://domain.dom/');
* $this->getRelativePath(); // '../../'
*
* Note:The relative path is calculated when we set the base url.
*
* @return string
* @see \eftec\bladeone\BladeOne::setBaseUrl
*/
public function getRelativePath()
{
return $this->relativePath;
}
/**
* It gets the full current canonical url.
* $this->addInsideQuote("'hello'"," world"); // 'hello world'
* $this->addInsideQuote("hello"," world"); // hello world
*
*
* @param $quoted
* @param $newFragment
* @return string
*/
public function addInsideQuote($quoted, $newFragment)
{
if ($this->isQuoted($quoted)) {
return substr($quoted, 0, -1) . $newFragment . substr($quoted, -1);
}
return $quoted . $newFragment;
}
/**
* Return true if the string is a php variable (it starts with $)
*
* @param string|null $text
* @return bool
*/
public function isVariablePHP($text)
{
if (!$text || strlen($text) < 2) {
return false;
}
return $text[0] === '$';
}
/**
* It's the same as "@_e", however it parses the text (using sprintf).
* If the operation fails then, it returns the original expression without translation.
*
* @param $phrase
*
* @return string
*/
public function _ef($phrase)
{
$argv = \func_get_args();
$r = $this->_e($phrase);
$argv[0] = $r; // replace the first argument with the translation.
$result = @sprintf(...$argv);
return ($result == false) ? $r : $result;
}
/**
* Tries to translate the word if it's in the array defined by BladeOneLang::$dictionary
* If the operation fails then, it returns the original expression without translation.
*
* @param $phrase
*
* @return string
*/
public function _e($phrase)
{
if ((!\array_key_exists($phrase, static::$dictionary))) {
$this->missingTranslation($phrase);
return $phrase;
}
return static::$dictionary[$phrase];
}
/**
* Log a missing translation into the file $this->missingLog.'; var_dump$expression; echo '';?>"; } /** * Execute the case tag. * * @param $expression * @return string */ protected function compileCase($expression) { if ($this->firstCaseInSwitch) { $this->firstCaseInSwitch = false; return 'case ' . $expression . ': ?>'; } return $this->phpTag . "case $expression: ?>"; } /** * Compile the while statements into valid PHP. * * @param string $expression * @return string */ protected function compileWhile($expression) { return $this->phpTag . "while$expression: ?>"; } /** * default tag used for switch/case * * @return string */ protected function compileDefault() { if ($this->firstCaseInSwitch) { return $this->showError('@default', '@switch without any @case', true); } return $this->phpTag . 'default: ?>'; } protected function compileEndSwitch() { --$this->switchCount; if ($this->switchCount < 0) { return $this->showError('@endswitch', 'Missing @switch', true); } return $this->phpTag . '} // end switch ?>'; } /** * Compile while statements into valid PHP. * * @param string $expression * @return string */ protected function compileInject($expression) { $ex = $this->stripParentheses($expression); $p0 = \strpos($ex, ','); if ($p0 == false) { $var = $this->stripQuotes($ex); $namespace = ''; } else { $var = $this->stripQuotes(\substr($ex, 0, $p0)); $namespace = $this->stripQuotes(\substr($ex, $p0 + 1)); } return $this->phpTag . "\$$var = \$this->injectClass('$namespace', '$var'); ?>"; } /** * Remove first and end quote from a quoted string of text * * @param mixed $text * @return null|string|string[] */ public function stripQuotes($text) { if (!$text || strlen($text) < 2) { return $text; } $text = trim($text); $p0 = $text[0]; $p1 = \substr($text, -1); if ($p0 === $p1 && ($p0 === '"' || $p0 === "'")) { return \substr($text, 1, -1); } return $text; } /** * Execute the user defined extensions. * * @param string $value * @return string */ protected function compileExtensions($value) { foreach ($this->extensions as $compiler) { $value = $compiler($value, $this); } return $value; } /** * Compile Blade comments into valid PHP. * * @param string $value * @return string */ protected function compileComments($value) { $pattern = \sprintf('/%s--(.*?)--%s/s', $this->contentTags[0], $this->contentTags[1]); return \preg_replace($pattern, $this->phpTag . '/*$1*/ ?>', $value); } /** * Compile Blade echos into valid PHP. * * @param string $value * @return string */ protected function compileEchos($value) { foreach ($this->getEchoMethods() as $method => $length) { $value = $this->$method($value); } return $value; } /** * Get the echo methods in the proper order for compilation. * * @return array */ protected function getEchoMethods() { $methods = [ 'compileRawEchos' => \strlen(\stripcslashes($this->rawTags[0])), 'compileEscapedEchos' => \strlen(\stripcslashes($this->escapedTags[0])), 'compileRegularEchos' => \strlen(\stripcslashes($this->contentTags[0])), ]; \uksort($methods, static function ($method1, $method2) use ($methods) { // Ensure the longest tags are processed first if ($methods[$method1] > $methods[$method2]) { return -1; } if ($methods[$method1] < $methods[$method2]) { return 1; } // Otherwise, give preference to raw tags (assuming they've overridden) if ($method1 === 'compileRawEchos') { return -1; } if ($method2 === 'compileRawEchos') { return 1; } if ($method1 === 'compileEscapedEchos') { return -1; } if ($method2 === 'compileEscapedEchos') { return 1; } throw new BadMethodCallException("Method [$method1] not defined"); }); return $methods; } /** * Compile Blade statements that start with "@". * * @param string $value * * @return array|string|string[]|null */ protected function compileStatements($value) { /** * @param array $match * [0]=full expression with @ and parenthesis * [1]=expression without @ and argument * [2]=???? * [3]=argument with parenthesis and without the first @ * [4]=argument without parenthesis. * * @return mixed|string */ $callback = function ($match) { if (static::contains($match[1], '@')) { // @@escaped tag $match[0] = isset($match[3]) ? $match[1] . $match[3] : $match[1]; } else { if (strpos($match[1], '::') !== false) { // Someclass::method return $this->compileStatementClass($match); } if (isset($this->customDirectivesRT[$match[1]])) { if ($this->customDirectivesRT[$match[1]] == true) { $match[0] = $this->compileStatementCustom($match); } else { $match[0] = \call_user_func( $this->customDirectives[$match[1]], $this->stripParentheses(static::get($match, 3)) ); } } elseif (\method_exists($this, $method = 'compile' . \ucfirst($match[1]))) { // it calls the function compile
* $this->parseArgs('a=2,b='a,b,c',d'); // ['a'=>'2','b'=>'a,b,c','d'=>null]
* $this->parseArgs('a=2,b=c,d'); // ['a'=>'2','b'=>'c','d'=>null]
* $this->parseArgs('a=2 b=c',' '); // ['a'=>'2','b'=>'c']
* $this->parseArgs('a:2 b:c',' ',':'); // ['a'=>'2','b'=>'c']
*
* Note: parseArgs('a = 2 b = c',' '); with return 4 values instead of 2.
*
* @param string $text the text to separate
* @param string $separator the separator of arguments
* @param string $assigment the character used to assign a new value
* @param bool $emptyKey if the argument is without value, we return it as key (true) or value (false) ?
* @return array
*/
public function parseArgs($text, $separator = ',', $assigment = '=', $emptyKey = true)
{
if ($text === null || $text === '') {
return []; //nothing to convert.
}
$chars = $text; // str_split($text);
$parts = [];
$nextpart = '';
$strL = strlen($chars);
$stringArr='"\'¬';
$parenthesis='([{';
$parenthesisClose=')]}';
$insidePar=false;
for ($i = 0; $i < $strL; $i++) {
$char = $chars[$i];
// we check if the character is a parenthesis.
$pp=strpos($parenthesis, $char);
if ($pp!==false) {
// is a parenthesis, so we mark as inside a parenthesis.
$insidePar=$parenthesisClose[$pp];
}
if ($char===$insidePar) {
// we close the parenthesis.
$insidePar=false;
}
if (strpos($stringArr, $char)!==false) { // if ($char === '"' || $char === "'" || $char === "¬") {
// we found a string initializer
$inext = strpos($text, $char, $i + 1);
$inext = $inext === false ? $strL : $inext;
$nextpart .= substr($text, $i, $inext - $i + 1);
$i = $inext;
} else {
$nextpart .= $char;
}
if ($char === $separator && $insidePar==false) {
$parts[] = substr($nextpart, 0, -1);
$nextpart = '';
}
}
if ($nextpart !== '') {
$parts[] = $nextpart;
}
$result = [];
// duct taping for key= argument (it has a space). however, it doesn't work with key =argument
/*
foreach ($parts as $k=>$part) {
if(substr($part,-1)===$assigment && isset($parts[$k+1])) {
var_dump('ok');
$parts[$k].=$parts[$k+1];
unset($parts[$k+1]);
}
}
*/
foreach ($parts as $part) {
if ($part) {
$part=trim($part);
$char = $part[0];
if (strpos($stringArr, $char)!==false) { // if ($char === '"' || $char === "'" || $char === "¬") {
if ($emptyKey) {
$result[$part] = null;
} else {
$result[] = $part;
}
} else {
$r = explode($assigment, $part, 2);
if (count($r) === 2) {
// key=value.
$result[trim($r[0])] = trim($r[1]);
} elseif ($emptyKey) {
$result[trim($r[0])] = null;
} else {
$result[] = trim($r[0]);
}
}
}
}
return $result;
}
public function parseArgsOld($text, $separator = ',')
{
if ($text === null || $text === '') {
return []; //nothing to convert.
}
$chars = str_split($text);
$parts = [];
$nextpart = '';
$strL = count($chars);
/** @noinspection ForeachInvariantsInspection */
for ($i = 0; $i < $strL; $i++) {
$char = $chars[$i];
if ($char === '"' || $char === "'") {
$inext = strpos($text, $char, $i + 1);
$inext = $inext === false ? $strL : $inext;
$nextpart .= substr($text, $i, $inext - $i + 1);
$i = $inext;
} else {
$nextpart .= $char;
}
if ($char === $separator) {
$parts[] = substr($nextpart, 0, -1);
$nextpart = '';
}
}
if ($nextpart !== '') {
$parts[] = $nextpart;
}
$result = [];
foreach ($parts as $part) {
$r = explode('=', $part, 2);
$result[trim($r[0])] = count($r) === 2 ? trim($r[1]) : null;
}
return $result;
}
/**
* Compile the "raw" echo statements.
*
* @param string $value
* @return string
*/
protected function compileRawEchos($value)
{
$pattern = \sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->rawTags[0], $this->rawTags[1]);
$callback = function ($matches) {
$whitespace = empty($matches[3]) ? '' : $matches[3] . $matches[3];
return $matches[1] ? \substr(
$matches[0],
1
) : $this->phpTagEcho . $this->compileEchoDefaults($matches[2]) . '; ?>' . $whitespace;
};
return \preg_replace_callback($pattern, $callback, $value);
}
/**
* Compile the default values for the echo statement.
*
* @param string $value
* @return string
*/
protected function compileEchoDefaults($value)
{
$result = \preg_replace('/^(?=\$)(.+?)\s+or\s+(.+?)$/s', 'isset($1) ? $1 : $2', $value);
if (!$this->pipeEnable) {
return $this->fixNamespaceClass($result);
}
return $this->pipeDream($this->fixNamespaceClass($result));
}
/**
* It converts a string separated by pipes | into an filtered expression.
* $this->pipeDream('$name | strtolower | substr:0,4'); // strtolower(substr($name ,0,4)
* $this->pipeDream('$name| getMode') // $this->getMode($name)
*
*
* @param string $result
* @return string
* @\eftec\bladeone\BladeOne::$pipeEnable
*/
protected function pipeDream($result)
{
$array = preg_split('~\\\\.(*SKIP)(*FAIL)|\|~s', $result);
$c = count($array) - 1; // base zero.
if ($c === 0) {
return $result;
}
$prev = '';
for ($i = $c; $i >= 1; $i--) {
$r = @explode(':', $array[$i], 2);
$fnName = trim($r[0]);
$fnNameF = $fnName[0]; // first character
if ($fnNameF === '"' || $fnNameF === '\'' || $fnNameF === '$' || is_numeric($fnNameF)) {
$fnName = '!isset(' . $array[0] . ') ? ' . $fnName . ' : ';
} elseif (isset($this->customDirectives[$fnName])) {
$fnName = '$this->customDirectives[\'' . $fnName . '\']';
} elseif (method_exists($this, $fnName)) {
$fnName = '$this->' . $fnName;
}
if ($i === 1) {
$prev = $fnName . '(' . $array[0];
if (count($r) === 2) {
$prev .= ',' . $r[1];
}
$prev .= ')';
} else {
$prev = $fnName . '(' . $prev;
if (count($r) === 2) {
if ($i === 2) {
$prev .= ',';
}
$prev .= $r[1] . ')';
}
}
}
return $prev;
}
/**
* Compile the "regular" echo statements. {{ }}
*
* @param string $value
* @return string
*/
protected function compileRegularEchos($value)
{
$pattern = \sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->contentTags[0], $this->contentTags[1]);
$callback = function ($matches) {
$whitespace = empty($matches[3]) ? '' : $matches[3] . $matches[3];
$wrapped = \sprintf($this->echoFormat, $this->compileEchoDefaults($matches[2]));
return $matches[1] ? \substr($matches[0], 1) : $this->phpTagEcho . $wrapped . '; ?>' . $whitespace;
};
return \preg_replace_callback($pattern, $callback, $value);
}
/**
* Compile the escaped echo statements. {!! !!}
*
* @param string $value
* @return string
*/
protected function compileEscapedEchos($value)
{
$pattern = \sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->escapedTags[0], $this->escapedTags[1]);
$callback = function ($matches) {
$whitespace = empty($matches[3]) ? '' : $matches[3] . $matches[3];
return $matches[1] ? $matches[0] : $this->phpTag
. \sprintf($this->echoFormat, $this->compileEchoDefaults($matches[2])) . '; ?>'
. $whitespace;
//return $matches[1] ? $matches[0] : $this->phpTag
// . 'echo static::e(' . $this->compileEchoDefaults($matches[2]) . '); ? >' . $whitespace;
};
return \preg_replace_callback($pattern, $callback, $value);
}
/**
* Compile the "@each" tag into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEach($expression)
{
return $this->phpTagEcho . "\$this->renderEach$expression; ?>";
}
protected function compileSet($expression)
{
//$segments = \explode('=', \preg_replace("/[()\\\']/", '', $expression));
$segments = \explode('=', $this->stripParentheses($expression));
$value = (\count($segments) >= 2) ? '=@' . $segments[1] : '++';
return $this->phpTag . \trim($segments[0]) . $value . ';?>';
}
/**
* Compile the yield statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileYield($expression)
{
return $this->phpTagEcho . "\$this->yieldContent$expression; ?>";
}
/**
* Compile the show statements into valid PHP.
*
* @return string
*/
protected function compileShow()
{
return $this->phpTagEcho . '$this->yieldSection(); ?>';
}
/**
* Compile the section statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileSection($expression)
{
return $this->phpTag . "\$this->startSection$expression; ?>";
}
/**
* Compile the append statements into valid PHP.
*
* @return string
*/
protected function compileAppend()
{
return $this->phpTag . '$this->appendSection(); ?>';
}
/**
* Compile the auth statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileAuth($expression = '')
{
$role = $this->stripParentheses($expression);
if ($role == '') {
return $this->phpTag . 'if(isset($this->currentUser)): ?>';
}
return $this->phpTag . "if(isset(\$this->currentUser) && \$this->currentRole==$role): ?>";
}
/**
* Compile the elseauth statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileElseAuth($expression = '')
{
$role = $this->stripParentheses($expression);
if ($role == '') {
return $this->phpTag . 'else: ?>';
}
return $this->phpTag . "elseif(isset(\$this->currentUser) && \$this->currentRole==$role): ?>";
}
/**
* Compile the end-auth statements into valid PHP.
*
* @return string
*/
protected function compileEndAuth()
{
return $this->phpTag . 'endif; ?>';
}
protected function compileCan($expression)
{
$v = $this->stripParentheses($expression);
return $this->phpTag . 'if (call_user_func($this->authCallBack,' . $v . ')): ?>';
}
/**
* Compile the else statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileElseCan($expression = '')
{
$v = $this->stripParentheses($expression);
if ($v) {
return $this->phpTag . 'elseif (call_user_func($this->authCallBack,' . $v . ')): ?>';
}
return $this->phpTag . 'else: ?>';
}
//