Skip to content

Commit

Permalink
Ensure valid FileItem instances
Browse files Browse the repository at this point in the history
  • Loading branch information
Toflar committed Sep 19, 2024
1 parent c368842 commit e721a25
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 5 deletions.
2 changes: 1 addition & 1 deletion config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@

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

Expand Down
19 changes: 17 additions & 2 deletions src/BulkyItem/FileItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,28 @@
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,
) {
try {
\assert('' !== $this->name, 'Name must not be empty');
\assert('' !== $this->mimeType, 'Mime type must not be empty');
\assert($this->size >= 0, 'File size must not be smaller than 0');
} catch (\AssertionError $e) {

Check failure on line 27 in src/BulkyItem/FileItem.php

View workflow job for this annotation

GitHub Actions / ci / build-tools

Dead catch - AssertionError is never thrown in the try block.

Check failure on line 27 in src/BulkyItem/FileItem.php

View workflow job for this annotation

GitHub Actions / ci / build-tools

Dead catch - AssertionError is never thrown in the try block.
throw new InvalidFileItemException($e->getMessage(), $e->getCode(), $e);
}
}

public function getName(): string
Expand Down Expand Up @@ -53,22 +63,27 @@ 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);
Expand Down
11 changes: 9 additions & 2 deletions src/BulkyItem/FileItemFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,33 @@
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 $mimeTypeGuesser)
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)
*/
public function createFromLocalPath(string $path): FileItem
{
if (!(new Filesystem())->exists($path)) {
throw new \InvalidArgumentException(\sprintf('The file "%s" does not exist.', $path));
}

$name = basename($path);
$mimeType = (string) $this->mimeTypeGuesser->guessMimeType($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());
Expand Down
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
{
}
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);
}
}

0 comments on commit e721a25

Please sign in to comment.