Skip to content

Commit

Permalink
Merge branch 'master' of github.com:/PhpGt/DomTemplate
Browse files Browse the repository at this point in the history
  • Loading branch information
g105b committed Nov 10, 2023
2 parents acfca16 + 3fd24a9 commit afc9a52
Show file tree
Hide file tree
Showing 21 changed files with 823 additions and 207 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Composer install
uses: php-actions/composer@v6
with:
php_version: 8.1
php_version: 8.2

- name: Archive build
run: mkdir /tmp/github-actions/ && tar -cvf /tmp/github-actions/build.tar ./
Expand Down Expand Up @@ -50,7 +50,7 @@ jobs:
env:
XDEBUG_MODE: cover
with:
php_version: 8.1
php_version: 8.2
php_extensions: xdebug
configuration: test/phpunit/phpunit.xml
bootstrap: vendor/autoload.php
Expand Down Expand Up @@ -97,7 +97,9 @@ jobs:
- name: PHP Static Analysis
uses: php-actions/phpstan@v3
with:
php_version: 8.2
path: src/
level: 6

remove_old_artifacts:
runs-on: ubuntu-latest
Expand Down
46 changes: 34 additions & 12 deletions src/BindableCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use ReflectionNamedType;
use ReflectionObject;
use ReflectionProperty;
use ReflectionType;
use stdClass;
use Stringable;

