TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\TypoScript\AST\Node;
|
||||
|
||||
/**
|
||||
* Generic child node. Implements common methods of NodeInterface used
|
||||
* in all Node classes.
|
||||
*
|
||||
* @internal: Internal AST structure.
|
||||
*/
|
||||
abstract class AbstractChildNode extends AbstractNode implements ChildNodeInterface
|
||||
{
|
||||
public function __construct(protected string $name) {}
|
||||
|
||||
/**
|
||||
* Dereference children on clone().
|
||||
* Used with '<' operator to create a deep-copy of the tree to copy.
|
||||
*/
|
||||
public function __clone(): void
|
||||
{
|
||||
foreach ($this->children as $childName => $child) {
|
||||
$this->children[$childName] = clone $child;
|
||||
}
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function toArray(): ?array
|
||||
{
|
||||
if (!$this->hasChildren()) {
|
||||
return null;
|
||||
}
|
||||
$result = [];
|
||||
foreach ($this->getNextChild() as $child) {
|
||||
$childName = $child->getName();
|
||||
$childValue = $child->getValue();
|
||||
if ($child instanceof ReferenceChildNode) {
|
||||
// Hack for b/w compat parsing of `=<` operator. See ContentObjectRenderer cObjGetSingle() and mergeTSRef()
|
||||
// @todo: adding the whitespace after '<' is another bit of a hack here ... maybe solve in tokenizer?
|
||||
// compare this for what happens when doing 'foo = bar' in old parser: Is the whitespace kept for
|
||||
// value to not trigger the ref lookup to often if doing 'foo = <div...' ?
|
||||
// @todo: same situation in RootNode!
|
||||
$childValue = '< ' . $child->getReferenceSourceStream();
|
||||
}
|
||||
if ($childValue !== null) {
|
||||
$result[$child->getName()] = $childValue;
|
||||
}
|
||||
$grandChildren = $child->toArray();
|
||||
if ($grandChildren !== null) {
|
||||
$result[$childName . '.'] = $grandChildren;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function flatten(string $prefix = ''): array
|
||||
{
|
||||
$flatArray = [];
|
||||
$prefixedQuotedNodeName = $prefix . addcslashes($this->getName(), '.');
|
||||
if (!$this->isValueNull()) {
|
||||
$flatArray[$prefixedQuotedNodeName] = $this->getValue();
|
||||
}
|
||||
foreach ($this->getNextChild() as $child) {
|
||||
$flatArray = array_merge($flatArray, $child->flatten($prefixedQuotedNodeName . '.'));
|
||||
}
|
||||
return $flatArray;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\TypoScript\AST\Node;
|
||||
|
||||
use TYPO3\CMS\Core\TypoScript\Tokenizer\Token\TokenStreamInterface;
|
||||
|
||||
/**
|
||||
* Generic node. Implements common methods of NodeInterface used
|
||||
* in all Node classes.
|
||||
*
|
||||
* @internal: Internal AST structure.
|
||||
*/
|
||||
abstract class AbstractNode implements NodeInterface
|
||||
{
|
||||
private ?string $identifier = null;
|
||||
protected string $name;
|
||||
private ?string $value = null;
|
||||
private ?string $previousValue = null;
|
||||
|
||||
/**
|
||||
* @var array<string, ChildNodeInterface>
|
||||
*/
|
||||
protected array $children = [];
|
||||
private ?TokenStreamInterface $originalValueTokenStream = null;
|
||||
private array $comments = [];
|
||||
|
||||
/**
|
||||
* When storing to cache, we only store FE relevant properties and skip
|
||||
* various BE related properties which then default to class defaults when
|
||||
* unserialized. This is done to create smaller php cache files.
|
||||
*/
|
||||
final public function __serialize(): array
|
||||
{
|
||||
return $this->serialize();
|
||||
}
|
||||
|
||||
protected function serialize(): array
|
||||
{
|
||||
$result = [
|
||||
'name' => $this->name,
|
||||
'children' => $this->children,
|
||||
];
|
||||
if ($this->value !== null) {
|
||||
$result['value'] = $this->value;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function setIdentifier(string $identifier): void
|
||||
{
|
||||
$this->identifier = hash('xxh3', $identifier);
|
||||
$childCounter = 0;
|
||||
foreach ($this->getNextChild() as $child) {
|
||||
$child->setIdentifier($this->identifier . $childCounter);
|
||||
$childCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This forces $this->name NOT to be readonly.
|
||||
* Used with '<' operator on tree root to copy:
|
||||
* foo = value
|
||||
* bar < foo
|
||||
* The 'foo' object node is copied, but added to AST as name 'bar'
|
||||
*/
|
||||
public function updateName(string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
if ($this->identifier === null) {
|
||||
throw new \RuntimeException(
|
||||
'Identifier has not been initialized. This happens when getIdentifier() is called on'
|
||||
. ' trees retrieved from cache. The identifier is not supposed to be used in this context.',
|
||||
1674620169
|
||||
);
|
||||
}
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
public function addChild(ChildNodeInterface $node): void
|
||||
{
|
||||
$this->children[$node->getName()] = $node;
|
||||
}
|
||||
|
||||
public function getChildByName(string $name): ?ChildNodeInterface
|
||||
{
|
||||
return $this->children[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note this does *not* choke if that child does not exist, so we can "blindly" remove without error.
|
||||
*/
|
||||
public function removeChildByName(string $name): void
|
||||
{
|
||||
unset($this->children[$name]);
|
||||
}
|
||||
|
||||
public function hasChildren(): bool
|
||||
{
|
||||
return !empty($this->children);
|
||||
}
|
||||
|
||||
public function getNextChild(): iterable
|
||||
{
|
||||
foreach ($this->children as $child) {
|
||||
yield $child;
|
||||
}
|
||||
}
|
||||
|
||||
public function sortChildren(): void
|
||||
{
|
||||
ksort($this->children, SORT_FLAG_CASE | SORT_STRING);
|
||||
}
|
||||
|
||||
public function setValue(?string $value): void
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function appendValue(string $value): void
|
||||
{
|
||||
$this->value .= $value;
|
||||
}
|
||||
|
||||
public function getValue(): ?string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function isValueNull(): bool
|
||||
{
|
||||
return $this->value === null;
|
||||
}
|
||||
|
||||
public function setPreviousValue(?string $value): void
|
||||
{
|
||||
$this->previousValue = $value;
|
||||
}
|
||||
|
||||
public function getPreviousValue(): ?string
|
||||
{
|
||||
return $this->previousValue;
|
||||
}
|
||||
|
||||
public function setOriginalValueTokenStream(?TokenStreamInterface $tokenStream): void
|
||||
{
|
||||
$this->originalValueTokenStream = $tokenStream;
|
||||
}
|
||||
|
||||
public function getOriginalValueTokenStream(): ?TokenStreamInterface
|
||||
{
|
||||
return $this->originalValueTokenStream;
|
||||
}
|
||||
|
||||
public function addComment(TokenStreamInterface $tokenStream): void
|
||||
{
|
||||
$this->comments[] = $tokenStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TokenStreamInterface[]
|
||||
*/
|
||||
public function getComments(): array
|
||||
{
|
||||
return $this->comments;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\TypoScript\AST\Node;
|
||||
|
||||
/**
|
||||
* A generic child node that is not the node root.
|
||||
*/
|
||||
final class ChildNode extends AbstractChildNode {}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\TypoScript\AST\Node;
|
||||
|
||||
/**
|
||||
* The created AST consists of a NodeRoot object with nested NodeObject children.
|
||||
* This is the interface implemented by all children nodes (not RootNode).
|
||||
*
|
||||
* @internal: Internal AST structure.
|
||||
*/
|
||||
interface ChildNodeInterface extends NodeInterface {}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\TypoScript\AST\Node;
|
||||
|
||||
use TYPO3\CMS\Core\TypoScript\Tokenizer\Token\TokenStreamInterface;
|
||||
|
||||
/**
|
||||
* The created AST consists of a NodeRoot object with nested NodeObject children.
|
||||
* This is the main interface to any node type.
|
||||
*
|
||||
* Example TypoScript:
|
||||
* "foo = fooValue"
|
||||
* "foo.bar = barValue"
|
||||
* This creates a RootNode with one ChildNode name "foo" and value "fooValue",
|
||||
* that has a child ChildNode name "bar" and value "barValue".
|
||||
*
|
||||
* @internal: Internal AST structure.
|
||||
*/
|
||||
interface NodeInterface
|
||||
{
|
||||
/**
|
||||
* An identifier for this node. Typically, a hash of some kind. This identifier
|
||||
* is unique within the tree, by being created from the parent identifier plus
|
||||
* the name. This identifier is used in the backend, when referencing single nodes.
|
||||
* Calculating identifiers is initiated by calling setIdentifier() on RootNode, which
|
||||
* will recurse the tree. Call this on the final tree, after AST calculation finished,
|
||||
* so AST building itself does not need to fiddle with identifier updates when for
|
||||
* instance tree parts are cloned using '<' operator.
|
||||
* Note this value is skipped when persisting to caches since it's a Backend related
|
||||
* thing that does not use cached context: When retrieving nodes from cache (e.g. in Frontend),
|
||||
* the identifier is null and calling the getter will throw an exception.
|
||||
*/
|
||||
public function setIdentifier(string $identifier): void;
|
||||
public function getIdentifier(): string;
|
||||
|
||||
/**
|
||||
* Helper methods for node name.
|
||||
*/
|
||||
public function getName(): ?string;
|
||||
public function updateName(string $name): void;
|
||||
|
||||
/**
|
||||
* Helper methods to operate on children.
|
||||
*/
|
||||
public function addChild(ChildNodeInterface $node): void;
|
||||
public function getChildByName(string $name): ?ChildNodeInterface;
|
||||
public function removeChildByName(string $name): void;
|
||||
public function hasChildren(): bool;
|
||||
/**
|
||||
* @return iterable<ChildNodeInterface>
|
||||
*/
|
||||
public function getNextChild(): iterable;
|
||||
public function sortChildren(): void;
|
||||
|
||||
/**
|
||||
* Helper methods for value.
|
||||
*/
|
||||
public function setValue(?string $value): void;
|
||||
public function appendValue(string $value): void;
|
||||
public function getValue(): ?string;
|
||||
public function isValueNull(): bool;
|
||||
|
||||
/**
|
||||
* Previous value is only set by comment aware ast builder. It is used in
|
||||
* constant editor to see if a value has been changed.
|
||||
*/
|
||||
public function setPreviousValue(?string $value): void;
|
||||
public function getPreviousValue(): ?string;
|
||||
|
||||
/**
|
||||
* Helper method for backend object browser to retrieve the original
|
||||
* stream when a constant substitution happened, only set by CommentAwareAstBuilder.
|
||||
*/
|
||||
public function setOriginalValueTokenStream(?TokenStreamInterface $tokenStream): void;
|
||||
public function getOriginalValueTokenStream(): ?TokenStreamInterface;
|
||||
|
||||
/**
|
||||
* Helper methods to attach TypoScript tokens to a node.
|
||||
* This is used in ext:tstemplate "Constant Editor" and "Object Browser" and handled
|
||||
* by CommentAwareAstBuilder.
|
||||
*/
|
||||
public function addComment(TokenStreamInterface $tokenStream): void;
|
||||
/**
|
||||
* @return TokenStreamInterface[]
|
||||
*/
|
||||
public function getComments(): array;
|
||||
|
||||
/**
|
||||
* b/w compat method to turn AST into an array.
|
||||
* Note we're NOT using magic __toArray() here to avoid calling array-cast of AST by
|
||||
* accident: toArray() should be called explicitly if needed, which makes it much easier
|
||||
* to drop this b/w compat method when we later want to drop that layer.
|
||||
*
|
||||
* Note RootNode *always* returns an array, while ObjectNode's may return null.
|
||||
*/
|
||||
public function toArray(): ?array;
|
||||
|
||||
/**
|
||||
* Flatten the tree. A RootNode with a ChildNode "foo" and value "fooValue", with this
|
||||
* ChildNode again having a ChildNode "bar" and value "barValue" becomes:
|
||||
* [
|
||||
* 'foo' => 'fooValue',
|
||||
* 'foo.bar' => 'barValue',
|
||||
* ]
|
||||
*
|
||||
* Flattening a TypoScript tree is especially used for constants to quickly look
|
||||
* up constants when parsing setup node value streams that use T_CONSTANT tokens.
|
||||
*/
|
||||
public function flatten(string $prefix = ''): array;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\TypoScript\AST\Node;
|
||||
|
||||
use TYPO3\CMS\Core\TypoScript\Tokenizer\Token\IdentifierTokenStream;
|
||||
|
||||
/**
|
||||
* A node object created for LineIdentifierReference lines which use the T_OPERATOR_REFERENCE
|
||||
* operator and have a TokenStreamIdentifier stream for "the right side" of the expression.
|
||||
*
|
||||
* The reference operator is nasty, since it's no "true" reference / pointer:
|
||||
* foo.bar = barValue1
|
||||
* baz =< foo
|
||||
* baz.bar = barValue2
|
||||
* This ends up with "barValue1" for "foo.bar", and "barValue2" for "baz.bar". "barValue1"
|
||||
* for "foo.bar" is kept!
|
||||
*
|
||||
* Note the reference operator *only* works for TS "setup" code, not for "constants", and it
|
||||
* is only resolved in these cases. See ContentObjectRenderer->cObjGetSingle() for details.
|
||||
*
|
||||
* @internal: Internal AST structure.
|
||||
*/
|
||||
final class ReferenceChildNode extends AbstractChildNode
|
||||
{
|
||||
private ?IdentifierTokenStream $referenceSourceStream;
|
||||
|
||||
protected function serialize(): array
|
||||
{
|
||||
$result = parent::serialize();
|
||||
if ($this->referenceSourceStream !== null) {
|
||||
$result['referenceSourceStream'] = $this->referenceSourceStream;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function setReferenceSourceStream(?IdentifierTokenStream $referenceSourceStream): void
|
||||
{
|
||||
$this->referenceSourceStream = $referenceSourceStream;
|
||||
}
|
||||
|
||||
public function getReferenceSourceStream(): IdentifierTokenStream
|
||||
{
|
||||
return $this->referenceSourceStream;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\TypoScript\AST\Node;
|
||||
|
||||
/**
|
||||
* AST entry node.
|
||||
*
|
||||
* @internal: Internal AST structure.
|
||||
*/
|
||||
final class RootNode extends AbstractNode
|
||||
{
|
||||
/**
|
||||
* Attempting to clone the RootNode indicates a bug in AstBuilder.
|
||||
* It should never happen.
|
||||
*/
|
||||
public function __clone(): void
|
||||
{
|
||||
throw new \LogicException('Can not clone RootNode', 1655988945);
|
||||
}
|
||||
|
||||
/**
|
||||
* RootNode has no properties to cache, just children.
|
||||
*/
|
||||
protected function serialize(): array
|
||||
{
|
||||
return [
|
||||
'children' => $this->children,
|
||||
];
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function updateName(string $name): void
|
||||
{
|
||||
throw new \RuntimeException('RootNode has no name. Don\'t call updateName().', 1653743453);
|
||||
}
|
||||
|
||||
public function setValue(?string $value): void
|
||||
{
|
||||
throw new \RuntimeException('RootNode has no value. Don\'t call setValue().', 1653743454);
|
||||
}
|
||||
|
||||
public function appendValue(string $value): void
|
||||
{
|
||||
throw new \RuntimeException('RootNode has no value. Don\'t call appendValue().', 1653743455);
|
||||
}
|
||||
|
||||
public function getValue(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isValueNull(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->getNextChild() as $child) {
|
||||
$childName = $child->getName();
|
||||
if ($child instanceof ReferenceChildNode) {
|
||||
// Hack for b/w compat parsing of `=<` operator. See ContentObjectRenderer cObjGetSingle() and mergeTSRef()
|
||||
$childValue = '< ' . $child->getReferenceSourceStream();
|
||||
} else {
|
||||
$childValue = $child->getValue();
|
||||
}
|
||||
if ($childValue !== null) {
|
||||
$result[$childName] = $childValue;
|
||||
}
|
||||
$grandChildren = $child->toArray();
|
||||
if ($grandChildren !== null) {
|
||||
$result[$childName . '.'] = $grandChildren;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function flatten(string $prefix = ''): array
|
||||
{
|
||||
$flatArray = [];
|
||||
foreach ($this->getNextChild() as $child) {
|
||||
$flatArray = array_merge($flatArray, $child->flatten(''));
|
||||
}
|
||||
return $flatArray;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user