From 73ebafdb5c5f83bf4257fa877c634e8db92357b3 Mon Sep 17 00:00:00 2001 From: roadiz-ci Date: Mon, 17 Feb 2025 09:55:23 +0000 Subject: [PATCH] chore: Bumped --- .github/workflows/run-test.yml | 4 +- composer.json | 22 +-- 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, 1864 insertions(+), 1062 deletions(-) create mode 100644 phpcs.xml.dist create mode 100644 src/MediaFinders/AbstractTwitchEmbedFinder.php diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml index 99e3d46..a28c61b 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.2', '8.3'] + php-version: ['8.1', '8.2', '8.3'] steps: - uses: shivammathur/setup-php@v2 with: @@ -37,5 +37,7 @@ 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 02257dc..1b6941d 100644 --- a/composer.json +++ b/composer.json @@ -18,15 +18,17 @@ } ], "require": { - "php": ">=8.2", + "php": ">=8.1", "ext-json": "*", "ext-gd": "*", "ext-dom": "*", "ext-zip": "*", "ext-simplexml": "*", "ext-fileinfo": "*", - "doctrine/orm": "~2.20.0", + "doctrine/orm": "~2.19.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", @@ -36,19 +38,17 @@ "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.16" + "twig/twig": "^3.1" }, "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/phpdoc-parser": "<2", - "phpstan/phpstan-doctrine": "^1.3", - "phpunit/phpunit": "^9.5" + "phpstan/phpstan-doctrine": "^1.3" }, "autoload": { "psr-4": { @@ -68,8 +68,8 @@ }, "extra": { "branch-alias": { - "dev-master": "2.4.x-dev", - "dev-develop": "2.5.x-dev" + "dev-master": "2.3.x-dev", + "dev-develop": "2.4.x-dev" } } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..19bff0c --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,13 @@ + + + + + + + + + + + src/ + diff --git a/src/AbstractDocumentFactory.php b/src/AbstractDocumentFactory.php index 472f78c..1e3a0dd 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,56 +42,69 @@ 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 ( - ('text/plain' === $document->getMimeType() || 'text/html' === $document->getMimeType()) - && preg_match('#\.svg$#', $document->getFilename()) + ($document->getMimeType() === 'text/plain' || $document->getMimeType() === 'text/html') && + 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 @@ -103,14 +116,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 (false === $allowEmpty) { + if ($allowEmpty === false) { // Getter throw exception on null file $file = $this->getFile(); } else { @@ -134,8 +147,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; } @@ -147,7 +160,6 @@ 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; } } @@ -163,8 +175,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()); @@ -184,6 +196,8 @@ 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 @@ -217,7 +231,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()); @@ -233,6 +247,9 @@ public function updateDocument(DocumentInterface $document): DocumentInterface } /** + * @param File $localFile + * @param DocumentInterface $document + * @return void * @throws FilesystemException */ public function moveFile(File $localFile, DocumentInterface $document): void @@ -250,6 +267,9 @@ public function moveFile(File $localFile, DocumentInterface $document): void } } + /** + * @return string + */ protected function getFileName(): string { $file = $this->getFile(); @@ -258,8 +278,8 @@ protected function getFileName(): string $fileName = $file->getClientOriginalName(); } elseif ( $file instanceof DownloadedFile - && null !== $file->getOriginalFilename() - && '' !== $file->getOriginalFilename() + && $file->getOriginalFilename() !== null + && $file->getOriginalFilename() !== '' ) { $fileName = $file->getOriginalFilename(); } else { @@ -272,6 +292,9 @@ 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 @@ -280,7 +303,6 @@ 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 e400617..ff42399 100644 --- a/src/AbstractDocumentFinder.php +++ b/src/AbstractDocumentFinder.php @@ -6,40 +6,49 @@ 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); @@ -61,7 +70,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 56c9ead..14a4f01 100644 --- a/src/ArrayDocumentFinder.php +++ b/src/ArrayDocumentFinder.php @@ -24,7 +24,6 @@ public function __construct() /** * @param array $fileNames - * * @return ArrayCollection */ public function findAllByFilenames(array $fileNames): ArrayCollection @@ -50,7 +49,9 @@ public function findOneByHashAndAlgorithm(string $hash, string $algorithm): ?Doc return null; } + /** + * @param DocumentInterface $document * @return $this */ public function addDocument(DocumentInterface $document): self @@ -58,7 +59,6 @@ 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 b798905..39ce610 100644 --- a/src/AverageColorResolver.php +++ b/src/AverageColorResolver.php @@ -11,7 +11,6 @@ class AverageColorResolver public function getAverageColor(Image $image): string { $colorArray = $this->getAverageColorAsArray($image); - return sprintf( '#%02x%02x%02x', $colorArray[0], @@ -19,13 +18,16 @@ 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 5536b40..0edba4a 100644 --- a/src/Console/AbstractDocumentCommand.php +++ b/src/Console/AbstractDocumentCommand.php @@ -16,13 +16,16 @@ abstract class AbstractDocumentCommand extends Command { - public function __construct( - protected ManagerRegistry $managerRegistry, - protected ImageManager $imageManager, - protected FilesystemOperator $documentsStorage, - ?string $name = null, - ) { - parent::__construct($name); + 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; } protected function getManager(): ObjectManager @@ -31,23 +34,26 @@ 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): int + protected function onEachDocument(callable $method, SymfonyStyle $io, int $batchSize = 20) { $i = 0; $manager = $this->getManager(); @@ -59,7 +65,6 @@ protected function onEachDocument(callable $method, SymfonyStyle $io, int $batch if ($count < 1) { $io->success('No document found'); - return 0; } @@ -80,7 +85,5 @@ 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 fb6750b..796047d 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); - return $this->onEachDocument(function (DocumentInterface $document) { + $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)) { - 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.'); + 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.'); + } } } } diff --git a/src/Console/DocumentClearFolderCommand.php b/src/Console/DocumentClearFolderCommand.php index 13c0881..e52f187 100644 --- a/src/Console/DocumentClearFolderCommand.php +++ b/src/Console/DocumentClearFolderCommand.php @@ -28,7 +28,6 @@ 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); @@ -45,7 +44,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $em = $this->getManager(); /** @var FolderInterface|null $folder */ $folder = $em->find(FolderInterface::class, $folderId); - if (null === $folder) { + if ($folder === null) { throw new \InvalidArgumentException(sprintf('Folder #%d does not exist.', $folderId)); } @@ -59,36 +58,33 @@ 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 )) ) { - 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. + /** @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(); } - ++$i; - $this->io->progressAdvance(); + $em->flush(); + $this->io->progressFinish(); } - $em->flush(); - $this->io->progressFinish(); return 0; } diff --git a/src/Console/DocumentDownscaleCommand.php b/src/Console/DocumentDownscaleCommand.php index ad3834b..3868230 100644 --- a/src/Console/DocumentDownscaleCommand.php +++ b/src/Console/DocumentDownscaleCommand.php @@ -12,11 +12,9 @@ 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; /** @@ -24,24 +22,27 @@ */ class DocumentDownscaleCommand extends AbstractDocumentCommand { + private ?int $maxPixelSize; + private DownscaleImageManager $downscaler; + private EventDispatcherInterface $dispatcher; + public function __construct( ManagerRegistry $managerRegistry, ImageManager $imageManager, FilesystemOperator $documentsStorage, - private readonly ?int $maxPixelSize, - private readonly DownscaleImageManager $downscaler, - private readonly EventDispatcherInterface $dispatcher, - ?string $name = null, + ?int $maxPixelSize, + DownscaleImageManager $downscaler, + EventDispatcherInterface $dispatcher ) { - parent::__construct($managerRegistry, $imageManager, $documentsStorage, $name); + parent::__construct($managerRegistry, $imageManager, $documentsStorage); + $this->maxPixelSize = $maxPixelSize; + $this->downscaler = $downscaler; + $this->dispatcher = $dispatcher; } 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.'); } @@ -49,101 +50,48 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - if (null === $this->maxPixelSize || $this->maxPixelSize <= 0) { + 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 { $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 f26007d..0beb383 100644 --- a/src/Console/DocumentDuplicatesCommand.php +++ b/src/Console/DocumentDuplicatesCommand.php @@ -29,13 +29,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int $count = \count($documents); $rows = []; - if (0 === $count) { + if ($count <= 0) { $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, @@ -46,7 +45,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 416372f..ecfe884 100644 --- a/src/Console/DocumentFileHashCommand.php +++ b/src/Console/DocumentFileHashCommand.php @@ -39,7 +39,11 @@ 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(); @@ -48,7 +52,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($count <= 0) { $this->io->success('All document files have hash.'); - return 0; } @@ -56,25 +59,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** @var DocumentInterface $document */ foreach ($documents as $document) { $mountPath = $document->getMountPath(); - if (null === $mountPath || !($document instanceof FileHashInterface)) { - $this->io->progressAdvance(); - continue; - } + 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); + } - $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. + if (($i % $batchSize) === 0) { + $em->flush(); // Executes all updates. + } + ++$i; + $this->io->progressAdvance(); } - ++$i; - $this->io->progressAdvance(); } $em->flush(); $this->io->progressFinish(); diff --git a/src/Console/DocumentFilesizeCommand.php b/src/Console/DocumentFilesizeCommand.php index 1b8ec76..a398f25 100644 --- a/src/Console/DocumentFilesizeCommand.php +++ b/src/Console/DocumentFilesizeCommand.php @@ -24,24 +24,25 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { + $em = $this->getManager(); $this->io = new SymfonyStyle($input, $output); - return $this->onEachDocument(function (DocumentInterface $document) { + $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()) { - return; - } - try { - $document->setFilesize($this->documentsStorage->fileSize($document->getMountPath())); - } catch (FilesystemException $exception) { - $this->io->error($exception->getMessage()); + if (null !== $document->getMountPath()) { + 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 008b0cd..c2954f6 100644 --- a/src/Console/DocumentPruneCommand.php +++ b/src/Console/DocumentPruneCommand.php @@ -36,40 +36,36 @@ 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, a custom-form answer or an attribute.', + '%d documents are not used by a node-source, a tag, a setting 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 )) ) { - 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. + $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(); } - ++$i; - $this->io->progressAdvance(); + $em->flush(); + $this->io->progressFinish(); } - $em->flush(); - $this->io->progressFinish(); return 0; } diff --git a/src/Console/DocumentPruneOrphansCommand.php b/src/Console/DocumentPruneOrphansCommand.php index a3e9e85..b34ed74 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()) { - 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; + 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++; + } + } } } } diff --git a/src/Console/DocumentSizeCommand.php b/src/Console/DocumentSizeCommand.php index 466b9f9..c9368b7 100644 --- a/src/Console/DocumentSizeCommand.php +++ b/src/Console/DocumentSizeCommand.php @@ -27,11 +27,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $this->io = new SymfonyStyle($input, $output); - return $this->onEachDocument(function (DocumentInterface $document) { + $this->onEachDocument(function (DocumentInterface $document) { if ($document instanceof SizeableInterface) { $this->updateDocumentSize($document); } }, new SymfonyStyle($input, $output)); + + return 0; } private function updateDocumentSize(DocumentInterface $document): void @@ -40,18 +42,7 @@ private function updateDocumentSize(DocumentInterface $document): void return; } $mountPath = $document->getMountPath(); - 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()) { + if (null !== $mountPath && $document->isImage()) { try { $imageProcess = $this->imageManager->make($this->documentsStorage->readStream($mountPath)); $document->setImageWidth($imageProcess->width()); @@ -61,7 +52,15 @@ private function updateDocumentSize(DocumentInterface $document): void * Do nothing * just return 0 width and height */ - $this->io->error($document->getMountPath().' is not a readable image.'); + $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()); } } } diff --git a/src/DocumentArchiver.php b/src/DocumentArchiver.php index e4cc8f8..e29f746 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,14 +59,17 @@ 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( @@ -77,7 +80,6 @@ public function archiveAndServe( 'attachment' ); $response->deleteFileAfterSend($unlink); - return $response; } } diff --git a/src/DocumentFinderInterface.php b/src/DocumentFinderInterface.php index a446de4..336f23a 100644 --- a/src/DocumentFinderInterface.php +++ b/src/DocumentFinderInterface.php @@ -16,24 +16,37 @@ 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 838c42f..9677fa0 100644 --- a/src/DownloadedFile.php +++ b/src/DownloadedFile.php @@ -4,6 +4,8 @@ namespace RZ\Roadiz\Documents; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Psr7\Utils; use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\String\UnicodeString; @@ -11,11 +13,19 @@ 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; @@ -24,7 +34,10 @@ public function setOriginalFilename(?string $originalFilename): DownloadedFile } /** - * Final constructor for safe usage in DownloadedFile::fromUrl. + * Final constructor for safe usage in DownloadedFile::fromUrl + * + * @param string $path + * @param bool $checkPath */ final public function __construct(string $path, bool $checkPath = true) { @@ -33,6 +46,10 @@ 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 { @@ -49,27 +66,29 @@ 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)); - $distantResource = fopen($url, 'r'); - if (false === $distantResource) { + $distantHandle = fopen($url, 'r'); + if (false === $distantHandle) { return null; } - + $original = Utils::streamFor($distantHandle); $tmpFile = tempnam(sys_get_temp_dir(), static::sanitizeFilename($baseName)); if (false === $tmpFile) { return null; } - $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.'); - } + $handle = fopen($tmpFile, 'w'); + $local = Utils::streamFor($handle); + $local->write($original->getContents()); + $local->close(); $file = new static($tmpFile); if (!empty($originalName)) { @@ -80,17 +99,18 @@ 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 (\RuntimeException $e) { + } catch (RequestException $e) { + return null; + } catch (\ErrorException $e) { return null; } - return null; } } diff --git a/src/DownscaleImageManager.php b/src/DownscaleImageManager.php index af555ad..f141b3a 100644 --- a/src/DownscaleImageManager.php +++ b/src/DownscaleImageManager.php @@ -22,13 +22,14 @@ 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 @@ -49,7 +50,7 @@ public function processAndOverrideDocument(?DocumentInterface $document = null): $this->logger->info( 'Document has been downscaled.', [ - 'path' => $mountPath, + 'path' => $mountPath ] ); } @@ -60,6 +61,7 @@ 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 @@ -91,11 +93,14 @@ 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 ( - 'image/gif' !== $processImage->mime() + $processImage->mime() !== 'image/gif' && ($processImage->width() > $this->maxPixelSize || $processImage->height() > $this->maxPixelSize) ) { // prevent possible upsizing @@ -107,23 +112,25 @@ 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; @@ -136,16 +143,20 @@ 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. @@ -162,7 +173,7 @@ protected function createDocumentFromImage( $this->em->flush(); } - if (null === $originalDocument->getRawDocument() || false === $keepExistingRaw) { + if (null === $originalDocument->getRawDocument() || $keepExistingRaw === false) { if (null === $processImage) { return $originalDocument; } @@ -175,7 +186,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) { @@ -186,10 +197,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. @@ -215,7 +226,6 @@ protected function createDocumentFromImage( return $originalDocument; } - return null; } elseif (null !== $processImage) { /* diff --git a/src/Events/DocumentCreatedEvent.php b/src/Events/DocumentCreatedEvent.php index aaa6890..0a15eac 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 1337647..c5825bd 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 123d21d..5879c51 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 45a24fa..714f217 100644 --- a/src/Events/DocumentLifeCycleSubscriber.php +++ b/src/Events/DocumentLifeCycleSubscriber.php @@ -4,10 +4,10 @@ namespace RZ\Roadiz\Documents\Events; -use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; -use Doctrine\ORM\Event\PostRemoveEventArgs; +use Doctrine\Common\EventSubscriber; 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,40 +17,49 @@ /** * Handle file management on document's lifecycle events. */ -#[AsDoctrineListener(event: Events::postRemove)] -#[AsDoctrineListener(event: Events::preUpdate)] -final readonly class DocumentLifeCycleSubscriber +class DocumentLifeCycleSubscriber implements EventSubscriber { - public function __construct(private FilesystemOperator $documentsStorage) + private FilesystemOperator $documentsStorage; + + public function __construct(FilesystemOperator $documentsStorage) + { + $this->documentsStorage = $documentsStorage; + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents(): array { + 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 ( - $args->hasChangedField('filename') + $document instanceof DocumentInterface + && $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 ($args->hasChangedField('private')) { - if (true === $document->isPrivate()) { - $this->makePrivate($document); + if ($document instanceof DocumentInterface && $args->hasChangedField('private')) { + if ($document->isPrivate() === true) { + $this->makePrivate($document, $args); } else { - $this->makePublic($document); + $this->makePublic($document, $args); } } } @@ -78,7 +87,12 @@ private function renameDocumentFilename(DocumentInterface $document, PreUpdateEv $this->documentsStorage->move($oldPath, $newPath); } - private function makePublic(DocumentInterface $document): void + /** + * @param DocumentInterface $document + * @param PreUpdateEventArgs $args + * @throws FilesystemException + */ + protected function makePublic(DocumentInterface $document, PreUpdateEventArgs $args): void { $this->validateDocument($document); $documentPublicPath = $this->getDocumentPublicPath($document); @@ -94,7 +108,12 @@ private function makePublic(DocumentInterface $document): void } } - private function makePrivate(DocumentInterface $document): void + /** + * @param DocumentInterface $document + * @param PreUpdateEventArgs $args + * @throws FilesystemException + */ + protected function makePrivate(DocumentInterface $document, PreUpdateEventArgs $args): void { $this->validateDocument($document); $documentPublicPath = $this->getDocumentPublicPath($document); @@ -113,36 +132,36 @@ private function makePrivate(DocumentInterface $document): void /** * Unlink file after document has been deleted. * + * @param LifecycleEventArgs $args * @throws FilesystemException */ - public function postRemove(PostRemoveEventArgs $args): void + public function postRemove(LifecycleEventArgs $args): void { $document = $args->getObject(); - - 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); + 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. } - $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 */ - private function cleanFileDirectory(string $documentFolderPath): void + protected function cleanFileDirectory(string $documentFolderPath): void { if ($this->documentsStorage->directoryExists($documentFolderPath)) { $isDirEmpty = \count($this->documentsStorage->listContents($documentFolderPath)->toArray()) <= 0; @@ -152,66 +171,100 @@ private function cleanFileDirectory(string $documentFolderPath): void } } - private function getDocumentRelativePathForFilename(DocumentInterface $document, string $filename): string + /** + * @param DocumentInterface $document + * @param string $filename + * + * @return string + */ + protected function getDocumentRelativePathForFilename(DocumentInterface $document, string $filename): string { $this->validateDocument($document); - return $document->getFolder().DIRECTORY_SEPARATOR.$filename; + return $document->getFolder() . DIRECTORY_SEPARATOR . $filename; } - private function getDocumentMountPathForFilename(DocumentInterface $document, string $filename): string + /** + * @param DocumentInterface $document + * @param string $filename + * + * @return string + */ + protected 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); } - private function getDocumentPath(DocumentInterface $document): string + /** + * @param DocumentInterface $document + * @return string + */ + protected function getDocumentPath(DocumentInterface $document): string { $this->validateDocument($document); if ($document->isPrivate()) { return $this->getDocumentPrivatePath($document); } - return $this->getDocumentPublicPath($document); } - private function getDocumentPublicPath(DocumentInterface $document): string + /** + * @param DocumentInterface $document + * @return string + */ + protected function getDocumentPublicPath(DocumentInterface $document): string { - return 'public://'.$document->getRelativePath(); + return 'public://' . $document->getRelativePath(); } - private function getDocumentPrivatePath(DocumentInterface $document): string + /** + * @param DocumentInterface $document + * @return string + */ + protected function getDocumentPrivatePath(DocumentInterface $document): string { - return 'private://'.$document->getRelativePath(); + return 'private://' . $document->getRelativePath(); } - private function getDocumentFolderPath(DocumentInterface $document): string + /** + * @param DocumentInterface $document + * @return string + */ + protected function getDocumentFolderPath(DocumentInterface $document): string { if ($document->isPrivate()) { return $this->getDocumentPrivateFolderPath($document); } - return $this->getDocumentPublicFolderPath($document); } - private function getDocumentPublicFolderPath(DocumentInterface $document): string + /** + * @param DocumentInterface $document + * @return string + */ + protected function getDocumentPublicFolderPath(DocumentInterface $document): string { - return 'public://'.$document->getFolder(); + return 'public://' . $document->getFolder(); } - private function getDocumentPrivateFolderPath(DocumentInterface $document): string + /** + * @param DocumentInterface $document + * @return string + */ + protected function getDocumentPrivateFolderPath(DocumentInterface $document): string { - return 'private://'.$document->getFolder(); + return 'private://' . $document->getFolder(); } /** + * @param DocumentInterface $document * @throws DocumentWithoutFileException */ - private function validateDocument(DocumentInterface $document): void + protected 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 085512a..8289f34 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 54c3a93..fef5487 100644 --- a/src/Exceptions/EmbedDocumentAlreadyExistsException.php +++ b/src/Exceptions/EmbedDocumentAlreadyExistsException.php @@ -4,12 +4,14 @@ 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 ff48dab..292aa2a 100644 --- a/src/Exceptions/InvalidEmbedId.php +++ b/src/Exceptions/InvalidEmbedId.php @@ -18,11 +18,17 @@ 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 73550f9..7eeb90c 100644 --- a/src/MediaFinders/AbstractDailymotionEmbedFinder.php +++ b/src/MediaFinders/AbstractDailymotionEmbedFinder.php @@ -12,6 +12,7 @@ abstract class AbstractDailymotionEmbedFinder extends AbstractEmbedFinder { /** + * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'dailymotion'; @@ -21,8 +22,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 @@ -30,38 +31,53 @@ public static function getPlatform(): string return static::$platform; } - protected function validateEmbedId(string $embedId = ''): string + /** + * @inheritDoc + */ + protected function validateEmbedId(string $embedId = ""): string { - if (1 === preg_match(static::$idPattern, $embedId, $matches)) { + if (preg_match(static::$idPattern, $embedId, $matches) === 1) { return $embedId; } - if (1 === preg_match(static::$realIdPattern, $embedId, $matches)) { + if (preg_match(static::$realIdPattern, $embedId, $matches) === 1) { 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'] ?? ''; } - public function getFeed(): array|\SimpleXMLElement|null + /** + * @inheritDoc + */ + public function getFeed() { $oEmbedIframePattern = '#src\=\"https\:\/\/(?:www\.|geo\.)?dailymotion\.com\/(?:embed\/video\/|player\.html\?video\=)(?[a-zA-Z0-9\_\-]+)#'; $feed = parent::getFeed(); @@ -80,23 +96,29 @@ public function getFeed(): array|\SimpleXMLElement|null return $feed; } - public function getMediaFeed(?string $search = null): string + /** + * {@inheritdoc} + */ + public function getMediaFeed($search = null) { 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) { @@ -104,16 +126,16 @@ public function getThumbnailName(string $pathinfo): string } else { $embed = $this->embedUrl; } - if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $matches)) { - $pathinfo = '.'.$matches['extension']; + if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $matches) === 1) { + $pathinfo = '.' . $matches['extension']; } else { $pathinfo = '.jpg'; } - if (1 === preg_match(static::$realIdPattern, $embed, $matches)) { - return 'dailymotion_'.$matches['id'].$pathinfo; + if (preg_match(static::$realIdPattern, $embed, $matches) === 1) { + return 'dailymotion_' . $matches['id'] . $pathinfo; } - if (1 === preg_match(static::$idPattern, $embed, $matches)) { - return 'dailymotion_'.$matches['id'].$pathinfo; + if (preg_match(static::$idPattern, $embed, $matches) === 1) { + return 'dailymotion_' . $matches['id'] . $pathinfo; } throw new InvalidEmbedId($embed, static::$platform); } @@ -126,6 +148,10 @@ public function getThumbnailName(string $pathinfo): string * * loop * * autoplay * * controls + * + * @param array $options + * + * @return string */ public function getSource(array &$options = []): string { @@ -139,6 +165,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 dbcfa99..27f23bf 100644 --- a/src/MediaFinders/AbstractDeezerEmbedFinder.php +++ b/src/MediaFinders/AbstractDeezerEmbedFinder.php @@ -9,6 +9,7 @@ abstract class AbstractDeezerEmbedFinder extends AbstractEmbedFinder { /** + * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'deezer'; @@ -32,34 +33,40 @@ public function isEmptyThumbnailAllowed(): bool return true; } - protected function validateEmbedId(string $embedId = ''): string + protected function validateEmbedId(string $embedId = ""): string { - if (1 === preg_match(static::$idPattern, $embedId, $matches)) { + if (preg_match(static::$idPattern, $embedId, $matches) === 1) { return $embedId; } - if (1 === preg_match(static::$realIdPattern, $embedId, $matches)) { + if (preg_match(static::$realIdPattern, $embedId, $matches) === 1) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - public function getMediaFeed(?string $search = null): string + /** + * @inheritDoc + */ + public function getMediaFeed($search = null) { 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)); } - public function getFeed(): array|\SimpleXMLElement|null + /** + * @inheritDoc + */ + public function getFeed() { $feed = parent::getFeed(); /* @@ -67,7 +74,7 @@ public function getFeed(): array|\SimpleXMLElement|null */ $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; @@ -85,7 +92,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 @@ -93,32 +100,39 @@ public function getThumbnailURL(): string return $this->getFeed()['thumbnail_url'] ?? ''; } + /** + * @inheritDoc + */ public function getThumbnailName(string $pathinfo): string { - if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext)) { - $pathinfo = '.'.$ext['extension']; + if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { + $pathinfo = '.' . $ext['extension']; } else { $pathinfo = '.jpg'; } - if (1 === preg_match(static::$idPattern, $this->embedId, $matches)) { - return $matches['type'].'_'.$matches['id'].$pathinfo; + if (preg_match(static::$idPattern, $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; + if (preg_match(static::$realIdPattern, $this->embedId, $matches) === 1) { + 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)) { @@ -141,15 +155,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 abe9c01..84ca230 100644 --- a/src/MediaFinders/AbstractEmbedFinder.php +++ b/src/MediaFinders/AbstractEmbedFinder.php @@ -5,7 +5,10 @@ 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; @@ -15,23 +18,28 @@ 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\Contracts\HttpClient\Exception\ClientExceptionInterface; -use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Component\HttpFoundation\Response; /** * Abstract class to handle external media via their Json API. */ abstract class AbstractEmbedFinder implements EmbedFinderInterface { - protected array|\SimpleXMLElement|null $feed = null; + /** + * @var array|SimpleXMLElement|null + */ + protected $feed = null; protected string $embedId; protected ?string $key = null; /** - * @param bool $validate validate the embed id passed at the constructor [default: true] + * @param string $embedId + * @param bool $validate Validate the embed id passed at the constructor [default: true]. + * @throws InvalidEmbedId When embedId string is malformed */ - public function __construct(protected readonly HttpClientInterface $client, string $embedId = '', bool $validate = true) + public function __construct(string $embedId = '', bool $validate = true) { if ($validate) { $this->embedId = $this->validateEmbedId($embedId); @@ -50,26 +58,35 @@ 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 (1 === preg_match('#(?[^\/^=^?]+)$#', $embedId, $matches)) { + if (preg_match('#(?[^\/^=^?]+)$#', $embedId, $matches) === 1) { return $matches['id']; } throw new InvalidEmbedId($embedId); @@ -77,6 +94,8 @@ protected function validateEmbedId(string $embedId = ''): string /** * Tell if embed media exists after its API feed. + * + * @return bool */ public function exists(): bool { @@ -85,12 +104,17 @@ public function exists(): bool /** * Crawl and parse an API json feed for current embedID. + * + * @return array|SimpleXMLElement|null */ - public function getFeed(): array|\SimpleXMLElement|null + public function getFeed() { if (null === $this->feed) { $rawFeed = $this->getMediaFeed(); - if (\json_validate($rawFeed)) { + if ($rawFeed instanceof StreamInterface) { + $rawFeed = $rawFeed->getContents(); + } + if (null !== $rawFeed) { $feed = json_decode($rawFeed, true); if (is_array($feed)) { $this->feed = $feed; @@ -99,30 +123,43 @@ public function getFeed(): array|\SimpleXMLElement|null } } } - 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(?string $search = null): string; + abstract public function getMediaFeed($search = null); /** * 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): ?string + public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15) { return null; } @@ -136,7 +173,9 @@ public function getSearchFeed(string $searchTerm, ?string $author = null, int $m * * id * * class * + * @param array $options * @final + * @return string */ public function getIFrame(array &$options = []): string { @@ -149,7 +188,7 @@ public function getIFrame(array &$options = []): string 'accelerometer', 'encrypted-media', 'gyroscope', - 'picture-in-picture', + 'picture-in-picture' ]; if ($options['width'] > 0) { @@ -158,8 +197,8 @@ public function getIFrame(array &$options = []): string /* * Default height is defined to 16:10 */ - if (0 === $options['height']) { - $attributes['height'] = (int) (($options['width'] * 10) / 16); + if ($options['height'] === 0) { + $attributes['height'] = (int)(($options['width'] * 10) / 16); } } @@ -190,14 +229,14 @@ public function getIFrame(array &$options = []): string $htmlAttrs = []; foreach ($attributes as $key => $value) { - if ('' == $value || true === $value) { + if ($value == '' || $value === true) { $htmlAttrs[] = $key; } else { - $htmlAttrs[] = $key.'="'.addslashes((string) $value).'"'; + $htmlAttrs[] = $key . '="' . addslashes((string) $value) . '"'; } } - return ''; + return ''; } /** @@ -205,14 +244,15 @@ 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, - ): DocumentInterface|array { + AbstractDocumentFactory $documentFactory + ) { if ($this->documentExists($objectManager, $this->getEmbedId(), $this->getPlatform())) { throw new EmbedDocumentAlreadyExistsException(); } @@ -238,10 +278,10 @@ public function createDocumentFromFeed( } } catch (APINeedsAuthentificationException $exception) { $document = $documentFactory->getDocument(true, $this->areDuplicatesAllowed()); - $document?->setFilename($this->getPlatform().'_'.$this->embedId.'.jpg'); - } catch (ClientExceptionInterface $exception) { + $document?->setFilename($this->getPlatform() . '_' . $this->embedId . '.jpg'); + } catch (RequestException $exception) { $document = $documentFactory->getDocument(true, $this->areDuplicatesAllowed()); - $document?->setFilename($this->getPlatform().'_'.$this->embedId.'.jpg'); + $document?->setFilename($this->getPlatform() . '_' . $this->embedId . '.jpg'); } if (null === $document) { @@ -263,47 +303,74 @@ 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; @@ -311,22 +378,39 @@ 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): string + public function downloadFeedFromAPI(string $url): StreamInterface { - $response = $this->client->request('GET', $url); + $client = new Client(); + $response = $client->get($url); + + if (Response::HTTP_OK == $response->getStatusCode()) { + return $response->getBody(); + } - return $response->getContent(); + throw new \RuntimeException($response->getReasonPhrase()); } + /** + * @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 { @@ -334,7 +418,6 @@ public function downloadThumbnail(): ?File if (null !== $url && '' !== $url) { $thumbnailName = $this->getThumbnailName(basename($url)); - return DownloadedFile::fromUrl($url, $thumbnailName); } @@ -347,6 +430,8 @@ 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 { @@ -362,12 +447,11 @@ public function getKey(): ?string * * @param string|null $key the key * - * @return $this + * @return self */ - public function setKey(?string $key): self + public function setKey(?string $key) { $this->key = $key; - return $this; } diff --git a/src/MediaFinders/AbstractMixcloudEmbedFinder.php b/src/MediaFinders/AbstractMixcloudEmbedFinder.php index 34c81eb..3d3f1ca 100644 --- a/src/MediaFinders/AbstractMixcloudEmbedFinder.php +++ b/src/MediaFinders/AbstractMixcloudEmbedFinder.php @@ -9,6 +9,7 @@ abstract class AbstractMixcloudEmbedFinder extends AbstractEmbedFinder { /** + * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'mixcloud'; @@ -24,54 +25,75 @@ public static function getPlatform(): string return static::$platform; } - protected function validateEmbedId(string $embedId = ''): string + /** + * @inheritDoc + */ + protected function validateEmbedId(string $embedId = ""): string { - if (1 === preg_match(static::$idPattern, $embedId, $matches)) { + if (preg_match(static::$idPattern, $embedId, $matches) === 1) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - public function getMediaFeed(?string $search = null): string + /** + * @inheritDoc + */ + public function getMediaFeed($search = null) { - $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 (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext)) { - $pathinfo = '.'.$ext['extension']; + if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { + $pathinfo = '.' . $ext['extension']; } else { $pathinfo = '.jpg'; } - if (1 === preg_match(static::$idPattern, $this->embedId, $matches)) { - return $matches['author'].'_'.$matches['id'].$pathinfo; + if (preg_match(static::$idPattern, $this->embedId, $matches) === 1) { + return $matches['author'] . '_' . $matches['id'] . $pathinfo; } throw new InvalidEmbedId($this->embedId, static::$platform); } @@ -85,6 +107,10 @@ public function getThumbnailName(string $pathinfo): string * * end * * mini * * hide_cover + * + * @param array $options + * + * @return string */ public function getSource(array &$options = []): string { @@ -104,20 +130,20 @@ public function getSource(array &$options = []): string if ($options['end']) { $queryString['end'] = (int) $options['end']; } - if (true === $options['mini']) { + if ($options['mini'] === true) { $queryString['mini'] = 1; } - if (true === $options['hide_cover']) { + if ($options['hide_cover'] === true) { $queryString['hide_cover'] = 1; } - if (true === $options['hide_artwork']) { + if ($options['hide_artwork'] === true) { $queryString['hide_artwork'] = 1; } - if (true === $options['light']) { + if ($options['light'] === true) { $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 0789633..fa238d0 100644 --- a/src/MediaFinders/AbstractPodcastFinder.php +++ b/src/MediaFinders/AbstractPodcastFinder.php @@ -5,11 +5,15 @@ 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 { @@ -23,28 +27,42 @@ public static function getPlatform(): string return 'podcast'; } - protected function validateEmbedId(string $embedId = ''): string + /** + * @inheritDoc + */ + protected function validateEmbedId(string $embedId = ""): string { return $embedId; } - public function getFeed(): array|\SimpleXMLElement|null + /** + * @return array|SimpleXMLElement|null + */ + public function getFeed() { if (null === $this->feed) { $rawFeed = $this->getMediaFeed(); - try { - $this->feed = new \SimpleXMLElement($rawFeed); - - return $this->feed; - } catch (\Exception $errorException) { - throw new \RuntimeException('Feed content is not a valid Podcast XML'); + 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'); + } } } - return $this->feed; } - protected function getAudioName(\SimpleXMLElement $item): string + /** + * @param SimpleXMLElement $item + * + * @return string + */ + protected function getAudioName(SimpleXMLElement $item): string { if (null !== $item->enclosure->attributes()) { $url = (string) $item->enclosure->attributes()->url; @@ -54,10 +72,8 @@ 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); } @@ -66,17 +82,18 @@ 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, - ): array { + AbstractDocumentFactory $documentFactory + ) { $documents = []; $feed = $this->getFeed(); - if ($feed instanceof \SimpleXMLElement) { + if ($feed instanceof SimpleXMLElement) { foreach ($feed->channel->item as $item) { if ( !empty($item->enclosure->attributes()->url) @@ -128,12 +145,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 @@ -152,59 +169,74 @@ protected function getPodcastItemCopyright(\SimpleXMLElement $item): ?string if (empty($copyright)) { return $this->getMediaCopyright(); } - - return $copyright.' – '.$this->getMediaCopyright(); + return $copyright . ' – ' . $this->getMediaCopyright(); } - public function getMediaFeed(?string $search = null): string + /** + * @inheritDoc + */ + public function getMediaFeed($search = null) { $url = $this->embedId; - $response = $this->client->request('GET', $url); + $client = new Client(); + $response = $client->get($url); + + if (Response::HTTP_OK == $response->getStatusCode()) { + return $response->getBody(); + } - return $response->getContent(); + throw new \RuntimeException($response->getReasonPhrase()); } + /** + * @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 4383550..3d15b3d 100644 --- a/src/MediaFinders/AbstractSoundcloudEmbedFinder.php +++ b/src/MediaFinders/AbstractSoundcloudEmbedFinder.php @@ -12,6 +12,7 @@ abstract class AbstractSoundcloudEmbedFinder extends AbstractEmbedFinder { /** + * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'soundcloud'; @@ -21,9 +22,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 @@ -31,29 +32,38 @@ public static function getPlatform(): string return static::$platform; } - protected function validateEmbedId(string $embedId = ''): string + /** + * @inheritDoc + */ + protected function validateEmbedId(string $embedId = ""): string { - if (1 === preg_match(static::$idPattern, $embedId, $matches)) { + if (preg_match(static::$idPattern, $embedId, $matches) === 1) { return $embedId; } - if (1 === preg_match(static::$realIdPattern, $embedId, $matches)) { + if (preg_match(static::$realIdPattern, $embedId, $matches) === 1) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - public function getMediaFeed(?string $search = null): string + /** + * @inheritDoc + */ + public function getMediaFeed($search = null) { - $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)); } - public function getFeed(): array|\SimpleXMLElement|null + /** + * @inheritDoc + */ + public function getFeed() { $feed = parent::getFeed(); /* @@ -79,7 +89,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 @@ -87,6 +97,9 @@ public function getThumbnailURL(): string return $this->getFeed()['thumbnail_url'] ?? ''; } + /** + * @inheritDoc + */ public function getThumbnailName(string $pathinfo): string { if (null === $this->embedUrl) { @@ -94,13 +107,13 @@ public function getThumbnailName(string $pathinfo): string } else { $embed = $this->embedUrl; } - if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext)) { - $pathinfo = '.'.$ext['extension']; + if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { + $pathinfo = '.' . $ext['extension']; } else { $pathinfo = '.jpg'; } - if (1 === preg_match(static::$idPattern, $embed, $matches)) { - return 'soundcloud_'.$matches['user'].$pathinfo; + if (preg_match(static::$idPattern, $embed, $matches) === 1) { + return 'soundcloud_' . $matches['user'] . $pathinfo; } throw new InvalidEmbedId($embed, static::$platform); } @@ -115,6 +128,10 @@ public function getThumbnailName(string $pathinfo): string * * show_user * * show_reposts * * visual + * + * @param array $options + * + * @return string */ public function getSource(array &$options = []): string { @@ -135,7 +152,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 6620a31..1fed0b8 100644 --- a/src/MediaFinders/AbstractSpotifyEmbedFinder.php +++ b/src/MediaFinders/AbstractSpotifyEmbedFinder.php @@ -9,6 +9,7 @@ abstract class AbstractSpotifyEmbedFinder extends AbstractEmbedFinder { /** + * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'spotify'; @@ -28,34 +29,43 @@ public static function getPlatform(): string return static::$platform; } - protected function validateEmbedId(string $embedId = ''): string + /** + * @inheritDoc + */ + protected function validateEmbedId(string $embedId = ""): string { - if (1 === preg_match(static::$idPattern, $embedId, $matches)) { + if (preg_match(static::$idPattern, $embedId, $matches) === 1) { return $embedId; } - if (1 === preg_match(static::$realIdPattern, $embedId, $matches)) { + if (preg_match(static::$realIdPattern, $embedId, $matches) === 1) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - public function getMediaFeed(?string $search = null): string + /** + * @inheritDoc + */ + public function getMediaFeed($search = null) { 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)); } - public function getFeed(): array|\SimpleXMLElement|null + /** + * @inheritDoc + */ + public function getFeed() { $feed = parent::getFeed(); /* @@ -63,66 +73,82 @@ public function getFeed(): array|\SimpleXMLElement|null */ $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 (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext)) { - $pathinfo = '.'.$ext['extension']; + if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { + $pathinfo = '.' . $ext['extension']; } else { $pathinfo = '.jpg'; } - if (1 === preg_match(static::$idPattern, $this->embedId, $matches)) { - return $matches['type'].'_'.$matches['id'].$pathinfo; + if (preg_match(static::$idPattern, $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; + if (preg_match(static::$realIdPattern, $this->embedId, $matches) === 1) { + 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 f4077de..1c71110 100644 --- a/src/MediaFinders/AbstractTedEmbedFinder.php +++ b/src/MediaFinders/AbstractTedEmbedFinder.php @@ -9,6 +9,7 @@ abstract class AbstractTedEmbedFinder extends AbstractEmbedFinder { /** + * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'ted'; @@ -24,7 +25,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; @@ -32,14 +33,14 @@ protected function validateEmbedId(string $embedId = ''): string throw new InvalidEmbedId($embedId, static::$platform); } - public function getMediaFeed(?string $search = null): string + public function getMediaFeed($search = null) { - $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 @@ -54,7 +55,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 @@ -64,26 +65,30 @@ public function getThumbnailURL(): string public function getThumbnailName(string $pathinfo): string { - if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext)) { - $pathinfo = '.'.$ext['extension']; + if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $ext) === 1) { + $pathinfo = '.' . $ext['extension']; } else { $pathinfo = '.jpg'; } - if (1 === preg_match(static::$idPattern, $this->embedId, $matches)) { - return 'ted_talk_'.$matches['id'].$pathinfo; + if (preg_match(static::$idPattern, $this->embedId, $matches) === 1) { + 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 new file mode 100644 index 0000000..2088076 --- /dev/null +++ b/src/MediaFinders/AbstractTwitchEmbedFinder.php @@ -0,0 +1,113 @@ +[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 794791d..80d2774 100644 --- a/src/MediaFinders/AbstractUnsplashPictureFinder.php +++ b/src/MediaFinders/AbstractUnsplashPictureFinder.php @@ -4,12 +4,15 @@ namespace RZ\Roadiz\Documents\MediaFinders; -use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; -use Symfony\Contracts\HttpClient\HttpClientInterface; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Exception\GuzzleException; abstract class AbstractUnsplashPictureFinder extends AbstractEmbedFinder implements RandomImageFinder { + protected Client $client; /** + * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'unsplash'; @@ -29,44 +32,54 @@ public static function getPlatform(): string return static::$platform; } - public function __construct(HttpClientInterface $client, string $clientId, string $embedId = '') + /** + * @param string $clientId + * @param string $embedId + */ + public function __construct(string $clientId, string $embedId = '') { - parent::__construct($client->withOptions([ + parent::__construct($embedId); + + $this->client = new Client( + [ // 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, - ], - ]), $embedId); + 'Authorization' => 'Client-ID ' . $clientId + ] + ] + ); } - protected function validateEmbedId(string $embedId = ''): string + protected function validateEmbedId(string $embedId = ""): string { return $embedId; } /** - * @see https://unsplash.com/documentation#get-a-random-photo + * @see https://unsplash.com/documentation#get-a-random-photo + * @param array $options + * @return array|null + * @throws GuzzleException */ public function getRandom(array $options = []): ?array { try { - $response = $this->client->request( - 'GET', + $response = $this->client->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->getContent(), true) ?? null; + $feed = json_decode($response->getBody()->getContents(), true) ?? null; if (!is_array($feed)) { return null; } @@ -76,59 +89,85 @@ public function getRandom(array $options = []): ?array if (null !== $url) { $this->embedId = (string) $feed['id']; $this->feed = $feed; - return $this->feed; } - return null; - } catch (ClientExceptionInterface) { + } catch (ClientException $e) { return null; } } - public function getRandomBySearch(string $keyword, array $options = []): ?array + /** + * @param string $keyword + * @param array $options + * @return array|null + * @throws GuzzleException + */ + public function getRandomBySearch(string $keyword, array $options = []) { return $this->getRandom( [ - 'query' => $keyword, + 'query' => $keyword ] ); } - public function getMediaFeed(?string $search = null): string + + /** + * {@inheritdoc} + */ + public function getMediaFeed($search = null) { - throw new \LogicException('Unsplash API does not provide a feed.'); + return ''; } + /** + * {@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) { @@ -141,16 +180,19 @@ 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 effafc9..5d0db9f 100644 --- a/src/MediaFinders/AbstractVimeoEmbedFinder.php +++ b/src/MediaFinders/AbstractVimeoEmbedFinder.php @@ -13,6 +13,7 @@ abstract class AbstractVimeoEmbedFinder extends AbstractEmbedFinder { protected static string $realIdPattern = '#(?[0-9]+)$#'; /** + * @var string * @internal Use getPlatform() instead */ protected static string $platform = 'vimeo'; @@ -24,13 +25,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 (1 === preg_match('#(?[0-9]+)$#', $embedId, $matches)) { + if (preg_match('#(?[0-9]+)$#', $embedId, $matches) === 1) { return $matches['id']; } throw new InvalidEmbedId($embedId, static::$platform); @@ -46,11 +47,12 @@ 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']); } @@ -89,15 +91,18 @@ public function getMediaDuration(): ?int return $this->getFeed()['duration'] ?? null; } - public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15): ?string + public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15) { return null; } - public function getMediaFeed(?string $search = null): string + /** + * {@inheritdoc} + */ + public function getMediaFeed($search = null) { 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; } @@ -107,7 +112,7 @@ public function getMediaFeed(?string $search = null): string 'format' => 'json', ]; - return $this->downloadFeedFromAPI($endpoint.'?'.http_build_query($query)); + return $this->downloadFeedFromAPI($endpoint . '?' . http_build_query($query)); } /** @@ -123,6 +128,10 @@ public function getMediaFeed(?string $search = null): string * * muted * * autopause * * automute + * + * @param array $options + * + * @return string */ public function getSource(array &$options = []): string { @@ -160,6 +169,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 df108d9..e67f5ba 100644 --- a/src/MediaFinders/AbstractYoutubeEmbedFinder.php +++ b/src/MediaFinders/AbstractYoutubeEmbedFinder.php @@ -14,6 +14,7 @@ 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'; @@ -28,39 +29,48 @@ 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/'); } - protected function validateEmbedId(string $embedId = ''): string + /** + * @inheritDoc + */ + protected function validateEmbedId(string $embedId = ""): string { - if (1 === preg_match(static::$idPattern, $embedId, $matches)) { + if (preg_match(static::$idPattern, $embedId, $matches) === 1) { return $embedId; } - if (1 === preg_match(static::$realIdPattern, $embedId, $matches)) { + if (preg_match(static::$realIdPattern, $embedId, $matches) === 1) { return $embedId; } throw new InvalidEmbedId($embedId, static::$platform); } - public function getMediaFeed(?string $search = null): string + /** + * @inheritDoc + */ + public function getMediaFeed($search = null) { 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)); } - public function getFeed(): array|\SimpleXMLElement|null + /** + * @inheritDoc + */ + public function getFeed() { $feed = parent::getFeed(); /* @@ -86,13 +96,12 @@ 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 @@ -100,11 +109,17 @@ 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; @@ -117,34 +132,34 @@ public function getThumbnailName(string $pathinfo): string } else { $embed = $this->embedUrl; } - if (1 === preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $matches)) { - $pathinfo = '.'.$matches['extension']; + if (preg_match('#\.(?[jpe?g|png|gif])$#', $pathinfo, $matches) === 1) { + $pathinfo = '.' . $matches['extension']; } else { $pathinfo = '.jpg'; } - if (1 === preg_match(static::$realIdPattern, $embed, $matches)) { - return 'youtube_'.$matches['id'].$pathinfo; + if (preg_match(static::$realIdPattern, $embed, $matches) === 1) { + return 'youtube_' . $matches['id'] . $pathinfo; } - if (1 === preg_match(static::$idPattern, $embed, $matches)) { - return 'youtube_'.$matches['id'].$pathinfo; + if (preg_match(static::$idPattern, $embed, $matches) === 1) { + return 'youtube_' . $matches['id'] . $pathinfo; } throw new InvalidEmbedId($embed, static::$platform); } /** - * @throws APINeedsAuthentificationException + * @inheritdoc + * @throws APINeedsAuthentificationException */ - public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15): ?string + public function getSearchFeed(string $searchTerm, ?string $author = null, int $maxResults = 15) { - 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; + 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; } - 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); } } @@ -159,6 +174,10 @@ public function getSearchFeed(string $searchTerm, ?string $author = null, int $m * * start * * enablejsapi * * muted + * + * @param array $options + * + * @return string */ public function getSource(array &$options = []): string { @@ -199,6 +218,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 5d3f8a5..3694d18 100644 --- a/src/MediaFinders/EmbedFinderFactory.php +++ b/src/MediaFinders/EmbedFinderFactory.php @@ -4,8 +4,6 @@ namespace RZ\Roadiz\Documents\MediaFinders; -use Symfony\Contracts\HttpClient\HttpClientInterface; - class EmbedFinderFactory { /** @@ -23,22 +21,23 @@ class EmbedFinderFactory /** * @param array> $embedPlatforms */ - public function __construct(protected readonly HttpClientInterface $client, array $embedPlatforms = []) + public function __construct(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($this->client, $embedId); + return new $class($embedId); } - return null; } @@ -57,14 +56,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); } @@ -73,11 +72,16 @@ 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 3139cd7..c0388f4 100644 --- a/src/MediaFinders/EmbedFinderInterface.php +++ b/src/MediaFinders/EmbedFinderInterface.php @@ -4,14 +4,20 @@ 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; @@ -22,9 +28,4 @@ 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 c263cb9..3a76670 100644 --- a/src/MediaFinders/FacebookPictureFinder.php +++ b/src/MediaFinders/FacebookPictureFinder.php @@ -4,38 +4,39 @@ namespace RZ\Roadiz\Documents\MediaFinders; -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; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; +use Psr\Http\Message\ResponseInterface; /** * Util to grab a facebook profile picture from userAlias. */ -final readonly class FacebookPictureFinder +class FacebookPictureFinder { - public function __construct(private HttpClientInterface $client) + protected string $facebookUserAlias; + protected ResponseInterface $response; + + /** + * @param string $facebookUserAlias + */ + public function __construct(string $facebookUserAlias) { + $this->facebookUserAlias = $facebookUserAlias; } /** * @return string|null Facebook profile image URL - * - * @throws TransportExceptionInterface - * @throws ClientExceptionInterface - * @throws RedirectionExceptionInterface - * @throws ServerExceptionInterface + * @throws GuzzleException */ - public function getPictureUrl(string $facebookUserAlias): ?string + public function getPictureUrl(): ?string { - $response = $this->client->request('GET', 'https://graph.facebook.com/'.$facebookUserAlias.'/picture?redirect=false&width=200&height=200'); - $json = json_decode($response->getContent(), true); + $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); 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 6c52246..d510b44 100644 --- a/src/MediaFinders/RandomImageFinder.php +++ b/src/MediaFinders/RandomImageFinder.php @@ -7,12 +7,15 @@ interface RandomImageFinder { /** - * @return array|null a data feed for a random image + * @param array $options + * @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 = []): mixed; + public function getRandomBySearch(string $keyword, array $options = []); } diff --git a/src/Models/AdvancedDocumentInterface.php b/src/Models/AdvancedDocumentInterface.php index 1e3bf32..f86ca2b 100644 --- a/src/Models/AdvancedDocumentInterface.php +++ b/src/Models/AdvancedDocumentInterface.php @@ -6,9 +6,13 @@ 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 8b7f99a..aeb4a9e 100644 --- a/src/Models/DisplayableInterface.php +++ b/src/Models/DisplayableInterface.php @@ -6,9 +6,13 @@ 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 4ab2789..1f990bf 100644 --- a/src/Models/DocumentInterface.php +++ b/src/Models/DocumentInterface.php @@ -11,60 +11,82 @@ 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; @@ -87,6 +109,7 @@ public function getMountFolderPath(): ?string; public function getEmbedId(): ?string; /** + * @param string|null $embedId * @return $this */ public function setEmbedId(?string $embedId): static; @@ -94,18 +117,25 @@ 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; @@ -114,25 +144,27 @@ 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; @@ -142,22 +174,27 @@ 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 false if no local file is linked to document. i.e no filename, no folder + * + * @return bool */ 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 4677043..9ed46e3 100644 --- a/src/Models/DocumentTrait.php +++ b/src/Models/DocumentTrait.php @@ -26,7 +26,6 @@ trait DocumentTrait * - 3d * * @var array - * * @internal */ #[SymfonySerializer\Ignore()] @@ -137,8 +136,7 @@ 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()] @@ -155,6 +153,8 @@ trait DocumentTrait /** * Get short type name for current document Mime type. + * + * @return string */ #[SymfonySerializer\Ignore()] public function getShortType(): string @@ -168,86 +168,99 @@ 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 'image' === static::getShortType(); + return static::getShortType() === 'image'; } /** * Is current document a vector SVG file. + * + * @return bool */ #[SymfonySerializer\Ignore()] public function isSvg(): bool { - return 'image/svg+xml' === $this->getMimeType() || 'image/svg' === $this->getMimeType(); + return $this->getMimeType() === 'image/svg+xml' || $this->getMimeType() === 'image/svg'; } /** * Is current document a video. + * + * @return bool */ #[SymfonySerializer\Ignore()] public function isVideo(): bool { - return 'video' === static::getShortType(); + return static::getShortType() === 'video'; } /** * Is current document an audio file. + * + * @return bool */ #[SymfonySerializer\Ignore()] public function isAudio(): bool { - return 'audio' === static::getShortType(); + return static::getShortType() === 'audio'; } /** * Is current document a PDF file. + * + * @return bool */ #[SymfonySerializer\Ignore()] public function isPdf(): bool { - return 'pdf' === static::getShortType(); + return static::getShortType() === 'pdf'; } + /** + * @return bool + */ #[SymfonySerializer\Ignore()] public function isWebp(): bool { - return 'image/webp' === $this->getMimeType(); + return $this->getMimeType() === 'image/webp'; } #[ - 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 { @@ -255,9 +268,9 @@ public function getMountPath(): ?string return null; } if ($this->isPrivate()) { - return 'private://'.$relativePath; + return 'private://' . $relativePath; } else { - return 'public://'.$relativePath; + return 'public://' . $relativePath; } } @@ -271,29 +284,31 @@ 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, @@ -305,8 +320,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 { @@ -314,11 +329,13 @@ public function getAlternativeText(): string } /** - * Return false if no local file is linked to document. i.e no filename, no folder. + * Return false if no local file is linked to document. i.e no filename, no folder + * + * @return bool */ #[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 0570588..d63bb07 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 8b1d8ed..49e76e3 100644 --- a/src/Models/FileHashInterface.php +++ b/src/Models/FileHashInterface.php @@ -7,10 +7,7 @@ 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 f1e483e..f5555fd 100644 --- a/src/Models/FolderInterface.php +++ b/src/Models/FolderInterface.php @@ -14,36 +14,49 @@ 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 a4f6b82..6684430 100644 --- a/src/Models/HasThumbnailInterface.php +++ b/src/Models/HasThumbnailInterface.php @@ -8,9 +8,13 @@ interface HasThumbnailInterface { + /** + * @return HasThumbnailInterface|null + */ public function getOriginal(): ?HasThumbnailInterface; /** + * @param HasThumbnailInterface|null $original * @return $this */ public function setOriginal(?HasThumbnailInterface $original): static; @@ -22,14 +26,22 @@ 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 142c5c1..58315a9 100644 --- a/src/Models/SimpleDocument.php +++ b/src/Models/SimpleDocument.php @@ -31,6 +31,9 @@ public function __construct() $this->folders = new ArrayCollection(); } + /** + * @return string + */ public function getFilename(): string { return $this->filename; @@ -39,7 +42,6 @@ public function getFilename(): string public function setFilename(string $filename): static { $this->filename = $filename; - return $this; } @@ -51,10 +53,12 @@ public function getMimeType(): ?string public function setMimeType(?string $mimeType): static { $this->mimeType = $mimeType; - return $this; } + /** + * @return string + */ public function getFolder(): string { return $this->folder; @@ -67,6 +71,9 @@ public function setFolder(string $folder): static return $this; } + /** + * @return string|null + */ public function getEmbedId(): ?string { return $this->embedId; @@ -79,6 +86,9 @@ public function setEmbedId(?string $embedId): static return $this; } + /** + * @return string|null + */ public function getEmbedPlatform(): ?string { return $this->embedPlatform; @@ -91,6 +101,9 @@ public function setEmbedPlatform(?string $embedPlatform): static return $this; } + /** + * @return bool + */ public function isPrivate(): bool { return $this->private; @@ -103,6 +116,9 @@ public function setPrivate(bool $private): static return $this; } + /** + * @return DocumentInterface|null + */ public function getRawDocument(): ?DocumentInterface { return $this->rawDocument; @@ -115,6 +131,9 @@ public function setRawDocument(?DocumentInterface $rawDocument = null): static return $this; } + /** + * @return bool + */ public function isRaw(): bool { return $this->raw; @@ -139,6 +158,9 @@ public function setDownscaledDocument(?DocumentInterface $downscaledDocument): s return $this; } + /** + * @return Collection + */ public function getFolders(): Collection { return $this->folders; @@ -147,21 +169,18 @@ 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 bbff652..0f6f0ce 100644 --- a/src/Models/SimpleFileAware.php +++ b/src/Models/SimpleFileAware.php @@ -11,6 +11,9 @@ class SimpleFileAware implements FileAwareInterface { private string $basePath; + /** + * @param string $basePath + */ public function __construct(string $basePath) { $this->basePath = $basePath; @@ -18,7 +21,7 @@ public function __construct(string $basePath) public function getPublicFilesPath(): string { - return $this->basePath.$this->getPublicFilesBasePath(); + return $this->basePath . $this->getPublicFilesBasePath(); } public function getPublicFilesBasePath(): string @@ -28,7 +31,7 @@ public function getPublicFilesBasePath(): string public function getPrivateFilesPath(): string { - return $this->basePath.$this->getPrivateFilesBasePath(); + return $this->basePath . $this->getPrivateFilesBasePath(); } public function getPrivateFilesBasePath(): string @@ -38,7 +41,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 bdbcca9..5bf55a7 100644 --- a/src/Models/SizeableInterface.php +++ b/src/Models/SizeableInterface.php @@ -6,19 +6,30 @@ 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 4951c77..25fc81f 100644 --- a/src/Models/TimeableInterface.php +++ b/src/Models/TimeableInterface.php @@ -6,9 +6,13 @@ 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 834428a..ac34da7 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,7 +73,6 @@ function (Options $options) { if (1 === preg_match('#(?[0-9]+)[x:\.](?[0-9]+)#', $compositing, $matches)) { return ((float) $matches['width']) / ((float) $matches['height']); } - return null; } ); @@ -87,10 +86,9 @@ 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'] && 0 !== $options['height'] && 0 !== $options['ratio']) { + } elseif (null !== $options['ratio'] && $options['height'] !== 0 && $options['ratio'] !== 0) { return (int) (intval($options['height']) * floatval($options['ratio'])); } - return 0; } ); @@ -101,10 +99,9 @@ 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'] && 0 !== $options['width'] && 0 !== $options['ratio']) { + } elseif (null !== $options['ratio'] && $options['width'] !== 0 && $options['ratio'] !== 0) { return (int) (intval($options['width']) / floatval($options['ratio'])); } - return 0; } ); diff --git a/src/Renderer/AbstractImageRenderer.php b/src/Renderer/AbstractImageRenderer.php index 2bd767d..4c7c60c 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,19 +28,23 @@ 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']) - && true === $options['embed'] - && null !== $document->getEmbedPlatform() - && $this->embedFinderFactory->supports($document->getEmbedPlatform()); + return isset($options['embed']) && + $options['embed'] === true && + 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) { @@ -52,38 +56,52 @@ 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); @@ -91,55 +109,65 @@ 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'] && 0 !== $options['ratio']) { + if (null !== $options['ratio'] && $options['ratio'] !== 0) { $assignation['ratio'] = $options['ratio']; } elseif (null !== $document->getImageRatio()) { $assignation['ratio'] = $document->getImageRatio(); } if ( null !== $document->getImageAverageColor() - && '#ffffff' !== $document->getImageAverageColor() - && '#000000' !== $document->getImageAverageColor() + && $document->getImageAverageColor() !== '#ffffff' + && $document->getImageAverageColor() !== '#000000' ) { $assignation['averageColor'] = $document->getImageAverageColor(); } - if (true === $options['blurredFallback']) { + if ($options['blurredFallback'] === true) { if (!empty($options['fit'])) { // Both Fit and Width cannot be explicitly set // need to revert on Crop @@ -155,7 +183,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 dabd17a..71abbce 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,6 +35,12 @@ 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())) { @@ -42,22 +48,27 @@ 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 { @@ -77,7 +88,7 @@ protected function getSourcesFilesArray(DocumentInterface $document, iterable $s } krsort($sources); - if (0 === count($sources)) { + if (count($sources) === 0) { // 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 694f512..f63dc0e 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,6 +31,10 @@ 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 @@ -49,6 +53,10 @@ 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 0de8b7a..50dac81 100644 --- a/src/Renderer/ChainRenderer.php +++ b/src/Renderer/ChainRenderer.php @@ -13,6 +13,9 @@ class ChainRenderer implements RendererInterface */ private array $renderers; + /** + * @param array $renderers + */ public function __construct(array $renderers) { /** @@ -27,12 +30,13 @@ 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 de82d0f..7ed8c2b 100644 --- a/src/Renderer/EmbedRenderer.php +++ b/src/Renderer/EmbedRenderer.php @@ -12,6 +12,9 @@ class EmbedRenderer implements RendererInterface { protected EmbedFinderFactory $embedFinderFactory; + /** + * @param EmbedFinderFactory $embedFinderFactory + */ public function __construct(EmbedFinderFactory $embedFinderFactory) { $this->embedFinderFactory = $embedFinderFactory; @@ -23,7 +26,7 @@ public function supports(DocumentInterface $document, array $options): bool $document->isEmbed() && $this->embedFinderFactory->supports($document->getEmbedPlatform()) && isset($options['embed']) - && true === $options['embed'] + && $options['embed'] === true ) { return true; } else { @@ -41,10 +44,9 @@ 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 b5d54a2..f862a0d 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']) || false === $options['picture']) - && parent::supports($document, $options); + return (!isset($options['picture']) || $options['picture'] === false) && + 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 b6497a0..13a39a0 100644 --- a/src/Renderer/InlineSvgRenderer.php +++ b/src/Renderer/InlineSvgRenderer.php @@ -23,10 +23,13 @@ public function __construct(FilesystemOperator $documentsStorage) public function supports(DocumentInterface $document, array $options): bool { - return $document->isLocal() && $document->isSvg() && (isset($options['inline']) && true === $options['inline']); + return $document->isLocal() && $document->isSvg() && (isset($options['inline']) && $options['inline'] === true); } /** + * @param DocumentInterface $document + * @param array $options + * @return string * @throws FilesystemException */ public function render(DocumentInterface $document, array $options): string @@ -40,10 +43,9 @@ 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 d61f685..4ab5de0 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) - && true === $options['embed']; + return $document->isPdf() && + key_exists('embed', $options) && + $options['embed'] === true; } /** diff --git a/src/Renderer/PictureRenderer.php b/src/Renderer/PictureRenderer.php index 78dd663..9ed7fa8 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']) - && true === $options['picture'] - && parent::supports($document, $options); + return isset($options['picture']) && + $options['picture'] === true && + 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,10 +80,9 @@ 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 f000463..76e4402 100644 --- a/src/Renderer/RendererInterface.php +++ b/src/Renderer/RendererInterface.php @@ -8,7 +8,19 @@ 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 a5f26be..06eaef9 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']) || false === $options['inline']); + return $document->isSvg() && (!isset($options['inline']) || $options['inline'] === false); } public function render(DocumentInterface $document, array $options): string @@ -41,12 +41,17 @@ 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 = []; @@ -54,19 +59,18 @@ protected function getAttributes(array $options): array SvgDocumentViewer::$allowedAttributes, [ 'loading', - 'alt', + 'alt' ] ); foreach ($options as $key => $value) { if (in_array($key, $allowedAttributes)) { - if ('identifier' === $key) { + if ($key === 'identifier') { $attributes['id'] = $value; } else { $attributes[$key] = $value; } } } - return $attributes; } } diff --git a/src/Renderer/ThumbnailRenderer.php b/src/Renderer/ThumbnailRenderer.php index 157701b..72bddca 100644 --- a/src/Renderer/ThumbnailRenderer.php +++ b/src/Renderer/ThumbnailRenderer.php @@ -14,33 +14,47 @@ 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) - || true !== $options['embed']) - && $document instanceof HasThumbnailInterface - && $document->hasThumbnails() - && false !== $document->getThumbnails()->first(); + return null !== $this->chainRenderer && + (!key_exists('embed', $options) || + $options['embed'] !== true) && + $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 c979e35..15a84e4 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,28 +54,33 @@ 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); } } @@ -91,7 +96,6 @@ protected function getPosterUrl( foreach ($sourcesDocs as $sourcesDoc) { $this->documentUrlGenerator->setOptions($options); $this->documentUrlGenerator->setDocument($sourcesDoc); - return $this->documentUrlGenerator->getUrl($absolute); } @@ -103,6 +107,10 @@ 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 1de60ae..d6bd01c 100644 --- a/src/Repository/DocumentRepositoryInterface.php +++ b/src/Repository/DocumentRepositoryInterface.php @@ -8,9 +8,7 @@ /** * @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 bf07b87..b460c56 100644 --- a/src/SvgSizeResolver.php +++ b/src/SvgSizeResolver.php @@ -4,6 +4,7 @@ namespace RZ\Roadiz\Documents; +use DOMNamedNodeMap; use League\Flysystem\FilesystemException; use League\Flysystem\FilesystemOperator; use RZ\Roadiz\Documents\Models\DocumentInterface; @@ -15,7 +16,7 @@ final class SvgSizeResolver public function __construct( private readonly DocumentInterface $document, - private readonly FilesystemOperator $documentsStorage, + private readonly FilesystemOperator $documentsStorage ) { } @@ -26,7 +27,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) { @@ -36,13 +37,17 @@ 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; @@ -50,12 +55,13 @@ 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 { @@ -67,7 +73,6 @@ public function getWidth(): int $viewBoxAttr = $this->getViewBoxAttributes(); if (null !== $viewBoxAttr) { [$x, $y, $width, $height] = $viewBoxAttr; - return (int) $width; } @@ -76,6 +81,8 @@ public function getWidth(): int /** * First, find height attr, then resolve height from viewBox. + * + * @return int */ public function getHeight(): int { @@ -86,7 +93,6 @@ public function getHeight(): int $viewBoxAttr = $this->getViewBoxAttributes(); if (null !== $viewBoxAttr) { [$x, $y, $width, $height] = $viewBoxAttr; - return (int) $height; } @@ -108,7 +114,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'); @@ -133,7 +139,6 @@ 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 381b2a1..390944e 100644 --- a/src/TwigExtension/DocumentExtension.php +++ b/src/TwigExtension/DocumentExtension.php @@ -23,16 +23,22 @@ 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 [ @@ -43,25 +49,28 @@ public function getFilters(): array new TwigFilter('path', [$this, 'getPath']), new TwigFilter('exists', [$this, 'exists']), new TwigFilter('embedFinder', [$this, 'getEmbedFinder']), - new TwigFilter('formatBytes', [$this, 'formatBytes']), + new TwigFilter('formatBytes', array($this, 'formatBytes')), ]; } /** - * @param string|int $bytes + * @param string|int $bytes + * @param int $precision + * @return string */ 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) { @@ -93,15 +102,19 @@ public function getEmbedFinder(?DocumentInterface $document = null): ?EmbedFinde } /** + * @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) { @@ -113,7 +126,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); @@ -127,9 +140,11 @@ 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) { @@ -139,16 +154,15 @@ public function getImageOrientation(?SizeableInterface $document = null): ?strin } } $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) { @@ -160,7 +174,6 @@ public function getImageSize(?SizeableInterface $document = null): array ]; } } - return [ 'width' => $document->getImageWidth(), 'height' => $document->getImageHeight(), @@ -168,9 +181,11 @@ 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) { @@ -187,13 +202,17 @@ public function getImageRatio(?SizeableInterface $document = null): float return 0.0; } - public function getPath(?DocumentInterface $document = null): ?string + /** + * @param DocumentInterface|null $document + * @return 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); } @@ -202,9 +221,11 @@ 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 21f11b4..2c05f7f 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,18 +50,22 @@ 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; } @@ -76,7 +80,7 @@ public function getUrl(bool $absolute = false): string $mountPath = $this->document->getMountPath(); - if (null !== $mountPath && (true === $this->options['noProcess'] || !$this->document->isProcessable())) { + if (null !== $mountPath && ($this->options['noProcess'] === true || !$this->document->isProcessable())) { $publicUrl = $this->documentsStorage->publicUrl($mountPath); if ($absolute && \str_starts_with($publicUrl, '/')) { return $this->urlHelper->getAbsoluteUrl($publicUrl); @@ -88,5 +92,9 @@ 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 612688e..1e1dd5f 100644 --- a/src/UrlGenerators/DocumentUrlGeneratorInterface.php +++ b/src/UrlGenerators/DocumentUrlGeneratorInterface.php @@ -8,14 +8,23 @@ 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 fb596db..e896b61 100644 --- a/src/UrlGenerators/DummyDocumentUrlGenerator.php +++ b/src/UrlGenerators/DummyDocumentUrlGenerator.php @@ -20,33 +20,30 @@ public function getUrl(bool $absolute = false): string throw new \BadMethodCallException('noProcess option is not set'); } - if (true === $this->options['noProcess'] || !$this->document->isProcessable()) { - $path = '/files/'.$this->document->getRelativePath(); + if ($this->options['noProcess'] === true || !$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 cd02b66..b186409 100644 --- a/src/UrlGenerators/OptionsCompiler.php +++ b/src/UrlGenerators/OptionsCompiler.php @@ -14,7 +14,8 @@ class OptionsCompiler /** * Compile Intervention Request options into a single query string. * - * @param array $options Resolved options + * @param array $options Resolved options + * @return string */ public function compile(array $options): string { @@ -26,40 +27,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'; @@ -84,7 +85,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 6545e8d..15746a0 100644 --- a/src/Viewers/SvgDocumentViewer.php +++ b/src/Viewers/SvgDocumentViewer.php @@ -29,15 +29,18 @@ class SvgDocumentViewer ]; /** - * @param bool $asObject Default false - * @param string $imageUrl only needed if you set $asObject to true + * @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. */ public function __construct( FilesystemOperator $documentsStorage, DocumentInterface $document, array $attributes = [], bool $asObject = false, - string $imageUrl = '', + string $imageUrl = "" ) { $this->imageUrl = $imageUrl; $this->attributes = $attributes; @@ -49,6 +52,7 @@ public function __construct( /** * Get SVG string to be used inside HTML content. * + * @return string * @throws FilesystemException */ public function getContent(): string @@ -60,23 +64,26 @@ public function getContent(): string } } + /** + * @return array + */ protected function getAllowedAttributes(): array { $attributes = []; foreach ($this->attributes as $key => $value) { if (in_array($key, static::$allowedAttributes)) { - if ('identifier' === $key) { + if ($key === 'identifier') { $attributes['id'] = $value; } else { $attributes[$key] = $value; } } } - return $attributes; } /** + * @return string * @throws FilesystemException */ protected function getInlineSvg(): string @@ -88,7 +95,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(); @@ -104,11 +111,12 @@ 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 @@ -147,7 +155,8 @@ protected function injectAttributes(string $svg): string } /** - * @deprecated use SvgRenderer to render HTML object + * @return string + * @deprecated Use SvgRenderer to render HTML object. */ protected function getObjectSvg(): string { @@ -167,9 +176,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 d2c87d1..050b382 100644 --- a/tests/MediaFinders/SimpleVimeoEmbedFinder.php +++ b/tests/MediaFinders/SimpleVimeoEmbedFinder.php @@ -10,11 +10,17 @@ 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 5c1fc0f..571e158 100644 --- a/tests/MediaFinders/SimpleYoutubeEmbedFinder.php +++ b/tests/MediaFinders/SimpleYoutubeEmbedFinder.php @@ -10,11 +10,17 @@ 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 b5de777..659fd71 100644 --- a/tests/Renderer/AbstractRendererTestCase.php +++ b/tests/Renderer/AbstractRendererTestCase.php @@ -16,7 +16,6 @@ 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; @@ -25,9 +24,8 @@ 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); } @@ -40,40 +38,46 @@ 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(HttpClient::create(), [ + return new EmbedFinderFactory([ 'youtube' => SimpleYoutubeEmbedFinder::class, 'vimeo' => SimpleVimeoEmbedFinder::class, ]); diff --git a/tests/Renderer/AudioRendererTest.php b/tests/Renderer/AudioRendererTest.php index 55b8f66..e97a701 100644 --- a/tests/Renderer/AudioRendererTest.php +++ b/tests/Renderer/AudioRendererTest.php @@ -61,49 +61,56 @@ 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 941b1d9..5fb5eac 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 b332af8..60b6e18 100644 --- a/tests/Renderer/EmbedRendererTest.php +++ b/tests/Renderer/EmbedRendererTest.php @@ -65,90 +65,91 @@ 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, @@ -157,16 +158,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 7d70def..a0be991 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 10da4c2..931a04c 100644 --- a/tests/Renderer/PdfRendererTest.php +++ b/tests/Renderer/PdfRendererTest.php @@ -17,7 +17,6 @@ protected function getRenderer(): PdfRenderer $this->getUrlGenerator() ); } - public function testSupports(): void { $mockValidDocument = new SimpleDocument(); @@ -36,7 +35,7 @@ public function testSupports(): void ); $this->assertTrue( $renderer->supports($mockValidDocument, [ - 'embed' => true, + 'embed' => true ]) ); @@ -65,7 +64,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 829e087..829ce0e 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 7d80fe0..e311a4e 100644 --- a/tests/Renderer/VideoRendererTest.php +++ b/tests/Renderer/VideoRendererTest.php @@ -134,11 +134,14 @@ public function testRender(): void EOT , $renderer->render($mockDocument, [ - 'controls' => false, + 'controls' => false ]) ); } + /** + * @return DocumentFinderInterface + */ private function getDocumentFinder(): DocumentFinderInterface { $finder = new ArrayDocumentFinder();