Passed
Push — master ( 6c41a6...c3c8a7 )
by Bjørn
02:45
created

Stack::converterIdToClassname()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.1825

Importance

Changes 0
Metric Value
cc 3
eloc 11
nc 4
nop 1
dl 0
loc 14
ccs 8
cts 11
cp 0.7272
crap 3.1825
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
// TODO: Quality option
4
5
namespace WebPConvert\Convert\Converters;
6
7
use WebPConvert\Convert\BaseConverters\AbstractConverter;
8
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\ConverterNotFoundException;
9
use WebPConvert\Convert\Exceptions\ConversionFailedException;
10
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
11
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
12
use WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException;
13
14
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
15
16
/**
17
 * Convert images to webp by trying a stack of converters until success.
18
 *
19
 * @package    WebPConvert
20
 * @author     Bjørn Rosell <[email protected]>
21
 * @since      Class available since Release 2.0.0
22
 */
23
class Stack extends AbstractConverter
24
{
25
    protected $processLosslessAuto = false;
26
    protected $supportsLossless = true;
27
28 8
    protected function getOptionDefinitionsExtra()
29
    {
30
        return [
31 8
            ['converters', 'array', ['cwebp', 'vips', 'gd', 'imagick', 'gmagick', 'imagickbinary'], true],
32 8
        ];
33
    }
34
35
    public static $availableConverters = ['cwebp', 'gd', 'imagick', 'gmagick', 'imagickbinary', 'wpc', 'ewww'];
36
    public static $localConverters = ['cwebp', 'gd', 'imagick', 'gmagick', 'imagickbinary'];
37
38 1
    public static function converterIdToClassname($converterId)
39
    {
40
        switch ($converterId) {
41 1
            case 'imagickbinary':
42
                $classNameShort = 'ImagickBinary';
43
                break;
44 1
            default:
45 1
                $classNameShort = ucfirst($converterId);
46 1
        }
47 1
        $className = 'WebPConvert\\Convert\\Converters\\' . $classNameShort;
48 1
        if (is_callable([$className, 'convert'])) {
49
            return $className;
50
        } else {
51 1
            throw new ConverterNotFoundException('There is no converter with id:' . $converterId);
52
        }
53
    }
54
55 5
    public static function getClassNameOfConverter($converterId)
56
    {
57 5
        if (strtolower($converterId) == $converterId) {
58 1
            return self::converterIdToClassname($converterId);
59
        }
60 4
        $className = $converterId;
61 4
        if (!is_callable([$className, 'convert'])) {
62
            throw new ConverterNotFoundException('There is no converter with class name:' . $className);
63
        }
64
65 4
        return $className;
66
    }
67
68
    /**
69
     * Check (general) operationality of imagack converter executable
70
     *
71
     * @throws SystemRequirementsNotMetException  if system requirements are not met
72
     */
73 6
    public function checkOperationality()
74
    {
75 6
        if (count($this->options['converters']) == 0) {
76 1
            throw new ConverterNotOperationalException(
77
                'Converter stack is empty! - no converters to try, no conversion can be made!'
78 1
            );
79
        }
80
81
        // TODO: We should test if all converters are found in order to detect problems early
82
83 5
        $this->logLn('Stack converter ignited');
84 5
    }
85
86 5
    protected function doActualConvert()
87
    {
88 5
        $options = $this->options;
89
90 5
        $beginTimeStack = microtime(true);
91
92
93
        // If we have set converter options for a converter, which is not in the converter array,
94
        // then we add it to the array
95
        /*
96
        if (isset($options['converter-options'])) {
97
            foreach ($options['converter-options'] as $converterName => $converterOptions) {
98
                if (!in_array($converterName, $options['converters'])) {
99
                    $options['converters'][] = $converterName;
100
                }
101
            }
102
        }*/
103
104
        //$this->logLn('converters: ' . print_r($options['converters'], true));
105
106 5
        $defaultConverterOptions = $options;
107
108 5
        unset($defaultConverterOptions['converters']);
109 5
        unset($defaultConverterOptions['converter-options']);
110 5
        $defaultConverterOptions['_skip_input_check'] = true;
111 5
        $defaultConverterOptions['_suppress_success_message'] = true;
112
113 5
        $anyRuntimeErrors = false;
114
115 5
        foreach ($options['converters'] as $converter) {
116 5
            if (is_array($converter)) {
117
                $converterId = $converter['converter'];
118
                $converterOptions = $converter['options'];
119
            } else {
120 5
                $converterId = $converter;
121 5
                $converterOptions = [];
122 5
                if (isset($options['converter-options'][$converterId])) {
123
                    // Note: right now, converter-options are not meant to be used,
124
                    //       when you have several converters of the same type
125
                    $converterOptions = $options['converter-options'][$converterId];
126
                }
127
            }
128
129 5
            $converterOptions = array_merge($defaultConverterOptions, $converterOptions);
130
131 5
            $beginTime = microtime(true);
132
133 5
            $className = self::getClassNameOfConverter($converterId);
134
135
136
            try {
137 4
                $converterDisplayName = call_user_func(
138 4
                    [$className, 'getConverterDisplayName']
139 4
                );
140 4
            } catch (\Exception $e) {
141
                // TODO: handle failure better than this
142
                $converterDisplayName = 'Untitled converter';
143
            }
144
145
            try {
146 4
                $this->ln();
147 4
                $this->logLn('Trying: ' . $converterId, 'italic');
148
149 4
                call_user_func(
150 4
                    [$className, 'convert'],
151 4
                    $this->source,
152 4
                    $this->destination,
153 4
                    $converterOptions,
154 4
                    $this->logger
155 4
                );
156
157
                //self::runConverterWithTiming($converterId, $source, $destination, $converterOptions, false, $logger);
158
159 3
                $this->logLn($converterDisplayName . ' succeeded :)');
160 3
                return;
161 1
            } catch (ConverterNotOperationalException $e) {
162
                $this->logLn($e->getMessage());
163 1
            } catch (ConversionFailedException $e) {
164 1
                $this->logLn($e->getMessage(), 'italic');
165 1
                $prev = $e->getPrevious();
166 1
                if (!is_null($prev)) {
167
                    $this->logLn($prev->getMessage(), 'italic');
168
                    $this->logLn(' in ' . $prev->getFile() . ', line ' . $prev->getLine(), 'italic');
169
                    $this->ln();
170
                }
171
                //$this->logLn($e->getTraceAsString());
172 1
                $anyRuntimeErrors = true;
173 1
            } catch (ConversionSkippedException $e) {
174
                $this->logLn($e->getMessage());
175
            }
176
177 1
            $this->logLn($converterDisplayName . ' failed in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
178 1
        }
179
180 1
        $this->ln();
181 1
        $this->logLn('Stack failed in ' . round((microtime(true) - $beginTimeStack) * 1000) . ' ms');
182
183 1
        if ($anyRuntimeErrors) {
0 ignored issues
show
introduced by
The condition $anyRuntimeErrors is always false.
Loading history...
184
            // At least one converter failed
185 1
            throw new ConversionFailedException(
186
                'None of the converters in the stack could convert the image. ' .
187
                'At least one failed, even though its requirements seemed to be met.'
188 1
            );
189
        } else {
190
            // All converters threw a SystemRequirementsNotMetException
191
            throw new ConverterNotOperationalException('None of the converters in the stack are operational');
192
        }
193
    }
194
}
195