Просмотр файла system/inc/classes/http.class.php

Размер файла: 25.59Kb
<?php
/*
    SHCMS_http является клиентом класса для протокола HTTP.
        
    Так же как и получение информации с сервера, могут взаимодействовать SHCMS_http
    с сервером через POST или GET. Поэтому он может быть использован как часть любой
    скрипт, который должен взаимодействовать с приложением, работающим на другом сайте.
        
    Он поддерживает основные аутентификации и куки (и таким образом сессий).

*/

class SHCMS_http {
        
        //Запрос переменные:     
        protected $host, $port, $path;
        protected $method;
		protected $http_link;
        protected $postdata = '';
        protected $cookies = array();
        protected $referer;
        protected $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*';
        protected $accept_encoding = 'gzip';
        protected $accept_language = 'ru-RU';
        protected $user_agent = 'SHCMS_agent';
        
        //Опции:
        protected $timeout = 20;
        protected $use_gzip = true;
        protected $persist_cookies = true;
        protected $persist_referers = true;
        protected $debug = false;
        protected $handle_redirects = true;
        protected $max_redirects = 5;
        protected $headers_only = false;
        
        //Основные переменные разрешения:      
        protected $username, $password;
        
        //Ответ переменные:
        protected $status;
        protected $headers = array();
        protected $content = '';
        protected $errormsg;
        
        protected $redirect_count = 0;
        
        //Конструкторов / деструкторов:
        
        public function __construct($link,$host, $port=80) {
                
                /*
                $host: веб-сервера
                $port: дополнительный номер порта
                $link: Обработка ссылки
				*/   
				$this->http_link = $link;
                $this->host = $host;
                $this->port = $port;          
        }
        
        public function __destruct() {
                foreach ($this as $index => $value) unset($this->$index);
        }
        
        public function __toString() {
                return $this->getContent();
        }
        
        //Запрос методов выполнения:
        public function get($path, $data = null) { 
                /*
                    Выполняет GET запрос для указанного пути.
                
                    Возвращает в случае успеха и в случае возникновения ошибки. Если ложь, ошибка
                    сообщение с описанием возникшей проблемы могут быть доступны
                    использованием GetError () метод.
                
                    $data: дополнительно - если указано, добавляет его в строку запроса как часть
                    запрос получите. $data могут быть массивом пар ключ-значение, в
                    этом случае соответствующий строке запроса будет построен.
                */
                
                $this->path = $path;
                $this->method = 'GET';
                
                if ($data) $this->path .= '?'.http_build_query($data);
                
                return $this->doRequest();       
        }
        
        public function post($path, $data) {
                
                /*
                    Выполняет POST запрос по указанному пути, отправки информации
                    указанные в $ данных.
                
                    Возвращает в случае успешного завершения или в случае возникновения ошибки. Если ложь, ошибка
                    сообщение с описанием возникшей проблемы могут быть доступны
                    использованием GetError () метод.
                
                    $data: дополнительно - массив пар ключ-значение, и в этом случае соответствия
                    Разместить спрос будет построен.
                */
                
                $this->path = $path;
                $this->method = 'POST';
                $this->postdata = http_build_query($data);
                
                $result = $this->doRequest();
                
                $this->postdata = null;
                
        }
        
        public function ok() {
                // Используйте это после get()  или post(), чтобы проверить состояние последнего запроса.
                // Возвращает истину, если статус был 200 OK - в противном случае возвращает ложь.
            return ($this->status == 200);
        }
        
        //Ответ доступа: 
        public function getContent() {
                // Возвращает содержимое HTTP ответ. Это, как правило HTML документа.
            return $this->content;
        }
        
        public function getStatus() {
                // Возвращает код состояния ответа - 200 означает, ОК, 404 средств файл не найден и т.д.
            return $this->status;
        }
        
        public function getHeaders() {
                //Возвращает HTTP заголовки возвращаемые сервером как ассоциативный массив.
            return $this->headers;
        }
		
