%PDF- %PDF-
Server IP : 37.220.80.31 / Your IP : 3.148.107.193 Web Server : Apache/2.4.52 (Ubuntu) System : Linux 3051455-guretool.twc1.net 5.15.0-107-generic #117-Ubuntu SMP Fri Apr 26 12:26:49 UTC 2024 x86_64 User : www-root ( 1010) PHP Version : 7.4.33 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority, MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/www-root/data/www/dev.artlot24.ru/bitrix/modules/dev2fun.imagecompress/lib/ |
Upload File : |
<?php /** * @author darkfriend <hi@darkfriend.ru> * @copyright dev2fun * @version 0.7.1 */ namespace Dev2fun\ImageCompress; use Bitrix\Main\Localization\Loc; use Bitrix\Main\Config\Option; IncludeModuleLangFile(__FILE__); class Convert { private $MODULE_ID = 'dev2fun.imagecompress'; public $LAST_ERROR; /** @var string[] support tag attributes */ public $supportAttrs = []; /** @var string[] */ public $convertMode = []; public $cacheTime = 3600; public static $supportContentType = [ 'image/jpeg', 'image/png', 'image/svg', ]; public static $convertModes = [ 'hitConvert', 'postConvert', ]; public static $convertClasses = [ 'cwebp' => '\Dev2fun\ImageCompress\Webp', 'phpWebp' => '\Dev2fun\ImageCompress\WebpConvertPhp', 'phpAvif' => '\Dev2fun\ImageCompress\AvifConvertPhp', 'imagickAvif' => '\Dev2fun\ImageCompress\AvifConvertImagick', ]; /** @var self */ private static $instance; /** @var bool state */ public static $enable = false; private function __construct() { static::$enable = Option::get($this->MODULE_ID, 'convert_enable', 'N') === 'Y'; $supportAttrs = Option::get($this->MODULE_ID, 'convert_attributes', []); if($supportAttrs) { $supportAttrs = \unserialize($supportAttrs, ['allowed_classes' => false]); } $this->supportAttrs = $supportAttrs; $convertMode = Option::get($this->MODULE_ID, 'convert_mode'); if($convertMode) { $convertMode = \unserialize($convertMode, ['allowed_classes' => false]); } else { $convertMode = ['postConvert']; } $this->convertMode = $convertMode; $this->cacheTime = Option::get($this->MODULE_ID, 'cache_time', 3600); } /** * @static * @return self */ public static function getInstance() { if (!isset(self::$instance)) { $c = __CLASS__; self::$instance = new $c; } return self::$instance; } /** * Get algorithm class * @param string $algorithm * @return null|Webp|WebpConvertPhp|AvifConvertImagick|AvifConvertPhp */ public static function getAlgInstance($algorithm) { $obj = null; switch ($algorithm) { case 'cwebp': $obj = \Dev2fun\ImageCompress\Webp::getInstance(); break; case 'phpWebp': $obj = \Dev2fun\ImageCompress\WebpConvertPhp::getInstance(); break; case 'imagickAvif': $obj = \Dev2fun\ImageCompress\AvifConvertImagick::getInstance(); break; case 'phpAvif': $obj = \Dev2fun\ImageCompress\AvifConvertPhp::getInstance(); break; } return $obj; // return self::$optiClasses[$algorithm]::getInstance(); // PHP7+ } /** * Get exclude pages * @return array */ public static function getSettingsExcludePage() { $pages = Option::get(\Dev2funImageCompress::MODULE_ID, 'exclude_pages'); if ($pages) { $pages = \json_decode($pages, true); } else { $pages = []; } array_unshift($pages, '#(\/bitrix\/.*)#'); return $pages; } /** * Get exclude files * @return array */ public static function getSettingsExcludeFiles() { $files = Option::get(\Dev2funImageCompress::MODULE_ID, 'exclude_files'); if ($files) { $files = \json_decode($files, true); } else { $files = []; } return $files; } /** * Save exclude pages * @param array $sFields * @return bool * @throws \Bitrix\Main\ArgumentOutOfRangeException */ public static function saveSettingsExcludePage($sFields = []) { if($sFields) { foreach ($sFields as $key => $field) { if (empty($field)) { unset($sFields[$key]); } } } elseif(!\is_array($sFields)) { $sFields = []; } Option::set( \Dev2funImageCompress::MODULE_ID, 'exclude_pages', \json_encode(\array_values($sFields)) ); return true; } /** * Check page on exclude * @return bool */ public static function isExcludePage() { global $APPLICATION; $arExcluded = self::getSettingsExcludePage(); if($arExcluded) { $curPage = $APPLICATION->GetCurPage(); if ($curPage === '/') { $curPage = 'index.php'; } if (in_array(ltrim($curPage, '/'), $arExcluded)) { return true; } foreach ($arExcluded as $exc) { if (preg_match($exc, $curPage)) { return true; } } } return false; } /** * Check page on exclude * @param string $file * @return bool */ public static function isExcludeFile($file) { $file = ltrim($file, '/'); $arExcluded = self::getSettingsExcludeFiles(); $arExcludedRegExp = array_filter($arExcluded, function($item) { return strpos($item, '#'); }); foreach ($arExcludedRegExp as $item) { if (preg_match($item, $file)) { return true; } } return in_array($file, $arExcluded); } /** * Save exclude files * @param array $sFields * @return bool * @throws \Bitrix\Main\ArgumentOutOfRangeException */ public static function saveSettingsExcludeFile($sFields = []) { if($sFields) { foreach ($sFields as $key => $field) { if (empty($field)) { unset($sFields[$key]); } } } elseif(!is_array($sFields)) { $sFields = []; } Option::set( \Dev2funImageCompress::MODULE_ID, 'exclude_files', json_encode(array_values($sFields)) ); return true; } /** * Start process convert * @param array $arFile * @param array $options * @return bool|null|string */ public function process($arFile, $options=[]) { if(!static::$enable) return false; $res = false; $event = new \Bitrix\Main\Event($this->MODULE_ID, "OnBeforeConvertImage", [&$arFile]); $event->send(); if($arFile === false) { return false; } if(!static::checkWebpSupport()) { return false; } if (!\in_array($arFile["CONTENT_TYPE"], static::$supportContentType)) { return false; } $alg = Option::get($this->MODULE_ID, 'convert_algorithm', 'phpWebp'); $algInstance = static::getAlgInstance($alg); if (!$algInstance->isOptim()) { $this->LAST_ERROR = Loc::getMessage('DEV2FUN_IMAGECOMPRESS_NO_MODULE', ['#MODULE#' => $alg]); return $res; } $upload_dir = Option::get('main', 'upload_dir', 'upload'); $res = "/$upload_dir/{$arFile["SUBDIR"]}/{$arFile["FILE_NAME"]}"; // исключение файла из списка исключений if (static::isExcludeFile($res)) { return false; } $strFilePath = $_SERVER["DOCUMENT_ROOT"] . $res; if (!\is_file($strFilePath)) { return false; } // $upload_dir = Option::get('main', 'upload_dir', 'upload'); // $src = "{$_SERVER["DOCUMENT_ROOT"]}/$upload_dir/{$arFile["SUBDIR"]}/{$arFile["FILE_NAME"]}"; // $srcWebp = "/{$upload_dir}/resize_cache/webp/{$arFile["SUBDIR"]}/{$arFile['FILE_NAME']}.webp"; return $algInstance->convert( $arFile, \array_merge( [ 'changeChmod' => $this->getChmod(Option::get($this->MODULE_ID, 'change_chmod', 777)), ], $options ) ); } /** * @param string[] $arFiles * @param array $options * @return array|false * @throws \Bitrix\Main\ArgumentNullException * @throws \Bitrix\Main\ArgumentOutOfRangeException * @throws \ErrorException */ public function postProcess($arFiles, $options=[]) { if(!static::$enable) return false; if(!static::checkWebpSupport()) { return false; } $alg = Option::get($this->MODULE_ID, 'convert_algorithm', 'phpWebp'); $algInstance = static::getAlgInstance($alg); if (!$algInstance->isOptim()) { $this->LAST_ERROR = Loc::getMessage('DEV2FUN_IMAGECOMPRESS_NO_MODULE', ['#MODULE#' => $alg]); return false; } $arFilesReplace = []; foreach ($arFiles as $file) { if($file) { $fileScheme = \parse_url($file, \PHP_URL_SCHEME); if($fileScheme==='data') { continue; } } $event = new \Bitrix\Main\Event($this->MODULE_ID, "OnBeforePostConvertImage", [&$file]); $event->send(); if(!$file) { continue; } // исключение файла из списка исключений if (static::isExcludeFile($file)) { continue; } $absFile = "{$_SERVER["DOCUMENT_ROOT"]}$file"; if(!\is_file($absFile)) continue; $fileInfo = \pathinfo($absFile); $arFile = [ 'CONTENT_TYPE' => \mime_content_type($absFile), 'SUBDIR' => \str_replace($_SERVER["DOCUMENT_ROOT"], '', $fileInfo['dirname']), 'FILE_NAME' => $fileInfo['basename'], 'ABS_PATH' => $absFile, ]; if (!\in_array($arFile["CONTENT_TYPE"], static::$supportContentType)) { continue; } if (!\is_file($absFile)) { continue; } $resFile = $algInstance->convert( $arFile, \array_merge( [ 'changeChmod' => $this->getChmod(Option::get($this->MODULE_ID, 'change_chmod', 777)), ], $options ) ); if($resFile) { $arFilesReplace[$file] = $resFile; } } return $arFilesReplace; } /** * Событие на ресайзе (OnAfterResizeImage) * @param array $arFile * @param array $arInfo * @param array $callbackData * @param string $cacheImageFile * @param string $cacheImageFileTmp * @param array $arImageSize * @return bool * @throws \ErrorException */ public static function CompressImageCacheOnConvertEvent( $arFile, $arInfo, &$callbackData, &$cacheImageFile, &$cacheImageFileTmp, &$arImageSize ) { if(!static::$enable) return false; if(self::isExcludePage()) return false; if(!static::checkWebpSupport()) return false; $urlFile = \parse_url($cacheImageFile); $uploadDir = Option::get('main', 'upload_dir', 'upload'); $urlFile['path'] = \str_replace("/$uploadDir/",'',$urlFile['path']); $urlFile['path'] = \str_replace("/{$arFile['FILE_NAME']}",'',$urlFile['path']); $arFileConvert = [ 'CONTENT_TYPE' => $arFile['CONTENT_TYPE'], 'SUBDIR' => $urlFile['path'], 'FILE_NAME' => $arFile['FILE_NAME'], ]; $event = new \Bitrix\Main\Event(self::getInstance()->MODULE_ID, "OnBeforeConvertImageResize", [&$arFileConvert]); $event->send(); if($arFile === false) { return false; } // исключение файла из списка исключений $resFile = "/$uploadDir/{$arFile["SUBDIR"]}/{$arFile["FILE_NAME"]}"; if (static::isExcludeFile($resFile)) { return false; } // исключение файла из-за типа файла if (!\in_array($arFileConvert["CONTENT_TYPE"], static::$supportContentType)) { return false; } $alg = Option::get(self::getInstance()->MODULE_ID, 'convert_algorithm', 'phpWebp'); $algInstance = static::getAlgInstance($alg); if (!$algInstance->isOptim()) { return false; } $strFilePath = $_SERVER["DOCUMENT_ROOT"] . $resFile; if (!\is_file($strFilePath)) { return false; } $webpPath = $algInstance->convert( $arFileConvert, [ 'changeChmod' => self::getInstance()->getChmod(Option::get(self::getInstance()->MODULE_ID, 'change_chmod', 777)), ] ); if($webpPath) { $cacheImageFile = $webpPath; return true; } return false; } /** * Compress image by fileID * @param integer $intFileID * @return bool|null */ public function convertImageByID($intFileID) { if(!\in_array('hitConvert', self::getInstance()->convertMode) || !self::$enable) { return false; } if(!$intFileID) { return null; } // исключение страницы из списка исключений if (self::isExcludePage()) { return false; } $arFile = \CFile::GetByID($intFileID)->GetNext(); // if ($this->enableImageResize) { // $this->resize($intFileID, $strFilePath); // } return $this->process($arFile); } /** * Resize image file * @param integer $fileId * @param string $strFilePath * @return bool */ public function resize($fileId, $strFilePath) { if(!static::$enable) return false; if (!$strFilePath) return false; $width = Option::get($this->MODULE_ID, 'resize_image_width'); $height = Option::get($this->MODULE_ID, 'resize_image_height'); $algorithm = Option::get($this->MODULE_ID, 'resize_image_algorithm'); if (!$algorithm) $algorithm = BX_RESIZE_IMAGE_PROPORTIONAL; $destinationFile = $_SERVER['DOCUMENT_ROOT'] . "/upload/{$this->MODULE_ID}/" . basename($strFilePath); $res = \CFile::ResizeImageFile( $strFilePath, $destinationFile, [ 'width' => $width, 'height' => $height, ], $algorithm ); if ($res) { chmod($destinationFile, 0777); copy($destinationFile, $strFilePath); $this->saveWidthHeight($fileId, $strFilePath); unlink($destinationFile); } return $res; } /** * Handler for OnGetFileSRC * @param array $arFile * @return bool|null */ public static function CompressImageOnConvertEvent($arFile) { if( !\in_array('hitConvert', self::getInstance()->convertMode) || !self::$enable ) { return false; } if(self::isExcludePage()) { return false; } return self::getInstance()->process($arFile); } public function getSupportAttributesString() { return $this->supportAttrs ? \trim(\implode('|', $this->supportAttrs)).'|' : ''; } /** * Handler for post converter * @param string $content * @return bool|null */ public static function PostConverterEvent(&$content) { if(!$content) { return $content; } if( !\in_array('postConvert', self::getInstance()->convertMode) || !self::$enable ) { return $content; } if(self::isExcludePage()) { return $content; } if(!static::checkWebpSupport()) { return $content; } // global $APPLICATION, $USER; $moduleId = self::getInstance()->MODULE_ID; // $curPage = $APPLICATION->GetCurPage(); // $domain = $_SERVER['HTTP_HOST']; // if (!$domain) $domain = \SITE_ID; // $obCache = new \CPHPCache(); // $cachePath = "/{$moduleId}/{$domain}/"; // $cacheId = \md5( // $domain // . $curPage // . \LANGUAGE_ID // . $_SERVER['HTTP_USER_AGENT'] // . $_SERVER['REQUEST_METHOD'] // . implode($_REQUEST) // . $USER->GetUserGroupString() // ); // $cacheTime = self::getInstance()->cacheTime; // if(!$cacheTime) $cacheTime = 3600; // if ($USER->IsAdmin() && !empty($_REQUEST['clear_cache'])) { // $obCache->Clean($cacheId, $cachePath); // } $arFileReplace = []; // if ($obCache->InitCache($cacheTime, $cacheId, $cachePath)) { // $cacheData = $obCache->GetVars(); // if(!empty($cacheData['files'])) { // $arFileReplace = $cacheData['files']; // } // } elseif ($obCache->StartDataCache()) { $arFiles = []; \preg_match_all('/([^"\'=\s]+\.(?:jpe?g|png))/mi', $content, $matchInlineImages); // \preg_match_all('/url\([\'|"](.*?(?:png|jpg|jpeg))[\'|"]\)/mi', $content, $matchInlineImages); if(!empty($matchInlineImages[1])) { $arFiles = $matchInlineImages[1]; } \preg_match_all('/url\(([^"\'=\s]+\.(?:jpe?g|png))\)/mi', $content, $matchInlineImages); if(!empty($matchInlineImages[1])) { $arFiles = \array_unique(\array_merge( $arFiles, $matchInlineImages[1] )); } // \preg_match_all( // '/(?:'.self::getInstance()->getSupportAttributesString().'src)=[\'|"](.*?(?:png|jpg|jpeg)?)[\'|"]/mi', // $content, // $matchTags // ); // if(!empty($matchTags[1])) { // \preg_match_all( // '/^(.*?\.(?:jpg|png|jpeg))(?:\?.*?|$)$/mi', // \implode(\PHP_EOL, $matchTags[1]), // $matchTagImages // ); // if(!empty($matchTagImages[1])) { // $arFiles = \array_merge( // $arFiles, // $matchTagImages[1] // ); // } // } $event = new \Bitrix\Main\Event($moduleId, "OnBeforePostConvertImage", [&$arFiles]); $event->send(); if($arFiles) { $arFileReplace = self::getInstance()->postProcess($arFiles); } // $obCache->EndDataCache([ // 'files' => $arFileReplace, // ]); // } if($arFileReplace) { $event = new \Bitrix\Main\Event( $moduleId, "OnBeforePostConvertReplaceImage", [&$arFileReplace] ); $event->send(); if($arFileReplace) { $content = \strtr($content, $arFileReplace); } } return $content; } /** * Get normalize file size * @param int $fileSize * @param int $digits * @return string */ public function getNiceFileSize($fileSize, $digits = 2) { $sizes = ["TB", "GB", "MB", "KB", "B"]; $total = \count($sizes); while ($total-- && $fileSize > 1024) { $fileSize /= 1024; } return \round($fileSize, $digits) . " " . $sizes[$total]; } /** * Get normalize chmod value * @param string|int $num * @return int */ public function getChmod($num) { if (!$num) return 0777; $num = \intval($num); switch ($num) { case 644: $num = 0644; break; case 660: $num = 0660; break; case 664: $num = 0664; break; case 666: $num = 0666; break; case 700: $num = 0700; break; case 744: $num = 0744; break; case 755: $num = 0755; break; case 775: $num = 0775; break; case 777: $num = 0777; break; default: $num = 0777; } return $num; } /** * Set state module * @param bool $enable */ public static function setEnable($enable) { static::$enable = $enable; } /** * Get current state module * @return bool */ public static function getEnable() { return static::$enable; } /** * Check current path on support webp * @return bool */ public static function checkSupportWebpCurrentPath() { global $APPLICATION; return !\preg_match('#\/bitrix\/admin\/#', $APPLICATION->GetCurPage()); } /** * Check header accept on support webp * @return bool */ public static function checkSupportWebpAccept() { return \strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false; } /** * Get result check webp support * @return bool */ public static function checkWebpSupport() { if(!static::checkSupportWebpCurrentPath()) { return false; } $supportBrowsers = [ 'chrome', 'opera', ]; $event = new \Bitrix\Main\Event( self::getInstance()->MODULE_ID, 'OnBeforeCheckWebpBrowserSupport', [&$supportBrowsers] ); $event->send(); $result = \in_array(self::getBrowserAgentName($_SERVER["HTTP_USER_AGENT"]), $supportBrowsers) || self::checkSupportWebpAccept(); $event = new \Bitrix\Main\Event(self::getInstance()->MODULE_ID, "OnAfterCheckWebpSupport", [$result]); $event->send(); if ($event->getResults()) { foreach ($event->getResults() as $evenResult) { if ($evenResult->getResultType() == \Bitrix\Main\EventResult::SUCCESS) { $result = (bool) $evenResult->getParameters(); } } } return (bool) $result; } /** * Get browser name * @param string $userAgent * @return string */ public static function getBrowserAgentName($userAgent) { $result = 'Other'; if(!$userAgent) { return $result; } $userAgent = \mb_strtolower($userAgent); switch ($userAgent) { case \strpos($userAgent, 'opera')!==false || \strpos($userAgent, 'opr/')!==false: $result = 'opera'; break; case \strpos($userAgent, 'edge')!==false: $result = 'edge'; break; case \strpos($userAgent, 'chrome')!==false: $result = 'chrome'; break; case \strpos($userAgent, 'safari')!==false: $result = 'safari'; break; case \strpos($userAgent, 'firefox')!==false: $result = 'firefox'; break; case \strpos($userAgent, 'msie')!==false || \strpos($userAgent, 'trident/7')!==false: $result = 'msie'; break; } return $result; } }