Completed
Push — master ( 70a6e8...9554c6 )
by Bjørn
04:06
created

Stack   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 181
Duplicated Lines 0 %

Test Coverage

Coverage 53.33%

Importance

Changes 0
Metric Value
eloc 84
dl 0
loc 181
ccs 40
cts 75
cp 0.5333
rs 10
c 0
b 0
f 0
wmc 20

4 Methods

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