Modules

Request_Client_External
extends Kohana_Request_Client_External
extends Request_Client
extends Kohana_Request_Client

package
Kohana
category
Base
author
Kohana Team
copyright
© 2008-2012 Kohana Team
license
http://kohanaframework.org/license

Class declared in SYSPATH/classes/request/client/external.php on line 3.

Properties

protected boolean $_allow_private_cache

Defines whether this client should cache private cache directives

protected Cache $_cache

Caching library for request caching

protected array $_options

curl options

protected static array $_processed_headers

internal header cache for curl processing

array(0) 

protected int $_request_time

The timestamp of the request

protected int $_response_time

The timestamp of the response

Methods

public execute( Request $request ) (defined in Kohana_Request_Client_External)

Processes the request, executing the controller action that handles this request, determined by the Route.

  1. Before the controller action is called, the Controller::before method will be called.
  2. Next the controller action will be called.
  3. After the controller action is called, the Controller::after method will be called.

By default, the output from the controller is captured and returned, and no headers are sent.

$request->execute();

Parameters

  • Request $request required - A request object

Tags

Return Values

  • Response

Source Code

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();
}

public options( [ mixed $key = NULL , mixed $value = NULL ] ) (defined in Kohana_Request_Client_External)

Set and get options for this request.

Parameters

  • mixed $key = NULL - Option name, or array of options
  • mixed $value = NULL - Option value

Return Values

  • mixed
  • Request_Client_External

Source Code

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;
}

public __construct( [ array $params = array(0) ] ) (defined in Kohana_Request_Client)

Creates a new Request_Client object, allows for dependency injection.

Parameters

  • array $params = array(0) - Params

Source Code

public 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);
				}
			}
		}
	}
}

public allow_private_cache( [ boolean $setting = NULL ] ) (defined in Kohana_Request_Client)

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.

Parameters

  • boolean $setting = NULL - Allow caching of privately marked responses

Tags

  • See - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9

Return Values

  • boolean
  • [Request_Client]

Source Code

public function allow_private_cache($setting = NULL)
{
	if ($setting === NULL)
		return $this->_allow_private_cache;

	$this->_allow_private_cache = (bool) $setting;
	return $this;
}

public cache( [ Kohana_Cache $cache = NULL ] ) (defined in Kohana_Request_Client)

Getter and setter for the internal caching engine, used to cache responses if available and valid.

Parameters

  • Kohana_Cache $cache = NULL - Cache engine to use for caching

Return Values

  • Kohana_Cache
  • Kohana_Request_Client

Source Code

public function cache(Cache $cache = NULL)
{
	if ($cache === NULL)
		return $this->_cache;

	$this->_cache = $cache;
	return $this;
}

public cache_lifetime( Response $response ) (defined in Kohana_Request_Client)

Calculates the total Time To Live based on the specification RFC 2616 cache lifetime rules.

Parameters

  • Response $response required - Response to evaluate

Return Values

  • mixed - TTL value or false if the response should not be cached

Source Code

public 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;
}

public cache_response( Request $request [, Response $response = NULL ] ) (defined in Kohana_Request_Client)

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.

Parameters

  • Request $request required - The request
  • Response $response = NULL - Response

Return Values

  • mixed

Source Code

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);
	}
}

public create_cache_key( Request $request ) (defined in Kohana_Request_Client)

Creates a cache key for the request to use for caching Kohana_Response returned by Request::execute.

Parameters

  • Request $request required - Request

Return Values

  • string
  • boolean

Source Code

public function create_cache_key(Request $request)
{
	return sha1($request->url());
}

public invalidate_cache( Request $request ) (defined in Kohana_Request_Client)

Invalidate a cached response for the Request supplied. This has the effect of deleting the response from the Cache entry.

Parameters

  • Request $request required - Response to remove from cache

Return Values

  • void

Source Code

public function invalidate_cache(Request $request)
{
	if ( ! $this->_cache instanceof Cache)
		return;

	$this->_cache->delete($this->_create_cache_key($request));

	return;
}

public request_execution_time( ) (defined in Kohana_Request_Client)

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.

Return Values

  • mixed

Source Code

public function request_execution_time()
{
	if ($this->_request_time === NULL OR $this->_response_time === NULL)
		return FALSE;

	return $this->_response_time - $this->_request_time;
}

public set_cache( Response $response ) (defined in Kohana_Request_Client)

Controls whether the response can be cached. Uses HTTP protocol to determine whether the response can be cached.

Parameters

  • Response $response required - The Response

Tags

  • See - RFC 2616 http://www.w3.org/Protocols/rfc2616/

Return Values

  • boolean

Source Code

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;
}

protected _curl_execute( Request $request ) (defined in Kohana_Request_Client_External)

Execute the request using the CURL extension. (recommended)

Parameters

  • Request $request required - Request to execute

Return Values

  • Response

Source Code

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;
}

protected _http_execute( Request $request ) (defined in Kohana_Request_Client_External)

Execute the request using the PECL HTTP extension. (recommended)

Parameters

  • Request $request required - Request to execute

Return Values

  • Response

Source Code

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;
}

protected _native_execute( Request $request ) (defined in Kohana_Request_Client_External)

Execute the request using PHP stream. (not recommended)

Parameters

  • Request $request required - Request to execute

Return Values

  • Response

Source Code

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;
}

protected static _parse_headers( resource $remote , string $header ) (defined in Kohana_Request_Client_External)

Parses the returned headers from the remote request

Parameters

  • resource $remote required - The curl resource
  • string $header required - The full header string

Return Values

  • int

Source Code

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);
}