Passed
Push — master ( 1ada49...6f88dc )
by Bjørn
02:29
created

Stack::getOptionDefinitionsExtra()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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