Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
16bc04e
Defined strict types for IO contracts
alongosz Jul 8, 2025
9ed4b40
Defined template type for IO HandlerRegistry
alongosz Jul 8, 2025
e2fffbb
Fixed type hints for IOBinarydataHandler implementations
alongosz Jul 8, 2025
04d4062
Fixed type hints for IOMetadataHandler implementations
alongosz Jul 8, 2025
81e7128
Added missing type hints to IO UrlDecorator
alongosz Jul 8, 2025
0a2bfe2
Aligned NotFoundException `$previous` arg type with `\Exception` sign…
alongosz Jul 8, 2025
d06f1cf
Added missing type hints to IO\MimeTypeDetector\FileInfo
alongosz Jul 8, 2025
74d38cd
Fixed strict types in \Ibexa\Bundle\IO\Migration namespace
alongosz Jul 8, 2025
b41dd5d
Added missing type hints and improved code of Imagine VariationPurger
alongosz Jul 9, 2025
01ab1e3
Added missing type hints to IOServiceInterface and aligned related code
alongosz Jul 14, 2025
072d688
Added missing type hints to \Ibexa\Bundle\Core\Imagine\Filter\FilterC…
alongosz Jul 14, 2025
946ac5c
Added missing type hints to \Ibexa\Core\IO\FilePathNormalizer\Flysystem
alongosz Jul 14, 2025
578d353
Fixed strict types of MigrateFilesCommand and improved its code
alongosz Jul 15, 2025
77d86d6
[DI] Added missing type hints to IO Bundle Configuration
alongosz Jul 15, 2025
e1ce13a
Fixed strict types and improved IO Handler Configuration Factories
alongosz Jul 15, 2025
b3b5197
Added missing type hints to StreamFileListener
alongosz Jul 15, 2025
b482e84
Declared strict types and fixed IbexaIOBundle
alongosz Jul 15, 2025
30cb1ad
Declared strict types for MigrationFileListerPass
alongosz Jul 15, 2025
5d230f7
Declared strict types for FileMigratorInterface
alongosz Jul 15, 2025
78100f8
Declared strict types for BinaryStreamResponse
alongosz Jul 15, 2025
66bc4a4
Simplified LegacyStorageImageFileRowReader::getRow implementation
alongosz Jul 21, 2025
1077810
[Tests] Dropped obsolete test replaced by strict types
alongosz Jul 8, 2025
c2e18eb
[Tests] Aligned tests with the changes
alongosz Jul 8, 2025
689887a
[Tests] Aligned VariationPurger unit tests with the changes
alongosz Jul 9, 2025
242e14c
[Tests] Aligned IO\Migration tests with the changes
alongosz Jul 9, 2025
00a2d20
[Tests] Aligned IOServiceTest with the changes
alongosz Jul 9, 2025
c117694
[Tests] Fixed & refactored IORepositoryResolverTest
alongosz Jul 14, 2025
fb8c4c8
[Tests] Fixed & refactored LegacyTest after BinaryFile strict types c…
alongosz Jul 14, 2025
914063e
[Tests] Fixed strict types of IOConfigurationPassTest
alongosz Jul 15, 2025
fbb3bfc
[Tests] Improved quality of LegacyDFSClusterTest
alongosz Jul 15, 2025
be319fb
[Tests] Fixed ConfigurationFactory test cases
alongosz Jul 15, 2025
ce33ab0
[Tests] Improved \Ibexa\Tests\Bundle\IO\DependencyInjection\Configura…
alongosz Jul 15, 2025
c824970
[PHPStan] Removed resolved issues from the baseline
alongosz Jul 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fixed type hints for IOMetadataHandler implementations
  • Loading branch information
alongosz committed Aug 6, 2025
commit 04d40624a939789014b91eec13c5b07ad75e30cb
36 changes: 12 additions & 24 deletions src/lib/IO/IOMetadataHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Core\IO;

use Ibexa\Contracts\Core\IO\BinaryFile;
use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct;

