Passed
Branch master (145c86)
by Whallysson
01:22
created

Cwebp::convert()   A

Complexity

Conditions 5
Paths 10

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 12
nc 10
nop 0
dl 0
loc 23
rs 9.5555
c 1
b 0
f 0
1
<?php
2
3
namespace CodeBlog\ToWebP\Convert\Converters;
4
5
use CodeBlog\ToWebP\AbstractConverter;
6
use Exception;
7
8
/**
9
 * Class Cwebp
10
 *
11
 * @author Whallysson Avelino <https://github.com/whallysson>
12
 * @package CodeBlog\ToWebP\Convert\Converters
13
 */
14
class Cwebp extends AbstractConverter
15
{
16
    /**
17
     * System paths to look for cwebp binary
18
     *
19
     * @var array
20
     */
21
    private $defaultPaths = [
22
        '/usr/bin/cwebp',
23
        '/usr/local/bin/cwebp',
24
        '/usr/gnu/bin/cwebp',
25
        '/usr/syno/bin/cwebp',
26
    ];
27
28
    /**
29
     * OS-specific binaries included in this library
30
     *
31
     * @var mixed
32
     */
33
    private $binary = [
34
        'WinNT' => ['cwebp.exe', '49e9cb98db30bfa27936933e6fd94d407e0386802cb192800d9fd824f6476873'],
35
        'Darwin' => ['cwebp-mac12', 'a06a3ee436e375c89dbc1b0b2e8bd7729a55139ae072ed3f7bd2e07de0ebb379'],
36
        'SunOS' => ['cwebp-sol', '1febaffbb18e52dc2c524cda9eefd00c6db95bc388732868999c0f48deb73b4f'],
37
        'FreeBSD' => ['cwebp-fbsd', 'e5cbea11c97fadffe221fdf57c093c19af2737e4bbd2cb3cd5e908de64286573'],
38
        'Linux' => ['cwebp-linux', '916623e5e9183237c851374d969aebdb96e0edc0692ab7937b95ea67dc3b2568'],
39
    ][PHP_OS];
40
41
    /**
42
     * @return bool|mixed
43
     * @throws Exception
44
     */
45
    public function checkRequirements()
46
    {
47
        if (!function_exists('exec')) {
48
            throw new Exception('exec() is not enabled.');
49
        }
50
51
        return true;
52
    }
53
54
    /**
55
     * @return array
56
     * @throws Exception
57
     */
58
    public function setUpBinaries()
59
    {
60
        // Removes system paths if the corresponding binary doesn't exist
61
        $binaries = array_filter($this->defaultPaths, function($binary) {
62
            return file_exists($binary);
63
        });
64
65
        $binaryFile = __DIR__ . '/Binaries/' . $this->binary[0];
66
67
        // Throws an exception if binary file does not exist
68
        if (!file_exists($binaryFile)) {
69
            throw new Exception('Operating system is currently not supported: ' . PHP_OS);
70
        }
71
72
        // File exists, now generate its hash
73
        $binaryHash = hash_file('sha256', $binaryFile);
74
75
        // Throws an exception if binary file checksum & deposited checksum do not match
76
        if ($binaryHash != $this->binary[1]) {
77
            throw new Exception('Binary checksum is invalid.');
78
        }
79
80
        $binaries[] = $binaryFile;
81
82
        return $binaries;
83
    }
84
85
    /**
86
     * Checks if 'Nice' is available
87
     *
88
     * @return bool
89
     */
90
    public function hasNiceSupport()
91
    {
92
        exec("nice 2>&1", $niceOutput);
93
94
        if (is_array($niceOutput) && isset($niceOutput[0])) {
95
            if (preg_match('/usage/', $niceOutput[0]) || (preg_match('/^\d+$/', $niceOutput[0]))) {
96
                /*
97
                 * Nice is available - default niceness (+10)
98
                 * https://www.lifewire.com/uses-of-commands-nice-renice-2201087
99
                 * https://www.computerhope.com/unix/unice.htm
100
                 */
101
102
                return true;
103
            }
104
105
            return false;
106
        }
107
    }
108
109
    /**
110
     * @return bool|mixed
111
     */
112
    public function convert()
113
    {
114
        try {
115
            $this->checkRequirements();
116
            // Preparing array holding possible binaries
117
            $binaries = $this->setUpBinaries();
118
        } catch (Exception $e) {
119
            return false;
120
        }
121
122
        // lossless PNG conversion
123
        $lossless = ( $this->extension == 'png' ? '-lossless' : '' );
124
125
        // Metadata (all, exif, icc, xmp or none (default))
126
        $metadata = ( $this->strip ? '-metadata none' : '-metadata all' );
127
128
        $optionsArray = [ $lossless, '-q ' . $this->quality, '-m 6', $metadata, '-low_memory',
129
            $this->escapeFilename($this->source), '-o ' . $this->escapeFilename($this->destination), '2>&1',
130
        ];
131
132
        $options = implode(' ', array_filter($optionsArray));
133
        $nice = ( $this->hasNiceSupport() ? 'nice' : '' );
134
        return $this->toConvert($binaries, $nice, $options);
135
    }
136
137
    /**
138
     * @param array $binaries
139
     * @param string $nice
140
     * @param string $options
141
     * @return bool
142
     */
143
    private function toConvert(array $binaries, string $nice, string $options)
144
    {
145
        $success = false;
146
147
        // Try all paths
148
        foreach ($binaries as $index => $binary) {
149
            $command = $nice . ' ' . $binary . ' ' . $options;
150
            exec($command, $result, $returnCode);
151
152
            if ($returnCode == 0) { // Everything okay!
153
                // cwebp sets file permissions to 664 but instead ..
154
                $fileStatistics = stat(dirname($this->destination));
155
156
                // Apply same permissions as parent folder but strip off the executable bits
157
                $permissions = $fileStatistics['mode'] & 0000666;
158
                chmod($this->destination, $permissions);
159
160
                $success = true;
161
                break;
162
            }
163
        }
164
        return $success;
165
    }
166
}
167