Add Balanced Payments API
Summary: Adds the Balanced PHP API to externals/. Ref T2787. Test Plan: Used in next diff. Reviewers: btrahan, chad Reviewed By: chad CC: aran, aurelijus Maniphest Tasks: T2787 Differential Revision: https://secure.phabricator.com/D5764
This commit is contained in:
12
externals/restful/.gitignore
vendored
Normal file
12
externals/restful/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# composer
|
||||
.buildpath
|
||||
composer.lock
|
||||
composer.phar
|
||||
vendor
|
||||
|
||||
# phar
|
||||
*.phar
|
||||
|
||||
# eclipse-pdt
|
||||
.settings
|
||||
.project
|
||||
8
externals/restful/.travis.yml
vendored
Normal file
8
externals/restful/.travis.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
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
Normal file
22
externals/restful/LICENSE
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
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
Normal file
111
externals/restful/README.md
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
# RESTful
|
||||
|
||||
Library for writing RESTful PHP clients.
|
||||
|
||||
[](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
Normal file
4
externals/restful/bootstrap.php
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
require(__DIR__ . '/src/RESTful/Bootstrap.php');
|
||||
\RESTful\Bootstrap::init();
|
||||
36
externals/restful/build-phar
vendored
Executable file
36
externals/restful/build-phar
vendored
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/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
Normal file
18
externals/restful/composer.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"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
Normal file
44
externals/restful/src/RESTful/Bootstrap.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?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
Normal file
78
externals/restful/src/RESTful/Client.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?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
Normal file
49
externals/restful/src/RESTful/Collection.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
10
externals/restful/src/RESTful/Exceptions/Base.php
vendored
Normal file
10
externals/restful/src/RESTful/Exceptions/Base.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Base class for all RESTful\Exceptions.
|
||||
*/
|
||||
class Base extends \Exception
|
||||
{
|
||||
}
|
||||
28
externals/restful/src/RESTful/Exceptions/HTTPError.php
vendored
Normal file
28
externals/restful/src/RESTful/Exceptions/HTTPError.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
externals/restful/src/RESTful/Exceptions/MultipleResultsFound.php
vendored
Normal file
11
externals/restful/src/RESTful/Exceptions/MultipleResultsFound.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Indicates that a query unexpectedly returned multiple results when at most
|
||||
* one was expected.
|
||||
*/
|
||||
class MultipleResultsFound extends Base
|
||||
{
|
||||
}
|
||||
10
externals/restful/src/RESTful/Exceptions/NoResultFound.php
vendored
Normal file
10
externals/restful/src/RESTful/Exceptions/NoResultFound.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Indicates that a query unexpectedly returned no results.
|
||||
*/
|
||||
class NoResultFound extends Base
|
||||
{
|
||||
}
|
||||
85
externals/restful/src/RESTful/Field.php
vendored
Normal file
85
externals/restful/src/RESTful/Field.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?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
Normal file
11
externals/restful/src/RESTful/Fields.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class Fields
|
||||
{
|
||||
public function __get($name)
|
||||
{
|
||||
return new Field($name);
|
||||
}
|
||||
}
|
||||
31
externals/restful/src/RESTful/FilterExpression.php
vendored
Normal file
31
externals/restful/src/RESTful/FilterExpression.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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
Normal file
99
externals/restful/src/RESTful/Itemization.php
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
45
externals/restful/src/RESTful/ItemizationIterator.php
vendored
Normal file
45
externals/restful/src/RESTful/ItemizationIterator.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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
Normal file
72
externals/restful/src/RESTful/Page.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?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
Normal file
90
externals/restful/src/RESTful/Pagination.php
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
37
externals/restful/src/RESTful/PaginationIterator.php
vendored
Normal file
37
externals/restful/src/RESTful/PaginationIterator.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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
Normal file
161
externals/restful/src/RESTful/Query.php
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
<?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
Normal file
29
externals/restful/src/RESTful/Registry.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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
Normal file
205
externals/restful/src/RESTful/Resource.php
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
<?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
Normal file
12
externals/restful/src/RESTful/Settings.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
/**
|
||||
* Settings.
|
||||
*
|
||||
*/
|
||||
class Settings
|
||||
{
|
||||
const VERSION = '0.1.7';
|
||||
}
|
||||
15
externals/restful/src/RESTful/SortExpression.php
vendored
Normal file
15
externals/restful/src/RESTful/SortExpression.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?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
Normal file
58
externals/restful/src/RESTful/URISpec.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?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
Normal file
241
externals/restful/tests/RESTful/CoreTest.php
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
<?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
Normal file
8
externals/restful/tests/phpunit.xml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<phpunit>
|
||||
<testsuite name="RESTful">
|
||||
<directory>.</directory>
|
||||
</testsuite>
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
Reference in New Issue
Block a user