/**
* Provides reading & writing of files meta data (size, modification time...).
* Provides reading & writing of files meta-data (size, modification time...).
*/
interface IOMetadataHandler
{
Expand All @@ -19,49 +21,35 @@ interface IOMetadataHandler
*
* @param \Ibexa\Contracts\Core\IO\BinaryFileCreateStruct $spiBinaryFileCreateStruct
*
* @return \Ibexa\Contracts\Core\IO\BinaryFile
*
* @throws \RuntimeException if an error occured creating the file
* @throws \RuntimeException if an error occurred creating the file
*/
public function create(BinaryFileCreateStruct $spiBinaryFileCreateStruct);
public function create(BinaryFileCreateStruct $spiBinaryFileCreateStruct): BinaryFile;

/**
* Deletes file $spiBinaryFileId.
* Deletes file by its $binaryFileId.
*
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If $spiBinaryFileId is not found
*
* @param string $spiBinaryFileId
*/
public function delete($spiBinaryFileId);
public function delete(string $binaryFileId): void;

/**
* Loads and returns metadata for $spiBinaryFileId.
*
* @param string $spiBinaryFileId
*
* @return \Ibexa\Contracts\Core\IO\BinaryFile
*
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
*/
public function load($spiBinaryFileId);
public function load(string $spiBinaryFileId): BinaryFile;

/**
* Checks if a file $spiBinaryFileId exists.
*
* @param string $spiBinaryFileId
*
* @return bool
*/
public function exists($spiBinaryFileId);
public function exists(string $spiBinaryFileId): bool;

/**
* Returns the file's mimetype. Example: text/plain.
*
* @param $spiBinaryFileId
*
* @return string
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
*/
public function getMimeType($spiBinaryFileId);
public function getMimeType(string $spiBinaryFileId): string;

public function deleteDirectory($spiPath);
public function deleteDirectory(string $path): void;
}
21 changes: 13 additions & 8 deletions src/lib/IO/IOMetadataHandler/Flysystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Core\IO\IOMetadataHandler;

