Your IP : 18.216.91.245


Current Path : /home/bitrix/ext_www/klimatlend.ua/optipic-cdn/optipic-cdn-php/
Upload File :
Current File : /home/bitrix/ext_www/klimatlend.ua/optipic-cdn/optipic-cdn-php/ImgUrlConverter.php

<?php

/**
 * OptiPic CDN library to convert image urls contains in html/text data
 *
 * @author optipic.io
 * @package https://github.com/optipic-io/optipic-cdn-php
 * @copyright (c) 2020, https://optipic.io
 * @license    GNU General Public License version 2 or later
 */

namespace optipic\cdn;

class ImgUrlConverter {
    
    /**
     * Library version number
     */
    const VERSION = '1.15';
    
    /**
     * ID of your site on CDN OptiPic.io service
     */
    public static $siteId = 0;
    
    /**
     * List of domains should replace to cdn.optipic.io
     */
    public static $domains = array();
    
    /**
     * List of URL exclusions - where is URL should not converted
     */
    public static $exclusionsUrl = array();
    
    /**
     * Whitelist of images URL - what should to be converted 
     */
    public static $whitelistImgUrls = array();
    
    public static $configFullPath = '';
    
    public static $adminKey = '';
    
    public static $srcsetAttrs = array();
    
    public static $baseUrl = false;
    
    public static $enableLog = false;
    
    /**
     * Constructor
     */
    public function __construct($config=array()) {
        if(is_array($config) && count($config)>0) {
            self::loadConfig($config);
        }
    }
    
