From 7e581d64ef4b3c64f3817e78cc03912f65fe4d33 Mon Sep 17 00:00:00 2001 From: roadiz-ci Date: Fri, 6 Dec 2024 08:56:41 +0000 Subject: [PATCH] Merge branch release/v2.4.0 --- .github/workflows/run-test.yml | 4 +- composer.json | 21 ++- phpcs.xml.dist | 13 -- src/AbstractDocumentFactory.php | 58 ++----- src/AbstractDocumentFinder.php | 35 ++-- src/ArrayDocumentFinder.php | 4 +- src/AverageColorResolver.php | 8 +- src/Console/AbstractDocumentCommand.php | 31 ++-- src/Console/DocumentAverageColorCommand.php | 40 ++--- src/Console/DocumentClearFolderCommand.php | 40 +++-- src/Console/DocumentDownscaleCommand.php | 152 ++++++++++------ src/Console/DocumentDuplicatesCommand.php | 7 +- src/Console/DocumentFileHashCommand.php | 38 ++-- src/Console/DocumentFilesizeCommand.php | 17 +- src/Console/DocumentPruneCommand.php | 30 ++-- src/Console/DocumentPruneOrphansCommand.php | 38 ++-- src/Console/DocumentSizeCommand.php | 27 +-- src/DocumentArchiver.php | 18 +- src/DocumentFinderInterface.php | 13 -- src/DownloadedFile.php | 52 ++---- src/DownscaleImageManager.php | 48 ++---- src/Events/DocumentCreatedEvent.php | 2 +- src/Events/DocumentDeletedEvent.php | 2 +- src/Events/DocumentFileUpdatedEvent.php | 2 +- src/Events/DocumentLifeCycleSubscriber.php | 163 ++++++------------ src/Events/DocumentUpdatedEvent.php | 2 +- .../EmbedDocumentAlreadyExistsException.php | 6 +- src/Exceptions/InvalidEmbedId.php | 6 - .../AbstractDailymotionEmbedFinder.php | 66 +++---- .../AbstractDeezerEmbedFinder.php | 54 +++--- src/MediaFinders/AbstractEmbedFinder.php | 156 ++++------------- .../AbstractMixcloudEmbedFinder.php | 56 ++---- src/MediaFinders/AbstractPodcastFinder.php | 100 ++++------- .../AbstractSoundcloudEmbedFinder.php | 49 ++---- .../AbstractSpotifyEmbedFinder.php | 68 +++----- src/MediaFinders/AbstractTedEmbedFinder.php | 25 ++- .../AbstractTwitchEmbedFinder.php | 113 ------------ .../AbstractUnsplashPictureFinder.php | 102 ++++------- src/MediaFinders/AbstractVimeoEmbedFinder.php | 29 ++-- .../AbstractYoutubeEmbedFinder.php | 75 +++----- src/MediaFinders/EmbedFinderFactory.php | 32 ++-- src/MediaFinders/EmbedFinderInterface.php | 19 +- src/MediaFinders/FacebookPictureFinder.php | 33 ++-- src/MediaFinders/RandomImageFinder.php | 7 +- src/Models/AdvancedDocumentInterface.php | 4 - src/Models/DisplayableInterface.php | 4 - src/Models/DocumentInterface.php | 45 +---- src/Models/DocumentTrait.php | 73 +++----- src/Models/FileAwareInterface.php | 12 +- src/Models/FileHashInterface.php | 3 + src/Models/FolderInterface.php | 15 +- src/Models/HasThumbnailInterface.php | 14 +- src/Models/SimpleDocument.php | 29 +--- src/Models/SimpleFileAware.php | 9 +- src/Models/SizeableInterface.php | 11 -- src/Models/TimeableInterface.php | 4 - src/OptionsResolver/UrlOptionsResolver.php | 27 +-- src/Renderer/AbstractImageRenderer.php | 92 ++++------ src/Renderer/AbstractRenderer.php | 19 +- src/Renderer/AudioRenderer.php | 10 +- src/Renderer/ChainRenderer.php | 6 +- src/Renderer/EmbedRenderer.php | 8 +- src/Renderer/ImageRenderer.php | 12 +- src/Renderer/InlineSvgRenderer.php | 8 +- src/Renderer/PdfRenderer.php | 6 +- src/Renderer/PictureRenderer.php | 15 +- src/Renderer/RendererInterface.php | 12 -- src/Renderer/SvgRenderer.php | 16 +- src/Renderer/ThumbnailRenderer.php | 32 +--- src/Renderer/VideoRenderer.php | 24 +-- .../DocumentRepositoryInterface.php | 2 + src/SvgSizeResolver.php | 21 +-- src/TwigExtension/DocumentExtension.php | 65 +++---- .../AbstractDocumentUrlGenerator.php | 16 +- .../DocumentUrlGeneratorInterface.php | 9 - .../DummyDocumentUrlGenerator.php | 13 +- src/UrlGenerators/OptionsCompiler.php | 27 ++- src/Viewers/SvgDocumentViewer.php | 29 ++-- tests/MediaFinders/SimpleVimeoEmbedFinder.php | 6 - .../MediaFinders/SimpleYoutubeEmbedFinder.php | 6 - tests/Renderer/AbstractRendererTestCase.php | 32 ++-- tests/Renderer/AudioRendererTest.php | 35 ++-- tests/Renderer/ChainRendererTest.php | 32 ++-- tests/Renderer/EmbedRendererTest.php | 41 +++-- tests/Renderer/ImageRendererTest.php | 78 ++++----- tests/Renderer/PdfRendererTest.php | 5 +- tests/Renderer/PictureRendererTest.php | 132 +++++++------- tests/Renderer/VideoRendererTest.php | 5 +- 88 files changed, 1061 insertions(+), 1864 deletions(-) delete mode 100644 phpcs.xml.dist delete mode 100644 src/MediaFinders/AbstractTwitchEmbedFinder.php diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml index a28c61b..99e3d46 100644 --- a/.github/workflows/run-test.yml +++ b/.github/workflows/run-test.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: ['8.1', '8.2', '8.3'] + php-version: ['8.2', '8.3'] steps: - uses: shivammathur/setup-php@v2 with: @@ -37,7 +37,5 @@ jobs: run: composer install --no-scripts --no-ansi --no-interaction --no-progress - name: Run Unit tests run: vendor/bin/phpunit -v --whitelist ./src tests - - name: Run PHP Code Sniffer - run: vendor/bin/phpcs -p ./src - name: Run PHPStan run: vendor/bin/phpstan analyse --no-progress -c phpstan.neon diff --git a/composer.json b/composer.json index 1b6941d..98c44c8 100644 --- a/composer.json +++ b/composer.json @@ -18,17 +18,15 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "ext-json": "*", "ext-gd": "*", "ext-dom": "*", "ext-zip": "*", "ext-simplexml": "*", "ext-fileinfo": "*", - "doctrine/orm": "~2.19.0", + "doctrine/orm": "~2.20.0", "enshrined/svg-sanitize": "^0.15", - "guzzlehttp/guzzle": "^7.2.0", - "guzzlehttp/psr7": "^2.0", "intervention/image": "^2.5", "league/flysystem": "^3.0", "monolog/monolog": "^1.24.0 || ^2.1.1", @@ -38,17 +36,18 @@ "symfony/filesystem": "6.4.*", "symfony/finder": "6.4.*", "symfony/http-foundation": "6.4.*", + "symfony/http-client-contracts": "^3.5", "symfony/options-resolver": "6.4.*", "symfony/serializer": "6.4.*", - "twig/twig": "^3.1" + "twig/twig": "^3.16" }, "require-dev": { + "api-platform/metadata": "~3.3.11", + "doctrine/doctrine-bundle": "^2.8.1", "php-coveralls/php-coveralls": "^2.4", - "phpunit/phpunit": "^9.5", - "api-platform/metadata": "^3.2.12", - "squizlabs/php_codesniffer": "^3.5", "phpstan/phpstan": "^1.5.3", - "phpstan/phpstan-doctrine": "^1.3" + "phpstan/phpstan-doctrine": "^1.3", + "phpunit/phpunit": "^9.5" }, "autoload": { "psr-4": { @@ -68,8 +67,8 @@ }, "extra": { "branch-alias": { - "dev-master": "2.3.x-dev", - "dev-develop": "2.4.x-dev" + "dev-master": "2.4.x-dev", + "dev-develop": "2.5.x-dev" } } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist deleted file mode 100644 index 19bff0c..0000000 --- a/phpcs.xml.dist +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - src/ - diff --git a/src/AbstractDocumentFactory.php b/src/AbstractDocumentFactory.php index 1e3a0dd..472f78c 100644 --- a/src/AbstractDocumentFactory.php +++ b/src/AbstractDocumentFactory.php @@ -32,7 +32,7 @@ abstract class AbstractDocumentFactory public function __construct( FilesystemOperator $documentsStorage, DocumentFinderInterface $documentFinder, - ?LoggerInterface $logger = null + ?LoggerInterface $logger = null, ) { if (!$documentsStorage instanceof MountManager) { trigger_error('Document Storage must be a MountManager to address public and private files.', E_USER_WARNING); @@ -42,69 +42,56 @@ public function __construct( $this->logger = $logger ?? new NullLogger(); } - /** - * @return File - */ public function getFile(): File { if (null === $this->file) { throw new \BadMethodCallException('File should be defined before using it.'); } + return $this->file; } /** - * @param File $file * @return $this */ public function setFile(File $file): static { $this->file = $file; + return $this; } - /** - * @return FolderInterface|null - */ public function getFolder(): ?FolderInterface { return $this->folder; } /** - * @param FolderInterface|null $folder * @return $this */ public function setFolder(?FolderInterface $folder = null): static { $this->folder = $folder; + return $this; } /** * Special case for SVG without XML statement. - * - * @param DocumentInterface $document */ protected function parseSvgMimeType(DocumentInterface $document): void { if ( - ($document->getMimeType() === 'text/plain' || $document->getMimeType() === 'text/html') && - preg_match('#\.svg$#', $document->getFilename()) + ('text/plain' === $document->getMimeType() || 'text/html' === $document->getMimeType()) + && preg_match('#\.svg$#', $document->getFilename()) ) { $this->logger->debug('Uploaded a SVG without xml declaration. Presuming it’s a valid SVG file.'); $document->setMimeType('image/svg+xml'); } } - /** - * @return DocumentInterface - */ abstract protected function createDocument(): DocumentInterface; - /** - * @param DocumentInterface $document - */ abstract protected function persistDocument(DocumentInterface $document): void; protected function getHashAlgorithm(): string @@ -116,14 +103,14 @@ protected function getHashAlgorithm(): string * Create a document from UploadedFile, Be careful, this method does not flush, only * persists current Document. * - * @param bool $allowEmpty Default false, requires a local file to create new document entity + * @param bool $allowEmpty Default false, requires a local file to create new document entity * @param bool $allowDuplicates Default false, always import new document even if file already exists - * @return null|DocumentInterface + * * @throws FilesystemException */ public function getDocument(bool $allowEmpty = false, bool $allowDuplicates = false): ?DocumentInterface { - if ($allowEmpty === false) { + if (false === $allowEmpty) { // Getter throw exception on null file $file = $this->getFile(); } else { @@ -147,8 +134,8 @@ public function getDocument(bool $allowEmpty = false, bool $allowDuplicates = fa $existingDocument = $this->documentFinder->findOneByHashAndAlgorithm($fileHash, $this->getHashAlgorithm()); if (null !== $existingDocument) { if ( - $existingDocument->isRaw() && - null !== $existingDownscaledDocument = $existingDocument->getDownscaledDocument() + $existingDocument->isRaw() + && null !== $existingDownscaledDocument = $existingDocument->getDownscaledDocument() ) { $existingDocument = $existingDownscaledDocument; } @@ -160,6 +147,7 @@ public function getDocument(bool $allowEmpty = false, bool $allowDuplicates = fa 'File %s already exists with same checksum, do not upload it twice.', $existingDocument->getFilename() )); + return $existingDocument; } } @@ -175,8 +163,8 @@ public function getDocument(bool $allowEmpty = false, bool $allowDuplicates = fa $this->parseSvgMimeType($document); if ( - $document instanceof FileHashInterface && - false !== $fileHash + $document instanceof FileHashInterface + && false !== $fileHash ) { $document->setFileHash($fileHash); $document->setFileHashAlgorithm($this->getHashAlgorithm()); @@ -196,8 +184,6 @@ public function getDocument(bool $allowEmpty = false, bool $allowDuplicates = fa /** * Updates a document from UploadedFile, Be careful, this method does not flush. * - * @param DocumentInterface $document - * @return DocumentInterface * @throws FilesystemException */ public function updateDocument(DocumentInterface $document): DocumentInterface @@ -231,7 +217,7 @@ public function updateDocument(DocumentInterface $document): DocumentInterface } } - $document->setFolder(\mb_substr(hash("crc32b", date('YmdHi')), 0, 12)); + $document->setFolder(\mb_substr(hash('crc32b', date('YmdHi')), 0, 12)); } $document->setFilename($this->getFileName()); @@ -247,9 +233,6 @@ public function updateDocument(DocumentInterface $document): DocumentInterface } /** - * @param File $localFile - * @param DocumentInterface $document - * @return void * @throws FilesystemException */ public function moveFile(File $localFile, DocumentInterface $document): void @@ -267,9 +250,6 @@ public function moveFile(File $localFile, DocumentInterface $document): void } } - /** - * @return string - */ protected function getFileName(): string { $file = $this->getFile(); @@ -278,8 +258,8 @@ protected function getFileName(): string $fileName = $file->getClientOriginalName(); } elseif ( $file instanceof DownloadedFile - && $file->getOriginalFilename() !== null - && $file->getOriginalFilename() !== '' + && null !== $file->getOriginalFilename() + && '' !== $file->getOriginalFilename() ) { $fileName = $file->getOriginalFilename(); } else { @@ -292,9 +272,6 @@ protected function getFileName(): string /** * Create a Document from an external URL. * - * @param string $downloadUrl - * - * @return DocumentInterface|null * @throws FilesystemException */ public function getDocumentFromUrl(string $downloadUrl): ?DocumentInterface @@ -303,6 +280,7 @@ public function getDocumentFromUrl(string $downloadUrl): ?DocumentInterface if (null !== $downloadedFile) { return $this->setFile($downloadedFile)->getDocument(); } + return null; } } diff --git a/src/AbstractDocumentFinder.php b/src/AbstractDocumentFinder.php index ff42399..e400617 100644 --- a/src/AbstractDocumentFinder.php +++ b/src/AbstractDocumentFinder.php @@ -6,49 +6,40 @@ abstract class AbstractDocumentFinder implements DocumentFinderInterface { - /** - * @inheritDoc - */ public function findVideosWithFilename(string $fileName): iterable { $basename = pathinfo($fileName); $basename = $basename['filename']; $sourcesDocsName = [ - $basename . '.ogg', - $basename . '.ogv', - $basename . '.mp4', - $basename . '.mov', - $basename . '.avi', - $basename . '.webm', - $basename . '.mkv', + $basename.'.ogg', + $basename.'.ogv', + $basename.'.mp4', + $basename.'.mov', + $basename.'.avi', + $basename.'.webm', + $basename.'.mkv', ]; return $this->findAllByFilenames($sourcesDocsName); } - /** - * @inheritDoc - */ public function findAudiosWithFilename(string $fileName): iterable { $basename = pathinfo($fileName); $basename = $basename['filename']; $sourcesDocsName = [ - $basename . '.mp3', - $basename . '.ogg', - $basename . '.wav', - $basename . '.m4a', - $basename . '.aac', + $basename.'.mp3', + $basename.'.ogg', + $basename.'.wav', + $basename.'.m4a', + $basename.'.aac', ]; return $this->findAllByFilenames($sourcesDocsName); } - /** - * @inheritDoc - */ public function findPicturesWithFilename(string $fileName): iterable { $pathInfo = pathinfo($fileName); @@ -70,7 +61,7 @@ public function findPicturesWithFilename(string $fileName): iterable $extensionsList = array_diff($extensionsList, [$currentExtension]); // list sources paths for extensions $sourcesDocsName = array_values(array_map(function ($extension) use ($basename) { - return $basename . '.' . $extension; + return $basename.'.'.$extension; }, $extensionsList)); return $this->findAllByFilenames($sourcesDocsName); diff --git a/src/ArrayDocumentFinder.php b/src/ArrayDocumentFinder.php index 14a4f01..56c9ead 100644 --- a/src/ArrayDocumentFinder.php +++ b/src/ArrayDocumentFinder.php @@ -24,6 +24,7 @@ public function __construct() /** * @param array $fileNames + * * @return ArrayCollection */ public function findAllByFilenames(array $fileNames): ArrayCollection @@ -49,9 +50,7 @@ public function findOneByHashAndAlgorithm(string $hash, string $algorithm): ?Doc return null; } - /** - * @param DocumentInterface $document * @return $this */ public function addDocument(DocumentInterface $document): self @@ -59,6 +58,7 @@ public function addDocument(DocumentInterface $document): self if (!$this->documents->contains($document)) { $this->documents->add($document); } + return $this; } } diff --git a/src/AverageColorResolver.php b/src/AverageColorResolver.php index 39ce610..b798905 100644 --- a/src/AverageColorResolver.php +++ b/src/AverageColorResolver.php @@ -11,6 +11,7 @@ class AverageColorResolver public function getAverageColor(Image $image): string { $colorArray = $this->getAverageColorAsArray($image); + return sprintf( '#%02x%02x%02x', $colorArray[0], @@ -18,16 +19,13 @@ public function getAverageColor(Image $image): string $colorArray[2] ); } - /** - * @param Image $image - * - * @return array - */ + public function getAverageColorAsArray(Image $image): array { $image->resize(1, 1); /** @var array $array */ $array = $image->pickColor(0, 0); + return $array; } } diff --git a/src/Console/AbstractDocumentCommand.php b/src/Console/AbstractDocumentCommand.php index 0edba4a..5536b40 100644 --- a/src/Console/AbstractDocumentCommand.php +++ b/src/Console/AbstractDocumentCommand.php @@ -16,16 +16,13 @@ abstract class AbstractDocumentCommand extends Command { - protected ManagerRegistry $managerRegistry; - protected ImageManager $imageManager; - protected FilesystemOperator $documentsStorage; - - public function __construct(ManagerRegistry $managerRegistry, ImageManager $imageManager, FilesystemOperator $documentsStorage) - { - parent::__construct(); - $this->managerRegistry = $managerRegistry; - $this->imageManager = $imageManager; - $this->documentsStorage = $documentsStorage; + public function __construct( + protected ManagerRegistry $managerRegistry, + protected ImageManager $imageManager, + protected FilesystemOperator $documentsStorage, + ?string $name = null, + ) { + parent::__construct($name); } protected function getManager(): ObjectManager @@ -34,26 +31,23 @@ protected function getManager(): ObjectManager } /** - * @return DocumentRepositoryInterface & EntityRepository + * @return DocumentRepositoryInterface&EntityRepository */ protected function getDocumentRepository(): DocumentRepositoryInterface { $repository = $this->managerRegistry->getRepository(DocumentInterface::class); if (!$repository instanceof DocumentRepositoryInterface) { - throw new \InvalidArgumentException('Document repository must implement ' . DocumentRepositoryInterface::class); + throw new \InvalidArgumentException('Document repository must implement '.DocumentRepositoryInterface::class); } + return $repository; } /** - * @param callable $method - * @param SymfonyStyle $io - * @param int $batchSize - * @return int|void * @throws \Doctrine\ORM\NoResultException * @throws \Doctrine\ORM\NonUniqueResultException */ - protected function onEachDocument(callable $method, SymfonyStyle $io, int $batchSize = 20) + protected function onEachDocument(callable $method, SymfonyStyle $io, int $batchSize = 20): int { $i = 0; $manager = $this->getManager(); @@ -65,6 +59,7 @@ protected function onEachDocument(callable $method, SymfonyStyle $io, int $batch if ($count < 1) { $io->success('No document found'); + return 0; } @@ -85,5 +80,7 @@ protected function onEachDocument(callable $method, SymfonyStyle $io, int $batch } $manager->flush(); $io->progressFinish(); + + return 0; } } diff --git a/src/Console/DocumentAverageColorCommand.php b/src/Console/DocumentAverageColorCommand.php index 796047d..fb6750b 100644 --- a/src/Console/DocumentAverageColorCommand.php +++ b/src/Console/DocumentAverageColorCommand.php @@ -27,32 +27,32 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $this->io = new SymfonyStyle($input, $output); - $this->onEachDocument(function (DocumentInterface $document) { + return $this->onEachDocument(function (DocumentInterface $document) { $this->updateDocumentColor($document); }, new SymfonyStyle($input, $output)); - - return 0; } private function updateDocumentColor(DocumentInterface $document): void { - if ($document->isImage() && $document instanceof AdvancedDocumentInterface) { - $mountPath = $document->getMountPath(); - if (null === $mountPath) { - return; - } - try { - $mediumColor = (new AverageColorResolver())->getAverageColor($this->imageManager->make( - $this->documentsStorage->readStream($mountPath) - )); - $document->setImageAverageColor($mediumColor); - } catch (NotReadableException $exception) { - /* - * Do nothing - * just return 0 width and height - */ - $this->io->error($mountPath . ' is not a readable image.'); - } + if (!$document->isImage() || !($document instanceof AdvancedDocumentInterface)) { + return; + } + + $mountPath = $document->getMountPath(); + if (null === $mountPath) { + return; + } + try { + $mediumColor = (new AverageColorResolver())->getAverageColor($this->imageManager->make( + $this->documentsStorage->readStream($mountPath) + )); + $document->setImageAverageColor($mediumColor); + } catch (NotReadableException $exception) { + /* + * Do nothing + * just return 0 width and height + */ + $this->io->error($mountPath.' is not a readable image.'); } } } diff --git a/src/Console/DocumentClearFolderCommand.php b/src/Console/DocumentClearFolderCommand.php index e52f187..13c0881 100644 --- a/src/Console/DocumentClearFolderCommand.php +++ b/src/Console/DocumentClearFolderCommand.php @@ -28,6 +28,7 @@ protected function configure(): void protected function getDocumentQueryBuilder(FolderInterface $folder): QueryBuilder { $qb = $this->getDocumentRepository()->createQueryBuilder('d'); + return $qb->innerJoin('d.folders', 'f') ->andWhere($qb->expr()->eq('f.id', ':folderId')) ->setParameter(':folderId', $folder); @@ -44,7 +45,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $em = $this->getManager(); /** @var FolderInterface|null $folder */ $folder = $em->find(FolderInterface::class, $folderId); - if ($folder === null) { + if (null === $folder) { throw new \InvalidArgumentException(sprintf('Folder #%d does not exist.', $folderId)); } @@ -58,33 +59,36 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($count <= 0) { $this->io->warning('No documents were found in this folder.'); + return 0; } if ( - $this->io->askQuestion(new ConfirmationQuestion( + !$this->io->askQuestion(new ConfirmationQuestion( sprintf('Are you sure to delete permanently %d documents?', $count), false )) ) { - /** @var DocumentInterface[] $results */ - $results = $this->getDocumentQueryBuilder($folder) - ->select('d') - ->getQuery() - ->getResult(); - - $this->io->progressStart($count); - foreach ($results as $document) { - $em->remove($document); - if (($i % $batchSize) === 0) { - $em->flush(); // Executes all updates. - } - ++$i; - $this->io->progressAdvance(); + return 0; + } + + /** @var DocumentInterface[] $results */ + $results = $this->getDocumentQueryBuilder($folder) + ->select('d') + ->getQuery() + ->getResult(); + + $this->io->progressStart($count); + foreach ($results as $document) { + $em->remove($document); + if (($i % $batchSize) === 0) { + $em->flush(); // Executes all updates. } - $em->flush(); - $this->io->progressFinish(); + ++$i; + $this->io->progressAdvance(); } + $em->flush(); + $this->io->progressFinish(); return 0; } diff --git a/src/Console/DocumentDownscaleCommand.php b/src/Console/DocumentDownscaleCommand.php index 3868230..ad3834b 100644 --- a/src/Console/DocumentDownscaleCommand.php +++ b/src/Console/DocumentDownscaleCommand.php @@ -12,9 +12,11 @@ use RZ\Roadiz\Documents\Events\CachePurgeAssetsRequestEvent; use RZ\Roadiz\Documents\Models\DocumentInterface; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Process\PhpSubprocess; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** @@ -22,27 +24,24 @@ */ class DocumentDownscaleCommand extends AbstractDocumentCommand { - private ?int $maxPixelSize; - private DownscaleImageManager $downscaler; - private EventDispatcherInterface $dispatcher; - public function __construct( ManagerRegistry $managerRegistry, ImageManager $imageManager, FilesystemOperator $documentsStorage, - ?int $maxPixelSize, - DownscaleImageManager $downscaler, - EventDispatcherInterface $dispatcher + private readonly ?int $maxPixelSize, + private readonly DownscaleImageManager $downscaler, + private readonly EventDispatcherInterface $dispatcher, + ?string $name = null, ) { - parent::__construct($managerRegistry, $imageManager, $documentsStorage); - $this->maxPixelSize = $maxPixelSize; - $this->downscaler = $downscaler; - $this->dispatcher = $dispatcher; + parent::__construct($managerRegistry, $imageManager, $documentsStorage, $name); } protected function configure(): void { $this->setName('documents:downscale') + ->addOption('process-count', 'p', InputOption::VALUE_REQUIRED, 'Number of processes to run in parallel.', 1) + ->addOption('limit', null, InputOption::VALUE_REQUIRED, 'Number of document to process in one process', null) + ->addOption('offset', null, InputOption::VALUE_REQUIRED, 'Offset number of document to process after', null) ->setDescription('Downscale every document according to max pixel size defined in configuration.'); } @@ -50,48 +49,101 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - if (null !== $this->maxPixelSize && $this->maxPixelSize > 0) { - $confirmation = new ConfirmationQuestion( - 'Are you sure to downscale all your image documents to ' . $this->maxPixelSize . 'px?', - false - ); - if ( - $io->askQuestion( - $confirmation - ) - ) { - /** @var DocumentInterface[] $documents */ - $documents = $this->getDocumentRepository() - ->findBy([ - 'mimeType' => [ - 'image/png', - 'image/jpeg', - 'image/gif', - 'image/tiff', - ], - 'raw' => false, - ]); - $io->progressStart(\count($documents)); - - foreach ($documents as $document) { - try { - $this->downscaler->processDocumentFromExistingRaw($document); - } catch (NotReadableException $exception) { - $io->error($exception->getMessage() . ' - ' . (string) $document); - } - $io->progressAdvance(); - } - - $io->progressFinish(); - $io->success('Every documents have been downscaled, a raw version has been kept.'); - - $this->dispatcher->dispatch(new CachePurgeAssetsRequestEvent()); - } - return 0; - } else { + if (null === $this->maxPixelSize || $this->maxPixelSize <= 0) { $io->warning('Your configuration is not set for downscaling documents.'); $io->note('Add assetsProcessing.maxPixelSize parameter in your config.yml file.'); + return 1; } + + $confirmation = new ConfirmationQuestion( + 'Are you sure to downscale all your image documents to '.$this->maxPixelSize.'px?', + false + ); + if ($input->isInteractive() && !$io->askQuestion($confirmation)) { + return 0; + } + + $criteria = [ + 'mimeType' => [ + 'image/avif', + 'image/bmp', + 'image/gif', + 'image/heic', + 'image/heif', + 'image/jpeg', + 'image/png', + 'image/tiff', + 'image/webp', + ], + 'raw' => false, + ]; + $processCount = (int) $input->getOption('process-count'); + /* + * Switch to async processes to batch document downscaling. + */ + if ($processCount > 1) { + $documentsCount = $this->getDocumentRepository()->countBy($criteria); + $io->info(sprintf('Using %d processes to downscale %d documents.', $processCount, $documentsCount)); + + // Spawn processes for current command with limit and offset parameters + $documentsPerProcess = (int) ceil($documentsCount / $processCount); + /** @var array $processes */ + $processes = []; + + for ($i = 0; $i < $processCount; ++$i) { + $offset = $i * $documentsPerProcess; + $limit = $documentsPerProcess; + + $command = [ + 'bin/console', + 'documents:downscale', + '-n', + '--process-count=1', + '--limit='.$limit, + '--offset='.$offset, + ]; + + $process = new PhpSubprocess($command); + $process->setTimeout(3600); + $process->start(); + $processes[] = $process; + + $io->text(sprintf('Started documents:downscale process %d with offset %d and limit %d', $i + 1, $offset, $limit)); + } + // Wait for all processes to finish + foreach ($processes as $process) { + $process->wait(); + } + + $io->success('All processes have finished.'); + + return 0; + } + + /** @var DocumentInterface[] $documents */ + $documents = $this->getDocumentRepository()->findBy( + $criteria, + [], + is_numeric($input->getOption('limit')) ? (int) $input->getOption('limit') : null, + is_numeric($input->getOption('offset')) ? (int) $input->getOption('offset') : null + ); + $io->progressStart(count($documents)); + + foreach ($documents as $document) { + try { + $this->downscaler->processDocumentFromExistingRaw($document); + } catch (NotReadableException $exception) { + $io->error($exception->getMessage().' - '.(string) $document); + } + $io->progressAdvance(); + } + + $io->progressFinish(); + $io->success('Every documents have been downscaled, a raw version has been kept.'); + + $this->dispatcher->dispatch(new CachePurgeAssetsRequestEvent()); + + return 0; } } diff --git a/src/Console/DocumentDuplicatesCommand.php b/src/Console/DocumentDuplicatesCommand.php index 0beb383..f26007d 100644 --- a/src/Console/DocumentDuplicatesCommand.php +++ b/src/Console/DocumentDuplicatesCommand.php @@ -29,12 +29,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int $count = \count($documents); $rows = []; - if ($count <= 0) { + if (0 === $count) { $this->io->success('No duplicated documents were found.'); + return 0; } - /** @var DocumentInterface & FileHashInterface $document */ + /** @var DocumentInterface&FileHashInterface $document */ foreach ($documents as $document) { $rows[] = [ 'ID' => (string) $document, @@ -45,7 +46,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $this->io->table([ - 'ID', 'Filename', 'Hash', 'Algo' + 'ID', 'Filename', 'Hash', 'Algo', ], $rows); return 0; diff --git a/src/Console/DocumentFileHashCommand.php b/src/Console/DocumentFileHashCommand.php index ecfe884..416372f 100644 --- a/src/Console/DocumentFileHashCommand.php +++ b/src/Console/DocumentFileHashCommand.php @@ -39,11 +39,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $defaultAlgorithm = 'sha256'; } if (!\in_array($defaultAlgorithm, \hash_algos())) { - throw new \RuntimeException(sprintf( - '“%s” algorithm is not available. Choose one from \hash_algos() method (%s)', - $defaultAlgorithm, - implode(', ', \hash_algos()) - )); + throw new \RuntimeException(sprintf('“%s” algorithm is not available. Choose one from \hash_algos() method (%s)', $defaultAlgorithm, implode(', ', \hash_algos()))); } $em = $this->getManager(); @@ -52,6 +48,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($count <= 0) { $this->io->success('All document files have hash.'); + return 0; } @@ -59,22 +56,25 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** @var DocumentInterface $document */ foreach ($documents as $document) { $mountPath = $document->getMountPath(); - if (null !== $mountPath && $document instanceof FileHashInterface) { - $algorithm = $document->getFileHashAlgorithm() ?? $defaultAlgorithm; - # https://flysystem.thephpleague.com/docs/usage/checksums/ - $this->documentsStorage->checksum($mountPath, ['checksum_algo' => $algorithm]); - if ($this->documentsStorage->fileExists($mountPath)) { - $fileHash = $this->documentsStorage->checksum($mountPath, ['checksum_algo' => $algorithm]); - $document->setFileHash($fileHash); - $document->setFileHashAlgorithm($algorithm); - } - - if (($i % $batchSize) === 0) { - $em->flush(); // Executes all updates. - } - ++$i; + if (null === $mountPath || !($document instanceof FileHashInterface)) { $this->io->progressAdvance(); + continue; + } + + $algorithm = $document->getFileHashAlgorithm() ?? $defaultAlgorithm; + // https://flysystem.thephpleague.com/docs/usage/checksums/ + $this->documentsStorage->checksum($mountPath, ['checksum_algo' => $algorithm]); + if ($this->documentsStorage->fileExists($mountPath)) { + $fileHash = $this->documentsStorage->checksum($mountPath, ['checksum_algo' => $algorithm]); + $document->setFileHash($fileHash); + $document->setFileHashAlgorithm($algorithm); + } + + if (($i % $batchSize) === 0) { + $em->flush(); // Executes all updates. } + ++$i; + $this->io->progressAdvance(); } $em->flush(); $this->io->progressFinish(); diff --git a/src/Console/DocumentFilesizeCommand.php b/src/Console/DocumentFilesizeCommand.php index a398f25..1b8ec76 100644 --- a/src/Console/DocumentFilesizeCommand.php +++ b/src/Console/DocumentFilesizeCommand.php @@ -24,25 +24,24 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $em = $this->getManager(); $this->io = new SymfonyStyle($input, $output); - $this->onEachDocument(function (DocumentInterface $document) { + return $this->onEachDocument(function (DocumentInterface $document) { if ($document instanceof AdvancedDocumentInterface) { $this->updateDocumentFilesize($document); } }, new SymfonyStyle($input, $output)); - return 0; } private function updateDocumentFilesize(AdvancedDocumentInterface $document): void { - if (null !== $document->getMountPath()) { - try { - $document->setFilesize($this->documentsStorage->fileSize($document->getMountPath())); - } catch (FilesystemException $exception) { - $this->io->error($exception->getMessage()); - } + if (null === $document->getMountPath()) { + return; + } + try { + $document->setFilesize($this->documentsStorage->fileSize($document->getMountPath())); + } catch (FilesystemException $exception) { + $this->io->error($exception->getMessage()); } } } diff --git a/src/Console/DocumentPruneCommand.php b/src/Console/DocumentPruneCommand.php index c2954f6..008b0cd 100644 --- a/src/Console/DocumentPruneCommand.php +++ b/src/Console/DocumentPruneCommand.php @@ -36,36 +36,40 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($count <= 0) { $this->io->warning('All documents are used.'); + return 0; } if ($input->getOption('dry-run')) { $this->io->info(sprintf( - '%d documents are not used by a node-source, a tag, a setting or an attribute.', + '%d documents are not used by a node-source, a tag, a setting, a custom-form answer or an attribute.', $count )); + return 0; } if ( - $this->io->askQuestion(new ConfirmationQuestion( + !$this->io->askQuestion(new ConfirmationQuestion( sprintf('Are you sure to delete permanently %d unused documents?', $count), false )) ) { - $this->io->progressStart($count); - /** @var DocumentInterface $document */ - foreach ($documents as $document) { - $em->remove($document); - if (($i % $batchSize) === 0) { - $em->flush(); // Executes all updates. - } - ++$i; - $this->io->progressAdvance(); + return 0; + } + + $this->io->progressStart($count); + /** @var DocumentInterface $document */ + foreach ($documents as $document) { + $em->remove($document); + if (($i % $batchSize) === 0) { + $em->flush(); // Executes all updates. } - $em->flush(); - $this->io->progressFinish(); + ++$i; + $this->io->progressAdvance(); } + $em->flush(); + $this->io->progressFinish(); return 0; } diff --git a/src/Console/DocumentPruneOrphansCommand.php b/src/Console/DocumentPruneOrphansCommand.php index b34ed74..a3e9e85 100644 --- a/src/Console/DocumentPruneOrphansCommand.php +++ b/src/Console/DocumentPruneOrphansCommand.php @@ -39,40 +39,40 @@ protected function execute(InputInterface $input, OutputInterface $output): int }, new SymfonyStyle($input, $output)); $this->io->success(sprintf('%d documents were deleted.', $deleteCount)); + return 0; } /** - * @param DocumentInterface $document - * @param ObjectManager $entityManager - * @param int $deleteCount - * @param bool $dryRun * @throws FilesystemException */ private function checkDocumentFilesystem( DocumentInterface $document, ObjectManager $entityManager, int &$deleteCount, - bool $dryRun = false + bool $dryRun = false, ): void { /* * Do not prune embed documents which may not have any file */ $mountPath = $document->getMountPath(); - if (null !== $mountPath && !$document->isEmbed()) { - if (!$this->documentsStorage->fileExists($mountPath)) { - if ($this->io->isDebug() && !$this->io->isQuiet()) { - $this->io->writeln(sprintf( - '%s file does not exist, pruning document %s', - $document->getMountPath(), - (string) $document - )); - } - if (!$dryRun) { - $entityManager->remove($document); - $deleteCount++; - } - } + if (null === $mountPath || $document->isEmbed()) { + return; + } + if ($this->documentsStorage->fileExists($mountPath)) { + return; + } + + if ($this->io->isDebug() && !$this->io->isQuiet()) { + $this->io->writeln(sprintf( + '%s file does not exist, pruning document %s', + $document->getMountPath(), + (string) $document + )); + } + if (!$dryRun) { + $entityManager->remove($document); + ++$deleteCount; } } } diff --git a/src/Console/DocumentSizeCommand.php b/src/Console/DocumentSizeCommand.php index c9368b7..466b9f9 100644 --- a/src/Console/DocumentSizeCommand.php +++ b/src/Console/DocumentSizeCommand.php @@ -27,13 +27,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $this->io = new SymfonyStyle($input, $output); - $this->onEachDocument(function (DocumentInterface $document) { + return $this->onEachDocument(function (DocumentInterface $document) { if ($document instanceof SizeableInterface) { $this->updateDocumentSize($document); } }, new SymfonyStyle($input, $output)); - - return 0; } private function updateDocumentSize(DocumentInterface $document): void @@ -42,7 +40,18 @@ private function updateDocumentSize(DocumentInterface $document): void return; } $mountPath = $document->getMountPath(); - if (null !== $mountPath && $document->isImage()) { + if (null === $mountPath) { + return; + } + if ($document->isSvg()) { + try { + $svgSizeResolver = new SvgSizeResolver($document, $this->documentsStorage); + $document->setImageWidth($svgSizeResolver->getWidth()); + $document->setImageHeight($svgSizeResolver->getHeight()); + } catch (\RuntimeException $exception) { + $this->io->error($exception->getMessage()); + } + } elseif ($document->isImage()) { try { $imageProcess = $this->imageManager->make($this->documentsStorage->readStream($mountPath)); $document->setImageWidth($imageProcess->width()); @@ -52,15 +61,7 @@ private function updateDocumentSize(DocumentInterface $document): void * Do nothing * just return 0 width and height */ - $this->io->error($document->getMountPath() . ' is not a readable image.'); - } - } elseif ($document->isSvg()) { - try { - $svgSizeResolver = new SvgSizeResolver($document, $this->documentsStorage); - $document->setImageWidth($svgSizeResolver->getWidth()); - $document->setImageHeight($svgSizeResolver->getHeight()); - } catch (\RuntimeException $exception) { - $this->io->error($exception->getMessage()); + $this->io->error($document->getMountPath().' is not a readable image.'); } } } diff --git a/src/DocumentArchiver.php b/src/DocumentArchiver.php index e29f746..e4cc8f8 100644 --- a/src/DocumentArchiver.php +++ b/src/DocumentArchiver.php @@ -22,15 +22,15 @@ public function __construct(private readonly FilesystemOperator $documentsStorag /** * @param iterable $documents - * @param string $name - * @param bool $keepFolders + * * @return string Zip file path + * * @throws FilesystemException */ public function archive(iterable $documents, string $name, bool $keepFolders = true): string { - $filename = (new AsciiSlugger())->slug($name . ' ' . date('YmdHis'), '_') . '.zip'; - $tmpFileName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $filename; + $filename = (new AsciiSlugger())->slug($name.' '.date('YmdHis'), '_').'.zip'; + $tmpFileName = sys_get_temp_dir().DIRECTORY_SEPARATOR.$filename; $zip = new \ZipArchive(); $zip->open($tmpFileName, \ZipArchive::CREATE); @@ -44,7 +44,7 @@ public function archive(iterable $documents, string $name, bool $keepFolders = t $mountPath = $document->getMountPath(); if (null !== $mountPath && $this->documentsStorage->fileExists($mountPath)) { if ($keepFolders) { - $zipPathname = $document->getFolder() . DIRECTORY_SEPARATOR . $document->getFilename(); + $zipPathname = $document->getFolder().DIRECTORY_SEPARATOR.$document->getFilename(); } else { $zipPathname = $document->getFilename(); } @@ -59,17 +59,14 @@ public function archive(iterable $documents, string $name, bool $keepFolders = t /** * @param iterable $documents - * @param string $name - * @param bool $keepFolders - * @param bool $unlink - * @return BinaryFileResponse + * * @throws FilesystemException */ public function archiveAndServe( iterable $documents, string $name, bool $keepFolders = true, - bool $unlink = true + bool $unlink = true, ): BinaryFileResponse { $filename = $this->archive($documents, $name, $keepFolders); $response = new BinaryFileResponse( @@ -80,6 +77,7 @@ public function archiveAndServe( 'attachment' ); $response->deleteFileAfterSend($unlink); + return $response; } } diff --git a/src/DocumentFinderInterface.php b/src/DocumentFinderInterface.php index 336f23a..a446de4 100644 --- a/src/DocumentFinderInterface.php +++ b/src/DocumentFinderInterface.php @@ -16,37 +16,24 @@ interface DocumentFinderInterface public function findAllByFilenames(array $fileNames): iterable; /** - * @param string $fileName - * * @return iterable */ public function findVideosWithFilename(string $fileName): iterable; /** - * @param string $fileName - * * @return iterable */ public function findAudiosWithFilename(string $fileName): iterable; /** - * @param string $fileName - * * @return iterable */ public function findPicturesWithFilename(string $fileName): iterable; /** * @param array $fileNames - * - * @return DocumentInterface|null */ public function findOneByFilenames(array $fileNames): ?DocumentInterface; - /** - * @param string $hash - * @param string $algorithm - * @return DocumentInterface|null - */ public function findOneByHashAndAlgorithm(string $hash, string $algorithm): ?DocumentInterface; } diff --git a/src/DownloadedFile.php b/src/DownloadedFile.php index 9677fa0..838c42f 100644 --- a/src/DownloadedFile.php +++ b/src/DownloadedFile.php @@ -4,8 +4,6 @@ namespace RZ\Roadiz\Documents; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Psr7\Utils; use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\String\UnicodeString; @@ -13,19 +11,11 @@ class DownloadedFile extends File { protected ?string $originalFilename; - /** - * @return string|null - */ public function getOriginalFilename(): ?string { return $this->originalFilename; } - /** - * @param string|null $originalFilename - * - * @return DownloadedFile - */ public function setOriginalFilename(?string $originalFilename): DownloadedFile { $this->originalFilename = $originalFilename; @@ -34,10 +24,7 @@ public function setOriginalFilename(?string $originalFilename): DownloadedFile } /** - * Final constructor for safe usage in DownloadedFile::fromUrl - * - * @param string $path - * @param bool $checkPath + * Final constructor for safe usage in DownloadedFile::fromUrl. */ final public function __construct(string $path, bool $checkPath = true) { @@ -46,10 +33,6 @@ final public function __construct(string $path, bool $checkPath = true) /** * Transform to lowercase and replace every non-alpha character with an underscore. - * - * @param string|null $string - * - * @return string */ public static function sanitizeFilename(?string $string): string { @@ -66,29 +49,27 @@ public static function sanitizeFilename(?string $string): string ; } - /** - * @param string $url - * @param string|null $originalName - * - * @return DownloadedFile|null - */ public static function fromUrl(string $url, ?string $originalName = null): ?DownloadedFile { try { $baseName = static::sanitizeFilename(pathinfo($url, PATHINFO_BASENAME)); - $distantHandle = fopen($url, 'r'); - if (false === $distantHandle) { + $distantResource = fopen($url, 'r'); + if (false === $distantResource) { return null; } - $original = Utils::streamFor($distantHandle); + $tmpFile = tempnam(sys_get_temp_dir(), static::sanitizeFilename($baseName)); if (false === $tmpFile) { return null; } - $handle = fopen($tmpFile, 'w'); - $local = Utils::streamFor($handle); - $local->write($original->getContents()); - $local->close(); + $localResource = fopen($tmpFile, 'w'); + if (false === $localResource) { + throw new \RuntimeException('Unable to open local resource.'); + } + $result = \stream_copy_to_stream($distantResource, $localResource); + if (false === $result) { + throw new \RuntimeException('Unable to copy distant stream to local resource.'); + } $file = new static($tmpFile); if (!empty($originalName)) { @@ -99,18 +80,17 @@ public static function fromUrl(string $url, ?string $originalName = null): ?Down /* * Some OEmbed providers won't add any extension in original filename. */ - if ($file->getExtension() === '' && null !== $guessedExtension = $file->guessExtension()) { - $file->setOriginalFilename($file->getOriginalFilename() . '.' . $guessedExtension); + if ('' === $file->getExtension() && null !== $guessedExtension = $file->guessExtension()) { + $file->setOriginalFilename($file->getOriginalFilename().'.'.$guessedExtension); } if ($file->isReadable() && filesize($file->getPathname()) > 0) { return $file; } - } catch (RequestException $e) { - return null; - } catch (\ErrorException $e) { + } catch (\RuntimeException $e) { return null; } + return null; } } diff --git a/src/DownscaleImageManager.php b/src/DownscaleImageManager.php index f141b3a..af555ad 100644 --- a/src/DownscaleImageManager.php +++ b/src/DownscaleImageManager.php @@ -22,14 +22,13 @@ public function __construct( private readonly ImageManager $imageManager, private readonly ?LoggerInterface $logger = null, private readonly int $maxPixelSize = 0, - private readonly string $rawImageSuffix = ".raw" + private readonly string $rawImageSuffix = '.raw', ) { } /** * Downscale document if needed, overriding raw document. * - * @param DocumentInterface|null $document * @throws FilesystemException */ public function processAndOverrideDocument(?DocumentInterface $document = null): void @@ -50,7 +49,7 @@ public function processAndOverrideDocument(?DocumentInterface $document = null): $this->logger->info( 'Document has been downscaled.', [ - 'path' => $mountPath + 'path' => $mountPath, ] ); } @@ -61,7 +60,6 @@ public function processAndOverrideDocument(?DocumentInterface $document = null): /** * Downscale document if needed, keeping existing raw document. * - * @param DocumentInterface|null $document * @throws FilesystemException */ public function processDocumentFromExistingRaw(?DocumentInterface $document = null): void @@ -93,14 +91,11 @@ public function processDocumentFromExistingRaw(?DocumentInterface $document = nu /** * Get downscaled image if size is higher than limit, * returns original image if lower or if image is a GIF. - * - * @param Image $processImage - * @return Image|null */ protected function getDownscaledImage(Image $processImage): ?Image { if ( - $processImage->mime() !== 'image/gif' + 'image/gif' !== $processImage->mime() && ($processImage->width() > $this->maxPixelSize || $processImage->height() > $this->maxPixelSize) ) { // prevent possible upsizing @@ -112,25 +107,23 @@ function (Constraint $constraint) { $constraint->upsize(); } ); + return $processImage; } + return null; } - /** - * @param DocumentInterface $document - * @return void - */ protected function updateDocumentFileHash(DocumentInterface $document): void { /* * We need to re-hash file after being downscaled */ if ( - $document instanceof FileHashInterface && - null !== $document->getFileHashAlgorithm() + $document instanceof FileHashInterface + && null !== $document->getFileHashAlgorithm() ) { - /** @var DocumentInterface & FileHashInterface $document */ + /** @var DocumentInterface&FileHashInterface $document */ $mountPath = $document->getMountPath(); if (null === $mountPath) { return; @@ -143,20 +136,16 @@ protected function updateDocumentFileHash(DocumentInterface $document): void } /** - * @param DocumentInterface $originalDocument - * @param Image|null $processImage - * @param bool $keepExistingRaw - * @return DocumentInterface|null * @throws FilesystemException */ protected function createDocumentFromImage( DocumentInterface $originalDocument, - Image $processImage = null, - bool $keepExistingRaw = false + ?Image $processImage = null, + bool $keepExistingRaw = false, ): ?DocumentInterface { if ( - false === $keepExistingRaw && - null !== $formerRawDoc = $originalDocument->getRawDocument() + false === $keepExistingRaw + && null !== $formerRawDoc = $originalDocument->getRawDocument() ) { /* * When document already exists with a raw doc reference. @@ -173,7 +162,7 @@ protected function createDocumentFromImage( $this->em->flush(); } - if (null === $originalDocument->getRawDocument() || $keepExistingRaw === false) { + if (null === $originalDocument->getRawDocument() || false === $keepExistingRaw) { if (null === $processImage) { return $originalDocument; } @@ -186,7 +175,7 @@ protected function createDocumentFromImage( $rawDocument = clone $originalDocument; $rawDocumentName = preg_replace( '#\.(jpe?g|gif|tiff?|png|psd|webp|avif|heic|heif)$#', - $this->rawImageSuffix . '.$1', + $this->rawImageSuffix.'.$1', $originalDocument->getFilename() ); if (null === $rawDocumentName) { @@ -197,10 +186,10 @@ protected function createDocumentFromImage( $rawDocumentPath = $rawDocument->getMountPath(); if ( - null !== $originalDocumentPath && - null !== $rawDocumentPath && - $this->documentsStorage->fileExists($originalDocumentPath) && - !$this->documentsStorage->fileExists($rawDocumentPath) + null !== $originalDocumentPath + && null !== $rawDocumentPath + && $this->documentsStorage->fileExists($originalDocumentPath) + && !$this->documentsStorage->fileExists($rawDocumentPath) ) { /* * Original document path becomes raw document path. Rename it. @@ -226,6 +215,7 @@ protected function createDocumentFromImage( return $originalDocument; } + return null; } elseif (null !== $processImage) { /* diff --git a/src/Events/DocumentCreatedEvent.php b/src/Events/DocumentCreatedEvent.php index 0a15eac..aaa6890 100644 --- a/src/Events/DocumentCreatedEvent.php +++ b/src/Events/DocumentCreatedEvent.php @@ -5,7 +5,7 @@ namespace RZ\Roadiz\Documents\Events; /** - * Event dispatched on document creation AFTER DB flushed + * Event dispatched on document creation AFTER DB flushed. */ final class DocumentCreatedEvent extends FilterDocumentEvent { diff --git a/src/Events/DocumentDeletedEvent.php b/src/Events/DocumentDeletedEvent.php index c5825bd..1337647 100644 --- a/src/Events/DocumentDeletedEvent.php +++ b/src/Events/DocumentDeletedEvent.php @@ -5,7 +5,7 @@ namespace RZ\Roadiz\Documents\Events; /** - * Event dispatched on document deletion BEFORE DB flushed + * Event dispatched on document deletion BEFORE DB flushed. */ final class DocumentDeletedEvent extends FilterDocumentEvent { diff --git a/src/Events/DocumentFileUpdatedEvent.php b/src/Events/DocumentFileUpdatedEvent.php index 5879c51..123d21d 100644 --- a/src/Events/DocumentFileUpdatedEvent.php +++ b/src/Events/DocumentFileUpdatedEvent.php @@ -5,7 +5,7 @@ namespace RZ\Roadiz\Documents\Events; /** - * Event dispatched on document file updated AFTER DB flushed + * Event dispatched on document file updated AFTER DB flushed. */ final class DocumentFileUpdatedEvent extends FilterDocumentEvent { diff --git a/src/Events/DocumentLifeCycleSubscriber.php b/src/Events/DocumentLifeCycleSubscriber.php index 714f217..45a24fa 100644 --- a/src/Events/DocumentLifeCycleSubscriber.php +++ b/src/Events/DocumentLifeCycleSubscriber.php @@ -4,10 +4,10 @@ namespace RZ\Roadiz\Documents\Events; -use Doctrine\Common\EventSubscriber; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; +use Doctrine\ORM\Event\PostRemoveEventArgs; use Doctrine\ORM\Event\PreUpdateEventArgs; use Doctrine\ORM\Events; -use Doctrine\Persistence\Event\LifecycleEventArgs; use League\Flysystem\FilesystemException; use League\Flysystem\FilesystemOperator; use League\Flysystem\Visibility; @@ -17,49 +17,40 @@ /** * Handle file management on document's lifecycle events. */ -class DocumentLifeCycleSubscriber implements EventSubscriber +#[AsDoctrineListener(event: Events::postRemove)] +#[AsDoctrineListener(event: Events::preUpdate)] +final readonly class DocumentLifeCycleSubscriber { - private FilesystemOperator $documentsStorage; - - public function __construct(FilesystemOperator $documentsStorage) - { - $this->documentsStorage = $documentsStorage; - } - - /** - * {@inheritdoc} - */ - public function getSubscribedEvents(): array + public function __construct(private FilesystemOperator $documentsStorage) { - return array( - Events::postRemove, - Events::preUpdate, - ); } /** - * @param PreUpdateEventArgs $args * @throws FilesystemException */ public function preUpdate(PreUpdateEventArgs $args): void { $document = $args->getObject(); + + if (!$document instanceof DocumentInterface) { + return; + } + if ( - $document instanceof DocumentInterface - && $args->hasChangedField('filename') + $args->hasChangedField('filename') && is_string($args->getOldValue('filename')) && is_string($args->getNewValue('filename')) - && $args->getOldValue('filename') !== '' + && '' !== $args->getOldValue('filename') ) { // This method must not throw any exception // because filename WILL change if document file is updated too. $this->renameDocumentFilename($document, $args); } - if ($document instanceof DocumentInterface && $args->hasChangedField('private')) { - if ($document->isPrivate() === true) { - $this->makePrivate($document, $args); + if ($args->hasChangedField('private')) { + if (true === $document->isPrivate()) { + $this->makePrivate($document); } else { - $this->makePublic($document, $args); + $this->makePublic($document); } } } @@ -87,12 +78,7 @@ private function renameDocumentFilename(DocumentInterface $document, PreUpdateEv $this->documentsStorage->move($oldPath, $newPath); } - /** - * @param DocumentInterface $document - * @param PreUpdateEventArgs $args - * @throws FilesystemException - */ - protected function makePublic(DocumentInterface $document, PreUpdateEventArgs $args): void + private function makePublic(DocumentInterface $document): void { $this->validateDocument($document); $documentPublicPath = $this->getDocumentPublicPath($document); @@ -108,12 +94,7 @@ protected function makePublic(DocumentInterface $document, PreUpdateEventArgs $a } } - /** - * @param DocumentInterface $document - * @param PreUpdateEventArgs $args - * @throws FilesystemException - */ - protected function makePrivate(DocumentInterface $document, PreUpdateEventArgs $args): void + private function makePrivate(DocumentInterface $document): void { $this->validateDocument($document); $documentPublicPath = $this->getDocumentPublicPath($document); @@ -132,36 +113,36 @@ protected function makePrivate(DocumentInterface $document, PreUpdateEventArgs $ /** * Unlink file after document has been deleted. * - * @param LifecycleEventArgs $args * @throws FilesystemException */ - public function postRemove(LifecycleEventArgs $args): void + public function postRemove(PostRemoveEventArgs $args): void { $document = $args->getObject(); - if ($document instanceof DocumentInterface) { - try { - $this->validateDocument($document); - $document->setRawDocument(null); - $documentPath = $this->getDocumentPath($document); - - if ($this->documentsStorage->fileExists($documentPath)) { - $this->documentsStorage->delete($documentPath); - } - $this->cleanFileDirectory($this->getDocumentFolderPath($document)); - } catch (DocumentWithoutFileException $e) { - // Do nothing when document does not have any file on system. + + if (!$document instanceof DocumentInterface) { + return; + } + + try { + $this->validateDocument($document); + $document->setRawDocument(null); + $documentPath = $this->getDocumentPath($document); + + if ($this->documentsStorage->fileExists($documentPath)) { + $this->documentsStorage->delete($documentPath); } + $this->cleanFileDirectory($this->getDocumentFolderPath($document)); + } catch (DocumentWithoutFileException $e) { + // Do nothing when document does not have any file on system. } } /** * Remove document directory if there is no other file in it. * - * @param string $documentFolderPath - * @return void * @throws FilesystemException */ - protected function cleanFileDirectory(string $documentFolderPath): void + private function cleanFileDirectory(string $documentFolderPath): void { if ($this->documentsStorage->directoryExists($documentFolderPath)) { $isDirEmpty = \count($this->documentsStorage->listContents($documentFolderPath)->toArray()) <= 0; @@ -171,100 +152,66 @@ protected function cleanFileDirectory(string $documentFolderPath): void } } - /** - * @param DocumentInterface $document - * @param string $filename - * - * @return string - */ - protected function getDocumentRelativePathForFilename(DocumentInterface $document, string $filename): string + private function getDocumentRelativePathForFilename(DocumentInterface $document, string $filename): string { $this->validateDocument($document); - return $document->getFolder() . DIRECTORY_SEPARATOR . $filename; + return $document->getFolder().DIRECTORY_SEPARATOR.$filename; } - /** - * @param DocumentInterface $document - * @param string $filename - * - * @return string - */ - protected function getDocumentMountPathForFilename(DocumentInterface $document, string $filename): string + private function getDocumentMountPathForFilename(DocumentInterface $document, string $filename): string { if ($document->isPrivate()) { - return 'private://' . $this->getDocumentRelativePathForFilename($document, $filename); + return 'private://'.$this->getDocumentRelativePathForFilename($document, $filename); } - return 'public://' . $this->getDocumentRelativePathForFilename($document, $filename); + + return 'public://'.$this->getDocumentRelativePathForFilename($document, $filename); } - /** - * @param DocumentInterface $document - * @return string - */ - protected function getDocumentPath(DocumentInterface $document): string + private function getDocumentPath(DocumentInterface $document): string { $this->validateDocument($document); if ($document->isPrivate()) { return $this->getDocumentPrivatePath($document); } + return $this->getDocumentPublicPath($document); } - /** - * @param DocumentInterface $document - * @return string - */ - protected function getDocumentPublicPath(DocumentInterface $document): string + private function getDocumentPublicPath(DocumentInterface $document): string { - return 'public://' . $document->getRelativePath(); + return 'public://'.$document->getRelativePath(); } - /** - * @param DocumentInterface $document - * @return string - */ - protected function getDocumentPrivatePath(DocumentInterface $document): string + private function getDocumentPrivatePath(DocumentInterface $document): string { - return 'private://' . $document->getRelativePath(); + return 'private://'.$document->getRelativePath(); } - /** - * @param DocumentInterface $document - * @return string - */ - protected function getDocumentFolderPath(DocumentInterface $document): string + private function getDocumentFolderPath(DocumentInterface $document): string { if ($document->isPrivate()) { return $this->getDocumentPrivateFolderPath($document); } + return $this->getDocumentPublicFolderPath($document); } - /** - * @param DocumentInterface $document - * @return string - */ - protected function getDocumentPublicFolderPath(DocumentInterface $document): string + private function getDocumentPublicFolderPath(DocumentInterface $document): string { - return 'public://' . $document->getFolder(); + return 'public://'.$document->getFolder(); } - /** - * @param DocumentInterface $document - * @return string - */ - protected function getDocumentPrivateFolderPath(DocumentInterface $document): string + private function getDocumentPrivateFolderPath(DocumentInterface $document): string { - return 'private://' . $document->getFolder(); + return 'private://'.$document->getFolder(); } /** - * @param DocumentInterface $document * @throws DocumentWithoutFileException */ - protected function validateDocument(DocumentInterface $document): void + private function validateDocument(DocumentInterface $document): void { if (!$document->isLocal()) { throw new DocumentWithoutFileException($document); diff --git a/src/Events/DocumentUpdatedEvent.php b/src/Events/DocumentUpdatedEvent.php index 8289f34..085512a 100644 --- a/src/Events/DocumentUpdatedEvent.php +++ b/src/Events/DocumentUpdatedEvent.php @@ -5,7 +5,7 @@ namespace RZ\Roadiz\Documents\Events; /** - * Event dispatched on document updated AFTER DB flushed + * Event dispatched on document updated AFTER DB flushed. */ final class DocumentUpdatedEvent extends FilterDocumentEvent { diff --git a/src/Exceptions/EmbedDocumentAlreadyExistsException.php b/src/Exceptions/EmbedDocumentAlreadyExistsException.php index fef5487..54c3a93 100644 --- a/src/Exceptions/EmbedDocumentAlreadyExistsException.php +++ b/src/Exceptions/EmbedDocumentAlreadyExistsException.php @@ -4,14 +4,12 @@ namespace RZ\Roadiz\Documents\Exceptions; -use Throwable; - class EmbedDocumentAlreadyExistsException extends \InvalidArgumentException { public function __construct( - string $message = "embed.document.already_exists", + string $message = 'embed.document.already_exists', int $code = 0, - ?Throwable $previous = null + ?\Throwable $previous = null, ) { parent::__construct($message, $code, $previous); } diff --git a/src/Exceptions/InvalidEmbedId.php b/src/Exceptions/InvalidEmbedId.php index 292aa2a..ff48dab 100644 --- a/src/Exceptions/InvalidEmbedId.php +++ b/src/Exceptions/InvalidEmbedId.php @@ -18,17 +18,11 @@ public function __construct(?string $embedId = null, ?string $platform = null) $this->platform = $platform; } - /** - * @return string|null - */ public function getEmbedId(): ?string { return $this->embedId; } - /** - * @return string|null - */ public function getPlatform(): ?string { return $this->platform; diff --git a/src/MediaFinders/AbstractDailymotionEmbedFinder.php b/src/MediaFinders/AbstractDailymotionEmbedFinder.php index 7eeb90c..73550f9 100644 --- a/src/MediaFinders/AbstractDailymotionEmbedFinder.php +++ b/src/MediaFinders/AbstractDailymotionEmbedFinder.php @@ -12,7 +12,6 @@ abstract class AbstractDailymotionEmbedFinder extends AbstractEmbedFinder { /** - * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'dailymotion'; @@ -22,8 +21,8 @@ abstract class AbstractDailymotionEmbedFinder extends AbstractEmbedFinder public static function supportEmbedUrl(string $embedUrl): bool { - return str_starts_with($embedUrl, 'https://dailymotion.com') || - str_starts_with($embedUrl, 'https://www.dailymotion.com'); + return str_starts_with($embedUrl, 'https://dailymotion.com') + || str_starts_with($embedUrl, 'https://www.dailymotion.com'); } public static function getPlatform(): string @@ -31,53 +30,38 @@ public static function getPlatform(): string return static::$platform; } - /** - * @inheritDoc - */ - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { - if (preg_match(static::$idPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$idPattern, $embedId, $matches)) { return $embedId; } - if (preg_match(static::$realIdPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$realIdPattern, $embedId, $matches)) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - /** - * {@inheritdoc} - */ public function getMediaTitle(): string { return $this->getFeed()['title'] ?? ''; } - /** - * {@inheritdoc} - */ + public function getMediaDescription(): string { return $this->getFeed()['description'] ?? ''; } - /** - * {@inheritdoc} - */ + public function getMediaCopyright(): string { return $this->getFeed()['author_name'] ?? ''; } - /** - * {@inheritdoc} - */ + public function getThumbnailURL(): string { return $this->getFeed()['thumbnail_url'] ?? ''; } - /** - * @inheritDoc - */ - public function getFeed() + public function getFeed(): array|\SimpleXMLElement|null { $oEmbedIframePattern = '#src\=\"https\:\/\/(?:www\.|geo\.)?dailymotion\.com\/(?:embed\/video\/|player\.html\?video\=)(?[a-zA-Z0-9\_\-]+)#'; $feed = parent::getFeed(); @@ -96,29 +80,23 @@ public function getFeed() return $feed; } - /** - * {@inheritdoc} - */ - public function getMediaFeed($search = null) + public function getMediaFeed(?string $search = null): string { if (preg_match(static::$realIdPattern, $this->embedId, $matches)) { - $url = 'https://www.dailymotion.com/video/' . $this->embedId; + $url = 'https://www.dailymotion.com/video/'.$this->embedId; } else { $url = $this->embedId; } - $endpoint = "https://www.dailymotion.com/services/oembed"; + $endpoint = 'https://www.dailymotion.com/services/oembed'; $query = [ 'url' => $url, 'format' => 'json', ]; - return $this->downloadFeedFromAPI($endpoint . '?' . http_build_query($query)); + return $this->downloadFeedFromAPI($endpoint.'?'.http_build_query($query)); } - /** - * @inheritDoc - */ public function getThumbnailName(string $pathinfo): string { if (null === $this->embedUrl) { @@ -126,16 +104,16 @@ public function getThumbnailName(string $pathinfo): string } else { $embed = $this->embedUrl; } - if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $matches) === 1) { - $pathinfo = '.' . $matches['extension']; + if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $matches)) { + $pathinfo = '.'.$matches['extension']; } else { $pathinfo = '.jpg'; } - if (preg_match(static::$realIdPattern, $embed, $matches) === 1) { - return 'dailymotion_' . $matches['id'] . $pathinfo; + if (1 === preg_match(static::$realIdPattern, $embed, $matches)) { + return 'dailymotion_'.$matches['id'].$pathinfo; } - if (preg_match(static::$idPattern, $embed, $matches) === 1) { - return 'dailymotion_' . $matches['id'] . $pathinfo; + if (1 === preg_match(static::$idPattern, $embed, $matches)) { + return 'dailymotion_'.$matches['id'].$pathinfo; } throw new InvalidEmbedId($embed, static::$platform); } @@ -148,10 +126,6 @@ public function getThumbnailName(string $pathinfo): string * * loop * * autoplay * * controls - * - * @param array $options - * - * @return string */ public function getSource(array &$options = []): string { @@ -165,6 +139,6 @@ public function getSource(array &$options = []): string $queryString['muted'] = (int) $options['muted']; $queryString['video'] = $this->embedId; - return 'https://geo.dailymotion.com/player.html?' . http_build_query($queryString); + return 'https://geo.dailymotion.com/player.html?'.http_build_query($queryString); } } diff --git a/src/MediaFinders/AbstractDeezerEmbedFinder.php b/src/MediaFinders/AbstractDeezerEmbedFinder.php index 27f23bf..dbcfa99 100644 --- a/src/MediaFinders/AbstractDeezerEmbedFinder.php +++ b/src/MediaFinders/AbstractDeezerEmbedFinder.php @@ -9,7 +9,6 @@ abstract class AbstractDeezerEmbedFinder extends AbstractEmbedFinder { /** - * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'deezer'; @@ -33,40 +32,34 @@ public function isEmptyThumbnailAllowed(): bool return true; } - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { - if (preg_match(static::$idPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$idPattern, $embedId, $matches)) { return $embedId; } - if (preg_match(static::$realIdPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$realIdPattern, $embedId, $matches)) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - /** - * @inheritDoc - */ - public function getMediaFeed($search = null) + public function getMediaFeed(?string $search = null): string { if (preg_match(static::$realIdPattern, $this->embedId)) { - $url = 'https://www.deezer.com/fr/' . $this->embedId; + $url = 'https://www.deezer.com/fr/'.$this->embedId; } else { $url = $this->embedId; } - $endpoint = "https://api.deezer.com/oembed"; + $endpoint = 'https://api.deezer.com/oembed'; $query = [ 'url' => $url, 'format' => 'json', ]; - return $this->downloadFeedFromAPI($endpoint . '?' . http_build_query($query)); + return $this->downloadFeedFromAPI($endpoint.'?'.http_build_query($query)); } - /** - * @inheritDoc - */ - public function getFeed() + public function getFeed(): array|\SimpleXMLElement|null { $feed = parent::getFeed(); /* @@ -74,7 +67,7 @@ public function getFeed() */ $this->embedUrl = $this->embedId; if (preg_match(static::$idPattern, $this->embedId, $matches)) { - $this->embedId = $matches['type'] . '/' . $matches['id']; + $this->embedId = $matches['type'].'/'.$matches['id']; } return $feed; @@ -92,7 +85,7 @@ public function getMediaDescription(): string public function getMediaCopyright(): string { - return ($this->getFeed()['provider_name'] ?? '') . ' (' . ($this->getFeed()['provider_url'] ?? '') . ')'; + return ($this->getFeed()['provider_name'] ?? '').' ('.($this->getFeed()['provider_url'] ?? '').')'; } public function getThumbnailURL(): string @@ -100,39 +93,32 @@ public function getThumbnailURL(): string return $this->getFeed()['thumbnail_url'] ?? ''; } - /** - * @inheritDoc - */ public function getThumbnailName(string $pathinfo): string { - if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { - $pathinfo = '.' . $ext['extension']; + if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext)) { + $pathinfo = '.'.$ext['extension']; } else { $pathinfo = '.jpg'; } - if (preg_match(static::$idPattern, $this->embedId, $matches) === 1) { - return $matches['type'] . '_' . $matches['id'] . $pathinfo; + if (1 === preg_match(static::$idPattern, $this->embedId, $matches)) { + return $matches['type'].'_'.$matches['id'].$pathinfo; } - if (preg_match(static::$realIdPattern, $this->embedId, $matches) === 1) { - return $matches['type'] . '_' . $matches['id'] . $pathinfo; + if (1 === preg_match(static::$realIdPattern, $this->embedId, $matches)) { + return $matches['type'].'_'.$matches['id'].$pathinfo; } throw new InvalidEmbedId($this->embedId, static::$platform); } /** * Get embed media source URL. - * - * @param array $options - * - * @return string */ public function getSource(array &$options = []): string { parent::getSource($options); $queryString = [ - 'id' => $this->embedId + 'id' => $this->embedId, ]; if (key_exists('autoplay', $options)) { @@ -155,15 +141,15 @@ public function getSource(array &$options = []): string $queryString['mute'] = (int) $options['muted']; if (preg_match(static::$realIdPattern, $this->embedId, $matches)) { - $baseUri = 'https://widget.deezer.com/widget/auto/' . $this->embedId; + $baseUri = 'https://widget.deezer.com/widget/auto/'.$this->embedId; } elseif (preg_match(static::$idPattern, $this->embedId, $matches)) { - $baseUri = 'https://widget.deezer.com/widget/auto/' . $matches['type'] . '/' . $matches['id']; + $baseUri = 'https://widget.deezer.com/widget/auto/'.$matches['type'].'/'.$matches['id']; } else { $baseUri = 'https://widget.deezer.com/widget/auto/'; } // https://widget.deezer.com/widget/dark/playlist/9313425622 - return $baseUri . '?' . http_build_query($queryString); + return $baseUri.'?'.http_build_query($queryString); } protected function areDuplicatesAllowed(): bool diff --git a/src/MediaFinders/AbstractEmbedFinder.php b/src/MediaFinders/AbstractEmbedFinder.php index 84ca230..abe9c01 100644 --- a/src/MediaFinders/AbstractEmbedFinder.php +++ b/src/MediaFinders/AbstractEmbedFinder.php @@ -5,10 +5,7 @@ namespace RZ\Roadiz\Documents\MediaFinders; use Doctrine\Persistence\ObjectManager; -use GuzzleHttp\Client; -use GuzzleHttp\Exception\RequestException; use League\Flysystem\FilesystemException; -use Psr\Http\Message\StreamInterface; use RZ\Roadiz\Documents\AbstractDocumentFactory; use RZ\Roadiz\Documents\DownloadedFile; use RZ\Roadiz\Documents\Exceptions\APINeedsAuthentificationException; @@ -18,28 +15,23 @@ use RZ\Roadiz\Documents\Models\SizeableInterface; use RZ\Roadiz\Documents\Models\TimeableInterface; use RZ\Roadiz\Documents\OptionsResolver\ViewOptionsResolver; -use SimpleXMLElement; use Symfony\Component\HttpFoundation\File\File; -use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; /** * Abstract class to handle external media via their Json API. */ abstract class AbstractEmbedFinder implements EmbedFinderInterface { - /** - * @var array|SimpleXMLElement|null - */ - protected $feed = null; + protected array|\SimpleXMLElement|null $feed = null; protected string $embedId; protected ?string $key = null; /** - * @param string $embedId - * @param bool $validate Validate the embed id passed at the constructor [default: true]. - * @throws InvalidEmbedId When embedId string is malformed + * @param bool $validate validate the embed id passed at the constructor [default: true] */ - public function __construct(string $embedId = '', bool $validate = true) + public function __construct(protected readonly HttpClientInterface $client, string $embedId = '', bool $validate = true) { if ($validate) { $this->embedId = $this->validateEmbedId($embedId); @@ -58,35 +50,26 @@ public function isEmptyThumbnailAllowed(): bool return false; } - /** - * @return string - */ public function getEmbedId(): string { return $this->embedId; } - /** - * @param string $embedId - * - * @return AbstractEmbedFinder - */ public function setEmbedId(string $embedId): AbstractEmbedFinder { $this->embedId = $this->validateEmbedId($embedId); + return $this; } /** - * Validate extern Id against platform naming policy. + * Validate extern ID against platform naming policy. * - * @param string $embedId - * @return string * @throws InvalidEmbedId When embedId string is malformed */ - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { - if (preg_match('#(?[^\/^=^?]+)$#', $embedId, $matches) === 1) { + if (1 === preg_match('#(?[^\/^=^?]+)$#', $embedId, $matches)) { return $matches['id']; } throw new InvalidEmbedId($embedId); @@ -94,8 +77,6 @@ protected function validateEmbedId(string $embedId = ""): string /** * Tell if embed media exists after its API feed. - * - * @return bool */ public function exists(): bool { @@ -104,17 +85,12 @@ public function exists(): bool /** * Crawl and parse an API json feed for current embedID. - * - * @return array|SimpleXMLElement|null */ - public function getFeed() + public function getFeed(): array|\SimpleXMLElement|null { if (null === $this->feed) { $rawFeed = $this->getMediaFeed(); - if ($rawFeed instanceof StreamInterface) { - $rawFeed = $rawFeed->getContents(); - } - if (null !== $rawFeed) { + if (\json_validate($rawFeed)) { $feed = json_decode($rawFeed, true); if (is_array($feed)) { $this->feed = $feed; @@ -123,43 +99,30 @@ public function getFeed() } } } + return $this->feed; } /** * Get embed media source URL. - * - * @param array $options - * - * @return string */ public function getSource(array &$options = []): string { $resolver = new ViewOptionsResolver(); $options = $resolver->resolve($options); - return ""; + return ''; } /** * Crawl an embed API to get a Json feed. - * - * @param string|bool|null $search - * - * @return string|StreamInterface */ - abstract public function getMediaFeed($search = null); + abstract public function getMediaFeed(?string $search = null): string; /** * Crawl an embed API to get a Json feed against a search query. - * - * @param string $searchTerm - * @param ?string $author - * @param int $maxResults - * - * @return string|StreamInterface|null */ - public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15) + public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15): ?string { return null; } @@ -173,9 +136,7 @@ public function getSearchFeed(string $searchTerm, ?string $author = null, int $m * * id * * class * - * @param array $options * @final - * @return string */ public function getIFrame(array &$options = []): string { @@ -188,7 +149,7 @@ public function getIFrame(array &$options = []): string 'accelerometer', 'encrypted-media', 'gyroscope', - 'picture-in-picture' + 'picture-in-picture', ]; if ($options['width'] > 0) { @@ -197,8 +158,8 @@ public function getIFrame(array &$options = []): string /* * Default height is defined to 16:10 */ - if ($options['height'] === 0) { - $attributes['height'] = (int)(($options['width'] * 10) / 16); + if (0 === $options['height']) { + $attributes['height'] = (int) (($options['width'] * 10) / 16); } } @@ -229,14 +190,14 @@ public function getIFrame(array &$options = []): string $htmlAttrs = []; foreach ($attributes as $key => $value) { - if ($value == '' || $value === true) { + if ('' == $value || true === $value) { $htmlAttrs[] = $key; } else { - $htmlAttrs[] = $key . '="' . addslashes((string) $value) . '"'; + $htmlAttrs[] = $key.'="'.addslashes((string) $value).'"'; } } - return ''; + return ''; } /** @@ -244,15 +205,14 @@ public function getIFrame(array &$options = []): string * * Be careful, this method does not flush. * - * @param ObjectManager $objectManager - * @param AbstractDocumentFactory $documentFactory * @return DocumentInterface|array + * * @throws FilesystemException */ public function createDocumentFromFeed( ObjectManager $objectManager, - AbstractDocumentFactory $documentFactory - ) { + AbstractDocumentFactory $documentFactory, + ): DocumentInterface|array { if ($this->documentExists($objectManager, $this->getEmbedId(), $this->getPlatform())) { throw new EmbedDocumentAlreadyExistsException(); } @@ -278,10 +238,10 @@ public function createDocumentFromFeed( } } catch (APINeedsAuthentificationException $exception) { $document = $documentFactory->getDocument(true, $this->areDuplicatesAllowed()); - $document?->setFilename($this->getPlatform() . '_' . $this->embedId . '.jpg'); - } catch (RequestException $exception) { + $document?->setFilename($this->getPlatform().'_'.$this->embedId.'.jpg'); + } catch (ClientExceptionInterface $exception) { $document = $documentFactory->getDocument(true, $this->areDuplicatesAllowed()); - $document?->setFilename($this->getPlatform() . '_' . $this->embedId . '.jpg'); + $document?->setFilename($this->getPlatform().'_'.$this->embedId.'.jpg'); } if (null === $document) { @@ -303,74 +263,47 @@ public function createDocumentFromFeed( return $document; } - /** - * @param ObjectManager $objectManager - * @param string $embedId - * @param string|null $embedPlatform - * @return bool - */ abstract protected function documentExists( ObjectManager $objectManager, string $embedId, - ?string $embedPlatform + ?string $embedPlatform, ): bool; /** * Store additional information into Document. - * - * @param ObjectManager $objectManager - * @param DocumentInterface $document - * @return DocumentInterface */ abstract protected function injectMetaInDocument(ObjectManager $objectManager, DocumentInterface $document): DocumentInterface; /** * Get media title from feed. - * - * @return string|null */ abstract public function getMediaTitle(): ?string; /** * Get media description from feed. - * - * @return string|null */ abstract public function getMediaDescription(): ?string; /** * Get media copyright from feed. - * - * @return string|null */ abstract public function getMediaCopyright(): ?string; /** * Get media thumbnail external URL from its feed. - * - * @return string|null */ abstract public function getThumbnailURL(): ?string; - /** - * @return int|null - */ public function getMediaWidth(): ?int { return null; } - /** - * @return int|null - */ public function getMediaHeight(): ?int { return null; } - /** - * @return int|null - */ public function getMediaDuration(): ?int { return null; @@ -378,39 +311,22 @@ public function getMediaDuration(): ?int /** * Send a CURL request and get its string output. - * - * @param string $url - * - * @return StreamInterface - * @throws \RuntimeException */ - public function downloadFeedFromAPI(string $url): StreamInterface + public function downloadFeedFromAPI(string $url): string { - $client = new Client(); - $response = $client->get($url); - - if (Response::HTTP_OK == $response->getStatusCode()) { - return $response->getBody(); - } + $response = $this->client->request('GET', $url); - throw new \RuntimeException($response->getReasonPhrase()); + return $response->getContent(); } - /** - * @param string $pathinfo - * - * @return string - */ public function getThumbnailName(string $pathinfo): string { - return $this->getEmbedId() . '_' . $pathinfo; + return $this->getEmbedId().'_'.$pathinfo; } /** * Download a picture from the embed media platform * to get a thumbnail. - * - * @return File|null */ public function downloadThumbnail(): ?File { @@ -418,6 +334,7 @@ public function downloadThumbnail(): ?File if (null !== $url && '' !== $url) { $thumbnailName = $this->getThumbnailName(basename($url)); + return DownloadedFile::fromUrl($url, $thumbnailName); } @@ -430,8 +347,6 @@ public function downloadThumbnail(): ?File * Key is the access_token which could be asked to consume an API. * For example, for Youtube it must be your API server key. For SoundCloud * it should be you app client Id. - * - * @return string|null */ public function getKey(): ?string { @@ -447,11 +362,12 @@ public function getKey(): ?string * * @param string|null $key the key * - * @return self + * @return $this */ - public function setKey(?string $key) + public function setKey(?string $key): self { $this->key = $key; + return $this; } diff --git a/src/MediaFinders/AbstractMixcloudEmbedFinder.php b/src/MediaFinders/AbstractMixcloudEmbedFinder.php index 3d3f1ca..34c81eb 100644 --- a/src/MediaFinders/AbstractMixcloudEmbedFinder.php +++ b/src/MediaFinders/AbstractMixcloudEmbedFinder.php @@ -9,7 +9,6 @@ abstract class AbstractMixcloudEmbedFinder extends AbstractEmbedFinder { /** - * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'mixcloud'; @@ -25,75 +24,54 @@ public static function getPlatform(): string return static::$platform; } - /** - * @inheritDoc - */ - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { - if (preg_match(static::$idPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$idPattern, $embedId, $matches)) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - /** - * @inheritDoc - */ - public function getMediaFeed($search = null) + public function getMediaFeed(?string $search = null): string { - $endpoint = "https://www.mixcloud.com/oembed/"; + $endpoint = 'https://www.mixcloud.com/oembed/'; $query = [ 'url' => $this->embedId, 'format' => 'json', ]; - return $this->downloadFeedFromAPI($endpoint . '?' . http_build_query($query)); + return $this->downloadFeedFromAPI($endpoint.'?'.http_build_query($query)); } - /** - * @inheritDoc - */ public function getMediaTitle(): string { return $this->getFeed()['title'] ?? ''; } - /** - * @inheritDoc - */ public function getMediaDescription(): string { return $this->getFeed()['description'] ?? ''; } - /** - * @inheritDoc - */ public function getMediaCopyright(): string { - return ($this->getFeed()['author_name'] ?? '') . ' (' . ($this->getFeed()['author_url'] ?? '') . ')'; + return ($this->getFeed()['author_name'] ?? '').' ('.($this->getFeed()['author_url'] ?? '').')'; } - /** - * @inheritDoc - */ public function getThumbnailURL(): string { return $this->getFeed()['image'] ?? ''; } - /** - * @inheritDoc - */ public function getThumbnailName(string $pathinfo): string { - if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { - $pathinfo = '.' . $ext['extension']; + if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext)) { + $pathinfo = '.'.$ext['extension']; } else { $pathinfo = '.jpg'; } - if (preg_match(static::$idPattern, $this->embedId, $matches) === 1) { - return $matches['author'] . '_' . $matches['id'] . $pathinfo; + if (1 === preg_match(static::$idPattern, $this->embedId, $matches)) { + return $matches['author'].'_'.$matches['id'].$pathinfo; } throw new InvalidEmbedId($this->embedId, static::$platform); } @@ -107,10 +85,6 @@ public function getThumbnailName(string $pathinfo): string * * end * * mini * * hide_cover - * - * @param array $options - * - * @return string */ public function getSource(array &$options = []): string { @@ -130,20 +104,20 @@ public function getSource(array &$options = []): string if ($options['end']) { $queryString['end'] = (int) $options['end']; } - if ($options['mini'] === true) { + if (true === $options['mini']) { $queryString['mini'] = 1; } - if ($options['hide_cover'] === true) { + if (true === $options['hide_cover']) { $queryString['hide_cover'] = 1; } - if ($options['hide_artwork'] === true) { + if (true === $options['hide_artwork']) { $queryString['hide_artwork'] = 1; } - if ($options['light'] === true) { + if (true === $options['light']) { $queryString['light'] = 1; } - return 'https://www.mixcloud.com/widget/iframe/?' . http_build_query($queryString); + return 'https://www.mixcloud.com/widget/iframe/?'.http_build_query($queryString); } protected function areDuplicatesAllowed(): bool diff --git a/src/MediaFinders/AbstractPodcastFinder.php b/src/MediaFinders/AbstractPodcastFinder.php index fa238d0..0789633 100644 --- a/src/MediaFinders/AbstractPodcastFinder.php +++ b/src/MediaFinders/AbstractPodcastFinder.php @@ -5,15 +5,11 @@ namespace RZ\Roadiz\Documents\MediaFinders; use Doctrine\Persistence\ObjectManager; -use GuzzleHttp\Client; use League\Flysystem\FilesystemException; -use Psr\Http\Message\StreamInterface; use RZ\Roadiz\Documents\AbstractDocumentFactory; use RZ\Roadiz\Documents\DownloadedFile; use RZ\Roadiz\Documents\Models\DocumentInterface; use RZ\Roadiz\Documents\Models\TimeableInterface; -use SimpleXMLElement; -use Symfony\Component\HttpFoundation\Response; abstract class AbstractPodcastFinder extends AbstractEmbedFinder { @@ -27,42 +23,28 @@ public static function getPlatform(): string return 'podcast'; } - /** - * @inheritDoc - */ - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { return $embedId; } - /** - * @return array|SimpleXMLElement|null - */ - public function getFeed() + public function getFeed(): array|\SimpleXMLElement|null { if (null === $this->feed) { $rawFeed = $this->getMediaFeed(); - if ($rawFeed instanceof StreamInterface) { - $rawFeed = $rawFeed->getContents(); - } - if (null !== $rawFeed) { - try { - $this->feed = new SimpleXMLElement($rawFeed); - return $this->feed; - } catch (\Exception $errorException) { - throw new \RuntimeException('Feed content is not a valid Podcast XML'); - } + try { + $this->feed = new \SimpleXMLElement($rawFeed); + + return $this->feed; + } catch (\Exception $errorException) { + throw new \RuntimeException('Feed content is not a valid Podcast XML'); } } + return $this->feed; } - /** - * @param SimpleXMLElement $item - * - * @return string - */ - protected function getAudioName(SimpleXMLElement $item): string + protected function getAudioName(\SimpleXMLElement $item): string { if (null !== $item->enclosure->attributes()) { $url = (string) $item->enclosure->attributes()->url; @@ -72,8 +54,10 @@ protected function getAudioName(SimpleXMLElement $item): string if (!empty((string) $item->title)) { $extension = pathinfo($url, PATHINFO_EXTENSION); - return ((string) $item->title) . '.' . $extension; + + return ((string) $item->title).'.'.$extension; } + return pathinfo($url, PATHINFO_BASENAME); } @@ -82,18 +66,17 @@ protected function getAudioName(SimpleXMLElement $item): string * * Be careful, this method does not flush. * - * @param ObjectManager $objectManager - * @param AbstractDocumentFactory $documentFactory * @return array + * * @throws FilesystemException */ public function createDocumentFromFeed( ObjectManager $objectManager, - AbstractDocumentFactory $documentFactory - ) { + AbstractDocumentFactory $documentFactory, + ): array { $documents = []; $feed = $this->getFeed(); - if ($feed instanceof SimpleXMLElement) { + if ($feed instanceof \SimpleXMLElement) { foreach ($feed->channel->item as $item) { if ( !empty($item->enclosure->attributes()->url) @@ -145,12 +128,12 @@ public function createDocumentFromFeed( abstract protected function injectMetaFromPodcastItem( ObjectManager $objectManager, DocumentInterface $document, - \SimpleXMLElement $item + \SimpleXMLElement $item, ): void; protected function getPodcastItemTitle(\SimpleXMLElement $item): ?string { - return (string) $item->title . ' – ' . $this->getMediaTitle(); + return (string) $item->title.' – '.$this->getMediaTitle(); } protected function getPodcastItemDescription(\SimpleXMLElement $item): ?string @@ -169,74 +152,59 @@ protected function getPodcastItemCopyright(\SimpleXMLElement $item): ?string if (empty($copyright)) { return $this->getMediaCopyright(); } - return $copyright . ' – ' . $this->getMediaCopyright(); + + return $copyright.' – '.$this->getMediaCopyright(); } - /** - * @inheritDoc - */ - public function getMediaFeed($search = null) + public function getMediaFeed(?string $search = null): string { $url = $this->embedId; - $client = new Client(); - $response = $client->get($url); - - if (Response::HTTP_OK == $response->getStatusCode()) { - return $response->getBody(); - } + $response = $this->client->request('GET', $url); - throw new \RuntimeException($response->getReasonPhrase()); + return $response->getContent(); } - /** - * @inheritDoc - */ public function getMediaTitle(): ?string { $feed = $this->getFeed(); - if ($feed instanceof SimpleXMLElement && $feed->channel instanceof SimpleXMLElement) { + if ($feed instanceof \SimpleXMLElement && $feed->channel instanceof \SimpleXMLElement) { return (string) ($feed->channel->title ?? null); } + return null; } - /** - * @inheritDoc - */ public function getMediaDescription(): ?string { $feed = $this->getFeed(); - if ($feed instanceof SimpleXMLElement && $feed->channel instanceof SimpleXMLElement) { + if ($feed instanceof \SimpleXMLElement && $feed->channel instanceof \SimpleXMLElement) { return (string) ($feed->channel->description ?? null); } + return null; } - /** - * @inheritDoc - */ public function getMediaCopyright(): ?string { $feed = $this->getFeed(); - if ($feed instanceof SimpleXMLElement && $feed->channel instanceof SimpleXMLElement) { + if ($feed instanceof \SimpleXMLElement && $feed->channel instanceof \SimpleXMLElement) { return (string) ($feed->channel->copyright ?? null); } + return null; } - /** - * @inheritDoc - */ public function getThumbnailURL(): ?string { $feed = $this->getFeed(); if ( - $feed instanceof SimpleXMLElement - && $feed->channel instanceof SimpleXMLElement - && $feed->channel->image instanceof SimpleXMLElement + $feed instanceof \SimpleXMLElement + && $feed->channel instanceof \SimpleXMLElement + && $feed->channel->image instanceof \SimpleXMLElement ) { return (string) ($feed->channel->image->url ?? null); } + return null; } diff --git a/src/MediaFinders/AbstractSoundcloudEmbedFinder.php b/src/MediaFinders/AbstractSoundcloudEmbedFinder.php index 3d15b3d..4383550 100644 --- a/src/MediaFinders/AbstractSoundcloudEmbedFinder.php +++ b/src/MediaFinders/AbstractSoundcloudEmbedFinder.php @@ -12,7 +12,6 @@ abstract class AbstractSoundcloudEmbedFinder extends AbstractEmbedFinder { /** - * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'soundcloud'; @@ -22,9 +21,9 @@ abstract class AbstractSoundcloudEmbedFinder extends AbstractEmbedFinder public static function supportEmbedUrl(string $embedUrl): bool { - return str_starts_with($embedUrl, 'https://api.soundcloud.com') || - str_starts_with($embedUrl, 'https://www.soundcloud.com') || - str_starts_with($embedUrl, 'https://soundcloud.com'); + return str_starts_with($embedUrl, 'https://api.soundcloud.com') + || str_starts_with($embedUrl, 'https://www.soundcloud.com') + || str_starts_with($embedUrl, 'https://soundcloud.com'); } public static function getPlatform(): string @@ -32,38 +31,29 @@ public static function getPlatform(): string return static::$platform; } - /** - * @inheritDoc - */ - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { - if (preg_match(static::$idPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$idPattern, $embedId, $matches)) { return $embedId; } - if (preg_match(static::$realIdPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$realIdPattern, $embedId, $matches)) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - /** - * @inheritDoc - */ - public function getMediaFeed($search = null) + public function getMediaFeed(?string $search = null): string { - $endpoint = "https://soundcloud.com/oembed"; + $endpoint = 'https://soundcloud.com/oembed'; $query = [ 'url' => $this->embedId, 'format' => 'json', ]; - return $this->downloadFeedFromAPI($endpoint . '?' . http_build_query($query)); + return $this->downloadFeedFromAPI($endpoint.'?'.http_build_query($query)); } - /** - * @inheritDoc - */ - public function getFeed() + public function getFeed(): array|\SimpleXMLElement|null { $feed = parent::getFeed(); /* @@ -89,7 +79,7 @@ public function getMediaDescription(): string public function getMediaCopyright(): string { - return ($this->getFeed()['author_name'] ?? '') . ' (' . ($this->getFeed()['author_url'] ?? '') . ')'; + return ($this->getFeed()['author_name'] ?? '').' ('.($this->getFeed()['author_url'] ?? '').')'; } public function getThumbnailURL(): string @@ -97,9 +87,6 @@ public function getThumbnailURL(): string return $this->getFeed()['thumbnail_url'] ?? ''; } - /** - * @inheritDoc - */ public function getThumbnailName(string $pathinfo): string { if (null === $this->embedUrl) { @@ -107,13 +94,13 @@ public function getThumbnailName(string $pathinfo): string } else { $embed = $this->embedUrl; } - if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { - $pathinfo = '.' . $ext['extension']; + if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext)) { + $pathinfo = '.'.$ext['extension']; } else { $pathinfo = '.jpg'; } - if (preg_match(static::$idPattern, $embed, $matches) === 1) { - return 'soundcloud_' . $matches['user'] . $pathinfo; + if (1 === preg_match(static::$idPattern, $embed, $matches)) { + return 'soundcloud_'.$matches['user'].$pathinfo; } throw new InvalidEmbedId($embed, static::$platform); } @@ -128,10 +115,6 @@ public function getThumbnailName(string $pathinfo): string * * show_user * * show_reposts * * visual - * - * @param array $options - * - * @return string */ public function getSource(array &$options = []): string { @@ -152,7 +135,7 @@ public function getSource(array &$options = []): string } $queryString['controls'] = (int) $options['controls']; - return 'https://w.soundcloud.com/player/?' . http_build_query($queryString); + return 'https://w.soundcloud.com/player/?'.http_build_query($queryString); } protected function areDuplicatesAllowed(): bool diff --git a/src/MediaFinders/AbstractSpotifyEmbedFinder.php b/src/MediaFinders/AbstractSpotifyEmbedFinder.php index 1fed0b8..6620a31 100644 --- a/src/MediaFinders/AbstractSpotifyEmbedFinder.php +++ b/src/MediaFinders/AbstractSpotifyEmbedFinder.php @@ -9,7 +9,6 @@ abstract class AbstractSpotifyEmbedFinder extends AbstractEmbedFinder { /** - * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'spotify'; @@ -29,43 +28,34 @@ public static function getPlatform(): string return static::$platform; } - /** - * @inheritDoc - */ - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { - if (preg_match(static::$idPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$idPattern, $embedId, $matches)) { return $embedId; } - if (preg_match(static::$realIdPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$realIdPattern, $embedId, $matches)) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - /** - * @inheritDoc - */ - public function getMediaFeed($search = null) + public function getMediaFeed(?string $search = null): string { if (preg_match(static::$realIdPattern, $this->embedId, $matches)) { - $url = 'https://open.spotify.com/' . $this->embedId; + $url = 'https://open.spotify.com/'.$this->embedId; } else { $url = $this->embedId; } - $endpoint = "https://embed.spotify.com/oembed"; + $endpoint = 'https://embed.spotify.com/oembed'; $query = [ 'url' => $url, 'format' => 'json', ]; - return $this->downloadFeedFromAPI($endpoint . '?' . http_build_query($query)); + return $this->downloadFeedFromAPI($endpoint.'?'.http_build_query($query)); } - /** - * @inheritDoc - */ - public function getFeed() + public function getFeed(): array|\SimpleXMLElement|null { $feed = parent::getFeed(); /* @@ -73,82 +63,66 @@ public function getFeed() */ $this->embedUrl = $this->embedId; if (preg_match(static::$idPattern, $this->embedId, $matches)) { - $this->embedId = $matches['type'] . '/' . $matches['id']; + $this->embedId = $matches['type'].'/'.$matches['id']; } return $feed; } - /** - * @inheritDoc - */ public function getMediaTitle(): string { $feed = $this->getFeed(); + return is_array($feed) && isset($feed['title']) ? $feed['title'] : ''; } - /** - * @inheritDoc - */ public function getMediaDescription(): string { $feed = $this->getFeed(); + return is_array($feed) && isset($feed['description']) ? $feed['description'] : ''; } - /** - * @inheritDoc - */ public function getMediaCopyright(): string { $feed = $this->getFeed(); - return is_array($feed) ? $feed['provider_name'] . ' (' . $feed['provider_url'] . ')' : ''; + + return is_array($feed) ? $feed['provider_name'].' ('.$feed['provider_url'].')' : ''; } - /** - * @inheritDoc - */ public function getThumbnailURL(): string { return $this->getFeed()['thumbnail_url'] ?? ''; } - /** - * @inheritDoc - */ public function getThumbnailName(string $pathinfo): string { - if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { - $pathinfo = '.' . $ext['extension']; + if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext)) { + $pathinfo = '.'.$ext['extension']; } else { $pathinfo = '.jpg'; } - if (preg_match(static::$idPattern, $this->embedId, $matches) === 1) { - return $matches['type'] . '_' . $matches['id'] . $pathinfo; + if (1 === preg_match(static::$idPattern, $this->embedId, $matches)) { + return $matches['type'].'_'.$matches['id'].$pathinfo; } - if (preg_match(static::$realIdPattern, $this->embedId, $matches) === 1) { - return $matches['type'] . '_' . $matches['id'] . $pathinfo; + if (1 === preg_match(static::$realIdPattern, $this->embedId, $matches)) { + return $matches['type'].'_'.$matches['id'].$pathinfo; } throw new InvalidEmbedId($this->embedId, static::$platform); } /** * Get embed media source URL. - * - * @param array $options - * - * @return string */ public function getSource(array &$options = []): string { parent::getSource($options); if (preg_match(static::$realIdPattern, $this->embedId, $matches)) { - return 'https://open.spotify.com/embed/' . $this->embedId; + return 'https://open.spotify.com/embed/'.$this->embedId; } if (preg_match(static::$idPattern, $this->embedId, $matches)) { - return 'https://open.spotify.com/embed/' . $matches['type'] . '/' . $matches['id']; + return 'https://open.spotify.com/embed/'.$matches['type'].'/'.$matches['id']; } return $this->embedId; diff --git a/src/MediaFinders/AbstractTedEmbedFinder.php b/src/MediaFinders/AbstractTedEmbedFinder.php index 1c71110..f4077de 100644 --- a/src/MediaFinders/AbstractTedEmbedFinder.php +++ b/src/MediaFinders/AbstractTedEmbedFinder.php @@ -9,7 +9,6 @@ abstract class AbstractTedEmbedFinder extends AbstractEmbedFinder { /** - * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'ted'; @@ -25,7 +24,7 @@ public static function getPlatform(): string return static::$platform; } - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { if (preg_match(static::$idPattern, $embedId, $matches)) { return $embedId; @@ -33,14 +32,14 @@ protected function validateEmbedId(string $embedId = ""): string throw new InvalidEmbedId($embedId, static::$platform); } - public function getMediaFeed($search = null) + public function getMediaFeed(?string $search = null): string { - $endpoint = "https://www.ted.com/services/v1/oembed.json"; + $endpoint = 'https://www.ted.com/services/v1/oembed.json'; $query = [ 'url' => $this->embedId, ]; - return $this->downloadFeedFromAPI($endpoint . '?' . http_build_query($query)); + return $this->downloadFeedFromAPI($endpoint.'?'.http_build_query($query)); } public function getMediaTitle(): string @@ -55,7 +54,7 @@ public function getMediaDescription(): string public function getMediaCopyright(): string { - return ($this->getFeed()['author_name'] ?? '') . ' - ' . ($this->getFeed()['provider_name'] ?? '') . ' (' . ($this->getFeed()['author_url'] ?? '') . ')'; + return ($this->getFeed()['author_name'] ?? '').' - '.($this->getFeed()['provider_name'] ?? '').' ('.($this->getFeed()['author_url'] ?? '').')'; } public function getThumbnailURL(): string @@ -65,30 +64,26 @@ public function getThumbnailURL(): string public function getThumbnailName(string $pathinfo): string { - if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { - $pathinfo = '.' . $ext['extension']; + if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext)) { + $pathinfo = '.'.$ext['extension']; } else { $pathinfo = '.jpg'; } - if (preg_match(static::$idPattern, $this->embedId, $matches) === 1) { - return 'ted_talk_' . $matches['id'] . $pathinfo; + if (1 === preg_match(static::$idPattern, $this->embedId, $matches)) { + return 'ted_talk_'.$matches['id'].$pathinfo; } throw new InvalidEmbedId($this->embedId, static::$platform); } /** * Get embed media source URL. - * - * @param array $options - * - * @return string */ public function getSource(array &$options = []): string { parent::getSource($options); if (preg_match(static::$idPattern, $this->embedId, $matches)) { - return 'https://embed.ted.com/talks/' . $matches['id']; + return 'https://embed.ted.com/talks/'.$matches['id']; } return $this->embedId; diff --git a/src/MediaFinders/AbstractTwitchEmbedFinder.php b/src/MediaFinders/AbstractTwitchEmbedFinder.php deleted file mode 100644 index 2088076..0000000 --- a/src/MediaFinders/AbstractTwitchEmbedFinder.php +++ /dev/null @@ -1,113 +0,0 @@ -[0-9]+)#'; - - public static function supportEmbedUrl(string $embedUrl): bool - { - return str_starts_with($embedUrl, 'https://twitch.tv') || - str_starts_with($embedUrl, 'https://www.twitch.tv'); - } - - public static function getPlatform(): string - { - return static::$platform; - } - - /** - * @inheritDoc - */ - protected function validateEmbedId(string $embedId = ""): string - { - if (preg_match(static::$idPattern, $embedId, $matches) === 1) { - return $embedId; - } - throw new InvalidEmbedId($embedId, static::$platform); - } - - /** - * @inheritDoc - */ - public function getMediaFeed($search = null) - { - $endpoint = "https://api.twitch.tv/v4/oembed"; - $query = [ - 'url' => $this->embedId, - ]; - - return $this->downloadFeedFromAPI($endpoint . '?' . http_build_query($query)); - } - - public function getMediaTitle(): string - { - return $this->getFeed()['title'] ?? ''; - } - - public function getMediaDescription(): string - { - return $this->getFeed()['description'] ?? ''; - } - - public function getMediaCopyright(): string - { - return ($this->getFeed()['author_name'] ?? '') . ' - ' . ($this->getFeed()['provider_name'] ?? '') . ' (' . ($this->getFeed()['author_url'] ?? '') . ')'; - } - - public function getThumbnailURL(): string - { - return $this->getFeed()['thumbnail_url'] ?? ''; - } - - public function getThumbnailName(string $pathinfo): string - { - if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { - $pathinfo = '.' . $ext['extension']; - } else { - $pathinfo = '.jpg'; - } - if (preg_match(static::$idPattern, $this->embedId, $matches) === 1) { - return 'twitch_' . $matches['id'] . $pathinfo; - } - throw new InvalidEmbedId($this->embedId, static::$platform); - } - - /** - * Get embed media source URL. - * - * @param array $options - * - * @return string - */ - public function getSource(array &$options = []): string - { - parent::getSource($options); - - if (preg_match(static::$idPattern, $this->embedId, $matches)) { - $queryString = [ - 'video' => $matches['id'], - 'branding' => 0, - ]; - - if ($options['autoplay']) { - $queryString['autoplay'] = (int) $options['autoplay']; - $queryString['playsinline'] = (int) $options['autoplay']; - } - - return 'https://player.twitch.tv/?' . http_build_query($queryString); - } - - return $this->embedId; - } -} diff --git a/src/MediaFinders/AbstractUnsplashPictureFinder.php b/src/MediaFinders/AbstractUnsplashPictureFinder.php index 80d2774..794791d 100644 --- a/src/MediaFinders/AbstractUnsplashPictureFinder.php +++ b/src/MediaFinders/AbstractUnsplashPictureFinder.php @@ -4,15 +4,12 @@ namespace RZ\Roadiz\Documents\MediaFinders; -use GuzzleHttp\Client; -use GuzzleHttp\Exception\ClientException; -use GuzzleHttp\Exception\GuzzleException; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; abstract class AbstractUnsplashPictureFinder extends AbstractEmbedFinder implements RandomImageFinder { - protected Client $client; /** - * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'unsplash'; @@ -32,54 +29,44 @@ public static function getPlatform(): string return static::$platform; } - /** - * @param string $clientId - * @param string $embedId - */ - public function __construct(string $clientId, string $embedId = '') + public function __construct(HttpClientInterface $client, string $clientId, string $embedId = '') { - parent::__construct($embedId); - - $this->client = new Client( - [ + parent::__construct($client->withOptions([ // Base URI is used with relative requests 'base_uri' => 'https://api.unsplash.com', // You can set any number of default request options. 'timeout' => 3.0, 'headers' => [ - 'Authorization' => 'Client-ID ' . $clientId - ] - ] - ); + 'Authorization' => 'Client-ID '.$clientId, + ], + ]), $embedId); } - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { return $embedId; } /** - * @see https://unsplash.com/documentation#get-a-random-photo - * @param array $options - * @return array|null - * @throws GuzzleException + * @see https://unsplash.com/documentation#get-a-random-photo */ public function getRandom(array $options = []): ?array { try { - $response = $this->client->get( + $response = $this->client->request( + 'GET', '/photos/random', [ - 'query' => array_merge( - [ - 'content_filter' => 'high', - 'orientation' => 'landscape' - ], - $options - ) + 'query' => array_merge( + [ + 'content_filter' => 'high', + 'orientation' => 'landscape', + ], + $options + ), ] ); - $feed = json_decode($response->getBody()->getContents(), true) ?? null; + $feed = json_decode($response->getContent(), true) ?? null; if (!is_array($feed)) { return null; } @@ -89,85 +76,59 @@ public function getRandom(array $options = []): ?array if (null !== $url) { $this->embedId = (string) $feed['id']; $this->feed = $feed; + return $this->feed; } + return null; - } catch (ClientException $e) { + } catch (ClientExceptionInterface) { return null; } } - /** - * @param string $keyword - * @param array $options - * @return array|null - * @throws GuzzleException - */ - public function getRandomBySearch(string $keyword, array $options = []) + public function getRandomBySearch(string $keyword, array $options = []): ?array { return $this->getRandom( [ - 'query' => $keyword + 'query' => $keyword, ] ); } - - /** - * {@inheritdoc} - */ - public function getMediaFeed($search = null) + public function getMediaFeed(?string $search = null): string { - return ''; + throw new \LogicException('Unsplash API does not provide a feed.'); } - /** - * {@inheritdoc} - */ public function getMediaTitle(): string { return $this->feed['description'] ?? ''; } - /** - * @return int|null - */ public function getMediaWidth(): ?int { return $this->feed['width'] ?? null; } - /** - * @return int|null - */ public function getMediaHeight(): ?int { return $this->feed['height'] ?? null; } - /** - * {@inheritdoc} - */ public function getMediaDescription(): string { return $this->feed['alt_description'] ?? ''; } - /** - * {@inheritdoc} - */ public function getMediaCopyright(): string { if (isset($this->feed['user'])) { - return trim(($this->feed['user']['name'] ?? '') . ', Unsplash', " \t\n\r\0\x0B-"); + return trim(($this->feed['user']['name'] ?? '').', Unsplash', " \t\n\r\0\x0B-"); } + return 'Unsplash'; } - /** - * @inheritdoc - * @throws GuzzleException - */ public function getThumbnailURL(): ?string { if (null === $this->feed) { @@ -180,19 +141,16 @@ public function getThumbnailURL(): ?string if (is_array($this->feed)) { return $this->getBestUrl($this->feed); } + return null; } - /** - * @param array|null $feed - * - * @return string|null - */ protected function getBestUrl(?array $feed): ?string { if (null === $feed) { return null; } + return $feed['urls']['full'] ?? $feed['urls']['raw'] ?? null; } } diff --git a/src/MediaFinders/AbstractVimeoEmbedFinder.php b/src/MediaFinders/AbstractVimeoEmbedFinder.php index 5d0db9f..effafc9 100644 --- a/src/MediaFinders/AbstractVimeoEmbedFinder.php +++ b/src/MediaFinders/AbstractVimeoEmbedFinder.php @@ -13,7 +13,6 @@ abstract class AbstractVimeoEmbedFinder extends AbstractEmbedFinder { protected static string $realIdPattern = '#(?[0-9]+)$#'; /** - * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'vimeo'; @@ -25,13 +24,13 @@ public static function getPlatform(): string public static function supportEmbedUrl(string $embedUrl): bool { - return str_starts_with($embedUrl, 'https://vimeo.com/') || - str_starts_with($embedUrl, 'https://www.vimeo.com/'); + return str_starts_with($embedUrl, 'https://vimeo.com/') + || str_starts_with($embedUrl, 'https://www.vimeo.com/'); } - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { - if (preg_match('#(?[0-9]+)$#', $embedId, $matches) === 1) { + if (1 === preg_match('#(?[0-9]+)$#', $embedId, $matches)) { return $matches['id']; } throw new InvalidEmbedId($embedId, static::$platform); @@ -47,12 +46,11 @@ public function isEmptyThumbnailAllowed(): bool /** * Tell if embed media exists after its API feed. - * - * @return bool */ public function exists(): bool { $feed = $this->getFeed(); + return is_array($feed) && isset($feed['video_id']); } @@ -91,18 +89,15 @@ public function getMediaDuration(): ?int return $this->getFeed()['duration'] ?? null; } - public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15) + public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15): ?string { return null; } - /** - * {@inheritdoc} - */ - public function getMediaFeed($search = null) + public function getMediaFeed(?string $search = null): string { if (preg_match(static::$realIdPattern, $this->embedId, $matches)) { - $url = 'https://vimeo.com/video/' . $this->embedId; + $url = 'https://vimeo.com/video/'.$this->embedId; } else { $url = $this->embedId; } @@ -112,7 +107,7 @@ public function getMediaFeed($search = null) 'format' => 'json', ]; - return $this->downloadFeedFromAPI($endpoint . '?' . http_build_query($query)); + return $this->downloadFeedFromAPI($endpoint.'?'.http_build_query($query)); } /** @@ -128,10 +123,6 @@ public function getMediaFeed($search = null) * * muted * * autopause * * automute - * - * @param array $options - * - * @return string */ public function getSource(array &$options = []): string { @@ -169,6 +160,6 @@ public function getSource(array &$options = []): string $queryString['muted'] = (int) $options['muted']; } - return 'https://player.vimeo.com/video/' . $this->embedId . '?' . http_build_query($queryString); + return 'https://player.vimeo.com/video/'.$this->embedId.'?'.http_build_query($queryString); } } diff --git a/src/MediaFinders/AbstractYoutubeEmbedFinder.php b/src/MediaFinders/AbstractYoutubeEmbedFinder.php index e67f5ba..df108d9 100644 --- a/src/MediaFinders/AbstractYoutubeEmbedFinder.php +++ b/src/MediaFinders/AbstractYoutubeEmbedFinder.php @@ -14,7 +14,6 @@ abstract class AbstractYoutubeEmbedFinder extends AbstractEmbedFinder { protected const YOUTUBE_EMBED_DOMAIN = 'https://www.youtube-nocookie.com'; /** - * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'youtube'; @@ -29,48 +28,39 @@ public static function getPlatform(): string public static function supportEmbedUrl(string $embedUrl): bool { - return str_starts_with($embedUrl, 'https://www.youtube.com/') || - str_starts_with($embedUrl, 'https://youtube.com/') || - str_starts_with($embedUrl, 'https://youtu.be/'); + return str_starts_with($embedUrl, 'https://www.youtube.com/') + || str_starts_with($embedUrl, 'https://youtube.com/') + || str_starts_with($embedUrl, 'https://youtu.be/'); } - /** - * @inheritDoc - */ - protected function validateEmbedId(string $embedId = ""): string + protected function validateEmbedId(string $embedId = ''): string { - if (preg_match(static::$idPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$idPattern, $embedId, $matches)) { return $embedId; } - if (preg_match(static::$realIdPattern, $embedId, $matches) === 1) { + if (1 === preg_match(static::$realIdPattern, $embedId, $matches)) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - /** - * @inheritDoc - */ - public function getMediaFeed($search = null) + public function getMediaFeed(?string $search = null): string { if (preg_match(static::$realIdPattern, $this->embedId, $matches)) { - $url = 'https://www.youtube.com/watch?v=' . $this->embedId; + $url = 'https://www.youtube.com/watch?v='.$this->embedId; } else { $url = $this->embedId; } - $endpoint = "https://www.youtube.com/oembed"; + $endpoint = 'https://www.youtube.com/oembed'; $query = [ 'url' => $url, 'format' => 'json', ]; - return $this->downloadFeedFromAPI($endpoint . '?' . http_build_query($query)); + return $this->downloadFeedFromAPI($endpoint.'?'.http_build_query($query)); } - /** - * @inheritDoc - */ - public function getFeed() + public function getFeed(): array|\SimpleXMLElement|null { $feed = parent::getFeed(); /* @@ -96,12 +86,13 @@ public function getMediaTitle(): string public function getMediaDescription(): string { $feed = $this->getFeed(); + return (is_array($feed) && isset($feed['description'])) ? ($feed['description']) : (''); } public function getMediaCopyright(): string { - return ($this->getFeed()['author_name'] ?? '') . ' (' . ($this->getFeed()['author_url'] ?? '') . ')'; + return ($this->getFeed()['author_name'] ?? '').' ('.($this->getFeed()['author_url'] ?? '').')'; } public function getThumbnailURL(): string @@ -109,17 +100,11 @@ public function getThumbnailURL(): string return $this->getFeed()['thumbnail_url'] ?? ''; } - /** - * @inheritDoc - */ public function getMediaWidth(): ?int { return $this->getFeed()['width'] ?? null; } - /** - * @inheritDoc - */ public function getMediaHeight(): ?int { return $this->getFeed()['height'] ?? null; @@ -132,34 +117,34 @@ public function getThumbnailName(string $pathinfo): string } else { $embed = $this->embedUrl; } - if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $matches) === 1) { - $pathinfo = '.' . $matches['extension']; + if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $matches)) { + $pathinfo = '.'.$matches['extension']; } else { $pathinfo = '.jpg'; } - if (preg_match(static::$realIdPattern, $embed, $matches) === 1) { - return 'youtube_' . $matches['id'] . $pathinfo; + if (1 === preg_match(static::$realIdPattern, $embed, $matches)) { + return 'youtube_'.$matches['id'].$pathinfo; } - if (preg_match(static::$idPattern, $embed, $matches) === 1) { - return 'youtube_' . $matches['id'] . $pathinfo; + if (1 === preg_match(static::$idPattern, $embed, $matches)) { + return 'youtube_'.$matches['id'].$pathinfo; } throw new InvalidEmbedId($embed, static::$platform); } /** - * @inheritdoc - * @throws APINeedsAuthentificationException + * @throws APINeedsAuthentificationException */ - public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15) + public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15): ?string { - if (null !== $this->getKey() && $this->getKey() != "") { - $url = "https://www.googleapis.com/youtube/v3/search?q=" . $searchTerm . "&part=snippet&key=" . $this->getKey() . "&maxResults=" . $maxResults; - if (null !== $author && !empty($author)) { - $url .= '&author=' . $author; + if (null !== $this->getKey() && '' != $this->getKey()) { + $url = 'https://www.googleapis.com/youtube/v3/search?q='.$searchTerm.'&part=snippet&key='.$this->getKey().'&maxResults='.$maxResults; + if (!empty($author)) { + $url .= '&author='.$author; } + return $this->downloadFeedFromAPI($url); } else { - throw new APINeedsAuthentificationException("YoutubeEmbedFinder needs a Google server key, create a “google_server_id” setting.", 1); + throw new APINeedsAuthentificationException('YoutubeEmbedFinder needs a Google server key, create a “google_server_id” setting.', 1); } } @@ -174,10 +159,6 @@ public function getSearchFeed(string $searchTerm, ?string $author = null, int $m * * start * * enablejsapi * * muted - * - * @param array $options - * - * @return string */ public function getSource(array &$options = []): string { @@ -218,6 +199,6 @@ public function getSource(array &$options = []): string $queryString['enablejsapi'] = (int) $options['enablejsapi']; $queryString['mute'] = (int) $options['muted']; - return static::YOUTUBE_EMBED_DOMAIN . '/embed/' . $this->embedId . '?' . http_build_query($queryString); + return static::YOUTUBE_EMBED_DOMAIN.'/embed/'.$this->embedId.'?'.http_build_query($queryString); } } diff --git a/src/MediaFinders/EmbedFinderFactory.php b/src/MediaFinders/EmbedFinderFactory.php index 3694d18..5d3f8a5 100644 --- a/src/MediaFinders/EmbedFinderFactory.php +++ b/src/MediaFinders/EmbedFinderFactory.php @@ -4,6 +4,8 @@ namespace RZ\Roadiz\Documents\MediaFinders; +use Symfony\Contracts\HttpClient\HttpClientInterface; + class EmbedFinderFactory { /** @@ -21,23 +23,22 @@ class EmbedFinderFactory /** * @param array> $embedPlatforms */ - public function __construct(array $embedPlatforms = []) + public function __construct(protected readonly HttpClientInterface $client, array $embedPlatforms = []) { $this->embedPlatforms = $embedPlatforms; } - /** - * @param string|null $mediaPlatform - * @param string|null $embedId - * - * @return EmbedFinderInterface|null - */ public function createForPlatform(?string $mediaPlatform, ?string $embedId): ?EmbedFinderInterface { if (null !== $embedId && $this->supports($mediaPlatform)) { + /** + * @var class-string $class + */ $class = $this->embedPlatforms[$mediaPlatform]; - return new $class($embedId); + + return new $class($this->client, $embedId); } + return null; } @@ -56,14 +57,14 @@ public function createForUrl(?string $embedUrl): ?EmbedFinderInterface } /** - * @var string $platform + * @var string $platform * @var class-string $class */ foreach ($this->embedPlatforms as $platform => $class) { $callback = [$class, 'supportEmbedUrl']; if ( - is_callable($callback) && - call_user_func($callback, $embedUrl) + is_callable($callback) + && call_user_func($callback, $embedUrl) ) { return $this->createForPlatform($platform, $embedUrl); } @@ -72,16 +73,11 @@ public function createForUrl(?string $embedUrl): ?EmbedFinderInterface return null; } - /** - * @param string|null $mediaPlatform - * - * @return bool - */ public function supports(?string $mediaPlatform): bool { return - null !== $mediaPlatform && - in_array( + null !== $mediaPlatform + && in_array( $mediaPlatform, array_keys($this->embedPlatforms) ); diff --git a/src/MediaFinders/EmbedFinderInterface.php b/src/MediaFinders/EmbedFinderInterface.php index c0388f4..3139cd7 100644 --- a/src/MediaFinders/EmbedFinderInterface.php +++ b/src/MediaFinders/EmbedFinderInterface.php @@ -4,20 +4,14 @@ namespace RZ\Roadiz\Documents\MediaFinders; +use Doctrine\Persistence\ObjectManager; +use RZ\Roadiz\Documents\AbstractDocumentFactory; +use RZ\Roadiz\Documents\Models\DocumentInterface; + interface EmbedFinderInterface { - /** - * @param array $options - * - * @return string - */ public function getIFrame(array &$options = []): string; - /** - * @param array $options - * - * @return string - */ public function getSource(array &$options = []): string; public static function supportEmbedUrl(string $embedUrl): bool; @@ -28,4 +22,9 @@ public static function getPlatform(): string; * @return string Embed short type for displaying icons */ public function getShortType(): string; + + public function createDocumentFromFeed( + ObjectManager $objectManager, + AbstractDocumentFactory $documentFactory, + ): DocumentInterface|array; } diff --git a/src/MediaFinders/FacebookPictureFinder.php b/src/MediaFinders/FacebookPictureFinder.php index 3a76670..c263cb9 100644 --- a/src/MediaFinders/FacebookPictureFinder.php +++ b/src/MediaFinders/FacebookPictureFinder.php @@ -4,39 +4,38 @@ namespace RZ\Roadiz\Documents\MediaFinders; -use GuzzleHttp\Client; -use GuzzleHttp\Exception\GuzzleException; -use Psr\Http\Message\ResponseInterface; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; /** * Util to grab a facebook profile picture from userAlias. */ -class FacebookPictureFinder +final readonly class FacebookPictureFinder { - protected string $facebookUserAlias; - protected ResponseInterface $response; - - /** - * @param string $facebookUserAlias - */ - public function __construct(string $facebookUserAlias) + public function __construct(private HttpClientInterface $client) { - $this->facebookUserAlias = $facebookUserAlias; } /** * @return string|null Facebook profile image URL - * @throws GuzzleException + * + * @throws TransportExceptionInterface + * @throws ClientExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ServerExceptionInterface */ - public function getPictureUrl(): ?string + public function getPictureUrl(string $facebookUserAlias): ?string { - $client = new Client(); - $this->response = $client->get('https://graph.facebook.com/' . $this->facebookUserAlias . '/picture?redirect=false&width=200&height=200'); - $json = json_decode($this->response->getBody()->getContents(), true); + $response = $this->client->request('GET', 'https://graph.facebook.com/'.$facebookUserAlias.'/picture?redirect=false&width=200&height=200'); + $json = json_decode($response->getContent(), true); if (is_array($json) && isset($json['data']) && !empty($json['data']['url'])) { return $json['data']['url']; } + return null; } } diff --git a/src/MediaFinders/RandomImageFinder.php b/src/MediaFinders/RandomImageFinder.php index d510b44..6c52246 100644 --- a/src/MediaFinders/RandomImageFinder.php +++ b/src/MediaFinders/RandomImageFinder.php @@ -7,15 +7,12 @@ interface RandomImageFinder { /** - * @param array $options - * @return array|null A data feed for a random image. + * @return array|null a data feed for a random image */ public function getRandom(array $options = []): ?array; /** - * @param string $keyword - * @param array $options * @return array|bool|mixed A data feed for a random image by keyword */ - public function getRandomBySearch(string $keyword, array $options = []); + public function getRandomBySearch(string $keyword, array $options = []): mixed; } diff --git a/src/Models/AdvancedDocumentInterface.php b/src/Models/AdvancedDocumentInterface.php index f86ca2b..1e3bf32 100644 --- a/src/Models/AdvancedDocumentInterface.php +++ b/src/Models/AdvancedDocumentInterface.php @@ -6,13 +6,9 @@ interface AdvancedDocumentInterface extends DocumentInterface, SizeableInterface, DisplayableInterface { - /** - * @return int|null - */ public function getFilesize(): ?int; /** - * @param int|null $filesize * @return $this */ public function setFilesize(?int $filesize): static; diff --git a/src/Models/DisplayableInterface.php b/src/Models/DisplayableInterface.php index aeb4a9e..8b7f99a 100644 --- a/src/Models/DisplayableInterface.php +++ b/src/Models/DisplayableInterface.php @@ -6,13 +6,9 @@ interface DisplayableInterface { - /** - * @return string|null - */ public function getImageAverageColor(): ?string; /** - * @param string|null $imageAverageColor * @return $this */ public function setImageAverageColor(?string $imageAverageColor): static; diff --git a/src/Models/DocumentInterface.php b/src/Models/DocumentInterface.php index 1f990bf..4ab2789 100644 --- a/src/Models/DocumentInterface.php +++ b/src/Models/DocumentInterface.php @@ -11,82 +11,60 @@ interface DocumentInterface public function getFilename(): string; /** - * @param string $filename * @return $this */ public function setFilename(string $filename): static; - /** - * @return string|null - */ public function getMimeType(): ?string; /** - * @param string|null $mimeType * @return $this */ public function setMimeType(?string $mimeType): static; /** * Get short type name for current document Mime type. - * - * @return string */ public function getShortType(): string; /** * Get short Mime type. - * - * @return string */ public function getShortMimeType(): string; /** * Is current document an image. - * - * @return bool */ public function isImage(): bool; /** * Is current document a vector SVG file. - * - * @return bool */ public function isSvg(): bool; /** * Is current document a Webp image. - * - * @return bool */ public function isWebp(): bool; /** * Is current document a video. - * - * @return bool */ public function isVideo(): bool; /** * Is current document an audio file. - * - * @return bool */ public function isAudio(): bool; /** * Is current document a PDF file. - * - * @return bool */ public function isPdf(): bool; public function getFolder(): string; /** - * @param string $folder * @return $this */ public function setFolder(string $folder): static; @@ -109,7 +87,6 @@ public function getMountFolderPath(): ?string; public function getEmbedId(): ?string; /** - * @param string|null $embedId * @return $this */ public function setEmbedId(?string $embedId): static; @@ -117,25 +94,18 @@ public function setEmbedId(?string $embedId): static; public function getEmbedPlatform(): ?string; /** - * @param string|null $embedPlatform * @return $this */ public function setEmbedPlatform(?string $embedPlatform): static; /** * Tells if current document has embed media information. - * - * @return bool */ public function isEmbed(): bool; - /** - * @return bool - */ public function isPrivate(): bool; /** - * @param bool $private * @return $this */ public function setPrivate(bool $private): static; @@ -144,27 +114,25 @@ public function getRawDocument(): ?DocumentInterface; /** * @param DocumentInterface|null $rawDocument the raw document + * * @return $this */ public function setRawDocument(?DocumentInterface $rawDocument = null): static; /** * Is document a raw one. - * - * @return bool */ public function isRaw(): bool; /** * @param bool $raw the raw + * * @return $this */ public function setRaw(bool $raw): static; /** * Gets the downscaledDocument. - * - * @return DocumentInterface|null */ public function getDownscaledDocument(): ?DocumentInterface; @@ -174,27 +142,22 @@ public function getDownscaledDocument(): ?DocumentInterface; public function getFolders(): Collection; /** - * @param FolderInterface $folder * @return $this */ public function addFolder(FolderInterface $folder): static; /** - * @param FolderInterface $folder * @return $this */ public function removeFolder(FolderInterface $folder): static; /** - * Return false if no local file is linked to document. i.e no filename, no folder - * - * @return bool + * Return false if no local file is linked to document. i.e no filename, no folder. */ public function isLocal(): bool; + /** * Return true if current document can be processed by intervention-image (GD, Imagick…). - * - * @return bool */ public function isProcessable(): bool; diff --git a/src/Models/DocumentTrait.php b/src/Models/DocumentTrait.php index 9ed46e3..4677043 100644 --- a/src/Models/DocumentTrait.php +++ b/src/Models/DocumentTrait.php @@ -26,6 +26,7 @@ trait DocumentTrait * - 3d * * @var array + * * @internal */ #[SymfonySerializer\Ignore()] @@ -136,7 +137,8 @@ trait DocumentTrait ]; /** - * @var string[] Processable file mime type by GD or Imagick. + * @var string[] processable file mime type by GD or Imagick + * * @internal */ #[SymfonySerializer\Ignore()] @@ -153,8 +155,6 @@ trait DocumentTrait /** * Get short type name for current document Mime type. - * - * @return string */ #[SymfonySerializer\Ignore()] public function getShortType(): string @@ -168,99 +168,86 @@ public function getShortType(): string /** * Get short Mime type. - * - * @return string */ #[SymfonySerializer\Ignore()] public function getShortMimeType(): string { if (!empty($this->getMimeType())) { $mime = explode('/', $this->getMimeType()); + return $mime[\count($mime) - 1]; } + return 'unknown'; } /** * Is current document an image. - * - * @return bool */ #[SymfonySerializer\Ignore()] public function isImage(): bool { - return static::getShortType() === 'image'; + return 'image' === static::getShortType(); } /** * Is current document a vector SVG file. - * - * @return bool */ #[SymfonySerializer\Ignore()] public function isSvg(): bool { - return $this->getMimeType() === 'image/svg+xml' || $this->getMimeType() === 'image/svg'; + return 'image/svg+xml' === $this->getMimeType() || 'image/svg' === $this->getMimeType(); } /** * Is current document a video. - * - * @return bool */ #[SymfonySerializer\Ignore()] public function isVideo(): bool { - return static::getShortType() === 'video'; + return 'video' === static::getShortType(); } /** * Is current document an audio file. - * - * @return bool */ #[SymfonySerializer\Ignore()] public function isAudio(): bool { - return static::getShortType() === 'audio'; + return 'audio' === static::getShortType(); } /** * Is current document a PDF file. - * - * @return bool */ #[SymfonySerializer\Ignore()] public function isPdf(): bool { - return static::getShortType() === 'pdf'; + return 'pdf' === static::getShortType(); } - /** - * @return bool - */ #[SymfonySerializer\Ignore()] public function isWebp(): bool { - return $this->getMimeType() === 'image/webp'; + return 'image/webp' === $this->getMimeType(); } #[ - SymfonySerializer\Groups(["document", "document_display", "nodes_sources", "tag", "attribute"]), - SymfonySerializer\SerializedName("relativePath"), + SymfonySerializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute']), + SymfonySerializer\SerializedName('relativePath'), ] public function getRelativePath(): ?string { if ($this->isLocal()) { - return $this->getFolder() . '/' . $this->getFilename(); + return $this->getFolder().'/'.$this->getFilename(); } else { return null; } } #[ - SymfonySerializer\Groups(["document_mount"]), - SymfonySerializer\SerializedName("mountPath"), + SymfonySerializer\Groups(['document_mount']), + SymfonySerializer\SerializedName('mountPath'), ] public function getMountPath(): ?string { @@ -268,9 +255,9 @@ public function getMountPath(): ?string return null; } if ($this->isPrivate()) { - return 'private://' . $relativePath; + return 'private://'.$relativePath; } else { - return 'public://' . $relativePath; + return 'public://'.$relativePath; } } @@ -284,31 +271,29 @@ public function getMountFolderPath(): ?string return null; } if ($this->isPrivate()) { - return 'private://' . $folder; + return 'private://'.$folder; } else { - return 'public://' . $folder; + return 'public://'.$folder; } } /** * Tells if current document has embed media information. - * - * @return bool */ #[SymfonySerializer\Ignore()] public function isEmbed(): bool { - return (!empty($this->getEmbedId()) && !empty($this->getEmbedPlatform())); + return !empty($this->getEmbedId()) && !empty($this->getEmbedPlatform()); } protected function initDocumentTrait(): void { - $this->setFolder(\mb_substr(hash("crc32b", date('YmdHi')), 0, 12)); + $this->setFolder(\mb_substr(hash('crc32b', date('YmdHi')), 0, 12)); } #[ - SymfonySerializer\Groups(["document", "document_display", "nodes_sources", "tag", "attribute"]), - SymfonySerializer\SerializedName("processable"), + SymfonySerializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute']), + SymfonySerializer\SerializedName('processable'), ApiProperty( description: 'Document can be processed as an image for resampling and other image operations.', writable: false, @@ -320,8 +305,8 @@ public function isProcessable(): bool } #[ - SymfonySerializer\Groups(["document", "document_display", "nodes_sources", "tag", "attribute"]), - SymfonySerializer\SerializedName("alt"), + SymfonySerializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute']), + SymfonySerializer\SerializedName('alt'), ] public function getAlternativeText(): string { @@ -329,13 +314,11 @@ public function getAlternativeText(): string } /** - * Return false if no local file is linked to document. i.e no filename, no folder - * - * @return bool + * Return false if no local file is linked to document. i.e no filename, no folder. */ #[SymfonySerializer\Ignore()] public function isLocal(): bool { - return $this->getFilename() !== '' && $this->getFolder() !== ''; + return '' !== $this->getFilename() && '' !== $this->getFolder(); } } diff --git a/src/Models/FileAwareInterface.php b/src/Models/FileAwareInterface.php index d63bb07..0570588 100644 --- a/src/Models/FileAwareInterface.php +++ b/src/Models/FileAwareInterface.php @@ -10,12 +10,12 @@ interface FileAwareInterface { /** - * @return string Return absolute path to public files folder. + * @return string return absolute path to public files folder */ public function getPublicFilesPath(): string; /** - * @return string Return relative path to public files folder. + * @return string return relative path to public files folder */ public function getPublicFilesBasePath(): string; @@ -25,7 +25,7 @@ public function getPublicFilesBasePath(): string; public function getPrivateFilesPath(): string; /** - * @return string Return relative path to private files folder. + * @return string return relative path to private files folder */ public function getPrivateFilesBasePath(): string; @@ -35,17 +35,17 @@ public function getPrivateFilesBasePath(): string; public function getFontsFilesPath(): string; /** - * @return string Return relative path to private font files folder. + * @return string return relative path to private font files folder */ public function getFontsFilesBasePath(): string; /** - * @return string Return absolute path to public images cache. + * @return string return absolute path to public images cache */ public function getPublicCachePath(): string; /** - * @return string Return relative path to public images cache. + * @return string return relative path to public images cache */ public function getPublicCacheBasePath(): string; } diff --git a/src/Models/FileHashInterface.php b/src/Models/FileHashInterface.php index 49e76e3..8b1d8ed 100644 --- a/src/Models/FileHashInterface.php +++ b/src/Models/FileHashInterface.php @@ -7,7 +7,10 @@ interface FileHashInterface { public function setFileHash(?string $hash): static; + public function getFileHash(): ?string; + public function setFileHashAlgorithm(?string $algorithm): static; + public function getFileHashAlgorithm(): ?string; } diff --git a/src/Models/FolderInterface.php b/src/Models/FolderInterface.php index f5555fd..f1e483e 100644 --- a/src/Models/FolderInterface.php +++ b/src/Models/FolderInterface.php @@ -14,49 +14,36 @@ interface FolderInterface public function getDocuments(): Collection; /** - * @param DocumentInterface $document * @return $this */ public function addDocument(DocumentInterface $document): static; /** - * @param DocumentInterface $document * @return $this */ public function removeDocument(DocumentInterface $document): static; public function getVisible(): bool; + public function isVisible(): bool; /** - * @param bool $visible * @return $this */ public function setVisible(bool $visible): static; - /** - * @return string - */ public function getFolderName(): string; - /** - * @return string|null - */ public function getName(): ?string; /** - * @param string $folderName * @return $this */ public function setFolderName(string $folderName): static; - /** - * @return string - */ public function getDirtyFolderName(): string; /** - * @param string $dirtyFolderName * @return $this */ public function setDirtyFolderName(string $dirtyFolderName): static; diff --git a/src/Models/HasThumbnailInterface.php b/src/Models/HasThumbnailInterface.php index 6684430..a4f6b82 100644 --- a/src/Models/HasThumbnailInterface.php +++ b/src/Models/HasThumbnailInterface.php @@ -8,13 +8,9 @@ interface HasThumbnailInterface { - /** - * @return HasThumbnailInterface|null - */ public function getOriginal(): ?HasThumbnailInterface; /** - * @param HasThumbnailInterface|null $original * @return $this */ public function setOriginal(?HasThumbnailInterface $original): static; @@ -26,22 +22,14 @@ public function getThumbnails(): Collection; /** * @param Collection $thumbnails + * * @return $this */ public function setThumbnails(Collection $thumbnails): static; - /** - * @return bool - */ public function isThumbnail(): bool; - /** - * @return bool - */ public function hasThumbnails(): bool; - /** - * @return bool - */ public function needsThumbnail(): bool; } diff --git a/src/Models/SimpleDocument.php b/src/Models/SimpleDocument.php index 58315a9..142c5c1 100644 --- a/src/Models/SimpleDocument.php +++ b/src/Models/SimpleDocument.php @@ -31,9 +31,6 @@ public function __construct() $this->folders = new ArrayCollection(); } - /** - * @return string - */ public function getFilename(): string { return $this->filename; @@ -42,6 +39,7 @@ public function getFilename(): string public function setFilename(string $filename): static { $this->filename = $filename; + return $this; } @@ -53,12 +51,10 @@ public function getMimeType(): ?string public function setMimeType(?string $mimeType): static { $this->mimeType = $mimeType; + return $this; } - /** - * @return string - */ public function getFolder(): string { return $this->folder; @@ -71,9 +67,6 @@ public function setFolder(string $folder): static return $this; } - /** - * @return string|null - */ public function getEmbedId(): ?string { return $this->embedId; @@ -86,9 +79,6 @@ public function setEmbedId(?string $embedId): static return $this; } - /** - * @return string|null - */ public function getEmbedPlatform(): ?string { return $this->embedPlatform; @@ -101,9 +91,6 @@ public function setEmbedPlatform(?string $embedPlatform): static return $this; } - /** - * @return bool - */ public function isPrivate(): bool { return $this->private; @@ -116,9 +103,6 @@ public function setPrivate(bool $private): static return $this; } - /** - * @return DocumentInterface|null - */ public function getRawDocument(): ?DocumentInterface { return $this->rawDocument; @@ -131,9 +115,6 @@ public function setRawDocument(?DocumentInterface $rawDocument = null): static return $this; } - /** - * @return bool - */ public function isRaw(): bool { return $this->raw; @@ -158,9 +139,6 @@ public function setDownscaledDocument(?DocumentInterface $downscaledDocument): s return $this; } - /** - * @return Collection - */ public function getFolders(): Collection { return $this->folders; @@ -169,18 +147,21 @@ public function getFolders(): Collection public function setFolders(Collection $folders): static { $this->folders = $folders; + return $this; } public function addFolder(FolderInterface $folder): static { $this->folders->add($folder); + return $this; } public function removeFolder(FolderInterface $folder): static { $this->folders->removeElement($folder); + return $this; } diff --git a/src/Models/SimpleFileAware.php b/src/Models/SimpleFileAware.php index 0f6f0ce..bbff652 100644 --- a/src/Models/SimpleFileAware.php +++ b/src/Models/SimpleFileAware.php @@ -11,9 +11,6 @@ class SimpleFileAware implements FileAwareInterface { private string $basePath; - /** - * @param string $basePath - */ public function __construct(string $basePath) { $this->basePath = $basePath; @@ -21,7 +18,7 @@ public function __construct(string $basePath) public function getPublicFilesPath(): string { - return $this->basePath . $this->getPublicFilesBasePath(); + return $this->basePath.$this->getPublicFilesBasePath(); } public function getPublicFilesBasePath(): string @@ -31,7 +28,7 @@ public function getPublicFilesBasePath(): string public function getPrivateFilesPath(): string { - return $this->basePath . $this->getPrivateFilesBasePath(); + return $this->basePath.$this->getPrivateFilesBasePath(); } public function getPrivateFilesBasePath(): string @@ -41,7 +38,7 @@ public function getPrivateFilesBasePath(): string public function getFontsFilesPath(): string { - return $this->basePath . $this->getPrivateFilesBasePath(); + return $this->basePath.$this->getPrivateFilesBasePath(); } public function getFontsFilesBasePath(): string diff --git a/src/Models/SizeableInterface.php b/src/Models/SizeableInterface.php index 5bf55a7..bdbcca9 100644 --- a/src/Models/SizeableInterface.php +++ b/src/Models/SizeableInterface.php @@ -6,30 +6,19 @@ interface SizeableInterface { - /** - * @return int - */ public function getImageWidth(): int; /** - * @param int $imageWidth * @return $this */ public function setImageWidth(int $imageWidth): static; - /** - * @return int - */ public function getImageHeight(): int; /** - * @param int $imageHeight * @return $this */ public function setImageHeight(int $imageHeight): static; - /** - * @return float|null - */ public function getImageRatio(): ?float; } diff --git a/src/Models/TimeableInterface.php b/src/Models/TimeableInterface.php index 25fc81f..4951c77 100644 --- a/src/Models/TimeableInterface.php +++ b/src/Models/TimeableInterface.php @@ -6,13 +6,9 @@ interface TimeableInterface { - /** - * @return int - */ public function getMediaDuration(): int; /** - * @param int $duration * @return $this */ public function setMediaDuration(int $duration): static; diff --git a/src/OptionsResolver/UrlOptionsResolver.php b/src/OptionsResolver/UrlOptionsResolver.php index ac34da7..834428a 100644 --- a/src/OptionsResolver/UrlOptionsResolver.php +++ b/src/OptionsResolver/UrlOptionsResolver.php @@ -41,16 +41,16 @@ public function __construct() $this->setAllowedValues( 'align', [ - null, - 'top-left', - 'top', - 'top-right', - 'left', - 'center', - 'right', - 'bottom-left', - 'bottom', - 'bottom-right', + null, + 'top-left', + 'top', + 'top-right', + 'left', + 'center', + 'right', + 'bottom-left', + 'bottom', + 'bottom-right', ] ); $this->setAllowedTypes('background', ['null', 'string']); @@ -73,6 +73,7 @@ function (Options $options) { if (1 === preg_match('#(?[0-9]+)[x:\.](?[0-9]+)#', $compositing, $matches)) { return ((float) $matches['width']) / ((float) $matches['height']); } + return null; } ); @@ -86,9 +87,10 @@ function (Options $options) { $compositing = $options['fit'] ?? ''; if (1 === preg_match('#(?[0-9]+)[x:\.](?[0-9]+)#', $compositing, $matches)) { return (int) $matches['width']; - } elseif (null !== $options['ratio'] && $options['height'] !== 0 && $options['ratio'] !== 0) { + } elseif (null !== $options['ratio'] && 0 !== $options['height'] && 0 !== $options['ratio']) { return (int) (intval($options['height']) * floatval($options['ratio'])); } + return 0; } ); @@ -99,9 +101,10 @@ function (Options $options) { $compositing = $options['fit'] ?? ''; if (1 === preg_match('#(?[0-9]+)[x:\.](?[0-9]+)#', $compositing, $matches)) { return (int) $matches['height']; - } elseif (null !== $options['ratio'] && $options['width'] !== 0 && $options['ratio'] !== 0) { + } elseif (null !== $options['ratio'] && 0 !== $options['width'] && 0 !== $options['ratio']) { return (int) (intval($options['width']) / floatval($options['ratio'])); } + return 0; } ); diff --git a/src/Renderer/AbstractImageRenderer.php b/src/Renderer/AbstractImageRenderer.php index 4c7c60c..2bd767d 100644 --- a/src/Renderer/AbstractImageRenderer.php +++ b/src/Renderer/AbstractImageRenderer.php @@ -20,7 +20,7 @@ public function __construct( EmbedFinderFactory $embedFinderFactory, Environment $templating, DocumentUrlGeneratorInterface $documentUrlGenerator, - string $templateBasePath = 'documents' + string $templateBasePath = 'documents', ) { parent::__construct($documentsStorage, $templating, $documentUrlGenerator, $templateBasePath); $this->embedFinderFactory = $embedFinderFactory; @@ -28,23 +28,19 @@ public function __construct( public function supports(DocumentInterface $document, array $options): bool { - return $document->isImage() && - !empty($document->getRelativePath()) && - !$this->isEmbeddable($document, $options); + return $document->isImage() + && !empty($document->getRelativePath()) + && !$this->isEmbeddable($document, $options); } public function isEmbeddable(DocumentInterface $document, array $options): bool { - return isset($options['embed']) && - $options['embed'] === true && - null !== $document->getEmbedPlatform() && - $this->embedFinderFactory->supports($document->getEmbedPlatform()); + return isset($options['embed']) + && true === $options['embed'] + && null !== $document->getEmbedPlatform() + && $this->embedFinderFactory->supports($document->getEmbedPlatform()); } - /** - * @param array $options - * @return string|null - */ protected function parseSizes(array $options = []): ?string { if (count($options['sizes']) > 0) { @@ -56,52 +52,38 @@ protected function parseSizes(array $options = []): ?string protected function willResample(array &$assignation): bool { - return !empty($assignation['fit']) || - !empty($assignation['crop']) || - !empty($assignation['rotate']) || - !empty($assignation['width']) || - !empty($assignation['height']); + return !empty($assignation['fit']) + || !empty($assignation['crop']) + || !empty($assignation['rotate']) + || !empty($assignation['width']) + || !empty($assignation['height']); } - /** - * @param DocumentInterface $document - * @param array $options - * @param bool $convertToWebP - * - * @return string|null - */ protected function parseSrcSet( DocumentInterface $document, array $options = [], - bool $convertToWebP = false + bool $convertToWebP = false, ): ?string { if (count($options['srcset']) > 0) { return $this->parseSrcSetInner($document, $options['srcset'], $convertToWebP, $options['absolute']); } + return null; } - /** - * @param DocumentInterface $document - * @param array $srcSetArray - * @param bool $convertToWebP - * @param bool $absolute - * - * @return string - */ protected function parseSrcSetInner( DocumentInterface $document, array $srcSetArray = [], bool $convertToWebP = false, - bool $absolute = false + bool $absolute = false, ): string { $output = []; foreach ($srcSetArray as $set) { if ( - isset($set['format']) && - isset($set['rule']) && - !$document->isPrivate() && - !empty($document->getRelativePath()) + isset($set['format']) + && isset($set['rule']) + && !$document->isPrivate() + && !empty($document->getRelativePath()) ) { $this->documentUrlGenerator->setOptions($this->urlOptionsResolver->resolve($set['format'])); $this->documentUrlGenerator->setDocument($document); @@ -109,65 +91,55 @@ protected function parseSrcSetInner( if ($convertToWebP) { $path .= '.webp'; } - $output[] = $path . ' ' . $set['rule']; + $output[] = $path.' '.$set['rule']; } } + return implode(', ', $output); } - /** - * @param string $hexColor - * @param int $width - * @param int $height - * @return string - */ protected function createTransparentDataURI(string $hexColor, int $width = 1, int $height = 1): string { - $hexColorArray = \sscanf($hexColor, "#%02x%02x%02x"); + $hexColorArray = \sscanf($hexColor, '#%02x%02x%02x'); if (null === $hexColorArray) { throw new \RuntimeException('Color is not a valid hexadecimal RGB format'); } [$r, $g, $b] = $hexColorArray; - $im = \imageCreateTrueColor($width, $height); + $im = \imagecreatetruecolor($width, $height); if ($im) { - \imageFill( + \imagefill( $im, 0, 0, - \imageColorAllocate($im, $r ?? 0, $g ?? 0, $b ?? 0) ?: 0 + \imagecolorallocate($im, $r ?? 0, $g ?? 0, $b ?? 0) ?: 0 ); \ob_start(); \imagejpeg($im, null, 30); $img = \ob_get_contents(); \ob_end_clean(); if ($img) { - return 'data:image/jpeg;base64,' . \base64_encode($img); + return 'data:image/jpeg;base64,'.\base64_encode($img); } } throw new \RuntimeException('Cannot generate imageCreateTrueColor'); } - /** - * @param DocumentInterface $document - * @param array $options - * @param array $assignation - */ protected function additionalAssignation(DocumentInterface $document, array $options, array &$assignation): void { if ($document instanceof AdvancedDocumentInterface) { - if (null !== $options['ratio'] && $options['ratio'] !== 0) { + if (null !== $options['ratio'] && 0 !== $options['ratio']) { $assignation['ratio'] = $options['ratio']; } elseif (null !== $document->getImageRatio()) { $assignation['ratio'] = $document->getImageRatio(); } if ( null !== $document->getImageAverageColor() - && $document->getImageAverageColor() !== '#ffffff' - && $document->getImageAverageColor() !== '#000000' + && '#ffffff' !== $document->getImageAverageColor() + && '#000000' !== $document->getImageAverageColor() ) { $assignation['averageColor'] = $document->getImageAverageColor(); } - if ($options['blurredFallback'] === true) { + if (true === $options['blurredFallback']) { if (!empty($options['fit'])) { // Both Fit and Width cannot be explicitly set // need to revert on Crop @@ -183,7 +155,7 @@ protected function additionalAssignation(DocumentInterface $document, array $opt $options, [ 'quality' => 10, - 'width' => 60 + 'width' => 60, ] ) ); diff --git a/src/Renderer/AbstractRenderer.php b/src/Renderer/AbstractRenderer.php index 71abbce..dabd17a 100644 --- a/src/Renderer/AbstractRenderer.php +++ b/src/Renderer/AbstractRenderer.php @@ -25,7 +25,7 @@ public function __construct( FilesystemOperator $documentsStorage, Environment $templating, DocumentUrlGeneratorInterface $documentUrlGenerator, - string $templateBasePath = 'documents' + string $templateBasePath = 'documents', ) { $this->documentsStorage = $documentsStorage; $this->templating = $templating; @@ -35,12 +35,6 @@ public function __construct( $this->viewOptionsResolver = new ViewOptionsResolver(); } - /** - * @param DocumentInterface $document - * @param array $options - * - * @return string - */ protected function getSource(DocumentInterface $document, array $options): string { if (empty($document->getRelativePath())) { @@ -48,27 +42,22 @@ protected function getSource(DocumentInterface $document, array $options): strin } $this->documentUrlGenerator->setOptions($options); $this->documentUrlGenerator->setDocument($document); + return $this->documentUrlGenerator->getUrl($options['absolute']); } /** - * @param string $template - * @param array $assignation - * - * @return string * @throws \Twig\Error\LoaderError * @throws \Twig\Error\RuntimeError * @throws \Twig\Error\SyntaxError */ protected function renderHtmlElement(string $template, array $assignation): string { - return $this->templating->render($this->templateBasePath . '/' . $template, $assignation); + return $this->templating->render($this->templateBasePath.'/'.$template, $assignation); } /** - * @param DocumentInterface $document * @param iterable $sourcesDocs - * @return array */ protected function getSourcesFilesArray(DocumentInterface $document, iterable $sourcesDocs): array { @@ -88,7 +77,7 @@ protected function getSourcesFilesArray(DocumentInterface $document, iterable $s } krsort($sources); - if (count($sources) === 0) { + if (0 === count($sources)) { // If exotic extension, fallbacks using original file $documentMountPath = $document->getMountPath(); if (null !== $documentMountPath) { diff --git a/src/Renderer/AudioRenderer.php b/src/Renderer/AudioRenderer.php index f63dc0e..694f512 100644 --- a/src/Renderer/AudioRenderer.php +++ b/src/Renderer/AudioRenderer.php @@ -19,7 +19,7 @@ public function __construct( DocumentFinderInterface $documentFinder, Environment $templating, DocumentUrlGeneratorInterface $documentUrlGenerator, - string $templateBasePath = 'documents' + string $templateBasePath = 'documents', ) { parent::__construct($documentsStorage, $templating, $documentUrlGenerator, $templateBasePath); $this->documentFinder = $documentFinder; @@ -31,10 +31,6 @@ public function supports(DocumentInterface $document, array $options): bool } /** - * @param DocumentInterface $document - * @param array $options - * - * @return string * @throws \Twig\Error\LoaderError * @throws \Twig\Error\RuntimeError * @throws \Twig\Error\SyntaxError @@ -53,10 +49,6 @@ public function render(DocumentInterface $document, array $options): string * * This method will search for document which filename is the same * except the extension. If you choose an MP4 file, it will look for a OGV and WEBM file. - * - * @param DocumentInterface $document - * - * @return array */ protected function getSourcesFiles(DocumentInterface $document): array { diff --git a/src/Renderer/ChainRenderer.php b/src/Renderer/ChainRenderer.php index 50dac81..0de8b7a 100644 --- a/src/Renderer/ChainRenderer.php +++ b/src/Renderer/ChainRenderer.php @@ -13,9 +13,6 @@ class ChainRenderer implements RendererInterface */ private array $renderers; - /** - * @param array $renderers - */ public function __construct(array $renderers) { /** @@ -30,13 +27,12 @@ public function __construct(array $renderers) } /** - * @param RendererInterface $renderer - * * @return $this */ public function addRenderer(RendererInterface $renderer): ChainRenderer { $this->renderers[] = $renderer; + return $this; } diff --git a/src/Renderer/EmbedRenderer.php b/src/Renderer/EmbedRenderer.php index 7ed8c2b..de82d0f 100644 --- a/src/Renderer/EmbedRenderer.php +++ b/src/Renderer/EmbedRenderer.php @@ -12,9 +12,6 @@ class EmbedRenderer implements RendererInterface { protected EmbedFinderFactory $embedFinderFactory; - /** - * @param EmbedFinderFactory $embedFinderFactory - */ public function __construct(EmbedFinderFactory $embedFinderFactory) { $this->embedFinderFactory = $embedFinderFactory; @@ -26,7 +23,7 @@ public function supports(DocumentInterface $document, array $options): bool $document->isEmbed() && $this->embedFinderFactory->supports($document->getEmbedPlatform()) && isset($options['embed']) - && $options['embed'] === true + && true === $options['embed'] ) { return true; } else { @@ -44,9 +41,10 @@ public function render(DocumentInterface $document, array $options): string if (null !== $finder) { return $finder->getIFrame($options); } + return ''; } catch (InvalidEmbedId $exception) { - return '

' . $exception->getMessage() . '

'; + return '

'.$exception->getMessage().'

'; } } } diff --git a/src/Renderer/ImageRenderer.php b/src/Renderer/ImageRenderer.php index f862a0d..b5d54a2 100644 --- a/src/Renderer/ImageRenderer.php +++ b/src/Renderer/ImageRenderer.php @@ -12,8 +12,8 @@ class ImageRenderer extends AbstractImageRenderer { public function supports(DocumentInterface $document, array $options): bool { - return (!isset($options['picture']) || $options['picture'] === false) && - parent::supports($document, $options); + return (!isset($options['picture']) || false === $options['picture']) + && parent::supports($document, $options); } public function render(DocumentInterface $document, array $options): string @@ -24,9 +24,9 @@ public function render(DocumentInterface $document, array $options): string * Override image by its first thumbnail if existing */ if ( - !$options['no_thumbnail'] && - $document instanceof HasThumbnailInterface && - $thumbnail = $document->getThumbnails()->first() + !$options['no_thumbnail'] + && $document instanceof HasThumbnailInterface + && $thumbnail = $document->getThumbnails()->first() ) { if ($thumbnail instanceof DocumentInterface) { $document = $thumbnail; @@ -38,7 +38,7 @@ public function render(DocumentInterface $document, array $options): string [ 'mimetype' => $document->getMimeType(), 'url' => $this->getSource($document, $options), - 'media' => null + 'media' => null, ] ); $assignation['alt'] = !empty($options['alt']) ? $options['alt'] : $document->getAlternativeText(); diff --git a/src/Renderer/InlineSvgRenderer.php b/src/Renderer/InlineSvgRenderer.php index 13a39a0..b6497a0 100644 --- a/src/Renderer/InlineSvgRenderer.php +++ b/src/Renderer/InlineSvgRenderer.php @@ -23,13 +23,10 @@ public function __construct(FilesystemOperator $documentsStorage) public function supports(DocumentInterface $document, array $options): bool { - return $document->isLocal() && $document->isSvg() && (isset($options['inline']) && $options['inline'] === true); + return $document->isLocal() && $document->isSvg() && (isset($options['inline']) && true === $options['inline']); } /** - * @param DocumentInterface $document - * @param array $options - * @return string * @throws FilesystemException */ public function render(DocumentInterface $document, array $options): string @@ -43,9 +40,10 @@ public function render(DocumentInterface $document, array $options): string $document, $assignation ); + return trim($this->htmlTidy($viewer->getContent())); } catch (\Exception $e) { - return '

' . $e->getMessage() . '

'; + return '

'.$e->getMessage().'

'; } } diff --git a/src/Renderer/PdfRenderer.php b/src/Renderer/PdfRenderer.php index 4ab5de0..d61f685 100644 --- a/src/Renderer/PdfRenderer.php +++ b/src/Renderer/PdfRenderer.php @@ -10,9 +10,9 @@ class PdfRenderer extends AbstractRenderer { public function supports(DocumentInterface $document, array $options): bool { - return $document->isPdf() && - key_exists('embed', $options) && - $options['embed'] === true; + return $document->isPdf() + && key_exists('embed', $options) + && true === $options['embed']; } /** diff --git a/src/Renderer/PictureRenderer.php b/src/Renderer/PictureRenderer.php index 9ed7fa8..78dd663 100644 --- a/src/Renderer/PictureRenderer.php +++ b/src/Renderer/PictureRenderer.php @@ -11,9 +11,9 @@ class PictureRenderer extends AbstractImageRenderer { public function supports(DocumentInterface $document, array $options): bool { - return isset($options['picture']) && - $options['picture'] === true && - parent::supports($document, $options); + return isset($options['picture']) + && true === $options['picture'] + && parent::supports($document, $options); } /** @@ -29,9 +29,9 @@ public function render(DocumentInterface $document, array $options): string * Override image by its first thumbnail if existing */ if ( - !$options['no_thumbnail'] && - $document instanceof HasThumbnailInterface && - $thumbnail = $document->getThumbnails()->first() + !$options['no_thumbnail'] + && $document instanceof HasThumbnailInterface + && $thumbnail = $document->getThumbnails()->first() ) { if ($thumbnail instanceof DocumentInterface) { $document = $thumbnail; @@ -80,9 +80,10 @@ private function parseMedia(DocumentInterface $document, array $options = []): a $mediaList[] = [ 'srcset' => $this->parseSrcSetInner($document, $media['srcset'], false, $options['absolute']), 'webp_srcset' => !$document->isWebp() ? $this->parseSrcSetInner($document, $media['srcset'], true, $options['absolute']) : null, - 'rule' => $media['rule'] + 'rule' => $media['rule'], ]; } + return $mediaList; } } diff --git a/src/Renderer/RendererInterface.php b/src/Renderer/RendererInterface.php index 76e4402..f000463 100644 --- a/src/Renderer/RendererInterface.php +++ b/src/Renderer/RendererInterface.php @@ -8,19 +8,7 @@ interface RendererInterface { - /** - * @param DocumentInterface $document - * @param array $options - * - * @return bool - */ public function supports(DocumentInterface $document, array $options): bool; - /** - * @param DocumentInterface $document - * @param array $options - * - * @return string - */ public function render(DocumentInterface $document, array $options): string; } diff --git a/src/Renderer/SvgRenderer.php b/src/Renderer/SvgRenderer.php index 06eaef9..a5f26be 100644 --- a/src/Renderer/SvgRenderer.php +++ b/src/Renderer/SvgRenderer.php @@ -22,7 +22,7 @@ public function __construct(FilesystemOperator $documentsStorage) public function supports(DocumentInterface $document, array $options): bool { - return $document->isSvg() && (!isset($options['inline']) || $options['inline'] === false); + return $document->isSvg() && (!isset($options['inline']) || false === $options['inline']); } public function render(DocumentInterface $document, array $options): string @@ -41,17 +41,12 @@ public function render(DocumentInterface $document, array $options): string if (is_string($value)) { $value = htmlspecialchars($value); } - $attrs[] = $key . '="' . $value . '"'; + $attrs[] = $key.'="'.$value.'"'; } - return ''; + return ''; } - /** - * @param array $options - * - * @return array - */ protected function getAttributes(array $options): array { $attributes = []; @@ -59,18 +54,19 @@ protected function getAttributes(array $options): array SvgDocumentViewer::$allowedAttributes, [ 'loading', - 'alt' + 'alt', ] ); foreach ($options as $key => $value) { if (in_array($key, $allowedAttributes)) { - if ($key === 'identifier') { + if ('identifier' === $key) { $attributes['id'] = $value; } else { $attributes[$key] = $value; } } } + return $attributes; } } diff --git a/src/Renderer/ThumbnailRenderer.php b/src/Renderer/ThumbnailRenderer.php index 72bddca..157701b 100644 --- a/src/Renderer/ThumbnailRenderer.php +++ b/src/Renderer/ThumbnailRenderer.php @@ -14,47 +14,33 @@ class ThumbnailRenderer implements RendererInterface { protected ?ChainRenderer $chainRenderer = null; - /** - * @param ChainRenderer|null $chainRenderer - */ public function __construct(?ChainRenderer $chainRenderer = null) { $this->chainRenderer = $chainRenderer; } - /** - * @param DocumentInterface $document - * @param array $options - * - * @return bool - */ public function supports(DocumentInterface $document, array $options): bool { - return null !== $this->chainRenderer && - (!key_exists('embed', $options) || - $options['embed'] !== true) && - $document instanceof HasThumbnailInterface && - $document->hasThumbnails() && - false !== $document->getThumbnails()->first(); + return null !== $this->chainRenderer + && (!key_exists('embed', $options) + || true !== $options['embed']) + && $document instanceof HasThumbnailInterface + && $document->hasThumbnails() + && false !== $document->getThumbnails()->first(); } - /** - * @param DocumentInterface $document - * @param array $options - * - * @return string - */ public function render(DocumentInterface $document, array $options): string { if ( - null !== $this->chainRenderer && - $document instanceof HasThumbnailInterface + null !== $this->chainRenderer + && $document instanceof HasThumbnailInterface ) { $thumbnail = $document->getThumbnails()->first(); if ($thumbnail instanceof DocumentInterface) { return $this->chainRenderer->render($thumbnail, $options); } } + return ''; } } diff --git a/src/Renderer/VideoRenderer.php b/src/Renderer/VideoRenderer.php index 15a84e4..c979e35 100644 --- a/src/Renderer/VideoRenderer.php +++ b/src/Renderer/VideoRenderer.php @@ -20,7 +20,7 @@ public function __construct( DocumentFinderInterface $documentFinder, Environment $templating, DocumentUrlGeneratorInterface $documentUrlGenerator, - string $templateBasePath = 'documents' + string $templateBasePath = 'documents', ) { parent::__construct($documentsStorage, $templating, $documentUrlGenerator, $templateBasePath); $this->documentFinder = $documentFinder; @@ -54,33 +54,28 @@ public function render(DocumentInterface $document, array $options): string */ $assignation['poster'] = $this->getPosterUrl($document, $options, $options['absolute']); } + return $this->renderHtmlElement('video.html.twig', $assignation); } - /** - * @param DocumentInterface $document - * @param array $options - * @param bool $absolute - * - * @return string|null - */ protected function getPosterUrl( DocumentInterface $document, array $options = [], - bool $absolute = false + bool $absolute = false, ): ?string { /* * Use document thumbnail first */ if ( - !$options['no_thumbnail'] && - $document instanceof HasThumbnailInterface && - $document->hasThumbnails() + !$options['no_thumbnail'] + && $document instanceof HasThumbnailInterface + && $document->hasThumbnails() ) { $thumbnail = $document->getThumbnails()->first(); if (false !== $thumbnail) { $this->documentUrlGenerator->setOptions($options); $this->documentUrlGenerator->setDocument($thumbnail); + return $this->documentUrlGenerator->getUrl($absolute); } } @@ -96,6 +91,7 @@ protected function getPosterUrl( foreach ($sourcesDocs as $sourcesDoc) { $this->documentUrlGenerator->setOptions($options); $this->documentUrlGenerator->setDocument($sourcesDoc); + return $this->documentUrlGenerator->getUrl($absolute); } @@ -107,10 +103,6 @@ protected function getPosterUrl( * * This method will search for document which filename is the same * except the extension. If you choose an MP4 file, it will look for a OGV and WEBM file. - * - * @param DocumentInterface $document - * - * @return array */ protected function getSourcesFiles(DocumentInterface $document): array { diff --git a/src/Repository/DocumentRepositoryInterface.php b/src/Repository/DocumentRepositoryInterface.php index d6bd01c..1de60ae 100644 --- a/src/Repository/DocumentRepositoryInterface.php +++ b/src/Repository/DocumentRepositoryInterface.php @@ -8,7 +8,9 @@ /** * @template T of \RZ\Roadiz\Documents\Models\DocumentInterface + * * @template-extends ObjectRepository + * * @extends ObjectRepository */ interface DocumentRepositoryInterface extends ObjectRepository diff --git a/src/SvgSizeResolver.php b/src/SvgSizeResolver.php index b460c56..bf07b87 100644 --- a/src/SvgSizeResolver.php +++ b/src/SvgSizeResolver.php @@ -4,7 +4,6 @@ namespace RZ\Roadiz\Documents; -use DOMNamedNodeMap; use League\Flysystem\FilesystemException; use League\Flysystem\FilesystemOperator; use RZ\Roadiz\Documents\Models\DocumentInterface; @@ -16,7 +15,7 @@ final class SvgSizeResolver public function __construct( private readonly DocumentInterface $document, - private readonly FilesystemOperator $documentsStorage + private readonly FilesystemOperator $documentsStorage, ) { } @@ -27,7 +26,7 @@ protected function getViewBoxAttributes(): ?array { try { $viewBox = $this->getSvgNodeAttributes()->getNamedItem('viewBox'); - if (null !== $viewBox && $viewBox->textContent !== "") { + if (null !== $viewBox && '' !== $viewBox->textContent) { return explode(' ', $viewBox->textContent); } } catch (\RuntimeException $exception) { @@ -37,17 +36,13 @@ protected function getViewBoxAttributes(): ?array return null; } - /** - * @param string $name - * @return int|null - */ protected function getIntegerAttribute(string $name): ?int { try { $attribute = $this->getSvgNodeAttributes()->getNamedItem($name); if ( null !== $attribute - && $attribute->textContent !== "" + && '' !== $attribute->textContent && !\str_contains($attribute->textContent, '%') ) { return (int) $attribute->textContent; @@ -55,13 +50,12 @@ protected function getIntegerAttribute(string $name): ?int } catch (\RuntimeException $exception) { return null; } + return null; } /** * First, find width attr, then resolve width from viewBox. - * - * @return int */ public function getWidth(): int { @@ -73,6 +67,7 @@ public function getWidth(): int $viewBoxAttr = $this->getViewBoxAttributes(); if (null !== $viewBoxAttr) { [$x, $y, $width, $height] = $viewBoxAttr; + return (int) $width; } @@ -81,8 +76,6 @@ public function getWidth(): int /** * First, find height attr, then resolve height from viewBox. - * - * @return int */ public function getHeight(): int { @@ -93,6 +86,7 @@ public function getHeight(): int $viewBoxAttr = $this->getViewBoxAttributes(); if (null !== $viewBoxAttr) { [$x, $y, $width, $height] = $viewBoxAttr; + return (int) $height; } @@ -114,7 +108,7 @@ private function getSvgNode(): \DOMElement private function getSvgNodeAttributes(): \DOMNamedNodeMap { - /** @var DOMNamedNodeMap|null $attributes */ + /** @var \DOMNamedNodeMap|null $attributes */ $attributes = $this->getSvgNode()->attributes; if (null === $attributes) { throw new \RuntimeException('SVG tag does not contain any attribute'); @@ -139,6 +133,7 @@ private function getDOMDocument(): \DOMDocument throw new \RuntimeException(sprintf('SVG (%s) could not be loaded.', $mountPath)); } } + return $this->xmlDocument; } } diff --git a/src/TwigExtension/DocumentExtension.php b/src/TwigExtension/DocumentExtension.php index 390944e..381b2a1 100644 --- a/src/TwigExtension/DocumentExtension.php +++ b/src/TwigExtension/DocumentExtension.php @@ -23,22 +23,16 @@ final class DocumentExtension extends AbstractExtension { /** - * @param RendererInterface $renderer - * @param EmbedFinderFactory $embedFinderFactory - * @param FilesystemOperator $documentsStorage * @param bool $throwExceptions Trigger exception if using filter on NULL values (default: false) */ public function __construct( private readonly RendererInterface $renderer, private readonly EmbedFinderFactory $embedFinderFactory, private readonly FilesystemOperator $documentsStorage, - private readonly bool $throwExceptions = false + private readonly bool $throwExceptions = false, ) { } - /** - * @return array - */ public function getFilters(): array { return [ @@ -49,28 +43,25 @@ public function getFilters(): array new TwigFilter('path', [$this, 'getPath']), new TwigFilter('exists', [$this, 'exists']), new TwigFilter('embedFinder', [$this, 'getEmbedFinder']), - new TwigFilter('formatBytes', array($this, 'formatBytes')), + new TwigFilter('formatBytes', [$this, 'formatBytes']), ]; } /** - * @param string|int $bytes - * @param int $precision - * @return string + * @param string|int $bytes */ public function formatBytes($bytes, int $precision = 2): string { - $size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB']; + $size = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; $factor = floor((\mb_strlen((string) $bytes) - 1) / 3); - return sprintf("%.{$precision}f", (int) $bytes / pow(1024, $factor)) . @$size[$factor]; + + return sprintf("%.{$precision}f", (int) $bytes / pow(1024, $factor)).@$size[$factor]; } /** - * @param DocumentInterface|null $document - * @return null|EmbedFinderInterface * @throws RuntimeError */ - public function getEmbedFinder(DocumentInterface $document = null): ?EmbedFinderInterface + public function getEmbedFinder(?DocumentInterface $document = null): ?EmbedFinderInterface { if (null === $document) { if ($this->throwExceptions) { @@ -102,19 +93,15 @@ public function getEmbedFinder(DocumentInterface $document = null): ?EmbedFinder } /** - * @param DocumentInterface|null $document - * @param array|null $options - * - * @return string * @throws RuntimeError */ - public function display(DocumentInterface $document = null, ?array $options = []): string + public function display(?DocumentInterface $document = null, ?array $options = []): string { if (null === $document) { if ($this->throwExceptions) { throw new RuntimeError('Document can’t be null to be displayed.'); } else { - return ""; + return ''; } } if (null === $options) { @@ -126,7 +113,7 @@ public function display(DocumentInterface $document = null, ?array $options = [] if ($this->throwExceptions) { throw new RuntimeError($embedException->getMessage()); } else { - return '

' . $embedException->getMessage() . '

'; + return '

'.$embedException->getMessage().'

'; } } catch (InvalidArgumentException $e) { throw new RuntimeError($e->getMessage(), -1, null, $e); @@ -140,11 +127,9 @@ public function display(DocumentInterface $document = null, ?array $options = [] * - Return `'landscape'` if width is higher or equal to height * - Return `'portrait'` if height is strictly lower to width * - * @param SizeableInterface |null $document - * @return null|string * @throws RuntimeError */ - public function getImageOrientation(SizeableInterface $document = null): ?string + public function getImageOrientation(?SizeableInterface $document = null): ?string { if (null === $document) { if ($this->throwExceptions) { @@ -154,15 +139,16 @@ public function getImageOrientation(SizeableInterface $document = null): ?string } } $size = $this->getImageSize($document); + return $size['width'] >= $size['height'] ? 'landscape' : 'portrait'; } /** - * @param SizeableInterface |null $document * @return array + * * @throws RuntimeError */ - public function getImageSize(SizeableInterface $document = null): array + public function getImageSize(?SizeableInterface $document = null): array { if (null === $document) { if ($this->throwExceptions) { @@ -174,6 +160,7 @@ public function getImageSize(SizeableInterface $document = null): array ]; } } + return [ 'width' => $document->getImageWidth(), 'height' => $document->getImageHeight(), @@ -181,11 +168,9 @@ public function getImageSize(SizeableInterface $document = null): array } /** - * @param SizeableInterface|null $document - * @return float * @throws RuntimeError */ - public function getImageRatio(SizeableInterface $document = null): float + public function getImageRatio(?SizeableInterface $document = null): float { if (null === $document) { if ($this->throwExceptions) { @@ -202,17 +187,13 @@ public function getImageRatio(SizeableInterface $document = null): float return 0.0; } - /** - * @param DocumentInterface|null $document - * @return null|string - */ - public function getPath(DocumentInterface $document = null): ?string + public function getPath(?DocumentInterface $document = null): ?string { if ( - null !== $document && - $document->isLocal() && - !$document->isPrivate() && - null !== $mountPath = $document->getMountPath() + null !== $document + && $document->isLocal() + && !$document->isPrivate() + && null !== $mountPath = $document->getMountPath() ) { return $this->documentsStorage->publicUrl($mountPath); } @@ -221,11 +202,9 @@ public function getPath(DocumentInterface $document = null): ?string } /** - * @param DocumentInterface|null $document - * @return bool * @throws FilesystemException */ - public function exists(DocumentInterface $document = null): bool + public function exists(?DocumentInterface $document = null): bool { if (null !== $document && $document->isLocal() && null !== $mountPath = $document->getMountPath()) { return $this->documentsStorage->fileExists($mountPath); diff --git a/src/UrlGenerators/AbstractDocumentUrlGenerator.php b/src/UrlGenerators/AbstractDocumentUrlGenerator.php index 2c05f7f..21f11b4 100644 --- a/src/UrlGenerators/AbstractDocumentUrlGenerator.php +++ b/src/UrlGenerators/AbstractDocumentUrlGenerator.php @@ -23,7 +23,7 @@ public function __construct( protected FilesystemOperator $documentsStorage, protected UrlHelper $urlHelper, protected CacheItemPoolInterface $optionsCacheAdapter, - array $options = [] + array $options = [], ) { $this->viewOptionsResolver = new ViewOptionsResolver(); $this->optionCompiler = new OptionsCompiler(); @@ -31,8 +31,8 @@ public function __construct( } /** - * @param array $options * @return $this + * * @throws InvalidArgumentException */ public function setOptions(array $options = []): static @@ -50,22 +50,18 @@ public function setOptions(array $options = []): static return $this; } - /** - * @return DocumentInterface|null - */ public function getDocument(): ?DocumentInterface { return $this->document; } /** - * @param DocumentInterface $document - * * @return $this */ public function setDocument(DocumentInterface $document): static { $this->document = $document; + return $this; } @@ -80,7 +76,7 @@ public function getUrl(bool $absolute = false): string $mountPath = $this->document->getMountPath(); - if (null !== $mountPath && ($this->options['noProcess'] === true || !$this->document->isProcessable())) { + if (null !== $mountPath && (true === $this->options['noProcess'] || !$this->document->isProcessable())) { $publicUrl = $this->documentsStorage->publicUrl($mountPath); if ($absolute && \str_starts_with($publicUrl, '/')) { return $this->urlHelper->getAbsoluteUrl($publicUrl); @@ -92,9 +88,5 @@ public function getUrl(bool $absolute = false): string return $this->getProcessedDocumentUrlByArray($absolute); } - /** - * @param bool $absolute - * @return string - */ abstract protected function getProcessedDocumentUrlByArray(bool $absolute = false): string; } diff --git a/src/UrlGenerators/DocumentUrlGeneratorInterface.php b/src/UrlGenerators/DocumentUrlGeneratorInterface.php index 1e1dd5f..612688e 100644 --- a/src/UrlGenerators/DocumentUrlGeneratorInterface.php +++ b/src/UrlGenerators/DocumentUrlGeneratorInterface.php @@ -8,23 +8,14 @@ interface DocumentUrlGeneratorInterface { - /** - * @param bool $absolute - * - * @return string - */ public function getUrl(bool $absolute = false): string; /** - * @param DocumentInterface $document - * * @return $this */ public function setDocument(DocumentInterface $document): static; /** - * @param array $options - * * @return $this */ public function setOptions(array $options = []): static; diff --git a/src/UrlGenerators/DummyDocumentUrlGenerator.php b/src/UrlGenerators/DummyDocumentUrlGenerator.php index e896b61..fb596db 100644 --- a/src/UrlGenerators/DummyDocumentUrlGenerator.php +++ b/src/UrlGenerators/DummyDocumentUrlGenerator.php @@ -20,30 +20,33 @@ public function getUrl(bool $absolute = false): string throw new \BadMethodCallException('noProcess option is not set'); } - if ($this->options['noProcess'] === true || !$this->document->isProcessable()) { - $path = '/files/' . $this->document->getRelativePath(); + if (true === $this->options['noProcess'] || !$this->document->isProcessable()) { + $path = '/files/'.$this->document->getRelativePath(); - return ($absolute) ? ('http://dummy.test' . $path) : ($path); + return ($absolute) ? ('http://dummy.test'.$path) : ($path); } $compiler = new OptionsCompiler(); $compiledOptions = $compiler->compile($this->options); if ($absolute) { - return 'http://dummy.test/assets/' . $compiledOptions . '/' . $this->document->getRelativePath(); + return 'http://dummy.test/assets/'.$compiledOptions.'/'.$this->document->getRelativePath(); } - return '/assets/' . $compiledOptions . '/' . $this->document->getRelativePath(); + + return '/assets/'.$compiledOptions.'/'.$this->document->getRelativePath(); } public function setDocument(DocumentInterface $document): static { $this->document = $document; + return $this; } public function setOptions(array $options = []): static { $this->options = $options; + return $this; } } diff --git a/src/UrlGenerators/OptionsCompiler.php b/src/UrlGenerators/OptionsCompiler.php index b186409..cd02b66 100644 --- a/src/UrlGenerators/OptionsCompiler.php +++ b/src/UrlGenerators/OptionsCompiler.php @@ -14,8 +14,7 @@ class OptionsCompiler /** * Compile Intervention Request options into a single query string. * - * @param array $options Resolved options - * @return string + * @param array $options Resolved options */ public function compile(array $options): string { @@ -27,40 +26,40 @@ public function compile(array $options): string $shortOptions = []; if (null === $this->options['fit'] && $this->options['width'] > 0) { - $shortOptions['w'] = 'w' . (int) $this->options['width']; + $shortOptions['w'] = 'w'.(int) $this->options['width']; } if (null === $this->options['fit'] && $this->options['height'] > 0) { - $shortOptions['h'] = 'h' . (int) $this->options['height']; + $shortOptions['h'] = 'h'.(int) $this->options['height']; } if (null !== $this->options['crop']) { - $shortOptions['c'] = 'c' . strip_tags($this->options['crop']); + $shortOptions['c'] = 'c'.strip_tags($this->options['crop']); } if ($this->options['blur'] > 0) { - $shortOptions['l'] = 'l' . ($this->options['blur']); + $shortOptions['l'] = 'l'.$this->options['blur']; } if (null !== $this->options['fit']) { - $shortOptions['f'] = 'f' . strip_tags($this->options['fit']); + $shortOptions['f'] = 'f'.strip_tags($this->options['fit']); } if (null !== $this->options['flip']) { - $shortOptions['m'] = 'm' . trim(strip_tags($this->options['flip'])); + $shortOptions['m'] = 'm'.trim(strip_tags($this->options['flip'])); } if ($this->options['rotate'] > 0) { - $shortOptions['r'] = 'r' . ($this->options['rotate']); + $shortOptions['r'] = 'r'.$this->options['rotate']; } if ($this->options['sharpen'] > 0) { - $shortOptions['s'] = 's' . ($this->options['sharpen']); + $shortOptions['s'] = 's'.$this->options['sharpen']; } if ($this->options['contrast'] > 0) { - $shortOptions['k'] = 'k' . ($this->options['contrast']); + $shortOptions['k'] = 'k'.$this->options['contrast']; } if ($this->options['grayscale']) { $shortOptions['g'] = 'g1'; } if ($this->options['quality'] > 0) { - $shortOptions['q'] = 'q' . $this->options['quality']; + $shortOptions['q'] = 'q'.$this->options['quality']; } if (null !== $this->options['background']) { - $shortOptions['b'] = 'b' . strip_tags($this->options['background']); + $shortOptions['b'] = 'b'.strip_tags($this->options['background']); } if ($this->options['progressive']) { $shortOptions['p'] = 'p1'; @@ -85,7 +84,7 @@ public function compile(array $options): string null !== $this->options['align'] && isset($availablePositionShort[$this->options['align']]) ) { - $shortOptions['a'] = 'a' . $availablePositionShort[$this->options['align']]; + $shortOptions['a'] = 'a'.$availablePositionShort[$this->options['align']]; } return implode('-', $shortOptions); diff --git a/src/Viewers/SvgDocumentViewer.php b/src/Viewers/SvgDocumentViewer.php index 15746a0..6545e8d 100644 --- a/src/Viewers/SvgDocumentViewer.php +++ b/src/Viewers/SvgDocumentViewer.php @@ -29,18 +29,15 @@ class SvgDocumentViewer ]; /** - * @param FilesystemOperator $documentsStorage - * @param DocumentInterface $document - * @param array $attributes - * @param bool $asObject Default false - * @param string $imageUrl Only needed if you set $asObject to true. + * @param bool $asObject Default false + * @param string $imageUrl only needed if you set $asObject to true */ public function __construct( FilesystemOperator $documentsStorage, DocumentInterface $document, array $attributes = [], bool $asObject = false, - string $imageUrl = "" + string $imageUrl = '', ) { $this->imageUrl = $imageUrl; $this->attributes = $attributes; @@ -52,7 +49,6 @@ public function __construct( /** * Get SVG string to be used inside HTML content. * - * @return string * @throws FilesystemException */ public function getContent(): string @@ -64,26 +60,23 @@ public function getContent(): string } } - /** - * @return array - */ protected function getAllowedAttributes(): array { $attributes = []; foreach ($this->attributes as $key => $value) { if (in_array($key, static::$allowedAttributes)) { - if ($key === 'identifier') { + if ('identifier' === $key) { $attributes['id'] = $value; } else { $attributes[$key] = $value; } } } + return $attributes; } /** - * @return string * @throws FilesystemException */ protected function getInlineSvg(): string @@ -95,7 +88,7 @@ protected function getInlineSvg(): string } if (!$this->documentsStorage->fileExists($mountPath)) { - throw new FileNotFoundException('SVG file does not exist: ' . $mountPath); + throw new FileNotFoundException('SVG file does not exist: '.$mountPath); } // Create a new sanitizer instance $sanitizer = new Sanitizer(); @@ -111,12 +104,11 @@ protected function getInlineSvg(): string // Pass it to the sanitizer and get it back clean return $this->injectAttributes($cleanSVG); } + return $dirtySVG; } /** - * @param string $svg - * @return string * @throws \Exception */ protected function injectAttributes(string $svg): string @@ -155,8 +147,7 @@ protected function injectAttributes(string $svg): string } /** - * @return string - * @deprecated Use SvgRenderer to render HTML object. + * @deprecated use SvgRenderer to render HTML object */ protected function getObjectSvg(): string { @@ -176,9 +167,9 @@ protected function getObjectSvg(): string $attrs = []; foreach ($attributes as $key => $value) { - $attrs[] = $key . '="' . htmlspecialchars($value) . '"'; + $attrs[] = $key.'="'.htmlspecialchars($value).'"'; } - return ''; + return ''; } } diff --git a/tests/MediaFinders/SimpleVimeoEmbedFinder.php b/tests/MediaFinders/SimpleVimeoEmbedFinder.php index 050b382..d2c87d1 100644 --- a/tests/MediaFinders/SimpleVimeoEmbedFinder.php +++ b/tests/MediaFinders/SimpleVimeoEmbedFinder.php @@ -10,17 +10,11 @@ final class SimpleVimeoEmbedFinder extends AbstractVimeoEmbedFinder { - /** - * @inheritDoc - */ protected function documentExists(ObjectManager $objectManager, string $embedId, ?string $embedPlatform): bool { throw new \RuntimeException('Not implemented'); } - /** - * @inheritDoc - */ protected function injectMetaInDocument(ObjectManager $objectManager, DocumentInterface $document): DocumentInterface { throw new \RuntimeException('Not implemented'); diff --git a/tests/MediaFinders/SimpleYoutubeEmbedFinder.php b/tests/MediaFinders/SimpleYoutubeEmbedFinder.php index 571e158..5c1fc0f 100644 --- a/tests/MediaFinders/SimpleYoutubeEmbedFinder.php +++ b/tests/MediaFinders/SimpleYoutubeEmbedFinder.php @@ -10,17 +10,11 @@ final class SimpleYoutubeEmbedFinder extends AbstractYoutubeEmbedFinder { - /** - * @inheritDoc - */ protected function documentExists(ObjectManager $objectManager, string $embedId, ?string $embedPlatform): bool { throw new \RuntimeException('Not implemented'); } - /** - * @inheritDoc - */ protected function injectMetaInDocument(ObjectManager $objectManager, DocumentInterface $document): DocumentInterface { throw new \RuntimeException('Not implemented'); diff --git a/tests/Renderer/AbstractRendererTestCase.php b/tests/Renderer/AbstractRendererTestCase.php index 659fd71..b5de777 100644 --- a/tests/Renderer/AbstractRendererTestCase.php +++ b/tests/Renderer/AbstractRendererTestCase.php @@ -16,6 +16,7 @@ use RZ\Roadiz\Documents\Tests\MediaFinders\SimpleYoutubeEmbedFinder; use RZ\Roadiz\Documents\UrlGenerators\DocumentUrlGeneratorInterface; use RZ\Roadiz\Documents\UrlGenerators\DummyDocumentUrlGenerator; +use Symfony\Component\HttpClient\HttpClient; use Twig\Environment; use Twig\Loader\FilesystemLoader; @@ -24,8 +25,9 @@ abstract class AbstractRendererTestCase extends TestCase protected function htmlTidy(string $body): string { $body = preg_replace('#[\n\r\t\s]{2,}#', ' ', $body); - $body = str_replace("/", '/', $body); + $body = str_replace('/', '/', $body); $body = html_entity_decode($body); + return preg_replace('#\>[\n\r\t\s]+\<#', '><', $body); } @@ -38,46 +40,40 @@ protected function getFilesystemOperator(): FilesystemOperator { return new MountManager([ 'public' => new Filesystem( - new LocalFilesystemAdapter(dirname(__DIR__) . '/../files/'), - publicUrlGenerator: new class () implements PublicUrlGenerator - { + new LocalFilesystemAdapter(dirname(__DIR__).'/../files/'), + publicUrlGenerator: new class implements PublicUrlGenerator { public function publicUrl(string $path, Config $config): string { - return '/files/' . $path; + return '/files/'.$path; } } ), 'private' => new Filesystem( - new LocalFilesystemAdapter(dirname(__DIR__) . '/../files/'), - publicUrlGenerator: new class () implements PublicUrlGenerator - { + new LocalFilesystemAdapter(dirname(__DIR__).'/../files/'), + publicUrlGenerator: new class implements PublicUrlGenerator { public function publicUrl(string $path, Config $config): string { - return '/files/' . $path; + return '/files/'.$path; } } - ) + ), ]); } protected function getEnvironment(): Environment { $loader = new FilesystemLoader([ - dirname(__DIR__) . '/../src/Resources/views' + dirname(__DIR__).'/../src/Resources/views', ]); + return new Environment($loader, [ - 'autoescape' => false + 'autoescape' => false, ]); } - - - /** - * @return EmbedFinderFactory - */ protected function getEmbedFinderFactory(): EmbedFinderFactory { - return new EmbedFinderFactory([ + return new EmbedFinderFactory(HttpClient::create(), [ 'youtube' => SimpleYoutubeEmbedFinder::class, 'vimeo' => SimpleVimeoEmbedFinder::class, ]); diff --git a/tests/Renderer/AudioRendererTest.php b/tests/Renderer/AudioRendererTest.php index e97a701..55b8f66 100644 --- a/tests/Renderer/AudioRendererTest.php +++ b/tests/Renderer/AudioRendererTest.php @@ -61,56 +61,49 @@ public function testRender(): void $mockDocument2->setFolder('folder'); $mockDocument2->setMimeType('audio/mpeg'); - $renderer = $this->getRenderer(); - $this->assertHtmlTidyEquals((<<assertHtmlTidyEquals(<<

Your browser does not support native audio.

EOT - ), ($renderer->render($mockDocument, []))); - + , $renderer->render($mockDocument, [])); - $this->assertHtmlTidyEquals((<<assertHtmlTidyEquals(<<

Your browser does not support native audio.

EOT - ), ($renderer->render($mockDocument2, []))); + , $renderer->render($mockDocument2, [])); - - $this->assertHtmlTidyEquals((<<assertHtmlTidyEquals(<<

Your browser does not support native audio.

EOT - ), ($renderer->render($mockDocument, [ - 'controls' => true, - 'loop' => true, - 'autoplay' => true, - ]))); - + , $renderer->render($mockDocument, [ + 'controls' => true, + 'loop' => true, + 'autoplay' => true, + ])); - $this->assertHtmlTidyEquals((<<assertHtmlTidyEquals(<<

Your browser does not support native audio.

EOT - ), ($renderer->render($mockDocument, [ - 'controls' => false - ]))); + , $renderer->render($mockDocument, [ + 'controls' => false, + ])); } - /** - * @return DocumentFinderInterface - */ private function getDocumentFinder(): DocumentFinderInterface { $finder = new ArrayDocumentFinder(); diff --git a/tests/Renderer/ChainRendererTest.php b/tests/Renderer/ChainRendererTest.php index 5fb5eac..941b1d9 100644 --- a/tests/Renderer/ChainRendererTest.php +++ b/tests/Renderer/ChainRendererTest.php @@ -48,57 +48,57 @@ public function testRender(): void $this->assertHtmlTidyEquals( '

Your browser does not support PDF native viewer.

', - ($renderer->render($mockPdfDocument, [ - 'embed' => true - ])) + $renderer->render($mockPdfDocument, [ + 'embed' => true, + ]) ); $this->assertHtmlTidyEquals( '

Your browser does not support PDF native viewer.

', - ($renderer->render($mockPdfDocument, ['absolute' => true, 'embed' => true])) + $renderer->render($mockPdfDocument, ['absolute' => true, 'embed' => true]) ); $this->assertHtmlTidyEquals( '', - ($renderer->render($mockSvgDocument, [])) + $renderer->render($mockSvgDocument, []) ); $this->assertHtmlTidyEquals( - (<< EOT - ), - ($renderer->render($mockSvgDocument, ['inline' => true])) + , + $renderer->render($mockSvgDocument, ['inline' => true]) ); $this->assertIsBool($mockDocumentYoutube->isEmbed()); $this->assertTrue($mockDocumentYoutube->isEmbed()); $this->assertHtmlTidyEquals( - (<< EOT - ), - ($renderer->render($mockDocumentYoutube, ['embed' => true])) + , + $renderer->render($mockDocumentYoutube, ['embed' => true]) ); $this->assertHtmlTidyEquals( - (<< file.jpg EOT - ), - ($renderer->render($mockPictureDocument, [ + , + $renderer->render($mockPictureDocument, [ 'width' => 300, - 'picture' => true - ])) + 'picture' => true, + ]) ); } } diff --git a/tests/Renderer/EmbedRendererTest.php b/tests/Renderer/EmbedRendererTest.php index 60b6e18..b332af8 100644 --- a/tests/Renderer/EmbedRendererTest.php +++ b/tests/Renderer/EmbedRendererTest.php @@ -65,91 +65,90 @@ public function testRender(): void $this->assertTrue($mockDocumentYoutube->isEmbed()); $this->assertHtmlTidyEquals( - (<< EOT - ), + , $renderer->render($mockDocumentYoutube, ['embed' => true]) ); $this->assertHtmlTidyEquals( - (<< EOT - ), + , $renderer->render($mockDocumentYoutube, [ 'embed' => true, - 'loading' => 'lazy' + 'loading' => 'lazy', ]) ); $this->assertHtmlTidyEquals( - (<< EOT - ), + , $renderer->render($mockDocumentYoutube, [ 'embed' => true, - 'width' => 500 + 'width' => 500, // height is auto calculated based on 16/10 ratio ]) ); $this->assertHtmlTidyEquals( - (<< EOT - ), + , $renderer->render($mockDocumentYoutube, [ 'embed' => true, 'width' => 500, - 'height' => 500 + 'height' => 500, ]) ); $this->assertHtmlTidyEquals( - (<< EOT - ), + , $renderer->render($mockDocumentYoutube, [ 'embed' => true, 'autoplay' => true, ]) ); - $this->assertIsBool($mockDocumentVimeo->isEmbed()); $this->assertTrue($mockDocumentVimeo->isEmbed()); $this->assertHtmlTidyEquals( - (<< EOT - ), + , $renderer->render($mockDocumentVimeo, ['embed' => true]) ); $this->assertHtmlTidyEquals( - (<< EOT - ), + , $renderer->render($mockDocumentVimeo, [ 'embed' => true, 'autoplay' => true, @@ -158,16 +157,16 @@ public function testRender(): void ); $this->assertHtmlTidyEquals( - (<< EOT - ), + , $renderer->render($mockDocumentVimeo, [ 'embed' => true, 'autoplay' => true, - 'background' => "1", // Hack background conflict option with background color + 'background' => '1', // Hack background conflict option with background color ]) ); } diff --git a/tests/Renderer/ImageRendererTest.php b/tests/Renderer/ImageRendererTest.php index a0be991..7d70def 100644 --- a/tests/Renderer/ImageRendererTest.php +++ b/tests/Renderer/ImageRendererTest.php @@ -82,7 +82,7 @@ public function testRender(): void EOT, $renderer->render($mockDocument, [ 'width' => 300, - 'absolute' => true + 'absolute' => true, ]) ); @@ -96,7 +96,7 @@ class="awesome-image responsive" /> $renderer->render($mockDocument, [ 'width' => 300, 'class' => 'awesome-image responsive', - 'absolute' => true + 'absolute' => true, ]) ); @@ -115,7 +115,7 @@ class="lazyload" /> EOT, $renderer->render($mockDocument, [ 'width' => 300, - 'lazyload' => true + 'lazyload' => true, ]) ); @@ -135,7 +135,7 @@ class="lazyload" /> $renderer->render($mockDocument, [ 'width' => 300, 'lazyload' => true, - 'fallback' => 'https://test.test/fallback.png' + 'fallback' => 'https://test.test/fallback.png', ]) ); @@ -147,7 +147,7 @@ class="lazyload" /> EOT, $renderer->render($mockDocument, [ 'width' => 300, - 'fallback' => 'https://test.test/fallback.png' + 'fallback' => 'https://test.test/fallback.png', ]) ); @@ -161,7 +161,7 @@ class="lazyload" /> EOT, $renderer->render($mockDocument, [ 'fit' => '600x400', - 'quality' => 70 + 'quality' => 70, ]) ); @@ -197,15 +197,15 @@ class="awesome-image responsive" /> 'width' => 300, 'srcset' => [[ 'format' => [ - 'width' => 300 + 'width' => 300, ], - 'rule' => '1x' - ],[ + 'rule' => '1x', + ], [ 'format' => [ - 'width' => 600 + 'width' => 600, ], - 'rule' => '2x' - ]] + 'rule' => '2x', + ]], ]) ); @@ -220,19 +220,19 @@ class="awesome-image responsive" /> 'width' => 300, 'srcset' => [[ 'format' => [ - 'width' => 300 + 'width' => 300, ], - 'rule' => '1x' - ],[ + 'rule' => '1x', + ], [ 'format' => [ - 'width' => 600 + 'width' => 600, ], - 'rule' => '2x' + 'rule' => '2x', ]], 'sizes' => [ '(max-width: 767px) 300px', - '(min-width: 768px) 400px' - ] + '(min-width: 768px) 400px', + ], ]) ); @@ -250,17 +250,17 @@ class="awesome-image responsive" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' - ],[ + 'rule' => '1x', + ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], 'sizes' => [ '(max-width: 767px) 300px', - '(min-width: 768px) 400px' - ] + '(min-width: 768px) 400px', + ], ]) ); @@ -280,17 +280,17 @@ class="awesome-image responsive" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' - ],[ + 'rule' => '1x', + ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], 'sizes' => [ '(max-width: 767px) 300px', - '(min-width: 768px) 400px' - ] + '(min-width: 768px) 400px', + ], ]) ); @@ -318,17 +318,17 @@ class="lazyload" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' - ],[ + 'rule' => '1x', + ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], 'sizes' => [ '(max-width: 767px) 300px', - '(min-width: 768px) 400px' - ] + '(min-width: 768px) 400px', + ], ]) ); @@ -359,17 +359,17 @@ class="lazyload" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' - ],[ + 'rule' => '1x', + ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], 'sizes' => [ '(max-width: 767px) 300px', - '(min-width: 768px) 400px' - ] + '(min-width: 768px) 400px', + ], ]) ); } diff --git a/tests/Renderer/PdfRendererTest.php b/tests/Renderer/PdfRendererTest.php index 931a04c..10da4c2 100644 --- a/tests/Renderer/PdfRendererTest.php +++ b/tests/Renderer/PdfRendererTest.php @@ -17,6 +17,7 @@ protected function getRenderer(): PdfRenderer $this->getUrlGenerator() ); } + public function testSupports(): void { $mockValidDocument = new SimpleDocument(); @@ -35,7 +36,7 @@ public function testSupports(): void ); $this->assertTrue( $renderer->supports($mockValidDocument, [ - 'embed' => true + 'embed' => true, ]) ); @@ -64,7 +65,7 @@ public function testRender(): void $this->assertHtmlTidyEquals( '

Your browser does not support PDF native viewer.

', $renderer->render($mockDocument, [ - 'embed' => true + 'embed' => true, ]) ); } diff --git a/tests/Renderer/PictureRendererTest.php b/tests/Renderer/PictureRendererTest.php index 829ce0e..829e087 100644 --- a/tests/Renderer/PictureRendererTest.php +++ b/tests/Renderer/PictureRendererTest.php @@ -133,7 +133,7 @@ public function testRender(): void , $renderer->render($mockDocument, [ 'noProcess' => true, - 'picture' => true + 'picture' => true, ]) ); @@ -163,7 +163,7 @@ public function testRender(): void , $renderer->render($mockWebpDocument, [ 'noProcess' => true, - 'picture' => true + 'picture' => true, ]) ); @@ -179,7 +179,7 @@ public function testRender(): void $renderer->render($mockDocument, [ 'absolute' => true, 'noProcess' => true, - 'picture' => true + 'picture' => true, ]) ); @@ -187,7 +187,7 @@ public function testRender(): void $renderer->render($mockDocument, [ 'width' => 300, 'absolute' => true, - 'picture' => true + 'picture' => true, ]), << @@ -203,7 +203,7 @@ public function testRender(): void 'width' => 300, 'class' => 'awesome-image responsive', 'absolute' => true, - 'picture' => true + 'picture' => true, ]), << @@ -218,7 +218,7 @@ public function testRender(): void $renderer->render($mockDocument, [ 'width' => 300, 'lazyload' => true, - 'picture' => true + 'picture' => true, ]), << @@ -253,7 +253,7 @@ class="lazyload" /> 'width' => 300, 'lazyload' => true, 'picture' => true, - 'fallback' => 'https://test.test/fallback.png' + 'fallback' => 'https://test.test/fallback.png', ]), << @@ -280,7 +280,7 @@ class="lazyload" /> $renderer->render($mockDocument, [ 'width' => 300, 'fallback' => 'https://test.test/fallback.png', - 'picture' => true + 'picture' => true, ]), << @@ -297,7 +297,7 @@ class="lazyload" /> 'width' => 300, 'lazyload' => true, 'class' => 'awesome-image responsive', - 'picture' => true + 'picture' => true, ]), << @@ -332,16 +332,16 @@ class="awesome-image responsive" /> 'width' => 300, 'srcset' => [[ 'format' => [ - 'width' => 300 + 'width' => 300, ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ - 'width' => 600 + 'width' => 600, ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'picture' => true + 'picture' => true, ]), << @@ -361,20 +361,20 @@ class="awesome-image responsive" /> 'width' => 300, 'srcset' => [[ 'format' => [ - 'width' => 300 + 'width' => 300, ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ - 'width' => 600 + 'width' => 600, ], - 'rule' => '2x' + 'rule' => '2x', ]], 'sizes' => [ '(max-width: 767px) 300px', - '(min-width: 768px) 400px' + '(min-width: 768px) 400px', ], - 'picture' => true + 'picture' => true, ]), << @@ -398,18 +398,18 @@ class="awesome-image responsive" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], 'sizes' => [ '(max-width: 767px) 300px', - '(min-width: 768px) 400px' + '(min-width: 768px) 400px', ], - 'picture' => true + 'picture' => true, ]), << @@ -436,18 +436,18 @@ class="awesome-image responsive" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], 'sizes' => [ '(max-width: 767px) 300px', - '(min-width: 768px) 400px' + '(min-width: 768px) 400px', ], - 'picture' => true + 'picture' => true, ]), << @@ -474,14 +474,14 @@ class="awesome-image responsive" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'picture' => true + 'picture' => true, ]), << @@ -525,14 +525,14 @@ class="lazyload" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'picture' => true + 'picture' => true, ]), << @@ -576,16 +576,16 @@ class="lazyload" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'rule' => '(min-width: 600px)' + 'rule' => '(min-width: 600px)', ]], - 'picture' => true + 'picture' => true, ]), << @@ -612,29 +612,29 @@ class="lazyload" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'rule' => '(min-width: 600px)' + 'rule' => '(min-width: 600px)', ], [ 'srcset' => [[ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '2400x1600', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'rule' => '(min-width: 1200px)' + 'rule' => '(min-width: 1200px)', ]], - 'picture' => true + 'picture' => true, ]), << @@ -670,29 +670,29 @@ class="lazyload" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'rule' => '(min-width: 600px)' + 'rule' => '(min-width: 600px)', ], [ 'srcset' => [[ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '2400x1600', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'rule' => '(min-width: 1200px)' + 'rule' => '(min-width: 1200px)', ]], - 'picture' => true + 'picture' => true, ]), << @@ -729,29 +729,29 @@ class="lazyload" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'rule' => '(min-width: 600px)' + 'rule' => '(min-width: 600px)', ], [ 'srcset' => [[ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '2400x1600', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'rule' => '(min-width: 1200px)' + 'rule' => '(min-width: 1200px)', ]], - 'picture' => true + 'picture' => true, ]), << @@ -803,29 +803,29 @@ class="lazyload" /> 'format' => [ 'fit' => '600x400', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'rule' => '(min-width: 600px)' + 'rule' => '(min-width: 600px)', ], [ 'srcset' => [[ 'format' => [ 'fit' => '1200x800', ], - 'rule' => '1x' + 'rule' => '1x', ], [ 'format' => [ 'fit' => '2400x1600', ], - 'rule' => '2x' + 'rule' => '2x', ]], - 'rule' => '(min-width: 1200px)' + 'rule' => '(min-width: 1200px)', ]], - 'picture' => true + 'picture' => true, ]), << diff --git a/tests/Renderer/VideoRendererTest.php b/tests/Renderer/VideoRendererTest.php index e311a4e..7d80fe0 100644 --- a/tests/Renderer/VideoRendererTest.php +++ b/tests/Renderer/VideoRendererTest.php @@ -134,14 +134,11 @@ public function testRender(): void EOT , $renderer->render($mockDocument, [ - 'controls' => false + 'controls' => false, ]) ); } - /** - * @return DocumentFinderInterface - */ private function getDocumentFinder(): DocumentFinderInterface { $finder = new ArrayDocumentFinder();