Completed
Push — master ( 5f5fca...2fa1e0 )
by Bjørn
12:28 queued 02:24
created

Stack   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Test Coverage

Coverage 63.44%

Importance

Changes 0
Metric Value
eloc 89
dl 0
loc 178
ccs 59
cts 93
cp 0.6344
rs 10
c 0
b 0
f 0
wmc 22

5 Methods

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