    /**
     * Convert whole HTML-block contains image urls
     */
    public static function convertHtml($content) {
        
        $timeStart = microtime(true);
        
        //ini_set('pcre.backtrack_limit', 100000000);
        
        $content = self::removeBomFromUtf($content);
        
        // try auto load config from __DIR__.'config.php'
        if(empty(self::$siteId)) {
            self::loadConfig();
        }
        
        if(empty(self::$siteId)) {
            return $content;
        }
        
        if(!self::isEnabled()) {
            return $content;
        }
        
        $gziped = false;
        if(self::isGz($content)) {
            if($contentUngzip = gzdecode($content)) {
                $gziped = true;
                $content = $contentUngzip;
            }
        }
        
        self::$baseUrl = self::getBaseUrlFromHtml($content);
        if(self::$baseUrl) {
            self::$baseUrl = parse_url(self::$baseUrl, PHP_URL_PATH);
        }
        
        //if(self::isBinary($content)) {
        //    return $content;
        //}
        
        /*$domains = self::$domains;
        if(!is_array($domains)) {
            $domains = array();
        }
        $domains = array_merge(array(''), $domains);
        
        $hostsForRegexp = array();
        foreach($domains as $domain) {
            //$domain = str_replace(".", "\.", $domain);
            if($domain && stripos($domain, 'http://')!==0 && stripos($domain, 'https://')!==0) {
                $hostsForRegexp[] = 'http://'.$domain;
                $hostsForRegexp[] = 'https://'.$domain;
            }
            else {
                $hostsForRegexp[] = $domain;
            }
            
        }*/
        //foreach($hostsForRegexp as $host) {
            
            /*$firstPartsOfUrl = array();
            foreach(self::$whitelistImgUrls as $whiteImgUrl) {
                if(substr($whiteImgUrl, -1, 1)=='/') {
                    $whiteImgUrl = substr($whiteImgUrl, 0, -1);
                }
                $firstPartsOfUrl[] = preg_quote($host.$whiteImgUrl, '#');
            }
            if(count($firstPartsOfUrl)==0) {
                $firstPartsOfUrl[] = preg_quote($host, '#');
            }
            //var_dump($firstPartsOfUrl);
            //$host = preg_quote($host, '#');
            //var_dump(self::$whitelistImgUrls);
            
            $host = implode('|', $firstPartsOfUrl);
            var_dump($host);*/
            
            /*$firstPartsOfUrl = array();
            foreach(self::$whitelistImgUrls as $whiteImgUrl) {
                $firstPartsOfUrl[] = preg_quote($whiteImgUrl, '#');
            }
            if(empty($firstPartsOfUrl)) {
                $firstPartsOfUrl = array('/');
            }
            
            $firstPartOfUrl = implode('|', $firstPartsOfUrl);
            */
            
            //$host = preg_quote($host, '#');
            $host = '';
            
            //$firstPartOfUrl = '/';
            $firstPartOfUrl = '';
            
            // --------------------------------------------
            // <img srcset="">
            // @see https://developer.mozilla.org/ru/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images
            if(!empty(self::$srcsetAttrs)) {
                // srcset|data-srcset|data-wpfc-original-srcset
                $srcSetAttrsRegexp = array();
                foreach(self::$srcsetAttrs as $attr) {
                    $srcSetAttrsRegexp[] = preg_quote($attr, '#');
                }
                $srcSetAttrsRegexp = implode('|', $srcSetAttrsRegexp);
                //$content = preg_replace_callback('#<(?P<tag>[^\s]+)(?P<prefix>.*?)\s+(?P<attr>'.$srcSetAttrsRegexp.')=(?P<quote1>"|\')(?P<set>[^"]+?)(?P<quote2>"|\')(?P<suffix>[^>]*?)>#siS', array(__NAMESPACE__ .'\ImgUrlConverter', 'callbackForPregReplaceSrcset'), $content);
                $contentAfterReplace = preg_replace_callback('#<(?P<tag>source|img|picture)(?P<prefix>[^>]*)\s+(?P<attr>'.$srcSetAttrsRegexp.')=(?P<quote1>"|\')(?P<set>[^"\']+?)(?P<quote2>"|\')(?P<suffix>[^>]*)>#siS', array(__NAMESPACE__ .'\ImgUrlConverter', 'callbackForPregReplaceSrcset'), $content);
                if(!empty($contentAfterReplace)) {
                    $content = $contentAfterReplace;
                }
            }
            // --------------------------------------------
            
            //$regexp = '#("|\'|\()'.$host.'('.$firstPartOfUrl.'[^/"\'\s]{1}[^"\']*\.(png|jpg|jpeg){1}(\?.*?)?)("|\'|\))#siS';
            
            // from 1.10 version
            //$regexp = '#("|\'|\()'.$host.'('.$firstPartOfUrl.'[^"|\'|\)\(]+\.(png|jpg|jpeg){1}(\?.*?)?)("|\'|\))#siS';
            
            $urlBorders = array(
                array('"', '"'),   // "<url>"
                array('\'', '\''), // '<url>'
                array('\(', '\)'), // (<url>)
            );
            $regexp = array();
            foreach($urlBorders as $border) {
                $regexp[] = '#('.$border[0].')'.$host.'('.$firstPartOfUrl.'(?!\/\/cdn\.optipic\.io)[^'.$border[1].']+\.(png|jpg|jpeg){1}(\?[^"\'\s]*?)?)('.$border[1].')#siS';
            }
            //var_dump($regexp);exit;
            
            //$regexp = str_replace('//', '/');
            
            //$content = preg_replace($regexp, '${1}//cdn.optipic.io/site-'.self::$siteId.'${2}${5}', $content);
            $contentAfterReplace = preg_replace_callback($regexp, array(__NAMESPACE__ .'\ImgUrlConverter', 'callbackForPregReplace'), $content);
            if(!empty($contentAfterReplace)) {
                $content = $contentAfterReplace;
            }
            
        //}
        
        self::$baseUrl = false;
        
        if($gziped) {
            $content = gzencode($content);
            
            // modify Content-Length if it's already sent
            $headersList = self::getResponseHeadersList();
            if(is_array($headersList) && !empty($headersList['Content-Length'])) {
                header('Content-Length: ' . strlen($content));
            }
        }
        
        $timeEnd = microtime(true);
        self::log(($timeEnd-$timeStart), 'Conversion finished in (sec.):');
        
        return $content;
    }
    
    public static function trimList($list) {
        $trimmed = array();
        foreach ($list as $ind => $item) {
            $item = trim(str_replace(array("\r\n", "\n", "\r"), '', $item));
            if(!empty($item)) {
                $trimmed[] = $item;
            }
        }
        return $trimmed;
    }
    
    public static function textToArray($data) {
        if(is_array($data)) {
            $array = $data;
        }
        else {
            $array = explode("\n", $data); 
        }
        
        if(!is_array($array)) {
            $array = array();
        }
        $array = self::trimList($array);
        $array = array_unique($array);
        
        
        
        return $array;
    }
    
    /**
     * Load config from file or array
     */
    public static function loadConfig($source=false) {
        if($source===false) {
            $source = __DIR__ . '/config.php';
        }
        
        if(is_array($source)) {
            self::$siteId = $source['site_id'];
            
            self::$domains = self::textToArray($source['domains']);
            
            self::$exclusionsUrl = self::textToArray($source['exclusions_url']);
            
            self::$whitelistImgUrls = self::textToArray($source['whitelist_img_urls']);
            
            self::$srcsetAttrs = self::textToArray($source['srcset_attrs']);
            
            if(isset($source['admin_key'])) {
                self::$adminKey = $source['admin_key'];
            }
            
            if(isset($source['log'])) {
                if($source['log']) {
                    self::$enableLog = true;
                }
            }
        }
        elseif(file_exists($source)) {
            $config = require($source);
            if(is_array($config)) {
                self::$configFullPath = $source;
                self::loadConfig($config);
            }
        }
    }
    
