9 * This source file is subject to the new BSD license that is bundled
10 * with this package in the file LICENSE.
11 * It is also available through the world-wide-web at this URL:
12 * http://phergie.org/license
15 * @package Phergie_Plugin_Http
16 * @author Phergie Development Team <team@phergie.org>
17 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
18 * @license http://phergie.org/license New BSD License
19 * @link http://pear.phergie.org/package/Phergie_Plugin_Http
23 * Provides an HTTP client for plugins to use in contacting web services or
24 * retrieving feeds or web pages.
27 * @package Phergie_Plugin_Http
28 * @author Phergie Development Team <team@phergie.org>
29 * @license http://phergie.org/license New BSD License
30 * @link http://pear.phergie.org/package/Phergie_Plugin_Http
31 * @uses extension simplexml optional
32 * @uses extension json optional
34 class Phergie_Plugin_Http extends Phergie_Plugin_Abstract
37 * Response to the last executed HTTP request
39 * @var Phergie_Plugin_Http_Response
44 * Mapping of content types to handlers for them
51 * Initializes the handler lookup table.
55 public function onLoad()
57 $this->handlers = array(
58 '(?:application|text)/xml(?:;.*)?'
59 => 'simplexml_load_string',
60 '(?:(?:application|text)/(?:x-)?json)|text/javascript.*'
64 if (is_array($this->config['http.handlers'])) {
65 $this->handlers = array_merge(
67 $this->config['http.handlers']
73 * Sets a handler callback for a content type, which is called when a
74 * response of that content type is received to perform any needed
75 * transformations on the response body content before storing it in the
76 * response object. Note that the calling plugin is responsible for
77 * indicating any dependencies related to specified handler callbacks.
79 * @param string $type PCRE regular expression (without delimiters) that
80 * matches one or more MIME types
81 * @param callback $callback Callback to execute when a response of a content
82 * type matched by $type is encountered
84 * @return Phergie_Plugin_Http Provides a fluent interface
86 public function setHandler($type, $callback)
88 if (!is_callable($callback)) {
89 throw new Phergie_Plugin_Exception(
90 'Invalid callback specified',
91 Phergie_Plugin_Exception::ERR_FATAL_ERROR
95 $this->handlers[$type] = $callback;
101 * Supporting method that parses the status line of an HTTP response
104 * @param string $status Status line
106 * @return array Associative array containing the HTTP version, response
107 * code, and response description
109 protected function parseStatusLine($status)
111 $parts = explode(' ', $status, 3);
113 'version' => str_replace('HTTP/', '', $parts[0]),
115 'message' => rtrim($parts[2])
121 * Supporting method that acts as an error handler to intercept HTTP
122 * responses resulting in PHP-level errors.
124 * @param int $errno Level of the error raised
125 * @param string $errstr Error message
126 * @param string $errfile Name of the file in which the error was raised
127 * @param string $errline Line number on which the error was raised
129 * @return bool Always returns TRUE to allow normal execution to
130 * continue once this method terminates
132 public function handleError($errno, $errstr, $errfile, $errline)
134 if ($httperr = strstr($errstr, 'HTTP/')) {
135 $parts = $this->parseStatusLine($httperr);
137 ->setCode($parts['code'])
138 ->setMessage($parts['message']);
145 * Supporting method that executes a request and handles the response.
147 * @param string $url URL to request
148 * @param array $context Associative array of stream context parameters
150 * @return Phergie_Plugin_Http_Response Object representing the response
151 * resulting from the request
153 public function request($url, array $context)
155 $this->response = new Phergie_Plugin_Http_Response;
157 $url = (string) $url;
158 $context = stream_context_create(array('http' => $context));
160 set_error_handler(array($this, 'handleError'), E_WARNING);
161 $stream = fopen($url, 'r', false, $context);
163 $meta = stream_get_meta_data($stream);
164 $status = $this->parseStatusLine($meta['wrapper_data'][0]);
165 $code = $status['code'];
166 $message = $status['message'];
168 foreach (array_slice($meta['wrapper_data'], 1) as $header) {
169 if (!strpos($header, ':')) {
172 list($name, $value) = explode(': ', $header, 2);
173 $headers[$name] = $value;
175 unset($meta['wrapper_data']);
179 ->setMessage($message)
180 ->setHeaders($headers)
183 $body = stream_get_contents($stream);
184 $type = $this->response->getHeaders('content-type');
185 foreach ($this->handlers as $expr => $handler) {
186 if (preg_match('#^' . $expr . '$#i', $type)) {
187 $handled = call_user_func($handler, $body);
188 if (!empty($handled)) {
194 $this->response->setContent($body);
196 restore_error_handler();
198 return $this->response;
202 * Performs a GET request.
204 * @param string $url URL for the request
205 * @param array $query Optional associative array of parameters
206 * constituting the URL query string if $url has none
207 * @param array $context Optional associative array of additional stream
210 * @return Phergie_Plugin_Http_Response Received response data
212 public function get($url, array $query = array(), array $context = array())
214 if (!empty($query)) {
215 $url .= '?' . http_build_query($query);
218 $context['method'] = 'GET';
220 return $this->request($url, $context);
224 * Performs a HEAD request.
226 * @param string $url URL for the request
227 * @param array $query Optional associative array of parameters
228 * constituting the URL query string if $url has none
229 * @param array $context Optional associative array of additional stream
232 * @return Phergie_Plugin_Http_Response Received response data
234 public function head($url, array $query = array(), array $context = array())
236 if (!empty($query)) {
237 $url .= '?' . http_build_query($query);
240 $context['method'] = 'HEAD';
242 return $this->request($url, $context);
246 * Performs a POST request.
248 * @param string $url URL for the request
249 * @param array $query Optional associative array of parameters
250 * constituting the URL query string if $url has none
251 * @param array $post Optional associative array of parameters
252 * constituting the POST request body if it is using the
253 * traditional URL-encoded format
254 * @param array $context Optional associative array of additional stream
257 * @return Phergie_Plugin_Http_Response Received response data
259 public function post($url, array $query = array(),
260 array $post = array(), array $context = array()
262 if (!empty($query)) {
263 $url .= '?' . http_build_query($query);
266 $context['method'] = 'POST';
269 && (!empty($context['header'])
270 xor stripos($context['header'], 'Content-Type'))
272 if (!empty($context['header'])) {
273 $context['header'] .= "\r\n";
275 $context['header'] = '';
277 $context['header'] .=
278 'Content-Type: application/x-www-form-urlencoded';
279 $context['content'] = http_build_query($post);
282 return $this->request($url, $context);