Completed
Push — master ( 95756a...125428 )
by Bjørn
05:21
created

Stack::doActualConvert()   F

Complexity

Conditions 16
Paths 800

Size

Total Lines 132
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 52.6432

Importance

Changes 0
Metric Value
cc 16
eloc 70
nc 800
nop 0
dl 0
loc 132
ccs 31
cts 65
cp 0.4769
crap 52.6432
rs 1.6777
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
// TODO: Quality option
4
5
namespace WebPConvert\Convert\Converters;
6
7
use WebPConvert\Convert\Converters\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
26 5
    protected function getOptionDefinitionsExtra()
27
    {
28
        return [
29
            [
30 5
                'converters',
31
                'array', [
32
                    'cwebp', 'vips', 'wpc', 'imagickbinary', 'ewww', 'imagick', 'gmagick', 'gmagickbinary', 'gd'
33
                ],
34
                true
35
            ],
36
            ['shuffle', 'boolean', false],
37
            ['prioritized-converters', 'array', []]
38
        ];
39
    }
40
41
    public static $availableConverters = ['cwebp', 'gd', 'imagick', 'gmagick', 'imagickbinary', 'wpc', 'ewww'];
42
    public static $localConverters = ['cwebp', 'gd', 'imagick', 'gmagick', 'imagickbinary', 'gmagickbinary'];
43
44 1
    public static function converterIdToClassname($converterId)
45
    {
46
        switch ($converterId) {
47 1
            case 'imagickbinary':
48
                $classNameShort = 'ImagickBinary';
49
                break;
50 1
            case 'gmagickbinary':
51
                $classNameShort = 'GmagickBinary';
52
                break;
53
            default:
54 1
                $classNameShort = ucfirst($converterId);
55
        }
56 1
        $className = 'WebPConvert\\Convert\\Converters\\' . $classNameShort;
57 1
        if (is_callable([$className, 'convert'])) {
58
            return $className;
59
        } else {
60 1
            throw new ConverterNotFoundException('There is no converter with id:' . $converterId);
61
        }
62
    }
63
64 2
    public static function getClassNameOfConverter($converterId)
65
    {
66 2
        if (strtolower($converterId) == $converterId) {
67 1
            return self::converterIdToClassname($converterId);
68
        }
69 1
        $className = $converterId;
70 1
        if (!is_callable([$className, 'convert'])) {
71
            throw new ConverterNotFoundException('There is no converter with class name:' . $className);
72
        }
73
74 1
        return $className;
75
    }
76
77
    /**
78
     * Check (general) operationality of imagack converter executable
79
     *
80
     * @throws SystemRequirementsNotMetException  if system requirements are not met
81
     */
82 3
    public function checkOperationality()
83
    {
84 3
        if (count($this->options['converters']) == 0) {
85 1
            throw new ConverterNotOperationalException(
86 1
                'Converter stack is empty! - no converters to try, no conversion can be made!'
87
            );
88
        }
89
90
        // TODO: We should test if all converters are found in order to detect problems early
91
92 2
        $this->logLn('Stack converter ignited');
93 2
    }
94
95 2
    protected function doActualConvert()
