Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/fileitem factory #362

Merged
merged 13 commits into from
Oct 14, 2024
7 changes: 7 additions & 0 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Symfony\Contracts\Translation\TranslatorInterface;
use Terminal42\NotificationCenterBundle\Backend\AutoSuggester;
use Terminal42\NotificationCenterBundle\BulkyItem\BulkyItemStorage;
use Terminal42\NotificationCenterBundle\BulkyItem\FileItemFactory;
use Terminal42\NotificationCenterBundle\Config\ConfigLoader;
use Terminal42\NotificationCenterBundle\Cron\PruneBulkyItemStorageCron;
use Terminal42\NotificationCenterBundle\DependencyInjection\Terminal42NotificationCenterExtension;
Expand Down Expand Up @@ -59,6 +60,12 @@
])
;

$services->set(FileItemFactory::class)
->args([
service('mime_types')->nullOnInvalid(),
])
;

$services->set(PruneBulkyItemStorageCron::class)
->args([
service(BulkyItemStorage::class),
Expand Down
22 changes: 20 additions & 2 deletions src/BulkyItem/FileItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,24 @@
namespace Terminal42\NotificationCenterBundle\BulkyItem;

use Symfony\Component\Filesystem\Filesystem;
use Terminal42\NotificationCenterBundle\Exception\BulkyItem\InvalidFileItemException;

class FileItem implements BulkyItemInterface
{
/**
* @param resource $contents
*
* @throws InvalidFileItemException
*/
private function __construct(
private $contents,
private readonly string $name,
private readonly string $mimeType,
private readonly int $size,
) {
$this->assert('' !== $this->name, 'Name must not be empty');
$this->assert('' !== $this->mimeType, 'Mime type must not be empty');
$this->assert($this->size >= 0, 'File size must not be smaller than 0');
}

public function getName(): string
Expand Down Expand Up @@ -53,24 +59,36 @@ public static function restore($contents, array $meta): BulkyItemInterface
return new self($contents, $meta['name'], $meta['type'], $meta['size']);
}

/**
* @throws InvalidFileItemException
*/
public static function fromPath(string $path, string $name, string $mimeType, int $size): self
{
if (!(new Filesystem())->exists($path)) {
throw new \InvalidArgumentException(\sprintf('The file "%s" does not exist.', $path));
throw new InvalidFileItemException(\sprintf('The file "%s" does not exist.', $path));
}

return new self(fopen($path, 'r'), $name, $mimeType, $size);
}

/**
* @param resource $resource
*
* @throws InvalidFileItemException
*/
public static function fromStream($resource, string $name, string $mimeType, int $size): self
{
if (!\is_resource($resource)) {
throw new \InvalidArgumentException('$contents must be a resource.');
throw new InvalidFileItemException('$contents must be a resource.');
}

return new self($resource, $name, $mimeType, $size);
}

private function assert(bool $condition, string $message): void
{
if (!$condition) {
throw new InvalidFileItemException($message);
}
}
}
44 changes: 44 additions & 0 deletions src/BulkyItem/FileItemFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Terminal42\NotificationCenterBundle\BulkyItem;

use Contao\CoreBundle\Filesystem\FilesystemItem;
use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
use Terminal42\NotificationCenterBundle\Exception\BulkyItem\InvalidFileItemException;

class FileItemFactory
{
public function __construct(private readonly MimeTypeGuesserInterface|null $mimeTypeGuesser = null)
{
}

/**
* @throws InvalidFileItemException if the information cannot be fetched automatically (e.g. missing mime type guesser service)
Toflar marked this conversation as resolved.
Show resolved Hide resolved
*/
public function createFromLocalPath(string $path): FileItem
{
if (!(new Filesystem())->exists($path)) {
throw new InvalidFileItemException(\sprintf('The file "%s" does not exist.', $path));
Toflar marked this conversation as resolved.
Show resolved Hide resolved
}

$name = basename($path);
$mimeType = (string) $this->mimeTypeGuesser?->guessMimeType($path);
$size = (int) filesize($path);

return FileItem::fromPath($path, $name, $mimeType, $size);
}

/**
* @throws InvalidFileItemException
*/
public function createFromVfsFilesystemItem(FilesystemItem $file, VirtualFilesystemInterface $virtualFilesystem): FileItem
{
$stream = $virtualFilesystem->readStream($file->getPath());

return FileItem::fromStream($stream, $file->getName(), $file->getMimeType(), $file->getFileSize());
Toflar marked this conversation as resolved.
Show resolved Hide resolved
}
}
9 changes: 9 additions & 0 deletions src/Exception/BulkyItem/InvalidFileItemException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Terminal42\NotificationCenterBundle\Exception\BulkyItem;

class InvalidFileItemException extends \InvalidArgumentException
{
}
41 changes: 41 additions & 0 deletions tests/BulkyItem/FileItemFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Terminal42\NotificationCenterBundle\Test\BulkyItem;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Mime\MimeTypes;
use Terminal42\NotificationCenterBundle\BulkyItem\FileItemFactory;
use Terminal42\NotificationCenterBundle\Test\VirtualFilesystemTestTrait;

class FileItemFactoryTest extends TestCase
{
use VirtualFilesystemTestTrait;

public function testCreateFromLocalPath(): void
{
$factory = new FileItemFactory(new MimeTypes());
$item = $factory->createFromLocalPath(__DIR__.'/../Fixtures/name.jpg');
$this->assertSame('name.jpg', $item->getName());
$this->assertSame('image/jpeg', $item->getMimeType());
$this->assertSame(333, $item->getSize());
$this->assertIsResource($item->getContents());
}

public function testCreateFromVfsFilesystemItem(): void
{
$vfsCollection = $this->createVfsCollection();
$vfs = $vfsCollection->get('files');
$vfs->write('media/name.jpg', file_get_contents(__DIR__.'/../Fixtures/name.jpg'));

$item = $vfs->get('media/name.jpg');

$factory = new FileItemFactory(new MimeTypes());
$item = $factory->createFromVfsFilesystemItem($item, $vfs);
$this->assertSame('name.jpg', $item->getName());
$this->assertSame('image/jpeg', $item->getMimeType());
$this->assertSame(333, $item->getSize());
$this->assertIsResource($item->getContents());
}
}
36 changes: 36 additions & 0 deletions tests/BulkyItem/FileItemTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Terminal42\NotificationCenterBundle\Test\BulkyItem;

use PHPUnit\Framework\TestCase;
use Terminal42\NotificationCenterBundle\BulkyItem\FileItem;
use Terminal42\NotificationCenterBundle\Exception\BulkyItem\InvalidFileItemException;

class FileItemTest extends TestCase
{
public function testCannotCreateEmptyNameFileItem(): void
{
$this->expectException(InvalidFileItemException::class);
$this->expectExceptionMessage('Name must not be empty');

FileItem::fromPath(__DIR__.'/../Fixtures/name.jpg', '', 'image/jpg', 0);
}

public function testCannotCreateEmptyMimeTypeFileItem(): void
{
$this->expectException(InvalidFileItemException::class);
$this->expectExceptionMessage('Mime type must not be empty');

FileItem::fromPath(__DIR__.'/../Fixtures/name.jpg', 'name.jpg', '', 0);
}

public function testCannotCreateInvalidFileSizeFileItem(): void
{
$this->expectException(InvalidFileItemException::class);
$this->expectExceptionMessage('File size must not be smaller than 0');

FileItem::fromPath(__DIR__.'/../Fixtures/name.jpg', 'name.jpg', 'image/jpg', -42);
}
}
34 changes: 34 additions & 0 deletions tests/VirtualFilesystemTestTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Terminal42\NotificationCenterBundle\Test;

use Contao\CoreBundle\Filesystem\Dbafs\DbafsManager;
use Contao\CoreBundle\Filesystem\MountManager;
use Contao\CoreBundle\Filesystem\VirtualFilesystem;
use League\Flysystem\InMemory\InMemoryFilesystemAdapter;
use Terminal42\NotificationCenterBundle\Test\BulkyItem\InMemoryDbafs;
use Terminal42\NotificationCenterBundle\Test\BulkyItem\VirtualFilesystemCollection;

trait VirtualFilesystemTestTrait
{
private function createVfsCollection(): VirtualFilesystemCollection
{
$mountManager = (new MountManager())
->mount(new InMemoryFilesystemAdapter(), 'files')
->mount(new InMemoryFilesystemAdapter(), 'bulky_item')
;

$dbafsManager = new DbafsManager();
$dbafsManager->register(new InMemoryDbafs(), 'files');
$dbafsManager->register(new InMemoryDbafs(), 'bulky_item');

$vfsCollection = new VirtualFilesystemCollection();
$vfsCollection->add(new VirtualFilesystem($mountManager, $dbafsManager, 'files'));
$vfsCollection->add(new VirtualFilesystem($mountManager, $dbafsManager, 'bulky_item'));
$vfsCollection->add(new VirtualFilesystem($mountManager, $dbafsManager, '')); // Global one

return $vfsCollection;
}
}
Loading