/lib/Molinos/HTTP/API.php
PHP | 382 lines | 251 code | 61 blank | 70 comment | 45 complexity | 5bd7efcc2dd8bed50b8633dc6b4aa9a3 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, GPL-2.0
- <?php
- /**
- * HTTP ??????.
- *
- * @package Molinos_CMS
- * @subpackage HTTP
- * @author Justin Forest <justin.forest@gmail.com>
- * @copyright 2006-2011 molinos.ru
- * @license http://www.gnu.org/copyleft/gpl.html GPL
- */
- class Molinos_HTTP_API extends Molinos_Core_API
- {
- const CONTENT = 1;
- const NO_CACHE = 2;
- const NO_ERROR = 4;
- const TEXT = 8;
- const EXTRA = 16;
- public function head($url, $loop = 10)
- {
- $this->log('HEAD ' . $url);
- if (false === ($result = get_headers($url, 1)))
- throw new RuntimeException('Bad URL: ' . $url);
- // ?????? ?????????? ?????? ????????, ??? ???????????
- // ??????????. ??????????? ?????????.
- if (is_array($result['Content-Type'])) {
- $result[0] = $result[count($result['Content-Type']) - 1];
- foreach ($result as $k => $v)
- if (is_array($v))
- list($result[$k]) = array_reverse($v);
- elseif (is_numeric($k) and $k !== 0)
- unset($result[$k]);
- }
- $parts = explode(' ', $result[0], 3);
- list($result['_protocol'], $result['_status'], $result['_message']) = $parts;
- if ($loop and $result['_status'] >= 300 and $result['_status'] < 400 and isset($result['Location']))
- return $this->head($result['Location'], $loop - 1);
- return $result;
- }
- /**
- * ?????????? ????? ?? ?????????.
- */
- public function fetch($url, $options = null, $outfile = null)
- {
- $this->log('FETCH ' . $url);
- if (null === $outfile)
- $this->ctx->addTempFile($outfile = tempnam(Molinos_Core_Utils::tmpdir(), 'fetch'));
- if (ini_get('allow_url_fopen')) {
- $result = $this->fetch_fopen($url, $options, $outfile);
- } elseif (function_exists('curl_init')) {
- $result = $this->fetch_curl($url, $options, $outfile);
- } elseif ($options & self::NO_ERROR) {
- $result = false;
- } else {
- throw new RuntimeException(t('All methods of file fetching are disabled.'));
- }
- if (!$result and !($options & self::NO_ERROR))
- throw new RuntimeException(t('Could not fetch %url. More information could be available in the log file.', array(
- '%url' => $url,
- )));
- if ($options & self::CONTENT) {
- $result = file_get_contents($outfile);
- unlink($outfile);
- } else {
- $result = $outfile;
- }
- return $result;
- }
- /**
- * ?????????? ?????????? ?????.
- */
- public function fetchContent($url)
- {
- return $this->fetch($url, self::CONTENT);
- }
- /**
- * ?????????? ?????, ?????????? ??????????? ????????.
- */
- public function fetchEx($url)
- {
- $in = fopen($url, 'rb');
- $out = fopen($this->ctx->addTempFile($fileName = tempnam(MCMS_TEMP_FOLDER, 'mcms-fetch.')), 'wb');
- $meta = $this->copyStream($in, $out);
- $ct = explode(';', $meta['content-type']);
- $result = array(
- 'name' => basename($fileName),
- 'tmp_name' => $fileName,
- 'type' => array_shift($ct),
- 'size' => filesize($fileName),
- 'remove' => true,
- 'url' => isset($meta['location'])
- ? $meta['location']
- : $url,
- );
- // ???????? ?????????? ?????????? ?????.
- if (isset($meta['content-length']) and $meta['content-length'] != $result['size'])
- throw new RuntimeException(sprintf('Incomplete file received (%u bytes instead of %u).', $result['size'], $meta['content-length']));
- // ???????????? ?????, ???? ?????.
- if (isset($meta['location']))
- $url = $meta['location'];
- // ?????????? ??? ?????.
- if (isset($meta['content-disposition'])) {
- // attachment; filename="thumbnailer-9.05.12.zip"
- if (preg_match('/filename="([^"]+)"/', $meta['content-disposition'], $m))
- $result['name'] = $m[1];
- } else {
- $u = parse_url($result['url']);
- $result['name'] = basename($u['path']);
- }
- return $result;
- }
- /**
- * ?????????? ?????????? ????? ????????????.
- */
- public function pass($fileName, $type = null)
- {
- if (null === $type)
- $type = Molinos_Core_Utils::getFileType($fileName, $fileName);
- throw new Molinos_Core_Exceptions_Response(Molinos_HTTP_Responses_File($fileName, $type, false));
- }
- /**
- * ?????? ????????? HTTP.
- */
- public function parseHeaders($headers)
- {
- $result = array();
- foreach (preg_split('/[\r\n]+/', $headers, -1, PREG_SPLIT_NO_EMPTY) as $h) {
- if (0 === strpos($h, 'HTTP/'))
- list($result['_protocol'], $result['_status'], $result['_message']) = explode(' ', $h, 3);
- else {
- list($k, $v) = explode(':', $h, 2);
- $result[strtolower($k)] = trim($v);
- }
- }
- return $result;
- }
- /**
- * ?????????? ????? ?????????? CURL.
- */
- protected function fetch_curl($url, $options, $outfile)
- {
- $ch = curl_init($url);
- $fp = fopen($outfile, "wb+");
- curl_setopt($ch, CURLOPT_FILE, $fp);
- curl_setopt($ch, CURLOPT_HEADER, 0);
- if (null !== ($time = ini_get('max_execution_time')))
- curl_setopt($ch, CURLOPT_TIMEOUT, $time);
- curl_setopt($ch, CURLOPT_USERAGENT, 'Molinos.CMS/' . MCMS_VERSION . ' at http://' . $ctx->request->host);
- if (!ini_get('safe_mode'))
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
- curl_exec($ch);
- $status = curl_getinfo($ch);
- curl_close($ch);
- // ??? ?????? ?????? ??????????? filesize($outfile) ???????? ????????
- // ??????, ??? ???????? ? ?????? ?????????? ?? ??????? ??? ??????????.
- fflush($fp);
- fclose($fp);
- if (200 != $status['http_code']) {
- $this->log($url . ': error ' . $status['http_code']);
- return false;
- }
- if ($status['size_download'] != $status['download_content_length']) {
- $this->log("{$url}: incomplete transfer, got {$status['size_download']} bytes instead of {$status['download_content_length']}");
- return false;
- }
- if ($options & self::TEXT)
- return $this->transcode($outfile, $status['content_type']);
- return true;
- }
- /**
- * ?????????? ????? ?????????? fopen().
- */
- protected function fetch_fopen($url, $options, $outfile)
- {
- if (!($f = @fopen($url, 'rb'))) {
- $this->log($url . ': could not fopen(rb)');
- return false;
- }
- if (!($out = fopen($outfile, 'w'))) {
- fclose($f);
- $this->log($outfile . ': could not fopen(w)');
- return false;
- }
- while (!feof($f))
- fwrite($out, fread($f, 1024));
- $meta = stream_get_meta_data($f);
- $head = $this->parseHeaders(implode("\n", $meta['wrapper_data']));
- fclose($f);
- fclose($out);
- if ($options & self::TEXT)
- return $this->transcode($outfile, $head['content-type']);
- if (!empty($head['content-length']) and $head['content-length'] != ($got = filesize($outfile))) {
- $this->log($url . ": incomplete file, {$got} bytes instead of {$url}");
- return false;
- }
- return true;
- }
- /**
- * ????? ????????? ? ???.
- */
- protected function log($message)
- {
- Molinos_Core_Logger::getInstance()->debug($message, 'http');
- }
- /**
- * ??????????? ????????? ??????, ???? ??? ??????? ?? utf-8. ????????
- * ????????? ??????? ? ????????? Content-Type ($content_type).
- */
- protected function transcode($filename, $content_type)
- {
- if (substr($content_type, 0, 5) != 'text/')
- return false;
- if ('utf-8' != ($charset = $this->get_charset($content_type))) {
- $data = mb_convert_encoding(file_get_contents($filename), 'utf-8', $charset);
- if (mb_check_encoding($data, 'utf-8')) {
- file_put_contents($filename, $data);
- return true;
- }
- } elseif (mb_check_encoding(file_get_contents($filename), 'utf-8')) {
- return true;
- }
- return false;
- }
- /**
- * ?????????? ?????????.
- */
- protected function get_charset($content_type)
- {
- $cpos = strpos($content_type, 'charset=');
- return (false === $cpos)
- ? 'utf-8'
- : strtolower(substr($content_type, $cpos + 8));
- }
- /**
- * ???????? ???? ????? ? ??????, ?????????? ??????????.
- */
- protected function copyStream($from, $to)
- {
- while (!feof($from))
- fwrite($to, fread($from, 1024));
- fflush($to);
- fclose($to);
- $meta = stream_get_meta_data($from);
- if (!isset($meta['wrapper_data']))
- throw new RuntimeException('Fetch error.');
- $meta = $this->parseHeaders(implode("\n", $meta['wrapper_data']));
- fclose($from);
- return $meta;
- }
- /**
- * ???????? ?????? ??????? POST.
- *
- * @param string $url ?????, ???? ??????? ????????? ?????.
- * @param array $data ???????????? ??????.
- * @return string ?????????? ?????????.
- */
- public function post($url, array $data)
- {
- $c = stream_context_create(array(
- 'http' => array(
- 'method' => 'POST',
- 'header' => 'Content-Type: application/x-www-form-urlencoded' . PHP_EOL,
- 'content' => Molinos_Core_API::getInstance('http')->build_query($data),
- ),
- ));
- return file_get_contents($url, false, $c);
- }
- /**
- * ?????????? ????????? ??? ?????? ?????.
- *
- * @todo ???????? ????????? If-Modified-Since ? If-None-Match, ??.
- * http://drupal.org/files/issues/147310-29.patch ?
- * http://drupal.org/node/147310.
- *
- * @param string $fileName ??? ??????????? ?????.
- * @param string $mimeType ??? ???????????.
- * @param bool $inline True, ???? ???? ????? ??????? ????? ? ???????.
- * @return array ????????????? ?????? ??????????.
- */
- public function getFileServeHeaders($fileName, $mimeType = null, $inline = false)
- {
- $result = array();
- $cacheLifeTime = intval(Molinos_Core_API::getInstance('settings')->get('cache.ttl', 900));
- if (null === $mimeType)
- $mimeType = Molinos_Core_Utils::getFileType($fileName);
- $result['Content-Type'] = $mimeType;
- $result['Content-Length'] = filesize($fileName);
- if ($inline)
- $result['Content-Disposition'] = 'inline';
- else
- $result['Content-Disposition'] = sprintf('attachment; filename="%s"', urlencode(basename($fileName)));
- $result['Expires'] = gmdate("D, d M Y H:i:s", time() + $cacheLifeTime) . 'GMT';
- $result['Cache-Control'] = 'public, max-age=' . $cacheLifeTime;
- $result['Last-Modified'] = gmdate('D, d M Y H:i:s', filemtime($fileName)) . 'GMT';
- $result['ETag'] = '"' . md5($result['Last-Modified'] .';'. $fileName) . '"';
- return $result;
- }
- public function build_query(array $args)
- {
- $parts = array();
- foreach ($args as $k => $v) {
- if (is_numeric($k))
- $parts[] = urlencode($v);
- elseif (is_scalar($v))
- $parts[] = urlencode($k) . '=' . urlencode($v);
- }
- return implode(ini_get('arg_separator.output'), $parts);
- }
- public function build_cookie(array $cookie)
- {
- $result = array();
- foreach ($cookie['cookies'] as $k => $v)
- $result[] = urlencode($k) . '=' . urlencode($v);
- if (isset($cookie['path']))
- $result[] = 'path=' . $cookie['path'];
- if (isset($cookie['domain']))
- $result[] = 'domain=' . $cookie['domain'];
- if (isset($cookie['expires']))
- $result[] = 'expires=' . date('r', $cookie['expires']);
- return implode('; ', $result);
- }
- }