From 0cb87eb47cd027cf9b2e8ffc9dd1dee690f29098 Mon Sep 17 00:00:00 2001 From: rosell-dk Date: Tue, 18 May 2021 14:26:50 +0200 Subject: [PATCH] Postpone hash-check for supplied cwebp binaries till needed. Closes #287 --- src/Convert/Converters/Cwebp.php | 285 +++++++++++++++++++------------ 1 file changed, 179 insertions(+), 106 deletions(-) diff --git a/src/Convert/Converters/Cwebp.php b/src/Convert/Converters/Cwebp.php index accf062..3ab397b 100644 --- a/src/Convert/Converters/Cwebp.php +++ b/src/Convert/Converters/Cwebp.php @@ -58,40 +58,44 @@ public function getUniqueOptions($imageType) // 2: Set permission to 775. 755 causes unzipping to fail on some hosts private static $suppliedBinariesInfo = [ 'WINNT' => [ - ['cwebp-120-windows-x64.exe', '2849fd06012a9eb311b02a4f8918ae4b16775693bc21e95f4cc6a382eac299f9'], + ['cwebp-120-windows-x64.exe', '2849fd06012a9eb311b02a4f8918ae4b16775693bc21e95f4cc6a382eac299f9', '1.2.0'], // Keep the 1.1.0 version a while, in case some may have problems with the 1.2.0 version - ['cwebp-110-windows-x64.exe', '442682869402f92ad2c8b3186c02b0ea6d6da68d2f908df38bf905b3411eb9fb'], + ['cwebp-110-windows-x64.exe', '442682869402f92ad2c8b3186c02b0ea6d6da68d2f908df38bf905b3411eb9fb', '1.1.0'], ], 'Darwin' => [ - ['cwebp-110-mac-10_15', 'bfce742da09b959f9f2929ba808fed9ade25c8025530434b6a47d217a6d2ceb5'], + ['cwebp-110-mac-10_15', 'bfce742da09b959f9f2929ba808fed9ade25c8025530434b6a47d217a6d2ceb5', '1.1.0'], ], 'SunOS' => [ // Got this from ewww Wordpress plugin, which unfortunately still uses the old 0.6.0 versions // Can you help me get a 1.0.3 version? - ['cwebp-060-solaris', '1febaffbb18e52dc2c524cda9eefd00c6db95bc388732868999c0f48deb73b4f'] + ['cwebp-060-solaris', '1febaffbb18e52dc2c524cda9eefd00c6db95bc388732868999c0f48deb73b4f', '0.6.0'] ], 'FreeBSD' => [ // Got this from ewww Wordpress plugin, which unfortunately still uses the old 0.6.0 versions // Can you help me get a 1.0.3 version? - ['cwebp-060-fbsd', 'e5cbea11c97fadffe221fdf57c093c19af2737e4bbd2cb3cd5e908de64286573'] + ['cwebp-060-fbsd', 'e5cbea11c97fadffe221fdf57c093c19af2737e4bbd2cb3cd5e908de64286573', '0.6.0'] ], 'Linux' => [ // PS: Some experience the following error with 1.20: // /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found // (see #278) - ['cwebp-120-linux-x86-64', 'f1b7dc03e95535a6b65852de07c0404be4dba078af48369f434ee39b2abf8f4e'], + ['cwebp-120-linux-x86-64', 'f1b7dc03e95535a6b65852de07c0404be4dba078af48369f434ee39b2abf8f4e', '1.2.0'], // As some experience the an error with 1.20 (see #278), we keep the 1.10 - ['cwebp-110-linux-x86-64', '1603b07b592876dd9fdaa62b44aead800234c9474ff26dc7dd01bc0f4785c9c6'], + ['cwebp-110-linux-x86-64', '1603b07b592876dd9fdaa62b44aead800234c9474ff26dc7dd01bc0f4785c9c6', '1.1.0'], // Statically linked executable // It may be that it on some systems works, where the dynamically linked does not (see #196) - ['cwebp-103-linux-x86-64-static', 'ab96f01b49336da8b976c498528080ff614112d5985da69943b48e0cb1c5228a'], + [ + 'cwebp-103-linux-x86-64-static', + 'ab96f01b49336da8b976c498528080ff614112d5985da69943b48e0cb1c5228a', + '1.0.3' + ], // Old executable for systems in case all of the above fails - ['cwebp-061-linux-x86-64', '916623e5e9183237c851374d969aebdb96e0edc0692ab7937b95ea67dc3b2568'], + ['cwebp-061-linux-x86-64', '916623e5e9183237c851374d969aebdb96e0edc0692ab7937b95ea67dc3b2568', '0.6.1'], ] ]; @@ -147,21 +151,13 @@ private function executeBinary($binary, $commandOptions, $useNice) //$logger->logLn('command options:' . $commandOptions); $this->logLn('Trying to convert by executing the following command:'); - $beginExecuteBinaryTime = 0; - if (function_exists('microtime')) { - $beginExecuteBinaryTime = microtime(true); - } + $startExecuteBinaryTime = self::startTimer(); + ; $this->logLn($command); exec($command, $output, $returnCode); $this->logExecOutput($output); - if (function_exists('microtime')) { - $endExecuteBinaryTime = microtime(true); - $seconds = ($endExecuteBinaryTime - $beginExecuteBinaryTime); - $this->logLn( - 'Executing cwebp binary took: ' . round(($seconds * 1000)) . ' microseconds' - ); - $this->logLn(''); - } + $this->logTimeSpent($startExecuteBinaryTime, 'Executing cwebp binary took: '); + $this->logLn(''); /* if ($returnCode == 255) { if (isset($output[0])) { @@ -364,12 +360,58 @@ private function createCommandLineOptions($version) return $commandOptions; } + private function checkHashForSuppliedBinary($binaryFile, $hash) + { + // File exists, now generate its hash + // hash_file() is normally available, but it is not always + // - https://stackoverflow.com/questions/17382712/php-5-3-20-undefined-function-hash + // If available, validate that hash is correct. + + if (function_exists('hash_file')) { + $this->logLn( + 'Checking checksum for supplied binary: ' . $binaryFile + ); + $beginHashCheckTime = 0; + if (function_exists('microtime')) { + $beginHashCheckTime = microtime(true); + } + + $binaryHash = hash_file('sha256', $binaryFile); + + if ($binaryHash != $hash) { + $this->logLn( + 'Binary checksum of supplied binary is invalid! ' . + 'Did you transfer with FTP, but not in binary mode? ' . + 'File:' . $binaryFile . '. ' . + 'Expected checksum: ' . $hash . '. ' . + 'Actual checksum:' . $binaryHash . '.', + 'bold' + ); + return false; + ; + } + + $endHashCheckTime = 0; + if (function_exists('microtime')) { + $endHashCheckTime = microtime(true); + $seconds = ($endHashCheckTime - $beginHashCheckTime); + $this->logLn( + 'Checksum test took: ' . round(($seconds * 1000)) . ' microseconds' + ); + } + } + return true; + } + /** - * Get path for supplied binary for current OS - and validate hash. + * Get supplied binary info for current OS. + * paths are made absolute and checked. Missing are removed * - * @return array Array of supplied binaries (which actually exists, and where hash validates) + * @return array Two arrays. + * First array: array of files (absolute paths) + * Second array: array of info objects (absolute path, hash and version) */ - private function getSuppliedBinaryPathForOS() + private function getSuppliedBinaryInfoForCurrentOS() { $this->log('Checking if we have a supplied precompiled binary for your OS (' . PHP_OS . ')... '); @@ -380,7 +422,8 @@ private function getSuppliedBinaryPathForOS() return []; } - $result = []; + $filesFound = []; + $info = []; $files = self::$suppliedBinariesInfo[PHP_OS]; if (count($files) == 1) { $this->logLn('We do.'); @@ -388,7 +431,7 @@ private function getSuppliedBinaryPathForOS() $this->logLn('We do. We in fact have ' . count($files)); } - foreach ($files as $i => list($file, $hash)) { + foreach ($files as $i => list($file, $hash, $version)) { //$file = $info[0]; //$hash = $info[1]; @@ -409,47 +452,10 @@ private function getSuppliedBinaryPathForOS() continue; } $binaryFile = $realPathResult; - - // File exists, now generate its hash - // hash_file() is normally available, but it is not always - // - https://stackoverflow.com/questions/17382712/php-5-3-20-undefined-function-hash - // If available, validate that hash is correct. - - if (function_exists('hash_file')) { - $this->logLn( - 'Checking checksum for supplied binary: ' . $file - ); - $beginHashCheckTime = 0; - if (function_exists('microtime')) { - $beginHashCheckTime = microtime(true); - } - - $binaryHash = hash_file('sha256', $binaryFile); - - if ($binaryHash != $hash) { - $this->logLn( - 'Binary checksum of supplied binary is invalid! ' . - 'Did you transfer with FTP, but not in binary mode? ' . - 'File:' . $binaryFile . '. ' . - 'Expected checksum: ' . $hash . '. ' . - 'Actual checksum:' . $binaryHash . '.', - 'bold' - ); - continue; - } - - $endHashCheckTime = 0; - if (function_exists('microtime')) { - $endHashCheckTime = microtime(true); - $seconds = ($endHashCheckTime - $beginHashCheckTime); - $this->logLn( - 'Checksum test took: ' . round(($seconds * 1000)) . ' microseconds' - ); - } - } - $result[] = $binaryFile; + $filesFound[] = $realPathResult; + $info[] = [$realPathResult, $hash, $version]; } - return $result; + return [$filesFound, $info]; } private function who() @@ -525,12 +531,12 @@ private function detectVersions($binaries) return ['detected' => $binariesWithVersions, 'failed' => $binariesWithFailCodes]; } - private function logBinariesFound($binaries) + private function logBinariesFound($binaries, $startTime) { if (count($binaries) == 0) { - $this->logLn('Found 0 binaries'); + $this->logLn('Found 0 binaries' . self::getTimeStr($startTime)); } else { - $this->logLn('Found ' . count($binaries) . ' binaries: '); + $this->logLn('Found ' . count($binaries) . ' binaries' . self::getTimeStr($startTime)); foreach ($binaries as $binary) { $this->logLn('- ' . $binary); } @@ -552,16 +558,55 @@ private function logDiscoverAction($optionName, $description) } } + private static function startTimer() + { + if (function_exists('microtime')) { + return microtime(true); + } else { + return 0; + } + } + + private static function readTimer($startTime) + { + if (function_exists('microtime')) { + $endTime = microtime(true); + $seconds = ($endTime - $startTime); + return round(($seconds * 1000)); + } else { + return 0; + } + } + + private static function getTimeStr($startTime, $pre = ' (spent ', $post = ')') + { + if (function_exists('microtime')) { + $ms = self::readTimer($startTime); + return $pre . $ms . ' ms' . $post; + } + return ''; + } + + private function logTimeSpent($startTime, $pre = 'Spent: ') + { + if (function_exists('microtime')) { + $ms = self::readTimer($startTime); + $this->logLn($pre . $ms . ' ms'); + } + } + + /** + * @return array Two arrays (in an array). + * First array: binaries found, + * Second array: supplied binaries info for current OS + */ private function discoverCwebpBinaries() { $this->logLn( 'Looking for cwebp binaries.' ); - $beginDiscoveryTime = 0; - if (function_exists('microtime')) { - $beginDiscoveryTime = microtime(true); - } + $startDiscoveryTime = self::startTimer(); $binaries = []; @@ -577,15 +622,17 @@ private function discoverCwebpBinaries() } if ($this->options['try-cwebp']) { + $startTime = self::startTimer(); $this->logLn( 'Discovering if a plain cwebp call works (to skip this step, disable the "try-cwebp" option)' ); $result = $this->detectVersion('cwebp'); if (gettype($result) == 'string') { - $this->logLn('We could get the version, so yes, a plain cwebp call works'); + $this->logLn('We could get the version, so yes, a plain cwebp call works ' . + '(spent ' . self::readTimer($startTime) . ' ms)'); $binaries[] = 'cwebp'; } else { - $this->logLn('Nope a plain cwebp call does not work'); + $this->logLn('Nope a plain cwebp call does not work' . self::getTimeStr($startTime)); } } else { $this->logLn( @@ -595,40 +642,40 @@ private function discoverCwebpBinaries() } // try-discovering-cwebp + $startTime = self::startTimer(); $this->logDiscoverAction('try-discovering-cwebp', 'using "which -a cwebp" command.'); if ($this->options['try-discovering-cwebp']) { $moreBinaries = BinaryDiscovery::discoverInstalledBinaries('cwebp'); - $this->logBinariesFound($moreBinaries); + $this->logBinariesFound($moreBinaries, $startTime); $binaries = array_merge($binaries, $moreBinaries); } // 'try-common-system-paths' + $startTime = self::startTimer(); $this->logDiscoverAction('try-common-system-paths', 'by peeking in common system paths'); if ($this->options['try-common-system-paths']) { $moreBinaries = BinaryDiscovery::discoverInCommonSystemPaths('cwebp'); - $this->logBinariesFound($moreBinaries); + $this->logBinariesFound($moreBinaries, $startTime); $binaries = array_merge($binaries, $moreBinaries); } // try-supplied-binary-for-os + $suppliedBinariesInfo = [[], []]; + $startTime = self::startTimer(); $this->logDiscoverAction('try-supplied-binary-for-os', 'which are distributed with the webp-convert library'); if ($this->options['try-supplied-binary-for-os']) { - $moreBinaries = $this->getSuppliedBinaryPathForOS(); - $this->logBinariesFound($moreBinaries); - $binaries = array_merge($binaries, $moreBinaries); + $suppliedBinariesInfo = $this->getSuppliedBinaryInfoForCurrentOS(); + $moreBinaries = $suppliedBinariesInfo[0]; + $this->logBinariesFound($moreBinaries, $startTime); + //$binaries = array_merge($binaries, $moreBinaries); } - $endDiscoveryTime = 0; if (function_exists('microtime')) { - $endDiscoveryTime = microtime(true); - $seconds = ($endDiscoveryTime - $beginDiscoveryTime); - $this->logLn( - 'Discovering cwebp binaries took: ' . round(($seconds * 1000)) . ' microseconds' - ); - $this->logLn(''); + $this->logTimeSpent($startDiscoveryTime, 'Discovering cwebp binaries took: '); } + $this->logLn(''); - return array_values(array_unique($binaries)); + return [array_values(array_unique($binaries)), $suppliedBinariesInfo]; } /** @@ -672,6 +719,7 @@ private function tryCwebpBinary($binary, $version, $useNice) */ private function composeMeaningfullErrorMessageNoVersionsWorking($versions) { + // TODO: Take "supplied" into account // PS: array_values() is used to reindex $uniqueFailCodes = array_values(array_unique(array_values($versions['failed']))); @@ -708,8 +756,12 @@ private function composeMeaningfullErrorMessageNoVersionsWorking($versions) protected function doActualConvert() { - $binaries = $this->discoverCwebpBinaries(); - if (count($binaries) == 0) { + list($foundBinaries, $suppliedBinariesInfo) = $this->discoverCwebpBinaries(); + $suppliedBinaries = $suppliedBinariesInfo[0]; + $allBinaries = array_merge($foundBinaries, $suppliedBinaries); + + //$binaries = $this->discoverCwebpBinaries(); + if (count($allBinaries) == 0) { $this->logLn('No cwebp binaries found!'); $discoverOptions = [ @@ -735,30 +787,46 @@ protected function doActualConvert() ); } } - $this->logLn( - 'Detecting versions of the cwebp binaries found' - ); - $beginDetectionTime = 0; - if (function_exists('microtime')) { - $beginDetectionTime = microtime(true); - } - $versions = $this->detectVersions($binaries); - if (function_exists('microtime')) { - $endDetectionTime = microtime(true); - $seconds = ($endDetectionTime - $beginDetectionTime); + + $detectedVersions = []; + if (count($foundBinaries) > 0) { $this->logLn( - 'Detecting versions took: ' . round(($seconds * 1000)) . ' microseconds' + 'Detecting versions of the cwebp binaries found' . + (count($suppliedBinaries) > 0 ? ' (except supplied binaries)' : '.') ); + $startDetectionTime = self::startTimer(); + if (function_exists('microtime')) { + $beginDetectionTime = microtime(true); + } + $versions = $this->detectVersions($foundBinaries); + $detectedVersions = $versions['detected']; + + $this->logTimeSpent($startDetectionTime, 'Detecting versions took: '); } + //$suppliedVersions = []; + $suppliedBinariesHash = []; + + $binaryVersions = $detectedVersions; + foreach ($suppliedBinariesInfo[1] as list($path, $hash, $version)) { + $binaryVersions[$path] = $version; + $suppliedBinariesHash[$path] = $hash; + } + + //$binaryVersions = array_merge($detectedVersions, $suppliedBinariesInfo); + + // TODO: reimplement + /* + $versions['supplied'] = $suppliedBinariesInfo; + $binaryVersions = $versions['detected']; - if (count($binaryVersions) == 0) { - // No working cwebp binaries found. + if ((count($binaryVersions) == 0) && (count($suppliedBinaries) == 0)) { + // No working cwebp binaries found, no supplied binaries found throw new SystemRequirementsNotMetException( $this->composeMeaningfullErrorMessageNoVersionsWorking($versions) ); - } + }*/ // Sort binaries so those with highest numbers comes first arsort($binaryVersions); @@ -771,12 +839,17 @@ protected function doActualConvert() // Execute! $this->logLn( - 'Starting conversion, using the first of these. If that should fail (it should not), ' . + 'Starting conversion, using the first of these. If that should fail, ' . 'the next will be tried and so on.' ); $useNice = (($this->options['use-nice']) && self::hasNiceSupport()); $success = false; foreach ($binaryVersions as $binary => $version) { + if (isset($suppliedBinariesHash[$binary])) { + if (!$this->checkHashForSuppliedBinary($binary, $suppliedBinariesHash[$binary])) { + continue; + } + } if ($this->tryCwebpBinary($binary, $version, $useNice)) { $success = true; break;