Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
47 / 47
100.00% covered (success)
100.00%
17 / 17
CRAP
100.00% covered (success)
100.00%
1 / 1
AbstractAdapter
100.00% covered (success)
100.00%
47 / 47
100.00% covered (success)
100.00%
17 / 17
38
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
9
 createResource
n/a
0 / 0
n/a
0 / 0
0
 getResource
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasResource
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getWidth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHeight
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getQuality
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getColorspace
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isIndexed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFormat
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExif
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isGray
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isRgb
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isCmyk
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setQuality
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 sendHeaders
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
7
 __get
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
8
 load
n/a
0 / 0
n/a
0 / 0
0
 loadFromString
n/a
0 / 0
n/a
0 / 0
0
 create
n/a
0 / 0
n/a
0 / 0
0
 createIndex
n/a
0 / 0
n/a
0 / 0
0
 resizeToWidth
n/a
0 / 0
n/a
0 / 0
0
 resizeToHeight
n/a
0 / 0
n/a
0 / 0
0
 resize
n/a
0 / 0
n/a
0 / 0
0
 scale
n/a
0 / 0
n/a
0 / 0
0
 crop
n/a
0 / 0
n/a
0 / 0
0
 cropThumb
n/a
0 / 0
n/a
0 / 0
0
 rotate
n/a
0 / 0
n/a
0 / 0
0
 flip
n/a
0 / 0
n/a
0 / 0
0
 flop
n/a
0 / 0
n/a
0 / 0
0
 adjust
n/a
0 / 0
n/a
0 / 0
0
 filter
n/a
0 / 0
n/a
0 / 0
0
 layer
n/a
0 / 0
n/a
0 / 0
0
 draw
n/a
0 / 0
n/a
0 / 0
0
 effect
n/a
0 / 0
n/a
0 / 0
0
 type
n/a
0 / 0
n/a
0 / 0
0
 convert
n/a
0 / 0
n/a
0 / 0
0
 writeToFile
n/a
0 / 0
n/a
0 / 0
0
 outputToRawString
n/a
0 / 0
n/a
0 / 0
0
 outputToHttp
n/a
0 / 0
n/a
0 / 0
0
 destroy
n/a
0 / 0
n/a
0 / 0
0
 createColor
n/a
0 / 0
n/a
0 / 0
0
 __toString
