diff --git a/src/Backend/WkHtmlToPdf/ExtraOption.php b/src/Backend/WkHtmlToPdf/ExtraOption.php index 6e0f4931..569d8d83 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption.php @@ -5,6 +5,8 @@ namespace KNPLabs\Snappy\Backend\WkHtmlToPdf; interface ExtraOption { + public function isRepeatable(): bool; + /** @return array */ public function compile(): array; } diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/CookieJarOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/CookieJarOption.php index 8c489377..1a9c1ecf 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/CookieJarOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/CookieJarOption.php @@ -10,6 +10,11 @@ final class CookieJarOption implements ExtraOption { public function __construct(public readonly string $path) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--no-collate']; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/CopiesOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/CopiesOption.php index 42f79dd6..8cdca3f8 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/CopiesOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/CopiesOption.php @@ -13,6 +13,11 @@ final class CopiesOption implements ExtraOption */ public function __construct(private readonly int $number) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--copies', $this->number]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/DpiOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/DpiOption.php index ab0dc067..0716dbc8 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/DpiOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/DpiOption.php @@ -13,6 +13,11 @@ final class DpiOptions implements ExtraOption */ public function __construct(private readonly int $dpi) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--dpi', $this->dpi]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/FooterCenterOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/FooterCenterOption.php new file mode 100644 index 00000000..9b5df67d --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/FooterCenterOption.php @@ -0,0 +1,22 @@ +text]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/FooterFontNameOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/FooterFontNameOption.php new file mode 100644 index 00000000..d7400ad4 --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/FooterFontNameOption.php @@ -0,0 +1,22 @@ +fontName]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/FooterFontSizeOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/FooterFontSizeOption.php new file mode 100644 index 00000000..0857c5aa --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/FooterFontSizeOption.php @@ -0,0 +1,25 @@ +size]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/FooterHtmlOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/FooterHtmlOption.php new file mode 100644 index 00000000..e57eb41b --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/FooterHtmlOption.php @@ -0,0 +1,22 @@ +uri]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/FooterLeftOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/FooterLeftOption.php new file mode 100644 index 00000000..c79a51b9 --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/FooterLeftOption.php @@ -0,0 +1,22 @@ +text]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/CollateOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/FooterLineOption.php similarity index 55% rename from src/Backend/WkHtmlToPdf/ExtraOption/CollateOption.php rename to src/Backend/WkHtmlToPdf/ExtraOption/FooterLineOption.php index 0b3f70c3..a74a0c93 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/CollateOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/FooterLineOption.php @@ -6,12 +6,15 @@ use KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption; -final class CollateOption implements ExtraOption +final class FooterLineOption implements ExtraOption { - public function __construct() {} + public function isRepeatable(): bool + { + return false; + } public function compile(): array { - return ['--collate']; + return ['--footer-line']; } } diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/FooterRightOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/FooterRightOption.php new file mode 100644 index 00000000..5742c564 --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/FooterRightOption.php @@ -0,0 +1,22 @@ +text]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/FooterSpacingOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/FooterSpacingOption.php new file mode 100644 index 00000000..dfc1a8b9 --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/FooterSpacingOption.php @@ -0,0 +1,22 @@ +spacing]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/GrayscaleOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/GrayscaleOption.php index 2b6b82c7..ac72fb59 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/GrayscaleOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/GrayscaleOption.php @@ -8,7 +8,10 @@ final class GrayscaleOption implements ExtraOption { - public function __construct() {} + public function isRepeatable(): bool + { + return false; + } public function compile(): array { diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/HeaderCenterOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderCenterOption.php new file mode 100644 index 00000000..159af6ca --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderCenterOption.php @@ -0,0 +1,22 @@ +text]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/HeaderFontNameOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderFontNameOption.php new file mode 100644 index 00000000..d511d83f --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderFontNameOption.php @@ -0,0 +1,22 @@ +fontName]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/HeaderFontSizeOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderFontSizeOption.php new file mode 100644 index 00000000..7a3c61c9 --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderFontSizeOption.php @@ -0,0 +1,25 @@ +size]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/HeaderHtmlOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderHtmlOption.php new file mode 100644 index 00000000..feaba3b4 --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderHtmlOption.php @@ -0,0 +1,22 @@ +uri]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/HeaderLeftOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderLeftOption.php new file mode 100644 index 00000000..961407d5 --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderLeftOption.php @@ -0,0 +1,22 @@ +text]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/Outline.php b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderLineOption.php similarity index 54% rename from src/Backend/WkHtmlToPdf/ExtraOption/Outline.php rename to src/Backend/WkHtmlToPdf/ExtraOption/HeaderLineOption.php index aedaf6b1..c6f5cc7d 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/Outline.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderLineOption.php @@ -6,10 +6,15 @@ use KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption; -class Outline implements ExtraOption +final class HeaderLineOption implements ExtraOption { + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { - return ['--outline']; + return ['--header-line']; } -} \ No newline at end of file +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/HeaderRightOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderRightOption.php new file mode 100644 index 00000000..659434c1 --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderRightOption.php @@ -0,0 +1,22 @@ +text]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/HeaderSpacingOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderSpacingOption.php new file mode 100644 index 00000000..9d6567c8 --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/HeaderSpacingOption.php @@ -0,0 +1,22 @@ +spacing]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/ImageDpiOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/ImageDpiOption.php index 2e9cd76f..30d68028 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/ImageDpiOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/ImageDpiOption.php @@ -13,6 +13,11 @@ final class ImageDpiOption implements ExtraOption */ public function __construct(public readonly int $dpi) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--image-dpi', $this->dpi]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/ImageQualityOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/ImageQualityOption.php index 068f204b..7ce6309f 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/ImageQualityOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/ImageQualityOption.php @@ -13,6 +13,11 @@ final class ImageQualityOption implements ExtraOption */ public function __construct(public readonly int $quality) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--image-quality', $this->quality]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/LowQualityOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/LowQualityOption.php index cf22d505..02930578 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/LowQualityOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/LowQualityOption.php @@ -8,7 +8,10 @@ final class LowQualityOption implements ExtraOption { - public function __construct() {} + public function isRepeatable(): bool + { + return false; + } public function compile(): array { diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/MarginBottomOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/MarginBottomOption.php index b4f8a2ed..39e2064b 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/MarginBottomOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/MarginBottomOption.php @@ -10,6 +10,11 @@ final class MarginBottomOption implements ExtraOption { public function __construct(public readonly string $margin) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--margin-bottom', $this->margin]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/MarginLeftOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/MarginLeftOption.php index 5d840673..8ba0e60b 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/MarginLeftOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/MarginLeftOption.php @@ -10,6 +10,11 @@ final class MarginLeftOption implements ExtraOption { public function __construct(public readonly string $margin) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--margin-left', $this->margin]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/MarginRightOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/MarginRightOption.php index 9f455bb0..27069c0d 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/MarginRightOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/MarginRightOption.php @@ -10,6 +10,11 @@ final class MarginRightOption implements ExtraOption { public function __construct(public readonly string $margin) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--margin-right', $this->margin]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/MarginTopOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/MarginTopOption.php index 4d777883..4bf364f0 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/MarginTopOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/MarginTopOption.php @@ -10,6 +10,11 @@ final class MarginTopOption implements ExtraOption { public function __construct(public readonly string $margin) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--margin-top', $this->margin]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/NoCollateOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/NoCollateOption.php index db44f679..f9a05c9c 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/NoCollateOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/NoCollateOption.php @@ -8,7 +8,10 @@ final class NoCollateOption implements ExtraOption { - public function __construct() {} + public function isRepeatable(): bool + { + return false; + } public function compile(): array { diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/NoOutline.php b/src/Backend/WkHtmlToPdf/ExtraOption/NoOutline.php index 745b2535..b3f679e7 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/NoOutline.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/NoOutline.php @@ -8,8 +8,13 @@ class NoOutline implements ExtraOption { + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--no-outline']; } -} \ No newline at end of file +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/NoPdfCompressionOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/NoPdfCompressionOption.php index d3d07cb4..7198a3b6 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/NoPdfCompressionOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/NoPdfCompressionOption.php @@ -8,7 +8,10 @@ final class NoPdfCompressionOption implements ExtraOption { - public function __construct() {} + public function isRepeatable(): bool + { + return false; + } public function compile(): array { diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/OrientationOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/OrientationOption.php index 86485e82..00f0150f 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/OrientationOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/OrientationOption.php @@ -11,6 +11,11 @@ final class OrientationOption implements ExtraOption { public function __construct(private readonly ExtraOption\Orientation\Value $orientation) {} + public function isRepeatable(): bool + { + return false; + } + public static function fromPageOrientation(PageOrientation $pageOrientation): self { return new self( diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/OutlineDepth.php b/src/Backend/WkHtmlToPdf/ExtraOption/OutlineDepth.php index b193b3f6..621b171f 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/OutlineDepth.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/OutlineDepth.php @@ -11,8 +11,13 @@ class OutlineDepth implements ExtraOption public function __construct(private readonly int $depth) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--outline-depth', $this->depth]; } -} \ No newline at end of file +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/PageHeightOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/PageHeightOption.php index 160ae39b..d8090eb7 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/PageHeightOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/PageHeightOption.php @@ -10,6 +10,11 @@ final class PageHeightOption implements ExtraOption { public function __construct(public readonly string $height) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--page-height', $this->height]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/PageSizeOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/PageSizeOption.php index 61025935..d23c51c9 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/PageSizeOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/PageSizeOption.php @@ -10,6 +10,11 @@ final class PageSizeOption implements ExtraOption { public function __construct(public readonly string $size) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--page-size', $this->size]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/PageWidthOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/PageWidthOption.php index 1cf72af4..7479ae81 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/PageWidthOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/PageWidthOption.php @@ -10,6 +10,11 @@ final class PageWidthOption implements ExtraOption { public function __construct(public readonly string $width) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--page-width', $this->width]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/ReplaceOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/ReplaceOption.php new file mode 100644 index 00000000..f034ddb2 --- /dev/null +++ b/src/Backend/WkHtmlToPdf/ExtraOption/ReplaceOption.php @@ -0,0 +1,22 @@ +name, $this->value]; + } +} diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/TitleOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/TitleOption.php index e4ac248b..ef11ce62 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/TitleOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/TitleOption.php @@ -10,6 +10,11 @@ final class TitleOption implements ExtraOption { public function __construct(public readonly string $title) {} + public function isRepeatable(): bool + { + return false; + } + public function compile(): array { return ['--title', $this->title]; diff --git a/src/Backend/WkHtmlToPdf/ExtraOption/UseXserverOption.php b/src/Backend/WkHtmlToPdf/ExtraOption/UseXserverOption.php index c5e9af5a..8ba9d914 100644 --- a/src/Backend/WkHtmlToPdf/ExtraOption/UseXserverOption.php +++ b/src/Backend/WkHtmlToPdf/ExtraOption/UseXserverOption.php @@ -8,7 +8,10 @@ final class UseXserverOption implements ExtraOption { - public function __construct() {} + public function isRepeatable(): bool + { + return false; + } public function compile(): array { diff --git a/src/Backend/WkHtmlToPdf/Options/GlobalOptions.php b/src/Backend/WkHtmlToPdf/Options/GlobalOptions.php deleted file mode 100644 index 8f35e54e..00000000 --- a/src/Backend/WkHtmlToPdf/Options/GlobalOptions.php +++ /dev/null @@ -1,22 +0,0 @@ -number]; - } -} diff --git a/src/Backend/WkHtmlToPdf/Options/GlobalOptions/DpiOption.php b/src/Backend/WkHtmlToPdf/Options/GlobalOptions/DpiOption.php deleted file mode 100644 index 27542c90..00000000 --- a/src/Backend/WkHtmlToPdf/Options/GlobalOptions/DpiOption.php +++ /dev/null @@ -1,15 +0,0 @@ -dpi]; - } -} diff --git a/src/Backend/WkHtmlToPdf/Options/GlobalOptions/GrayscaleOption.php b/src/Backend/WkHtmlToPdf/Options/GlobalOptions/GrayscaleOption.php deleted file mode 100644 index 7093e0a7..00000000 --- a/src/Backend/WkHtmlToPdf/Options/GlobalOptions/GrayscaleOption.php +++ /dev/null @@ -1,15 +0,0 @@ -extraOptions as $extraOption) { - if (!$extraOption instanceof ExtraOption) { - throw new \InvalidArgumentException("Invalid option type"); - } - } + self::validateOptions($options); $this->factory = $factory; $this->options = $options; @@ -62,15 +59,16 @@ public function generateFromHtmlFile(SplFileInfo $file): StreamInterface public function generateFromUri(UriInterface $uri): StreamInterface { - $outputStream = FileStream::createTmpFile($this->streamFactory); + $outputFile = SplResourceInfo::fromTmpFile(); $process = new Process( command: [ $this->binary, + '--log-level', 'none', '--quiet', ...$this->compileOptions(), $uri->toString(), - $outputStream->file->getPathname(), + $outputFile->getPathname(), ], timeout: $this->timeout, ); @@ -81,7 +79,31 @@ public function generateFromUri(UriInterface $uri): StreamInterface throw new ProcessFailedException($process); } - return $outputStream; + return $this->streamFactory->createFromResource($outputFile->resource); + } + + private static function validateOptions(Options $options): void + { + $optionTypes = []; + + foreach ($options->extraOptions as $option) { + if (!$option instanceof ExtraOption) { + throw new \InvalidArgumentException(sprintf( + "Invalid option type provided. Expected \"%s\", received \"%s\".", + ExtraOption::class, + gettype($option) === 'object' ? get_class($option) : gettype($option), + )); + } + + if (\in_array($option::class, $optionTypes, true) && !$option->isRepeatable()) { + throw new \InvalidArgumentException(sprintf( + "Duplicate option type provided: \"%s\".", + $option::class, + )); + } + + $optionTypes[] = $option::class; + } } /** diff --git a/src/Core/Filesystem/SplResourceInfo.php b/src/Core/Filesystem/SplResourceInfo.php new file mode 100644 index 00000000..5578c1d1 --- /dev/null +++ b/src/Core/Filesystem/SplResourceInfo.php @@ -0,0 +1,21 @@ +resource)['uri']); + } +}