		public function getName() {
		
		        $Files = @parse_url($this->http_link);
        return basename($Files['path']);
		}
        
        public function getHeader($header) {
                // Возвращает указанный в заголовке ответа, или ложным, если он не существует.
                $header = strtolower($header);
                if (isset($this->headers[$header])) {
                        return $this->headers[$header];
                } else {
                        return false;
                }
        }
        
        public function getError() {
                //Возвращает строку с описанием самой последней ошибки.
            return $this->errormsg;
        }
        
        public function getCookies($host = null) {
            return @$this->cookies[$host ? $host : $this->host];
                
        }
        
        public function getRequestURL() {
                // Возвращает полный URL, который был запрошен.
                $url = 'http://'.$this->host;
                if ($this->port != 80) {
                        $url .= ':'.$this->port;
                }            
                $url .= $this->path;
                return $url;
        }
        
        //Конфигурация методов:
        
        public function setUserAgent($string) {
                //Задает строку агента пользователя для использования в запросе.
                // По умолчанию "SHCMS_agent".
                $this->user_agent = $string;
        }
        
        public function setAuthorization($username, $password) {
                // Устанавливает HTTP имя пользователя и пароль разрешения для использования в запросах.
                // Внимание: не забудьте сбросить этот в последующие запросы на другие сервера!
                $this->username = $username;
                $this->password = $password;
        }
        
        public function setCookies($array, $replace = false) {
                if ($replace || !is_array(@$this->cookies[$this->host]))
                        $this->cookies[$this->host] = array();
                
                $this->cookies[$this->host] = ( $array + $this->cookies[$this->host] );
                
        }
        
        public function useGzip($boolean) {
                // Укажите, если клиент должен запросить GZIP закодированное содержимое с сервера -
                // это экономит пропускную способность, но может увеличить процессорного времени. По умолчанию включено.
                $this->use_gzip = $boolean;
        }
        
        public function setPersistCookies($boolean) {
                /*
                    Укажите, если клиент должен сохраняться печенья между запросами.
                    По умолчанию включено.
                
                    Примечание: Это в данный момент игнорируют печенья пути (и времени) полностью.
                    Время не важно, но путь мог привести к проблемам безопасности.
                */ 
                $this->persist_cookies = $boolean;
                
        }
        
        public function setPersistReferers($boolean) {
                // Укажите, если клиент должен использовать URL из предыдущего запроса как
                // Направление последующего запроса. По умолчанию включено.
                $this->persist_referers = $boolean;
        }
        
        public function setHandleRedirects($boolean) {
                // Укажите, если клиент должен автоматически следовать перенаправлены запросы.
                // По умолчанию включено.
                $this->handle_redirects = $boolean;
        }
        
        public function setMaxRedirects($num) {
                // Установить максимальное число перенаправлений допускается до клиента
                // сдается (в основном для предотвращения бесконечных циклов). По умолчанию 5 переадресацию.
                $this->max_redirects = $num;
        }
        
        public function setHeadersOnly($boolean) {
                // Если включен, клиент будет извлекать только заголовки со страницы.
                // Это может быть полезно для реализации вещей, как ссылка шашки.
                // По умолчанию отключена.
                $this->headers_only = $boolean;
        }
        
        public function setDebug($boolean) {
                // Включает отладочные сообщения в HTML выходе от клиента.
                // По умолчанию отключена.
                $this->debug = $boolean;
        }
        
        //Статические методы утилиты:
        public static function quickGet($url, $data = null) { 
                /*
                    Статический метод ярлык для быстрого создания и настройки нового
                    Экземпляр этого класса, а также выполнять GET запрос.
                
                    Благодаря строку-магии, вы можете использовать значения, возвращаемого этой
                    метод непосредственно в эхо заявление, если хотите.
                */
                
                $bits = parse_url($url);
                $host = $bits['host'];
                $port = isset($bits['port']) ? $bits['port'] : 80;
                $path = isset($bits['path']) ? $bits['path'] : '/';
                
                if (isset($bits['query']))
                        $path .= '?'.$bits['query'];
                
                $client = new SHCMS_http($host, $port);
                $client->get($path, $data);
                return $client;
                
        }
        
