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