96
    {
97 2
        $options = $this->options;
98
99 2
        $beginTimeStack = microtime(true);
100
101
102
        // If we have set converter options for a converter, which is not in the converter array,
103
        // then we add it to the array
104
        /*
105
        if (isset($options['converter-options'])) {
106
            foreach ($options['converter-options'] as $converterName => $converterOptions) {
107
                if (!in_array($converterName, $options['converters'])) {
108
                    $options['converters'][] = $converterName;
109
                }
110
            }
111
        }*/
112
113
        //$this->logLn('converters: ' . print_r($options['converters'], true));
114
115 2
        $defaultConverterOptions = $options;
116
117 2
        unset($defaultConverterOptions['converters']);
118 2
        unset($defaultConverterOptions['converter-options']);
119 2
        $defaultConverterOptions['_skip_input_check'] = true;
120 2
        $defaultConverterOptions['_suppress_success_message'] = true;
121
122 2
        $anyRuntimeErrors = false;
123
124
        // prioritized-converters
125 2
        if (count($options['prioritized-converters']) > 0) {
126
            foreach (array_reverse($options['prioritized-converters']) as $prioritizedConverter) {
127
                foreach ($options['converters'] as $i => $converter) {
128
                    if (is_array($converter)) {
129
                        $converterId = $converter['converter'];
130
                    } else {
131
                        $converterId = $converter;
132
                    }
133
                    if ($converterId == $prioritizedConverter) {
134
                        //$this->logLn($i . ':' . $prioritizedConverter);
135
                        unset($options['converters'][$i]);
136
                        array_unshift($options['converters'], $converter);
137
                        break;
138
                    }
139
                }
140
            }
141
            // perhaps write the order to the log? (without options) - but this requires some effort
142
            //$this->logLn(print_r($options['converters']));
143
        }
144
145
        // shuffle
146 2
        if ($options['shuffle']) {
147
            shuffle($options['converters']);
148
        }
149
150 2
        foreach ($options['converters'] as $converter) {
151 2
            if (is_array($converter)) {
152
                $converterId = $converter['converter'];
153
                $converterOptions = $converter['options'];
154
            } else {
155 2
                $converterId = $converter;
156 2
                $converterOptions = [];
157 2
                if (isset($options['converter-options'][$converterId])) {
158
                    // Note: right now, converter-options are not meant to be used,
159
                    //       when you have several converters of the same type
160
                    $converterOptions = $options['converter-options'][$converterId];
161
                }
162
            }
163
164 2
            $converterOptions = array_merge($defaultConverterOptions, $converterOptions);
165
166 2
            $beginTime = microtime(true);
167
168 2
            $className = self::getClassNameOfConverter($converterId);
169
170
171
            try {
172 1
                $converterDisplayName = call_user_func(
173 1
                    [$className, 'getConverterDisplayName']
174
                );
175
            } catch (\Exception $e) {
176
                // TODO: handle failure better than this
177
                $converterDisplayName = 'Untitled converter';
178
            }
179
180
            try {
181 1
                $this->ln();
182 1
                $this->logLn('Trying: ' . $converterId, 'italic');
183
184 1
                call_user_func(
185 1
                    [$className, 'convert'],
186 1
                    $this->source,
187 1
                    $this->destination,
188 1
                    $converterOptions,
189 1
                    $this->logger
190
                );
191
192
                //self::runConverterWithTiming($converterId, $source, $destination, $converterOptions, false, $logger);
193
194 1
                $this->logLn($converterDisplayName . ' succeeded :)');
195 1
                return;
196
            } catch (ConverterNotOperationalException $e) {
197
                $this->logLn($e->getMessage());
198
            } catch (ConversionFailedException $e) {
199
                $this->logLn($e->getMessage(), 'italic');
200
                $prev = $e->getPrevious();
201
                if (!is_null($prev)) {
202
                    $this->logLn($prev->getMessage(), 'italic');
203
                    $this->logLn(' in ' . $prev->getFile() . ', line ' . $prev->getLine(), 'italic');
204
                    $this->ln();
205
                }
206
                //$this->logLn($e->getTraceAsString());
207
                $anyRuntimeErrors = true;
208
            } catch (ConversionSkippedException $e) {
209
                $this->logLn($e->getMessage());
210
            }
211
212
            $this->logLn($converterDisplayName . ' failed in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
213
        }
214
215
        $this->ln();
216
        $this->logLn('Stack failed in ' . round((microtime(true) - $beginTimeStack) * 1000) . ' ms');
217
218
        if ($anyRuntimeErrors) {
0 ignored issues
show
introduced by
The condition $anyRuntimeErrors is always false.
Loading history...
219
            // At least one converter failed
220
            throw new ConversionFailedException(
221
                'None of the converters in the stack could convert the image. ' .
222
                'At least one failed, even though its requirements seemed to be met.'
223
            );
224
        } else {
225
            // All converters threw a SystemRequirementsNotMetException
226
            throw new ConverterNotOperationalException('None of the converters in the stack are operational');
227
        }
228
    }
229
}
230