diff --git a/docs/usage-advanced.md b/docs/usage-advanced.md index 60bd8c5da..5ccda246e 100755 --- a/docs/usage-advanced.md +++ b/docs/usage-advanced.md @@ -1,6 +1,36 @@ Advanced Usage ============== +Session Handling +---------------- +Making multiple requests to the same site with similar options can be a pain, +since you end up repeating yourself. The Session object can be used to set +default parameters for these. + +Let's simulate communicating with GitHub. + +```php +$session = new Requests_Session('https://api.github.com/'); +$session->headers['X-ContactAuthor'] = 'rmccue'; +$session->useragent = 'My-Awesome-App'; + +$response = $session->get('/zen'); +``` + +You can use the `url`, `headers`, `data` and `options` properties of the Session +object to set the defaults for this session, and the constructor also takes +parameters in the same order as `Requests::request()`. Accessing any other +properties will set the corresponding key in the options array; that is: + +```php +// Setting the property... +$session->useragent = 'My-Awesome-App'; + +// ...is the same as setting the option +$session->options['useragent'] = 'My-Awesome-App'; +``` + + Secure Requests with SSL ------------------------ By default, HTTPS requests will use the most secure options available: diff --git a/examples/session.php b/examples/session.php new file mode 100755 index 000000000..ff44d240a --- /dev/null +++ b/examples/session.php @@ -0,0 +1,24 @@ +headers['Accept'] = 'application/json'; +$session->useragent = 'Awesomesauce'; + +// Now let's make a request! +$request = $session->get('/get'); + +// Check what we received +var_dump($request); + +// Let's check our user agent! +$request = $session->get('/user-agent'); + +// And check again +var_dump($request); diff --git a/library/Requests/Session.php b/library/Requests/Session.php new file mode 100755 index 000000000..f03995132 --- /dev/null +++ b/library/Requests/Session.php @@ -0,0 +1,252 @@ +useragent = 'X';` + * + * @var array + */ + public $options = array(); + + /** + * Create a new session + * + * @param string|null $url Base URL for requests + * @param array $headers Default headers for requests + * @param array $data Default data for requests + * @param array $options Default options for requests + */ + public function __construct($url = null, $headers = array(), $data = array(), $options = array()) { + $this->url = $url; + $this->headers = $headers; + $this->data = $data; + $this->options = $options; + + if (empty($this->options['cookies'])) { + $this->options['cookies'] = new Requests_Cookie_Jar(); + } + } + + /** + * Get a property's value + * + * @param string $key Property key + * @return mixed|null Property value, null if none found + */ + public function __get($key) { + if (isset($this->options[$key])) + return $this->options[$key]; + + return null; + } + + /** + * Set a property's value + * + * @param string $key Property key + * @param mixed $value Property value + */ + public function __set($key, $value) { + $this->options[$key] = $value; + } + + /** + * Remove a property's value + * + * @param string $key Property key + */ + public function __isset($key) { + return isset($this->options[$key]); + } + + /** + * Remove a property's value + * + * @param string $key Property key + */ + public function __unset($key) { + $this->options[$key] = null; + } + + /**#@+ + * @see request() + * @param string $url + * @param array $headers + * @param array $options + * @return Requests_Response + */ + /** + * Send a GET request + */ + public function get($url, $headers = array(), $options = array()) { + return $this->request($url, $headers, null, Requests::GET, $options); + } + + /** + * Send a HEAD request + */ + public function head($url, $headers = array(), $options = array()) { + return $this->request($url, $headers, null, Requests::HEAD, $options); + } + + /** + * Send a DELETE request + */ + public function delete($url, $headers = array(), $options = array()) { + return $this->request($url, $headers, null, Requests::DELETE, $options); + } + + /**#@+ + * @see request() + * @param string $url + * @param array $headers + * @param array $data + * @param array $options + * @return Requests_Response + */ + /** + * Send a POST request + */ + public function post($url, $headers = array(), $data = array(), $options = array()) { + return $this->request($url, $headers, $data, Requests::POST, $options); + } + + /** + * Send a PUT request + */ + public function put($url, $headers = array(), $data = array(), $options = array()) { + return $this->request($url, $headers, $data, Requests::PUT, $options); + } + + /** + * Send a PATCH request + * + * Note: Unlike {@see post} and {@see put}, `$headers` is required, as the + * specification recommends that should send an ETag + * + * @link http://tools.ietf.org/html/rfc5789 + */ + public function patch($url, $headers, $data = array(), $options = array()) { + return $this->request($url, $headers, $data, Requests::PATCH, $options); + } + /**#@-*/ + + /** + * Main interface for HTTP requests + * + * This method initiates a request and sends it via a transport before + * parsing. + * + * @see Requests::request() + * + * @throws Requests_Exception On invalid URLs (`nonhttp`) + * + * @param string $url URL to request + * @param array $headers Extra headers to send with the request + * @param array $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests + * @param string $type HTTP request type (use Requests constants) + * @param array $options Options for the request (see {@see Requests::request}) + * @return Requests_Response + */ + public function request($url, $headers = array(), $data = array(), $type = Requests::GET, $options = array()) { + $request = $this->merge_request(compact('url', 'headers', 'data', 'options')); + + return Requests::request($request['url'], $request['headers'], $request['data'], $type, $request['options']); + } + + /** + * Send multiple HTTP requests simultaneously + * + * @see Requests::request_multiple() + * + * @param array $requests Requests data (see {@see Requests::request_multiple}) + * @param array $options Global and default options (see {@see Requests::request}) + * @return array Responses (either Requests_Response or a Requests_Exception object) + */ + public function request_multiple($requests, $options = array()) { + foreach ($requests as $key => $request) { + $requests[$key] = $this->merge_request($request, false); + } + + $options = array_merge($this->options, $options); + + // Disallow forcing the type, as that's a per request setting + unset($options['type']); + + return Requests::request_multiple($requests, $options); + } + + /** + * Merge a request's data with the default data + * + * @param array $request Request data (same form as {@see request_multiple}) + * @param boolean $merge_options Should we merge options as well? + * @return array Request data + */ + protected function merge_request($request, $merge_options = true) { + if ($this->url !== null) { + $request['url'] = Requests_IRI::absolutize($this->url, $request['url']); + $request['url'] = $request['url']->uri; + } + $request['headers'] = array_merge($this->headers, $request['headers']); + + if (is_array($request['data']) && is_array($this->data)) { + $request['data'] = array_merge($this->data, $request['data']); + } + + if ($merge_options !== false) { + $request['options'] = array_merge($this->options, $request['options']); + + // Disallow forcing the type, as that's a per request setting + unset($request['options']['type']); + } + return $request; + } +} \ No newline at end of file diff --git a/tests/Session.php b/tests/Session.php new file mode 100755 index 000000000..676c609da --- /dev/null +++ b/tests/Session.php @@ -0,0 +1,86 @@ + 'testing', + 'X-TestHeader2' => 'requests-test' + ); + $data = array( + 'testdata' => 'value1', + 'test2' => 'value2', + 'test3' => array( + 'foo' => 'bar', + 'abc' => 'xyz' + ) + ); + $options = array( + 'testoption' => 'test', + 'foo' => 'bar' + ); + + $session = new Requests_Session('http://example.com/', $headers, $data, $options); + $this->assertEquals('http://example.com/', $session->url); + $this->assertEquals($headers, $session->headers); + $this->assertEquals($data, $session->data); + $this->assertEquals($options['testoption'], $session->options['testoption']); + + // Test via property access + $this->assertEquals($options['testoption'], $session->testoption); + + // Test setting new property + $session->newoption = 'foobar'; + $options['newoption'] = 'foobar'; + $this->assertEquals($options['newoption'], $session->options['newoption']); + + // Test unsetting property + unset($session->newoption); + $this->assertFalse(isset($session->newoption)); + + // Update property + $session->testoption = 'foobar'; + $options['testoption'] = 'foobar'; + $this->assertEquals($options['testoption'], $session->testoption); + + // Test getting invalid property + $this->assertNull($session->invalidoption); + } + + public function testURLResolution() { + $session = new Requests_Session('http://httpbin.org/'); + + // Set the cookies up + $response = $session->get('/get'); + $this->assertTrue($response->success); + $this->assertEquals('http://httpbin.org/get', $response->url); + + $data = json_decode($response->body, true); + $this->assertNotNull($data); + $this->assertArrayHasKey('url', $data); + $this->assertEquals('http://httpbin.org/get', $data['url']); + } + + public function testSharedCookies() { + $session = new Requests_Session('http://httpbin.org/'); + + $options = array( + 'follow_redirects' => false + ); + $response = $session->get('/cookies/set?requests-testcookie=testvalue', array(), $options); + $this->assertEquals(302, $response->status_code); + + // Check the cookies + $response = $session->get('/cookies'); + $this->assertTrue($response->success); + + // Check the response + $data = json_decode($response->body, true); + $this->assertNotNull($data); + $this->assertArrayHasKey('cookies', $data); + + $cookies = array( + 'requests-testcookie' => 'testvalue' + ); + $this->assertEquals($cookies, $data['cookies']); + } +}