Expand Down Expand Up @@ -69,8 +70,7 @@ public function isBindable(object $object):bool {

foreach($refAttributes as $refAttr) {
$bindKey = $this->getBindKey($refAttr, $refMethod);
$attributeCache[$bindKey] = fn(object $object):null|iterable|string
=> $this->nullableStringOrIterable($object->$methodName());
$attributeCache[$bindKey] = fn(object $object):null|iterable|string => $this->nullableStringOrIterable($object->$methodName());
if(class_exists($refReturnName)) {
$cacheObjectKeys[$bindKey] = $refReturnName;
}
Expand All @@ -91,11 +91,17 @@ public function isBindable(object $object):bool {
elseif($refProp->isPublic()) {
$bindKey = $propName;

/** @var ?ReflectionNamedType $refType */
$refTypeName = null;
$refType = $refProp->getType();
$refTypeName = $refType?->getName();
$attributeCache[$bindKey]
= fn(object $object, $key):null|iterable|string => isset($object->$key) ? $this->nullableStringOrIterable($object->$key) : null;
if($refType instanceof ReflectionNamedType) {
$refTypeName = $refType->getName();
}
$attributeCache[$bindKey] =
fn(object $object, $key):null|iterable|string =>
isset($object->$key)
? $this->nullableStringOrIterable($object->$key)
: null;

if(class_exists($refTypeName)) {
$cacheObjectKeys[$bindKey] = $refTypeName;
}
Expand Down Expand Up @@ -143,9 +149,15 @@ private function expandObjects(array $cache, array $objectKeys):array {
return $cache;
}

/** @return array<string, string> */
public function convertToKvp(object $object):array {
/**
* @param object|array<string, string> $object
* @return array<string, string>
*/
public function convertToKvp(object|array $object):array {
$kvp = [];
if(is_array($object)) {
return $object;
}

if($object instanceof stdClass) {
foreach(get_object_vars($object) as $key => $value) {
Expand Down Expand Up @@ -178,10 +190,21 @@ public function convertToKvp(object $object):array {
// TODO: This "get*()" function should not be hard coded here - it should load the appropriate
// Bind/BindGetter by matching the correct Attribute.
$bindFunc = "get" . ucfirst($propName);
$objectToExtract = $objectToExtract->$propName ?? $objectToExtract->$bindFunc();
if($objectToExtract) {
if(property_exists($objectToExtract, $propName)) {
$objectToExtract = $objectToExtract->$propName;
}
elseif(method_exists($objectToExtract, $bindFunc)) {
$objectToExtract = $objectToExtract->$bindFunc();
}
}
}

$value = null;
if($objectToExtract) {
$value = $closure($objectToExtract, $deepestKey);
}

$value = $closure($objectToExtract, $deepestKey);
if(is_null($value)) {
$kvp[$key] = null;
}
Expand All @@ -200,8 +223,7 @@ public function convertToKvp(object $object):array {
private function getBindAttributes(ReflectionMethod|ReflectionProperty $ref):array {
return array_filter(
$ref->getAttributes(),
fn(ReflectionAttribute $refAttr) =>
$refAttr->getName() === Bind::class
fn(ReflectionAttribute $refAttr) => $refAttr->getName() === Bind::class
|| $refAttr->getName() === BindGetter::class
);
}
Expand Down
53 changes: 53 additions & 0 deletions src/Binder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
namespace Gt\DomTemplate;

use Gt\Dom\Element;

abstract class Binder {
abstract public function bindValue(
mixed $value,
?Element $context = null
):void;

/**
* Applies the string value of $value to any elements within $context
* that have the data-bind attribute matching the provided key.
*/
abstract public function bindKeyValue(
string $key,
mixed $value,
?Element $context = null
):void;

/**
* Binds multiple key-value-pairs to any matching elements within
* the $context element.
*/
abstract public function bindData(
mixed $kvp,
?Element $context = null
):void;

abstract public function bindTable(
mixed $tableData,
?Element $context = null,
?string $bindKey = null
):void;

/**
* @param iterable<int, mixed> $listData
*/
abstract public function bindList(
iterable $listData,
?Element $context = null,
?string $templateName = null
):int;

/** @param iterable<int, mixed> $listData */
abstract public function bindListCallback(
iterable $listData,
callable $callback,
?Element $context = null,
?string $templateName = null
):int;
}
4 changes: 3 additions & 1 deletion src/CommentIni.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ public function __construct(
$data = trim($commentNode->data);

try {
$ini = parse_ini_string($data, true);
// We know that sometimes this data will not be correct ini format, and it might actually be a textual comment.
// Therefore, we must suppress the warning that is emitted by parse_ini_string:
$ini = @parse_ini_string($data, true);
$commentNodeToRemove = $commentNode;
}
catch(Throwable) {
Expand Down
56 changes: 56 additions & 0 deletions src/ComponentBinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php
namespace Gt\DomTemplate;

use Gt\Dom\Document;
use Gt\Dom\Element;

class ComponentBinder extends DocumentBinder {
private Element $componentElement;

public function setComponentBinderDependencies(Element $componentElement):void {
$this->componentElement = $componentElement;
}

public function bindList(
iterable $listData,
?Element $context = null,
?string $templateName = null
):int {
if($context) {
$this->checkElementContainedWithinComponent($context);
}
else {
$context = $this->componentElement;
}

return parent::bindList($listData, $context, $templateName);
}

protected function bind(
?string $key,
mixed $value,
?Element $context = null
):void {
if($context) {
$this->checkElementContainedWithinComponent($context);
}
else {
$context = $this->componentElement;
}

if(is_callable($value) && !is_string($value)) {
$value = call_user_func($value);
}

$this->elementBinder->bind($key, $value, $context);
$this->placeholderBinder->bind($key, $value, $context);
}

private function checkElementContainedWithinComponent(Element $context):void {
if($this->componentElement !== $context && !$this->componentElement->contains($context)) {
throw new ComponentDoesNotContainContextException(
"<{$this->componentElement->tagName}> does not contain requested <$context->tagName>."
);
}
}
}
4 changes: 4 additions & 0 deletions src/ComponentDoesNotContainContextException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
namespace Gt\DomTemplate;

class ComponentDoesNotContainContextException extends DomTemplateException {}
62 changes: 26 additions & 36 deletions src/DocumentBinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,32 @@
use Gt\Dom\Document;
use Gt\Dom\Element;

class DocumentBinder {
private ElementBinder $elementBinder;
private PlaceholderBinder $placeholderBinder;
private TableBinder $tableBinder;
private ListBinder $listBinder;
private ListElementCollection $templateCollection;
private BindableCache $bindableCache;
class DocumentBinder extends Binder {
protected ElementBinder $elementBinder;
protected PlaceholderBinder $placeholderBinder;
protected TableBinder $tableBinder;
protected ListBinder $listBinder;
protected ListElementCollection $templateCollection;
protected BindableCache $bindableCache;

/**
* @param array<string, string> $config
*/
public function __construct(
private readonly Document $document,
private array $config = [],
?ElementBinder $elementBinder = null,
?PlaceholderBinder $placeholderBinder = null,
?TableBinder $tableBinder = null,
?ListBinder $listBinder = null,
?ListElementCollection $templateCollection = null,
?BindableCache $bindableCache = null
) {
$this->templateCollection = $templateCollection ?? new ListElementCollection($document);
$this->elementBinder = $elementBinder ?? new ElementBinder();
$this->placeholderBinder = $placeholderBinder ?? new PlaceholderBinder();
$this->tableBinder = $tableBinder ?? new TableBinder($this->templateCollection);
$this->listBinder = $listBinder ?? new ListBinder($this->templateCollection);
$this->bindableCache = $bindableCache ?? new BindableCache();

// This is temporary, to suppress PHPStan's complaints for declaring a variable
// without using it. There are plans to use the config variable, but it is
// currently not yet used, and this technique prevents the constructor
// parameters from changing over time.
if(!$this->config) {
$this->config = [];
}
protected readonly Document $document,
) {}

public function setDependencies(
ElementBinder $elementBinder,
PlaceholderBinder $placeholderBinder,
TableBinder $tableBinder,
ListBinder $listBinder,
ListElementCollection $listElementCollection,
BindableCache $bindableCache,
):void {
$this->elementBinder = $elementBinder;
$this->placeholderBinder = $placeholderBinder;
$this->tableBinder = $tableBinder;
$this->listBinder = $listBinder;
$this->templateCollection = $listElementCollection;
$this->bindableCache = $bindableCache;
}

/**
Expand Down Expand Up @@ -87,7 +78,7 @@ public function bindData(
}
}

foreach($kvp as $key => $value) {
foreach($kvp ?? [] as $key => $value) {
$this->bindKeyValue($key, $value, $context);
}
}
Expand Down Expand Up @@ -163,7 +154,7 @@ public function cleanupDocument():void {
}
}

private function bind(
protected function bind(
?string $key,
mixed $value,
?Element $context = null
Expand All @@ -177,7 +168,6 @@ private function bind(
}

$this->elementBinder->bind($key, $value, $context);
$this->placeholderBinder->bind($key, $value, $context);
}

private function isIndexedArray(mixed $data):bool {
Expand Down
16 changes: 8 additions & 8 deletions src/ElementBinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ class ElementBinder {
private HTMLAttributeCollection $htmlAttributeCollection;
private PlaceholderBinder $placeholderBinder;

public function __construct(
?HTMLAttributeBinder $htmlAttributeBinder = null,
?HTMLAttributeCollection $htmlAttributeCollection = null,
?PlaceholderBinder $placeholderBinder = null,
) {
$this->htmlAttributeBinder = $htmlAttributeBinder ?? new HTMLAttributeBinder();
$this->htmlAttributeCollection = $htmlAttributeCollection ?? new HTMLAttributeCollection();
$this->placeholderBinder = $placeholderBinder ?? new PlaceholderBinder();
public function setDependencies(
HTMLAttributeBinder $htmlAttributeBinder,
HTMLAttributeCollection $htmlAttributeCollection,
PlaceholderBinder $placeholderBinder,
):void {
$this->htmlAttributeBinder = $htmlAttributeBinder;
$this->htmlAttributeCollection = $htmlAttributeCollection;
$this->placeholderBinder = $placeholderBinder;
}

/**
Expand Down
10 changes: 7 additions & 3 deletions src/HTMLAttributeBinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@
use Gt\Dom\Element;

class HTMLAttributeBinder {
private ListBinder $listBinder;
private TableBinder $tableBinder;

public function setDependencies(ListBinder $listBinder, TableBinder $tableBinder):void {
$this->listBinder = $listBinder;
$this->tableBinder = $tableBinder;
}

public function bind(
?string $key,
mixed $value,
Expand Down Expand Up @@ -202,9 +208,6 @@ private function setBindProperty(
break;

case "table":
if(!isset($this->tableBinder)) {
$this->tableBinder = new TableBinder();
}
$this->tableBinder->bindTableData(
$bindValue,
$element,
Expand All @@ -217,6 +220,7 @@ private function setBindProperty(
break;

case "list";
$this->listBinder->bindListData($bindValue, $element);
break;

default:
Expand Down
Loading

0 comments on commit afc9a52

Please sign in to comment.