Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
5.13% covered (danger)
5.13%
2 / 39
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Karla
5.13% covered (danger)
5.13%
2 / 39
0.00% covered (danger)
0.00%
0 / 6
515.85
0.00% covered (danger)
0.00%
0 / 1
 perform
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 __construct
9.09% covered (danger)
9.09%
2 / 22
0.00% covered (danger)
0.00%
0 / 1
139.97
 raw
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
42
 convert
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 identify
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 composite
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
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    2010-06-05
13 */
14
15declare(strict_types=1);
16
17namespace Karla;
18
19/**
20 * Karla core class
21 *
22 * @category Utility
23 * @author   Johannes Skov Frandsen <jsf@greenoak.dk>
24 * @license  http://www.opensource.org/licenses/mit-license.php MIT
25 * @link     https://github.com/localgod/karla Karla
26 */
27class Karla
28{
29    /**
30     * Path to imagemagick binary
31     *
32     * @var string $binPath imagemagick binary
33     */
34    private string $binPath;
35
36    /**
37     * Cache controller
38     *
39     * @var Cache
40     */
41    private Cache|null $cache;
42
43    /**
44     * ImageMagick major version
45     *
46     * @var int|null
47     */
48    private int|null $version = null;
49
50    /**
51     * Get an instance of Karla.
52     *
53     * @param string $binPath Path to ImageMagick binaries (optional)
54     * @param Cache|null $cache Cache controller (optional)
55     *
56     * @deprecated Since 1.2.0, use `new Karla($binPath, $cache)` instead. Will be removed in 2.0.0.
57     *
58     * @throws \RuntimeException
59     */
60    public static function perform(string $binPath = '/opt/local/bin/', Cache|null $cache = null): Karla
61    {
62        try {
63            return new self($binPath, $cache);
64        } catch (\InvalidArgumentException $e) {
65            throw new \RuntimeException($e->getMessage() . ' (' . $binPath . ')', 0, $e);
66        }
67    }
68
69    /**
70     * Construct a new Karla object.
71     *
72     * @param string $binPath Path to ImageMagick binaries
73     * @param Cache|null $cache Cache controller
74     *
75     * @throws \InvalidArgumentException if path for ImageMagick is invalid
76     */
77    public function __construct(string $binPath, Cache|null $cache = null)
78    {
79        if (! file_exists($binPath)) {
80            throw new \InvalidArgumentException('Bin path not found');
81        }
82
83        // Detect ImageMagick version - try both v6 (convert) and v7 (magick)
84        $magickBin = Platform::getBinary('magick');
85        $convertBin = Platform::getBinary('convert');
86
87        $versionOutput = '';
88
89        // Try ImageMagick 7 first (magick command)
90        if (file_exists($binPath . $magickBin)) {
91            $versionOutput = shell_exec($binPath . $magickBin . ' -version');
92            if ($versionOutput && stripos($versionOutput, 'ImageMagick') !== false) {
93                // Extract major version (e.g., "Version: ImageMagick 7.1.2" -> 7)
94                if (preg_match('/ImageMagick (\d+)/', $versionOutput, $matches)) {
95                    $this->version = (int)$matches[1];
96                }
97            }
98        }
99
100        // Fallback to ImageMagick 6 (convert command)
101        if (empty($versionOutput) && file_exists($binPath . $convertBin)) {
102            $versionOutput = shell_exec($binPath . $convertBin . ' -version');
103            if ($versionOutput && stripos($versionOutput, 'ImageMagick') !== false) {
104                if (preg_match('/ImageMagick (\d+)/', $versionOutput, $matches)) {
105                    $this->version = (int)$matches[1];
106                } else {
107                    $this->version = 6; // Assume v6 if version not found but convert exists
108                }
109            }
110        }
111
112        if (empty($versionOutput)) {
113            throw new \InvalidArgumentException('ImageMagick could not be located at specified path');
114        }
115
116        // Set binPath appropriately for the OS
117        if (Platform::isWindows()) {
118            // On Windows, just use the direct path
119            $this->binPath = rtrim($binPath, '/\\') . '/';
120        } else {
121            // On Unix, use export PATH
122            $this->binPath = 'export PATH=$PATH:' . $binPath . ';';
123        }
124
125        $this->cache = $cache;
126    }
127
128    /**
129     * Run a raw ImageMagick command
130     *
131     * Karla was never intended to wrap all of the functionality of ImageMagick
132     * and likely never will, you will from time to time need to write arguments
133     * to ImageMagick like you would have done directly in the console.
134     *
135     * @param string $program ImageMagick tool to use
136     * @param string $arguments Arguments for the tool
137     *
138     * @throws \InvalidArgumentException if you try to run a non ImageMagick program
139     */
140    public function raw(string $program, string $arguments = ""): string
141    {
142        if (! Program\ImageMagick::validProgram($program)) {
143            throw new \InvalidArgumentException('ImageMagick could not be located at specified path');
144        }
145        $bin = Platform::getBinary($program);
146        // For ImageMagick 7+, prepend with magick command
147        if ($this->version !== null && $this->version >= 7) {
148            $magickBin = Platform::getBinary(Program\ImageMagick::IMAGEMAGICK_MAGICK);
149            $result = shell_exec($this->binPath . $magickBin . ' ' . $program . ' ' . $arguments);
150        } else {
151            $result = shell_exec($this->binPath . $bin . ' ' . $arguments);
152        }
153
154        return $result !== null && $result !== false ? $result : '';
155    }
156
157    /**
158     * Start a convert operation
159     */
160    public function convert(): Program\Convert
161    {
162        $bin = Platform::getBinary(Program\ImageMagick::IMAGEMAGICK_CONVERT);
163
164        return new Program\Convert($this->binPath, $bin, $this->cache, $this->version);
165    }
166
167    /**
168     * Start an identify operation
169     */
170    public function identify(): Program\Identify
171    {
172        $bin = Platform::getBinary(Program\ImageMagick::IMAGEMAGICK_IDENTIFY);
173
174        return new Program\Identify($this->binPath, $bin, $this->cache, $this->version);
175    }
176
177    /**
178     * Start a composite operation
179     */
180    public function composite(): Program\Composite
181    {
182        $bin = Platform::getBinary(Program\ImageMagick::IMAGEMAGICK_COMPOSITE);
183
184        return new Program\Composite($this->binPath, $bin, $this->cache, $this->version);
185    }
186}