mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-21 22:32:41 +01:00
Remove WePay support from Phortune, and Restful/Httpful dependencies
Summary: Ref PHI1166. I'm documenting our dependencies, and we have approximately 5,000 lines of external code to support WePay as a Phortune provider. We don't use it, I'm almost certain it doesn't work, and we have no plans to use it in the near future. If we did pursue it, I'd probably just wrap the API in a 100-line `WePayFuture` anyway since 5K lines of dependencies to make a couple method calls is ridiculous. Test Plan: Grepped for `wepay`, `httpful`, `restful`. Reviewers: amckinley Reviewed By: amckinley Subscribers: aurelijus Differential Revision: https://secure.phabricator.com/D20521
This commit is contained in:
parent
7a8d489ebd
commit
1d58f14469
75 changed files with 0 additions and 5450 deletions
4
externals/httpful/.gitignore
vendored
4
externals/httpful/.gitignore
vendored
|
@ -1,4 +0,0 @@
|
|||
.DS_Store
|
||||
composer.lock
|
||||
vendor
|
||||
downloads
|
5
externals/httpful/.travis.yml
vendored
5
externals/httpful/.travis.yml
vendored
|
@ -1,5 +0,0 @@
|
|||
language: php
|
||||
before_script: cd tests
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
7
externals/httpful/LICENSE.txt
vendored
7
externals/httpful/LICENSE.txt
vendored
|
@ -1,7 +0,0 @@
|
|||
Copyright (c) 2012 Nate Good <me@nategood.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
137
externals/httpful/README.md
vendored
137
externals/httpful/README.md
vendored
|
@ -1,137 +0,0 @@
|
|||
# Httpful
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/nategood/httpful.png?branch=master)](http://travis-ci.org/nategood/httpful)
|
||||
|
||||
[Httpful](http://phphttpclient.com) is a simple Http Client library for PHP 5.3+. There is an emphasis of readability, simplicity, and flexibility – basically provide the features and flexibility to get the job done and make those features really easy to use.
|
||||
|
||||
Features
|
||||
|
||||
- Readable HTTP Method Support (GET, PUT, POST, DELETE, HEAD, PATCH and OPTIONS)
|
||||
- Custom Headers
|
||||
- Automatic "Smart" Parsing
|
||||
- Automatic Payload Serialization
|
||||
- Basic Auth
|
||||
- Client Side Certificate Auth
|
||||
- Request "Templates"
|
||||
|
||||
# Sneak Peak
|
||||
|
||||
Here's something to whet your appetite. Search the twitter API for tweets containing "#PHP". Include a trivial header for the heck of it. Notice that the library automatically interprets the response as JSON (can override this if desired) and parses it as an array of objects.
|
||||
|
||||
$url = "http://search.twitter.com/search.json?q=" . urlencode('#PHP');
|
||||
$response = Request::get($url)
|
||||
->withXTrivialHeader('Just as a demo')
|
||||
->send();
|
||||
|
||||
foreach ($response->body->results as $tweet) {
|
||||
echo "@{$tweet->from_user} tweets \"{$tweet->text}\"\n";
|
||||
}
|
||||
|
||||
# Installation
|
||||
|
||||
## Phar
|
||||
|
||||
A [PHP Archive](http://php.net/manual/en/book.phar.php) (or .phar) file is available for [downloading](https://github.com/nategood/httpful/downloads). Simply [download](https://github.com/nategood/httpful/downloads) the .phar, drop it into your project, and include it like you would any other php file. _This method is ideal smaller projects, one off scripts, and quick API hacking_.
|
||||
|
||||
<?php
|
||||
include('httpful.phar');
|
||||
$r = \Httpful\Request::get($uri)->sendIt();
|
||||
...
|
||||
|
||||
## Composer
|
||||
|
||||
Httpful is PSR-0 compliant and can be installed using [composer](http://getcomposer.org/). Simply add `nategood/httpful` to your composer.json file. _Composer is the sane alternative to PEAR. It is excellent for managing dependancies in larger projects_.
|
||||
|
||||
{
|
||||
"require": {
|
||||
"nategood/httpful": "*"
|
||||
}
|
||||
}
|
||||
|
||||
## Install from Source
|
||||
|
||||
Because Httpful is PSR-0 compliant, you can also just clone the Httpful repository and use a PSR-0 compatible autoloader to load the library, like [Symfony's](http://symfony.com/doc/current/components/class_loader.html). Alternatively you can use the PSR-0 compliant autoloader included with the Httpful (simply `require("bootstrap.php")`).
|
||||
|
||||
# Show Me More!
|
||||
|
||||
You can checkout the [Httpful Landing Page](http://phphttpclient.com) for more info including many examples and [documentation](http:://phphttpclient.com/docs).
|
||||
|
||||
# Contributing
|
||||
|
||||
Httpful highly encourages sending in pull requests. When submitting a pull request please:
|
||||
|
||||
- All pull requests should target the `dev` branch (not `master`)
|
||||
- Make sure your code follows the [coding conventions](http://pear.php.net/manual/en/standards.php)
|
||||
- Please use soft tabs (four spaces) instead of hard tabs
|
||||
- Make sure you add appropriate test coverage for your changes
|
||||
- Run all unit tests in the test directory via `phpunit ./tests`
|
||||
- Include commenting where appropriate and add a descriptive pull request message
|
||||
|
||||
# Changelog
|
||||
|
||||
## 0.2.3
|
||||
|
||||
- FIX Overriding default Mime Handlers
|
||||
- FIX [PR #73](https://github.com/nategood/httpful/pull/73) Parsing http status codes
|
||||
|
||||
## 0.2.2
|
||||
|
||||
- FEATURE Add support for parsing JSON responses as associative arrays instead of objects
|
||||
- FEATURE Better support for setting constructor arguments on Mime Handlers
|
||||
|
||||
## 0.2.1
|
||||
|
||||
- FEATURE [PR #72](https://github.com/nategood/httpful/pull/72) Allow support for custom Accept header
|
||||
|
||||
## 0.2.0
|
||||
|
||||
- REFACTOR [PR #49](https://github.com/nategood/httpful/pull/49) Broke headers out into their own class
|
||||
- REFACTOR [PR #54](https://github.com/nategood/httpful/pull/54) Added more specific Exceptions
|
||||
- FIX [PR #58](https://github.com/nategood/httpful/pull/58) Fixes throwing an error on an empty xml response
|
||||
- FEATURE [PR #57](https://github.com/nategood/httpful/pull/57) Adds support for digest authentication
|
||||
|
||||
## 0.1.6
|
||||
|
||||
- Ability to set the number of max redirects via overloading `followRedirects(int max_redirects)`
|
||||
- Standards Compliant fix to `Accepts` header
|
||||
- Bug fix for bootstrap process when installed via Composer
|
||||
|
||||
## 0.1.5
|
||||
|
||||
- Use `DIRECTORY_SEPARATOR` constant [PR #33](https://github.com/nategood/httpful/pull/32)
|
||||
- [PR #35](https://github.com/nategood/httpful/pull/35)
|
||||
- Added the raw\_headers property reference to response.
|
||||
- Compose request header and added raw\_header to Request object.
|
||||
- Fixed response has errors and added more comments for clarity.
|
||||
- Fixed header parsing to allow the minimum (status line only) and also cater for the actual CRLF ended headers as per RFC2616.
|
||||
- Added the perfect test Accept: header for all Acceptable scenarios see @b78e9e82cd9614fbe137c01bde9439c4e16ca323 for details.
|
||||
- Added default User-Agent header
|
||||
- `User-Agent: Httpful/0.1.5` + curl version + server software + PHP version
|
||||
- To bypass this "default" operation simply add a User-Agent to the request headers even a blank User-Agent is sufficient and more than simple enough to produce me thinks.
|
||||
- Completed test units for additions.
|
||||
- Added phpunit coverage reporting and helped phpunit auto locate the tests a bit easier.
|
||||
|
||||
## 0.1.4
|
||||
|
||||
- Add support for CSV Handling [PR #32](https://github.com/nategood/httpful/pull/32)
|
||||
|
||||
## 0.1.3
|
||||
|
||||
- Handle empty responses in JsonParser and XmlParser
|
||||
|
||||
## 0.1.2
|
||||
|
||||
- Added support for setting XMLHandler configuration options
|
||||
- Added examples for overriding XmlHandler and registering a custom parser
|
||||
- Removed the httpful.php download (deprecated in favor of httpful.phar)
|
||||
|
||||
## 0.1.1
|
||||
|
||||
- Bug fix serialization default case and phpunit tests
|
||||
|
||||
## 0.1.0
|
||||
|
||||
- Added Support for Registering Mime Handlers
|
||||
- Created AbstractMimeHandler type that all Mime Handlers must extend
|
||||
- Pulled out the parsing/serializing logic from the Request/Response classes into their own MimeHandler classes
|
||||
- Added ability to register new mime handlers for mime types
|
4
externals/httpful/bootstrap.php
vendored
4
externals/httpful/bootstrap.php
vendored
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
|
||||
require(__DIR__ . '/src/Httpful/Bootstrap.php');
|
||||
\Httpful\Bootstrap::init();
|
51
externals/httpful/build
vendored
51
externals/httpful/build
vendored
|
@ -1,51 +0,0 @@
|
|||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Build the whole library into a single file
|
||||
* as an easy drop in solution as opposed to
|
||||
* relying on autoloader. Sometimes we just
|
||||
* want to hack with an API as a one off thing.
|
||||
* Httpful should make this easy.
|
||||
*/
|
||||
|
||||
function exit_unless($condition, $msg = null) {
|
||||
if ($condition)
|
||||
return;
|
||||
echo "[FAIL]\n$msg\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Create the Httpful Phar
|
||||
echo "Building Phar... ";
|
||||
$base_dir = dirname(__FILE__);
|
||||
$source_dir = $base_dir . '/src/Httpful/';
|
||||
$phar_path = $base_dir . '/downloads/httpful.phar';
|
||||
$phar = new Phar($phar_path, 0, 'httpful.phar');
|
||||
$stub = <<<HEREDOC
|
||||
<?php
|
||||
// Phar Stub File
|
||||
Phar::mapPhar('httpful.phar');
|
||||
include('phar://httpful.phar/Httpful/Bootstrap.php');
|
||||
\Httpful\Bootstrap::pharInit();
|
||||
|
||||
__HALT_COMPILER();
|
||||
HEREDOC;
|
||||
try {
|
||||
$phar->setStub($stub);
|
||||
} catch(Exception $e) {
|
||||
$phar = false;
|
||||
}
|
||||
exit_unless($phar, "Unable to create a phar. Make certain you have phar.readonly=0 set in your ini file.");
|
||||
$phar->buildFromDirectory(dirname($source_dir));
|
||||
echo "[ OK ]\n";
|
||||
|
||||
|
||||
|
||||
// Add it to git!
|
||||
echo "Adding httpful.phar to the repo... ";
|
||||
$return_code = 0;
|
||||
passthru("git add $phar_path", $return_code);
|
||||
exit_unless($return_code === 0, "Unable to add download files to git.");
|
||||
echo "[ OK ]\n";
|
||||
echo "\nBuild completed sucessfully.\n\n";
|
24
externals/httpful/composer.json
vendored
24
externals/httpful/composer.json
vendored
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"name": "nategood/httpful",
|
||||
"description": "A Readable, Chainable, REST friendly, PHP HTTP Client",
|
||||
"homepage": "http://github.com/nategood/httpful",
|
||||
"license": "MIT",
|
||||
"keywords": ["http", "curl", "rest", "restful", "api", "requests"],
|
||||
"version": "0.2.3",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nate Good",
|
||||
"email": "me@nategood.com",
|
||||
"homepage": "http://nategood.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3",
|
||||
"ext-curl": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Httpful": "src/"
|
||||
}
|
||||
}
|
||||
}
|
12
externals/httpful/examples/freebase.php
vendored
12
externals/httpful/examples/freebase.php
vendored
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Grab some The Dead Weather albums from Freebase
|
||||
*/
|
||||
require(__DIR__ . '/../bootstrap.php');
|
||||
|
||||
$uri = "https://www.googleapis.com/freebase/v1/mqlread?query=%7B%22type%22:%22/music/artist%22%2C%22name%22:%22The%20Dead%20Weather%22%2C%22album%22:%5B%5D%7D";
|
||||
$response = \Httpful\Request::get($uri)
|
||||
->expectsJson()
|
||||
->sendIt();
|
||||
|
||||
echo 'The Dead Weather has ' . count($response->body->result->album) . " albums.\n";
|
9
externals/httpful/examples/github.php
vendored
9
externals/httpful/examples/github.php
vendored
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
// XML Example from GitHub
|
||||
require(__DIR__ . '/../bootstrap.php');
|
||||
use \Httpful\Request;
|
||||
|
||||
$uri = 'https://github.com/api/v2/xml/user/show/nategood';
|
||||
$request = Request::get($uri)->send();
|
||||
|
||||
echo "{$request->body->name} joined GitHub on " . date('M jS', strtotime($request->body->{'created-at'})) ."\n";
|
44
externals/httpful/examples/override.php
vendored
44
externals/httpful/examples/override.php
vendored
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
require(__DIR__ . '/../bootstrap.php');
|
||||
|
||||
// We can override the default parser configuration options be registering
|
||||
// a parser with different configuration options for a particular mime type
|
||||
|
||||
// Example setting a namespace for the XMLHandler parser
|
||||
$conf = array('namespace' => 'http://example.com');
|
||||
\Httpful\Httpful::register(\Httpful\Mime::XML, new \Httpful\Handlers\XmlHandler($conf));
|
||||
|
||||
// We can also add the parsers with our own...
|
||||
class SimpleCsvHandler extends \Httpful\Handlers\MimeHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* Takes a response body, and turns it into
|
||||
* a two dimensional array.
|
||||
*
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
return str_getcsv($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a two dimensional array and turns it
|
||||
* into a serialized string to include as the
|
||||
* body of a request
|
||||
*
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
$serialized = '';
|
||||
foreach ($payload as $line) {
|
||||
$serialized .= '"' . implode('","', $line) . '"' . "\n";
|
||||
}
|
||||
return $serialized;
|
||||
}
|
||||
}
|
||||
|
||||
\Httpful\Httpful::register('text/csv', new SimpleCsvHandler());
|
24
externals/httpful/examples/showclix.php
vendored
24
externals/httpful/examples/showclix.php
vendored
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
require(__DIR__ . '/../bootstrap.php');
|
||||
|
||||
use \Httpful\Request;
|
||||
|
||||
// Get event details for a public event
|
||||
$uri = "http://api.showclix.com/Event/8175";
|
||||
$response = Request::get($uri)
|
||||
->expectsType('json')
|
||||
->sendIt();
|
||||
|
||||
// Print out the event details
|
||||
echo "The event {$response->body->event} will take place on {$response->body->event_start}\n";
|
||||
|
||||
// Example overriding the default JSON handler with one that encodes the response as an array
|
||||
\Httpful\Httpful::register(\Httpful\Mime::JSON, new \Httpful\Handlers\JsonHandler(array('decode_as_array' => true)));
|
||||
|
||||
$response = Request::get($uri)
|
||||
->expectsType('json')
|
||||
->sendIt();
|
||||
|
||||
// Print out the event details
|
||||
echo "The event {$response->body['event']} will take place on {$response->body['event_start']}\n";
|
13
externals/httpful/examples/twitter.php
vendored
13
externals/httpful/examples/twitter.php
vendored
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
require(__DIR__ . '/../bootstrap.php');
|
||||
|
||||
$query = urlencode('#PHP');
|
||||
$response = \Httpful\Request::get("http://search.twitter.com/search.json?q=$query")->send();
|
||||
|
||||
if (!$response->hasErrors()) {
|
||||
foreach ($response->body->results as $tweet) {
|
||||
echo "@{$tweet->from_user} tweets \"{$tweet->text}\"\n";
|
||||
}
|
||||
} else {
|
||||
echo "Uh oh. Twitter gave us the old {$response->code} status.\n";
|
||||
}
|
97
externals/httpful/src/Httpful/Bootstrap.php
vendored
97
externals/httpful/src/Httpful/Bootstrap.php
vendored
|
@ -1,97 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
/**
|
||||
* Bootstrap class that facilitates autoloading. A naive
|
||||
* PSR-0 autoloader.
|
||||
*
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
class Bootstrap
|
||||
{
|
||||
|
||||
const DIR_GLUE = DIRECTORY_SEPARATOR;
|
||||
const NS_GLUE = '\\';
|
||||
|
||||
public static $registered = false;
|
||||
|
||||
/**
|
||||
* Register the autoloader and any other setup needed
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
spl_autoload_register(array('\Httpful\Bootstrap', 'autoload'));
|
||||
self::registerHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* The autoload magic (PSR-0 style)
|
||||
*
|
||||
* @param string $classname
|
||||
*/
|
||||
public static function autoload($classname)
|
||||
{
|
||||
self::_autoload(dirname(dirname(__FILE__)), $classname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the autoloader and any other setup needed
|
||||
*/
|
||||
public static function pharInit()
|
||||
{
|
||||
spl_autoload_register(array('\Httpful\Bootstrap', 'pharAutoload'));
|
||||
self::registerHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Phar specific autoloader
|
||||
*
|
||||
* @param string $classname
|
||||
*/
|
||||
public static function pharAutoload($classname)
|
||||
{
|
||||
self::_autoload('phar://httpful.phar', $classname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string base
|
||||
* @param string classname
|
||||
*/
|
||||
private static function _autoload($base, $classname)
|
||||
{
|
||||
$parts = explode(self::NS_GLUE, $classname);
|
||||
$path = $base . self::DIR_GLUE . implode(self::DIR_GLUE, $parts) . '.php';
|
||||
|
||||
if (file_exists($path)) {
|
||||
require_once($path);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Register default mime handlers. Is idempotent.
|
||||
*/
|
||||
public static function registerHandlers()
|
||||
{
|
||||
if (self::$registered === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo check a conf file to load from that instead of
|
||||
// hardcoding into the library?
|
||||
$handlers = array(
|
||||
\Httpful\Mime::JSON => new \Httpful\Handlers\JsonHandler(),
|
||||
\Httpful\Mime::XML => new \Httpful\Handlers\XmlHandler(),
|
||||
\Httpful\Mime::FORM => new \Httpful\Handlers\FormHandler(),
|
||||
\Httpful\Mime::CSV => new \Httpful\Handlers\CsvHandler(),
|
||||
);
|
||||
|
||||
foreach ($handlers as $mime => $handler) {
|
||||
// Don't overwrite if the handler has already been registered
|
||||
if (Httpful::hasParserRegistered($mime))
|
||||
continue;
|
||||
Httpful::register($mime, $handler);
|
||||
}
|
||||
|
||||
self::$registered = true;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Httpful\Exception;
|
||||
|
||||
class ConnectionErrorException extends \Exception
|
||||
{
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Mime Type: text/csv
|
||||
* @author Raja Kapur <rajak@twistedthrottle.com>
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class CsvHandler extends MimeHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
if (empty($body))
|
||||
return null;
|
||||
|
||||
$parsed = array();
|
||||
$fp = fopen('data://text/plain;base64,' . base64_encode($body), 'r');
|
||||
while (($r = fgetcsv($fp)) !== FALSE) {
|
||||
$parsed[] = $r;
|
||||
}
|
||||
|
||||
if (empty($parsed))
|
||||
throw new \Exception("Unable to parse response as CSV");
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
$fp = fopen('php://temp/maxmemory:'. (6*1024*1024), 'r+');
|
||||
$i = 0;
|
||||
foreach ($payload as $fields) {
|
||||
if($i++ == 0) {
|
||||
fputcsv($fp, array_keys($fields));
|
||||
}
|
||||
fputcsv($fp, $fields);
|
||||
}
|
||||
rewind($fp);
|
||||
$data = stream_get_contents($fp);
|
||||
fclose($fp);
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Mime Type: application/x-www-urlencoded
|
||||
* @author Nathan Good <me@nategood.com>
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class FormHandler extends MimeHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
$parsed = array();
|
||||
parse_str($body, $parsed);
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
return http_build_query($payload, null, '&');
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Mime Type: application/json
|
||||
* @author Nathan Good <me@nategood.com>
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class JsonHandler extends MimeHandlerAdapter
|
||||
{
|
||||
private $decode_as_array = false;
|
||||
|
||||
public function init(array $args)
|
||||
{
|
||||
$this->decode_as_array = !!(array_key_exists('decode_as_array', $args) ? $args['decode_as_array'] : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
if (empty($body))
|
||||
return null;
|
||||
$parsed = json_decode($body, $this->decode_as_array);
|
||||
if (is_null($parsed))
|
||||
throw new \Exception("Unable to parse response as JSON");
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
return json_encode($payload);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Handlers are used to parse and serialize payloads for specific
|
||||
* mime types. You can register a custom handler via the register
|
||||
* method. You can also override a default parser in this way.
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class MimeHandlerAdapter
|
||||
{
|
||||
public function __construct(array $args = array())
|
||||
{
|
||||
$this->init($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial setup of
|
||||
* @param array $args
|
||||
*/
|
||||
public function init(array $args)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
function serialize($payload)
|
||||
{
|
||||
return (string) $payload;
|
||||
}
|
||||
}
|
44
externals/httpful/src/Httpful/Handlers/README.md
vendored
44
externals/httpful/src/Httpful/Handlers/README.md
vendored
|
@ -1,44 +0,0 @@
|
|||
# Handlers
|
||||
|
||||
Handlers are simple classes that are used to parse response bodies and serialize request payloads. All Handlers must extend the `MimeHandlerAdapter` class and implement two methods: `serialize($payload)` and `parse($response)`. Let's build a very basic Handler to register for the `text/csv` mime type.
|
||||
|
||||
<?php
|
||||
|
||||
class SimpleCsvHandler extends \Httpful\Handlers\MimeHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* Takes a response body, and turns it into
|
||||
* a two dimensional array.
|
||||
*
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
return str_getcsv($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a two dimensional array and turns it
|
||||
* into a serialized string to include as the
|
||||
* body of a request
|
||||
*
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
$serialized = '';
|
||||
foreach ($payload as $line) {
|
||||
$serialized .= '"' . implode('","', $line) . '"' . "\n";
|
||||
}
|
||||
return $serialized;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Finally, you must register this handler for a particular mime type.
|
||||
|
||||
Httpful::register('text/csv', new SimpleCsvHandler());
|
||||
|
||||
After this registering the handler in your source code, by default, any responses with a mime type of text/csv should be parsed by this handler.
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Mime Type: text/html
|
||||
* Mime Type: application/html+xml
|
||||
*
|
||||
* @author Nathan Good <me@nategood.com>
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class XHtmlHandler extends MimeHandlerAdapter
|
||||
{
|
||||
// @todo add html specific parsing
|
||||
// see DomDocument::load http://docs.php.net/manual/en/domdocument.loadhtml.php
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Mime Type: application/xml
|
||||
*
|
||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
||||
* @author Nathan Good <me@nategood.com>
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class XmlHandler extends MimeHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* @var string $namespace xml namespace to use with simple_load_string
|
||||
*/
|
||||
private $namespace;
|
||||
|
||||
/**
|
||||
* @var int $libxml_opts see http://www.php.net/manual/en/libxml.constants.php
|
||||
*/
|
||||
private $libxml_opts;
|
||||
|
||||
/**
|
||||
* @param array $conf sets configuration options
|
||||
*/
|
||||
public function __construct(array $conf = array())
|
||||
{
|
||||
$this->namespace = isset($conf['namespace']) ? $conf['namespace'] : '';
|
||||
$this->libxml_opts = isset($conf['libxml_opts']) ? $conf['libxml_opts'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
if (empty($body))
|
||||
return null;
|
||||
$parsed = simplexml_load_string($body, null, $this->libxml_opts, $this->namespace);
|
||||
if ($parsed === false)
|
||||
throw new \Exception("Unable to parse response as XML");
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
* @throws Exception if unable to serialize
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
list($_, $dom) = $this->_future_serializeAsXml($payload);
|
||||
return $dom->saveXml();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
||||
*/
|
||||
private function _future_serializeAsXml($value, $node = null, $dom = null)
|
||||
{
|
||||
if (!$dom) {
|
||||
$dom = new \DOMDocument;
|
||||
}
|
||||
if (!$node) {
|
||||
if (!is_object($value)) {
|
||||
$node = $dom->createElement('response');
|
||||
$dom->appendChild($node);
|
||||
} else {
|
||||
$node = $dom;
|
||||
}
|
||||
}
|
||||
if (is_object($value)) {
|
||||
$objNode = $dom->createElement(get_class($value));
|
||||
$node->appendChild($objNode);
|
||||
$this->_future_serializeObjectAsXml($value, $objNode, $dom);
|
||||
} else if (is_array($value)) {
|
||||
$arrNode = $dom->createElement('array');
|
||||
$node->appendChild($arrNode);
|
||||
$this->_future_serializeArrayAsXml($value, $arrNode, $dom);
|
||||
} else if (is_bool($value)) {
|
||||
$node->appendChild($dom->createTextNode($value?'TRUE':'FALSE'));
|
||||
} else {
|
||||
$node->appendChild($dom->createTextNode($value));
|
||||
}
|
||||
return array($node, $dom);
|
||||
}
|
||||
/**
|
||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
||||
*/
|
||||
private function _future_serializeArrayAsXml($value, &$parent, &$dom)
|
||||
{
|
||||
foreach ($value as $k => &$v) {
|
||||
$n = $k;
|
||||
if (is_numeric($k)) {
|
||||
$n = "child-{$n}";
|
||||
}
|
||||
$el = $dom->createElement($n);
|
||||
$parent->appendChild($el);
|
||||
$this->_future_serializeAsXml($v, $el, $dom);
|
||||
}
|
||||
return array($parent, $dom);
|
||||
}
|
||||
/**
|
||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
||||
*/
|
||||
private function _future_serializeObjectAsXml($value, &$parent, &$dom)
|
||||
{
|
||||
$refl = new \ReflectionObject($value);
|
||||
foreach ($refl->getProperties() as $pr) {
|
||||
if (!$pr->isPrivate()) {
|
||||
$el = $dom->createElement($pr->getName());
|
||||
$parent->appendChild($el);
|
||||
$this->_future_serializeAsXml($pr->getValue($value), $el, $dom);
|
||||
}
|
||||
}
|
||||
return array($parent, $dom);
|
||||
}
|
||||
}
|
86
externals/httpful/src/Httpful/Http.php
vendored
86
externals/httpful/src/Httpful/Http.php
vendored
|
@ -1,86 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
/**
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
class Http
|
||||
{
|
||||
const HEAD = 'HEAD';
|
||||
const GET = 'GET';
|
||||
const POST = 'POST';
|
||||
const PUT = 'PUT';
|
||||
const DELETE = 'DELETE';
|
||||
const PATCH = 'PATCH';
|
||||
const OPTIONS = 'OPTIONS';
|
||||
const TRACE = 'TRACE';
|
||||
|
||||
/**
|
||||
* @return array of HTTP method strings
|
||||
*/
|
||||
public static function safeMethods()
|
||||
{
|
||||
return array(self::HEAD, self::GET, self::OPTIONS, self::TRACE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param string HTTP method
|
||||
*/
|
||||
public static function isSafeMethod($method)
|
||||
{
|
||||
return in_array($method, self::safeMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param string HTTP method
|
||||
*/
|
||||
public static function isUnsafeMethod($method)
|
||||
{
|
||||
return !in_array($method, self::safeMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array list of (always) idempotent HTTP methods
|
||||
*/
|
||||
public static function idempotentMethods()
|
||||
{
|
||||
// Though it is possible to be idempotent, POST
|
||||
// is not guarunteed to be, and more often than
|
||||
// not, it is not.
|
||||
return array(self::HEAD, self::GET, self::PUT, self::DELETE, self::OPTIONS, self::TRACE, self::PATCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param string HTTP method
|
||||
*/
|
||||
public static function isIdempotent($method)
|
||||
{
|
||||
return in_array($method, self::safeidempotentMethodsMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param string HTTP method
|
||||
*/
|
||||
public static function isNotIdempotent($method)
|
||||
{
|
||||
return !in_array($method, self::idempotentMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Technically anything *can* have a body,
|
||||
* they just don't have semantic meaning. So say's Roy
|
||||
* http://tech.groups.yahoo.com/group/rest-discuss/message/9962
|
||||
*
|
||||
* @return array of HTTP method strings
|
||||
*/
|
||||
public static function canHaveBody()
|
||||
{
|
||||
return array(self::POST, self::PUT, self::PATCH, self::OPTIONS);
|
||||
}
|
||||
|
||||
}
|
46
externals/httpful/src/Httpful/Httpful.php
vendored
46
externals/httpful/src/Httpful/Httpful.php
vendored
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
class Httpful {
|
||||
const VERSION = '0.1.7';
|
||||
|
||||
private static $mimeRegistrar = array();
|
||||
private static $default = null;
|
||||
|
||||
/**
|
||||
* @param string $mime_type
|
||||
* @param MimeHandlerAdapter $handler
|
||||
*/
|
||||
public static function register($mimeType, \Httpful\Handlers\MimeHandlerAdapter $handler)
|
||||
{
|
||||
self::$mimeRegistrar[$mimeType] = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mime_type defaults to MimeHandlerAdapter
|
||||
* @return MimeHandlerAdapter
|
||||
*/
|
||||
public static function get($mimeType = null)
|
||||
{
|
||||
if (isset(self::$mimeRegistrar[$mimeType])) {
|
||||
return self::$mimeRegistrar[$mimeType];
|
||||
}
|
||||
|
||||
if (empty(self::$default)) {
|
||||
self::$default = new \Httpful\Handlers\MimeHandlerAdapter();
|
||||
}
|
||||
|
||||
return self::$default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this particular Mime Type have a parser registered
|
||||
* for it?
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasParserRegistered($mimeType)
|
||||
{
|
||||
return isset(self::$mimeRegistrar[$mimeType]);
|
||||
}
|
||||
}
|
58
externals/httpful/src/Httpful/Mime.php
vendored
58
externals/httpful/src/Httpful/Mime.php
vendored
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
/**
|
||||
* Class to organize the Mime stuff a bit more
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
class Mime
|
||||
{
|
||||
const JSON = 'application/json';
|
||||
const XML = 'application/xml';
|
||||
const XHTML = 'application/html+xml';
|
||||
const FORM = 'application/x-www-form-urlencoded';
|
||||
const PLAIN = 'text/plain';
|
||||
const JS = 'text/javascript';
|
||||
const HTML = 'text/html';
|
||||
const YAML = 'application/x-yaml';
|
||||
const CSV = 'text/csv';
|
||||
|
||||
/**
|
||||
* Map short name for a mime type
|
||||
* to a full proper mime type
|
||||
*/
|
||||
public static $mimes = array(
|
||||
'json' => self::JSON,
|
||||
'xml' => self::XML,
|
||||
'form' => self::FORM,
|
||||
'plain' => self::PLAIN,
|
||||
'text' => self::PLAIN,
|
||||
'html' => self::HTML,
|
||||
'xhtml' => self::XHTML,
|
||||
'js' => self::JS,
|
||||
'javascript'=> self::JS,
|
||||
'yaml' => self::YAML,
|
||||
'csv' => self::CSV,
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the full Mime Type name from a "short name".
|
||||
* Returns the short if no mapping was found.
|
||||
* @return string full mime type (e.g. application/json)
|
||||
* @param string common name for mime type (e.g. json)
|
||||
*/
|
||||
public static function getFullMime($short_name)
|
||||
{
|
||||
return array_key_exists($short_name, self::$mimes) ? self::$mimes[$short_name] : $short_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param string $short_name
|
||||
*/
|
||||
public static function supportsMimeType($short_name)
|
||||
{
|
||||
return array_key_exists($short_name, self::$mimes);
|
||||
}
|
||||
}
|
984
externals/httpful/src/Httpful/Request.php
vendored
984
externals/httpful/src/Httpful/Request.php
vendored
|
@ -1,984 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
use Httpful\Exception\ConnectionErrorException;
|
||||
|
||||
/**
|
||||
* Clean, simple class for sending HTTP requests
|
||||
* in PHP.
|
||||
*
|
||||
* There is an emphasis of readability without loosing concise
|
||||
* syntax. As such, you will notice that the library lends
|
||||
* itself very nicely to "chaining". You will see several "alias"
|
||||
* methods: more readable method definitions that wrap
|
||||
* their more concise counterparts. You will also notice
|
||||
* no public constructor. This two adds to the readability
|
||||
* and "chainabilty" of the library.
|
||||
*
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
class Request
|
||||
{
|
||||
|
||||
// Option constants
|
||||
const SERIALIZE_PAYLOAD_NEVER = 0;
|
||||
const SERIALIZE_PAYLOAD_ALWAYS = 1;
|
||||
const SERIALIZE_PAYLOAD_SMART = 2;
|
||||
|
||||
const MAX_REDIRECTS_DEFAULT = 25;
|
||||
|
||||
public $uri,
|
||||
$method = Http::GET,
|
||||
$headers = array(),
|
||||
$raw_headers = '',
|
||||
$strict_ssl = false,
|
||||
$content_type,
|
||||
$expected_type,
|
||||
$additional_curl_opts = array(),
|
||||
$auto_parse = true,
|
||||
$serialize_payload_method = self::SERIALIZE_PAYLOAD_SMART,
|
||||
$username,
|
||||
$password,
|
||||
$serialized_payload,
|
||||
$payload,
|
||||
$parse_callback,
|
||||
$error_callback,
|
||||
$follow_redirects = false,
|
||||
$max_redirects = self::MAX_REDIRECTS_DEFAULT,
|
||||
$payload_serializers = array();
|
||||
|
||||
// Options
|
||||
// private $_options = array(
|
||||
// 'serialize_payload_method' => self::SERIALIZE_PAYLOAD_SMART
|
||||
// 'auto_parse' => true
|
||||
// );
|
||||
|
||||
// Curl Handle
|
||||
public $_ch,
|
||||
$_debug;
|
||||
|
||||
// Template Request object
|
||||
private static $_template;
|
||||
|
||||
/**
|
||||
* We made the constructor private to force the factory style. This was
|
||||
* done to keep the syntax cleaner and better the support the idea of
|
||||
* "default templates". Very basic and flexible as it is only intended
|
||||
* for internal use.
|
||||
* @param array $attrs hash of initial attribute values
|
||||
*/
|
||||
private function __construct($attrs = null)
|
||||
{
|
||||
if (!is_array($attrs)) return;
|
||||
foreach ($attrs as $attr => $value) {
|
||||
$this->$attr = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Defaults Management
|
||||
|
||||
/**
|
||||
* Let's you configure default settings for this
|
||||
* class from a template Request object. Simply construct a
|
||||
* Request object as much as you want to and then pass it to
|
||||
* this method. It will then lock in those settings from
|
||||
* that template object.
|
||||
* The most common of which may be default mime
|
||||
* settings or strict ssl settings.
|
||||
* Again some slight memory overhead incurred here but in the grand
|
||||
* scheme of things as it typically only occurs once
|
||||
* @param Request $template
|
||||
*/
|
||||
public static function ini(Request $template)
|
||||
{
|
||||
self::$_template = clone $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the default template back to the
|
||||
* library defaults.
|
||||
*/
|
||||
public static function resetIni()
|
||||
{
|
||||
self::_initializeDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default for a value based on the template object
|
||||
* @return mixed default value
|
||||
* @param string|null $attr Name of attribute (e.g. mime, headers)
|
||||
* if null just return the whole template object;
|
||||
*/
|
||||
public static function d($attr)
|
||||
{
|
||||
return isset($attr) ? self::$_template->$attr : self::$_template;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
/**
|
||||
* @return bool does the request have a timeout?
|
||||
*/
|
||||
public function hasTimeout()
|
||||
{
|
||||
return isset($this->timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool has the internal curl request been initialized?
|
||||
*/
|
||||
public function hasBeenInitialized()
|
||||
{
|
||||
return isset($this->_ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Is this request setup for basic auth?
|
||||
*/
|
||||
public function hasBasicAuth()
|
||||
{
|
||||
return isset($this->password) && isset($this->username);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Is this request setup for digest auth?
|
||||
*/
|
||||
public function hasDigestAuth()
|
||||
{
|
||||
return isset($this->password) && isset($this->username) && $this->additional_curl_opts['CURLOPT_HTTPAUTH'] = CURLAUTH_DIGEST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a HTTP timeout
|
||||
* @return Request $this
|
||||
* @param |int $timeout seconds to timeout the HTTP call
|
||||
*/
|
||||
public function timeout($timeout)
|
||||
{
|
||||
$this->timeout = $timeout;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the response is a 301 or 302 redirect, automatically
|
||||
* send off another request to that location
|
||||
* @return Request $this
|
||||
* @param bool|int $follow follow or not to follow or maximal number of redirects
|
||||
*/
|
||||
public function followRedirects($follow = true)
|
||||
{
|
||||
$this->max_redirects = $follow === true ? self::MAX_REDIRECTS_DEFAULT : max(0, $follow);
|
||||
$this->follow_redirects = (bool) $follow;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Request $this
|
||||
* @see Request::followRedirects()
|
||||
*/
|
||||
public function doNotFollowRedirects()
|
||||
{
|
||||
return $this->followRedirects(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually send off the request, and parse the response
|
||||
* @return string|associative array of parsed results
|
||||
* @throws ConnectionErrorException when unable to parse or communicate w server
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
if (!$this->hasBeenInitialized())
|
||||
$this->_curlPrep();
|
||||
|
||||
$result = curl_exec($this->_ch);
|
||||
|
||||
if ($result === false) {
|
||||
$this->_error(curl_error($this->_ch));
|
||||
throw new ConnectionErrorException('Unable to connect.');
|
||||
}
|
||||
|
||||
$info = curl_getinfo($this->_ch);
|
||||
$response = explode("\r\n\r\n", $result, 2 + $info['redirect_count']);
|
||||
|
||||
$body = array_pop($response);
|
||||
$headers = array_pop($response);
|
||||
|
||||
return new Response($body, $headers, $this);
|
||||
}
|
||||
public function sendIt()
|
||||
{
|
||||
return $this->send();
|
||||
}
|
||||
|
||||
// Setters
|
||||
|
||||
/**
|
||||
* @return Request this
|
||||
* @param string $uri
|
||||
*/
|
||||
public function uri($uri)
|
||||
{
|
||||
$this->uri = $uri;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Basic Auth.
|
||||
* Only use when over SSL/TSL/HTTPS.
|
||||
* @return Request this
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*/
|
||||
public function basicAuth($username, $password)
|
||||
{
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
return $this;
|
||||
}
|
||||
// @alias of basicAuth
|
||||
public function authenticateWith($username, $password)
|
||||
{
|
||||
return $this->basicAuth($username, $password);
|
||||
}
|
||||
// @alias of basicAuth
|
||||
public function authenticateWithBasic($username, $password)
|
||||
{
|
||||
return $this->basicAuth($username, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* User Digest Auth.
|
||||
* @return Request this
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*/
|
||||
public function digestAuth($username, $password)
|
||||
{
|
||||
$this->addOnCurlOption(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
|
||||
return $this->basicAuth($username, $password);
|
||||
}
|
||||
|
||||
// @alias of digestAuth
|
||||
public function authenticateWithDigest($username, $password)
|
||||
{
|
||||
return $this->digestAuth($username, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return is this request setup for client side cert?
|
||||
*/
|
||||
public function hasClientSideCert() {
|
||||
return isset($this->client_cert) && isset($this->client_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Client Side Cert Authentication
|
||||
* @return Response $this
|
||||
* @param string $key file path to client key
|
||||
* @param string $cert file path to client cert
|
||||
* @param string $passphrase for client key
|
||||
* @param string $encoding default PEM
|
||||
*/
|
||||
public function clientSideCert($cert, $key, $passphrase = null, $encoding = 'PEM')
|
||||
{
|
||||
$this->client_cert = $cert;
|
||||
$this->client_key = $key;
|
||||
$this->client_passphrase = $passphrase;
|
||||
$this->client_encoding = $encoding;
|
||||
|
||||
return $this;
|
||||
}
|
||||
// @alias of basicAuth
|
||||
public function authenticateWithCert($cert, $key, $passphrase = null, $encoding = 'PEM')
|
||||
{
|
||||
return $this->clientSideCert($cert, $key, $passphrase, $encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the body of the request
|
||||
* @return Request this
|
||||
* @param mixed $payload
|
||||
* @param string $mimeType
|
||||
*/
|
||||
public function body($payload, $mimeType = null)
|
||||
{
|
||||
$this->mime($mimeType);
|
||||
$this->payload = $payload;
|
||||
// Iserntentially don't call _serializePayload yet. Wait until
|
||||
// we actually send off the request to convert payload to string.
|
||||
// At that time, the `serialized_payload` is set accordingly.
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set the Content type and Expected as same in
|
||||
* one swoop
|
||||
* @return Request this
|
||||
* @param string $mime mime type to use for content type and expected return type
|
||||
*/
|
||||
public function mime($mime)
|
||||
{
|
||||
if (empty($mime)) return $this;
|
||||
$this->content_type = $this->expected_type = Mime::getFullMime($mime);
|
||||
return $this;
|
||||
}
|
||||
// @alias of mime
|
||||
public function sendsAndExpectsType($mime)
|
||||
{
|
||||
return $this->mime($mime);
|
||||
}
|
||||
// @alias of mime
|
||||
public function sendsAndExpects($mime)
|
||||
{
|
||||
return $this->mime($mime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the method. Shouldn't be called often as the preferred syntax
|
||||
* for instantiation is the method specific factory methods.
|
||||
* @return Request this
|
||||
* @param string $method
|
||||
*/
|
||||
public function method($method)
|
||||
{
|
||||
if (empty($method)) return $this;
|
||||
$this->method = $method;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Request this
|
||||
* @param string $mime
|
||||
*/
|
||||
public function expects($mime)
|
||||
{
|
||||
if (empty($mime)) return $this;
|
||||
$this->expected_type = Mime::getFullMime($mime);
|
||||
return $this;
|
||||
}
|
||||
// @alias of expects
|
||||
public function expectsType($mime)
|
||||
{
|
||||
return $this->expects($mime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Request this
|
||||
* @param string $mime
|
||||
*/
|
||||
public function contentType($mime)
|
||||
{
|
||||
if (empty($mime)) return $this;
|
||||
$this->content_type = Mime::getFullMime($mime);
|
||||
return $this;
|
||||
}
|
||||
// @alias of contentType
|
||||
public function sends($mime)
|
||||
{
|
||||
return $this->contentType($mime);
|
||||
}
|
||||
// @alias of contentType
|
||||
public function sendsType($mime)
|
||||
{
|
||||
return $this->contentType($mime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do we strictly enforce SSL verification?
|
||||
* @return Request this
|
||||
* @param bool $strict
|
||||
*/
|
||||
public function strictSSL($strict)
|
||||
{
|
||||
$this->strict_ssl = $strict;
|
||||
return $this;
|
||||
}
|
||||
public function withoutStrictSSL()
|
||||
{
|
||||
return $this->strictSSL(false);
|
||||
}
|
||||
public function withStrictSSL()
|
||||
{
|
||||
return $this->strictSSL(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine how/if we use the built in serialization by
|
||||
* setting the serialize_payload_method
|
||||
* The default (SERIALIZE_PAYLOAD_SMART) is...
|
||||
* - if payload is not a scalar (object/array)
|
||||
* use the appropriate serialize method according to
|
||||
* the Content-Type of this request.
|
||||
* - if the payload IS a scalar (int, float, string, bool)
|
||||
* than just return it as is.
|
||||
* When this option is set SERIALIZE_PAYLOAD_ALWAYS,
|
||||
* it will always use the appropriate
|
||||
* serialize option regardless of whether payload is scalar or not
|
||||
* When this option is set SERIALIZE_PAYLOAD_NEVER,
|
||||
* it will never use any of the serialization methods.
|
||||
* Really the only use for this is if you want the serialize methods
|
||||
* to handle strings or not (e.g. Blah is not valid JSON, but "Blah"
|
||||
* is). Forcing the serialization helps prevent that kind of error from
|
||||
* happening.
|
||||
* @return Request $this
|
||||
* @param int $mode
|
||||
*/
|
||||
public function serializePayload($mode)
|
||||
{
|
||||
$this->serialize_payload_method = $mode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Request::serializePayload()
|
||||
* @return Request
|
||||
*/
|
||||
public function neverSerializePayload()
|
||||
{
|
||||
return $this->serializePayload(self::SERIALIZE_PAYLOAD_NEVER);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is the default behavior
|
||||
* @see Request::serializePayload()
|
||||
* @return Request
|
||||
*/
|
||||
public function smartSerializePayload()
|
||||
{
|
||||
return $this->serializePayload(self::SERIALIZE_PAYLOAD_SMART);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Request::serializePayload()
|
||||
* @return Request
|
||||
*/
|
||||
public function alwaysSerializePayload()
|
||||
{
|
||||
return $this->serializePayload(self::SERIALIZE_PAYLOAD_ALWAYS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an additional header to the request
|
||||
* Can also use the cleaner syntax of
|
||||
* $Request->withMyHeaderName($my_value);
|
||||
* @see Request::__call()
|
||||
*
|
||||
* @return Request this
|
||||
* @param string $header_name
|
||||
* @param string $value
|
||||
*/
|
||||
public function addHeader($header_name, $value)
|
||||
{
|
||||
$this->headers[$header_name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add group of headers all at once. Note: This is
|
||||
* here just as a convenience in very specific cases.
|
||||
* The preferred "readable" way would be to leverage
|
||||
* the support for custom header methods.
|
||||
* @return Response $this
|
||||
* @param array $headers
|
||||
*/
|
||||
public function addHeaders(array $headers)
|
||||
{
|
||||
foreach ($headers as $header => $value) {
|
||||
$this->addHeader($header, $value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Request
|
||||
* @param bool $auto_parse perform automatic "smart"
|
||||
* parsing based on Content-Type or "expectedType"
|
||||
* If not auto parsing, Response->body returns the body
|
||||
* as a string.
|
||||
*/
|
||||
public function autoParse($auto_parse = true)
|
||||
{
|
||||
$this->auto_parse = $auto_parse;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Request::autoParse()
|
||||
* @return Request
|
||||
*/
|
||||
public function withoutAutoParsing()
|
||||
{
|
||||
return $this->autoParse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Request::autoParse()
|
||||
* @return Request
|
||||
*/
|
||||
public function withAutoParsing()
|
||||
{
|
||||
return $this->autoParse(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a custom function to parse the response.
|
||||
* @return Request this
|
||||
* @param \Closure $callback Takes the raw body of
|
||||
* the http response and returns a mixed
|
||||
*/
|
||||
public function parseWith(\Closure $callback)
|
||||
{
|
||||
$this->parse_callback = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Request::parseResponsesWith()
|
||||
* @return Request $this
|
||||
* @param \Closure $callback
|
||||
*/
|
||||
public function parseResponsesWith(\Closure $callback)
|
||||
{
|
||||
return $this->parseWith($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback that will be used to serialize the payload
|
||||
* for a particular mime type. When using "*" for the mime
|
||||
* type, it will use that parser for all responses regardless of the mime
|
||||
* type. If a custom '*' and 'application/json' exist, the custom
|
||||
* 'application/json' would take precedence over the '*' callback.
|
||||
*
|
||||
* @return Request $this
|
||||
* @param string $mime mime type we're registering
|
||||
* @param Closure $callback takes one argument, $payload,
|
||||
* which is the payload that we'll be
|
||||
*/
|
||||
public function registerPayloadSerializer($mime, \Closure $callback)
|
||||
{
|
||||
$this->payload_serializers[Mime::getFullMime($mime)] = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Request::registerPayloadSerializer()
|
||||
* @return Request $this
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function serializePayloadWith(\Closure $callback)
|
||||
{
|
||||
return $this->regregisterPayloadSerializer('*', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method allows for neatly setting other headers in a
|
||||
* similar syntax as the other setters. This method also allows
|
||||
* for the sends* syntax.
|
||||
* @return Request this
|
||||
* @param string $method "missing" method name called
|
||||
* the method name called should be the name of the header that you
|
||||
* are trying to set in camel case without dashes e.g. to set a
|
||||
* header for Content-Type you would use contentType() or more commonly
|
||||
* to add a custom header like X-My-Header, you would use xMyHeader().
|
||||
* To promote readability, you can optionally prefix these methods with
|
||||
* "with" (e.g. withXMyHeader("blah") instead of xMyHeader("blah")).
|
||||
* @param array $args in this case, there should only ever be 1 argument provided
|
||||
* and that argument should be a string value of the header we're setting
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
// This method supports the sends* methods
|
||||
// like sendsJSON, sendsForm
|
||||
//!method_exists($this, $method) &&
|
||||
if (substr($method, 0, 5) === 'sends') {
|
||||
$mime = strtolower(substr($method, 5));
|
||||
if (Mime::supportsMimeType($mime)) {
|
||||
$this->sends(Mime::getFullMime($mime));
|
||||
return $this;
|
||||
}
|
||||
// else {
|
||||
// throw new \Exception("Unsupported Content-Type $mime");
|
||||
// }
|
||||
}
|
||||
if (substr($method, 0, 7) === 'expects') {
|
||||
$mime = strtolower(substr($method, 7));
|
||||
if (Mime::supportsMimeType($mime)) {
|
||||
$this->expects(Mime::getFullMime($mime));
|
||||
return $this;
|
||||
}
|
||||
// else {
|
||||
// throw new \Exception("Unsupported Content-Type $mime");
|
||||
// }
|
||||
}
|
||||
|
||||
// This method also adds the custom header support as described in the
|
||||
// method comments
|
||||
if (count($args) === 0)
|
||||
return;
|
||||
|
||||
// Strip the sugar. If it leads with "with", strip.
|
||||
// This is okay because: No defined HTTP headers begin with with,
|
||||
// and if you are defining a custom header, the standard is to prefix it
|
||||
// with an "X-", so that should take care of any collisions.
|
||||
if (substr($method, 0, 4) === 'with')
|
||||
$method = substr($method, 4);
|
||||
|
||||
// Precede upper case letters with dashes, uppercase the first letter of method
|
||||
$header = ucwords(implode('-', preg_split('/([A-Z][^A-Z]*)/', $method, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY)));
|
||||
$this->addHeader($header, $args[0]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Internal Functions
|
||||
|
||||
/**
|
||||
* This is the default template to use if no
|
||||
* template has been provided. The template
|
||||
* tells the class which default values to use.
|
||||
* While there is a slight overhead for object
|
||||
* creation once per execution (not once per
|
||||
* Request instantiation), it promotes readability
|
||||
* and flexibility within the class.
|
||||
*/
|
||||
private static function _initializeDefaults()
|
||||
{
|
||||
// This is the only place you will
|
||||
// see this constructor syntax. It
|
||||
// is only done here to prevent infinite
|
||||
// recusion. Do not use this syntax elsewhere.
|
||||
// It goes against the whole readability
|
||||
// and transparency idea.
|
||||
self::$_template = new Request(array('method' => Http::GET));
|
||||
|
||||
// This is more like it...
|
||||
self::$_template
|
||||
->withoutStrictSSL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the defaults on a newly instantiated object
|
||||
* Doesn't copy variables prefixed with _
|
||||
* @return Request this
|
||||
*/
|
||||
private function _setDefaults()
|
||||
{
|
||||
if (!isset(self::$_template))
|
||||
self::_initializeDefaults();
|
||||
foreach (self::$_template as $k=>$v) {
|
||||
if ($k[0] != '_')
|
||||
$this->$k = $v;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function _error($error)
|
||||
{
|
||||
// Default actions write to error log
|
||||
// TODO add in support for various Loggers
|
||||
error_log($error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory style constructor works nicer for chaining. This
|
||||
* should also really only be used internally. The Request::get,
|
||||
* Request::post syntax is preferred as it is more readable.
|
||||
* @return Request
|
||||
* @param string $method Http Method
|
||||
* @param string $mime Mime Type to Use
|
||||
*/
|
||||
public static function init($method = null, $mime = null)
|
||||
{
|
||||
// Setup our handlers, can call it here as it's idempotent
|
||||
Bootstrap::init();
|
||||
|
||||
// Setup the default template if need be
|
||||
if (!isset(self::$_template))
|
||||
self::_initializeDefaults();
|
||||
|
||||
$request = new Request();
|
||||
return $request
|
||||
->_setDefaults()
|
||||
->method($method)
|
||||
->sendsType($mime)
|
||||
->expectsType($mime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the heavy lifting. Uses de facto HTTP
|
||||
* library cURL to set up the HTTP request.
|
||||
* Note: It does NOT actually send the request
|
||||
* @return Request $this;
|
||||
*/
|
||||
public function _curlPrep()
|
||||
{
|
||||
// Check for required stuff
|
||||
if (!isset($this->uri))
|
||||
throw new \Exception('Attempting to send a request before defining a URI endpoint.');
|
||||
|
||||
$ch = curl_init($this->uri);
|
||||
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);
|
||||
|
||||
if ($this->hasBasicAuth()) {
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $this->username . ':' . $this->password);
|
||||
}
|
||||
|
||||
if ($this->hasClientSideCert()) {
|
||||
|
||||
if (!file_exists($this->client_key))
|
||||
throw new \Exception('Could not read Client Key');
|
||||
|
||||
if (!file_exists($this->client_cert))
|
||||
throw new \Exception('Could not read Client Certificate');
|
||||
|
||||
curl_setopt($ch, CURLOPT_SSLCERTTYPE, $this->client_encoding);
|
||||
curl_setopt($ch, CURLOPT_SSLKEYTYPE, $this->client_encoding);
|
||||
curl_setopt($ch, CURLOPT_SSLCERT, $this->client_cert);
|
||||
curl_setopt($ch, CURLOPT_SSLKEY, $this->client_key);
|
||||
curl_setopt($ch, CURLOPT_SSLKEYPASSWD, $this->client_passphrase);
|
||||
// curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $this->client_cert_passphrase);
|
||||
}
|
||||
|
||||
if ($this->hasTimeout()) {
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
|
||||
}
|
||||
|
||||
if ($this->follow_redirects) {
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_MAXREDIRS, $this->max_redirects);
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->strict_ssl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$headers = array();
|
||||
// https://github.com/nategood/httpful/issues/37
|
||||
// Except header removes any HTTP 1.1 Continue from response headers
|
||||
$headers[] = 'Expect:';
|
||||
|
||||
if (!isset($this->headers['User-Agent'])) {
|
||||
$headers[] = $this->buildUserAgent();
|
||||
}
|
||||
|
||||
$headers[] = "Content-Type: {$this->content_type}";
|
||||
|
||||
// allow custom Accept header if set
|
||||
if (!isset($this->headers['Accept'])) {
|
||||
// http://pretty-rfc.herokuapp.com/RFC2616#header.accept
|
||||
$accept = 'Accept: */*; q=0.5, text/plain; q=0.8, text/html;level=3;';
|
||||
|
||||
if (!empty($this->expected_type)) {
|
||||
$accept .= "q=0.9, {$this->expected_type}";
|
||||
}
|
||||
|
||||
$headers[] = $accept;
|
||||
}
|
||||
|
||||
foreach ($this->headers as $header => $value) {
|
||||
$headers[] = "$header: $value";
|
||||
}
|
||||
|
||||
$url = \parse_url($this->uri);
|
||||
$path = (isset($url['path']) ? $url['path'] : '/').(isset($url['query']) ? '?'.$url['query'] : '');
|
||||
$this->raw_headers = "{$this->method} $path HTTP/1.1\r\n";
|
||||
$host = (isset($url['host']) ? $url['host'] : 'localhost').(isset($url['port']) ? ':'.$url['port'] : '');
|
||||
$this->raw_headers .= "Host: $host\r\n";
|
||||
$this->raw_headers .= \implode("\r\n", $headers);
|
||||
$this->raw_headers .= "\r\n";
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
if (isset($this->payload)) {
|
||||
$this->serialized_payload = $this->_serializePayload($this->payload);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->serialized_payload);
|
||||
}
|
||||
|
||||
if ($this->_debug) {
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_HEADER, 1);
|
||||
|
||||
// If there are some additional curl opts that the user wants
|
||||
// to set, we can tack them in here
|
||||
foreach ($this->additional_curl_opts as $curlopt => $curlval) {
|
||||
curl_setopt($ch, $curlopt, $curlval);
|
||||
}
|
||||
|
||||
$this->_ch = $ch;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function buildUserAgent() {
|
||||
$user_agent = 'User-Agent: Httpful/' . Httpful::VERSION . ' (cURL/';
|
||||
$curl = \curl_version();
|
||||
|
||||
if (isset($curl['version'])) {
|
||||
$user_agent .= $curl['version'];
|
||||
} else {
|
||||
$user_agent .= '?.?.?';
|
||||
}
|
||||
|
||||
$user_agent .= ' PHP/'. PHP_VERSION . ' (' . PHP_OS . ')';
|
||||
|
||||
if (isset($_SERVER['SERVER_SOFTWARE'])) {
|
||||
$user_agent .= ' ' . \preg_replace('~PHP/[\d\.]+~U', '',
|
||||
$_SERVER['SERVER_SOFTWARE']);
|
||||
} else {
|
||||
if (isset($_SERVER['TERM_PROGRAM'])) {
|
||||
$user_agent .= " {$_SERVER['TERM_PROGRAM']}";
|
||||
}
|
||||
|
||||
if (isset($_SERVER['TERM_PROGRAM_VERSION'])) {
|
||||
$user_agent .= "/{$_SERVER['TERM_PROGRAM_VERSION']}";
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_SERVER['HTTP_USER_AGENT'])) {
|
||||
$user_agent .= " {$_SERVER['HTTP_USER_AGENT']}";
|
||||
}
|
||||
|
||||
$user_agent .= ')';
|
||||
|
||||
return $user_agent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Semi-reluctantly added this as a way to add in curl opts
|
||||
* that are not otherwise accessible from the rest of the API.
|
||||
* @return Request $this
|
||||
* @param string $curlopt
|
||||
* @param mixed $curloptval
|
||||
*/
|
||||
public function addOnCurlOption($curlopt, $curloptval)
|
||||
{
|
||||
$this->additional_curl_opts[$curlopt] = $curloptval;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn payload from structured data into
|
||||
* a string based on the current Mime type.
|
||||
* This uses the auto_serialize option to determine
|
||||
* it's course of action. See serialize method for more.
|
||||
* Renamed from _detectPayload to _serializePayload as of
|
||||
* 2012-02-15.
|
||||
*
|
||||
* Added in support for custom payload serializers.
|
||||
* The serialize_payload_method stuff still holds true though.
|
||||
* @see Request::registerPayloadSerializer()
|
||||
*
|
||||
* @return string
|
||||
* @param mixed $payload
|
||||
*/
|
||||
private function _serializePayload($payload)
|
||||
{
|
||||
if (empty($payload) || $this->serialize_payload_method === self::SERIALIZE_PAYLOAD_NEVER)
|
||||
return $payload;
|
||||
|
||||
// When we are in "smart" mode, don't serialize strings/scalars, assume they are already serialized
|
||||
if ($this->serialize_payload_method === self::SERIALIZE_PAYLOAD_SMART && is_scalar($payload))
|
||||
return $payload;
|
||||
|
||||
// Use a custom serializer if one is registered for this mime type
|
||||
if (isset($this->payload_serializers['*']) || isset($this->payload_serializers[$this->content_type])) {
|
||||
$key = isset($this->payload_serializers[$this->content_type]) ? $this->content_type : '*';
|
||||
return call_user_func($this->payload_serializers[$key], $payload);
|
||||
}
|
||||
|
||||
return Httpful::get($this->content_type)->serialize($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Method Get
|
||||
* @return Request
|
||||
* @param string $uri optional uri to use
|
||||
* @param string $mime expected
|
||||
*/
|
||||
public static function get($uri, $mime = null)
|
||||
{
|
||||
return self::init(Http::GET)->uri($uri)->mime($mime);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Like Request:::get, except that it sends off the request as well
|
||||
* returning a response
|
||||
* @return Response
|
||||
* @param string $uri optional uri to use
|
||||
* @param string $mime expected
|
||||
*/
|
||||
public static function getQuick($uri, $mime = null)
|
||||
{
|
||||
return self::get($uri, $mime)->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Method Post
|
||||
* @return Request
|
||||
* @param string $uri optional uri to use
|
||||
* @param string $payload data to send in body of request
|
||||
* @param string $mime MIME to use for Content-Type
|
||||
*/
|
||||
public static function post($uri, $payload = null, $mime = null)
|
||||
{
|
||||
return self::init(Http::POST)->uri($uri)->body($payload, $mime);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Method Put
|
||||
* @return Request
|
||||
* @param string $uri optional uri to use
|
||||
* @param string $payload data to send in body of request
|
||||
* @param string $mime MIME to use for Content-Type
|
||||
*/
|
||||
public static function put($uri, $payload = null, $mime = null)
|
||||
{
|
||||
return self::init(Http::PUT)->uri($uri)->body($payload, $mime);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Method Patch
|
||||
* @return Request
|
||||
* @param string $uri optional uri to use
|
||||
* @param string $payload data to send in body of request
|
||||
* @param string $mime MIME to use for Content-Type
|
||||
*/
|
||||
public static function patch($uri, $payload = null, $mime = null)
|
||||
{
|
||||
return self::init(Http::PATCH)->uri($uri)->body($payload, $mime);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Method Delete
|
||||
* @return Request
|
||||
* @param string $uri optional uri to use
|
||||
*/
|
||||
public static function delete($uri, $mime = null)
|
||||
{
|
||||
return self::init(Http::DELETE)->uri($uri)->mime($mime);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Method Head
|
||||
* @return Request
|
||||
* @param string $uri optional uri to use
|
||||
*/
|
||||
public static function head($uri)
|
||||
{
|
||||
return self::init(Http::HEAD)->uri($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Method Options
|
||||
* @return Request
|
||||
* @param string $uri optional uri to use
|
||||
*/
|
||||
public static function options($uri)
|
||||
{
|
||||
return self::init(Http::OPTIONS)->uri($uri);
|
||||
}
|
||||
}
|
189
externals/httpful/src/Httpful/Response.php
vendored
189
externals/httpful/src/Httpful/Response.php
vendored
|
@ -1,189 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
/**
|
||||
* Models an HTTP response
|
||||
*
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
class Response
|
||||
{
|
||||
|
||||
public $body,
|
||||
$raw_body,
|
||||
$headers,
|
||||
$raw_headers,
|
||||
$request,
|
||||
$code = 0,
|
||||
$content_type,
|
||||
$parent_type,
|
||||
$charset,
|
||||
$is_mime_vendor_specific = false,
|
||||
$is_mime_personal = false;
|
||||
|
||||
private $parsers;
|
||||
/**
|
||||
* @param string $body
|
||||
* @param string $headers
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct($body, $headers, Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->raw_headers = $headers;
|
||||
$this->raw_body = $body;
|
||||
|
||||
$this->code = $this->_parseCode($headers);
|
||||
$this->headers = Response\Headers::fromString($headers);
|
||||
|
||||
$this->_interpretHeaders();
|
||||
|
||||
$this->body = $this->_parse($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Status Code Definitions
|
||||
*
|
||||
* Informational 1xx
|
||||
* Successful 2xx
|
||||
* Redirection 3xx
|
||||
* Client Error 4xx
|
||||
* Server Error 5xx
|
||||
*
|
||||
* http://pretty-rfc.herokuapp.com/RFC2616#status.codes
|
||||
*
|
||||
* @return bool Did we receive a 4xx or 5xx?
|
||||
*/
|
||||
public function hasErrors()
|
||||
{
|
||||
return $this->code >= 400;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return return bool
|
||||
*/
|
||||
public function hasBody()
|
||||
{
|
||||
return !empty($this->body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the response into a clean data structure
|
||||
* (most often an associative array) based on the expected
|
||||
* Mime type.
|
||||
* @return array|string|object the response parse accordingly
|
||||
* @param string Http response body
|
||||
*/
|
||||
public function _parse($body)
|
||||
{
|
||||
// If the user decided to forgo the automatic
|
||||
// smart parsing, short circuit.
|
||||
if (!$this->request->auto_parse) {
|
||||
return $body;
|
||||
}
|
||||
|
||||
// If provided, use custom parsing callback
|
||||
if (isset($this->request->parse_callback)) {
|
||||
return call_user_func($this->request->parse_callback, $body);
|
||||
}
|
||||
|
||||
// Decide how to parse the body of the response in the following order
|
||||
// 1. If provided, use the mime type specifically set as part of the `Request`
|
||||
// 2. If a MimeHandler is registered for the content type, use it
|
||||
// 3. If provided, use the "parent type" of the mime type from the response
|
||||
// 4. Default to the content-type provided in the response
|
||||
$parse_with = $this->request->expected_type;
|
||||
if (empty($this->request->expected_type)) {
|
||||
$parse_with = Httpful::hasParserRegistered($this->content_type)
|
||||
? $this->content_type
|
||||
: $this->parent_type;
|
||||
}
|
||||
|
||||
return Httpful::get($parse_with)->parse($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse text headers from response into
|
||||
* array of key value pairs
|
||||
* @return array parse headers
|
||||
* @param string $headers raw headers
|
||||
*/
|
||||
public function _parseHeaders($headers)
|
||||
{
|
||||
$headers = preg_split("/(\r|\n)+/", $headers, -1, \PREG_SPLIT_NO_EMPTY);
|
||||
$parse_headers = array();
|
||||
for ($i = 1; $i < count($headers); $i++) {
|
||||
list($key, $raw_value) = explode(':', $headers[$i], 2);
|
||||
$key = trim($key);
|
||||
$value = trim($raw_value);
|
||||
if (array_key_exists($key, $parse_headers)) {
|
||||
// See HTTP RFC Sec 4.2 Paragraph 5
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
||||
// If a header appears more than once, it must also be able to
|
||||
// be represented as a single header with a comma-separated
|
||||
// list of values. We transform accordingly.
|
||||
$parse_headers[$key] .= ',' . $value;
|
||||
} else {
|
||||
$parse_headers[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $parse_headers;
|
||||
}
|
||||
|
||||
public function _parseCode($headers)
|
||||
{
|
||||
$parts = explode(' ', substr($headers, 0, strpos($headers, "\r\n")));
|
||||
if (count($parts) < 2 || !is_numeric($parts[1])) {
|
||||
throw new \Exception("Unable to parse response code from HTTP response due to malformed response");
|
||||
}
|
||||
return intval($parts[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* After we've parse the headers, let's clean things
|
||||
* up a bit and treat some headers specially
|
||||
*/
|
||||
public function _interpretHeaders()
|
||||
{
|
||||
// Parse the Content-Type and charset
|
||||
$content_type = isset($this->headers['Content-Type']) ? $this->headers['Content-Type'] : '';
|
||||
$content_type = explode(';', $content_type);
|
||||
|
||||
$this->content_type = $content_type[0];
|
||||
if (count($content_type) == 2 && strpos($content_type[1], '=') !== false) {
|
||||
list($nill, $this->charset) = explode('=', $content_type[1]);
|
||||
}
|
||||
|
||||
// RFC 2616 states "text/*" Content-Types should have a default
|
||||
// charset of ISO-8859-1. "application/*" and other Content-Types
|
||||
// are assumed to have UTF-8 unless otherwise specified.
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1
|
||||
// http://www.w3.org/International/O-HTTP-charset.en.php
|
||||
if (!isset($this->charset)) {
|
||||
$this->charset = substr($this->content_type, 5) === 'text/' ? 'iso-8859-1' : 'utf-8';
|
||||
}
|
||||
|
||||
// Is vendor type? Is personal type?
|
||||
if (strpos($this->content_type, '/') !== false) {
|
||||
list($type, $sub_type) = explode('/', $this->content_type);
|
||||
$this->is_mime_vendor_specific = substr($sub_type, 0, 4) === 'vnd.';
|
||||
$this->is_mime_personal = substr($sub_type, 0, 4) === 'prs.';
|
||||
}
|
||||
|
||||
// Parent type (e.g. xml for application/vnd.github.message+xml)
|
||||
$this->parent_type = $this->content_type;
|
||||
if (strpos($this->content_type, '+') !== false) {
|
||||
list($vendor, $this->parent_type) = explode('+', $this->content_type, 2);
|
||||
$this->parent_type = Mime::getFullMime($this->parent_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->raw_body;
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Httpful\Response;
|
||||
|
||||
final class Headers implements \ArrayAccess, \Countable {
|
||||
|
||||
private $headers;
|
||||
|
||||
private function __construct($headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
public static function fromString($string)
|
||||
{
|
||||
$lines = preg_split("/(\r|\n)+/", $string, -1, PREG_SPLIT_NO_EMPTY);
|
||||
array_shift($lines); // HTTP HEADER
|
||||
$headers = array();
|
||||
foreach ($lines as $line) {
|
||||
list($name, $value) = explode(':', $line, 2);
|
||||
$headers[strtolower(trim($name))] = trim($value);
|
||||
}
|
||||
return new self($headers);
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->headers[strtolower($offset)]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if (isset($this->headers[$name = strtolower($offset)])) {
|
||||
return $this->headers[$name];
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
throw new \Exception("Headers are read-only.");
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
throw new \Exception("Headers are read-only.");
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->headers);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
}
|
458
externals/httpful/tests/Httpful/HttpfulTest.php
vendored
458
externals/httpful/tests/Httpful/HttpfulTest.php
vendored
|
@ -1,458 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Port over the original tests into a more traditional PHPUnit
|
||||
* format. Still need to hook into a lightweight HTTP server to
|
||||
* better test some things (e.g. obscure cURL settings). I've moved
|
||||
* the old tests and node.js server to the tests/.legacy directory.
|
||||
*
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
namespace Httpful\Test;
|
||||
|
||||
require(dirname(dirname(dirname(__FILE__))) . '/bootstrap.php');
|
||||
\Httpful\Bootstrap::init();
|
||||
|
||||
use Httpful\Httpful;
|
||||
use Httpful\Request;
|
||||
use Httpful\Mime;
|
||||
use Httpful\Http;
|
||||
use Httpful\Response;
|
||||
|
||||
class HttpfulTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
const TEST_SERVER = '127.0.0.1:8008';
|
||||
const TEST_URL = 'http://127.0.0.1:8008';
|
||||
const TEST_URL_400 = 'http://127.0.0.1:8008/400';
|
||||
|
||||
const SAMPLE_JSON_HEADER =
|
||||
"HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked\r\n";
|
||||
const SAMPLE_JSON_RESPONSE = '{"key":"value","object":{"key":"value"},"array":[1,2,3,4]}';
|
||||
const SAMPLE_CSV_HEADER =
|
||||
"HTTP/1.1 200 OK
|
||||
Content-Type: text/csv
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked\r\n";
|
||||
const SAMPLE_CSV_RESPONSE =
|
||||
"Key1,Key2
|
||||
Value1,Value2
|
||||
\"40.0\",\"Forty\"";
|
||||
const SAMPLE_XML_RESPONSE = '<stdClass><arrayProp><array><k1><myClass><intProp>2</intProp></myClass></k1></array></arrayProp><stringProp>a string</stringProp><boolProp>TRUE</boolProp></stdClass>';
|
||||
const SAMPLE_XML_HEADER =
|
||||
"HTTP/1.1 200 OK
|
||||
Content-Type: application/xml
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked\r\n";
|
||||
const SAMPLE_VENDOR_HEADER =
|
||||
"HTTP/1.1 200 OK
|
||||
Content-Type: application/vnd.nategood.message+xml
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked\r\n";
|
||||
const SAMPLE_VENDOR_TYPE = "application/vnd.nategood.message+xml";
|
||||
const SAMPLE_MULTI_HEADER =
|
||||
"HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked
|
||||
X-My-Header:Value1
|
||||
X-My-Header:Value2\r\n";
|
||||
function testInit()
|
||||
{
|
||||
$r = Request::init();
|
||||
// Did we get a 'Request' object?
|
||||
$this->assertEquals('Httpful\Request', get_class($r));
|
||||
}
|
||||
|
||||
function testMethods()
|
||||
{
|
||||
$valid_methods = array('get', 'post', 'delete', 'put', 'options', 'head');
|
||||
$url = 'http://example.com/';
|
||||
foreach ($valid_methods as $method) {
|
||||
$r = call_user_func(array('Httpful\Request', $method), $url);
|
||||
$this->assertEquals('Httpful\Request', get_class($r));
|
||||
$this->assertEquals(strtoupper($method), $r->method);
|
||||
}
|
||||
}
|
||||
|
||||
function testDefaults()
|
||||
{
|
||||
// Our current defaults are as follows
|
||||
$r = Request::init();
|
||||
$this->assertEquals(Http::GET, $r->method);
|
||||
$this->assertFalse($r->strict_ssl);
|
||||
}
|
||||
|
||||
function testShortMime()
|
||||
{
|
||||
// Valid short ones
|
||||
$this->assertEquals(Mime::JSON, Mime::getFullMime('json'));
|
||||
$this->assertEquals(Mime::XML, Mime::getFullMime('xml'));
|
||||
$this->assertEquals(Mime::HTML, Mime::getFullMime('html'));
|
||||
$this->assertEquals(Mime::CSV, Mime::getFullMime('csv'));
|
||||
|
||||
// Valid long ones
|
||||
$this->assertEquals(Mime::JSON, Mime::getFullMime(Mime::JSON));
|
||||
$this->assertEquals(Mime::XML, Mime::getFullMime(Mime::XML));
|
||||
$this->assertEquals(Mime::HTML, Mime::getFullMime(Mime::HTML));
|
||||
$this->assertEquals(Mime::CSV, Mime::getFullMime(Mime::CSV));
|
||||
|
||||
// No false positives
|
||||
$this->assertNotEquals(Mime::XML, Mime::getFullMime(Mime::HTML));
|
||||
$this->assertNotEquals(Mime::JSON, Mime::getFullMime(Mime::XML));
|
||||
$this->assertNotEquals(Mime::HTML, Mime::getFullMime(Mime::JSON));
|
||||
$this->assertNotEquals(Mime::XML, Mime::getFullMime(Mime::CSV));
|
||||
}
|
||||
|
||||
function testSettingStrictSsl()
|
||||
{
|
||||
$r = Request::init()
|
||||
->withStrictSsl();
|
||||
|
||||
$this->assertTrue($r->strict_ssl);
|
||||
|
||||
$r = Request::init()
|
||||
->withoutStrictSsl();
|
||||
|
||||
$this->assertFalse($r->strict_ssl);
|
||||
}
|
||||
|
||||
function testSendsAndExpectsType()
|
||||
{
|
||||
$r = Request::init()
|
||||
->sendsAndExpectsType(Mime::JSON);
|
||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
||||
$this->assertEquals(Mime::JSON, $r->content_type);
|
||||
|
||||
$r = Request::init()
|
||||
->sendsAndExpectsType('html');
|
||||
$this->assertEquals(Mime::HTML, $r->expected_type);
|
||||
$this->assertEquals(Mime::HTML, $r->content_type);
|
||||
|
||||
$r = Request::init()
|
||||
->sendsAndExpectsType('form');
|
||||
$this->assertEquals(Mime::FORM, $r->expected_type);
|
||||
$this->assertEquals(Mime::FORM, $r->content_type);
|
||||
|
||||
$r = Request::init()
|
||||
->sendsAndExpectsType('application/x-www-form-urlencoded');
|
||||
$this->assertEquals(Mime::FORM, $r->expected_type);
|
||||
$this->assertEquals(Mime::FORM, $r->content_type);
|
||||
|
||||
$r = Request::init()
|
||||
->sendsAndExpectsType(Mime::CSV);
|
||||
$this->assertEquals(Mime::CSV, $r->expected_type);
|
||||
$this->assertEquals(Mime::CSV, $r->content_type);
|
||||
}
|
||||
|
||||
function testIni()
|
||||
{
|
||||
// Test setting defaults/templates
|
||||
|
||||
// Create the template
|
||||
$template = Request::init()
|
||||
->method(Http::POST)
|
||||
->withStrictSsl()
|
||||
->expectsType(Mime::HTML)
|
||||
->sendsType(Mime::FORM);
|
||||
|
||||
Request::ini($template);
|
||||
|
||||
$r = Request::init();
|
||||
|
||||
$this->assertTrue($r->strict_ssl);
|
||||
$this->assertEquals(Http::POST, $r->method);
|
||||
$this->assertEquals(Mime::HTML, $r->expected_type);
|
||||
$this->assertEquals(Mime::FORM, $r->content_type);
|
||||
|
||||
// Test the default accessor as well
|
||||
$this->assertTrue(Request::d('strict_ssl'));
|
||||
$this->assertEquals(Http::POST, Request::d('method'));
|
||||
$this->assertEquals(Mime::HTML, Request::d('expected_type'));
|
||||
$this->assertEquals(Mime::FORM, Request::d('content_type'));
|
||||
|
||||
Request::resetIni();
|
||||
}
|
||||
|
||||
function testAccept()
|
||||
{
|
||||
$r = Request::get('http://example.com/')
|
||||
->expectsType(Mime::JSON);
|
||||
|
||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
||||
$r->_curlPrep();
|
||||
$this->assertContains('application/json', $r->raw_headers);
|
||||
}
|
||||
|
||||
function testCustomAccept()
|
||||
{
|
||||
$accept = 'application/api-1.0+json';
|
||||
$r = Request::get('http://example.com/')
|
||||
->addHeader('Accept', $accept);
|
||||
|
||||
$r->_curlPrep();
|
||||
$this->assertContains($accept, $r->raw_headers);
|
||||
$this->assertEquals($accept, $r->headers['Accept']);
|
||||
}
|
||||
|
||||
function testUserAgent()
|
||||
{
|
||||
$r = Request::get('http://example.com/')
|
||||
->withUserAgent('ACME/1.2.3');
|
||||
|
||||
$this->assertArrayHasKey('User-Agent', $r->headers);
|
||||
$r->_curlPrep();
|
||||
$this->assertContains('User-Agent: ACME/1.2.3', $r->raw_headers);
|
||||
$this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers);
|
||||
|
||||
$r = Request::get('http://example.com/')
|
||||
->withUserAgent('');
|
||||
|
||||
$this->assertArrayHasKey('User-Agent', $r->headers);
|
||||
$r->_curlPrep();
|
||||
$this->assertContains('User-Agent:', $r->raw_headers);
|
||||
$this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers);
|
||||
}
|
||||
|
||||
function testAuthSetup()
|
||||
{
|
||||
$username = 'nathan';
|
||||
$password = 'opensesame';
|
||||
|
||||
$r = Request::get('http://example.com/')
|
||||
->authenticateWith($username, $password);
|
||||
|
||||
$this->assertEquals($username, $r->username);
|
||||
$this->assertEquals($password, $r->password);
|
||||
$this->assertTrue($r->hasBasicAuth());
|
||||
}
|
||||
|
||||
function testDigestAuthSetup()
|
||||
{
|
||||
$username = 'nathan';
|
||||
$password = 'opensesame';
|
||||
|
||||
$r = Request::get('http://example.com/')
|
||||
->authenticateWithDigest($username, $password);
|
||||
|
||||
$this->assertEquals($username, $r->username);
|
||||
$this->assertEquals($password, $r->password);
|
||||
$this->assertTrue($r->hasDigestAuth());
|
||||
}
|
||||
|
||||
function testJsonResponseParse()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
|
||||
$this->assertEquals("value", $response->body->key);
|
||||
$this->assertEquals("value", $response->body->object->key);
|
||||
$this->assertInternalType('array', $response->body->array);
|
||||
$this->assertEquals(1, $response->body->array[0]);
|
||||
}
|
||||
|
||||
function testXMLResponseParse()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::XML);
|
||||
$response = new Response(self::SAMPLE_XML_RESPONSE, self::SAMPLE_XML_HEADER, $req);
|
||||
$sxe = $response->body;
|
||||
$this->assertEquals("object", gettype($sxe));
|
||||
$this->assertEquals("SimpleXMLElement", get_class($sxe));
|
||||
$bools = $sxe->xpath('/stdClass/boolProp');
|
||||
list( , $bool ) = each($bools);
|
||||
$this->assertEquals("TRUE", (string) $bool);
|
||||
$ints = $sxe->xpath('/stdClass/arrayProp/array/k1/myClass/intProp');
|
||||
list( , $int ) = each($ints);
|
||||
$this->assertEquals("2", (string) $int);
|
||||
$strings = $sxe->xpath('/stdClass/stringProp');
|
||||
list( , $string ) = each($strings);
|
||||
$this->assertEquals("a string", (string) $string);
|
||||
}
|
||||
|
||||
function testCsvResponseParse()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::CSV);
|
||||
$response = new Response(self::SAMPLE_CSV_RESPONSE, self::SAMPLE_CSV_HEADER, $req);
|
||||
|
||||
$this->assertEquals("Key1", $response->body[0][0]);
|
||||
$this->assertEquals("Value1", $response->body[1][0]);
|
||||
$this->assertInternalType('string', $response->body[2][0]);
|
||||
$this->assertEquals("40.0", $response->body[2][0]);
|
||||
}
|
||||
|
||||
function testParsingContentTypeCharset()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
// $response = new Response(SAMPLE_JSON_RESPONSE, "", $req);
|
||||
// // Check default content type of iso-8859-1
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, "HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; charset=utf-8\r\n", $req);
|
||||
$this->assertInstanceOf('Httpful\Response\Headers', $response->headers);
|
||||
$this->assertEquals($response->headers['Content-Type'], 'text/plain; charset=utf-8');
|
||||
$this->assertEquals($response->content_type, 'text/plain');
|
||||
$this->assertEquals($response->charset, 'utf-8');
|
||||
}
|
||||
|
||||
function testEmptyResponseParse()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response("", self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertEquals(null, $response->body);
|
||||
|
||||
$reqXml = Request::init()->sendsAndExpects(Mime::XML);
|
||||
$responseXml = new Response("", self::SAMPLE_XML_HEADER, $reqXml);
|
||||
$this->assertEquals(null, $responseXml->body);
|
||||
}
|
||||
|
||||
function testNoAutoParse()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON)->withoutAutoParsing();
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertInternalType('string', $response->body);
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON)->withAutoParsing();
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertInternalType('object', $response->body);
|
||||
}
|
||||
|
||||
function testParseHeaders()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertEquals('application/json', $response->headers['Content-Type']);
|
||||
}
|
||||
|
||||
function testRawHeaders()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertContains('Content-Type: application/json', $response->raw_headers);
|
||||
}
|
||||
|
||||
function testHasErrors()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response('', "HTTP/1.1 100 Continue\r\n", $req);
|
||||
$this->assertFalse($response->hasErrors());
|
||||
$response = new Response('', "HTTP/1.1 200 OK\r\n", $req);
|
||||
$this->assertFalse($response->hasErrors());
|
||||
$response = new Response('', "HTTP/1.1 300 Multiple Choices\r\n", $req);
|
||||
$this->assertFalse($response->hasErrors());
|
||||
$response = new Response('', "HTTP/1.1 400 Bad Request\r\n", $req);
|
||||
$this->assertTrue($response->hasErrors());
|
||||
$response = new Response('', "HTTP/1.1 500 Internal Server Error\r\n", $req);
|
||||
$this->assertTrue($response->hasErrors());
|
||||
}
|
||||
|
||||
function test_parseCode()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$code = $response->_parseCode("HTTP/1.1 406 Not Acceptable\r\n");
|
||||
$this->assertEquals(406, $code);
|
||||
}
|
||||
|
||||
function testToString()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertEquals(self::SAMPLE_JSON_RESPONSE, (string)$response);
|
||||
}
|
||||
|
||||
function test_parseHeaders()
|
||||
{
|
||||
$parse_headers = Response\Headers::fromString(self::SAMPLE_JSON_HEADER);
|
||||
$this->assertCount(3, $parse_headers);
|
||||
$this->assertEquals('application/json', $parse_headers['Content-Type']);
|
||||
$this->assertTrue(isset($parse_headers['Connection']));
|
||||
}
|
||||
|
||||
function testMultiHeaders()
|
||||
{
|
||||
$req = Request::init();
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_MULTI_HEADER, $req);
|
||||
$parse_headers = $response->_parseHeaders(self::SAMPLE_MULTI_HEADER);
|
||||
$this->assertEquals('Value1,Value2', $parse_headers['X-My-Header']);
|
||||
}
|
||||
|
||||
function testDetectContentType()
|
||||
{
|
||||
$req = Request::init();
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertEquals('application/json', $response->headers['Content-Type']);
|
||||
}
|
||||
|
||||
function testMissingBodyContentType()
|
||||
{
|
||||
$body = 'A string';
|
||||
$request = Request::post(HttpfulTest::TEST_URL, $body)->_curlPrep();
|
||||
$this->assertEquals($body, $request->serialized_payload);
|
||||
}
|
||||
|
||||
function testParentType()
|
||||
{
|
||||
// Parent type
|
||||
$request = Request::init()->sendsAndExpects(Mime::XML);
|
||||
$response = new Response('<xml><name>Nathan</name></xml>', self::SAMPLE_VENDOR_HEADER, $request);
|
||||
|
||||
$this->assertEquals("application/xml", $response->parent_type);
|
||||
$this->assertEquals(self::SAMPLE_VENDOR_TYPE, $response->content_type);
|
||||
$this->assertTrue($response->is_mime_vendor_specific);
|
||||
|
||||
// Make sure we still parsed as if it were plain old XML
|
||||
$this->assertEquals("Nathan", $response->body->name->__toString());
|
||||
}
|
||||
|
||||
function testMissingContentType()
|
||||
{
|
||||
// Parent type
|
||||
$request = Request::init()->sendsAndExpects(Mime::XML);
|
||||
$response = new Response('<xml><name>Nathan</name></xml>',
|
||||
"HTTP/1.1 200 OK
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked\r\n", $request);
|
||||
|
||||
$this->assertEquals("", $response->content_type);
|
||||
}
|
||||
|
||||
function testCustomMimeRegistering()
|
||||
{
|
||||
// Register new mime type handler for "application/vnd.nategood.message+xml"
|
||||
Httpful::register(self::SAMPLE_VENDOR_TYPE, new DemoMimeHandler());
|
||||
|
||||
$this->assertTrue(Httpful::hasParserRegistered(self::SAMPLE_VENDOR_TYPE));
|
||||
|
||||
$request = Request::init();
|
||||
$response = new Response('<xml><name>Nathan</name></xml>', self::SAMPLE_VENDOR_HEADER, $request);
|
||||
|
||||
$this->assertEquals(self::SAMPLE_VENDOR_TYPE, $response->content_type);
|
||||
$this->assertEquals('custom parse', $response->body);
|
||||
}
|
||||
|
||||
public function testShorthandMimeDefinition()
|
||||
{
|
||||
$r = Request::init()->expects('json');
|
||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
||||
|
||||
$r = Request::init()->expectsJson();
|
||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
||||
}
|
||||
|
||||
public function testOverrideXmlHandler()
|
||||
{
|
||||
// Lazy test...
|
||||
$prev = \Httpful\Httpful::get(\Httpful\Mime::XML);
|
||||
$this->assertEquals($prev, new \Httpful\Handlers\XmlHandler());
|
||||
$conf = array('namespace' => 'http://example.com');
|
||||
\Httpful\Httpful::register(\Httpful\Mime::XML, new \Httpful\Handlers\XmlHandler($conf));
|
||||
$new = \Httpful\Httpful::get(\Httpful\Mime::XML);
|
||||
$this->assertNotEquals($prev, $new);
|
||||
}
|
||||
}
|
||||
|
||||
class DemoMimeHandler extends \Httpful\Handlers\MimeHandlerAdapter {
|
||||
public function parse($body) {
|
||||
return 'custom parse';
|
||||
}
|
||||
}
|
||||
|
9
externals/httpful/tests/phpunit.xml
vendored
9
externals/httpful/tests/phpunit.xml
vendored
|
@ -1,9 +0,0 @@
|
|||
<phpunit>
|
||||
<testsuite name="Httpful">
|
||||
<directory>.</directory>
|
||||
</testsuite>
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
|
12
externals/restful/.gitignore
vendored
12
externals/restful/.gitignore
vendored
|
@ -1,12 +0,0 @@
|
|||
# composer
|
||||
.buildpath
|
||||
composer.lock
|
||||
composer.phar
|
||||
vendor
|
||||
|
||||
# phar
|
||||
*.phar
|
||||
|
||||
# eclipse-pdt
|
||||
.settings
|
||||
.project
|
8
externals/restful/.travis.yml
vendored
8
externals/restful/.travis.yml
vendored
|
@ -1,8 +0,0 @@
|
|||
language: php
|
||||
before_script:
|
||||
- curl -s http://getcomposer.org/installer | php
|
||||
- php composer.phar install --prefer-source
|
||||
script: phpunit --bootstrap vendor/autoload.php tests/
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
22
externals/restful/LICENSE
vendored
22
externals/restful/LICENSE
vendored
|
@ -1,22 +0,0 @@
|
|||
Copyright (c) 2012 Noone
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
111
externals/restful/README.md
vendored
111
externals/restful/README.md
vendored
|
@ -1,111 +0,0 @@
|
|||
# RESTful
|
||||
|
||||
Library for writing RESTful PHP clients.
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/bninja/restful.png)](http://travis-ci.org/bninja/restful)
|
||||
|
||||
The design of this library was heavily influenced by [Httpful](https://github.com/nategood/httpful).
|
||||
|
||||
## Requirements
|
||||
|
||||
- [PHP](http://www.php.net) >= 5.3 **with** [cURL](http://www.php.net/manual/en/curl.installation.php)
|
||||
- [Httpful](https://github.com/nategood/httpful) >= 0.1
|
||||
|
||||
## Issues
|
||||
|
||||
Please use appropriately tagged github [issues](https://github.com/bninja/restful/issues) to request features or report bugs.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install using [composer](#composer), a [phar](#phar) package or from [source](#source). Note that RESTful is [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) compliant:
|
||||
|
||||
### Composer
|
||||
|
||||
If you don't have Composer [install](http://getcomposer.org/doc/00-intro.md#installation) it:
|
||||
|
||||
$ curl -s https://getcomposer.org/installer | php
|
||||
|
||||
Add this to your `composer.json`:
|
||||
|
||||
{
|
||||
"require": {
|
||||
"bninja/restful": "*"
|
||||
}
|
||||
}
|
||||
|
||||
Refresh your dependencies:
|
||||
|
||||
$ php composer.phar update
|
||||
|
||||
|
||||
Then make sure to `require` the autoloader and initialize both:
|
||||
|
||||
<?php
|
||||
require(__DIR__ . '/vendor/autoload.php');
|
||||
|
||||
Httpful\Bootstrap::init();
|
||||
RESTful\Bootstrap::init();
|
||||
...
|
||||
|
||||
### Phar
|
||||
|
||||
Download an Httpful [phar](http://php.net/manual/en/book.phar.php) file, which are all [here](https://github.com/nategood/httpful/downloads):
|
||||
|
||||
$ curl -s -L -o httpful.phar https://github.com/downloads/nategood/httpful/httpful.phar
|
||||
|
||||
Download a RESTful [phar](http://php.net/manual/en/book.phar.php) file, which are all [here](https://github.com/bninja/restful/downloads):
|
||||
|
||||
$ curl -s -L -o restful.phar https://github.com/bninja/restful/downloads/restful-{VERSION}.phar
|
||||
|
||||
And then `include` both:
|
||||
|
||||
<?php
|
||||
include(__DIR__ . '/httpful.phar');
|
||||
include(__DIR__ . '/restful.phar');
|
||||
...
|
||||
|
||||
### Source
|
||||
|
||||
Download [Httpful](https://github.com/nategood/httpful) source:
|
||||
|
||||
$ curl -s -L -o httpful.zip https://github.com/nategood/httpful/zipball/master;
|
||||
$ unzip httpful.zip; mv nategood-httpful* httpful; rm httpful.zip
|
||||
|
||||
Download the RESTful source:
|
||||
|
||||
$ curl -s -L -o restful.zip https://github.com/bninja/restful/zipball/master
|
||||
$ unzip restful.zip; mv bninja-restful-* restful; rm restful.zip
|
||||
|
||||
And then `require` both bootstrap files:
|
||||
|
||||
<?php
|
||||
require(__DIR__ . "/httpful/bootstrap.php")
|
||||
require(__DIR__ . "/restful/bootstrap.php")
|
||||
...
|
||||
|
||||
## Usage
|
||||
|
||||
TODO
|
||||
|
||||
## Testing
|
||||
|
||||
$ phpunit --bootstrap vendor/autoload.php tests/
|
||||
|
||||
## Publishing
|
||||
|
||||
1. Ensure that **all** [tests](#testing) pass
|
||||
2. Increment minor `VERSION` in `src/RESTful/Settings` and `composer.json` (`git commit -am 'v{VERSION} release'`)
|
||||
3. Tag it (`git tag -a v{VERSION} -m 'v{VERSION} release'`)
|
||||
4. Push the tag (`git push --tag`)
|
||||
5. [Packagist](http://packagist.org/packages/bninja/restful) will see the new tag and take it from there
|
||||
6. Build (`build-phar`) and upload a [phar](http://php.net/manual/en/book.phar.php) file
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Write your code **and [tests](#testing)**
|
||||
4. Ensure all tests still pass (`phpunit --bootstrap vendor/autoload.php tests/`)
|
||||
5. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
6. Push to the branch (`git push origin my-new-feature`)
|
||||
7. Create new pull request
|
4
externals/restful/bootstrap.php
vendored
4
externals/restful/bootstrap.php
vendored
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
|
||||
require(__DIR__ . '/src/RESTful/Bootstrap.php');
|
||||
\RESTful\Bootstrap::init();
|
36
externals/restful/build-phar
vendored
36
externals/restful/build-phar
vendored
|
@ -1,36 +0,0 @@
|
|||
#!/usr/bin/php
|
||||
<?php
|
||||
include('src/RESTful/Settings.php');
|
||||
|
||||
function exit_unless($condition, $msg = null) {
|
||||
if ($condition)
|
||||
return;
|
||||
echo "[FAIL] $msg";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Building Phar... ";
|
||||
$base_dir = dirname(__FILE__);
|
||||
$source_dir = $base_dir . '/src/RESTful/';
|
||||
$phar_name = 'restful.phar';
|
||||
$phar_path = $base_dir . '/' . $phar_name;
|
||||
$phar = new Phar($phar_path, 0, $phar_name);
|
||||
$stub = <<<HEREDOC
|
||||
<?php
|
||||
// Phar Stub File
|
||||
Phar::mapPhar('restful.phar');
|
||||
include('phar://restful.phar/RESTful/Bootstrap.php');
|
||||
\RESTful\Bootstrap::pharInit();
|
||||
|
||||
__HALT_COMPILER();
|
||||
HEREDOC;
|
||||
$phar->setStub($stub);
|
||||
exit_unless($phar, "Unable to create a phar. Make sure you have phar.readonly=0 set in your ini file.");
|
||||
$phar->buildFromDirectory(dirname($source_dir));
|
||||
echo "[ OK ]\n";
|
||||
|
||||
echo "Renaming Phar... ";
|
||||
$phar_versioned_name = 'restful-' . \RESTful\Settings::VERSION . '.phar';
|
||||
$phar_versioned_path = $base_dir . '/' . $phar_versioned_name;
|
||||
rename($phar_path, $phar_versioned_path);
|
||||
echo "[ OK ]\n";
|
18
externals/restful/composer.json
vendored
18
externals/restful/composer.json
vendored
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"name": "bninja/restful",
|
||||
"description": "Library for writing RESTful PHP clients.",
|
||||
"homepage": "http://github.com/bninja/restful",
|
||||
"license": "MIT",
|
||||
"keywords": ["http", "api", "client", "rest"],
|
||||
"version": "0.1.7",
|
||||
"authors": [
|
||||
],
|
||||
"require": {
|
||||
"nategood/httpful": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"RESTful": "src/"
|
||||
}
|
||||
}
|
||||
}
|
44
externals/restful/src/RESTful/Bootstrap.php
vendored
44
externals/restful/src/RESTful/Bootstrap.php
vendored
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
/**
|
||||
* Bootstrapper for RESTful does autoloading.
|
||||
*/
|
||||
class Bootstrap
|
||||
{
|
||||
const DIR_SEPARATOR = DIRECTORY_SEPARATOR;
|
||||
const NAMESPACE_SEPARATOR = '\\';
|
||||
|
||||
public static $initialized = false;
|
||||
|
||||
|
||||
public static function init()
|
||||
{
|
||||
spl_autoload_register(array('\RESTful\Bootstrap', 'autoload'));
|
||||
}
|
||||
|
||||
public static function autoload($classname)
|
||||
{
|
||||
self::_autoload(dirname(dirname(__FILE__)), $classname);
|
||||
}
|
||||
|
||||
public static function pharInit()
|
||||
{
|
||||
spl_autoload_register(array('\RESTful\Bootstrap', 'pharAutoload'));
|
||||
}
|
||||
|
||||
public static function pharAutoload($classname)
|
||||
{
|
||||
self::_autoload('phar://restful.phar', $classname);
|
||||
}
|
||||
|
||||
private static function _autoload($base, $classname)
|
||||
{
|
||||
$parts = explode(self::NAMESPACE_SEPARATOR, $classname);
|
||||
$path = $base . self::DIR_SEPARATOR . implode(self::DIR_SEPARATOR, $parts) . '.php';
|
||||
if (file_exists($path)) {
|
||||
require_once($path);
|
||||
}
|
||||
}
|
||||
}
|
78
externals/restful/src/RESTful/Client.php
vendored
78
externals/restful/src/RESTful/Client.php
vendored
|
@ -1,78 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
use RESTful\Exceptions\HTTPError;
|
||||
use RESTful\Settings;
|
||||
|
||||
class Client
|
||||
{
|
||||
public function __construct($settings_class, $request_class = null, $convert_error = null)
|
||||
{
|
||||
$this->request_class = $request_class == null ? '\Httpful\Request' : $request_class;
|
||||
$this->settings_class = $settings_class;
|
||||
$this->convert_error = $convert_error;
|
||||
}
|
||||
|
||||
public function get($uri)
|
||||
{
|
||||
$settings_class = $this->settings_class;
|
||||
$url = $settings_class::$url_root . $uri;
|
||||
$request_class = $this->request_class;
|
||||
$request = $request_class::get($url);
|
||||
|
||||
return $this->_op($request);
|
||||
}
|
||||
|
||||
public function post($uri, $payload)
|
||||
{
|
||||
$settings_class = $this->settings_class;
|
||||
$url = $settings_class::$url_root . $uri;
|
||||
$request_class = $this->request_class;
|
||||
$request = $request_class::post($url, $payload, 'json');
|
||||
|
||||
return $this->_op($request);
|
||||
}
|
||||
|
||||
public function put($uri, $payload)
|
||||
{
|
||||
$settings_class = $this->settings_class;
|
||||
$url = $settings_class::$url_root . $uri;
|
||||
$request_class = $this->request_class;
|
||||
$request = $request_class::put($url, $payload, 'json');
|
||||
|
||||
return $this->_op($request);
|
||||
}
|
||||
|
||||
public function delete($uri)
|
||||
{
|
||||
$settings_class = $this->settings_class;
|
||||
$url = $settings_class::$url_root . $uri;
|
||||
$request_class = $this->request_class;
|
||||
$request = $request_class::delete($url);
|
||||
|
||||
return $this->_op($request);
|
||||
}
|
||||
|
||||
private function _op($request)
|
||||
{
|
||||
$settings_class = $this->settings_class;
|
||||
$user_agent = $settings_class::$agent . '/' . $settings_class::$version;
|
||||
$request->headers['User-Agent'] = $user_agent;
|
||||
if ($settings_class::$api_key != null) {
|
||||
$request = $request->authenticateWith($settings_class::$api_key, '');
|
||||
}
|
||||
$request->expects('json');
|
||||
$response = $request->sendIt();
|
||||
if ($response->hasErrors() || $response->code == 300) {
|
||||
if ($this->convert_error != null) {
|
||||
$error = call_user_func($this->convert_error, $response);
|
||||
} else {
|
||||
$error = new HTTPError($response);
|
||||
}
|
||||
throw $error;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
49
externals/restful/src/RESTful/Collection.php
vendored
49
externals/restful/src/RESTful/Collection.php
vendored
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class Collection extends Itemization
|
||||
{
|
||||
public function __construct($resource, $uri, $data = null)
|
||||
{
|
||||
parent::__construct($resource, $uri, $data);
|
||||
$this->_parseUri();
|
||||
}
|
||||
|
||||
private function _parseUri()
|
||||
{
|
||||
$parsed = parse_url($this->uri);
|
||||
$this->_uri = $parsed['path'];
|
||||
if (array_key_exists('query', $parsed)) {
|
||||
foreach (explode('&', $parsed['query']) as $param) {
|
||||
$param = explode('=', $param);
|
||||
$key = urldecode($param[0]);
|
||||
$val = (count($param) == 1) ? null : urldecode($param[1]);
|
||||
|
||||
// size
|
||||
if ($key == 'limit') {
|
||||
$this->_size = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function create($payload)
|
||||
{
|
||||
$class = $this->resource;
|
||||
$client = $class::getClient();
|
||||
$response = $client->post($this->uri, $payload);
|
||||
|
||||
return new $this->resource($response->body);
|
||||
}
|
||||
|
||||
public function query()
|
||||
{
|
||||
return new Query($this->resource, $this->uri);
|
||||
}
|
||||
|
||||
public function paginate()
|
||||
{
|
||||
return new Pagination($this->resource, $this->uri);
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Base class for all RESTful\Exceptions.
|
||||
*/
|
||||
class Base extends \Exception
|
||||
{
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Indicates an HTTP level error has occurred. The underlying HTTP response is
|
||||
* stored as response member. The response payload fields if any are stored as
|
||||
* members of the same name.
|
||||
*
|
||||
* @see \Httpful\Response
|
||||
*/
|
||||
class HTTPError extends Base
|
||||
{
|
||||
public $response;
|
||||
|
||||
public function __construct($response)
|
||||
{
|
||||
$this->response = $response;
|
||||
$this->_objectify($this->response->body);
|
||||
}
|
||||
|
||||
protected function _objectify($fields)
|
||||
{
|
||||
foreach ($fields as $key => $val) {
|
||||
$this->$key = $val;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Indicates that a query unexpectedly returned multiple results when at most
|
||||
* one was expected.
|
||||
*/
|
||||
class MultipleResultsFound extends Base
|
||||
{
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Indicates that a query unexpectedly returned no results.
|
||||
*/
|
||||
class NoResultFound extends Base
|
||||
{
|
||||
}
|
85
externals/restful/src/RESTful/Field.php
vendored
85
externals/restful/src/RESTful/Field.php
vendored
|
@ -1,85 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class Field
|
||||
{
|
||||
public $name;
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
return new Field($this->name . '.' . $name);
|
||||
}
|
||||
|
||||
public function in($vals)
|
||||
{
|
||||
return new FilterExpression($this->name, 'in', $vals, '!in');
|
||||
}
|
||||
|
||||
public function startswith($prefix)
|
||||
{
|
||||
if (!is_string($prefix)) {
|
||||
throw new \InvalidArgumentException('"startswith" prefix must be a string');
|
||||
}
|
||||
|
||||
return new FilterExpression($this->name, 'contains', $prefix);
|
||||
}
|
||||
|
||||
public function endswith($suffix)
|
||||
{
|
||||
if (!is_string($suffix)) {
|
||||
throw new \InvalidArgumentException('"endswith" suffix must be a string');
|
||||
}
|
||||
|
||||
return new FilterExpression($this->name, 'contains', $suffix);
|
||||
}
|
||||
|
||||
public function contains($fragment)
|
||||
{
|
||||
if (!is_string($fragment)) {
|
||||
throw new \InvalidArgumentException('"contains" fragment must be a string');
|
||||
}
|
||||
|
||||
return new FilterExpression($this->name, 'contains', $fragment, '!contains');
|
||||
}
|
||||
|
||||
public function eq($val)
|
||||
{
|
||||
return new FilterExpression($this->name, '=', $val, '!eq');
|
||||
}
|
||||
|
||||
public function lt($val)
|
||||
{
|
||||
return new FilterExpression($this->name, '<', $val, '>=');
|
||||
}
|
||||
|
||||
public function lte($val)
|
||||
{
|
||||
return new FilterExpression($this->name, '<=', $val, '>');
|
||||
}
|
||||
|
||||
public function gt($val)
|
||||
{
|
||||
return new FilterExpression($this->name, '>', $val, '<=');
|
||||
}
|
||||
|
||||
public function gte($val)
|
||||
{
|
||||
return new FilterExpression($this->name, '>=', $val, '<');
|
||||
}
|
||||
|
||||
public function asc()
|
||||
{
|
||||
return new SortExpression($this->name, true);
|
||||
}
|
||||
|
||||
public function desc()
|
||||
{
|
||||
return new SortExpression($this->name, false);
|
||||
}
|
||||
}
|
11
externals/restful/src/RESTful/Fields.php
vendored
11
externals/restful/src/RESTful/Fields.php
vendored
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class Fields
|
||||
{
|
||||
public function __get($name)
|
||||
{
|
||||
return new Field($name);
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class FilterExpression
|
||||
{
|
||||
public $field,
|
||||
$op,
|
||||
$val,
|
||||
$not_op;
|
||||
|
||||
public function __construct($field, $op, $val, $not_op = null)
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->op = $op;
|
||||
$this->val = $val;
|
||||
$this->not_op = $not_op;
|
||||
}
|
||||
|
||||
public function not()
|
||||
{
|
||||
if (null === $this->not_op) {
|
||||
throw new \LogicException(sprintf('Filter cannot be inverted'));
|
||||
}
|
||||
$temp = $this->op;
|
||||
$this->op = $this->not_op;
|
||||
$this->not_op = $temp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
99
externals/restful/src/RESTful/Itemization.php
vendored
99
externals/restful/src/RESTful/Itemization.php
vendored
|
@ -1,99 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class Itemization implements \IteratorAggregate, \ArrayAccess
|
||||
{
|
||||
public $resource,
|
||||
$uri;
|
||||
|
||||
protected $_page,
|
||||
$_offset = 0,
|
||||
$_size = 25;
|
||||
|
||||
public function __construct($resource, $uri, $data = null)
|
||||
{
|
||||
$this->resource = $resource;
|
||||
$this->uri = $uri;
|
||||
if ($data != null) {
|
||||
$this->_page = new Page($resource, $uri, $data);
|
||||
} else {
|
||||
$this->_page = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _getPage($offset = null)
|
||||
{
|
||||
if ($this->_page == null) {
|
||||
$this->_offset = ($offset == null) ? 0 : $offset * $this->_size;
|
||||
$uri = $this->_buildUri();
|
||||
$this->_page = new Page($this->resource, $uri);
|
||||
} elseif ($offset != null) {
|
||||
$offset = $offset * $this->_size;
|
||||
if ($offset != $this->_offset) {
|
||||
$this->_offset = $offset;
|
||||
$uri = $this->_buildUri();
|
||||
$this->_page = new Page($this->resource, $uri);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_page;
|
||||
}
|
||||
|
||||
protected function _getItem($offset)
|
||||
{
|
||||
$page_offset = floor($offset / $this->_size);
|
||||
$page = $this->_getPage($page_offset);
|
||||
|
||||
return $page->items[$offset - $page->offset];
|
||||
}
|
||||
|
||||
public function total()
|
||||
{
|
||||
return $this->_getPage()->total;
|
||||
}
|
||||
|
||||
protected function _buildUri($offset = null)
|
||||
{
|
||||
# TODO: hacky but works for now
|
||||
$offset = ($offset == null) ? $this->_offset : $offset;
|
||||
if (strpos($this->uri, '?') === false) {
|
||||
$uri = $this->uri . '?';
|
||||
} else {
|
||||
$uri = $this->uri . '&';
|
||||
}
|
||||
$uri = $uri . 'offset=' . strval($offset);
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
// IteratorAggregate
|
||||
public function getIterator()
|
||||
{
|
||||
$uri = $this->_buildUri($offset = 0);
|
||||
$uri = $this->_buildUri($offset = 0);
|
||||
|
||||
return new ItemizationIterator($this->resource, $uri);
|
||||
}
|
||||
|
||||
// ArrayAccess
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
throw new \BadMethodCallException(get_class($this) . ' array access is read-only');
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return (0 <= $offset && $offset < $this->total());
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
throw new \BadMethodCallException(get_class($this) . ' array access is read-only');
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->_getItem($offset);
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class ItemizationIterator implements \Iterator
|
||||
{
|
||||
protected $_page,
|
||||
$_offset = 0;
|
||||
|
||||
public function __construct($resource, $uri, $data = null)
|
||||
{
|
||||
$this->_page = new Page($resource, $uri, $data);
|
||||
}
|
||||
|
||||
// Iterator
|
||||
public function current()
|
||||
{
|
||||
return $this->_page->items[$this->_offset];
|
||||
}
|
||||
|
||||
public function key()
|
||||
{
|
||||
return $this->_page->offset + $this->_offset;
|
||||
}
|
||||
|
||||
public function next()
|
||||
{
|
||||
$this->_offset += 1;
|
||||
if ($this->_offset >= count($this->_page->items)) {
|
||||
$this->_offset = 0;
|
||||
$this->_page = $this->_page->next();
|
||||
}
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
{
|
||||
$this->_page = $this->_page->first();
|
||||
$this->_offset = 0;
|
||||
}
|
||||
|
||||
public function valid()
|
||||
{
|
||||
return ($this->_page != null && $this->_offset < count($this->_page->items));
|
||||
}
|
||||
}
|
72
externals/restful/src/RESTful/Page.php
vendored
72
externals/restful/src/RESTful/Page.php
vendored
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class Page
|
||||
{
|
||||
public $resource,
|
||||
$total,
|
||||
$items,
|
||||
$offset,
|
||||
$limit;
|
||||
|
||||
private $_first_uri,
|
||||
$_previous_uri,
|
||||
$_next_uri,
|
||||
$_last_uri;
|
||||
|
||||
public function __construct($resource, $uri, $data = null)
|
||||
{
|
||||
$this->resource = $resource;
|
||||
if ($data == null) {
|
||||
$client = $resource::getClient();
|
||||
$data = $client->get($uri)->body;
|
||||
}
|
||||
$this->total = $data->total;
|
||||
$this->items = array_map(
|
||||
function ($x) use ($resource) {
|
||||
return new $resource($x);
|
||||
},
|
||||
$data->items);
|
||||
$this->offset = $data->offset;
|
||||
$this->limit = $data->limit;
|
||||
$this->_first_uri = property_exists($data, 'first_uri') ? $data->first_uri : null;
|
||||
$this->_previous_uri = property_exists($data, 'previous_uri') ? $data->previous_uri : null;
|
||||
$this->_next_uri = property_exists($data, 'next_uri') ? $data->next_uri : null;
|
||||
$this->_last_uri = property_exists($data, 'last_uri') ? $data->last_uri : null;
|
||||
}
|
||||
|
||||
public function first()
|
||||
{
|
||||
return new Page($this->resource, $this->_first_uri);
|
||||
}
|
||||
|
||||
public function next()
|
||||
{
|
||||
if (!$this->hasNext()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Page($this->resource, $this->_next_uri);
|
||||
}
|
||||
|
||||
public function hasNext()
|
||||
{
|
||||
return $this->_next_uri != null;
|
||||
}
|
||||
|
||||
public function previous()
|
||||
{
|
||||
return new Page($this->resource, $this->_previous_uri);
|
||||
}
|
||||
|
||||
public function hasPrevious()
|
||||
{
|
||||
return $this->_previous_uri != null;
|
||||
}
|
||||
|
||||
public function last()
|
||||
{
|
||||
return new Page($this->resource, $this->_last_uri);
|
||||
}
|
||||
}
|
90
externals/restful/src/RESTful/Pagination.php
vendored
90
externals/restful/src/RESTful/Pagination.php
vendored
|
@ -1,90 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class Pagination implements \IteratorAggregate, \ArrayAccess
|
||||
{
|
||||
public $resource,
|
||||
$uri;
|
||||
|
||||
protected $_page,
|
||||
$_offset = 0,
|
||||
$_size = 25;
|
||||
|
||||
public function __construct($resource, $uri, $data = null)
|
||||
{
|
||||
$this->resource = $resource;
|
||||
$this->uri = $uri;
|
||||
if ($data != null) {
|
||||
$this->_page = new Page($resource, $uri, $data);
|
||||
} else {
|
||||
$this->_page = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _getPage($offset = null)
|
||||
{
|
||||
if ($this->_page == null) {
|
||||
$this->_offset = ($offset == null) ? 0 : $offset * $this->_size;
|
||||
$uri = $this->_buildUri();
|
||||
$this->_page = new Page($this->resource, $uri);
|
||||
} elseif ($offset != null) {
|
||||
$offset = $offset * $this->_size;
|
||||
if ($offset != $this->_offset) {
|
||||
$this->_offset = $offset;
|
||||
$uri = $this->_buildUri();
|
||||
$this->_page = new Page($this->resource, $uri);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_page;
|
||||
}
|
||||
|
||||
public function total()
|
||||
{
|
||||
return floor($this->_getPage()->total / $this->_size);
|
||||
}
|
||||
|
||||
protected function _buildUri($offset = null)
|
||||
{
|
||||
# TODO: hacky but works for now
|
||||
$offset = ($offset == null) ? $this->_offset : $offset;
|
||||
if (strpos($this->uri, '?') === false) {
|
||||
$uri = $this->uri . '?';
|
||||
} else {
|
||||
$uri = $this->uri . '&';
|
||||
}
|
||||
$uri = $uri . 'offset=' . strval($offset);
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
// IteratorAggregate
|
||||
public function getIterator()
|
||||
{
|
||||
$uri = $this->_buildUri($offset = 0);
|
||||
|
||||
return new PaginationIterator($this->resource, $uri);
|
||||
}
|
||||
|
||||
// ArrayAccess
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
throw new \BadMethodCallException(get_class($this) . ' array access is read-only');
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return (0 <= $offset && $offset < $this->total());
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
throw new \BadMethodCallException(get_class($this) . ' array access is read-only');
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->_getPage($offset);
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class PaginationIterator implements \Iterator
|
||||
{
|
||||
public function __construct($resource, $uri, $data = null)
|
||||
{
|
||||
$this->_page = new Page($resource, $uri, $data);
|
||||
}
|
||||
|
||||
// Iterator
|
||||
public function current()
|
||||
{
|
||||
return $this->_page;
|
||||
}
|
||||
|
||||
public function key()
|
||||
{
|
||||
return $this->_page->index;
|
||||
}
|
||||
|
||||
public function next()
|
||||
{
|
||||
$this->_page = $this->_page->next();
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
{
|
||||
$this->_page = $this->_page->first();
|
||||
}
|
||||
|
||||
public function valid()
|
||||
{
|
||||
return $this->_page != null;
|
||||
}
|
||||
}
|
161
externals/restful/src/RESTful/Query.php
vendored
161
externals/restful/src/RESTful/Query.php
vendored
|
@ -1,161 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
use RESTful\Exceptions\NoResultFound;
|
||||
use RESTful\Exceptions\MultipleResultsFound;
|
||||
|
||||
class Query extends Itemization
|
||||
{
|
||||
public $filters = array(),
|
||||
$sorts = array(),
|
||||
$size;
|
||||
|
||||
public function __construct($resource, $uri)
|
||||
{
|
||||
parent::__construct($resource, $uri);
|
||||
$this->size = $this->_size;
|
||||
$this->_parseUri($uri);
|
||||
}
|
||||
|
||||
private function _parseUri($uri)
|
||||
{
|
||||
$parsed = parse_url($uri);
|
||||
$this->uri = $parsed['path'];
|
||||
if (array_key_exists('query', $parsed)) {
|
||||
foreach (explode('&', $parsed['query']) as $param) {
|
||||
$param = explode('=', $param);
|
||||
$key = urldecode($param[0]);
|
||||
$val = (count($param) == 1) ? null : urldecode($param[1]);
|
||||
|
||||
// limit
|
||||
if ($key == 'limit') {
|
||||
$this->size = $this->_size = $val;
|
||||
} // sorts
|
||||
else if ($key == 'sort') {
|
||||
array_push($this->sorts, $val);
|
||||
} // everything else
|
||||
else {
|
||||
if (!array_key_exists($key, $this->filters)) {
|
||||
$this->filters[$key] = array();
|
||||
}
|
||||
if (!is_array($val)) {
|
||||
$val = array($val);
|
||||
}
|
||||
$this->filters[$key] = array_merge($this->filters[$key], $val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function _buildUri($offset = null)
|
||||
{
|
||||
// params
|
||||
$params = array_merge(
|
||||
$this->filters,
|
||||
array(
|
||||
'sort' => $this->sorts,
|
||||
'limit' => $this->_size,
|
||||
'offset' => ($offset == null) ? $this->_offset : $offset
|
||||
)
|
||||
);
|
||||
$getSingle = function ($v) {
|
||||
if (is_array($v) && count($v) == 1)
|
||||
return $v[0];
|
||||
return $v;
|
||||
};
|
||||
$params = array_map($getSingle, $params);
|
||||
|
||||
// url encode params
|
||||
// NOTE: http://stackoverflow.com/a/8171667/1339571
|
||||
$qs = http_build_query($params);
|
||||
$qs = preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', $qs);
|
||||
|
||||
return $this->uri . '?' . $qs;
|
||||
}
|
||||
|
||||
private function _reset()
|
||||
{
|
||||
$this->_page = null;
|
||||
}
|
||||
|
||||
public function filter($expression)
|
||||
{
|
||||
if ($expression->op == '=') {
|
||||
$field = $expression->field;
|
||||
} else {
|
||||
$field = $expression->field . '[' . $expression->op . ']';
|
||||
}
|
||||
if (is_array($expression->val)) {
|
||||
$val = implode(',', $expression->val);
|
||||
} else {
|
||||
$val = $expression->val;
|
||||
}
|
||||
if (!array_key_exists($field, $this->filters)) {
|
||||
$this->filters[$field] = array();
|
||||
}
|
||||
array_push($this->filters[$field], $val);
|
||||
$this->_reset();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function sort($expression)
|
||||
{
|
||||
$dir = $expression->ascending ? 'asc' : 'desc';
|
||||
array_push($this->sorts, $expression->field . ',' . $dir);
|
||||
$this->_reset();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function limit($limit)
|
||||
{
|
||||
$this->size = $this->_size = $limit;
|
||||
$this->_reset();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function all()
|
||||
{
|
||||
$items = array();
|
||||
foreach ($this as $item) {
|
||||
array_push($items, $item);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function first()
|
||||
{
|
||||
$prev_size = $this->_size;
|
||||
$this->_size = 1;
|
||||
$page = new Page($this->resource, $this->_buildUri());
|
||||
$this->_size = $prev_size;
|
||||
$item = count($page->items) != 0 ? $page->items[0] : null;
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function one()
|
||||
{
|
||||
$prev_size = $this->_size;
|
||||
$this->_size = 2;
|
||||
$page = new Page($this->resource, $this->_buildUri());
|
||||
$this->_size = $prev_size;
|
||||
if (count($page->items) == 1) {
|
||||
return $page->items[0];
|
||||
}
|
||||
if (count($page->items) == 0) {
|
||||
throw new NoResultFound();
|
||||
}
|
||||
|
||||
throw new MultipleResultsFound();
|
||||
}
|
||||
|
||||
public function paginate()
|
||||
{
|
||||
return new Pagination($this->resource, $this->_buildUri());
|
||||
}
|
||||
}
|
29
externals/restful/src/RESTful/Registry.php
vendored
29
externals/restful/src/RESTful/Registry.php
vendored
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class Registry
|
||||
{
|
||||
protected $_resources = array();
|
||||
|
||||
public function add($resource)
|
||||
{
|
||||
array_push($this->_resources, $resource);
|
||||
}
|
||||
|
||||
public function match($uri)
|
||||
{
|
||||
foreach ($this->_resources as $resource) {
|
||||
$spec = $resource::getURISpec();
|
||||
$result = $spec->match($uri);
|
||||
if ($result == null) {
|
||||
continue;
|
||||
}
|
||||
$result['class'] = $resource;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
205
externals/restful/src/RESTful/Resource.php
vendored
205
externals/restful/src/RESTful/Resource.php
vendored
|
@ -1,205 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
abstract class Resource
|
||||
{
|
||||
protected $_collection_uris,
|
||||
$_member_uris;
|
||||
|
||||
public static function getClient()
|
||||
{
|
||||
$class = get_called_class();
|
||||
|
||||
return $class::$_client;
|
||||
}
|
||||
|
||||
public static function getRegistry()
|
||||
{
|
||||
$class = get_called_class();
|
||||
|
||||
return $class::$_registry;
|
||||
}
|
||||
|
||||
public static function getURISpec()
|
||||
{
|
||||
$class = get_called_class();
|
||||
|
||||
return $class::$_uri_spec;
|
||||
}
|
||||
|
||||
public function __construct($fields = null)
|
||||
{
|
||||
if ($fields == null) {
|
||||
$fields = array();
|
||||
}
|
||||
$this->_objectify($fields);
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
// collection uri
|
||||
if (array_key_exists($name, $this->_collection_uris)) {
|
||||
$result = $this->_collection_uris[$name];
|
||||
$this->$name = new Collection($result['class'], $result['uri']);
|
||||
|
||||
return $this->$name;
|
||||
} // member uri
|
||||
else if (array_key_exists($name, $this->_member_uris)) {
|
||||
$result = $this->$_collection_uris[$name];
|
||||
$response = self::getClient() . get($result['uri']);
|
||||
$class = $result['class'];
|
||||
$this->$name = new $class($response->body);
|
||||
|
||||
return $this->$name;
|
||||
}
|
||||
|
||||
// unknown
|
||||
$trace = debug_backtrace();
|
||||
trigger_error(
|
||||
sprintf('Undefined property via __get(): %s in %s on line %s', $name, $trace[0]['file'], $trace[0]['line']),
|
||||
E_USER_NOTICE
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function __isset($name)
|
||||
{
|
||||
if (array_key_exists($name, $this->_collection_uris) || array_key_exists($name, $this->_member_uris)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function _objectify($fields)
|
||||
{
|
||||
// initialize uris
|
||||
$this->_collection_uris = array();
|
||||
$this->_member_uris = array();
|
||||
|
||||
foreach ($fields as $key => $val) {
|
||||
// nested uri
|
||||
if ((strlen($key) - 3) == strrpos($key, 'uri', 0) && $key != 'uri') {
|
||||
$result = self::getRegistry()->match($val);
|
||||
if ($result != null) {
|
||||
$name = substr($key, 0, -4);
|
||||
$class = $result['class'];
|
||||
if ($result['collection']) {
|
||||
$this->_collection_uris[$name] = array(
|
||||
'class' => $class,
|
||||
'uri' => $val,
|
||||
);
|
||||
} else {
|
||||
$this->_member_uris[$name] = array(
|
||||
'class' => $class,
|
||||
'uri' => $val,
|
||||
);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
} elseif (is_object($val) && property_exists($val, 'uri')) {
|
||||
// nested
|
||||
$result = self::getRegistry()->match($val->uri);
|
||||
if ($result != null) {
|
||||
$class = $result['class'];
|
||||
if ($result['collection']) {
|
||||
$this->$key = new Collection($class, $val['uri'], $val);
|
||||
} else {
|
||||
$this->$key = new $class($val);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
} elseif (is_array($val) && array_key_exists('uri', $val)) {
|
||||
$result = self::getRegistry()->match($val['uri']);
|
||||
if ($result != null) {
|
||||
$class = $result['class'];
|
||||
if ($result['collection']) {
|
||||
$this->$key = new Collection($class, $val['uri'], $val);
|
||||
} else {
|
||||
$this->$key = new $class($val);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// default
|
||||
$this->$key = $val;
|
||||
}
|
||||
}
|
||||
|
||||
public static function query()
|
||||
{
|
||||
$uri_spec = self::getURISpec();
|
||||
if ($uri_spec == null || $uri_spec->collection_uri == null) {
|
||||
$msg = sprintf('Cannot directly query %s resources', get_called_class());
|
||||
throw new \LogicException($msg);
|
||||
}
|
||||
|
||||
return new Query(get_called_class(), $uri_spec->collection_uri);
|
||||
}
|
||||
|
||||
public static function get($uri)
|
||||
{
|
||||
# id
|
||||
if (strncmp($uri, '/', 1)) {
|
||||
$uri_spec = self::getURISpec();
|
||||
if ($uri_spec == null || $uri_spec->collection_uri == null) {
|
||||
$msg = sprintf('Cannot get %s resources by id %s', $class, $uri);
|
||||
throw new \LogicException($msg);
|
||||
}
|
||||
$uri = $uri_spec->collection_uri . '/' . $uri;
|
||||
}
|
||||
|
||||
$response = self::getClient()->get($uri);
|
||||
$class = get_called_class();
|
||||
|
||||
return new $class($response->body);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
// payload
|
||||
$payload = array();
|
||||
foreach ($this as $key => $val) {
|
||||
if ($key[0] == '_' || is_object($val)) {
|
||||
continue;
|
||||
}
|
||||
$payload[$key] = $val;
|
||||
}
|
||||
|
||||
// update
|
||||
if (array_key_exists('uri', $payload)) {
|
||||
$uri = $payload['uri'];
|
||||
unset($payload['uri']);
|
||||
$response = self::getClient()->put($uri, $payload);
|
||||
} else {
|
||||
// create
|
||||
$class = get_class($this);
|
||||
if ($class::$_uri_spec == null || $class::$_uri_spec->collection_uri == null) {
|
||||
$msg = sprintf('Cannot directly create %s resources', $class);
|
||||
throw new \LogicException($msg);
|
||||
}
|
||||
$response = self::getClient()->post($class::$_uri_spec->collection_uri, $payload);
|
||||
}
|
||||
|
||||
// re-objectify
|
||||
foreach ($this as $key => $val) {
|
||||
unset($this->$key);
|
||||
}
|
||||
$this->_objectify($response->body);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
self::getClient()->delete($this->uri);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
12
externals/restful/src/RESTful/Settings.php
vendored
12
externals/restful/src/RESTful/Settings.php
vendored
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
/**
|
||||
* Settings.
|
||||
*
|
||||
*/
|
||||
class Settings
|
||||
{
|
||||
const VERSION = '0.1.7';
|
||||
}
|
15
externals/restful/src/RESTful/SortExpression.php
vendored
15
externals/restful/src/RESTful/SortExpression.php
vendored
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class SortExpression
|
||||
{
|
||||
public $name,
|
||||
$ascending;
|
||||
|
||||
public function __construct($field, $ascending = true)
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->ascending = $ascending;
|
||||
}
|
||||
}
|
58
externals/restful/src/RESTful/URISpec.php
vendored
58
externals/restful/src/RESTful/URISpec.php
vendored
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class URISpec
|
||||
{
|
||||
public $collection_uri = null,
|
||||
$name,
|
||||
$idNames;
|
||||
|
||||
public function __construct($name, $idNames, $root = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
if (!is_array($idNames)) {
|
||||
$idNames = array($idNames);
|
||||
}
|
||||
$this->idNames = $idNames;
|
||||
if ($root != null) {
|
||||
if ($root == '' || substr($root, -1) == '/') {
|
||||
$this->collection_uri = $root . $name;
|
||||
} else {
|
||||
$this->collection_uri = $root . '/' . $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function match($uri)
|
||||
{
|
||||
$parts = explode('/', rtrim($uri, "/"));
|
||||
|
||||
// collection
|
||||
if ($parts[count($parts) - 1] == $this->name) {
|
||||
|
||||
return array(
|
||||
'collection' => true,
|
||||
);
|
||||
}
|
||||
|
||||
// non-member
|
||||
if (count($parts) < count($this->idNames) + 1 ||
|
||||
$parts[count($parts) - 1 - count($this->idNames)] != $this->name
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// member
|
||||
$ids = array_combine(
|
||||
$this->idNames,
|
||||
array_slice($parts, -count($this->idNames))
|
||||
);
|
||||
$result = array(
|
||||
'collection' => false,
|
||||
'ids' => $ids,
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
241
externals/restful/tests/RESTful/CoreTest.php
vendored
241
externals/restful/tests/RESTful/CoreTest.php
vendored
|
@ -1,241 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace RESTful\Test;
|
||||
|
||||
\RESTful\Bootstrap::init();
|
||||
\Httpful\Bootstrap::init();
|
||||
|
||||
use RESTful\URISpec;
|
||||
use RESTful\Client;
|
||||
use RESTful\Registry;
|
||||
use RESTful\Fields;
|
||||
use RESTful\Query;
|
||||
use RESTful\Page;
|
||||
|
||||
class Settings
|
||||
{
|
||||
public static $url_root = 'http://api.example.com';
|
||||
|
||||
public static $agent = 'example-php';
|
||||
|
||||
public static $version = '0.1.0';
|
||||
|
||||
public static $api_key = null;
|
||||
}
|
||||
|
||||
class Resource extends \RESTful\Resource
|
||||
{
|
||||
public static $fields, $f;
|
||||
|
||||
protected static $_client, $_registry, $_uri_spec;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_client = new Client('Settings');
|
||||
self::$_registry = new Registry();
|
||||
self::$f = self::$fields = new Fields();
|
||||
}
|
||||
|
||||
public static function getClient()
|
||||
{
|
||||
$class = get_called_class();
|
||||
return $class::$_client;
|
||||
}
|
||||
|
||||
public static function getRegistry()
|
||||
{
|
||||
$class = get_called_class();
|
||||
return $class::$_registry;
|
||||
}
|
||||
|
||||
public static function getURISpec()
|
||||
{
|
||||
$class = get_called_class();
|
||||
return $class::$_uri_spec;
|
||||
}
|
||||
}
|
||||
|
||||
Resource::init();
|
||||
|
||||
class A extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('as', 'id', '/');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
}
|
||||
|
||||
A::init();
|
||||
|
||||
class B extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('bs', 'id', '/');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
}
|
||||
|
||||
B::init();
|
||||
|
||||
class URISpecTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testNoRoot()
|
||||
{
|
||||
$uri_spec = new URISpec('grapes', 'seed');
|
||||
$this->assertEquals($uri_spec->collection_uri, null);
|
||||
|
||||
$result = $uri_spec->match('/some/raisins');
|
||||
$this->assertEquals($result, null);
|
||||
|
||||
$result = $uri_spec->match('/some/grapes');
|
||||
$this->assertEquals($result, array('collection' => true));
|
||||
|
||||
$result = $uri_spec->match('/some/grapes/1234');
|
||||
$expected = array(
|
||||
'collection' => false,
|
||||
'ids' => array('seed' => '1234')
|
||||
);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
public function testSingleId()
|
||||
{
|
||||
$uri_spec = new URISpec('tomatoes', 'stem', '/v1');
|
||||
$this->assertNotEquals($uri_spec->collection_uri, null);
|
||||
|
||||
$result = $uri_spec->match('/some/tomatoes/that/are/green');
|
||||
$this->assertEquals($result, null);
|
||||
|
||||
$result = $uri_spec->match('/some/tomatoes');
|
||||
$this->assertEquals($result, array('collection' => true));
|
||||
|
||||
$result = $uri_spec->match('/some/tomatoes/4321');
|
||||
$expected = array(
|
||||
'collection' => false,
|
||||
'ids' => array('stem' => '4321')
|
||||
);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
public function testMultipleIds()
|
||||
{
|
||||
$uri_spec = new URISpec('tomatoes', array('stem', 'root'), '/v1');
|
||||
$this->assertNotEquals($uri_spec->collection_uri, null);
|
||||
|
||||
$result = $uri_spec->match('/some/tomatoes/that/are/green');
|
||||
$this->assertEquals($result, null);
|
||||
|
||||
$result = $uri_spec->match('/some/tomatoes');
|
||||
$this->assertEquals($result, array('collection' => true));
|
||||
|
||||
$result = $uri_spec->match('/some/tomatoes/4321/1234');
|
||||
$expected = array(
|
||||
'collection' => false,
|
||||
'ids' => array('stem' => '4321', 'root' => '1234')
|
||||
);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
}
|
||||
|
||||
class QueryTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testParse()
|
||||
{
|
||||
$uri = '/some/uri?field2=123&sort=field5%2Cdesc&limit=101&field3.field4%5Bcontains%5D=hi';
|
||||
$query = new Query('Resource', $uri);
|
||||
$expected = array(
|
||||
'field2' => array('123'),
|
||||
'field3.field4[contains]' => array('hi')
|
||||
);
|
||||
$this->assertEquals($query->filters, $expected);
|
||||
$expected = array('field5,desc');
|
||||
$this->assertEquals($query->sorts, $expected);
|
||||
$this->assertEquals($query->size, 101);
|
||||
}
|
||||
|
||||
public function testBuild()
|
||||
{
|
||||
$query = new Query('Resource', '/some/uri');
|
||||
$query->filter(Resource::$f->name->eq('Wonka Chocs'))
|
||||
->filter(Resource::$f->support_email->endswith('gmail.com'))
|
||||
->filter(Resource::$f->variable_fee_percentage->gte(3.5))
|
||||
->sort(Resource::$f->name->asc())
|
||||
->sort(Resource::$f->variable_fee_percentage->desc())
|
||||
->limit(101);
|
||||
$this->assertEquals(
|
||||
$query->filters,
|
||||
array(
|
||||
'name' => array('Wonka Chocs'),
|
||||
'support_email[contains]' => array('gmail.com'),
|
||||
'variable_fee_percentage[>=]'=> array(3.5)
|
||||
)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$query->sorts,
|
||||
array('name,asc', 'variable_fee_percentage,desc')
|
||||
);
|
||||
$this->assertEquals(
|
||||
$query->size,
|
||||
101
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PageTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$data = new \stdClass();
|
||||
$data->first_uri = 'some/first/uri';
|
||||
$data->previous_uri = 'some/previous/uri';
|
||||
$data->next_uri = null;
|
||||
$data->last_uri = 'some/last/uri';
|
||||
$data->limit= 25;
|
||||
$data->offset = 0;
|
||||
$data->total = 101;
|
||||
$data->items = array();
|
||||
|
||||
$page = new Page(
|
||||
'Resource',
|
||||
'/some/uri',
|
||||
$data
|
||||
);
|
||||
|
||||
$this->assertEquals($page->resource, 'Resource');
|
||||
$this->assertEquals($page->total, 101);
|
||||
$this->assertEquals($page->items, array());
|
||||
$this->assertTrue($page->hasPrevious());
|
||||
$this->assertFalse($page->hasNext());
|
||||
}
|
||||
}
|
||||
|
||||
class ResourceTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testQuery()
|
||||
{
|
||||
$query = A::query();
|
||||
$this->assertEquals(get_class($query), 'RESTful\Query');
|
||||
}
|
||||
|
||||
public function testObjectify()
|
||||
{
|
||||
$a = new A(array(
|
||||
'uri' => '/as/123',
|
||||
'field1' => 123,
|
||||
'b' => array(
|
||||
'uri' => '/bs/321',
|
||||
'field2' => 321
|
||||
))
|
||||
);
|
||||
$this->assertEquals(get_class($a), 'RESTful\Test\A');
|
||||
$this->assertEquals($a->field1, 123);
|
||||
$this->assertEquals(get_class($a->b), 'RESTful\Test\B');
|
||||
$this->assertEquals($a->b->field2, 321);
|
||||
}
|
||||
}
|
8
externals/restful/tests/phpunit.xml
vendored
8
externals/restful/tests/phpunit.xml
vendored
|
@ -1,8 +0,0 @@
|
|||
<phpunit>
|
||||
<testsuite name="RESTful">
|
||||
<directory>.</directory>
|
||||
</testsuite>
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
|
||||
</logging>
|
||||
</phpunit>
|
21
externals/wepay/LICENSE
vendored
21
externals/wepay/LICENSE
vendored
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (C) 2013, WePay, Inc. <api at wepay dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
85
externals/wepay/README.md
vendored
85
externals/wepay/README.md
vendored
|
@ -1,85 +0,0 @@
|
|||
WePay PHP SDK
|
||||
=============
|
||||
|
||||
WePay's API allows you to easily add payments into your application.
|
||||
|
||||
For full documentation, see [WePay's developer documentation](https://www.wepay.com/developer)
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
In addition to the samples below, we have included a very basic demo application in the `demoapp` directory. See its README file for additional information.
|
||||
|
||||
### Configuration ###
|
||||
|
||||
For all requests, you must initialize the SDK with your Client ID and Client Secret, into either Staging or Production mode. All API calls made against WePay's staging environment mirror production in functionality, but do not actually move money. This allows you to develop your application and test the checkout experience from the perspective of your users without spending any money on payments. Our [full documentation](https://www.wepay.com/developer) contains additional information on test account numbers you can use in addition to "magic" amounts you can use to trigger payment failures and reversals (helpful for testing IPNs).
|
||||
|
||||
**Note:** Staging and Production are two completely independent environments and share NO data with each other. This means that in order to use staging, you must register at [stage.wepay.com](https://stage.wepay.com/developer) and get a set of API keys for your Staging application, and must do the same on Production when you are ready to go live. API keys and access tokens granted on stage *can not* be used on Production, and vice-versa.
|
||||
|
||||
<?php
|
||||
require './wepay.php';
|
||||
WePay::useProduction('YOUR CLIENT ID', 'YOUR CLIENT SECRET'); // To initialize staging, use WePay::useStaging('ID','SECRET'); instead.
|
||||
|
||||
### Authentication ###
|
||||
|
||||
To obtain an access token for your user, you must redirect the user to WePay for authentication. WePay uses OAuth2 for authorization, which is detailed [in our documentation](https://www.wepay.com/developer/reference/oauth2). To generate the URI to which you must redirect your user, the SDK contains `WePay::getAuthorizationUri($scope, $redirect_uri)`. `$scope` should be an array of scope strings detailed in the documentation. To request full access (most useful for testing, since users may be weary of granting permission to your application if it wants to do too much), you pay pass in `WePay::getAllScopes()`. `$redirect_uri` must be a fully qualified URI where we will send the user after permission is granted (or not granted), and the domain must match your application settings.
|
||||
|
||||
If the user grants permission, he or she will be redirected to your `$redirect_uri` with `code=XXXX` appended to the query string. If permission is not granted, we will instead put `error=XXXX` in the query string. If `code` is present, the following will exchange it for an access token. Note that codes are only valid for several minutes, so you should do this immediately after the user is redirected back to your website or application.
|
||||
|
||||
<?php
|
||||
if (!empty($_GET['error'])) {
|
||||
// user did not grant permissions
|
||||
}
|
||||
elseif (empty($_GET['code'])) {
|
||||
// set $scope and $redirect_uri before doing this
|
||||
// this will send the user to WePay to authenticate
|
||||
$uri = WePay::getAuthorizationUri($scope, $redirect_uri);
|
||||
header("Location: $uri");
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
$info = WePay::getToken($_GET['code'], $redirect_uri);
|
||||
if ($info) {
|
||||
// YOUR ACCESS TOKEN IS HERE
|
||||
$access_token = $info->access_token;
|
||||
}
|
||||
else {
|
||||
// Unable to obtain access token
|
||||
}
|
||||
}
|
||||
|
||||
Full details on the access token response are [here](https://www.wepay.com/developer/reference/oauth2#token).
|
||||
|
||||
**Note:** If you only need access for yourself (e.g., for a personal storefront), the application settings page automatically creates an access token for you. Simply copy and paste it into your code rather than manually going through the authentication flow.
|
||||
|
||||
### Making API Calls ###
|
||||
|
||||
With the `$access_token` from above, get a new SDK object:
|
||||
|
||||
<?php
|
||||
$wepay = new WePay($access_token);
|
||||
|
||||
Then you can make a simple API call. This will list the user's accounts available to your application:
|
||||
|
||||
// (continued from above)
|
||||
try {
|
||||
$accounts = $wepay->request('account/find');
|
||||
foreach ($accounts as $account) {
|
||||
echo "<a href=\"$account->account_uri\">$account->name</a>: $account->description <br />";
|
||||
}
|
||||
}
|
||||
catch (WePayException $e) {
|
||||
// Something went wrong - normally you would log
|
||||
// this and give your user a more informative message
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
And that's it! For more detail on what API calls are available, their parameters and responses, and what permissions they require, please see [our documentation](https://www.wepay.com/developer/reference). For some more detailed examples, look in the `demoapp` directory and check the README. Dropping the entire directory in a web-accessible location and adding your API keys should allow you to be up and running in just a few seconds.
|
||||
|
||||
### SSL Certificate ###
|
||||
|
||||
If making an API call causes the following problem:
|
||||
|
||||
Uncaught exception 'Exception' with message 'cURL error while making API call to WePay: SSL certificate problem, verify that the CA cert is OK. Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed'
|
||||
|
||||
You can read the solution here: https://support.wepay.com/entries/21095813-problem-with-ssl-certificate-verification
|
13
externals/wepay/composer.json
vendored
13
externals/wepay/composer.json
vendored
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"name": "wepay/php-sdk",
|
||||
"description": "WePay APIv2 SDK for PHP",
|
||||
"authors": [
|
||||
{
|
||||
"name": "WePay",
|
||||
"email": "api@wepay.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"files": ["wepay.php"]
|
||||
}
|
||||
}
|
19
externals/wepay/demoapp/README
vendored
19
externals/wepay/demoapp/README
vendored
|
@ -1,19 +0,0 @@
|
|||
After registering your application at wepay.com (or stage.wepay.com), you
|
||||
need to make two updates to this application:
|
||||
|
||||
1 - set your client_id and client_secret in _shared.php
|
||||
2 - set the redirect_uri in login.php
|
||||
|
||||
That should be enough to start making API calls against WePay's API. While
|
||||
this is by no means a production-ready example, it should provide you a
|
||||
couple ideas on how to get running.
|
||||
|
||||
It also defaults to requesting all possible scope fields in the
|
||||
authentication request. We suggest limiting the request to the minimum
|
||||
your application requires, which will maximize the chance the user
|
||||
grants permissions to your application. You can customize this in
|
||||
login.php.
|
||||
|
||||
If you have any questions, please contact the API team: api@wepay.com
|
||||
|
||||
- WePay
|
4
externals/wepay/demoapp/_shared.php
vendored
4
externals/wepay/demoapp/_shared.php
vendored
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
require '../wepay.php';
|
||||
Wepay::useStaging('YOUR CLIENT ID', 'YOUR CLIENT SECRET');
|
||||
session_start();
|
20
externals/wepay/demoapp/accountlist.php
vendored
20
externals/wepay/demoapp/accountlist.php
vendored
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
require './_shared.php';
|
||||
?>
|
||||
<h1>WePay Demo App: Account List</h1>
|
||||
<a href="index.php">Back</a>
|
||||
<br />
|
||||
|
||||
<?php
|
||||
try {
|
||||
$wepay = new WePay($_SESSION['wepay_access_token']);
|
||||
$accounts = $wepay->request('account/find');
|
||||
foreach ($accounts as $account) {
|
||||
echo "<a href=\"$account->account_uri\">$account->name</a>: $account->description <br />";
|
||||
}
|
||||
}
|
||||
catch (WePayException $e) {
|
||||
// Something went wrong - normally you would log
|
||||
// this and give your user a more informative message
|
||||
echo $e->getMessage();
|
||||
}
|
20
externals/wepay/demoapp/index.php
vendored
20
externals/wepay/demoapp/index.php
vendored
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
require './_shared.php';
|
||||
?>
|
||||
|
||||
<h1>WePay Demo App</h1>
|
||||
<?php if (empty($_SESSION['wepay_access_token'])): ?>
|
||||
|
||||
<a href="login.php">Log in with WePay</a>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<a href="user.php">User info</a>
|
||||
<br />
|
||||
<a href="openaccount.php">Open new account</a>
|
||||
<br />
|
||||
<a href="accountlist.php">Account list</a>
|
||||
<br />
|
||||
<a href="logout.php">Log out</a>
|
||||
|
||||
<?php endif; ?>
|
41
externals/wepay/demoapp/login.php
vendored
41
externals/wepay/demoapp/login.php
vendored
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
require './_shared.php';
|
||||
|
||||
// ** YOU MUST CHANGE THIS FOR THE SAMPLE APP TO WORK **
|
||||
$redirect_uri = 'http://YOUR SERVER NAME/login.php';
|
||||
$scope = WePay::getAllScopes();
|
||||
|
||||
// If we are already logged in, send the user home
|
||||
if (!empty($_SESSION['wepay_access_token'])) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// If the authentication dance returned an error, catch it to avoid a
|
||||
// redirect loop. This usually indicates some sort of application issue,
|
||||
// like a domain mismatch on your redirect_uri
|
||||
if (!empty($_GET['error'])) {
|
||||
echo 'Error during user authentication: ';
|
||||
echo htmlentities($_GET['error_description']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// If we don't have a code from being redirected back here,
|
||||
// send the user to WePay to grant permissions.
|
||||
if (empty($_GET['code'])) {
|
||||
$uri = WePay::getAuthorizationUri($scope, $redirect_uri);
|
||||
header("Location: $uri");
|
||||
}
|
||||
else {
|
||||
$info = WePay::getToken($_GET['code'], $redirect_uri);
|
||||
if ($info) {
|
||||
// Normally you'd integrate this into your existing auth system
|
||||
$_SESSION['wepay_access_token'] = $info->access_token;
|
||||
// If desired, you can also store $info->user_id somewhere
|
||||
header('Location: index.php');
|
||||
}
|
||||
else {
|
||||
// Unable to obtain access token
|
||||
echo 'Unable to obtain access token from WePay.';
|
||||
}
|
||||
}
|
6
externals/wepay/demoapp/logout.php
vendored
6
externals/wepay/demoapp/logout.php
vendored
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
session_start();
|
||||
$_SESSION = array();
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
exit;
|
50
externals/wepay/demoapp/openaccount.php
vendored
50
externals/wepay/demoapp/openaccount.php
vendored
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
require './_shared.php';
|
||||
?>
|
||||
<h1>WePay Demo App: Open Account</h1>
|
||||
<a href="index.php">Back</a>
|
||||
<br />
|
||||
|
||||
<?php
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
if (isset($_POST['account_name']) && isset($_POST['account_description'])) {
|
||||
// WePay sanitizes its own data, but displaying raw POST data on your own site is a XSS security hole.
|
||||
$name = htmlentities($_POST['account_name']);
|
||||
$desc = htmlentities($_POST['account_description']);
|
||||
try {
|
||||
$wepay = new WePay($_SESSION['wepay_access_token']);
|
||||
$account = $wepay->request('account/create', array(
|
||||
'name' => $name,
|
||||
'description' => $desc,
|
||||
));
|
||||
echo "Created account $name for '$desc'! View on WePay at <a href=\"$account->account_uri\">$account->account_uri</a>. See all of your accounts <a href=\"accountlist.php\">here</a>.";
|
||||
}
|
||||
catch (WePayException $e) {
|
||||
// Something went wrong - normally you would log
|
||||
// this and give your user a more informative message
|
||||
echo $e->getMessage();
|
||||
}
|
||||
}
|
||||
else {
|
||||
echo 'Account name and description are both required.';
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<form method="post">
|
||||
<fieldset>
|
||||
<legend>Account Info</legend>
|
||||
|
||||
<label for="account_name">Account Name:</label><br />
|
||||
<input type="text" id="account_name" name="account_name" placeholder="Ski Trip Savings"/>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<label for="account_description">Account Description: </label><br />
|
||||
<textarea name="account_description" rows="10" cols="40" placeholder="Saving up some dough for our ski trip!"></textarea>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<input type="submit" value="Open account" />
|
||||
</fieldset>
|
||||
</form>
|
22
externals/wepay/demoapp/user.php
vendored
22
externals/wepay/demoapp/user.php
vendored
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
require './_shared.php';
|
||||
?>
|
||||
<h1>WePay Demo App: User Info</h1>
|
||||
<a href="index.php">Back</a>
|
||||
<br />
|
||||
|
||||
<?php
|
||||
try {
|
||||
$wepay = new WePay($_SESSION['wepay_access_token']);
|
||||
$user = $wepay->request('user');
|
||||
echo '<dl>';
|
||||
foreach ($user as $key => $value) {
|
||||
echo "<dt>$key</dt><dd>$value</dd>";
|
||||
}
|
||||
echo '</dl>';
|
||||
}
|
||||
catch (WePayException $e) {
|
||||
// Something went wrong - normally you would log
|
||||
// this and give your user a more informative message
|
||||
echo $e->getMessage();
|
||||
}
|
69
externals/wepay/iframe_demoapp/checkout.php
vendored
69
externals/wepay/iframe_demoapp/checkout.php
vendored
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This PHP script helps you do the iframe checkout
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Put your API credentials here:
|
||||
* Get these from your API app details screen
|
||||
* https://stage.wepay.com/app
|
||||
*/
|
||||
$client_id = "PUT YOUR CLIENT_ID HERE";
|
||||
$client_secret = "PUT YOUR CLIENT_SECRET HERE";
|
||||
$access_token = "PUT YOUR ACCESS TOKEN HERE";
|
||||
$account_id = "PUT YOUR ACCOUNT_ID HERE"; // you can find your account ID via list_accounts.php which users the /account/find call
|
||||
|
||||
/**
|
||||
* Initialize the WePay SDK object
|
||||
*/
|
||||
require '../wepay.php';
|
||||
Wepay::useStaging($client_id, $client_secret);
|
||||
$wepay = new WePay($access_token);
|
||||
|
||||
/**
|
||||
* Make the API request to get the checkout_uri
|
||||
*
|
||||
*/
|
||||
try {
|
||||
$checkout = $wepay->request('/checkout/create', array(
|
||||
'account_id' => $account_id, // ID of the account that you want the money to go to
|
||||
'amount' => 100, // dollar amount you want to charge the user
|
||||
'short_description' => "this is a test payment", // a short description of what the payment is for
|
||||
'type' => "GOODS", // the type of the payment - choose from GOODS SERVICE DONATION or PERSONAL
|
||||
'mode' => "iframe", // put iframe here if you want the checkout to be in an iframe, regular if you want the user to be sent to WePay
|
||||
)
|
||||
);
|
||||
} catch (WePayException $e) { // if the API call returns an error, get the error message for display later
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>Checkout:</h1>
|
||||
|
||||
<p>The user will checkout here:</p>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<h2 style="color:red">ERROR: <?php echo $error ?></h2>
|
||||
<?php else: ?>
|
||||
<div id="checkout_div"></div>
|
||||
|
||||
<script type="text/javascript" src="https://stage.wepay.com/js/iframe.wepay.js">
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
WePay.iframe_checkout("checkout_div", "<?php echo $checkout->checkout_uri ?>");
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
74
externals/wepay/iframe_demoapp/list_accounts.php
vendored
74
externals/wepay/iframe_demoapp/list_accounts.php
vendored
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This PHP script helps you find your account_id
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Put your API credentials here:
|
||||
* Get these from your API app details screen
|
||||
* https://stage.wepay.com/app
|
||||
*/
|
||||
$client_id = "PUT YOUR CLIENT_ID HERE";
|
||||
$client_secret = "PUT YOUR CLIENT_SECRET HERE";
|
||||
$access_token = "PUT YOUR ACCESS TOKEN HERE";
|
||||
|
||||
/**
|
||||
* Initialize the WePay SDK object
|
||||
*/
|
||||
require '../wepay.php';
|
||||
Wepay::useStaging($client_id, $client_secret);
|
||||
$wepay = new WePay($access_token);
|
||||
|
||||
/**
|
||||
* Make the API request to get a list of all accounts this user owns
|
||||
*
|
||||
*/
|
||||
try {
|
||||
$accounts = $wepay->request('/account/find');
|
||||
} catch (WePayException $e) { // if the API call returns an error, get the error message for display later
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>List all accounts:</h1>
|
||||
|
||||
<p>The following is a list of all accounts that this user owns</p>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<h2 style="color:red">ERROR: <?php echo $error ?></h2>
|
||||
<?php elseif (empty($accounts)) : ?>
|
||||
<h2>You do not have any accounts. Go to <a href="https://stage.wepay.com.com">https://stage.wepay.com</a> to open an account.<h2>
|
||||
<?php else: ?>
|
||||
<table border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Account ID</td>
|
||||
<td>Account Name</td>
|
||||
<td>Account Description</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($accounts as $a): ?>
|
||||
<tr>
|
||||
<td><?php echo $a->account_id ?></td>
|
||||
<td><?php echo $a->name ?></td>
|
||||
<td><?php echo $a->description ?></td>
|
||||
</tr>
|
||||
<?php endforeach;?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
291
externals/wepay/wepay.php
vendored
291
externals/wepay/wepay.php
vendored
|
@ -1,291 +0,0 @@
|
|||
<?php
|
||||
|
||||
class WePay {
|
||||
|
||||
/**
|
||||
* Version number - sent in user agent string
|
||||
*/
|
||||
const VERSION = '0.1.4';
|
||||
|
||||
/**
|
||||
* Scope fields
|
||||
* Passed into Wepay::getAuthorizationUri as array
|
||||
*/
|
||||
const SCOPE_MANAGE_ACCOUNTS = 'manage_accounts'; // Open and interact with accounts
|
||||
const SCOPE_VIEW_BALANCE = 'view_balance'; // View account balances
|
||||
const SCOPE_COLLECT_PAYMENTS = 'collect_payments'; // Create and interact with checkouts
|
||||
const SCOPE_VIEW_USER = 'view_user'; // Get details about authenticated user
|
||||
const SCOPE_PREAPPROVE_PAYMENTS = 'preapprove_payments'; // Create and interact with preapprovals
|
||||
const SCOPE_SEND_MONEY = 'send_money'; // For withdrawals
|
||||
|
||||
/**
|
||||
* Application's client ID
|
||||
*/
|
||||
private static $client_id;
|
||||
|
||||
/**
|
||||
* Application's client secret
|
||||
*/
|
||||
private static $client_secret;
|
||||
|
||||
/**
|
||||
* @deprecated Use WePay::getAllScopes() instead.
|
||||
*/
|
||||
public static $all_scopes = array(
|
||||
self::SCOPE_MANAGE_ACCOUNTS,
|
||||
self::SCOPE_VIEW_BALANCE,
|
||||
self::SCOPE_COLLECT_PAYMENTS,
|
||||
self::SCOPE_PREAPPROVE_PAYMENTS,
|
||||
self::SCOPE_VIEW_USER,
|
||||
self::SCOPE_SEND_MONEY,
|
||||
);
|
||||
|
||||
/**
|
||||
* Determines whether to use WePay's staging or production servers
|
||||
*/
|
||||
private static $production = null;
|
||||
|
||||
/**
|
||||
* cURL handle
|
||||
*/
|
||||
private static $ch = NULL;
|
||||
|
||||
/**
|
||||
* Authenticated user's access token
|
||||
*/
|
||||
private $token;
|
||||
|
||||
/**
|
||||
* Pass WePay::getAllScopes() into getAuthorizationUri if your application desires full access
|
||||
*/
|
||||
public static function getAllScopes() {
|
||||
return array(
|
||||
self::SCOPE_MANAGE_ACCOUNTS,
|
||||
self::SCOPE_VIEW_BALANCE,
|
||||
self::SCOPE_COLLECT_PAYMENTS,
|
||||
self::SCOPE_PREAPPROVE_PAYMENTS,
|
||||
self::SCOPE_VIEW_USER,
|
||||
self::SCOPE_SEND_MONEY,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate URI used during oAuth authorization
|
||||
* Redirect your user to this URI where they can grant your application
|
||||
* permission to make API calls
|
||||
* @link https://www.wepay.com/developer/reference/oauth2
|
||||
* @param array $scope List of scope fields for which your application wants access
|
||||
* @param string $redirect_uri Where user goes after logging in at WePay (domain must match application settings)
|
||||
* @param array $options optional user_name,user_email which will be pre-filled on login form, state to be returned in querystring of redirect_uri
|
||||
* @return string URI to which you must redirect your user to grant access to your application
|
||||
*/
|
||||
public static function getAuthorizationUri(array $scope, $redirect_uri, array $options = array()) {
|
||||
// This does not use WePay::getDomain() because the user authentication
|
||||
// domain is different than the API call domain
|
||||
if (self::$production === null) {
|
||||
throw new RuntimeException('You must initialize the WePay SDK with WePay::useStaging() or WePay::useProduction()');
|
||||
}
|
||||
$domain = self::$production ? 'https://www.wepay.com' : 'https://stage.wepay.com';
|
||||
$uri = $domain . '/v2/oauth2/authorize?';
|
||||
$uri .= http_build_query(array(
|
||||
'client_id' => self::$client_id,
|
||||
'redirect_uri' => $redirect_uri,
|
||||
'scope' => implode(',', $scope),
|
||||
'state' => empty($options['state']) ? '' : $options['state'],
|
||||
'user_name' => empty($options['user_name']) ? '' : $options['user_name'],
|
||||
'user_email' => empty($options['user_email']) ? '' : $options['user_email'],
|
||||
), '', '&');
|
||||
return $uri;
|
||||
}
|
||||
|
||||
private static function getDomain() {
|
||||
if (self::$production === true) {
|
||||
return 'https://wepayapi.com/v2/';
|
||||
}
|
||||
elseif (self::$production === false) {
|
||||
return 'https://stage.wepayapi.com/v2/';
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException('You must initialize the WePay SDK with WePay::useStaging() or WePay::useProduction()');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange a temporary access code for a (semi-)permanent access token
|
||||
* @param string $code 'code' field from query string passed to your redirect_uri page
|
||||
* @param string $redirect_uri Where user went after logging in at WePay (must match value from getAuthorizationUri)
|
||||
* @return StdClass|false
|
||||
* user_id
|
||||
* access_token
|
||||
* token_type
|
||||
*/
|
||||
public static function getToken($code, $redirect_uri) {
|
||||
$params = (array(
|
||||
'client_id' => self::$client_id,
|
||||
'client_secret' => self::$client_secret,
|
||||
'redirect_uri' => $redirect_uri,
|
||||
'code' => $code,
|
||||
'state' => '', // do not hardcode
|
||||
));
|
||||
$result = self::make_request('oauth2/token', $params);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure SDK to run against WePay's production servers
|
||||
* @param string $client_id Your application's client id
|
||||
* @param string $client_secret Your application's client secret
|
||||
* @return void
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function useProduction($client_id, $client_secret) {
|
||||
if (self::$production !== null) {
|
||||
throw new RuntimeException('API mode has already been set.');
|
||||
}
|
||||
self::$production = true;
|
||||
self::$client_id = $client_id;
|
||||
self::$client_secret = $client_secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure SDK to run against WePay's staging servers
|
||||
* @param string $client_id Your application's client id
|
||||
* @param string $client_secret Your application's client secret
|
||||
* @return void
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function useStaging($client_id, $client_secret) {
|
||||
if (self::$production !== null) {
|
||||
throw new RuntimeException('API mode has already been set.');
|
||||
}
|
||||
self::$production = false;
|
||||
self::$client_id = $client_id;
|
||||
self::$client_secret = $client_secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new API session
|
||||
* @param string $token - access_token returned from WePay::getToken
|
||||
*/
|
||||
public function __construct($token) {
|
||||
if ($token && !is_string($token)) {
|
||||
throw new InvalidArgumentException('$token must be a string, ' . gettype($token) . ' provided');
|
||||
}
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up cURL handle
|
||||
*/
|
||||
public function __destruct() {
|
||||
if (self::$ch) {
|
||||
curl_close(self::$ch);
|
||||
self::$ch = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create the cURL request and execute it
|
||||
*/
|
||||
private static function make_request($endpoint, $values, $headers = array())
|
||||
{
|
||||
self::$ch = curl_init();
|
||||
$headers = array_merge(array("Content-Type: application/json"), $headers); // always pass the correct Content-Type header
|
||||
curl_setopt(self::$ch, CURLOPT_USERAGENT, 'WePay v2 PHP SDK v' . self::VERSION);
|
||||
curl_setopt(self::$ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt(self::$ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt(self::$ch, CURLOPT_TIMEOUT, 30); // 30-second timeout, adjust to taste
|
||||
curl_setopt(self::$ch, CURLOPT_POST, !empty($values)); // WePay's API is not strictly RESTful, so all requests are sent as POST unless there are no request values
|
||||
|
||||
$uri = self::getDomain() . $endpoint;
|
||||
curl_setopt(self::$ch, CURLOPT_URL, $uri);
|
||||
|
||||
if (!empty($values)) {
|
||||
curl_setopt(self::$ch, CURLOPT_POSTFIELDS, json_encode($values));
|
||||
}
|
||||
|
||||
$raw = curl_exec(self::$ch);
|
||||
if ($errno = curl_errno(self::$ch)) {
|
||||
// Set up special handling for request timeouts
|
||||
if ($errno == CURLE_OPERATION_TIMEOUTED) {
|
||||
throw new WePayServerException("Timeout occurred while trying to connect to WePay");
|
||||
}
|
||||
throw new Exception('cURL error while making API call to WePay: ' . curl_error(self::$ch), $errno);
|
||||
}
|
||||
$result = json_decode($raw);
|
||||
$httpCode = curl_getinfo(self::$ch, CURLINFO_HTTP_CODE);
|
||||
if ($httpCode >= 400) {
|
||||
if (!isset($result->error_code)) {
|
||||
throw new WePayServerException("WePay returned an error response with no error_code, please alert api@wepay.com. Original message: $result->error_description", $httpCode, $result, 0);
|
||||
}
|
||||
if ($httpCode >= 500) {
|
||||
throw new WePayServerException($result->error_description, $httpCode, $result, $result->error_code);
|
||||
}
|
||||
switch ($result->error) {
|
||||
case 'invalid_request':
|
||||
throw new WePayRequestException($result->error_description, $httpCode, $result, $result->error_code);
|
||||
case 'access_denied':
|
||||
default:
|
||||
throw new WePayPermissionException($result->error_description, $httpCode, $result, $result->error_code);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make API calls against authenticated user
|
||||
* @param string $endpoint - API call to make (ex. 'user', 'account/find')
|
||||
* @param array $values - Associative array of values to send in API call
|
||||
* @return StdClass
|
||||
* @throws WePayException on failure
|
||||
* @throws Exception on catastrophic failure (non-WePay-specific cURL errors)
|
||||
*/
|
||||
public function request($endpoint, array $values = array()) {
|
||||
$headers = array();
|
||||
|
||||
if ($this->token) { // if we have an access_token, add it to the Authorization header
|
||||
$headers[] = "Authorization: Bearer $this->token";
|
||||
}
|
||||
|
||||
$result = self::make_request($endpoint, $values, $headers);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Different problems will have different exception types so you can
|
||||
* catch and handle them differently.
|
||||
*
|
||||
* WePayServerException indicates some sort of 500-level error code and
|
||||
* was unavoidable from your perspective. You may need to re-run the
|
||||
* call, or check whether it was received (use a "find" call with your
|
||||
* reference_id and make a decision based on the response)
|
||||
*
|
||||
* WePayRequestException indicates a development error - invalid endpoint,
|
||||
* erroneous parameter, etc.
|
||||
*
|
||||
* WePayPermissionException indicates your authorization token has expired,
|
||||
* was revoked, or is lacking in scope for the call you made
|
||||
*/
|
||||
class WePayException extends Exception {
|
||||
public function __construct($description = '', $http_code = FALSE, $response = FALSE, $code = 0, $previous = NULL)
|
||||
{
|
||||
$this->response = $response;
|
||||
|
||||
if (!defined('PHP_VERSION_ID')) {
|
||||
$version = explode('.', PHP_VERSION);
|
||||
define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 50300) {
|
||||
parent::__construct($description, $code);
|
||||
} else {
|
||||
parent::__construct($description, $code, $previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
class WePayRequestException extends WePayException {}
|
||||
class WePayPermissionException extends WePayException {}
|
||||
class WePayServerException extends WePayException {}
|
|
@ -5248,7 +5248,6 @@ phutil_register_library_map(array(
|
|||
'PhortuneSubscriptionViewController' => 'applications/phortune/controller/subscription/PhortuneSubscriptionViewController.php',
|
||||
'PhortuneSubscriptionWorker' => 'applications/phortune/worker/PhortuneSubscriptionWorker.php',
|
||||
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
|
||||
'PhortuneWePayPaymentProvider' => 'applications/phortune/provider/PhortuneWePayPaymentProvider.php',
|
||||
'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php',
|
||||
'PhragmentCanCreateCapability' => 'applications/phragment/capability/PhragmentCanCreateCapability.php',
|
||||
'PhragmentConduitAPIMethod' => 'applications/phragment/conduit/PhragmentConduitAPIMethod.php',
|
||||
|
@ -11697,7 +11696,6 @@ phutil_register_library_map(array(
|
|||
'PhortuneSubscriptionViewController' => 'PhortuneController',
|
||||
'PhortuneSubscriptionWorker' => 'PhabricatorWorker',
|
||||
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhragmentBrowseController' => 'PhragmentController',
|
||||
'PhragmentCanCreateCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhragmentConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
|
|
|
@ -1,405 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider {
|
||||
|
||||
const WEPAY_CLIENT_ID = 'wepay.client-id';
|
||||
const WEPAY_CLIENT_SECRET = 'wepay.client-secret';
|
||||
const WEPAY_ACCESS_TOKEN = 'wepay.access-token';
|
||||
const WEPAY_ACCOUNT_ID = 'wepay.account-id';
|
||||
|
||||
public function isAcceptingLivePayments() {
|
||||
return preg_match('/^PRODUCTION_/', $this->getWePayAccessToken());
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return pht('WePay');
|
||||
}
|
||||
|
||||
public function getConfigureName() {
|
||||
return pht('Add WePay Payments Account');
|
||||
}
|
||||
|
||||
public function getConfigureDescription() {
|
||||
return pht(
|
||||
'Allows you to accept credit or debit card payments with a '.
|
||||
'wepay.com account.');
|
||||
}
|
||||
|
||||
public function getConfigureProvidesDescription() {
|
||||
return pht('This merchant accepts credit and debit cards via WePay.');
|
||||
}
|
||||
|
||||
public function getConfigureInstructions() {
|
||||
return pht(
|
||||
"To configure WePay, register or log in to an existing account on ".
|
||||
"[[https://wepay.com | wepay.com]] (for live payments) or ".
|
||||
"[[https://stage.wepay.com | stage.wepay.com]] (for testing). ".
|
||||
"Once logged in:\n\n".
|
||||
" - Create an API application if you don't already have one.\n".
|
||||
" - Click the API application name to go to the detail page.\n".
|
||||
" - Copy **Client ID**, **Client Secret**, **Access Token** and ".
|
||||
" **AccountID** from that page to the fields above.\n\n".
|
||||
"You can either use `stage.wepay.com` to retrieve test credentials, ".
|
||||
"or `wepay.com` to retrieve live credentials for accepting live ".
|
||||
"payments.");
|
||||
}
|
||||
|
||||
public function canRunConfigurationTest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function runConfigurationTest() {
|
||||
$this->loadWePayAPILibraries();
|
||||
|
||||
WePay::useStaging(
|
||||
$this->getWePayClientID(),
|
||||
$this->getWePayClientSecret());
|
||||
|
||||
$wepay = new WePay($this->getWePayAccessToken());
|
||||
$params = array(
|
||||
'client_id' => $this->getWePayClientID(),
|
||||
'client_secret' => $this->getWePayClientSecret(),
|
||||
);
|
||||
|
||||
$wepay->request('app', $params);
|
||||
}
|
||||
|
||||
public function getAllConfigurableProperties() {
|
||||
return array(
|
||||
self::WEPAY_CLIENT_ID,
|
||||
self::WEPAY_CLIENT_SECRET,
|
||||
self::WEPAY_ACCESS_TOKEN,
|
||||
self::WEPAY_ACCOUNT_ID,
|
||||
);
|
||||
}
|
||||
|
||||
public function getAllConfigurableSecretProperties() {
|
||||
return array(
|
||||
self::WEPAY_CLIENT_SECRET,
|
||||
);
|
||||
}
|
||||
|
||||
public function processEditForm(
|
||||
AphrontRequest $request,
|
||||
array $values) {
|
||||
|
||||
$errors = array();
|
||||
$issues = array();
|
||||
|
||||
if (!strlen($values[self::WEPAY_CLIENT_ID])) {
|
||||
$errors[] = pht('WePay Client ID is required.');
|
||||
$issues[self::WEPAY_CLIENT_ID] = pht('Required');
|
||||
}
|
||||
|
||||
if (!strlen($values[self::WEPAY_CLIENT_SECRET])) {
|
||||
$errors[] = pht('WePay Client Secret is required.');
|
||||
$issues[self::WEPAY_CLIENT_SECRET] = pht('Required');
|
||||
}
|
||||
|
||||
if (!strlen($values[self::WEPAY_ACCESS_TOKEN])) {
|
||||
$errors[] = pht('WePay Access Token is required.');
|
||||
$issues[self::WEPAY_ACCESS_TOKEN] = pht('Required');
|
||||
}
|
||||
|
||||
if (!strlen($values[self::WEPAY_ACCOUNT_ID])) {
|
||||
$errors[] = pht('WePay Account ID is required.');
|
||||
$issues[self::WEPAY_ACCOUNT_ID] = pht('Required');
|
||||
}
|
||||
|
||||
return array($errors, $issues, $values);
|
||||
}
|
||||
|
||||
public function extendEditForm(
|
||||
AphrontRequest $request,
|
||||
AphrontFormView $form,
|
||||
array $values,
|
||||
array $issues) {
|
||||
|
||||
$form
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName(self::WEPAY_CLIENT_ID)
|
||||
->setValue($values[self::WEPAY_CLIENT_ID])
|
||||
->setError(idx($issues, self::WEPAY_CLIENT_ID, true))
|
||||
->setLabel(pht('WePay Client ID')))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName(self::WEPAY_CLIENT_SECRET)
|
||||
->setValue($values[self::WEPAY_CLIENT_SECRET])
|
||||
->setError(idx($issues, self::WEPAY_CLIENT_SECRET, true))
|
||||
->setLabel(pht('WePay Client Secret')))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName(self::WEPAY_ACCESS_TOKEN)
|
||||
->setValue($values[self::WEPAY_ACCESS_TOKEN])
|
||||
->setError(idx($issues, self::WEPAY_ACCESS_TOKEN, true))
|
||||
->setLabel(pht('WePay Access Token')))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName(self::WEPAY_ACCOUNT_ID)
|
||||
->setValue($values[self::WEPAY_ACCOUNT_ID])
|
||||
->setError(idx($issues, self::WEPAY_ACCOUNT_ID, true))
|
||||
->setLabel(pht('WePay Account ID')));
|
||||
|
||||
}
|
||||
|
||||
public function getPaymentMethodDescription() {
|
||||
return pht('Credit or Debit Card');
|
||||
}
|
||||
|
||||
public function getPaymentMethodIcon() {
|
||||
return 'WePay';
|
||||
}
|
||||
|
||||
public function getPaymentMethodProviderDescription() {
|
||||
return 'WePay';
|
||||
}
|
||||
|
||||
protected function executeCharge(
|
||||
PhortunePaymentMethod $payment_method,
|
||||
PhortuneCharge $charge) {
|
||||
throw new Exception('!');
|
||||
}
|
||||
|
||||
private function getWePayClientID() {
|
||||
return $this
|
||||
->getProviderConfig()
|
||||
->getMetadataValue(self::WEPAY_CLIENT_ID);
|
||||
}
|
||||
|
||||
private function getWePayClientSecret() {
|
||||
return $this
|
||||
->getProviderConfig()
|
||||
->getMetadataValue(self::WEPAY_CLIENT_SECRET);
|
||||
}
|
||||
|
||||
private function getWePayAccessToken() {
|
||||
return $this
|
||||
->getProviderConfig()
|
||||
->getMetadataValue(self::WEPAY_ACCESS_TOKEN);
|
||||
}
|
||||
|
||||
private function getWePayAccountID() {
|
||||
return $this
|
||||
->getProviderConfig()
|
||||
->getMetadataValue(self::WEPAY_ACCOUNT_ID);
|
||||
}
|
||||
|
||||
protected function executeRefund(
|
||||
PhortuneCharge $charge,
|
||||
PhortuneCharge $refund) {
|
||||
$wepay = $this->loadWePayAPILibraries();
|
||||
|
||||
$checkout_id = $this->getWePayCheckoutID($charge);
|
||||
|
||||
$params = array(
|
||||
'checkout_id' => $checkout_id,
|
||||
'refund_reason' => pht('Refund'),
|
||||
'amount' => $refund->getAmountAsCurrency()->negate()->formatBareValue(),
|
||||
);
|
||||
|
||||
$wepay->request('checkout/refund', $params);
|
||||
}
|
||||
|
||||
public function updateCharge(PhortuneCharge $charge) {
|
||||
$wepay = $this->loadWePayAPILibraries();
|
||||
|
||||
$params = array(
|
||||
'checkout_id' => $this->getWePayCheckoutID($charge),
|
||||
);
|
||||
$wepay_checkout = $wepay->request('checkout', $params);
|
||||
|
||||
// TODO: Deal with disputes / chargebacks / surprising refunds.
|
||||
}
|
||||
|
||||
|
||||
/* -( One-Time Payments )-------------------------------------------------- */
|
||||
|
||||
public function canProcessOneTimePayments() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* -( Controllers )-------------------------------------------------------- */
|
||||
|
||||
|
||||
public function canRespondToControllerAction($action) {
|
||||
switch ($action) {
|
||||
case 'checkout':
|
||||
case 'charge':
|
||||
case 'cancel':
|
||||
return true;
|
||||
}
|
||||
return parent::canRespondToControllerAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class WePay
|
||||
*/
|
||||
public function processControllerRequest(
|
||||
PhortuneProviderActionController $controller,
|
||||
AphrontRequest $request) {
|
||||
$wepay = $this->loadWePayAPILibraries();
|
||||
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$cart = $controller->loadCart($request->getInt('cartID'));
|
||||
if (!$cart) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$charge = $controller->loadActiveCharge($cart);
|
||||
switch ($controller->getAction()) {
|
||||
case 'checkout':
|
||||
if ($charge) {
|
||||
throw new Exception(pht('Cart is already charging!'));
|
||||
}
|
||||
break;
|
||||
case 'charge':
|
||||
case 'cancel':
|
||||
if (!$charge) {
|
||||
throw new Exception(pht('Cart is not charging yet!'));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($controller->getAction()) {
|
||||
case 'checkout':
|
||||
$return_uri = $this->getControllerURI(
|
||||
'charge',
|
||||
array(
|
||||
'cartID' => $cart->getID(),
|
||||
));
|
||||
|
||||
$cancel_uri = $this->getControllerURI(
|
||||
'cancel',
|
||||
array(
|
||||
'cartID' => $cart->getID(),
|
||||
));
|
||||
|
||||
$price = $cart->getTotalPriceAsCurrency();
|
||||
|
||||
$params = array(
|
||||
'account_id' => $this->getWePayAccountID(),
|
||||
'short_description' => $cart->getName(),
|
||||
'type' => 'SERVICE',
|
||||
'amount' => $price->formatBareValue(),
|
||||
'long_description' => $cart->getName(),
|
||||
'reference_id' => $cart->getPHID(),
|
||||
'app_fee' => 0,
|
||||
'fee_payer' => 'Payee',
|
||||
'redirect_uri' => $return_uri,
|
||||
'fallback_uri' => $cancel_uri,
|
||||
|
||||
// NOTE: If we don't `auto_capture`, we might get a result back in
|
||||
// either an "authorized" or a "reserved" state. We can't capture
|
||||
// an "authorized" result, so just autocapture.
|
||||
|
||||
'auto_capture' => true,
|
||||
'require_shipping' => 0,
|
||||
'shipping_fee' => 0,
|
||||
'charge_tax' => 0,
|
||||
'mode' => 'regular',
|
||||
|
||||
// TODO: We could accept bank accounts but the hold/capture rules
|
||||
// are not quite clear. Just accept credit cards for now.
|
||||
'funding_sources' => 'cc',
|
||||
);
|
||||
|
||||
$charge = $cart->willApplyCharge($viewer, $this);
|
||||
$result = $wepay->request('checkout/create', $params);
|
||||
|
||||
$cart->setMetadataValue('provider.checkoutURI', $result->checkout_uri);
|
||||
$cart->save();
|
||||
|
||||
$charge->setMetadataValue('wepay.checkoutID', $result->checkout_id);
|
||||
$charge->save();
|
||||
|
||||
$uri = new PhutilURI($result->checkout_uri);
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setIsExternal(true)
|
||||
->setURI($uri);
|
||||
case 'charge':
|
||||
if ($cart->getStatus() !== PhortuneCart::STATUS_PURCHASING) {
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($cart->getCheckoutURI());
|
||||
}
|
||||
|
||||
$checkout_id = $request->getInt('checkout_id');
|
||||
$params = array(
|
||||
'checkout_id' => $checkout_id,
|
||||
);
|
||||
|
||||
$checkout = $wepay->request('checkout', $params);
|
||||
if ($checkout->reference_id != $cart->getPHID()) {
|
||||
throw new Exception(
|
||||
pht('Checkout reference ID does not match cart PHID!'));
|
||||
}
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
switch ($checkout->state) {
|
||||
case 'authorized':
|
||||
case 'reserved':
|
||||
case 'captured':
|
||||
// TODO: Are these all really "done" states, and not "hold"
|
||||
// states? Cards and bank accounts both come back as "authorized"
|
||||
// on the staging environment. Figure out what happens in
|
||||
// production?
|
||||
|
||||
$cart->didApplyCharge($charge);
|
||||
|
||||
$response = id(new AphrontRedirectResponse())->setURI(
|
||||
$cart->getCheckoutURI());
|
||||
break;
|
||||
default:
|
||||
// It's not clear if we can ever get here on the web workflow,
|
||||
// WePay doesn't seem to return back to us after a failure (the
|
||||
// workflow dead-ends instead).
|
||||
|
||||
$cart->didFailCharge($charge);
|
||||
|
||||
$response = $controller
|
||||
->newDialog()
|
||||
->setTitle(pht('Charge Failed'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Unable to make payment (checkout state is "%s").',
|
||||
$checkout->state))
|
||||
->addCancelButton($cart->getCheckoutURI(), pht('Continue'));
|
||||
break;
|
||||
}
|
||||
unset($unguarded);
|
||||
|
||||
return $response;
|
||||
case 'cancel':
|
||||
// TODO: I don't know how it's possible to cancel out of a WePay
|
||||
// charge workflow.
|
||||
throw new Exception(
|
||||
pht('How did you get here? WePay has no cancel flow in its UI...?'));
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Exception(
|
||||
pht('Unsupported action "%s".', $controller->getAction()));
|
||||
}
|
||||
|
||||
private function loadWePayAPILibraries() {
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
require_once $root.'/externals/wepay/wepay.php';
|
||||
|
||||
WePay::useStaging(
|
||||
$this->getWePayClientID(),
|
||||
$this->getWePayClientSecret());
|
||||
|
||||
return new WePay($this->getWePayAccessToken());
|
||||
}
|
||||
|
||||
private function getWePayCheckoutID(PhortuneCharge $charge) {
|
||||
$checkout_id = $charge->getMetadataValue('wepay.checkoutID');
|
||||
if ($checkout_id === null) {
|
||||
throw new Exception(pht('No WePay Checkout ID present on charge!'));
|
||||
}
|
||||
return $checkout_id;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue