1*a1a3b679SAndreas Boehler<?php 2*a1a3b679SAndreas Boehler 3*a1a3b679SAndreas Boehlernamespace Sabre\HTTP; 4*a1a3b679SAndreas Boehler 5*a1a3b679SAndreas Boehler/** 6*a1a3b679SAndreas Boehler * This is the abstract base class for both the Request and Response objects. 7*a1a3b679SAndreas Boehler * 8*a1a3b679SAndreas Boehler * This object contains a few simple methods that are shared by both. 9*a1a3b679SAndreas Boehler * 10*a1a3b679SAndreas Boehler * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). 11*a1a3b679SAndreas Boehler * @author Evert Pot (http://evertpot.com/) 12*a1a3b679SAndreas Boehler * @license http://sabre.io/license/ Modified BSD License 13*a1a3b679SAndreas Boehler */ 14*a1a3b679SAndreas Boehlerabstract class Message implements MessageInterface { 15*a1a3b679SAndreas Boehler 16*a1a3b679SAndreas Boehler /** 17*a1a3b679SAndreas Boehler * Request body 18*a1a3b679SAndreas Boehler * 19*a1a3b679SAndreas Boehler * This should be a stream resource 20*a1a3b679SAndreas Boehler * 21*a1a3b679SAndreas Boehler * @var resource 22*a1a3b679SAndreas Boehler */ 23*a1a3b679SAndreas Boehler protected $body; 24*a1a3b679SAndreas Boehler 25*a1a3b679SAndreas Boehler /** 26*a1a3b679SAndreas Boehler * Contains the list of HTTP headers 27*a1a3b679SAndreas Boehler * 28*a1a3b679SAndreas Boehler * @var array 29*a1a3b679SAndreas Boehler */ 30*a1a3b679SAndreas Boehler protected $headers = []; 31*a1a3b679SAndreas Boehler 32*a1a3b679SAndreas Boehler /** 33*a1a3b679SAndreas Boehler * HTTP message version (1.0 or 1.1) 34*a1a3b679SAndreas Boehler * 35*a1a3b679SAndreas Boehler * @var string 36*a1a3b679SAndreas Boehler */ 37*a1a3b679SAndreas Boehler protected $httpVersion = '1.1'; 38*a1a3b679SAndreas Boehler 39*a1a3b679SAndreas Boehler /** 40*a1a3b679SAndreas Boehler * Returns the body as a readable stream resource. 41*a1a3b679SAndreas Boehler * 42*a1a3b679SAndreas Boehler * Note that the stream may not be rewindable, and therefore may only be 43*a1a3b679SAndreas Boehler * read once. 44*a1a3b679SAndreas Boehler * 45*a1a3b679SAndreas Boehler * @return resource 46*a1a3b679SAndreas Boehler */ 47*a1a3b679SAndreas Boehler function getBodyAsStream() { 48*a1a3b679SAndreas Boehler 49*a1a3b679SAndreas Boehler $body = $this->getBody(); 50*a1a3b679SAndreas Boehler if (is_string($body) || is_null($body)) { 51*a1a3b679SAndreas Boehler $stream = fopen('php://temp', 'r+'); 52*a1a3b679SAndreas Boehler fwrite($stream, $body); 53*a1a3b679SAndreas Boehler rewind($stream); 54*a1a3b679SAndreas Boehler return $stream; 55*a1a3b679SAndreas Boehler } 56*a1a3b679SAndreas Boehler return $body; 57*a1a3b679SAndreas Boehler 58*a1a3b679SAndreas Boehler } 59*a1a3b679SAndreas Boehler 60*a1a3b679SAndreas Boehler /** 61*a1a3b679SAndreas Boehler * Returns the body as a string. 62*a1a3b679SAndreas Boehler * 63*a1a3b679SAndreas Boehler * Note that because the underlying data may be based on a stream, this 64*a1a3b679SAndreas Boehler * method could only work correctly the first time. 65*a1a3b679SAndreas Boehler * 66*a1a3b679SAndreas Boehler * @return string 67*a1a3b679SAndreas Boehler */ 68*a1a3b679SAndreas Boehler function getBodyAsString() { 69*a1a3b679SAndreas Boehler 70*a1a3b679SAndreas Boehler $body = $this->getBody(); 71*a1a3b679SAndreas Boehler if (is_string($body)) { 72*a1a3b679SAndreas Boehler return $body; 73*a1a3b679SAndreas Boehler } 74*a1a3b679SAndreas Boehler if (is_null($body)) { 75*a1a3b679SAndreas Boehler return ''; 76*a1a3b679SAndreas Boehler } 77*a1a3b679SAndreas Boehler return stream_get_contents($body); 78*a1a3b679SAndreas Boehler 79*a1a3b679SAndreas Boehler } 80*a1a3b679SAndreas Boehler 81*a1a3b679SAndreas Boehler /** 82*a1a3b679SAndreas Boehler * Returns the message body, as it's internal representation. 83*a1a3b679SAndreas Boehler * 84*a1a3b679SAndreas Boehler * This could be either a string or a stream. 85*a1a3b679SAndreas Boehler * 86*a1a3b679SAndreas Boehler * @return resource|string 87*a1a3b679SAndreas Boehler */ 88*a1a3b679SAndreas Boehler function getBody() { 89*a1a3b679SAndreas Boehler 90*a1a3b679SAndreas Boehler return $this->body; 91*a1a3b679SAndreas Boehler 92*a1a3b679SAndreas Boehler } 93*a1a3b679SAndreas Boehler 94*a1a3b679SAndreas Boehler /** 95*a1a3b679SAndreas Boehler * Replaces the body resource with a new stream or string. 96*a1a3b679SAndreas Boehler * 97*a1a3b679SAndreas Boehler * @param resource|string $body 98*a1a3b679SAndreas Boehler */ 99*a1a3b679SAndreas Boehler function setBody($body) { 100*a1a3b679SAndreas Boehler 101*a1a3b679SAndreas Boehler $this->body = $body; 102*a1a3b679SAndreas Boehler 103*a1a3b679SAndreas Boehler } 104*a1a3b679SAndreas Boehler 105*a1a3b679SAndreas Boehler /** 106*a1a3b679SAndreas Boehler * Returns all the HTTP headers as an array. 107*a1a3b679SAndreas Boehler * 108*a1a3b679SAndreas Boehler * Every header is returned as an array, with one or more values. 109*a1a3b679SAndreas Boehler * 110*a1a3b679SAndreas Boehler * @return array 111*a1a3b679SAndreas Boehler */ 112*a1a3b679SAndreas Boehler function getHeaders() { 113*a1a3b679SAndreas Boehler 114*a1a3b679SAndreas Boehler $result = []; 115*a1a3b679SAndreas Boehler foreach ($this->headers as $headerInfo) { 116*a1a3b679SAndreas Boehler $result[$headerInfo[0]] = $headerInfo[1]; 117*a1a3b679SAndreas Boehler } 118*a1a3b679SAndreas Boehler return $result; 119*a1a3b679SAndreas Boehler 120*a1a3b679SAndreas Boehler } 121*a1a3b679SAndreas Boehler 122*a1a3b679SAndreas Boehler /** 123*a1a3b679SAndreas Boehler * Will return true or false, depending on if a HTTP header exists. 124*a1a3b679SAndreas Boehler * 125*a1a3b679SAndreas Boehler * @param string $name 126*a1a3b679SAndreas Boehler * @return bool 127*a1a3b679SAndreas Boehler */ 128*a1a3b679SAndreas Boehler function hasHeader($name) { 129*a1a3b679SAndreas Boehler 130*a1a3b679SAndreas Boehler return isset($this->headers[strtolower($name)]); 131*a1a3b679SAndreas Boehler 132*a1a3b679SAndreas Boehler } 133*a1a3b679SAndreas Boehler 134*a1a3b679SAndreas Boehler /** 135*a1a3b679SAndreas Boehler * Returns a specific HTTP header, based on it's name. 136*a1a3b679SAndreas Boehler * 137*a1a3b679SAndreas Boehler * The name must be treated as case-insensitive. 138*a1a3b679SAndreas Boehler * If the header does not exist, this method must return null. 139*a1a3b679SAndreas Boehler * 140*a1a3b679SAndreas Boehler * If a header appeared more than once in a HTTP request, this method will 141*a1a3b679SAndreas Boehler * concatenate all the values with a comma. 142*a1a3b679SAndreas Boehler * 143*a1a3b679SAndreas Boehler * Note that this not make sense for all headers. Some, such as 144*a1a3b679SAndreas Boehler * `Set-Cookie` cannot be logically combined with a comma. In those cases 145*a1a3b679SAndreas Boehler * you *should* use getHeaderAsArray(). 146*a1a3b679SAndreas Boehler * 147*a1a3b679SAndreas Boehler * @param string $name 148*a1a3b679SAndreas Boehler * @return string|null 149*a1a3b679SAndreas Boehler */ 150*a1a3b679SAndreas Boehler function getHeader($name) { 151*a1a3b679SAndreas Boehler 152*a1a3b679SAndreas Boehler $name = strtolower($name); 153*a1a3b679SAndreas Boehler 154*a1a3b679SAndreas Boehler if (isset($this->headers[$name])) { 155*a1a3b679SAndreas Boehler return implode(',', $this->headers[$name][1]); 156*a1a3b679SAndreas Boehler } 157*a1a3b679SAndreas Boehler return null; 158*a1a3b679SAndreas Boehler 159*a1a3b679SAndreas Boehler } 160*a1a3b679SAndreas Boehler 161*a1a3b679SAndreas Boehler /** 162*a1a3b679SAndreas Boehler * Returns a HTTP header as an array. 163*a1a3b679SAndreas Boehler * 164*a1a3b679SAndreas Boehler * For every time the HTTP header appeared in the request or response, an 165*a1a3b679SAndreas Boehler * item will appear in the array. 166*a1a3b679SAndreas Boehler * 167*a1a3b679SAndreas Boehler * If the header did not exists, this method will return an empty array. 168*a1a3b679SAndreas Boehler * 169*a1a3b679SAndreas Boehler * @param string $name 170*a1a3b679SAndreas Boehler * @return string[] 171*a1a3b679SAndreas Boehler */ 172*a1a3b679SAndreas Boehler function getHeaderAsArray($name) { 173*a1a3b679SAndreas Boehler 174*a1a3b679SAndreas Boehler $name = strtolower($name); 175*a1a3b679SAndreas Boehler 176*a1a3b679SAndreas Boehler if (isset($this->headers[$name])) { 177*a1a3b679SAndreas Boehler return $this->headers[$name][1]; 178*a1a3b679SAndreas Boehler } 179*a1a3b679SAndreas Boehler 180*a1a3b679SAndreas Boehler return []; 181*a1a3b679SAndreas Boehler 182*a1a3b679SAndreas Boehler } 183*a1a3b679SAndreas Boehler 184*a1a3b679SAndreas Boehler /** 185*a1a3b679SAndreas Boehler * Updates a HTTP header. 186*a1a3b679SAndreas Boehler * 187*a1a3b679SAndreas Boehler * The case-sensitity of the name value must be retained as-is. 188*a1a3b679SAndreas Boehler * 189*a1a3b679SAndreas Boehler * If the header already existed, it will be overwritten. 190*a1a3b679SAndreas Boehler * 191*a1a3b679SAndreas Boehler * @param string $name 192*a1a3b679SAndreas Boehler * @param string|string[] $value 193*a1a3b679SAndreas Boehler * @return void 194*a1a3b679SAndreas Boehler */ 195*a1a3b679SAndreas Boehler function setHeader($name, $value) { 196*a1a3b679SAndreas Boehler 197*a1a3b679SAndreas Boehler $this->headers[strtolower($name)] = [$name, (array)$value]; 198*a1a3b679SAndreas Boehler 199*a1a3b679SAndreas Boehler } 200*a1a3b679SAndreas Boehler 201*a1a3b679SAndreas Boehler /** 202*a1a3b679SAndreas Boehler * Sets a new set of HTTP headers. 203*a1a3b679SAndreas Boehler * 204*a1a3b679SAndreas Boehler * The headers array should contain headernames for keys, and their value 205*a1a3b679SAndreas Boehler * should be specified as either a string or an array. 206*a1a3b679SAndreas Boehler * 207*a1a3b679SAndreas Boehler * Any header that already existed will be overwritten. 208*a1a3b679SAndreas Boehler * 209*a1a3b679SAndreas Boehler * @param array $headers 210*a1a3b679SAndreas Boehler * @return void 211*a1a3b679SAndreas Boehler */ 212*a1a3b679SAndreas Boehler function setHeaders(array $headers) { 213*a1a3b679SAndreas Boehler 214*a1a3b679SAndreas Boehler foreach ($headers as $name => $value) { 215*a1a3b679SAndreas Boehler $this->setHeader($name, $value); 216*a1a3b679SAndreas Boehler } 217*a1a3b679SAndreas Boehler 218*a1a3b679SAndreas Boehler } 219*a1a3b679SAndreas Boehler 220*a1a3b679SAndreas Boehler /** 221*a1a3b679SAndreas Boehler * Adds a HTTP header. 222*a1a3b679SAndreas Boehler * 223*a1a3b679SAndreas Boehler * This method will not overwrite any existing HTTP header, but instead add 224*a1a3b679SAndreas Boehler * another value. Individual values can be retrieved with 225*a1a3b679SAndreas Boehler * getHeadersAsArray. 226*a1a3b679SAndreas Boehler * 227*a1a3b679SAndreas Boehler * @param string $name 228*a1a3b679SAndreas Boehler * @param string $value 229*a1a3b679SAndreas Boehler * @return void 230*a1a3b679SAndreas Boehler */ 231*a1a3b679SAndreas Boehler function addHeader($name, $value) { 232*a1a3b679SAndreas Boehler 233*a1a3b679SAndreas Boehler $lName = strtolower($name); 234*a1a3b679SAndreas Boehler if (isset($this->headers[$lName])) { 235*a1a3b679SAndreas Boehler $this->headers[$lName][1] = array_merge( 236*a1a3b679SAndreas Boehler $this->headers[$lName][1], 237*a1a3b679SAndreas Boehler (array)$value 238*a1a3b679SAndreas Boehler ); 239*a1a3b679SAndreas Boehler } else { 240*a1a3b679SAndreas Boehler $this->headers[$lName] = [ 241*a1a3b679SAndreas Boehler $name, 242*a1a3b679SAndreas Boehler (array)$value 243*a1a3b679SAndreas Boehler ]; 244*a1a3b679SAndreas Boehler } 245*a1a3b679SAndreas Boehler 246*a1a3b679SAndreas Boehler } 247*a1a3b679SAndreas Boehler 248*a1a3b679SAndreas Boehler /** 249*a1a3b679SAndreas Boehler * Adds a new set of HTTP headers. 250*a1a3b679SAndreas Boehler * 251*a1a3b679SAndreas Boehler * Any existing headers will not be overwritten. 252*a1a3b679SAndreas Boehler * 253*a1a3b679SAndreas Boehler * @param array $headers 254*a1a3b679SAndreas Boehler * @return void 255*a1a3b679SAndreas Boehler */ 256*a1a3b679SAndreas Boehler function addHeaders(array $headers) { 257*a1a3b679SAndreas Boehler 258*a1a3b679SAndreas Boehler foreach ($headers as $name => $value) { 259*a1a3b679SAndreas Boehler $this->addHeader($name, $value); 260*a1a3b679SAndreas Boehler } 261*a1a3b679SAndreas Boehler 262*a1a3b679SAndreas Boehler } 263*a1a3b679SAndreas Boehler 264*a1a3b679SAndreas Boehler 265*a1a3b679SAndreas Boehler /** 266*a1a3b679SAndreas Boehler * Removes a HTTP header. 267*a1a3b679SAndreas Boehler * 268*a1a3b679SAndreas Boehler * The specified header name must be treated as case-insenstive. 269*a1a3b679SAndreas Boehler * This method should return true if the header was successfully deleted, 270*a1a3b679SAndreas Boehler * and false if the header did not exist. 271*a1a3b679SAndreas Boehler * 272*a1a3b679SAndreas Boehler * @return bool 273*a1a3b679SAndreas Boehler */ 274*a1a3b679SAndreas Boehler function removeHeader($name) { 275*a1a3b679SAndreas Boehler 276*a1a3b679SAndreas Boehler $name = strtolower($name); 277*a1a3b679SAndreas Boehler if (!isset($this->headers[$name])) { 278*a1a3b679SAndreas Boehler return false; 279*a1a3b679SAndreas Boehler } 280*a1a3b679SAndreas Boehler unset($this->headers[$name]); 281*a1a3b679SAndreas Boehler return true; 282*a1a3b679SAndreas Boehler 283*a1a3b679SAndreas Boehler } 284*a1a3b679SAndreas Boehler 285*a1a3b679SAndreas Boehler /** 286*a1a3b679SAndreas Boehler * Sets the HTTP version. 287*a1a3b679SAndreas Boehler * 288*a1a3b679SAndreas Boehler * Should be 1.0 or 1.1. 289*a1a3b679SAndreas Boehler * 290*a1a3b679SAndreas Boehler * @param string $version 291*a1a3b679SAndreas Boehler * @return void 292*a1a3b679SAndreas Boehler */ 293*a1a3b679SAndreas Boehler function setHttpVersion($version) { 294*a1a3b679SAndreas Boehler 295*a1a3b679SAndreas Boehler $this->httpVersion = $version; 296*a1a3b679SAndreas Boehler 297*a1a3b679SAndreas Boehler } 298*a1a3b679SAndreas Boehler 299*a1a3b679SAndreas Boehler /** 300*a1a3b679SAndreas Boehler * Returns the HTTP version. 301*a1a3b679SAndreas Boehler * 302*a1a3b679SAndreas Boehler * @return string 303*a1a3b679SAndreas Boehler */ 304*a1a3b679SAndreas Boehler function getHttpVersion() { 305*a1a3b679SAndreas Boehler 306*a1a3b679SAndreas Boehler return $this->httpVersion; 307*a1a3b679SAndreas Boehler 308*a1a3b679SAndreas Boehler } 309*a1a3b679SAndreas Boehler} 310