    /**
     * Check if convertation enabled on current URL
     */
    public static function isEnabled() {
        $url = $_SERVER['REQUEST_URI'];
        if(in_array($url, self::$exclusionsUrl)) {
            return false;
        }
        // check rules with mask
        foreach(self::$exclusionsUrl as $exclUrl) {
            if(substr($exclUrl, -1)=='*') {
                $regexp = "#^".substr($exclUrl, 0, -1)."#i";
                if(preg_match($regexp, $url)) {
                    return false;
                }
            }
        }
        return true;
    }
    
    /**
     * Callback-function for preg_replace() to replace image URLs
     */
    public static function callbackForPregReplace($matches) {
        self::log($matches, 'callbackForPregReplace -> $matches');
        $replaceWithoutOptiPic = $matches[0];
        
        // skip images from json (json-encoded)
        if(stripos($replaceWithoutOptiPic, "\\/")!==false) {
            return $replaceWithoutOptiPic;
        }
        
        $urlOriginal = $matches[2];
        
        $parseUrl = parse_url($urlOriginal);
        
        if(!empty($parseUrl['host'])) {
            if(!in_array($parseUrl['host'], self::$domains)) {
                self::log($urlOriginal, 'callbackForPregReplace -> url original:');
                self::log($replaceWithoutOptiPic, 'callbackForPregReplace -> url with optipic:');
                return $replaceWithoutOptiPic;
            }
        }
        
        $ext = pathinfo($parseUrl['path'], PATHINFO_EXTENSION);
        if(!in_array($ext, array('png', 'jpg', 'jpeg'))) {
            return $replaceWithoutOptiPic; 
        }
        
        $urlOriginal = $parseUrl['path'];
        if(!empty($parseUrl['query'])) {
            $urlOriginal .= '?'.$parseUrl['query'];
        }
        $urlOriginal = self::getUrlFromRelative($urlOriginal, self::$baseUrl);
        
        
        $replaceWithOptiPic = $matches[1].'//cdn.optipic.io/site-'.self::$siteId.$urlOriginal.$matches[5];
        
        self::log($urlOriginal, 'callbackForPregReplace -> url original:');
        self::log($replaceWithOptiPic, 'callbackForPregReplace -> url with optipic:');
        
        if(substr($urlOriginal, 0, 7)=='http://') {
            return $replaceWithoutOptiPic;
        }
        if(substr($urlOriginal, 0, 8)=='https://') {
            return $replaceWithoutOptiPic;
        }
        if(substr($urlOriginal, 0, 2)=='//') {
            return $replaceWithoutOptiPic;
        }
        
        if(empty(self::$whitelistImgUrls)) {
            return $replaceWithOptiPic;
        }
        
        if(in_array($urlOriginal, self::$whitelistImgUrls)) {
            return $replaceWithOptiPic;
        }
        
        foreach(self::$whitelistImgUrls as $whiteUrl) {
            if(strpos($urlOriginal, $whiteUrl)===0) {
                return $replaceWithOptiPic;
            }
        }
        
        return $replaceWithoutOptiPic;
        
    }
    
    /**
     * Callback-function for preg_replace() to replace "srcset" attributes
     */
    public static function callbackForPregReplaceSrcset($matches) {
        $isConverted = false;
        $originalContent = $matches[0];
        
        $listConverted = array();
        
        $list = explode(",", $matches['set']);
        foreach($list as $item) {
            $source = preg_split("/[\s,]+/siS", trim($item));
            $url = trim($source[0]);
            $size = (isset($source[1]))? trim($source[1]): '';
            $toConvertUrl = "(".$url.")";
            $convertedUrl = self::convertHtml($toConvertUrl);
            if($toConvertUrl!=$convertedUrl) {
                $isConverted = true;
                $listConverted[] = trim(substr($convertedUrl, 1, -1).' '.$size);
            }
        }
        
        if($isConverted) {
            return '<'.$matches['tag'].$matches['prefix'].' '.$matches['attr'].'='.$matches['quote1'].implode(", ", $listConverted).$matches['quote2'].$matches['suffix'].'>';
        }
        else {
            return $originalContent;
        }
    }
    