Expand Down Expand Up @@ -41,6 +42,7 @@ public function setLogger(LoggerInterface $logger): void
* Only reads & returns metadata, since the binary data handler took care of creating the file already.
*
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
* @throws \DateMalformedStringException
*/
public function create(SPIBinaryFileCreateStruct $spiBinaryFileCreateStruct): IOBinaryFile
{
Expand All @@ -49,23 +51,25 @@ public function create(SPIBinaryFileCreateStruct $spiBinaryFileCreateStruct): IO

/**
* Does really nothing, the binary data handler takes care of it.
*
* @param $spiBinaryFileId
*/
public function delete($spiBinaryFileId)
public function delete(string $binaryFileId): void
{
}

public function load($spiBinaryFileId): IOBinaryFile
/**
* @throws \DateMalformedStringException
* @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException
*/
public function load(string $spiBinaryFileId): IOBinaryFile
{
try {
return $this->getIOBinaryFile($spiBinaryFileId);
} catch (FilesystemException $e) {
} catch (FilesystemException) {
throw new BinaryFileNotFoundException($spiBinaryFileId);
}
}

public function exists($spiBinaryFileId): bool
public function exists(string $spiBinaryFileId): bool
{
try {
return $this->filesystem->fileExists($spiBinaryFileId);
Expand All @@ -84,7 +88,7 @@ public function exists($spiBinaryFileId): bool
}
}

public function getMimeType($spiBinaryFileId): string
public function getMimeType(string $spiBinaryFileId): string
{
try {
return $this->filesystem->mimeType($spiBinaryFileId);
Expand All @@ -99,12 +103,13 @@ public function getMimeType($spiBinaryFileId): string
/**
* Does nothing, as the binary data handler takes care of it.
*/
public function deleteDirectory($spiPath)
public function deleteDirectory(string $path): void
{
}

/**
* @throws \League\Flysystem\FilesystemException
* @throws \DateMalformedStringException
*/
private function getIOBinaryFile(string $spiBinaryFileId): IOBinaryFile
{
Expand Down
107 changes: 44 additions & 63 deletions src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Core\IO\IOMetadataHandler;

Expand All @@ -12,7 +13,6 @@
use Doctrine\DBAL\Exception;
use Ibexa\Contracts\Core\IO\BinaryFile as SPIBinaryFile;
use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct;
use Ibexa\Core\Base\Exceptions\InvalidArgumentException;
use Ibexa\Core\IO\Exception\BinaryFileNotFoundException;
use Ibexa\Core\IO\IOMetadataHandler;
use Ibexa\Core\IO\UrlDecorator;
Expand All @@ -21,20 +21,20 @@
* Manages IO metadata in a mysql table, ibexa_dfs_file.
*
* It will prevent simultaneous writes to the same file.
*
* @phpstan-type TLegacyDFSBinaryFileData array{id: string, name_hash: string, name: string, name_trunk: string, datatype: string, scope: string, size: int, mtime: int, expired: bool, status: bool}
*/
class LegacyDFSCluster implements IOMetadataHandler
{
public const string DFS_FILE_TABLE = 'ibexa_dfs_file';

/** @var \Doctrine\DBAL\Connection */
private $db;
private Connection $db;

/** @var \Ibexa\Core\IO\UrlDecorator */
private $urlDecorator;
private ?UrlDecorator $urlDecorator;

/**
* @param \Doctrine\DBAL\Connection $connection Doctrine DBAL connection
* @param \Ibexa\Core\IO\UrlDecorator $urlDecorator The URL decorator used to add a prefix to files path
* @param \Ibexa\Core\IO\UrlDecorator|null $urlDecorator The URL decorator used to add a prefix to files path
*/
public function __construct(Connection $connection, UrlDecorator $urlDecorator = null)
{
Expand All @@ -49,18 +49,11 @@ public function __construct(Connection $connection, UrlDecorator $urlDecorator =
*
* @return \Ibexa\Contracts\Core\IO\BinaryFile
*
*@throws \RuntimeException if a DBAL error occurs
* @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException if the $binaryFileCreateStruct is invalid
*
* @since 6.10 The mtime of the $binaryFileCreateStruct must be a DateTime, as specified in the struct doc.
* @throws \Doctrine\DBAL\Exception
*/
public function create(SPIBinaryFileCreateStruct $spiBinaryFileCreateStruct)
public function create(SPIBinaryFileCreateStruct $spiBinaryFileCreateStruct): SPIBinaryFile
{
if (!($spiBinaryFileCreateStruct->mtime instanceof DateTime)) {
throw new InvalidArgumentException('$binaryFileCreateStruct', 'Property \'mtime\' must be a DateTime');
}

$path = (string)$this->addPrefix($spiBinaryFileCreateStruct->id);
$path = $this->addPrefix($spiBinaryFileCreateStruct->id);
$params = [
'name' => $path,
'name_hash' => md5($path),
Expand Down Expand Up @@ -90,13 +83,14 @@ public function create(SPIBinaryFileCreateStruct $spiBinaryFileCreateStruct)
/**
* Deletes file $spiBinaryFileId.
*
* @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException If $spiBinaryFileId is not found
* @param string $binaryFileId
*
* @param string $spiBinaryFileId
* @throws \Doctrine\DBAL\Exception
* @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException If $spiBinaryFileId is not found
*/
public function delete($spiBinaryFileId)
public function delete(string $binaryFileId): void
{
$path = (string)$this->addPrefix($spiBinaryFileId);
$path = (string)$this->addPrefix($binaryFileId);

// Unlike the legacy cluster, the file is directly deleted. It was inherited from the DB cluster anyway
$affectedRows = (int)$this->db->delete(self::DFS_FILE_TABLE, [
Expand All @@ -112,16 +106,13 @@ public function delete($spiBinaryFileId)
/**
* Loads and returns metadata for $spiBinaryFileId.
*
* @param string $spiBinaryFileId
*
* @return \Ibexa\Contracts\Core\IO\BinaryFile
*
* @throws \DateMalformedStringException
* @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException if no row is found for $spiBinaryFileId
* @throws \Doctrine\DBAL\Exception Any unhandled DBAL exception
*/
public function load($spiBinaryFileId)
public function load(string $spiBinaryFileId): SPIBinaryFile
{
$path = (string)$this->addPrefix($spiBinaryFileId);
$path = $this->addPrefix($spiBinaryFileId);

$queryBuilder = $this->db->createQueryBuilder();
$result = $queryBuilder
Expand All @@ -148,6 +139,7 @@ public function load($spiBinaryFileId)
throw new BinaryFileNotFoundException($path);
}

/** @phpstan-var TLegacyDFSBinaryFileData $row */
$row = $result->fetchAssociative() + ['id' => $spiBinaryFileId];

return $this->mapArrayToSPIBinaryFile($row);
Expand All @@ -156,16 +148,11 @@ public function load($spiBinaryFileId)
/**
* Checks if a file $spiBinaryFileId exists.
*
* @param string $spiBinaryFileId
*
* @throws \Ibexa\Core\Base\Exceptions\NotFoundException
* @throws \Doctrine\DBAL\Exception Any unhandled DBAL exception
*
* @return bool
*/
public function exists($spiBinaryFileId): bool
public function exists(string $spiBinaryFileId): bool
{
$path = (string)$this->addPrefix($spiBinaryFileId);
$path = $this->addPrefix($spiBinaryFileId);

$queryBuilder = $this->db->createQueryBuilder();
$result = $queryBuilder
Expand Down Expand Up @@ -215,15 +202,11 @@ protected function getScope(SPIBinaryFileCreateStruct $binaryFileCreateStruct):
{
[$filePrefix] = explode('/', $binaryFileCreateStruct->id);

switch ($filePrefix) {
case 'images':
return 'image';

case 'original':
return 'binaryfile';
}

return 'UNKNOWN_SCOPE';
return match ($filePrefix) {
'images' => 'image',
'original' => 'binaryfile',
default => 'UNKNOWN_SCOPE',
};
}

/**
Expand All @@ -233,26 +216,26 @@ protected function getScope(SPIBinaryFileCreateStruct $binaryFileCreateStruct):
*
* @return string prefixed id
*/
protected function addPrefix($id)
protected function addPrefix(string $id): string
{
return isset($this->urlDecorator) ? $this->urlDecorator->decorate($id) : $id;
}

/**
* Removes the internal prefix string from $prefixedId.
*
* @param string $prefixedId
*
* @return string the id without the prefix
*
* @throws \Ibexa\Core\IO\Exception\InvalidBinaryFileIdException if the prefix isn't found in $prefixedId
*/
protected function removePrefix($prefixedId)
protected function removePrefix(string $prefixedId): string
{
return isset($this->urlDecorator) ? $this->urlDecorator->undecorate($prefixedId) : $prefixedId;
}

public function getMimeType($spiBinaryFileId)
/**
* @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException
* @throws \Doctrine\DBAL\Exception
*/
public function getMimeType(string $spiBinaryFileId): string
{
$queryBuilder = $this->db->createQueryBuilder();
$result = $queryBuilder
Expand All @@ -265,10 +248,11 @@ public function getMimeType($spiBinaryFileId)
->executeQuery()
;

if ($result->rowCount() == 0) {
if ($result->rowCount() === 0) {
throw new BinaryFileNotFoundException($spiBinaryFileId);
}

/** @var array{datatype: string} $row */
$row = $result->fetchAssociative();

return $row['datatype'];
Expand All @@ -277,9 +261,11 @@ public function getMimeType($spiBinaryFileId)
/**
* Delete directory and all the content under specified directory.
*
* @param string $spiPath SPI Path, not prefixed by URL decoration
* @param string $path persistence path, not prefixed by URL decoration
*
* @throws \Doctrine\DBAL\Exception
*/
public function deleteDirectory($spiPath)
public function deleteDirectory(string $path): void
{
$query = $this->db->createQueryBuilder();
$query
Expand All @@ -288,19 +274,19 @@ public function deleteDirectory($spiPath)
->setParameter('esc', '\\')
->setParameter(
'spiPath',
addcslashes($this->addPrefix(rtrim($spiPath, '/')), '%_') . '/%'
addcslashes($this->addPrefix(rtrim($path, '/')), '%_') . '/%'
);
$query->executeStatement();
}

/**
* Maps an array of data base properties (id, size, mtime, datatype, md5_path, path...) to an SPIBinaryFile object.
* Maps an array of database properties (id, size, mtime, datatype, md5_path, path...) to an SPIBinaryFile object.
*
* @param array $properties database properties array
* @param array{id: string, size: int, mtime: int} $properties database properties array
*
* @return \Ibexa\Contracts\Core\IO\BinaryFile
* @throws \DateMalformedStringException
*/
protected function mapArrayToSPIBinaryFile(array $properties)
protected function mapArrayToSPIBinaryFile(array $properties): SPIBinaryFile
{
$spiBinaryFile = new SPIBinaryFile();
$spiBinaryFile->id = $properties['id'];
Expand All @@ -310,12 +296,7 @@ protected function mapArrayToSPIBinaryFile(array $properties)
return $spiBinaryFile;
}

/**
* @param \Ibexa\Contracts\Core\IO\BinaryFileCreateStruct $binaryFileCreateStruct
*
* @return \Ibexa\Contracts\Core\IO\BinaryFile
*/
protected function mapSPIBinaryFileCreateStructToSPIBinaryFile(SPIBinaryFileCreateStruct $binaryFileCreateStruct)
protected function mapSPIBinaryFileCreateStructToSPIBinaryFile(SPIBinaryFileCreateStruct $binaryFileCreateStruct): SPIBinaryFile
{
$spiBinaryFile = new SPIBinaryFile();
$spiBinaryFile->id = $binaryFileCreateStruct->id;
Expand Down