Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
62.79% covered (warning)
62.79%
27 / 43
44.44% covered (danger)
44.44%
4 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImageMagick
62.79% covered (warning)
62.79%
27 / 43
44.44% covered (danger)
44.44%
4 / 9
60.83
0.00% covered (danger)
0.00%
0 / 1
 __construct
80.00% covered (warning)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
3.07
 getQuery
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setQuery
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __clone
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBinary
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
7.46
 getCommand
18.18% covered (danger)
18.18%
2 / 11
0.00% covered (danger)
0.00%
0 / 1
33.84
 execute
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
4.25
 raw
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 validProgram
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3/**
4 * Karla ImageMagick wrapper library
5 *
6 * PHP Version 8.0<
7 *
8 * @category Utility
9 * @author   Johannes Skov Frandsen <jsf@greenoak.dk>
10 * @license  http://www.opensource.org/licenses/mit-license.php MIT
11 * @link     https://github.com/localgod/karla Karla
12 * @since    2012-04-05
13 */
14
15declare(strict_types=1);
16
17namespace Karla\Program;
18
19use Karla\Query;
20use Karla\Cache;
21
22/**
23 * Class for wrapping ImageMagick arguments used by all tools
24 *
25 * @category Utility
26 * @author   Johannes Skov Frandsen <jsf@greenoak.dk>
27 * @license  http://www.opensource.org/licenses/mit-license.php MIT
28 * @link     https://github.com/localgod/karla Karla
29 */
30abstract class ImageMagick implements \Karla\Program
31{
32    /**
33     * ImageMagick tool animate
34     *
35     * @var string
36     */
37    public const IMAGEMAGICK_ANIMATE = 'animate';
38
39    /**
40     * ImageMagick tool compare
41     *
42     * @var string
43     */
44    public const IMAGEMAGICK_COMPARE = 'compare';
45
46    /**
47     * ImageMagick tool composite
48     *
49     * @var string
50     */
51    public const IMAGEMAGICK_COMPOSITE = 'composite';
52
53    /**
54     * ImageMagick tool
55     *
56     * @var string
57     */
58    public const IMAGEMAGICK_CONJURE = 'conjure';
59
60    /**
61     * ImageMagick tool conjure
62     *
63     * @var string
64     */
65    public const IMAGEMAGICK_CONVERT = 'convert';
66
67    /**
68     * ImageMagick tool display
69     *
70     * @var string
71     */
72    public const IMAGEMAGICK_DISPLAY = 'display';
73
74    /**
75     * ImageMagick tool identify
76     *
77     * @var string
78     */
79    public const IMAGEMAGICK_IDENTIFY = 'identify';
80
81    /**
82     * ImageMagick tool import
83     *
84     * @var string
85     */
86    public const IMAGEMAGICK_IMPORT = 'import';
87
88    /**
89     * ImageMagick tool mogrify
90     *
91     * @var string
92     */
93    public const IMAGEMAGICK_MOGRIFY = 'mogrify';
94
95    /**
96     * ImageMagick tool montage
97     *
98     * @var string
99     */
100    public const IMAGEMAGICK_MONTAGE = 'montage';
101
102    /**
103     * ImageMagick tool stream
104     *
105     * @var string
106     */
107    public const IMAGEMAGICK_STREAM = 'stream';
108
109    /**
110     * ImageMagick unified command (v7+)
111     *
112     * @var string
113     */
114    public const IMAGEMAGICK_MAGICK = 'magick';
115
116    /**
117     * Path to binaries
118     *
119     * @var string
120     */
121    public string $binPath;
122
123    /**
124     * ImageMagick major version
125     *
126     * @var int|null
127     */
128    protected int|null $version = null;
129
130    /**
131     * Name of binary
132     *
133     * @var string
134     */
135    protected string $bin;
136
137    /**
138     * Cache controller
139     *
140     * @var Cache
141     */
142    protected Cache|null $cache;
143
144    /**
145     * The current query
146     *
147     * @var Query
148     */
149    private Query $query;
150
151    /**
152     * Contructs a new program
153     *
154     * @param string $binPath
155     *            Path to binaries
156     * @param string $bin
157     *            Binary
158     * @param \Karla\Cache|null $cache
159     *            Cache controller (default null = no cache)
160     * @param int|null $version
161     *            ImageMagick major version (6 or 7)
162     *
163     * @throws \InvalidArgumentException
164     */
165    final public function __construct(
166        string $binPath,
167        string $bin,
168        \Karla\Cache|null $cache = null,
169        int|null $version = null
170    ) {
171        if ($binPath == '') {
172            throw new \InvalidArgumentException('Invalid bin path');
173        }
174        if ($bin == '') {
175            throw new \InvalidArgumentException('Invalid bin');
176        }
177        $this->binPath = $binPath;
178        $this->bin = $bin;
179        $this->cache = $cache;
180        $this->version = $version;
181        $this->query = new Query();
182        $this->getQuery()->reset();
183    }
184
185    /**
186     * Get the current query
187     */
188    public function getQuery(): Query
189    {
190        return $this->query;
191    }
192
193    /**
194     * Set the current query
195     *
196     * @param Query $query Query to set
197     */
198    public function setQuery(Query $query): void
199    {
200        $this->query = $query;
201    }
202
203    /**
204     * It should not be possible to clon the ImageMagick object.
205     */
206    final public function __clone(): void
207    {
208        throw new \BadMethodCallException("Clone is not allowed");
209    }
210
211    /**
212     * Get the binary executable path (for use in -list commands, etc.)
213     * This returns just the base binary without full command setup
214     */
215    public function getBinary(): string
216    {
217        // For ImageMagick 7+, use 'magick' instead of separate tools
218        if ($this->version !== null && $this->version >= 7) {
219            $isWindows = strtoupper(substr(PHP_OS, 0, 3)) == "WIN";
220            $magickBin = $isWindows ? self::IMAGEMAGICK_MAGICK . '.exe' : self::IMAGEMAGICK_MAGICK;
221            return $this->binPath . $magickBin;
222        }
223
224        return $this->binPath . $this->bin;
225    }
226
227    /**
228     * Get the command to run
229     */
230    public function getCommand(): string
231    {
232        // For ImageMagick 7+, use 'magick' instead of separate tools
233        if ($this->version !== null && $this->version >= 7) {
234            // Get the base command name without .exe extension
235            $command = str_replace('.exe', '', $this->bin);
236
237            // For convert, just use 'magick' (ImageMagick 7 combines convert functionality into magick)
238            // For identify and composite, use 'magick <subcommand>'
239            if ($command === self::IMAGEMAGICK_CONVERT) {
240                $isWindows = strtoupper(substr(PHP_OS, 0, 3)) == "WIN";
241                $magickBin = $isWindows ? self::IMAGEMAGICK_MAGICK . '.exe' : self::IMAGEMAGICK_MAGICK;
242                return $this->binPath . $magickBin;
243            } elseif (in_array($command, [self::IMAGEMAGICK_IDENTIFY, self::IMAGEMAGICK_COMPOSITE])) {
244                $isWindows = strtoupper(substr(PHP_OS, 0, 3)) == "WIN";
245                $magickBin = $isWindows ? self::IMAGEMAGICK_MAGICK . '.exe' : self::IMAGEMAGICK_MAGICK;
246                return $this->binPath . $magickBin . ' ' . $command;
247            }
248        }
249
250        return $this->binPath . $this->bin;
251    }
252
253    /**
254     * Execute the command
255     *
256     * @param bool $reset Reset the query
257     */
258    public function execute(bool $reset = true): string|object
259    {
260        $result = shell_exec($this->getCommand());
261        if ($reset) {
262            $this->getQuery()->reset();
263        }
264
265        return $result !== null && $result !== false ? $result : '';
266    }
267
268    /**
269     * Raw arguments directly to ImageMagick
270     *
271     * @param string $arguments Arguments
272     * @param bool $input Defaults to an input option, use false to use it as an output option
273     */
274    public function raw(string $arguments, bool $input = true): self
275    {
276        if ($input) {
277            $this->getQuery()->setInputOption($arguments);
278        } else {
279            $this->getQuery()->setOutputOption($arguments);
280        }
281        return $this;
282    }
283
284    /**
285     * Check if the input is a valid ImageMagick program
286     *
287     * @param string $program Program name
288     */
289    final public static function validProgram(string $program): bool
290    {
291        $class = new \ReflectionClass(__CLASS__);
292        $constants = $class->getConstants();
293        foreach ($constants as $constant) {
294            if ($constant == $program) {
295                return true;
296            }
297        }
298
299        return false;
300    }
301}