    /*public static function isBinary($str) {
        return preg_match('~[^\x20-\x7E\t\r\n]~', $str) > 0;
    }*/
    
    /**
     * Remove UTF-8 BOM-symbol from text
     */
    public static function removeBomFromUtf($text) {
        $bom = pack('H*','EFBBBF');
        $text = preg_replace("/^$bom/", '', $text);
        return $text;
    }
    
    /**
     * Check if gziped data
     */
    public static function isGz($str) {
        if (strlen($str) < 2) return false;
        return (ord(substr($str, 0, 1)) == 0x1f && ord(substr($str, 1, 1)) == 0x8b);
    }
    
    public static function getUrlFromRelative($relativeUrl, $baseUrl=false) {
        if(substr($relativeUrl, 0, 1)=='/') {
            return $relativeUrl;
        }
        if(substr($relativeUrl, 0, 2)=='\/') { // for json-encoded urls when / --> \/
            return $relativeUrl;
        }
        
        if(!$baseUrl) {
            //$baseUrl = pathinfo($_SERVER['REQUEST_URI'], PATHINFO_DIRNAME);
            $baseUrl = self::getBaseDirOfUrl($_SERVER['REQUEST_URI']);
        }
        $baseUrl .= '/';
        $url = str_replace('//', '/', $baseUrl.$relativeUrl);
        return $url;
    }
    
    
    
    /**
     * Get main base path (dir) from full URL
     *
     * https://domain.com/catalog/catalog.php --> https://domain.com/catalog/
     */
    public static function getBaseDirOfUrl($url) {
        $urlParsed = parse_url($url);
        if(empty($urlParsed['path'])) {
            return '/';
        }
        $urlPath = $urlParsed['path'];
        $pathinfo = pathinfo($urlPath);
        if(!empty($pathinfo['extension'])) {
            return $pathinfo['dirname'];
        }
        return $urlPath;
    }
    
    public static function getBaseUrlFromHtml($html) {
        preg_match('#(?P<tag>base)(?P<prefix>[^>]*)\s+href=(?P<base_url>[^>\s]+)#isS', $html, $matches);
        
        $baseUrl = false;
        if(!empty($matches['base_url'])) {
            $baseUrl = trim($matches['base_url'], '"/');
            $baseUrl = trim($baseUrl, "'");
            $baseUrl = self::getBaseDirOfUrl($baseUrl);
            if(strlen($baseUrl)>0 && substr($baseUrl, -1, 1)!='/') {
                $baseUrl .= '/';
            }
        }
        return $baseUrl;
    }
    
    public static function getResponseHeadersList() {
        $list = array();
        
        $headersList = headers_list();
        if(is_array($headersList)) {
            foreach($headersList as $row) {
                list($headerKey, $headerValue) = explode(":", $row);
                $headerKey = trim($headerKey);
                $headerValue = trim($headerValue);
                $list[$headerKey] = $headerValue;
            }
        }
        
        return $list;
    }
    
    
    
    /**
     * Log debug info into file
     */
    public static function log($data, $comment='') {
        if(!self::$enableLog) {
            return;
        }
        
        $date = \DateTime::createFromFormat('U.u', microtime(true));
        if(!$date) {
            $date = new \DateTime();
        }
        $dateFormatted = $date->format("Y-m-d H:i:s u");
        
        $line = "[$dateFormatted] {$_SERVER['REQUEST_URI']}\n";
        if($comment) {
            $line .= "# ".$comment."\n";
        }
        $line .= var_export($data, true)."\n";
        file_put_contents(__DIR__ . '/log.txt', $line, FILE_APPEND);
    }
    
    
    
    public static function getDefaultSettings($settingKey=false) {
        $settings = array(
            'srcset_attrs' => array(
                'srcset', 
                'data-srcset',
            ),
            'domains' => array(),
        );
        
        if($currentDomain = self::getCurrentDomain(true)) {
            $settings['domains'] = array(
                $currentDomain,
                'www.'.$currentDomain,
            );
        }
        
        if($settingKey) {
            return (!empty($settings[$settingKey]))? $settings[$settingKey]: '';
        }
        
        return $settings;
    }
    
    
    public static function getCurrentDomain($trimWww=false) {
        if(empty($_SERVER['HTTP_HOST'])) {
            return false;
        }
        
        $currentHost = explode(":", $_SERVER['HTTP_HOST']);
        $currentHost = trim($currentHost[0]);
        if($trimWww) {
            if(stripos($currentHost, 'www.')===0) {
                $currentHost = substr($currentHost, 4);
            }
        }
        
        return $currentHost;
    }
}
?>