Skip to content

Commit ad0642e

Browse files
committed
FileSystem extended
1 parent 7e6262f commit ad0642e

File tree

1 file changed

+169
-9
lines changed

1 file changed

+169
-9
lines changed

src/FileSystem.php

Lines changed: 169 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class FileSystem
2727
/** @var string */
2828
public const SCAN_DIRECTORIES = "SCAN_DIRECTORIES";
2929

30+
/** @var string[] */
31+
private static array $imageFunctionSuffixes = ["jpeg", "webp", "png", "gif", "bmp", "gd", "wbmp", "xbm", "xpn", "gd2", "gd2part"];
32+
3033
/**
3134
* @param string $source
3235
* @param string $destination
@@ -507,7 +510,7 @@ public static function zip(string $source, ?string $destination = null, bool $ov
507510
throw new RuntimeException("You have to enable zip extension to use this function.");
508511
}
509512
if (!$overwrite && $destination !== null && self::exists($destination)) {
510-
throw new RuntimeException(sprintf("Destination %s already exists.", $destination));
513+
throw new InvalidArgumentException(sprintf("Destination %s already exists.", $destination));
511514
}
512515
if (!self::exists($source)) {
513516
throw new InvalidArgumentException(sprintf("Source %s does not exist.", $source));
@@ -563,7 +566,7 @@ public static function unzip(string $source, ?string $destination = null, bool $
563566
throw new RuntimeException("You have to enable zip extension to use this function.");
564567
}
565568
if (!$overwrite && $destination !== null && self::exists($destination)) {
566-
throw new RuntimeException(sprintf("Destination %s already exists.", $destination));
569+
throw new InvalidArgumentException(sprintf("Destination %s already exists.", $destination));
567570
}
568571
if (!self::exists($source)) {
569572
throw new InvalidArgumentException(sprintf("Source %s does not exist.", $source));
@@ -587,13 +590,13 @@ public static function unzip(string $source, ?string $destination = null, bool $
587590
return $destination;
588591
}
589592

590-
public static function compressFile(string $source, ?string $destination = null, bool $overwrite = false, int $compressionLevel = 9, int $bufferSize = 512): string
593+
public static function compressFile(string $source, ?string $destination = null, bool $overwrite = false, int $compressionLevel = 9, int $bufferSize = 1024 * 512): string
591594
{
592595
if (!extension_loaded("zlib")) {
593596
throw new RuntimeException("You have to enable zlib extension to use this function.");
594597
}
595598
if (!$overwrite && $destination !== null && self::exists($destination)) {
596-
throw new RuntimeException(sprintf("Destination %s already exists.", $destination));
599+
throw new InvalidArgumentException(sprintf("Destination %s already exists.", $destination));
597600
}
598601
if (!self::isFile($source)) {
599602
throw new InvalidArgumentException(sprintf("Source %s does not exits.", $source));
@@ -602,7 +605,7 @@ public static function compressFile(string $source, ?string $destination = null,
602605
throw new InvalidArgumentException(sprintf('Parameter $compressionLevel has to be between 1 and 9.'));
603606
}
604607
if ($bufferSize < 1) {
605-
throw new RuntimeException(sprintf('Parameter $bufferSize has to be greater than 1.'));
608+
throw new InvalidArgumentException(sprintf('Parameter $bufferSize has to be greater than 1.'));
606609
}
607610
if ($destination !== null && self::exists($destination)) {
608611
self::delete($destination);
@@ -616,7 +619,7 @@ public static function compressFile(string $source, ?string $destination = null,
616619
throw new RuntimeException(sprintf("Function fopen('%s', '%s') has failed.", $source, "rb"));
617620
}
618621
while (!feof($sourceResource)) {
619-
$chunk = fread($sourceResource, 1024 * $bufferSize);
622+
$chunk = fread($sourceResource, $bufferSize);
620623
if ($chunk === false) {
621624
throw new RuntimeException("Could not read chunk from file {$source}.");
622625
}
@@ -631,7 +634,7 @@ public static function compressFile(string $source, ?string $destination = null,
631634
return $destination;
632635
}
633636

634-
public static function decompressFile(string $source, ?string $destination = null, bool $overwrite = false, int $bufferSize = 512)
637+
public static function decompressFile(string $source, ?string $destination = null, bool $overwrite = false, int $bufferSize = 1024 * 512)
635638
{
636639
if (!extension_loaded("zlib")) {
637640
throw new RuntimeException("You have to enable zlib extension to use this function.");
@@ -643,7 +646,7 @@ public static function decompressFile(string $source, ?string $destination = nul
643646
throw new InvalidArgumentException(sprintf("Source %s does not exits.", $source));
644647
}
645648
if ($bufferSize < 1) {
646-
throw new RuntimeException(sprintf('Parameter $bufferSize has to be greater than 1.'));
649+
throw new InvalidArgumentException(sprintf('Parameter $bufferSize has to be greater than 1.'));
647650
}
648651
if ($overwrite && $destination !== null && self::exists($destination)) {
649652
self::delete($destination);
@@ -658,7 +661,7 @@ public static function decompressFile(string $source, ?string $destination = nul
658661
throw new RuntimeException(sprintf("Function fopen('%s', 'wb') has failed.", $destination));
659662
}
660663
while (!gzeof($sourceResource)) {
661-
fwrite($destinationResource, gzread($sourceResource, 1024 * $bufferSize));
664+
fwrite($destinationResource, gzread($sourceResource, $bufferSize));
662665
}
663666
if (gzclose($sourceResource) === false) {
664667
throw new RuntimeException("Could not close file {$source}.");
@@ -668,4 +671,161 @@ public static function decompressFile(string $source, ?string $destination = nul
668671
}
669672
return $destination;
670673
}
674+
675+
public static function compressImageToSize(string $source, int $targetSize, ?string $destination = null, bool $overwrite = false, string $inputFormat = null, bool $outputFormat = null): string
676+
{
677+
if (!$overwrite && $destination !== null && self::exists($destination)) {
678+
throw new RuntimeException(sprintf("Destination %s already exists.", $destination));
679+
}
680+
if (!self::isFile($source)) {
681+
throw new InvalidArgumentException(sprintf("Source %s does not exits.", $source));
682+
}
683+
if ($targetSize < 1) {
684+
throw new RuntimeException(sprintf('Parameter $targetSize has to be greater than 1.'));
685+
}
686+
if ($inputFormat !== null && !function_exists("imagecreatefrom{$inputFormat}")) {
687+
throw new InvalidArgumentException(sprintf("Unknown format %s. This function tried to use imagecreatefrom%s function.", $inputFormat, $inputFormat));
688+
}
689+
if ($outputFormat !== null && !function_exists("image{$outputFormat}")) {
690+
throw new InvalidArgumentException(sprintf("Unknown format %s. This function tried to use image%s function.", $outputFormat, $outputFormat));
691+
}
692+
if ($overwrite && $destination !== null && self::exists($destination)) {
693+
self::delete($destination);
694+
}
695+
if ($destination === null) {
696+
$destination = self::generateFilename();
697+
}
698+
if (self::size($source) <= $targetSize) {
699+
self::copy($source, $destination);
700+
return $destination;
701+
}
702+
if ($inputFormat !== null) {
703+
$function = "imagecreatefrom{$inputFormat}";
704+
$sourceImage = $function($source);
705+
if ($sourceImage === false) {
706+
throw new RuntimeException(sprintf("Function %s('%s') has failed.", $function, $source));
707+
}
708+
} else {
709+
$sourceImage = null;
710+
foreach (self::$imageFunctionSuffixes as $suffix) {
711+
$function = "imagecreatefrom{$suffix}";
712+
$sourceImage = $function($source);
713+
if ($sourceImage !== false) {
714+
break;
715+
}
716+
}
717+
if ($sourceImage === false) {
718+
throw new RuntimeException(sprintf("Could not autodetect function which could open %s.", $source));
719+
}
720+
}
721+
if ($outputFormat === null) {
722+
$outputFormat = function_exists("imagewebp") ? "webp" : "jpeg";
723+
}
724+
$outputFunction = "image{$outputFormat}";
725+
$cleanup = function ($cleanDestination = false) use (&$sourceImage, $destination) {
726+
if (function_exists("imagedestroy")) {
727+
imagedestroy($sourceImage);
728+
}
729+
unset($sourceImage);
730+
if ($cleanDestination && self::exists($destination)) {
731+
self::delete($destination);
732+
}
733+
};
734+
$min = 0;
735+
$max = 100;
736+
$acceptableQualities = [];
737+
while ($min <= $max) {
738+
$mid = floor(($min + $max) / 2);
739+
if ($outputFunction($sourceImage, $destination, $mid) === false) {
740+
$cleanup(true);
741+
throw new RuntimeException(sprintf("Function %s('%s', '%s', %s) has failed.", $outputFunction, $source, $destination, $mid));
742+
}
743+
$size = filesize($destination);
744+
if ($size === false) {
745+
$cleanup(true);
746+
throw new RuntimeException(sprintf("Could not read file size of %s.", $destination));
747+
}
748+
clearstatcache(true, $destination);
749+
if ($size <= $targetSize) {
750+
$acceptableQualities[$size] = $mid;
751+
$min = $mid + 1;
752+
} else {
753+
$max = $mid - 1;
754+
}
755+
}
756+
if (empty($acceptableQualities)) {
757+
$cleanup(true);
758+
throw new RuntimeException(sprintf("Could not compress image to target size."));
759+
}
760+
$quality = $acceptableQualities[max(array_keys($acceptableQualities))];
761+
if ($outputFunction($sourceImage, $destination, $quality) === false) {
762+
$cleanup(true);
763+
throw new RuntimeException(sprintf("Function %s('%s', '%s', %s) has failed.", $outputFunction, $source, $destination, $quality));
764+
}
765+
$cleanup(false);
766+
return $destination;
767+
}
768+
769+
public static function compressImageToQuality(string $source, int $quality, ?string $destination = null, bool $overwrite = false, string $inputFormat = null, bool $outputFormat = null): string
770+
{
771+
if (!$overwrite && $destination !== null && self::exists($destination)) {
772+
throw new RuntimeException(sprintf("Destination %s already exists.", $destination));
773+
}
774+
if (!self::isFile($source)) {
775+
throw new InvalidArgumentException(sprintf("Source %s does not exits.", $source));
776+
}
777+
if ($quality < 0 || $quality > 100) {
778+
throw new InvalidArgumentException(sprintf('Parameter $quality is expected to be between 0 and 100.'));
779+
}
780+
if ($inputFormat !== null && !function_exists("imagecreatefrom{$inputFormat}")) {
781+
throw new InvalidArgumentException(sprintf("Unknown format %s. This function tried to use imagecreatefrom%s function.", $inputFormat, $inputFormat));
782+
}
783+
if ($outputFormat !== null && !function_exists("image{$outputFormat}")) {
784+
throw new InvalidArgumentException(sprintf("Unknown format %s. This function tried to use image%s function.", $outputFormat, $outputFormat));
785+
}
786+
if ($overwrite && $destination !== null && self::exists($destination)) {
787+
self::delete($destination);
788+
}
789+
if ($destination === null) {
790+
$destination = self::generateFilename();
791+
}
792+
if ($inputFormat !== null) {
793+
$function = "imagecreatefrom{$inputFormat}";
794+
$sourceImage = $function($source);
795+
if ($sourceImage === false) {
796+
throw new RuntimeException(sprintf("Function %s('%s') has failed.", $function, $source));
797+
}
798+
} else {
799+
$sourceImage = null;
800+
foreach (self::$imageFunctionSuffixes as $suffix) {
801+
$function = "imagecreatefrom{$suffix}";
802+
$sourceImage = $function($source);
803+
if ($sourceImage !== false) {
804+
break;
805+
}
806+
}
807+
if ($sourceImage === false) {
808+
throw new RuntimeException(sprintf("Could not autodetect function which could open %s.", $source));
809+
}
810+
}
811+
if ($outputFormat === null) {
812+
$outputFormat = function_exists("imagewebp") ? "webp" : "jpeg";
813+
}
814+
$outputFunction = "image{$outputFormat}";
815+
$cleanup = function ($cleanDestination = false) use (&$sourceImage, $destination) {
816+
if (function_exists("imagedestroy")) {
817+
imagedestroy($sourceImage);
818+
}
819+
unset($sourceImage);
820+
if ($cleanDestination && self::exists($destination)) {
821+
self::delete($destination);
822+
}
823+
};
824+
if ($outputFunction($sourceImage, $destination, $quality) === false) {
825+
$cleanup(true);
826+
throw new RuntimeException(sprintf("Function %s('%s', '%s', %s) has failed.", $outputFunction, $source, $destination, $quality));
827+
}
828+
$cleanup(false);
829+
return $destination;
830+
}
671831
}

0 commit comments

Comments
 (0)