Test Failed
Push — master ( 975525...307699 )
by Bjørn
02:39
created

Stack::createOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 9
ccs 0
cts 0
cp 0
crap 2
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\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
use WebPConvert\Options\BooleanOption;
14
use WebPConvert\Options\ArrayOption;
15
16
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
17
18
/**
19
 * Convert images to webp by trying a stack of converters until success.
20
 *
21
 * @package    WebPConvert
22
 * @author     Bjørn Rosell <[email protected]>
23
 * @since      Class available since Release 2.0.0
24
 */
25
class Stack extends AbstractConverter
26 5
{
27
28
    protected function getUnsupportedDefaultOptions()
29
    {
30 5
        return [];
31
    }
32
33
    protected function createOptions()
34
    {
35
        parent::createOptions();
36
37
        $this->options2->addOptions(
38
            new ArrayOption('converters', self::getAvailableConverters()),
39
            new BooleanOption('shuffle', false),
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $defaultValue of WebPConvert\Options\BooleanOption::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

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