Skip to content

Commit

Permalink
Merge pull request #13 from sroehrl/interpreter
Browse files Browse the repository at this point in the history
Interpreter
  • Loading branch information
sroehrl authored Aug 15, 2022
2 parents bfd1c04 + 30d35a5 commit 9519601
Show file tree
Hide file tree
Showing 16 changed files with 2,827 additions and 464 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ env:
language: php
dist: bionic
php:
- '7.4'
- '8.0'
- '8.1.0'

Expand Down
109 changes: 109 additions & 0 deletions Attributes/NFor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

namespace Neoan3\Apps\Template\Attributes;

use Neoan3\Apps\Template\Constants;
use Neoan3\Apps\Template\Interpreter;

class NFor
{
private array $contextData;

private array $currentDeclaration;

private string $currentParentKey;

private array $currentContext;
private array $flatData;

private function separateExpression(string $exp):?int
{
$split = explode(' as ', $exp);
$processable = isset($this->flatData[$split[0]]) && $this->flatData[$split[0]] === 'Array';
if(!$processable){
return null;
}
//current parent
$this->currentParentKey = $split[0];
// current declaration
$this->createCurrentDeclaration(explode(' ', $split[1]));
return $this->flatData[$split[0].'.length'];
}

private function createCurrentDeclaration(array $parts):void
{
foreach ($parts as $key => $value){
$parts[$key] = trim($value);
}
$this->currentDeclaration = $parts;
}

private function generateCurrentContext(int $iterator):void
{
$newContextCandidate = $this->contextData[$this->currentParentKey];
$key = array_keys($newContextCandidate)[$iterator];

if(!is_numeric($key)){
$this->assocContext($key, $newContextCandidate[$key]);
} elseif(is_array($newContextCandidate[$key])) {
$this->sequentialContext($key, $newContextCandidate[$key]);
} else {
$this->contextData[$this->currentDeclaration[0]] = $key;
$this->contextData[end($this->currentDeclaration)] = $newContextCandidate[$key];
}

}
private function sequentialContext(string $key, array $value): void
{
$finalInner = [];
foreach ($value as $existingKey => $existingValue){
$finalInner = [
$existingKey => $existingValue
];
if(count($this->currentDeclaration)>1){
$finalInner[$this->currentDeclaration[0]] = $key;
}
}
$this->contextData[end($this->currentDeclaration)] = $finalInner;
}
private function assocContext(string $key, mixed $value):void
{

$keyName = $this->currentDeclaration[0];
$valueName = end($this->currentDeclaration);
$this->contextData[$keyName] = $key;
$this->contextData[$valueName] = $value;
}


function __invoke(\DOMAttr &$attr, $contextData = []): void
{
$this->contextData = $contextData;
$this->flatData = Constants::flattenArray($contextData);

$clone = $attr->parentNode->cloneNode(true);
$clone->removeAttribute($attr->name);

if ($iterations = $this->separateExpression($attr->nodeValue)) {

for ($i = 0; $i < $iterations; $i++) {
$this->generateCurrentContext($i);

$newDoc = new Interpreter($attr->ownerDocument->saveHTML($clone), $this->contextData, true);
$html = $newDoc->asHtml();
if(!empty(trim($html))){
$fresh = new \DOMDocument();
$fresh->loadHTML( $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$imported = $attr->ownerDocument->importNode($fresh->documentElement, true);
$attr->parentNode->parentNode->appendChild($imported);
}

}
// remove original
$attr->parentNode->parentNode->removeChild($attr->parentNode);

}
}


}
78 changes: 78 additions & 0 deletions Attributes/NIf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace Neoan3\Apps\Template\Attributes;

use Error;
use Exception;
use Neoan3\Apps\Template\Constants;
use ParseError;

class NIf
{
private bool $hit = false;
function __construct()
{

}

/**
* @throws Exception
*/
function __invoke(\DOMAttr &$attr, $contextData = []):void
{
$this->hit = false;
$contextData = Constants::flattenArray($contextData);
$toString = $attr->nodeValue;
preg_match_all("/([\p{L}a-z0-9.]+)/", $toString, $matches, PREG_SET_ORDER);
foreach ($matches as $match){
if(array_key_exists($match[0], $contextData)) {
$toString = str_replace($match[0], $this->typeCheck($contextData[$match[0]]), $toString);

$this->hit = true;
}

}
$this->evaluateCustomFunctions($toString);
if(!$this->hit){
return;
}
try{
$result = eval("return $toString;");

} catch (ParseError|Error $e) {
return;
}
if(!$result){
$attr->parentNode->parentNode->removeChild($attr->parentNode);
} else {

$attr->parentNode->removeAttribute($attr->name);
}

}
function evaluateCustomFunctions(string &$currentString):void
{
foreach (Constants::getCustomFunctions() as $key => $execution){
$pattern = "/($key\(([^\)]*)\))/";
$currentString = preg_replace_callback($pattern, function($matches) use($key, $currentString){
$this->hit = true;
if(!empty($matches[2])){
return Constants::getCustomFunctions()[$key]($matches[2]);
}
return Constants::getCustomFunctions()[$key]();
}, $currentString);
}

}

function typeCheck(mixed $value): mixed
{
switch (gettype($value)){
case 'boolean': $value = $value ? 'true' : 'false'; break;
case 'string': $value = "'$value'"; break;
case 'NULL': $value = 'null'; break;
}

return $value;
}
}
152 changes: 152 additions & 0 deletions Constants.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<?php

namespace Neoan3\Apps\Template;

/**
*
*/
class Constants
{
/**
* @var array
*/
private static array $customAttributes = [];
/**
* @var string
*/
private static string $encoding = 'utf-8';
/**
* @var array
*/
private static array $customFunctions = [];
/**
* @var array|string[]
*/
private static array $delimiter = ['{{','}}'];
/**
* @var string
*/
private static string $path;

/**
* @return string
*/
public static function getPath(): string
{
if(!isset(self::$path)){
self::$path = dirname(__DIR__,3);
}
return self::$path;
}

/**
* @return array
*/
public static function getCustomAttributes(): array
{
return self::$customAttributes;
}

/**
* @return string
*/
public static function getEncoding(): string
{
return self::$encoding;
}

/**
* @return array
*/
public static function getCustomFunctions(): array
{
return self::$customFunctions;
}

/**
* @return array
*/
public static function getDelimiter(): array
{
return self::$delimiter;
}

public static function delimiterIsTag(): bool
{
return preg_match('/^<([^>]+)>$/',self::$delimiter[0]) === 1;
}


/**
* @param string $key
* @param callable $attributeInstance
* @return void
*/
public static function addCustomAttribute(string $key, callable $attributeInstance): void
{
self::$customAttributes[$key] = $attributeInstance;
}

/**
* @param string $encoding
*/
public static function setEncoding(string $encoding): void
{
self::$encoding = $encoding;
}


/**
* @param string $key
* @param callable $customFunction
* @return void
*/
public static function addCustomFunction(string $key, callable $customFunction): void
{
self::$customFunctions[$key] = $customFunction;
}


/**
* @param string $start
* @param string $end
* @return void
*/
public static function setDelimiter(string $start = '{{', string $end = '}}'): void
{
self::$delimiter = [$start, $end];
}

/**
* @param string $path
* @return void
*/
public static function setPath(string $path): void
{
self::$path = $path;
}


/**
* @param iterable $data
* @param string|null $parentKey
* @return array
*/
public static function flattenArray(iterable $data, string $parentKey = null): array
{
$answer = [];
foreach ($data as $key => $value) {
if ($parentKey) {
$key = $parentKey . '.' . $key;
}
if (!is_array($value)) {
$answer[$key] = $value;
} else {
$answer[$key] = 'Array';
$answer[$key.'.length'] = sizeof($value);
$answer = array_merge($answer, self::flattenArray($value, $key));
}
}
return $answer;
}
}
Loading

0 comments on commit 9519601

Please sign in to comment.