@@ -27,6 +27,9 @@ class FileSystem
27
27
/** @var string */
28
28
public const SCAN_DIRECTORIES = "SCAN_DIRECTORIES " ;
29
29
30
+ /** @var string[] */
31
+ private static array $ imageFunctionSuffixes = ["jpeg " , "webp " , "png " , "gif " , "bmp " , "gd " , "wbmp " , "xbm " , "xpn " , "gd2 " , "gd2part " ];
32
+
30
33
/**
31
34
* @param string $source
32
35
* @param string $destination
@@ -507,7 +510,7 @@ public static function zip(string $source, ?string $destination = null, bool $ov
507
510
throw new RuntimeException ("You have to enable zip extension to use this function. " );
508
511
}
509
512
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 ));
511
514
}
512
515
if (!self ::exists ($ source )) {
513
516
throw new InvalidArgumentException (sprintf ("Source %s does not exist. " , $ source ));
@@ -563,7 +566,7 @@ public static function unzip(string $source, ?string $destination = null, bool $
563
566
throw new RuntimeException ("You have to enable zip extension to use this function. " );
564
567
}
565
568
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 ));
567
570
}
568
571
if (!self ::exists ($ source )) {
569
572
throw new InvalidArgumentException (sprintf ("Source %s does not exist. " , $ source ));
@@ -587,13 +590,13 @@ public static function unzip(string $source, ?string $destination = null, bool $
587
590
return $ destination ;
588
591
}
589
592
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
591
594
{
592
595
if (!extension_loaded ("zlib " )) {
593
596
throw new RuntimeException ("You have to enable zlib extension to use this function. " );
594
597
}
595
598
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 ));
597
600
}
598
601
if (!self ::isFile ($ source )) {
599
602
throw new InvalidArgumentException (sprintf ("Source %s does not exits. " , $ source ));
@@ -602,7 +605,7 @@ public static function compressFile(string $source, ?string $destination = null,
602
605
throw new InvalidArgumentException (sprintf ('Parameter $compressionLevel has to be between 1 and 9. ' ));
603
606
}
604
607
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. ' ));
606
609
}
607
610
if ($ destination !== null && self ::exists ($ destination )) {
608
611
self ::delete ($ destination );
@@ -616,7 +619,7 @@ public static function compressFile(string $source, ?string $destination = null,
616
619
throw new RuntimeException (sprintf ("Function fopen('%s', '%s') has failed. " , $ source , "rb " ));
617
620
}
618
621
while (!feof ($ sourceResource )) {
619
- $ chunk = fread ($ sourceResource , 1024 * $ bufferSize );
622
+ $ chunk = fread ($ sourceResource , $ bufferSize );
620
623
if ($ chunk === false ) {
621
624
throw new RuntimeException ("Could not read chunk from file {$ source }. " );
622
625
}
@@ -631,7 +634,7 @@ public static function compressFile(string $source, ?string $destination = null,
631
634
return $ destination ;
632
635
}
633
636
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 )
635
638
{
636
639
if (!extension_loaded ("zlib " )) {
637
640
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
643
646
throw new InvalidArgumentException (sprintf ("Source %s does not exits. " , $ source ));
644
647
}
645
648
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. ' ));
647
650
}
648
651
if ($ overwrite && $ destination !== null && self ::exists ($ destination )) {
649
652
self ::delete ($ destination );
@@ -658,7 +661,7 @@ public static function decompressFile(string $source, ?string $destination = nul
658
661
throw new RuntimeException (sprintf ("Function fopen('%s', 'wb') has failed. " , $ destination ));
659
662
}
660
663
while (!gzeof ($ sourceResource )) {
661
- fwrite ($ destinationResource , gzread ($ sourceResource , 1024 * $ bufferSize ));
664
+ fwrite ($ destinationResource , gzread ($ sourceResource , $ bufferSize ));
662
665
}
663
666
if (gzclose ($ sourceResource ) === false ) {
664
667
throw new RuntimeException ("Could not close file {$ source }. " );
@@ -668,4 +671,161 @@ public static function decompressFile(string $source, ?string $destination = nul
668
671
}
669
672
return $ destination ;
670
673
}
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
+ }
671
831
}
0 commit comments