        public static function quickPost($url, $data) {
                
                // Как и в [SHCMS_http::quickGet ()], но выполняет POST запрос.
                
                $bits = parse_url($url);
                
                $host = $bits['host'];
                $port = isset($bits['port']) ? $bits['port'] : 80;
                $path = isset($bits['path']) ? $bits['path'] : '/';
                
                $client = new HttpClient($host, $port);
                $client->post($path, $data);
                return $client;
                
        }
        
        //Внутренние вспомогательные методы:
        
        protected function debug($msg, $object = false) {
                
                /*
                    Отображает внутреннее сообщение для отладки и устранения неполадок,
                    Если включена отладка.
                
                    Использование [SHCMS_http::setDebug ()], чтобы включить отладку.
                */
                
                if ($this->debug) {
                        
                        echo '<div style="border: 1px solid red; padding: 0.5em; margin: 0.5em;"><strong>SHCMS_http Debug:</strong> ' . $msg;
                        
                        if ($object)
                                echo '<pre>' . htmlspecialchars(print_r($object,true)) . '</pre>';
                        
                        echo '</div>';
                        
                }
                
        }
        
        protected function doRequest() {
                
                // Выполняет действительное запроса HTTP, возвращает истину в случае успеха, ложно в случае ошибки.
                
                if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) {
                        //Установить сообщение об ошибке:
                        switch($errno) {
                                case -3:
                                        $this->errormsg = 'Создание сокета не удалось (-3)';
                                case -4:
                                        $this->errormsg = 'Сбой поиска DNS(-4)';
                                case -5:
                                        $this->errormsg = 'Отказ в соединении или тайм-аут (-5)';
                                default:
                                        $this->errormsg = 'Сбой подключения ('.$errno.')';
                                        $this->errormsg .= ' '.$errstr;
                                        $this->debug($this->errormsg);
                        }
                        return false;
                }
                
                socket_set_timeout($fp, $this->timeout);
                
                $request = $this->buildRequest();
                $this->debug('Request', $request);
                fwrite($fp, $request);
                
                //Сброс всех переменных которые не должны существовать между запросами:
                
                $this->headers = array();
                $this->content = '';
                $this->errormsg = '';
                
                //Установка пару флагов:
                
                $inHeaders = true;
                $atStart = true;
                
                //Теперь начать читать обратно ответ:
                
                while (!feof($fp)) {
                        
                        $line = fgets($fp, 4096);
                        
                        if ($atStart) {
                                
                                //Сделка с первой строки возвращаемые данные:
                                $atStart = false;
                                
                                if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) {
                                        $this->errormsg = "Код состояния линии недействительными: ".htmlentities($line);
                                        $this->debug($this->errormsg);
                                        return false;
                                }
                                
                                $http_version = $m[1]; //Не используется
                                $this->status = $m[2];
                                $status_string = $m[3]; //Не используется
                                
                                $this->debug(trim($line));
                                
                                continue;
                                
                        }
                        
                        if ($inHeaders) {
                                
                                if (trim($line) == '') {
                                        $inHeaders = false;
                                        $this->debug('Полученные заголовки', $this->headers);
                                        if ($this->headers_only) {
                                                break; //Пропустите оставшуюся часть входного
                                        }
                                        continue;
                                }
                                
                                if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) {
                                        //Переход к следующему заголовку:
                                        continue;
                                }
                                
                                $key = strtolower(trim($m[1]));
                                $val = trim($m[2]);
                                
