request.php
来自「PHP 知识管理系统(基于树结构的知识管理系统), 英文原版的PHP源码。」· PHP 代码 · 共 1,192 行 · 第 1/3 页
PHP
1,192 行
if (!isset($boundary)) {
$postdata = implode('&', array_map(
create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
$this->_flattenArray('', $this->_postData)
));
// multipart request, probably with file uploads
} else {
$postdata = '';
if (!empty($this->_postData)) {
$flatData = $this->_flattenArray('', $this->_postData);
foreach ($flatData as $item) {
$postdata .= '--' . $boundary . "\r\n";
$postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
$postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
}
}
foreach ($this->_postFiles as $name => $value) {
if (is_array($value['name'])) {
$varname = $name . ($this->_useBrackets? '[]': '');
} else {
$varname = $name;
$value['name'] = array($value['name']);
}
foreach ($value['name'] as $key => $filename) {
$fp = fopen($filename, 'r');
$data = fread($fp, filesize($filename));
fclose($fp);
$basename = basename($filename);
$type = is_array($value['type'])? @$value['type'][$key]: $value['type'];
$postdata .= '--' . $boundary . "\r\n";
$postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"';
$postdata .= "\r\nContent-Type: " . $type;
$postdata .= "\r\n\r\n" . $data . "\r\n";
}
}
$postdata .= '--' . $boundary . "--\r\n";
}
$request .= 'Content-Length: ' . strlen($postdata) . "\r\n\r\n";
$request .= $postdata;
// Explicitly set request body
} elseif (!empty($this->_body)) {
$request .= 'Content-Length: ' . strlen($this->_body) . "\r\n\r\n";
$request .= $this->_body;
}
return $request;
}
/**
* Helper function to change the (probably multidimensional) associative array
* into the simple one.
*
* @param string name for item
* @param mixed item's values
* @return array array with the following items: array('item name', 'item value');
*/
function _flattenArray($name, $values)
{
if (!is_array($values)) {
return array(array($name, $values));
} else {
$ret = array();
foreach ($values as $k => $v) {
if (empty($name)) {
$newName = $k;
} elseif ($this->_useBrackets) {
$newName = $name . '[' . $k . ']';
} else {
$newName = $name;
}
$ret = array_merge($ret, $this->_flattenArray($newName, $v));
}
return $ret;
}
}
/**
* Adds a Listener to the list of listeners that are notified of
* the object's events
*
* @param object HTTP_Request_Listener instance to attach
* @return boolean whether the listener was successfully attached
* @access public
*/
function attach(&$listener)
{
if (!is_a($listener, 'HTTP_Request_Listener')) {
return false;
}
$this->_listeners[$listener->getId()] =& $listener;
return true;
}
/**
* Removes a Listener from the list of listeners
*
* @param object HTTP_Request_Listener instance to detach
* @return boolean whether the listener was successfully detached
* @access public
*/
function detach(&$listener)
{
if (!is_a($listener, 'HTTP_Request_Listener') ||
!isset($this->_listeners[$listener->getId()])) {
return false;
}
unset($this->_listeners[$listener->getId()]);
return true;
}
/**
* Notifies all registered listeners of an event.
*
* Events sent by HTTP_Request object
* 'sentRequest': after the request was sent
* Events sent by HTTP_Response object
* 'gotHeaders': after receiving response headers (headers are passed in $data)
* 'tick': on receiving a part of response body (the part is passed in $data)
* 'gzTick': on receiving a gzip-encoded part of response body (ditto)
* 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
*
* @param string Event name
* @param mixed Additional data
* @access private
*/
function _notify($event, $data = null)
{
foreach (array_keys($this->_listeners) as $id) {
$this->_listeners[$id]->update($this, $event, $data);
}
}
}
/**
* Response class to complement the Request class
*/
class HTTP_Response
{
/**
* Socket object
* @var object
*/
var $_sock;
/**
* Protocol
* @var string
*/
var $_protocol;
/**
* Return code
* @var string
*/
var $_code;
/**
* Response headers
* @var array
*/
var $_headers;
/**
* Cookies set in response
* @var array
*/
var $_cookies;
/**
* Response body
* @var string
*/
var $_body = '';
/**
* Used by _readChunked(): remaining length of the current chunk
* @var string
*/
var $_chunkLength = 0;
/**
* Attached listeners
* @var array
*/
var $_listeners = array();
/**
* Constructor
*
* @param object Net_Socket socket to read the response from
* @param array listeners attached to request
* @return mixed PEAR Error on error, true otherwise
*/
function HTTP_Response(&$sock, &$listeners)
{
$this->_sock =& $sock;
$this->_listeners =& $listeners;
}
/**
* Processes a HTTP response
*
* This extracts response code, headers, cookies and decodes body if it
* was encoded in some way
*
* @access public
* @param bool Whether to store response body in object property, set
* this to false if downloading a LARGE file and using a Listener.
* This is assumed to be true if body is gzip-encoded.
* @throws PEAR_Error
* @return mixed true on success, PEAR_Error in case of malformed response
*/
function process($saveBody = true)
{
do {
$line = $this->_sock->readLine();
if (sscanf($line, 'HTTP/%s %s', $http_version, $returncode) != 2) {
return PEAR::raiseError('Malformed response.');
} else {
$this->_protocol = 'HTTP/' . $http_version;
$this->_code = intval($returncode);
}
while ('' !== ($header = $this->_sock->readLine())) {
$this->_processHeader($header);
}
} while (100 == $this->_code);
$this->_notify('gotHeaders', $this->_headers);
// If response body is present, read it and decode
$chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
$gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
$hasBody = false;
if (!isset($this->_headers['content-length']) || 0 != $this->_headers['content-length']) {
while (!$this->_sock->eof()) {
if ($chunked) {
$data = $this->_readChunked();
} else {
$data = $this->_sock->read(4096);
}
if ('' == $data) {
break;
} else {
$hasBody = true;
if ($saveBody || $gzipped) {
$this->_body .= $data;
}
$this->_notify($gzipped? 'gzTick': 'tick', $data);
}
}
}
if ($hasBody) {
// Uncompress the body if needed
if ($gzipped) {
$this->_body = gzinflate(substr($this->_body, 10));
$this->_notify('gotBody', $this->_body);
} else {
$this->_notify('gotBody');
}
}
return true;
}
/**
* Processes the response header
*
* @access private
* @param string HTTP header
*/
function _processHeader($header)
{
list($headername, $headervalue) = explode(':', $header, 2);
$headername = strtolower($headername);
$headervalue = ltrim($headervalue);
if ('set-cookie' != $headername) {
if (isset($this->_headers[$headername])) {
$this->_headers[$headername] .= ',' . $headervalue;
} else {
$this->_headers[$headername] = $headervalue;
}
} else {
$this->_parseCookie($headervalue);
}
}
/**
* Parse a Set-Cookie header to fill $_cookies array
*
* @access private
* @param string value of Set-Cookie header
*/
function _parseCookie($headervalue)
{
$cookie = array(
'expires' => null,
'domain' => null,
'path' => null,
'secure' => false
);
// Only a name=value pair
if (!strpos($headervalue, ';')) {
$pos = strpos($headervalue, '=');
$cookie['name'] = trim(substr($headervalue, 0, $pos));
$cookie['value'] = trim(substr($headervalue, $pos + 1));
// Some optional parameters are supplied
} else {
$elements = explode(';', $headervalue);
$pos = strpos($elements[0], '=');
$cookie['name'] = trim(substr($elements[0], 0, $pos));
$cookie['value'] = trim(substr($elements[0], $pos + 1));
for ($i = 1; $i < count($elements); $i++) {
if (false === strpos($elements[$i], '=')) {
$elName = trim($elements[$i]);
$elValue = null;
} else {
list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
}
$elName = strtolower($elName);
if ('secure' == $elName) {
$cookie['secure'] = true;
} elseif ('expires' == $elName) {
$cookie['expires'] = str_replace('"', '', $elValue);
} elseif ('path' == $elName || 'domain' == $elName) {
$cookie[$elName] = urldecode($elValue);
} else {
$cookie[$elName] = $elValue;
}
}
}
$this->_cookies[] = $cookie;
}
/**
* Read a part of response body encoded with chunked Transfer-Encoding
*
* @access private
* @return string
*/
function _readChunked()
{
// at start of the next chunk?
if (0 == $this->_chunkLength) {
$line = $this->_sock->readLine();
if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
$this->_chunkLength = hexdec($matches[1]);
// Chunk with zero length indicates the end
if (0 == $this->_chunkLength) {
$this->_sock->readLine(); // make this an eof()
return '';
}
} else {
return '';
}
}
$data = $this->_sock->read($this->_chunkLength);
$this->_chunkLength -= strlen($data);
if (0 == $this->_chunkLength) {
$this->_sock->readLine(); // Trailing CRLF
}
return $data;
}
/**
* Notifies all registered listeners of an event.
*
* @param string Event name
* @param mixed Additional data
* @access private
* @see HTTP_Request::_notify()
*/
function _notify($event, $data = null)
{
foreach (array_keys($this->_listeners) as $id) {
$this->_listeners[$id]->update($this, $event, $data);
}
}
} // End class HTTP_Response
?>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?