Class declared in SYSPATH/classes/kohana/request/client/external.php on line 10.
boolean
$_allow_private_cacheDefines whether this client should cache private
cache directives
Cache
$_cacheCaching library for request caching
array
$_optionscurl options
array
$_processed_headersinternal header cache for curl processing
array(0)
int
$_request_timeThe timestamp of the request
int
$_response_timeThe timestamp of the response
Processes the request, executing the controller action that handles this request, determined by the Route.
By default, the output from the controller is captured and returned, and no headers are sent.
$request->execute();
Request
$request
required - A request objectResponse
public function execute(Request $request)
{
// Check for cache existance
if ($this->_cache instanceof Cache AND ($response = $this->cache_response($request)) instanceof Response)
return $response;
if (Kohana::$profiling)
{
// Set the benchmark name
$benchmark = '"'.$request->uri().'"';
if ($request !== Request::$initial AND Request::$current)
{
// Add the parent request uri
$benchmark .= ' « "'.Request::$current->uri().'"';
}
// Start benchmarking
$benchmark = Profiler::start('Requests', $benchmark);
}
// Store the current active request and replace current with new request
$previous = Request::$current;
Request::$current = $request;
// Resolve the POST fields
if ($post = $request->post())
{
$request->body(http_build_query($post, NULL, '&'))
->headers('content-type', 'application/x-www-form-urlencoded');
}
try
{
// If PECL_HTTP is present, use extension to complete request
if (extension_loaded('http'))
{
$this->_http_execute($request);
}
// Else if CURL is present, use extension to complete request
elseif (extension_loaded('curl'))
{
$this->_curl_execute($request);
}
// Else use the sloooow method
else
{
$this->_native_execute($request);
}
}
catch (Exception $e)
{
// Restore the previous request
Request::$current = $previous;
if (isset($benchmark))
{
// Delete the benchmark, it is invalid
Profiler::delete($benchmark);
}
// Re-throw the exception
throw $e;
}
// Restore the previous request
Request::$current = $previous;
if (isset($benchmark))
{
// Stop the benchmark
Profiler::stop($benchmark);
}
// Cache the response if cache is available
if ($this->_cache instanceof Cache)
{
$this->cache_response($request, $request->response());
}
// Return the response
return $request->response();
}
Set and get options for this request.
mixed
$key
= NULL - Option name, or array of optionsmixed
$value
= NULL - Option valuemixed
Request_Client_External
public function options($key = NULL, $value = NULL)
{
if ($key === NULL)
return $this->_options;
if (is_array($key))
{
$this->_options = $key;
}
elseif ($value === NULL)
{
return Arr::get($this->_options, $key);
}
else
{
$this->_options[$key] = $value;
}
return $this;
}
Creates a new Request_Client
object,
allows for dependency injection.
array
$params
= array(0) - Paramspublic function __construct(array $params = array())
{
if ($params)
{
foreach ($params as $key => $value)
{
if (method_exists($this, $key))
{
if (property_exists($this, $key) OR property_exists($this, '_'.$key))
{
$method = trim($key, '_');
$this->$method($value);
}
}
}
}
}
Gets or sets the Request_Client::allow_private_cache setting.
If set to TRUE
, the client will also cache cache-control directives
that have the private
setting.
boolean
$setting
= NULL - Allow caching of privately marked responsesboolean
[Request_Client]
public function allow_private_cache($setting = NULL)
{
if ($setting === NULL)
return $this->_allow_private_cache;
$this->_allow_private_cache = (bool) $setting;
return $this;
}
Getter and setter for the internal caching engine, used to cache responses if available and valid.
Kohana_Cache
$cache
= NULL - Cache engine to use for cachingKohana_Cache
Kohana_Request_Client
public function cache(Cache $cache = NULL)
{
if ($cache === NULL)
return $this->_cache;
$this->_cache = $cache;
return $this;
}
Calculates the total Time To Live based on the specification RFC 2616 cache lifetime rules.
Response
$response
required - Response to evaluatemixed
- TTL value or false if the response should not be cachedpublic function cache_lifetime(Response $response)
{
// Get out of here if this cannot be cached
if ( ! $this->set_cache($response))
return FALSE;
// Calculate apparent age
if ($date = $response->headers('date'))
{
$apparent_age = max(0, $this->_response_time - strtotime( (string) $date));
}
else
{
$apparent_age = max(0, $this->_response_time);
}
// Calculate corrected received age
if ($age = $response->headers('age'))
{
$corrected_received_age = max($apparent_age, intval( (string) $age));
}
else
{
$corrected_received_age = $apparent_age;
}
// Corrected initial age
$corrected_initial_age = $corrected_received_age + $this->request_execution_time();
// Resident time
$resident_time = time() - $this->_response_time;
// Current age
$current_age = $corrected_initial_age + $resident_time;
// Prepare the cache freshness lifetime
$ttl = NULL;
// Cache control overrides
if ($cache_control = $response->headers('cache-control'))
{
// Parse the cache control header
$cache_control = Response::parse_cache_control( (string) $cache_control);
if (isset($cache_control['max-age']))
{
$ttl = (int) $cache_control['max-age'];
}
if (isset($cache_control['s-maxage']) AND isset($cache_control['private']) AND $this->_allow_private_cache)
{
$ttl = (int) $cache_control['s-maxage'];
}
if (isset($cache_control['max-stale']) AND ! isset($cache_control['must-revalidate']))
{
$ttl = $current_age + (int) $cache_control['max-stale'];
}
}
// If we have a TTL at this point, return
if ($ttl !== NULL)
return $ttl;
if ($expires = $response->headers('expires'))
return strtotime( (string) $expires) - $current_age;
return FALSE;
}
Caches a Response using the supplied Cache and the key generated by Request_Client::_create_cache_key.
If not response is supplied, the cache will be checked for an existing one that is available.
Request
$request
required - The requestResponse
$response
= NULL - Responsemixed
public function cache_response(Request $request, Response $response = NULL)
{
if ( ! $this->_cache instanceof Cache)
return FALSE;
// Check for Pragma: no-cache
if ($pragma = $request->headers('pragma'))
{
if ($pragma instanceof HTTP_Header_Value and $pragma->key == 'no-cache')
return FALSE;
elseif (is_array($pragma) and isset($pragma['no-cache']))
return FALSE;
}
if ( ! $response)
{
$response = $this->_cache->get($this->create_cache_key($request));
return ($response !== NULL) ? $response : FALSE;
}
else
{
if (($ttl = $this->cache_lifetime($response)) === FALSE)
return FALSE;
return $this->_cache->set($this->create_cache_key($request), $response, $ttl);
}
}
Creates a cache key for the request to use for caching Kohana_Response returned by Request::execute.
Request
$request
required - Requeststring
boolean
public function create_cache_key(Request $request)
{
return sha1($request->url());
}
Invalidate a cached response for the Request supplied. This has the effect of deleting the response from the Cache entry.
Request
$request
required - Response to remove from cachevoid
public function invalidate_cache(Request $request)
{
if ( ! $this->_cache instanceof Cache)
return;
$this->_cache->delete($this->_create_cache_key($request));
return;
}
Returns the duration of the last request execution.
Either returns the time of completed requests or
FALSE
if the request hasn't finished executing, or
is yet to be run.
mixed
public function request_execution_time()
{
if ($this->_request_time === NULL OR $this->_response_time === NULL)
return FALSE;
return $this->_response_time - $this->_request_time;
}
Controls whether the response can be cached. Uses HTTP protocol to determine whether the response can be cached.
Response
$response
required - The Responseboolean
public function set_cache(Response $response)
{
$headers = (array) $response->headers();
if ($cache_control = arr::get($headers, 'cache-control'))
{
// Parse the cache control
$cache_control = Response::parse_cache_control( (string) $cache_control);
// If the no-cache or no-store directive is set, return
if (array_intersect_key($cache_control, array('no-cache' => NULL, 'no-store' => NULL)))
return FALSE;
// Get the directives
$directives = array_keys($cache_control);
// Check for private cache and get out of here if invalid
if ( ! $this->_allow_private_cache and in_array('private', $directives))
{
if ( ! isset($cache_control['s-maxage']))
return FALSE;
// If there is a s-maxage directive we can use that
$cache_control['max-age'] = $cache_control['s-maxage'];
}
// Check that max-age has been set and if it is valid for caching
if (isset($cache_control['max-age']) and (int) $cache_control['max-age'] < 1)
return FALSE;
}
if ($expires = arr::get($headers, 'expires') and ! isset($cache_control['max-age']))
{
// Can't cache things that have expired already
if (strtotime( (string) $expires) <= time())
return FALSE;
}
return TRUE;
}
Execute the request using the CURL extension. (recommended)
Request
$request
required - Request to executeResponse
protected function _curl_execute(Request $request)
{
// Reset the headers
Request_Client_External::$_processed_headers = array();
// Set the request method
$options[CURLOPT_CUSTOMREQUEST] = $request->method();
// Set the request body. This is perfectly legal in CURL even
// if using a request other than POST. PUT does support this method
// and DOES NOT require writing data to disk before putting it, if
// reading the PHP docs you may have got that impression. SdF
$options[CURLOPT_POSTFIELDS] = $request->body();
// Process headers
if ($headers = $request->headers())
{
$http_headers = array();
foreach ($headers as $key => $value)
{
$http_headers[] = $key.': '.$value;
}
$options[CURLOPT_HTTPHEADER] = $http_headers;
}
// Process cookies
if ($cookies = $request->cookie())
{
$options[CURLOPT_COOKIE] = http_build_query($cookies, NULL, '; ');
}
// Implement the default header parsing
$options[CURLOPT_HEADERFUNCTION] = array($this, '_parse_headers');
// The transfer must always be returned
$options[CURLOPT_RETURNTRANSFER] = TRUE;
// Apply any additional options set to Request_Client_External::$_options
$options += $this->_options;
$uri = $request->uri();
if ($query = $request->query())
{
$uri .= '?'.http_build_query($query, NULL, '&');
}
// Open a new remote connection
$curl = curl_init($uri);
// Set connection options
if ( ! curl_setopt_array($curl, $options))
{
throw new Kohana_Request_Exception('Failed to set CURL options, check CURL documentation: :url',
array(':url' => 'http://php.net/curl_setopt_array'));
}
// Get the response body
$body = curl_exec($curl);
// Get the response information
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($body === FALSE)
{
$error = curl_error($curl);
}
// Close the connection
curl_close($curl);
if (isset($error))
{
throw new Kohana_Request_Exception('Error fetching remote :url [ status :code ] :error',
array(':url' => $request->url(), ':code' => $code, ':error' => $error));
}
// Create response
$response = $request->create_response();
$response->status($code)
->headers(Request_Client_External::$_processed_headers)
->body($body);
return $response;
}
Execute the request using the PECL HTTP extension. (recommended)
Request
$request
required - Request to executeResponse
protected function _http_execute(Request $request)
{
$http_method_mapping = array(
HTTP_Request::GET => HTTPRequest::METH_GET,
HTTP_Request::HEAD => HTTPRequest::METH_HEAD,
HTTP_Request::POST => HTTPRequest::METH_POST,
HTTP_Request::PUT => HTTPRequest::METH_PUT,
HTTP_Request::DELETE => HTTPRequest::METH_DELETE,
HTTP_Request::OPTIONS => HTTPRequest::METH_OPTIONS,
HTTP_Request::TRACE => HTTPRequest::METH_TRACE,
HTTP_Request::CONNECT => HTTPRequest::METH_CONNECT,
);
// Create an http request object
$http_request = new HTTPRequest($request->uri(), $http_method_mapping[$request->method()]);
if ($this->_options)
{
// Set custom options
$http_request->setOptions($this->_options);
}
// Set headers
$http_request->setHeaders($request->headers()->getArrayCopy());
// Set cookies
$http_request->setCookies($request->cookie());
// Set the body
if ($request->method() == HTTP_Request::PUT)
{
$http_request->addPutData($request->body());
}
else
{
$http_request->setBody($request->body());
}
// Set the query
$http_request->setQueryData($request->query());
try
{
$http_request->send();
}
catch (HTTPRequestException $e)
{
throw new Kohana_Request_Exception($e->getMessage());
}
catch (HTTPMalformedHeaderException $e)
{
throw new Kohana_Request_Exception($e->getMessage());
}
catch (HTTPEncodingException $e)
{
throw new Kohana_Request_Exception($e->getMessage());
}
// Create the response
$response = $request->create_response();
// Build the response
$response->status($http_request->getResponseCode())
->headers($http_request->getResponseHeader())
->cookie($http_request->getResponseCookies())
->body($http_request->getResponseBody());
return $response;
}
Execute the request using PHP stream. (not recommended)
Request
$request
required - Request to executeResponse
protected function _native_execute(Request $request)
{
// Reset the headers
Request_Client_External::$_processed_headers = array();
// Calculate stream mode
$mode = ($request->method() === HTTP_Request::GET) ? 'r' : 'r+';
// Process cookies
if ($cookies = $request->cookie())
{
$request->headers('cookie', http_build_query($cookies, NULL, '; '));
}
// Get the message body
$body = $request->body();
// Set the content length
$request->headers('content-length', strlen($body));
// Create the context
$options = array(
$request->protocol() => array(
'method' => $request->method(),
'header' => (string) $request->headers(),
'content' => $body,
'user-agent' => 'Kohana Framework '.Kohana::VERSION.' ('.Kohana::CODENAME.')'
)
);
// Create the context stream
$context = stream_context_create($options);
stream_context_set_option($context, $this->_options);
$uri = $request->uri();
if ($query = $request->query())
{
$uri .= '?'.http_build_query($query, NULL, '&');
}
$stream = fopen($uri, $mode, FALSE, $context);
$meta_data = stream_get_meta_data($stream);
// Get the HTTP response code
$http_response = array_shift($meta_data['wrapper_data']);
if (preg_match_all('/(\w+\/\d\.\d) (\d{3})/', $http_response, $matches) !== FALSE)
{
$protocol = $matches[1][0];
$status = (int) $matches[2][0];
}
else
{
$protocol = NULL;
$status = NULL;
}
// Process headers
array_map(array('Request_Client_External', '_parse_headers'), array(), $meta_data['wrapper_data']);
// Create a response
$response = $request->create_response();
$response->status($status)
->protocol($protocol)
->headers(Request_Client_External::$_processed_headers)
->body(stream_get_contents($stream));
// Close the stream after use
fclose($stream);
return $response;
}
Parses the returned headers from the remote request
resource
$remote
required - The curl resourcestring
$header
required - The full header stringint
protected static function _parse_headers($remote, $header)
{
$headers = array();
if (preg_match_all('/(\w[^\s:]*):[ ]*([^\r\n]*(?:\r\n[ \t][^\r\n]*)*)/', $header, $matches))
{
foreach ($matches[0] as $key => $value)
$headers[$matches[1][$key]] = $matches[2][$key];
}
// If there are headers to apply
if ($headers)
{
Request_Client_External::$_processed_headers += $headers;
}
return strlen($header);
}