TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
<?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\Form\Service;
|
||||
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||
use TYPO3\CMS\Form\Domain\Repository\FormDefinitionRepository;
|
||||
|
||||
/**
|
||||
* This class is subjected to change.
|
||||
* **Do NOT subclass**
|
||||
*
|
||||
* Scope: frontend / backend
|
||||
* @internal
|
||||
*/
|
||||
class DatabaseService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ResourceFactory $resourceFactory,
|
||||
private readonly ConnectionPool $connectionPool,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Returns an array with all sys_refindex database rows which be
|
||||
* connected to a formDefinition identified by $persistenceIdentifier
|
||||
*
|
||||
* $persistenceIdentifier string can contain:
|
||||
* - number -> interpreted as a sys_file reference UID to a FAL-stored YAML file (user-generated content)
|
||||
* - EXT:... -> interpreted as a NON-FAL extension-based file
|
||||
* - any string -> interpreted as FAL-based filename
|
||||
*
|
||||
* Note that we explicitly do NOT check for file existence here,
|
||||
* because we want to be able to reveal sys_refindex entries to files
|
||||
* that have been deleted meanwhile!
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getReferencesByPersistenceIdentifier(string $persistenceIdentifier): array
|
||||
{
|
||||
if (empty($persistenceIdentifier)) {
|
||||
throw new \InvalidArgumentException('$persistenceIdentifier must not be empty.', 1472238493);
|
||||
}
|
||||
|
||||
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_refindex');
|
||||
$constraints = [$queryBuilder->expr()->eq('softref_key', $queryBuilder->createNamedParameter('formPersistenceIdentifier'))];
|
||||
|
||||
// Indicator whether the string-based lookup in sys_refindex shall be performed (true; non FAL-based) or not (false; FAL-based)
|
||||
$useStringReference = false;
|
||||
|
||||
// Check what $persistenceIdentifier contains.
|
||||
if (PathUtility::isExtensionPath($persistenceIdentifier)) {
|
||||
// Uses "EXT:" notation, so it cannot be a FAL identifier.
|
||||
// We pass the whole "EXT:..." lookup through to the sys_refindex query
|
||||
// due to its constraint on softref_key=formPersistenceIdentifier,
|
||||
// we expect no false entries even with "weird" string notations. If sys_refindex
|
||||
// has it, we yield it.
|
||||
$useStringReference = true;
|
||||
} elseif (MathUtility::canBeInterpretedAsInteger($persistenceIdentifier)) {
|
||||
$constraints[] = $queryBuilder->expr()->or(
|
||||
$queryBuilder->expr()->eq('ref_string', $queryBuilder->createNamedParameter($persistenceIdentifier)),
|
||||
$queryBuilder->expr()->eq('ref_uid', $queryBuilder->createNamedParameter($persistenceIdentifier, Connection::PARAM_INT))
|
||||
);
|
||||
} else {
|
||||
// Anything else would be either a notation like "/fileadmin/something.form.yaml"
|
||||
// or a numeric identifier for a sys_file.
|
||||
try {
|
||||
// We use this "bulk method" because this is the best-bet from resourceFactory
|
||||
// to resolve both an integer-ish input value or a FAL value. There is no
|
||||
// substitute for an "only get a file, not a directory" lookup.
|
||||
$file = $this->resourceFactory->retrieveFileOrFolderObject($persistenceIdentifier);
|
||||
|
||||
if ($file === null) {
|
||||
// The associated identifier could (no longer) be retrieved via FAL.
|
||||
// However, we do want to see existing entries to such stale entries to
|
||||
// be able to reveal bad references, either by its ref_string or ref_uid
|
||||
$useStringReference = true;
|
||||
} elseif ($file instanceof File) {
|
||||
// We succeeded in retrieving the FAL file object.
|
||||
$constraints[] = $queryBuilder->expr()->eq('ref_uid', $queryBuilder->createNamedParameter($file->getUid(), Connection::PARAM_INT));
|
||||
} else {
|
||||
// We might have retrieved a "Folder" object. Fall back to passthrough
|
||||
// with the intent, to retrieve all possible sys_refindex entries.
|
||||
// If that fails, it's ok to return an empty array.
|
||||
$useStringReference = true;
|
||||
}
|
||||
} catch (ResourceDoesNotExistException) {
|
||||
// This exception gets triggered when $persistenceIdentifier is not something
|
||||
// that could be resolved by the bulk-method.
|
||||
// As above, we want to retrieve all the possible sys_refindex entries,
|
||||
// so we fall back again to "ref_string".
|
||||
// This should happen when $persistenceIdentifier is set to a string like '/fileadmin/somefile.form.yaml',
|
||||
// and a FAL storage could be retrieved, but not the actual file.
|
||||
$useStringReference = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($useStringReference) {
|
||||
$constraints[] = $queryBuilder->expr()->eq('ref_string', $queryBuilder->createNamedParameter($persistenceIdentifier));
|
||||
}
|
||||
|
||||
return $queryBuilder
|
||||
->select('*')
|
||||
->from('sys_refindex')
|
||||
->where(...$constraints)
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all form definition persistenceIdentifiers
|
||||
* as keys and their reference counts as values.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getAllReferencesForPersistenceIdentifier(): array
|
||||
{
|
||||
$items = [];
|
||||
foreach ($this->getAllReferences('ref_string') as $item) {
|
||||
$items[$item['identifier']] = $item['items'];
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all form definition file uids as keys
|
||||
* and their reference counts as values.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getAllReferencesForFileUid(): array
|
||||
{
|
||||
$items = [];
|
||||
foreach ($this->getAllReferences('ref_uid') as $item) {
|
||||
$items[$item['identifier']] = $item['items'];
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all database-stored form definition UIDs as keys
|
||||
* and their reference counts as values.
|
||||
*
|
||||
* These are tracked in sys_refindex via ref_table='form_definition' and ref_uid=<form_definition UID>.
|
||||
*
|
||||
* @return array<string, int> persistenceIdentifier (UID as string) => reference count
|
||||
* @internal
|
||||
*/
|
||||
public function getAllReferencesForFormDefinitionUid(): array
|
||||
{
|
||||
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_refindex');
|
||||
|
||||
$rows = $queryBuilder
|
||||
->select('ref_uid AS identifier')
|
||||
->addSelectLiteral('COUNT(' . $queryBuilder->quoteIdentifier('ref_uid') . ') AS ' . $queryBuilder->quoteIdentifier('items'))
|
||||
->from('sys_refindex')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('softref_key', $queryBuilder->createNamedParameter('formPersistenceIdentifier')),
|
||||
$queryBuilder->expr()->eq('ref_table', $queryBuilder->createNamedParameter(FormDefinitionRepository::TABLE_NAME)),
|
||||
$queryBuilder->expr()->gt('ref_uid', $queryBuilder->createNamedParameter(0, Connection::PARAM_INT))
|
||||
)
|
||||
->groupBy('ref_uid')
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
|
||||
$items = [];
|
||||
foreach ($rows as $row) {
|
||||
$items[(string)$row['identifier']] = (int)$row['items'];
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
protected function getAllReferences(string $column): array
|
||||
{
|
||||
if ($column !== 'ref_string' && $column !== 'ref_uid') {
|
||||
throw new \InvalidArgumentException('$column must be "ref_string" or "ref_uid".', 1535406600);
|
||||
}
|
||||
|
||||
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_refindex');
|
||||
|
||||
$constraints = [
|
||||
$queryBuilder->expr()->eq('softref_key', $queryBuilder->createNamedParameter('formPersistenceIdentifier')),
|
||||
];
|
||||
|
||||
if ($column === 'ref_string') {
|
||||
$constraints[] = $queryBuilder->expr()->neq('ref_string', $queryBuilder->createNamedParameter(''));
|
||||
} else {
|
||||
$constraints[] = $queryBuilder->expr()->gt('ref_uid', $queryBuilder->createNamedParameter(0, Connection::PARAM_INT));
|
||||
}
|
||||
|
||||
return $queryBuilder
|
||||
->select($column . ' AS identifier')
|
||||
->addSelectLiteral('COUNT(' . $queryBuilder->quoteIdentifier($column) . ') AS ' . $queryBuilder->quoteIdentifier('items'))
|
||||
->from('sys_refindex')
|
||||
->where(...$constraints)
|
||||
->groupBy($column)
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user