n/a
0 / 0
n/a
0 / 0
0
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <dev@noladev.com>
8 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Image\Adapter;
16
17use Pop\Image\Adjust;
18use Pop\Color\Color;
19use Pop\Image\Draw;
20use Pop\Image\Effect;
21use Pop\Image\Filter;
22use Pop\Image\Layer;
23use Pop\Image\Type;
24
25/**
26 * Abstract adapter class
27 *
28 * @category   Pop
29 * @package    Pop\Image
30 * @author     Nick Sagona, III <dev@noladev.com>
31 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
32 * @license    https://www.popphp.org/license     New BSD License
33 * @version    5.0.0
34 */
35abstract class AbstractAdapter implements AdapterInterface
36{
37
38    /**
39     * Colorspace constants
40     */
41    const IMAGE_GRAY = 1;
42    const IMAGE_RGB  = 2;
43    const IMAGE_CMYK = 3;
44
45    /**
46     * Image resource
47     * @var mixed
48     */
49    protected mixed $resource = null;
50
51    /**
52     * Image name
53     * @var string
54     */
55    protected string $name = 'pop-image.jpg';
56
57    /**
58     * Image width
59     * @var int
60     */
61    protected int $width = 640;
62
63    /**
64     * Image height
65     * @var int
66     */
67    protected int $height = 480;
68
69    /**
70     * Image format
71     * @var string
72     */
73    protected string $format = 'jpg';
74
75    /**
76     * Image quality
77     * @var int
78     */
79    protected int $quality = 100;
80
81    /**
82     * Image colorspace
83     * @var int
84     */
85    protected int $colorspace = 2;
86
87    /**
88     * Index color flag
89     * @var bool
90     */
91    protected bool $indexed = false;
92
93    /**
94     * EXIF data
95     * @var array
96     */
97    protected array $exif = [];
98
99    /**
100     * Image adjust object
101     * @var ?Adjust\AdjustInterface
102     */
103    protected ?Adjust\AdjustInterface $adjust = null;
104
105    /**
106     * Image draw object
107     * @var ?Draw\DrawInterface
108     */
109    protected ?Draw\DrawInterface $draw = null;
110
111    /**
112     * Image effect object
113     * @var ?Effect\EffectInterface
114     */
115    protected ?Effect\EffectInterface $effect = null;
116
117    /**
118     * Image filter object
119     * @var ?Filter\FilterInterface
120     */
121    protected ?Filter\FilterInterface $filter = null;
122
123    /**
124     * Image layer object
125     * @var ?Layer\LayerInterface
126     */
127    protected ?Layer\LayerInterface $layer = null;
128
129    /**
130     * Image type object
131     * @var ?Type\TypeInterface
132     */
133    protected ?Type\TypeInterface $type = null;
134
135    /**
136     * Constructor
137     *
138     * Instantiate an image object based on either a pre-existing image
139     * file on disk, or a new image file.
140     *
141     */
142    public function __construct()
143    {
144        $args = func_get_args();
145
146        $this->createResource();
147
148        // $image
149        if (isset($args[0]) && !is_numeric($args[0]) && file_exists($args[0])) {
150            $this->name = $args[0];
151            $this->load();
152        // $width, $height, $name
153        } else if ((count($args) >= 2) && is_numeric($args[0]) && is_numeric($args[1])) {
154            $this->width  = $args[0];
155            $this->height = $args[1];
156            if (isset($args[2]) && !is_numeric($args[2])) {
157                $this->name = $args[2];
158            }
159            $this->create();
160        }
161    }
162
163    /**
164     * Create the image resource
165     *
166     * @return void
167     */
168    abstract public function createResource(): void;
169
170    /**
171     * Get the image resource
172     *
173     * @return mixed
174     */
175    public function getResource(): mixed
176    {
177        return $this->resource;
178    }
179
180    /**
181     * Determine if there is an image resource
182     *
183     * @return bool
184     */
185    public function hasResource(): bool
186    {
187        return ($this->resource !== null);
188    }
189
190    /**
191     * Get the image name
192     *
193     * @return string
194     */
195    public function getName(): string
196    {
197        return $this->name;
198    }
199
200    /**
201     * Get the image width
202     *
203     * @return int
204     */
205    public function getWidth(): int
206    {
207        return $this->width;
208    }
209
210    /**
211     * Get the image height
212     *
213     * @return int
214     */
215    public function getHeight(): int
216    {
217        return $this->height;
218    }
219
220    /**
221     * Get the image quality
222     *
223     * @return int
224     */
225    public function getQuality(): int
226    {
227        return $this->quality;
228    }
229
230    /**
231     * Get the colorspace
232     *
233     * @return int
234     */
235    public function getColorspace(): int
236    {
237        return $this->colorspace;
238    }
239
240    /**
241     * Determine if the image is index color
242     *
243     * @return bool
244     */
245    public function isIndexed(): bool
246    {
247        return $this->indexed;
248    }
249
250    /**
251     * Get the image format
252     *
253     * @return string
254     */
255    public function getFormat(): string
256    {
257        return $this->format;
258    }
259
260    /**
261     * Get the image EXIF data
262     *
263     * @return array
264     */
265    public function getExif(): array
266    {
267        return $this->exif;
268    }
269
270    /**
271     * Determine if the image is grayscale
272     *
273     * @return bool
274     */
275    public function isGray(): bool
276    {
277        return ($this->colorspace == self::IMAGE_GRAY);
278    }
279
280    /**
281     * Determine if the image is RGB
282     *
283     * @return bool
284     */
285    public function isRgb(): bool
286    {
287        return ($this->colorspace == self::IMAGE_RGB);
288    }
289
290    /**
291     * Determine if the image is CMYK
292     *
293     * @return bool
294     */
295    public function isCmyk(): bool
296    {
297        return ($this->colorspace == self::IMAGE_CMYK);
298    }
299
300    /**
301     * Set the image quality
302     *
303     * @oaram  int $quality
304     * @return static
305     */
306    public function setQuality(int $quality): static
307    {
308        $this->quality = $quality;
309        return $this;
310    }
311
312    /**
313     * Send image headers the image
314     *
315     * @param  ?string $to
316     * @param  bool    $download
317     * @param  array   $additionalHeaders
318     * @return void
319     */
320    public function sendHeaders(?string $to = null, bool $download = false, array $additionalHeaders = []): void
321    {
322        if ($to === null) {
323            $to = basename($this->name);
324        }
325
326        // Determine if the force download argument has been passed.
327        $headers = [
328            'Content-type'        => 'image/' . (($this->format == 'jpg') ? 'jpeg' : $this->format),
329            'Content-disposition' => (($download) ? 'attachment; ' : null) . 'filename=' . $to
330        ];
331
332        if (!empty($additionalHeaders)) {
333            $headers = $headers + $additionalHeaders;
334        }
335
336        // Send the headers and output the image
337        if (!headers_sent()) {
338            header('HTTP/1.1 200 OK');
339            foreach ($headers as $name => $value) {
340                header($name . ': ' . $value);
341            }
342        }
343    }
344
345    /**
346     * Magic get method to return a manipulation object
347     *
348     * @param  string $name
349     * @return mixed
350     */
351    public function __get(string $name): mixed
352    {
353        return match ($name) {
354            'adjust' => $this->adjust(),
355            'filter' => $this->filter(),
356            'layer'  => $this->layer(),
357            'draw'   => $this->draw(),
358            'effect' => $this->effect(),
359            'type'   => $this->type(),
360            default  => null,
361        };
362    }
363
364    /**
365     * Load the image resource from the existing image file
366     *
367     * @param  ?string $name
368     * @return AbstractAdapter
369     */
370    abstract public function load(?string $name = null): AbstractAdapter;
371
372    /**
373     * Load the image resource from data
374     *
375     * @param  string  $data
376     * @param  ?string $name
377     * @return AbstractAdapter
378     */
379    abstract public function loadFromString(string $data, ?string $name = null): AbstractAdapter;
380
381    /**
382     * Create a new image resource
383     *
384     * @param  ?int    $width
385     * @param  ?int    $height
386     * @param  ?string $name
387     * @return AbstractAdapter
388     */
389    abstract public function create(?int $width = null, ?int $height = null, ?string $name = null): AbstractAdapter;
390
391    /**
392     * Create a new indexed image resource
393     *
394     * @param  ?int    $width
395     * @param  ?int    $height
396     * @param  ?string $name
397     * @return AbstractAdapter
398     */
399    abstract public function createIndex(?int $width = null, ?int $height = null, ?string $name = null): AbstractAdapter;
400
401    /**
402     * Resize the image object to the width parameter passed
403     *
404     * @param  int $w
405     * @return AbstractAdapter
406     */
407    abstract public function resizeToWidth(int $w): AbstractAdapter;
408
409    /**
410     * Resize the image object to the height parameter passed
411     *
412     * @param  int $h
413     * @return AbstractAdapter
414     */
415    abstract public function resizeToHeight(int $h): AbstractAdapter;
416
417    /**
418     * Resize the image object, allowing for the largest dimension
419     * to be scaled to the value of the $px argument.
420     *
421     * @param  int $px
422     * @return AbstractAdapter
423     */
424    abstract public function resize(int $px): AbstractAdapter;
425
426    /**
427     * Scale the image object, allowing for the dimensions to be scaled
428     * proportionally to the value of the $scl argument.
429     *
430     * @param  float $scale
431     * @return AbstractAdapter
432     */
433    abstract public function scale(float $scale): AbstractAdapter;
434
435    /**
436     * Crop the image object to a image whose dimensions are based on the
437     * value of the $wid and $hgt argument. The optional $x and $y arguments
438     * allow for the adjustment of the crop to select a certain area of the
439     * image to be cropped.
440     *
441     * @param  int $w
442     * @param  int $h
443     * @param  int $x
444     * @param  int $y
445     * @return AbstractAdapter
446     */
447    abstract public function crop(int $w, int $h, int $x = 0, int $y = 0): AbstractAdapter;
448
449    /**
450     * Crop the image object to a square image whose dimensions are based on the
451     * value of the $px argument. The optional $offset argument allows for the
452     * adjustment of the crop to select a certain area of the image to be cropped.
453     *
454     * @param  int  $px
455     * @param  ?int $offset
456     * @return AbstractAdapter
457     */
458    abstract public function cropThumb(int $px, ?int $offset = null): AbstractAdapter;
459
460    /**
461     * Rotate the image object
462     *
463     * @param  int                   $degrees
464     * @param  ?Color\ColorInterface $bgColor
465     * @throws Exception
466     * @return AbstractAdapter
467     */
468    abstract public function rotate(int $degrees, ?Color\ColorInterface $bgColor = null): AbstractAdapter;
469
470    /**
471     * Method to flip the image over the x-axis
472     *
473     * @return AbstractAdapter
474     */
475    abstract public function flip(): AbstractAdapter;
476
477    /**
478     * Method to flip the image over the y-axis
479     *
480     * @return AbstractAdapter
481     */
482    abstract public function flop(): AbstractAdapter;
483
484    /**
485     * Get the image adjust object
486     *
487     * @return Adjust\AdjustInterface
488     */
489    abstract public function adjust(): Adjust\AdjustInterface;
490
491    /**
492     * Get the image filter object
493     *
494     * @return Filter\FilterInterface
495     */
496    abstract public function filter(): Filter\FilterInterface;
497
498    /**
499     * Get the image layer object
500     *
501     * @return Layer\LayerInterface
502     */
503    abstract public function layer(): Layer\LayerInterface;
504
505    /**
506     * Get the image draw object
507     *
508     * @return Draw\DrawInterface
509     */
510    abstract public function draw(): Draw\DrawInterface;
511
512    /**
513     * Get the image effect object
514     *
515     * @return Effect\EffectInterface
516     */
517    abstract public function effect(): Effect\EffectInterface;
518
519    /**
520     * Get the image type object
521     *
522     * @return Type\TypeInterface
523     */
524    abstract public function type(): Type\TypeInterface;
525
526    /**
527     * Convert the image object to another format
528     *
529     * @param  string $type
530     * @throws Exception
531     * @return AbstractAdapter
532     */
533    abstract public function convert(string $type): AbstractAdapter;
534
535    /**
536     * Write the image object to a file on disk
537     *
538     * @param  ?string $to
539     * @param  int     $quality
540     * @throws Exception
541     * @return void
542     */
543    abstract public function writeToFile(?string $to = null, int $quality = 100): void;
544
545    /**
546     * Output the image object to a raw string
547     *
548     * @param  int $quality
549     * @throws Exception
550     * @return string|false
551     */
552    abstract public function outputToRawString(int $quality = 100): string|false;
553
554    /**
555     * Output the image object directly to HTTP
556     *
557     * @param  int     $quality
558     * @param  ?string $to
559     * @param  bool    $download
560     * @param  bool    $sendHeaders
561     * @throws Exception
562     * @return void
563     */
564    abstract public function outputToHttp(int $quality = 100, ?string $to = null, bool $download = false, bool $sendHeaders = true): void;
565
566    /**
567     * Destroy the image object and the related image file directly
568     *
569     * @param  bool $delete
570     * @return void
571     */
572    abstract public function destroy(bool $delete = false): void;
573
574    /**
575     * Create and return a color.
576     *
577     * @param  ?Color\ColorInterface $color
578     * @param  int                   $alpha
579     * @throws Exception
580     * @return mixed
581     */
582    abstract public function createColor(?Color\ColorInterface $color = null, int $alpha = 100): mixed;
583
584    /**
585     * Output the image
586     *
587     * @return string
588     */
589    abstract public function __toString(): string;
590
591}