                                //Сделка с возможностью несколько заголовков с таким же именем:
                                if (isset($this->headers[$key])) {
                                        if (is_array($this->headers[$key])) {
                                                $this->headers[$key][] = $val;
                                        } else {
                                                $this->headers[$key] = array($this->headers[$key], $val);
                                        }
                                } else {
                                        $this->headers[$key] = $val;
                                }
                                
                                continue;
                                
                        }
                        
                        //Мы не в заголовках, так что добавить строку с содержанием:
                        $this->content .= $line;
                        
                }
                
                fclose($fp);
                
                //Если данные сжимаются, распаковать его:  
                if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') {
                        $this->debug('Содержимое GZIP закодированы, распаковать ее');
                        $this->content = substr($this->content, 10);
                        $this->content = gzinflate($this->content);
                }
                
                //Если $persist_cookies, иметь дело с любыми печенья:
                
                if ($this->persist_cookies && isset($this->headers['set-cookie'])) {
                        
                        $cookies = $this->headers['set-cookie'];
                        
                        if (!is_array($cookies))
                                $cookies = array($cookies);
                        
                        if (!is_array(@$this->cookies[$this->host]))
                                $this->cookies[$this->host] = array();
                        
                        foreach ($cookies as $cookie) {
                                if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) {
                                        $this->cookies[$this->host][$m[1]] = $m[2];
                                }
                        }
                        
                }
                
                //Если $persist_referers, установите Referer готово для следующего запроса:
                
                if ($this->persist_referers) {
                        $this->debug('Сохранение Referer: '.$this->getRequestURL());
                        $this->referer = $this->getRequestURL();
                }
                
                //Наконец, если handle_redirects и перенаправления отправляется, сделайте это:
                if ($this->handle_redirects) {

                        if (++$this->redirect_count >= $this->max_redirects) {
                                $this->errormsg = 'Количество перенаправления превышало предельно ('.$this->max_redirects.')';
                                $this->debug($this->errormsg);
                                $this->redirect_count = 0;
                                return false;
                        }
                        $location = isset($this->headers['location']) ? $this->headers['location'] : '';
                        $location .= isset($this->headers['uri']) ? $this->headers['uri'] : '';
                        if ($location) {
                                $this->debug("После перенаправления на: $location" . (@$url['host'] ? ", host: ".$url['host'] : ''));
                                $url = parse_url($location);
                                if (@$url['host']) $this->host = $url['host'];
                                return $this->get(($url['path']{0} == '/' ? '' : '/') . $url['path']);
                        }
                        
                }
                
                return true;
                
        }
        protected function buildRequest() {
                
                //Создает заголовки запроса HTTP.
                
                $headers = array();
                $headers[] = "{$this->method} {$this->path} HTTP/1.0"; // * Использование 1,1 приводит к всяким проблемам, таким как "поблочного" кодирования
                $headers[] = "Host: {$this->host}";
                $headers[] = "User-Agent: {$this->user_agent}";
                $headers[] = "Accept: {$this->accept}";
                
                if ($this->use_gzip)
                        $headers[] = "Accept-encoding: {$this->accept_encoding}";
                
                $headers[] = "Accept-language: {$this->accept_language}";
                
                if ($this->referer)
                        $headers[] = "Referer: {$this->referer}";
                
                // * Cookies:
                
                if (@$this->cookies[$this->host]) {
                        $cookie = 'Cookie: ';
                        foreach ($this->cookies[$this->host] as $key => $value) {
                                $cookie .= "$key=$value; ";
                        }
                        $headers[] = $cookie;
                }
                
                // * Обычная проверка подлинности:
                
                if ($this->username && $this->password)
                        $headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password);
                
                // * Если это POST, установить тип содержания и объема:
                
                if ($this->postdata) {
                        $headers[] = 'Content-Type: application/x-www-form-urlencoded';
                        $headers[] = 'Content-Length: '.strlen($this->postdata);
                }
                
                $request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata;
                
                return $request;
                
        }
        
}
?>