updated balanced library
This commit is contained in:
parent
903f1eed06
commit
8bab676488
@ -27,7 +27,7 @@ $GLOBALS['config'] = [
|
||||
'root' => dirname(__FILE__).'/../',
|
||||
'www' => dirname(__FILE__).'/../www/',
|
||||
'storage' => dirname(__FILE__).'/../storage/',
|
||||
],'libraries' => ['Crunchbutton','Cana','Services','Balanced','Ordrin','QueryPath','Github'],
|
||||
],'libraries' => ['Crunchbutton','Cana','Services','Balanced','RESTful','Ordrin','QueryPath','Github'],
|
||||
'alias' => []
|
||||
];
|
||||
|
||||
@ -91,6 +91,7 @@ spl_autoload_register(function ($className) {
|
||||
|
||||
|
||||
\Httpful\Bootstrap::init();
|
||||
\RESTful\Bootstrap::init();
|
||||
\Balanced\Bootstrap::init();
|
||||
\Ordrin\Bootstrap::init();
|
||||
\QueryPath\Bootstrap::init();
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Core\Resource;
|
||||
use Balanced\Core\URISpec;
|
||||
use Balanced\Resource;
|
||||
use Balanced\Settings;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/**
|
||||
* Represents an api key. These are used to autheticate you with the api.
|
||||
/**
|
||||
* Represents an api key. These are used to authenticate you with the api.
|
||||
*
|
||||
* Typically you create an initial api key:
|
||||
*
|
||||
@ -41,15 +41,15 @@ use Balanced\Settings;
|
||||
* ->sort(\Balanced\APIKey::f->created_at->desc())
|
||||
* ->first()
|
||||
* ->delete();
|
||||
* </code>
|
||||
*/
|
||||
class APIKey extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('api_keys', 'id', '/v1');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
}
|
||||
* </code>
|
||||
*/
|
||||
class APIKey extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('api_keys', 'id', '/v1');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Core\Resource;
|
||||
use Balanced\Core\URISpec;
|
||||
use Balanced\Resource;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/**
|
||||
* Represent a buyer or merchant account on a marketplace.
|
||||
@ -94,25 +94,43 @@ class Account extends Resource
|
||||
$appears_on_statement_as = null,
|
||||
$description = null,
|
||||
$meta = null,
|
||||
$source = null)
|
||||
$source = null,
|
||||
$on_behalf_of = null)
|
||||
{
|
||||
if ($source == null)
|
||||
if ($source == null) {
|
||||
$source_uri = null;
|
||||
else
|
||||
$source_uri = is_string($source) ? $source : $source->uri;
|
||||
} else if (is_string($source)) {
|
||||
$source_uri = $source;
|
||||
} else {
|
||||
$source_uri = $source->uri;
|
||||
}
|
||||
|
||||
if ($on_behalf_of == null) {
|
||||
$on_behalf_of_uri = null;
|
||||
} else if (is_string($on_behalf_of)) {
|
||||
$on_behalf_of_uri = $on_behalf_of;
|
||||
} else {
|
||||
$on_behalf_of_uri = $on_behalf_of->uri;
|
||||
}
|
||||
|
||||
if (isset($this->uri) && $on_behalf_of_uri == $this->uri)
|
||||
throw new \InvalidArgumentException(
|
||||
'The on_behalf_of parameter MAY NOT be the same account as the account you are debiting!'
|
||||
);
|
||||
|
||||
return $this->debits->create(array(
|
||||
'amount' => $amount,
|
||||
'appears_on_statement_as' => $appears_on_statement_as,
|
||||
'description' => $description,
|
||||
'meta' => $meta,
|
||||
'source_uri' => $source_uri,
|
||||
'on_behalf_of_uri' => $on_behalf_of_uri,
|
||||
'appears_on_statement_as' => $appears_on_statement_as
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hold (i.e. a guaranteed pending debit) for account funds. You
|
||||
* can later capture or void. A hold is assocaited with a account funding
|
||||
* can later capture or void. A hold is associated with a account funding
|
||||
* source (i.e. \Balanced\Card). If you don't specify the source then the
|
||||
* current primary funding source for the account is used.
|
||||
*
|
||||
@ -145,7 +163,7 @@ class Account extends Resource
|
||||
*
|
||||
* @param mixed card \Balanced\Card or URI referencing a card to associate with the account. Alternatively it can be an associative array describing a card to create and associate with the account.
|
||||
*
|
||||
* @return Balanced\Account
|
||||
* @return \Balanced\Account
|
||||
*/
|
||||
public function addCard($card)
|
||||
{
|
||||
@ -164,9 +182,9 @@ class Account extends Resource
|
||||
*
|
||||
* @see \Balanced\Marketplace->createBankAccount
|
||||
*
|
||||
* @param mixed bank_account \Balanced\BankAccount or URI for a bank account to assocaite with the account. Alternatively it can be an associative array describing a bank account to create and associate with the account.
|
||||
|
||||
* @return Balanced\Account
|
||||
* @param mixed bank_account \Balanced\BankAccount or URI for a bank account to associate with the account. Alternatively it can be an associative array describing a bank account to create and associate with the account.
|
||||
*
|
||||
* @return \Balanced\Account
|
||||
*/
|
||||
public function addBankAccount($bank_account)
|
||||
{
|
||||
@ -186,11 +204,14 @@ class Account extends Resource
|
||||
*
|
||||
* @param mixed merchant Associative array describing the merchants identity or a URI referencing a created merchant.
|
||||
*
|
||||
* @return Balanced\Account
|
||||
* @return \Balanced\Account
|
||||
*/
|
||||
public function promoteToMerchant($merchant)
|
||||
{
|
||||
$this->merchant = $merchant;
|
||||
if (is_string($merchant))
|
||||
$this->merchant_uri = $merchant;
|
||||
else
|
||||
$this->merchant = $merchant;
|
||||
return $this->save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Core\Resource;
|
||||
use Balanced\Core\URISpec;
|
||||
use Balanced\Resource;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/**
|
||||
* Represents an account bank account.
|
||||
@ -41,20 +41,87 @@ class BankAccount extends Resource
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
|
||||
public function credit(
|
||||
$amount,
|
||||
$description = null,
|
||||
$meta = null,
|
||||
$appears_on_statement_as = null)
|
||||
/**
|
||||
* Credit a bank account.
|
||||
*
|
||||
* @param int amount Amount to credit in USD pennies.
|
||||
* @param string description Optional description of the credit.
|
||||
* @param string appears_on_statement_as Optional description of the credit as it will appears on the customer's billing statement.
|
||||
*
|
||||
* @return \Balanced\Credit
|
||||
*
|
||||
* <code>
|
||||
* $bank_account = new \Balanced\BankAccount(array(
|
||||
* 'account_number' => '12341234',
|
||||
* 'name' => 'Fit Finlay',
|
||||
* 'bank_code' => '325182797',
|
||||
* 'type' => 'checking',
|
||||
* ));
|
||||
*
|
||||
* $credit = $bank_account->credit(123, 'something descriptive');
|
||||
* </code>
|
||||
*/
|
||||
public function credit(
|
||||
$amount,
|
||||
$description = null,
|
||||
$meta = null,
|
||||
$appears_on_statement_as = null)
|
||||
{
|
||||
if ($this->account == null) {
|
||||
throw new \UnexpectedValueException('Bank account is not associated with an account.');
|
||||
}
|
||||
return $this->account->credit(
|
||||
$amount,
|
||||
$description,
|
||||
$meta,
|
||||
$this->uri,
|
||||
$appears_on_statement_as);
|
||||
if (!property_exists($this, 'account') || $this->account == null) {
|
||||
$credit = $this->credits->create(array(
|
||||
'amount' => $amount,
|
||||
'description' => $description,
|
||||
));
|
||||
} else {
|
||||
$credit = $this->account->credit(
|
||||
$amount,
|
||||
$description,
|
||||
$meta,
|
||||
$this->uri,
|
||||
$appears_on_statement_as
|
||||
);
|
||||
}
|
||||
return $credit;
|
||||
}
|
||||
|
||||
public function verify()
|
||||
{
|
||||
$response = self::getClient()->post(
|
||||
$this->verifications_uri, null
|
||||
);
|
||||
$verification = new BankAccountVerification();
|
||||
$verification->_objectify($response->body);
|
||||
return $verification;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an verification for a bank account which is a pre-requisite if
|
||||
* you want to create debits using the associated bank account. The side-effect
|
||||
* of creating a verification is that 2 random amounts will be deposited into
|
||||
* the account which must then be confirmed via the confirm method to ensure
|
||||
* that you have access to the bank account in question.
|
||||
*
|
||||
* You can create these via Balanced\Marketplace::bank_accounts::verify.
|
||||
*
|
||||
* <code>
|
||||
* $marketplace = \Balanced\Marketplace::mine();
|
||||
*
|
||||
* $bank_account = $marketplace->bank_accounts->create(array(
|
||||
* 'name' => 'name',
|
||||
* 'account_number' => '11223344',
|
||||
* 'bank_code' => '1313123',
|
||||
* ));
|
||||
*
|
||||
* $verification = $bank_account->verify();
|
||||
* </code>
|
||||
*/
|
||||
class BankAccountVerification extends Resource {
|
||||
|
||||
public function confirm($amount1, $amount2) {
|
||||
$this->amount_1 = $amount1;
|
||||
$this->amount_2 = $amount2;
|
||||
$this->save();
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,72 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced;
|
||||
require_once(dirname(dirname(__FILE__)).'/Balanced/Errors.php');
|
||||
|
||||
/**
|
||||
* Bootstrapper for Balanced does autoloading and resource initialization.
|
||||
/**
|
||||
* Bootstrapper for Balanced does autoloading and resource initialization.
|
||||
*/
|
||||
class Bootstrap
|
||||
{
|
||||
const DIR_SEPARATOR = DIRECTORY_SEPARATOR;
|
||||
const NAMESPACE_SEPARATOR = '\\';
|
||||
const DIR_SEPARATOR = DIRECTORY_SEPARATOR;
|
||||
const NAMESPACE_SEPARATOR = '\\';
|
||||
|
||||
public static $initialized = false;
|
||||
|
||||
|
||||
public static function init()
|
||||
{
|
||||
spl_autoload_register(array('\Balanced\Bootstrap', 'autoload'));
|
||||
self::initializeResources();
|
||||
}
|
||||
|
||||
public static function autoload($classname)
|
||||
{
|
||||
self::_autoload(dirname(dirname(__FILE__)), $classname);
|
||||
}
|
||||
|
||||
public static function pharInit()
|
||||
{
|
||||
spl_autoload_register(array('\Balanced\Bootstrap', 'pharAutoload'));
|
||||
self::initializeResources();
|
||||
}
|
||||
|
||||
public static function pharAutoload($classname)
|
||||
{
|
||||
self::_autoload('phar://balanced.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);
|
||||
}
|
||||
}
|
||||
public static $initialized = false;
|
||||
|
||||
|
||||
public static function init()
|
||||
{
|
||||
spl_autoload_register(array('\Balanced\Bootstrap', 'autoload'));
|
||||
self::initializeResources();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes resources (i.e. registers them with Resource::_registry). Note
|
||||
* that if you add a Resource then you must initialize it here.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
private static function initializeResources()
|
||||
{
|
||||
if (self::$initialized)
|
||||
return;
|
||||
|
||||
\Balanced\Core\Resource::init();
|
||||
\Balanced\APIKey::init();
|
||||
\Balanced\Marketplace::init();
|
||||
\Balanced\Account::init();
|
||||
\Balanced\Credit::init();
|
||||
\Balanced\Debit::init();
|
||||
\Balanced\Refund::init();
|
||||
\Balanced\Card::init();
|
||||
\Balanced\BankAccount::init();
|
||||
\Balanced\Hold::init();
|
||||
\Balanced\Merchant::init();
|
||||
|
||||
self::$initialized = true;
|
||||
}
|
||||
public static function autoload($classname)
|
||||
{
|
||||
self::_autoload(dirname(dirname(__FILE__)), $classname);
|
||||
}
|
||||
|
||||
public static function pharInit()
|
||||
{
|
||||
spl_autoload_register(array('\Balanced\Bootstrap', 'pharAutoload'));
|
||||
self::initializeResources();
|
||||
}
|
||||
|
||||
public static function pharAutoload($classname)
|
||||
{
|
||||
self::_autoload('phar://balanced.phar', $classname);
|
||||
}
|
||||
|
||||
private static function _autoload($base, $classname) {
|
||||
if (!strncmp($classname, 'Balanced\Errors\\', strlen('Balanced\Errors\\'))) {
|
||||
$classname = 'Balanced\Errors';
|
||||
}
|
||||
$parts = explode(self::NAMESPACE_SEPARATOR, $classname);
|
||||
$path = $base . self::DIR_SEPARATOR. implode(self::DIR_SEPARATOR, $parts) . '.php';
|
||||
if (file_exists($path)) {
|
||||
require_once($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes resources (i.e. registers them with Resource::_registry). Note
|
||||
* that if you add a Resource then you must initialize it here.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
private static function initializeResources() {
|
||||
if (self::$initialized)
|
||||
return;
|
||||
|
||||
\Balanced\Errors\Error::init();
|
||||
\Balanced\Resource::init();
|
||||
\Balanced\APIKey::init();
|
||||
\Balanced\Marketplace::init();
|
||||
\Balanced\Account::init();
|
||||
\Balanced\Credit::init();
|
||||
\Balanced\Debit::init();
|
||||
\Balanced\Refund::init();
|
||||
\Balanced\Card::init();
|
||||
\Balanced\BankAccount::init();
|
||||
\Balanced\Hold::init();
|
||||
\Balanced\Merchant::init();
|
||||
\Balanced\Callback::init();
|
||||
\Balanced\Event::init();
|
||||
|
||||
self::$initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
24
include/library/Balanced/Callback.php
Executable file
24
include/library/Balanced/Callback.php
Executable file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Resource;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/*
|
||||
* A Callback is a publicly accessible location that can receive POSTed JSON
|
||||
* data whenever an Event is generated.
|
||||
*
|
||||
* You create these using Balanced\Marketplace->createCallback.
|
||||
*
|
||||
*/
|
||||
class Callback extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('callbacks', 'id');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Core\Resource;
|
||||
use Balanced\Core\URISpec;
|
||||
use Balanced\Resource;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/**
|
||||
* Represents an account card.
|
||||
@ -30,32 +30,32 @@ use Balanced\Core\URISpec;
|
||||
* ->one();
|
||||
* $account->addCard($card->uri);
|
||||
* </code>
|
||||
*/
|
||||
class Card extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('cards', 'id', '/v1');
|
||||
self::$_registry->add(get_called_class());
|
||||
*/
|
||||
class Card extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('cards', 'id', '/v1');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
|
||||
public function debit(
|
||||
$amount,
|
||||
$appears_on_statement_as = null,
|
||||
$description = null,
|
||||
$meta = null,
|
||||
public function debit(
|
||||
$amount,
|
||||
$appears_on_statement_as = null,
|
||||
$description = null,
|
||||
$meta = null,
|
||||
$source = null)
|
||||
{
|
||||
if ($this->account == null) {
|
||||
throw new \UnexpectedValueException('Card is not associated with an account.');
|
||||
}
|
||||
return $this->account->debit(
|
||||
$amount,
|
||||
return $this->account->debit(
|
||||
$amount,
|
||||
$appears_on_statement_as,
|
||||
$description,
|
||||
$meta,
|
||||
$meta,
|
||||
$this->uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Core;
|
||||
|
||||
use Balanced\Exceptions\HTTPError;
|
||||
use Balanced\Settings;
|
||||
use Httpful\Request;
|
||||
|
||||
class Client
|
||||
{
|
||||
public function __construct($request_class = null)
|
||||
{
|
||||
$this->request_class = $request_class == null ? 'Request' : $request_class;
|
||||
}
|
||||
|
||||
public function get($uri)
|
||||
{
|
||||
$url = Settings::$url_root . $uri;
|
||||
$request = \Httpful\Request::get($url);
|
||||
return $this->_op($request);
|
||||
}
|
||||
|
||||
public function post($uri, $payload)
|
||||
{
|
||||
$url = Settings::$url_root . $uri;
|
||||
$request = Request::post($url, $payload, 'json');
|
||||
return $this->_op($request);
|
||||
}
|
||||
|
||||
public function put($uri, $payload)
|
||||
{
|
||||
$url = Settings::$url_root . $uri;
|
||||
$request = Request::put($url, $payload, 'json');
|
||||
return $this->_op($request);
|
||||
}
|
||||
|
||||
public function delete($uri)
|
||||
{
|
||||
$url = Settings::$url_root . $uri;
|
||||
$request = Request::delete($url);
|
||||
return $this->_op($request);
|
||||
}
|
||||
|
||||
private function _op($request)
|
||||
{
|
||||
$user_agent = 'balanced-php/' . Settings::VERSION;
|
||||
$request->headers['User-Agent'] = $user_agent;
|
||||
if (Settings::$api_key != null)
|
||||
$request = $request->authenticateWith(Settings::$api_key , '');
|
||||
$request->expects('json');
|
||||
$response = $request->sendIt();
|
||||
if ($response->hasErrors() || $response->code == 300)
|
||||
throw new HTTPError($response);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@ -1,299 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Core;
|
||||
|
||||
class Resource
|
||||
{
|
||||
public static $fields,
|
||||
$f;
|
||||
|
||||
protected static $_client,
|
||||
$_registry,
|
||||
$_uri_spec;
|
||||
|
||||
protected $_collection_uris,
|
||||
$_member_uris;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_client = new Client();
|
||||
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;
|
||||
}
|
||||
|
||||
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(
|
||||
'Undefined property via __get(): ' . $name .
|
||||
' in ' . $trace[0]['file'] .
|
||||
' on line ' . $trace[0]['line'],
|
||||
E_USER_NOTICE);
|
||||
return null;
|
||||
}
|
||||
|
||||
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::$_registry->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;
|
||||
}
|
||||
}
|
||||
// nested
|
||||
else if (is_object($val) && property_exists($val, 'uri')) {
|
||||
$result = self::$_registry->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)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
// create
|
||||
else {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Fields
|
||||
{
|
||||
public function __get($name)
|
||||
{
|
||||
return new Field($name);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 ($not_op == null)
|
||||
throw new \LogicException(sprintf('Filter cannot be inverted'));
|
||||
$temp = $this->op;
|
||||
$this->op = $this->not_op;
|
||||
$this->not_op = $temp;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
class SortExpression
|
||||
{
|
||||
public $name,
|
||||
$ascending;
|
||||
|
||||
public function __construct($field, $ascending = true)
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->ascending= $ascending;
|
||||
}
|
||||
}
|
||||
@ -2,10 +2,10 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Core\Resource;
|
||||
use Balanced\Core\URISpec;
|
||||
use Balanced\Resource;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Represents an account credit transaction.
|
||||
*
|
||||
* You create these using Balanced\Account::credit.
|
||||
@ -26,16 +26,50 @@ use Balanced\Core\URISpec;
|
||||
* 'my_id': '112233'
|
||||
* )
|
||||
* );
|
||||
* </code>
|
||||
*/
|
||||
class Credit extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('credits', 'id');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
}
|
||||
* </code>
|
||||
*/
|
||||
class Credit extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('credits', 'id', '/v1');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
|
||||
/**
|
||||
* Credit an unstored bank account.
|
||||
*
|
||||
* @param int amount Amount to credit in USD pennies.
|
||||
* @param string description Optional description of the credit.
|
||||
* @param mixed bank_account Associative array describing a bank account to credit. The bank account will *not* be stored.
|
||||
*
|
||||
* @return \Balanced\Credit
|
||||
*
|
||||
* <code>
|
||||
* $credit = \Balanced\Credit::bankAccount(
|
||||
* 123,
|
||||
* array(
|
||||
* 'account_number' => '12341234',
|
||||
* 'name' => 'Fit Finlay',
|
||||
* 'bank_code' => '325182797',
|
||||
* 'type' => 'checking',
|
||||
* ),
|
||||
* 'something descriptive');
|
||||
* </code>
|
||||
*/
|
||||
public static function bankAccount(
|
||||
$amount,
|
||||
$bank_account,
|
||||
$description = null)
|
||||
{
|
||||
$credit = new Credit(array(
|
||||
'amount' => $amount,
|
||||
'bank_account' => $bank_account,
|
||||
'description' => $description
|
||||
));
|
||||
$credit->save();
|
||||
return $credit;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,23 +2,23 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Core\Resource;
|
||||
use Balanced\Core\URISpec;
|
||||
use Balanced\Resource;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Represents an account debit transaction.
|
||||
*
|
||||
* You create these using Balanced\Account::debit.
|
||||
*
|
||||
* <code>
|
||||
* $marketplace = \Balanced\Marketplace::mine();
|
||||
*
|
||||
*
|
||||
* $account = $marketplace
|
||||
* ->accounts
|
||||
* ->query()
|
||||
* ->filter(Account::f->email_address->eq('buyer@example.com'))
|
||||
* ->one();
|
||||
*
|
||||
*
|
||||
* $debit = $account->debit(
|
||||
* 100,
|
||||
* 'how it appears on the statement',
|
||||
@ -27,16 +27,16 @@ use Balanced\Core\URISpec;
|
||||
* 'my_id': '443322'
|
||||
* )
|
||||
* );
|
||||
* </code>
|
||||
*/
|
||||
class Debit extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('debits', 'id');
|
||||
self::$_registry->add(get_called_class());
|
||||
* </code>
|
||||
*/
|
||||
class Debit extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('debits', 'id');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,9 +56,9 @@ class Debit extends Resource
|
||||
$meta = null)
|
||||
{
|
||||
return $this->refunds->create(array(
|
||||
'amount' => $amount,
|
||||
'description' => $description,
|
||||
'amount' => $amount,
|
||||
'description' => $description,
|
||||
'meta' => $meta
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
136
include/library/Balanced/Errors.php
Executable file
136
include/library/Balanced/Errors.php
Executable file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Errors;
|
||||
|
||||
use RESTful\Exceptions\HTTPError;
|
||||
|
||||
class Error extends HTTPError
|
||||
{
|
||||
public static $codes = array();
|
||||
|
||||
public static function init()
|
||||
{
|
||||
return;
|
||||
foreach (get_declared_classes() as $class) {
|
||||
$parent_class = get_parent_class($class);
|
||||
if ($parent_class != 'Balanced\Errors\Error')
|
||||
continue;
|
||||
foreach ($class::$codes as $type)
|
||||
self::$codes[$type] = $class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DuplicateAccountEmailAddress extends Error
|
||||
{
|
||||
public static $codes = array('duplicate-email-address');
|
||||
}
|
||||
|
||||
class InvalidAmount extends Error
|
||||
{
|
||||
public static $codes = array('invalid-amount');
|
||||
}
|
||||
|
||||
class InvalidRoutingNumber extends Error
|
||||
{
|
||||
public static $codes = array('invalid-routing-number');
|
||||
}
|
||||
|
||||
class InvalidBankAccountNumber extends Error
|
||||
{
|
||||
public static $codes = array('invalid-bank-account-number');
|
||||
}
|
||||
|
||||
class Declined extends Error
|
||||
{
|
||||
public static $codes = array('funding-destination-declined', 'authorization-failed');
|
||||
}
|
||||
|
||||
class CannotAssociateMerchantWithAccount extends Error
|
||||
{
|
||||
public static $codes = array('cannot-associate-merchant-with-account');
|
||||
}
|
||||
|
||||
class AccountIsAlreadyAMerchant extends Error
|
||||
{
|
||||
public static $codes = array('account-already-merchant');
|
||||
}
|
||||
|
||||
class NoFundingSource extends Error
|
||||
{
|
||||
public static $codes = array('no-funding-source');
|
||||
}
|
||||
|
||||
class NoFundingDestination extends Error
|
||||
{
|
||||
public static $codes = array('no-funding-destination');
|
||||
}
|
||||
|
||||
class CardAlreadyAssociated extends Error
|
||||
{
|
||||
public static $codes = array('card-already-funding-src');
|
||||
}
|
||||
|
||||
class CannotAssociateCard extends Error
|
||||
{
|
||||
public static $codes = array('cannot-associate-card');
|
||||
}
|
||||
|
||||
class BankAccountAlreadyAssociated extends Error
|
||||
{
|
||||
public static $codes = array('bank-account-already-associated');
|
||||
}
|
||||
|
||||
class AddressVerificationFailed extends Error
|
||||
{
|
||||
public static $codes = array('address-verification-failed');
|
||||
}
|
||||
|
||||
class HoldExpired extends Error
|
||||
{
|
||||
public static $codes = array('authorization-expired');
|
||||
}
|
||||
|
||||
class MarketplaceAlreadyCreated extends Error
|
||||
{
|
||||
public static $codes = array('marketplace-already-created');
|
||||
}
|
||||
|
||||
class IdentityVerificationFailed extends Error
|
||||
{
|
||||
public static $codes = array('identity-verification-error', 'business-principal-kyc', 'business-kyc', 'person-kyc');
|
||||
}
|
||||
|
||||
class InsufficientFunds extends Error
|
||||
{
|
||||
public static $codes = array('insufficient-funds');
|
||||
}
|
||||
|
||||
class CannotHold extends Error
|
||||
{
|
||||
public static $codes = array('funding-source-not-hold');
|
||||
}
|
||||
|
||||
class CannotCredit extends Error
|
||||
{
|
||||
public static $codes = array('funding-destination-not-creditable');
|
||||
}
|
||||
|
||||
class CannotDebit extends Error
|
||||
{
|
||||
public static $codes = array('funding-source-not-debitable');
|
||||
}
|
||||
|
||||
class CannotRefund extends Error
|
||||
{
|
||||
public static $codes = array('funding-source-not-refundable');
|
||||
}
|
||||
|
||||
class BankAccountVerificationFailure extends Error
|
||||
{
|
||||
public static $codes = array(
|
||||
'bank-account-authentication-not-pending',
|
||||
'bank-account-authentication-failed',
|
||||
'bank-account-authentication-already-exists'
|
||||
);
|
||||
}
|
||||
23
include/library/Balanced/Event.php
Executable file
23
include/library/Balanced/Event.php
Executable file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Resource;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/*
|
||||
* An Event is a snapshot of another resource at a point in time when
|
||||
* something significant occurred. Events are created when resources are
|
||||
* created, updated, deleted or otherwise change state such as a Credit
|
||||
* being marked as failed.
|
||||
*/
|
||||
class Event extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('events', 'id', '/v1');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Exceptions;
|
||||
|
||||
/**
|
||||
* Base class for all Balanced\Exceptions.
|
||||
*/
|
||||
class Base extends \Exception
|
||||
{
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Exceptions;
|
||||
|
||||
/**
|
||||
* Indicates that a query unexpectedly returned no results.
|
||||
*/
|
||||
class NoResultFound extends Base
|
||||
{
|
||||
}
|
||||
@ -2,15 +2,15 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Core\Resource;
|
||||
use Balanced\Core\URISpec;
|
||||
use Balanced\Resource;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/**
|
||||
* Represents pending debit of funds for an account. The funds for that debit
|
||||
* are held by the processor. You can later capture the hold, which results in
|
||||
* debit, or void it, which releases the held funds.
|
||||
*
|
||||
* Note that a hold can expire so you shold always check
|
||||
* Note that a hold can expire so you should always check
|
||||
* Balanced\Hold::expires_at.
|
||||
*
|
||||
* You create these using \Balanced\Account::hold.
|
||||
@ -38,40 +38,40 @@ use Balanced\Core\URISpec;
|
||||
*/
|
||||
class Hold extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('holds', 'id');
|
||||
self::$_registry->add(get_called_class());
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('holds', 'id');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
** Voids a pending hold. This releases the held funds. Once voided a hold
|
||||
* is not longer pending can cannot be re-captured or re-voided.
|
||||
*
|
||||
* @return Balanced\Hold
|
||||
* @return \Balanced\Hold
|
||||
*/
|
||||
public function void()
|
||||
{
|
||||
$this->is_void = true;
|
||||
$this->is_void = true;
|
||||
return $this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Captures a pending hold. This results in a debit. Once captured a hold
|
||||
* is not longer pending can cannot be re-captured or re-voided.
|
||||
*
|
||||
* @param int amount Optional Portion of the pending hold to capture. If not specified the full amount associated with the hold is captured.
|
||||
*
|
||||
* @return Balanced\Debit
|
||||
* @return \Balanced\Debit
|
||||
*/
|
||||
public function capture($amount = null)
|
||||
{
|
||||
public function capture($amount = null)
|
||||
{
|
||||
$this->debit = $this->account->debits->create(array(
|
||||
'hold_uri' => $this->uri,
|
||||
'amount' => $amount,
|
||||
));
|
||||
return $this->debit;
|
||||
return $this->debit;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,60 +2,62 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Core\Resource;
|
||||
use Balanced\Core\URISpec;
|
||||
use Balanced\Resource;
|
||||
use Balanced\Errors;
|
||||
use Balanced\Account;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/**
|
||||
* Represents a marketplace.
|
||||
*
|
||||
*
|
||||
* To get started you create an api key and then create a marketplace:
|
||||
*
|
||||
*
|
||||
* <code>
|
||||
* $api_key = new \Balanced\APIKey();
|
||||
* $api_key->save();
|
||||
* $secret = $api_key->secret // better save this somewhere
|
||||
* print $secret;
|
||||
* \Balanced\Settings::$api_key = $secret;
|
||||
*
|
||||
*
|
||||
* $marketplace = new \Balanced\Marketplace();
|
||||
* $marketplace->save();
|
||||
* var_dump($marketplace);
|
||||
* var_dump($marketplace);
|
||||
* </code>
|
||||
*
|
||||
* Each api key is uniquely assocaited with an api key so once you've created a
|
||||
*
|
||||
* Each api key is uniquely associated with an api key so once you've created a
|
||||
* marketplace:
|
||||
*
|
||||
*
|
||||
* <code>
|
||||
* \Balanced\Settings::$api_key = $secret;
|
||||
* $marketplace = \Balanced\Marketplace::mine(); // this is the marketplace associated with $secret
|
||||
* $marketplace = \Balanced\Marketplace::mine(); // this is the marketplace associated with $secret
|
||||
* </code>
|
||||
*/
|
||||
class Marketplace extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('marketplaces', 'id', '/v1');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the marketplace associated with the currently configured
|
||||
* \Balanced\Settings::$api_key.
|
||||
* \Balanced\Settings::$api_key.
|
||||
*
|
||||
* @throws \Balanced\Exceptions\NoResult
|
||||
* @throws \RESTful\Exceptions\NoResultFound
|
||||
* @return \Balanced\Marketplace
|
||||
*/
|
||||
public static function mine()
|
||||
{
|
||||
return self::query()->one();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a card. These can later be associated with an account using
|
||||
* \Balanced\Account->addCard or \Balanced\Marketplace->createBuyer.
|
||||
*
|
||||
* \Balanced\Account->addCard or \Balanced\Marketplace->createBuyer.
|
||||
*
|
||||
* @param string street_address Street address. Use null if there is no address for the card.
|
||||
* @param string city City. Use null if there is no address for the card.
|
||||
* @param string postal_code Postal code. Use null if there is no address for the card.
|
||||
@ -64,7 +66,7 @@ class Marketplace extends Resource
|
||||
* @param string security_code Card security code. Use null if it is no available.
|
||||
* @param int expiration_month Expiration month.
|
||||
* @param int expiration_year Expiration year.
|
||||
*
|
||||
*
|
||||
* @return \Balanced\Card
|
||||
*/
|
||||
public function createCard(
|
||||
@ -94,77 +96,108 @@ class Marketplace extends Resource
|
||||
'expiration_year' => $expiration_year
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a bank account. These can later be associated with an account
|
||||
* using \Balanced\Account->addBankAccount.
|
||||
*
|
||||
*
|
||||
* @param string name Name of the account holder.
|
||||
* @param string account_number Account number.
|
||||
* @param string bank_code Bank code or routing number.
|
||||
*
|
||||
* @param string routing_number Bank code or routing number.
|
||||
* @param string type checking or savings
|
||||
* @param array meta Single level mapping from string keys to string values.
|
||||
*
|
||||
* @return \Balanced\BankAccount
|
||||
*/
|
||||
public function createBankAccount(
|
||||
$name,
|
||||
$account_number,
|
||||
$bank_code
|
||||
$routing_number,
|
||||
$type,
|
||||
$meta = null
|
||||
)
|
||||
{
|
||||
return $this->bank_accounts->create(array(
|
||||
'name' => $name,
|
||||
'name' => $name,
|
||||
'account_number' => $account_number,
|
||||
'bank_code' => $bank_code,
|
||||
'routing_number' => $routing_number,
|
||||
'type' => $type,
|
||||
'meta' => $meta
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a role-less account. You can later turn this into a buyer by
|
||||
* adding a funding source (e.g a card) or a merchant using
|
||||
* \Balanced\Account->promoteToMerchant.
|
||||
*
|
||||
* @param string email_address Email address. There can only be one account with this email address.
|
||||
* @param string email_address Optional email address. There can only be one account with this email address.
|
||||
* @param array[string]string meta Optional metadata to associate with the account.
|
||||
*
|
||||
* @return \Balanced\Account
|
||||
*/
|
||||
public function createAccount($email_address, $meta = null)
|
||||
public function createAccount($email_address = null, $meta = null)
|
||||
{
|
||||
return $this->accounts->create(array(
|
||||
'email_address' => $email_address,
|
||||
'meta' => $meta,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a buyer account.
|
||||
*
|
||||
* @param string email_address Email address. There can only be one account with this email address.
|
||||
* @param string card_uri URI referencing a card to associate with the account.
|
||||
* @param array[string]string meta Optional metadata to associate with the account.
|
||||
*
|
||||
* @return \Balanced\Account
|
||||
*/
|
||||
public function createBuyer($email_address, $card_uri, $meta = null)
|
||||
{
|
||||
return $this->accounts->create(array(
|
||||
'email_address' => $email_address,
|
||||
'card_uri' => $card_uri,
|
||||
'meta' => $meta,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find or create a role-less account by email address. You can later turn
|
||||
* this into a buyer by adding a funding source (e.g a card) or a merchant
|
||||
* using \Balanced\Account->promoteToMerchant.
|
||||
*
|
||||
* @param string email_address Email address. There can only be one account with this email address.
|
||||
*
|
||||
* @return \Balanced\Account
|
||||
*/
|
||||
function findOrCreateAccountByEmailAddress($email_address)
|
||||
{
|
||||
$marketplace = Marketplace::mine();
|
||||
try {
|
||||
$account = $this->accounts->create(array(
|
||||
'email_address' => $email_address
|
||||
));
|
||||
}
|
||||
catch (Errors\DuplicateAccountEmailAddress $e) {
|
||||
$account = Account::get($e->extras->account_uri);
|
||||
}
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a buyer account.
|
||||
*
|
||||
* @param string email_address Optional email address. There can only be one account with this email address.
|
||||
* @param string card_uri URI referencing a card to associate with the account.
|
||||
* @param array[string]string meta Optional metadata to associate with the account.
|
||||
* @param string name Optional name of the account.
|
||||
*
|
||||
* @return \Balanced\Account
|
||||
*/
|
||||
public function createBuyer($email_address, $card_uri, $meta = null, $name = null)
|
||||
{
|
||||
return $this->accounts->create(array(
|
||||
'email_address' => $email_address,
|
||||
'card_uri' => $card_uri,
|
||||
'meta' => $meta,
|
||||
'name' => $name
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a merchant account.
|
||||
*
|
||||
*
|
||||
* Unlike buyers the identity of a merchant must be established before
|
||||
* the account can function as a merchant (i.e. be credited). A merchant
|
||||
* can be either a person or a business. Either way that information is
|
||||
* represented as an associative array and passed as the merchant parameter
|
||||
* when creating the merchant account.
|
||||
*
|
||||
*
|
||||
* For a person the array looks like this:
|
||||
*
|
||||
*
|
||||
* <code>
|
||||
* array(
|
||||
* 'type' => 'person',
|
||||
@ -177,9 +210,9 @@ class Marketplace extends Resource
|
||||
* 'country_code' => 'USA'
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
*
|
||||
* For a business the array looks like this:
|
||||
*
|
||||
*
|
||||
* <code>
|
||||
* array(
|
||||
* 'type' => 'business',
|
||||
@ -200,10 +233,10 @@ class Marketplace extends Resource
|
||||
* )
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
*
|
||||
* In some cases the identity of the merchant, person or business, cannot
|
||||
* be verified in which case a \Balanced\Exceptions\HTTPError is thrown:
|
||||
*
|
||||
*
|
||||
* <code>
|
||||
* $identity = array(
|
||||
* 'type' => 'business',
|
||||
@ -223,7 +256,7 @@ class Marketplace extends Resource
|
||||
* 'country_code' => 'USA',
|
||||
* ),
|
||||
* );
|
||||
*
|
||||
*
|
||||
* try {
|
||||
* $merchant = \Balanced\Marketplace::mine()->createMerchant(
|
||||
* 'merchant@example.com',
|
||||
@ -231,15 +264,15 @@ class Marketplace extends Resource
|
||||
* );
|
||||
* catch (\Balanced\Exceptions\HTTPError $e) {
|
||||
* if ($e->code != 300) {
|
||||
* throw $e;
|
||||
* throw $e;
|
||||
* }
|
||||
* print e->response->header['Location'] // this is where merchant must signup
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
*
|
||||
* Once the merchant has completed signup you can use the resulting URI to
|
||||
* create an account for them on your marketplace:
|
||||
*
|
||||
*
|
||||
* <code>
|
||||
* $merchant = self::$marketplace->createMerchant(
|
||||
* 'merchant@example.com',
|
||||
@ -248,24 +281,23 @@ class Marketplace extends Resource
|
||||
* $merchant_uri
|
||||
* );
|
||||
* </coe>
|
||||
*
|
||||
* @param string email_address Email address. There can only be one account with this email address.
|
||||
*
|
||||
* @param string email_address Optional email address. There can only be one account with this email address.
|
||||
* @param array[string]mixed merchant Associative array describing the merchants identity.
|
||||
* @param string $bank_account_uri Optional URI referencing a bank account to associate with this account.
|
||||
* @param string $merchant_uri URI of a merchant created via the redirection sign-up flow.
|
||||
* @param string $name Optional name of the merchant.
|
||||
* @param array[string]string meta Optional metadata to associate with the account.
|
||||
*
|
||||
*
|
||||
* @return \Balanced\Account
|
||||
*/
|
||||
public function createMerchant(
|
||||
$email_address,
|
||||
$email_address = null,
|
||||
$merchant = null,
|
||||
$bank_account_uri = null,
|
||||
$merchant_uri = null,
|
||||
$name = null,
|
||||
$meta = null
|
||||
)
|
||||
$meta = null)
|
||||
{
|
||||
return $this->accounts->create(array(
|
||||
'email_address' => $email_address,
|
||||
@ -276,4 +308,18 @@ class Marketplace extends Resource
|
||||
'meta' => $meta,
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a callback.
|
||||
*
|
||||
* @param string url URL of callback.
|
||||
*/
|
||||
public function createCallback(
|
||||
$url
|
||||
)
|
||||
{
|
||||
return $this->callbacks->create(array(
|
||||
'url' => $url
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Core\Resource;
|
||||
use Balanced\Core\URISpec;
|
||||
use Balanced\Resource;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/**
|
||||
* Represents a merchant identity.
|
||||
@ -14,7 +14,7 @@ use Balanced\Core\URISpec;
|
||||
*
|
||||
* In some cases a merchant may need to be redirected to create a identity (e.g. the
|
||||
* information provided cannot be verified, more information is needed, etc). That
|
||||
* redirected signup results in a mechant_uri which is then asociated with an
|
||||
* redirected signup results in a merchant_uri which is then associated with an
|
||||
* account on the marketplace via \Balanced\Marketplace::createMerchant.
|
||||
*
|
||||
* @see \Balanced\Marketplace
|
||||
@ -41,7 +41,7 @@ class Merchant extends Resource
|
||||
* assert($merchant->id == $owner_account->merchant->id);
|
||||
* </code>
|
||||
*
|
||||
* @throws \Balanced\Exceptions\NoResult
|
||||
* @throws \RESTful\Exceptions\NoResultFound
|
||||
* @return \Balanced\Merchant
|
||||
*/
|
||||
public static function me()
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Core\Resource;
|
||||
use Balanced\Core\URISpec;
|
||||
use Balanced\Resource;
|
||||
use \RESTful\URISpec;
|
||||
|
||||
/**
|
||||
* Represents a refund of an account debit transaction.
|
||||
* Represents a refund of an account debit transaction.
|
||||
*
|
||||
* You create these via Balanced\Debit::refund.
|
||||
*
|
||||
@ -35,16 +35,16 @@ use Balanced\Core\URISpec;
|
||||
* 'my_id': '123123'
|
||||
* )
|
||||
* );
|
||||
* </code>
|
||||
*/
|
||||
class Refund extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('refunds', 'id');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
}
|
||||
|
||||
* </code>
|
||||
*/
|
||||
class Refund extends Resource
|
||||
{
|
||||
protected static $_uri_spec = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_uri_spec = new URISpec('refunds', 'id');
|
||||
self::$_registry->add(get_called_class());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
48
include/library/Balanced/Resource.php
Executable file
48
include/library/Balanced/Resource.php
Executable file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
use Balanced\Errors\Error;
|
||||
use RESTful\Exceptions\HTTPError;
|
||||
|
||||
class Resource extends \RESTful\Resource
|
||||
{
|
||||
public static $fields, $f;
|
||||
|
||||
protected static $_client, $_registry, $_uri_spec;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_client = new \RESTful\Client('\Balanced\Settings', null, __NAMESPACE__ .'\Resource::convertError');
|
||||
self::$_registry = new \RESTful\Registry();
|
||||
self::$f = self::$fields = new \RESTful\Fields();
|
||||
}
|
||||
|
||||
public static function convertError($response)
|
||||
{
|
||||
if (property_exists($response->body, 'category_code') &&
|
||||
array_key_exists($response->body->category_code, Error::$codes))
|
||||
$error = new Error::$codes[$response->body->category_code]($response);
|
||||
else
|
||||
$error = new HTTPError($response);
|
||||
return $error;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -2,40 +2,42 @@
|
||||
|
||||
namespace Balanced;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Configurable settings.
|
||||
*
|
||||
*
|
||||
* You can either set these settings individually:
|
||||
*
|
||||
*
|
||||
* <code>
|
||||
* \Balanced\Settngs::api_key = 'my-api-key-secret';
|
||||
* </code>
|
||||
*
|
||||
*
|
||||
* or all at once:
|
||||
*
|
||||
*
|
||||
* <code>
|
||||
* \Balanced\Settngs::configure(
|
||||
* 'https://api.balancedpayments.com',
|
||||
* 'my-api-key-secret'
|
||||
* );
|
||||
* </code>
|
||||
* </code>
|
||||
*/
|
||||
class Settings
|
||||
{
|
||||
const VERSION = '0.6.6';
|
||||
|
||||
const VERSION = '0.7.1';
|
||||
|
||||
public static $url_root = 'https://api.balancedpayments.com',
|
||||
$api_key = null;
|
||||
|
||||
$api_key = null,
|
||||
$agent = 'balanced-php',
|
||||
$version = Settings::VERSION;
|
||||
|
||||
/**
|
||||
* Configure all settings.
|
||||
*
|
||||
*
|
||||
* @param string url_root The root (schema://hostname[:port]) to use when constructing api URLs.
|
||||
* @param string api_key The api key secret to use for authenticating when talking to the api. If null then api usage is limited to uauthenticated endpoints.
|
||||
*/
|
||||
public static function configure($url_root, $api_key)
|
||||
{
|
||||
self::$url_root= $url_root;
|
||||
self::$api_key = $api_key;
|
||||
}
|
||||
* @param string api_key The api key secret to use for authenticating when talking to the api. If null then api usage is limited to uauthenticated endpoints.
|
||||
*/
|
||||
public static function configure($url_root, $api_key)
|
||||
{
|
||||
self::$url_root= $url_root;
|
||||
self::$api_key = $api_key;
|
||||
}
|
||||
}
|
||||
|
||||
44
include/library/RESTful/Bootstrap.php
Executable file
44
include/library/RESTful/Bootstrap.php
Executable 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
include/library/RESTful/Client.php
Executable file
78
include/library/RESTful/Client.php
Executable 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;
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Core;
|
||||
namespace RESTful;
|
||||
|
||||
class Collection extends Itemization
|
||||
{
|
||||
@ -9,40 +9,41 @@ class Collection extends Itemization
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
public function paginate()
|
||||
{
|
||||
return new Pagination($this->resource, $this->uri);
|
||||
}
|
||||
}
|
||||
10
include/library/RESTful/Exceptions/Base.php
Executable file
10
include/library/RESTful/Exceptions/Base.php
Executable file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Base class for all RESTful\Exceptions.
|
||||
*/
|
||||
class Base extends \Exception
|
||||
{
|
||||
}
|
||||
@ -1,27 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Exceptions;
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Indicates an HTTP level error has occured. The underlying HTTP response is
|
||||
* 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.
|
||||
*
|
||||
* 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;
|
||||
|
||||
protected function _objectify($fields)
|
||||
{
|
||||
foreach ($fields as $key => $val) {
|
||||
$this->$key = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Exceptions;
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Indicates that a query unexpectedly returned multiple results when at most
|
||||
* one was expected.
|
||||
*/
|
||||
class MultipleResultsFound extends Base
|
||||
{
|
||||
class MultipleResultsFound extends Base
|
||||
{
|
||||
}
|
||||
10
include/library/RESTful/Exceptions/NoResultFound.php
Executable file
10
include/library/RESTful/Exceptions/NoResultFound.php
Executable file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace RESTful\Exceptions;
|
||||
|
||||
/**
|
||||
* Indicates that a query unexpectedly returned no results.
|
||||
*/
|
||||
class NoResultFound extends Base
|
||||
{
|
||||
}
|
||||
85
include/library/RESTful/Field.php
Executable file
85
include/library/RESTful/Field.php
Executable 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
include/library/RESTful/Fields.php
Executable file
11
include/library/RESTful/Fields.php
Executable file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class Fields
|
||||
{
|
||||
public function __get($name)
|
||||
{
|
||||
return new Field($name);
|
||||
}
|
||||
}
|
||||
31
include/library/RESTful/FilterExpression.php
Executable file
31
include/library/RESTful/FilterExpression.php
Executable 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;
|
||||
}
|
||||
}
|
||||
@ -1,142 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Core;
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
class Itemization implements \IteratorAggregate, \ArrayAccess
|
||||
{
|
||||
public $resource,
|
||||
$uri;
|
||||
|
||||
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);
|
||||
}
|
||||
else if ($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)
|
||||
$_offset = 0,
|
||||
$_size = 25;
|
||||
|
||||
public function __construct($resource, $uri, $data = null)
|
||||
{
|
||||
$page_offset = floor($offset/$this->_size);
|
||||
$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];
|
||||
|
||||
return $page->items[$offset - $page->offset];
|
||||
}
|
||||
|
||||
public function total()
|
||||
{
|
||||
return $this->_getPage()->total;
|
||||
|
||||
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);
|
||||
$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);
|
||||
$uri = $this->_buildUri($offset = 0);
|
||||
|
||||
return new ItemizationIterator($this->resource, $uri);
|
||||
}
|
||||
|
||||
// ArrayAccess
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
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 offsetExists($offset)
|
||||
{
|
||||
throw new BadMethodCallException(get_class($this) . ' array access is read-only');
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
return (0 <= $offset && $offset < $this->total());
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
return $this->_getItem($offset);
|
||||
}
|
||||
throw new \BadMethodCallException(get_class($this) . ' array access is read-only');
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->_getItem($offset);
|
||||
}
|
||||
}
|
||||
45
include/library/RESTful/ItemizationIterator.php
Executable file
45
include/library/RESTful/ItemizationIterator.php
Executable 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));
|
||||
}
|
||||
}
|
||||
@ -1,70 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Core;
|
||||
|
||||
class Page
|
||||
{
|
||||
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)
|
||||
$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->resource = $resource;
|
||||
if ($data == null) {
|
||||
$client = $resource::getClient();
|
||||
$data = $client->get($uri)->body;
|
||||
}
|
||||
$this->total = $data->total;
|
||||
$this->total = $data->total;
|
||||
$this->items = array_map(
|
||||
function($x) use ($resource) {
|
||||
function ($x) use ($resource) {
|
||||
return new $resource($x);
|
||||
},
|
||||
$data->items);
|
||||
$this->offset = $data->offset;
|
||||
$this->limit = $data->limit;
|
||||
$this->_first_uri = $data->first_uri;
|
||||
$this->_previous_uri = $data->previous_uri;
|
||||
$this->_next_uri = $data->next_uri;
|
||||
$this->_last_uri = $data->last_uri;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
$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);
|
||||
}
|
||||
}
|
||||
@ -1,125 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Core;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Pagination implements \IteratorAggregate, \ArrayAccess
|
||||
class Pagination implements \IteratorAggregate, \ArrayAccess
|
||||
{
|
||||
public $resource,
|
||||
$uri;
|
||||
|
||||
$uri;
|
||||
|
||||
protected $_page,
|
||||
$_offset=0,
|
||||
$_size=25;
|
||||
|
||||
public function __construct($resource, $uri, $data = null)
|
||||
$_offset = 0,
|
||||
$_size = 25;
|
||||
|
||||
public function __construct($resource, $uri, $data = null)
|
||||
{
|
||||
$this->resource = $resource;
|
||||
$this->uri = $uri;
|
||||
if ($data != null)
|
||||
if ($data != null) {
|
||||
$this->_page = new Page($resource, $uri, $data);
|
||||
else
|
||||
$this->_page = null;
|
||||
} else {
|
||||
$this->_page = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function _getPage($offset = null)
|
||||
{
|
||||
if ($this->_page == null) {
|
||||
$this->_offset = ($offset == null) ? 0 : $offset * $this->_size;
|
||||
$uri = $this->_buildUri();
|
||||
$uri = $this->_buildUri();
|
||||
$this->_page = new Page($this->resource, $uri);
|
||||
}
|
||||
else if ($offset != null) {
|
||||
} elseif ($offset != null) {
|
||||
$offset = $offset * $this->_size;
|
||||
if ($offset != $this->_offset) {
|
||||
$this->_offset = $offset;
|
||||
$uri = $this->_buildUri();
|
||||
$uri = $this->_buildUri();
|
||||
$this->_page = new Page($this->resource, $uri);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_page;
|
||||
}
|
||||
|
||||
public function total()
|
||||
{
|
||||
return floor($this->_getPage()->total / $this->_size);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
if (strpos($this->uri, '?') === false) {
|
||||
$uri = $this->uri . '?';
|
||||
else
|
||||
} else {
|
||||
$uri = $this->uri . '&';
|
||||
}
|
||||
$uri = $uri . 'offset=' . strval($offset);
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
|
||||
// IteratorAggregate
|
||||
|
||||
public function getIterator()
|
||||
public function getIterator()
|
||||
{
|
||||
$uri = $this->_buildUri($offset = 0);
|
||||
return new PaginationIterator($this->resource, $uri);
|
||||
$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');
|
||||
throw new \BadMethodCallException(get_class($this) . ' array access is read-only');
|
||||
}
|
||||
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return (0 <= $offset && $offset < $this->total());
|
||||
return (0 <= $offset && $offset < $this->total());
|
||||
}
|
||||
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
throw new BadMethodCallException(get_class($this) . ' array access is read-only');
|
||||
throw new \BadMethodCallException(get_class($this) . ' array access is read-only');
|
||||
}
|
||||
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->_getPage($offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
include/library/RESTful/PaginationIterator.php
Executable file
37
include/library/RESTful/PaginationIterator.php
Executable 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;
|
||||
}
|
||||
}
|
||||
@ -1,149 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Core;
|
||||
namespace RESTful;
|
||||
|
||||
use Balanced\Exceptions\NoResultFound;
|
||||
use Balanced\Exceptions\MultipleResultsFound;
|
||||
use RESTful\Exceptions\NoResultFound;
|
||||
use RESTful\Exceptions\MultipleResultsFound;
|
||||
|
||||
|
||||
class Query extends Itemization
|
||||
{
|
||||
class Query extends Itemization
|
||||
{
|
||||
public $filters = array(),
|
||||
$sorts = array(),
|
||||
$size;
|
||||
|
||||
public function __construct($resource, $uri)
|
||||
{
|
||||
$sorts = array(),
|
||||
$size;
|
||||
|
||||
public function __construct($resource, $uri)
|
||||
{
|
||||
parent::__construct($resource, $uri);
|
||||
$this->size = $this->_size;
|
||||
$this->_parseUri($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)
|
||||
|
||||
private function _parseUri($uri)
|
||||
{
|
||||
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);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -1,24 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Core;
|
||||
namespace RESTful;
|
||||
|
||||
class Registry
|
||||
{
|
||||
protected $_resources = array();
|
||||
|
||||
public function add($resource) {
|
||||
public function add($resource)
|
||||
{
|
||||
array_push($this->_resources, $resource);
|
||||
}
|
||||
|
||||
public function match($uri) {
|
||||
public function match($uri)
|
||||
{
|
||||
foreach ($this->_resources as $resource) {
|
||||
$spec = $resource::getURISpec();
|
||||
$result = $spec->match($uri);
|
||||
if ($result == null)
|
||||
continue;
|
||||
if ($result == null) {
|
||||
continue;
|
||||
}
|
||||
$result['class'] = $resource;
|
||||
return $result;
|
||||
|
||||
return $result;
|
||||
}
|
||||
return null;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
205
include/library/RESTful/Resource.php
Executable file
205
include/library/RESTful/Resource.php
Executable 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
include/library/RESTful/Settings.php
Executable file
12
include/library/RESTful/Settings.php
Executable file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace RESTful;
|
||||
|
||||
/**
|
||||
* Settings.
|
||||
*
|
||||
*/
|
||||
class Settings
|
||||
{
|
||||
const VERSION = '0.1.7';
|
||||
}
|
||||
15
include/library/RESTful/SortExpression.php
Executable file
15
include/library/RESTful/SortExpression.php
Executable 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;
|
||||
}
|
||||
}
|
||||
@ -1,48 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Balanced\Core;
|
||||
namespace RESTful;
|
||||
|
||||
class URISpec
|
||||
{
|
||||
public $collection_uri = null,
|
||||
$name,
|
||||
$idNames;
|
||||
|
||||
$name,
|
||||
$idNames;
|
||||
|
||||
public function __construct($name, $idNames, $root = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
if (!is_array($idNames))
|
||||
if (!is_array($idNames)) {
|
||||
$idNames = array($idNames);
|
||||
}
|
||||
$this->idNames = $idNames;
|
||||
if ($root != null)
|
||||
$this->collection_uri = $root . '/' . $name;
|
||||
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('/', $uri);
|
||||
|
||||
$parts = explode('/', rtrim($uri, "/"));
|
||||
|
||||
// collection
|
||||
if ($parts[count($parts) - 1] == $this->name)
|
||||
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)
|
||||
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(
|
||||
);
|
||||
$result = array(
|
||||
'collection' => false,
|
||||
'ids' => $ids,
|
||||
);
|
||||
|
||||
'ids' => $ids,
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user