initfest/schedule/class.SmartCurl.php

136 lines
3.3 KiB
PHP

<?php
class SmartCurl {
protected static $etags = [];
protected $ch = null;
protected $cache_dir = __DIR__ . DIRECTORY_SEPARATOR . 'cache';
protected $cache_index;
protected $url_root = null;
public function __construct($url_root = null, $cache_dir = null) {
if (!is_null($cache_dir)) {
$this->cache_dir = __DIR__ . DIRECTORY_SEPARATOR . $cache_dir;
}
if (!file_exists($this->cache_dir)) {
mkdir($this->cache_dir, 0777, true);
}
$this->cache_index = $this->cache_dir . '.json';
$cache = file_exists($this->cache_index) ? file_get_contents($this->cache_index) : false;
if ($cache !== false) {
$cache = json_decode($cache, true);
}
if ($cache !== false) {
static::importEtags($cache);
}
if (!is_null($url_root)) {
$this->url_root = $url_root;
}
$ch = curl_init();
if ($ch === false) {
throw new Exception('curl init failed');
}
$this->ch = $ch;
if (curl_setopt_array($ch, [
CURLOPT_FAILONERROR => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
//CURLINFO_HEADER_OUT => true,
]) === false) {
throw new Exception('curl setopt failed');
}
}
public function __destruct() {
curl_close($this->ch);
file_put_contents($this->cache_index, json_encode(static::exportEtags()));
}
public static function importEtags(array $etags) {
static::$etags = array_merge(static::$etags, $etags);
}
public static function exportEtags() {
return static::$etags;
}
public function getUrl($filename) {
if (is_null($this->url_root)) {
$url = $filename;
}
else {
$url = $this->url_root . $filename;
}
if (curl_setopt($this->ch, CURLOPT_URL, $url) === false) {
throw new Exception('set url failed: ' . $url);
}
$cache_file = $this->cache_dir . DIRECTORY_SEPARATOR . str_replace('/', '@', $filename);
$etag = array_key_exists($url, static::$etags) && file_exists($cache_file) ? static::$etags[$url] : null;
if (curl_setopt($this->ch, CURLOPT_HTTPHEADER, [
'If-None-Match:' . (is_null($etag) ? '' : ' ' . $etag),
]) === false) {
throw new Exception('set etag failed: ' . $url);
}
$response = curl_exec($this->ch);
if ($response === false) {
if (file_exists($cache_file)) {
return file_get_contents($cache_file);
}
else {
return false;
}
}
//var_dump(curl_getinfo($this->ch, CURLINFO_HEADER_OUT));
//var_dump($response);
$header_size = curl_getinfo($this->ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $header_size);
$headers = array_filter(explode("\r\n", $header));
foreach ($headers as $header_line) {
if (stripos($header_line, 'etag:') === 0) {
static::$etags[$url] = trim(substr($header_line, strlen('etag:')));
}
}
$http_code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
if ($http_code === 304 && !is_null($this->url_root)) {
// use cache
if (file_exists($cache_file)) {
return file_get_contents($cache_file);
}
else {
return false;
}
}
if (strlen($response) === $header_size) {
return false;
}
$body = substr($response, $header_size);
if ($http_code === 200) {
file_put_contents($cache_file, $body);
}
return $body;
}
}