TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:09 +02:00
commit af8cc155b5
6818 changed files with 642608 additions and 0 deletions
@@ -0,0 +1,392 @@
<?php
/*
* 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\Collection;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Abstract implementation of a RecordCollection
*
* RecordCollection is a collections of TCA-Records.
* The collection is meant to be stored in TCA-table sys_file_collections and is manageable
* via FormEngine.
*
* A RecordCollection might be used to group a set of records (e.g. news, images, contentElements)
* for output in frontend
*
* The AbstractRecordCollection uses SplDoublyLinkedList for internal storage
*
* @template T
* @implements RecordCollectionInterface<T>
*/
abstract class AbstractRecordCollection implements RecordCollectionInterface, PersistableCollectionInterface
{
/**
* The table name collections are stored to
*
* @var string
*/
protected static $storageItemsField = 'items';
/**
* The table name collections are stored to, must be defined in the subclass
*
* @var string
*/
protected static $storageTableName = '';
/**
* Uid of the storage
*
* @var int
*/
protected $uid = 0;
/**
* Collection title
*
* @var string
*/
protected $title;
/**
* Collection description
*
* @var string
*/
protected $description;
/**
* Table name of the records stored in this collection
*
* @var string
*/
protected $itemTableName;
/**
* The local storage
*
* @var \SplDoublyLinkedList
*/
protected $storage;
/**
* Creates this object.
*/
public function __construct()
{
$this->storage = new \SplDoublyLinkedList();
}
/**
* Return the current element
*
* @return T|null
*/
public function current(): mixed
{
return $this->storage->current();
}
/**
* Move forward to next element
*/
public function next(): void
{
$this->storage->next();
}
/**
* Return the key of the current element
*
* @return int|string 0 on failure.
*/
public function key(): mixed
{
$currentRecord = $this->storage->current();
return $currentRecord['uid'] ?? 0;
}
/**
* Checks if current position is valid
*
* @return bool The return value will be cast to boolean and then evaluated.
*/
public function valid(): bool
{
return $this->storage->valid();
}
/**
* Rewind the Iterator to the first element
*/
public function rewind(): void
{
$this->storage->rewind();
}
/**
* Returns class state to be serialized.
*/
public function __serialize(): array
{
return [
'uid' => $this->getIdentifier(),
];
}
/**
* Load records with the given serialized information
*/
public function __unserialize(array $arrayRepresentation): void
{
self::load($arrayRepresentation['uid']);
}
/**
* Count elements of an object
*
* @return int The custom count as an integer.
*/
public function count(): int
{
return $this->storage->count();
}
/**
* Getter for the title
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Getter for the UID
*
* @return int
*/
public function getUid()
{
return $this->uid;
}
/**
* Getter for the description
*
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Setter for the title
*
* @param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Setter for the description
*
* @param string $desc
*/
public function setDescription($desc)
{
$this->description = $desc;
}
/**
* Setter for the name of the data-source table
*
* @return string
*/
public function getItemTableName()
{
return $this->itemTableName;
}
/**
* Setter for the name of the data-source table
*
* @param string $tableName
*/
public function setItemTableName($tableName)
{
$this->itemTableName = $tableName;
}
/**
* Returns the uid of the collection
*
* @return int
*/
public function getIdentifier()
{
return $this->uid;
}
/**
* Sets the identifier of the collection
*
* @param int $id
*/
public function setIdentifier($id)
{
$this->uid = (int)$id;
}
/**
* Loads the collections with the given id from persistence
*
* For memory reasons, per default only f.e. title, database-table,
* identifier (what ever static data is defined) is loaded.
* Entries can be load on first access.
*
* @param int $id Id of database record to be loaded
* @param bool $fillItems Populates the entries directly on load, might be bad for memory on large collections
* @return CollectionInterface
*/
public static function load($id, $fillItems = false)
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(static::getCollectionDatabaseTable());
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$collectionRecord = $queryBuilder->select('*')
->from(static::getCollectionDatabaseTable())
->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($id, Connection::PARAM_INT)))
->executeQuery()
->fetchAssociative();
return self::create($collectionRecord ?: [], $fillItems);
}
/**
* Creates a new collection objects and reconstitutes the
* given database record to the new object.
*
* @param array $collectionRecord Database record
* @param bool $fillItems Populates the entries directly on load, might be bad for memory on large collections
* @return CollectionInterface
*/
public static function create(array $collectionRecord, $fillItems = false)
{
// [phpstan] Unsafe usage of new static()
// todo: Either mark this class or its constructor final or use new self instead.
$collection = new static();
$collection->fromArray($collectionRecord);
if ($fillItems) {
$collection->loadContents();
}
return $collection;
}
/**
* Persists current collection state to underlying storage
*/
public function persist()
{
$uid = $this->getIdentifier() == 0 ? 'NEW' . random_int(100000, 999999) : $this->getIdentifier();
$data = [
trim(static::getCollectionDatabaseTable()) => [
$uid => $this->getPersistableDataArray(),
],
];
// New records always must have a pid
if ($this->getIdentifier() == 0) {
$data[trim(static::getCollectionDatabaseTable())][$uid]['pid'] = 0;
}
$tce = GeneralUtility::makeInstance(DataHandler::class);
$tce->start($data, []);
$tce->process_datamap();
}
/**
* Returns an array of the persistable properties and contents
* which are processable by DataHandler.
*
* For internal usage in persist only.
*
* @return array
*/
abstract protected function getPersistableDataArray();
/**
* Generates comma-separated list of entry uids for usage in DataHandler
*
* also allow to add table name, if it might be needed by DataHandler for
* storing the relation
*
* @param bool $includeTableName
* @return string
*/
protected function getItemUidList($includeTableName = true)
{
$list = [];
foreach ($this->storage as $entry) {
$list[] = ($includeTableName ? $this->getItemTableName() . '_' : '') . $entry['uid'];
}
return implode(',', $list);
}
/**
* Builds an array representation of this collection
*
* @return array
*/
public function toArray()
{
$itemArray = [];
foreach ($this->storage as $item) {
$itemArray[] = $item;
}
return [
'uid' => $this->getIdentifier(),
'title' => $this->getTitle(),
'description' => $this->getDescription(),
'table_name' => $this->getItemTableName(),
'items' => $itemArray,
];
}
/**
* Loads the properties of this collection from an array
*/
public function fromArray(array $array)
{
$this->uid = $array['uid'];
$this->title = $array['title'];
$this->description = $array['description'];
$this->itemTableName = $array['table_name'];
}
protected static function getCollectionDatabaseTable(): string
{
if (!empty(static::$storageTableName)) {
return static::$storageTableName;
}
throw new \RuntimeException('No storage table name was defined the class "' . static::class . '".', 1592207959);
}
}
@@ -0,0 +1,28 @@
<?php
/*
* 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\Collection;
/**
* Marker interface for collection classes
*
* Collections are containers-classes handling the storage
* of data values (f.e. strings, records, relations) in a
* common and generic way, while the class manages the storage
* in an appropriate way itself
*
* @template T
*/
interface CollectionInterface extends \Iterator, \Countable {}
@@ -0,0 +1,50 @@
<?php
/*
* 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\Collection;
/**
* Interface for collection classes which es enabled to be modified
*/
interface EditableCollectionInterface
{
/**
* Adds on entry to the collection
*
* @param mixed $data
*/
public function add($data);
/**
* Adds a set of entries to the collection
*/
public function addAll(CollectionInterface $other);
/**
* Remove the given entry from collection
*
* Note: not the given "index"
*
* @param mixed $data
*/
public function remove($data);
/**
* Removes all entries from the collection
*
* collection will be empty afterwards
*/
public function removeAll();
}
@@ -0,0 +1,98 @@
<?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\Collection;
use TYPO3\CMS\Core\Domain\RecordInterface;
/**
* When first accessed, this class will initialize itself and find the relations
* for this record field.
*
* This class acts as a "Value holder", as it only fetches the related records
* when needed.
*
* @todo: Evaluate if we should use a Ghost object instead.
*
* @internal not part of public API, as this needs to be streamlined and proven
*/
class LazyRecordCollection implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var RecordInterface[]|\Closure
*/
private array|\Closure $items;
public function __construct(
private readonly mixed $fieldValue,
\Closure $initialization
) {
$this->items = $initialization;
}
public function count(): int
{
$this->initialize();
return count($this->items);
}
private function initialize(): void
{
if ($this->items instanceof \Closure) {
$this->items = ($this->items)();
}
}
public function getIterator(): \Iterator
{
$this->initialize();
return new \ArrayIterator($this->items);
}
public function __toString(): string
{
return (string)$this->fieldValue;
}
public function offsetExists(mixed $offset): bool
{
$this->initialize();
return isset($this->items[$offset]);
}
public function offsetGet(mixed $offset): mixed
{
$this->initialize();
return $this->items[$offset] ?? null;
}
public function offsetSet(mixed $offset, mixed $value): void
{
if ($value instanceof RecordInterface === false) {
throw new \InvalidArgumentException(
'Modifying the record collection is only allowed by setting a value of type RecordInterface.',
1723188315
);
}
$this->items[$offset] = $value;
}
public function offsetUnset(mixed $offset): void
{
throw new \RuntimeException('Removing items from the record collection is not implemented.', 1723188316);
}
}
@@ -0,0 +1,53 @@
<?php
/*
* 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\Collection;
/**
* Marker interface for a collection class with title and description
*
* Collections might be used internally as well as being shown
* with the nameable interface a title and a description are added
* to a collection, allowing every collection implementing Nameable
* being displayed by the same logic.
*/
interface NameableCollectionInterface
{
/**
* Setter for the title
*
* @param string $title
*/
public function setTitle($title);
/**
* Setter for the description
*
* @param string $description
*/
public function setDescription($description);
/**
* Getter for the title
*
* @return string
*/
public function getTitle();
/**
* Getter for the description
*/
public function getDescription();
}
@@ -0,0 +1,75 @@
<?php
/*
* 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\Collection;
/**
* Interface for collection class being persistable
*
* Collections are containers-classes handling the storage
* of data values (f.e. strings, records, relations) in a
* common and generic way, while the class manages the storage
* in an appropriate way itself
*/
interface PersistableCollectionInterface
{
/**
* Get the identifier of the collection
*
* For database stored collections, this will be an integer,
* session stored, registry stored or other collections might
* use a string as well
*
* @return int|string
*/
public function getIdentifier();
/**
* Sets the identifier of the collection
*
* @param int|string $id
*/
public function setIdentifier($id);
/**
* Loads the collections with the given id from persistence
*
* For memory reasons, per default only f.e. title, database-table,
* identifier (what ever static data is defined) is loaded.
* Entries can be load on first access.
*
* @param int|string $id
* @param bool $fillItems Populates the entries directly on load, might be bad for memory on large collections
* @return \TYPO3\CMS\Core\Collection\CollectionInterface
*/
public static function load($id, $fillItems = false);
/**
* Persists current collection state to underlying storage
*/
public function persist();
/**
* Populates the content-entries of the storage
*
* Queries the underlying storage for entries of the collection
* and adds them to the collection data.
*
* If the content entries of the storage had not been loaded on creation
* ($fillItems = false) this function is to be used for loading the contents
* afterwards.
*/
public function loadContents();
}
@@ -0,0 +1,39 @@
<?php
/*
* 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\Collection;
/**
* Collection for handling records from a single database-table.
*
* @template T
* @extends CollectionInterface<T>
*/
interface RecordCollectionInterface extends CollectionInterface, NameableCollectionInterface
{
/**
* Setter for the name of the data-source table
*
* @param string $tableName
*/
public function setItemTableName($tableName);
/**
* Setter for the name of the data-source table
*
* @return string
*/
public function getItemTableName();
}