Add SMS support

Summary:
Provides a working SMS implementation with support for Twilio.

This version doesn't really retry if we get any gruff at all. Future versions should retry.

Test Plan: used bin/sms to send messages and look at them.

Reviewers: chad, epriestley

Reviewed By: epriestley

Subscribers: aurelijus, epriestley, Korvin

Maniphest Tasks: T920

Differential Revision: https://secure.phabricator.com/D8930
This commit is contained in:
Bob Trahan
2014-05-09 12:47:21 -07:00
parent e6aff100f2
commit e96c363eef
168 changed files with 15598 additions and 0 deletions

313
externals/twilio-php/Services/Twilio.php vendored Executable file
View File

@@ -0,0 +1,313 @@
<?php
/*
* Author: Neuman Vong neuman@twilio.com
* License: http://creativecommons.org/licenses/MIT/ MIT
* Link: https://twilio-php.readthedocs.org/en/latest/
*/
function Services_Twilio_autoload($className) {
if (substr($className, 0, 15) != 'Services_Twilio') {
return false;
}
$file = str_replace('_', '/', $className);
$file = str_replace('Services/', '', $file);
return include dirname(__FILE__) . "/$file.php";
}
spl_autoload_register('Services_Twilio_autoload');
/**
* Create a client to talk to the Twilio API.
*
*
* :param string $sid: Your Account SID
* :param string $token: Your Auth Token from `your dashboard
* <https://www.twilio.com/user/account>`_
* :param string $version: API version to use
* :param $_http: A HTTP client for making requests.
* :type $_http: :php:class:`Services_Twilio_TinyHttp`
* :param int $retryAttempts:
* Number of times to retry failed requests. Currently only idempotent
* requests (GET's and DELETE's) are retried.
*
* Here's an example:
*
* .. code-block:: php
*
* require('Services/Twilio.php');
* $client = new Services_Twilio('AC123', '456bef', null, null, 3);
* // Take some action with the client, etc.
*/
class Services_Twilio extends Services_Twilio_Resource
{
const USER_AGENT = 'twilio-php/3.12.4';
protected $http;
protected $retryAttempts;
protected $last_response;
protected $version;
protected $versions = array('2008-08-01', '2010-04-01');
public function __construct(
$sid,
$token,
$version = null,
Services_Twilio_TinyHttp $_http = null,
$retryAttempts = 1
) {
$this->version = in_array($version, $this->versions) ?
$version : end($this->versions);
if (null === $_http) {
if (!in_array('openssl', get_loaded_extensions())) {
throw new Services_Twilio_HttpException("The OpenSSL extension is required but not currently enabled. For more information, see http://php.net/manual/en/book.openssl.php");
}
if (in_array('curl', get_loaded_extensions())) {
$_http = new Services_Twilio_TinyHttp(
"https://api.twilio.com",
array(
"curlopts" => array(
CURLOPT_USERAGENT => self::qualifiedUserAgent(phpversion()),
CURLOPT_HTTPHEADER => array('Accept-Charset: utf-8'),
CURLOPT_CAINFO => dirname(__FILE__) . '/cacert.pem',
),
)
);
} else {
$_http = new Services_Twilio_HttpStream(
"https://api.twilio.com",
array(
"http_options" => array(
"http" => array(
"user_agent" => self::qualifiedUserAgent(phpversion()),
"header" => "Accept-Charset: utf-8\r\n",
),
"ssl" => array(
'verify_peer' => true,
'cafile' => dirname(__FILE__) . '/cacert.pem',
'verify_depth' => 5,
),
),
)
);
}
}
$_http->authenticate($sid, $token);
$this->http = $_http;
$this->accounts = new Services_Twilio_Rest_Accounts($this, "/{$this->version}/Accounts");
$this->account = $this->accounts->get($sid);
$this->retryAttempts = $retryAttempts;
}
/**
* Fully qualified user agent with the current PHP Version.
*
* :return: the user agent
* :rtype: string
*/
public static function qualifiedUserAgent($php_version) {
return self::USER_AGENT . " (php $php_version)";
}
/**
* Get the api version used by the rest client
*
* :return: the API version in use
* :returntype: string
*/
public function getVersion() {
return $this->version;
}
/**
* Get the retry attempt limit used by the rest client
*
* :return: the number of retry attempts
* :rtype: int
*/
public function getRetryAttempts() {
return $this->retryAttempts;
}
/**
* Construct a URI based on initial path, query params, and paging
* information
*
* We want to use the query params, unless we have a next_page_uri from the
* API.
*
* :param string $path: The request path (may contain query params if it's
* a next_page_uri)
* :param array $params: Query parameters to use with the request
* :param boolean $full_uri: Whether the $path contains the full uri
*
* :return: the URI that should be requested by the library
* :returntype: string
*/
public static function getRequestUri($path, $params, $full_uri = false) {
$json_path = $full_uri ? $path : "$path.json";
if (!$full_uri && !empty($params)) {
$query_path = $json_path . '?' . http_build_query($params, '', '&');
} else {
$query_path = $json_path;
}
return $query_path;
}
/**
* Helper method for implementing request retry logic
*
* :param array $callable: The function that makes an HTTP request
* :param string $uri: The URI to request
* :param int $retriesLeft: Number of times to retry
*
* :return: The object representation of the resource
* :rtype: object
*/
protected function _makeIdempotentRequest($callable, $uri, $retriesLeft) {
$response = call_user_func_array($callable, array($uri));
list($status, $headers, $body) = $response;
if ($status >= 500 && $retriesLeft > 0) {
return $this->_makeIdempotentRequest($callable, $uri, $retriesLeft - 1);
} else {
return $this->_processResponse($response);
}
}
/**
* GET the resource at the specified path.
*
* :param string $path: Path to the resource
* :param array $params: Query string parameters
* :param boolean $full_uri: Whether the full URI has been passed as an
* argument
*
* :return: The object representation of the resource
* :rtype: object
*/
public function retrieveData($path, $params = array(),
$full_uri = false
) {
$uri = self::getRequestUri($path, $params, $full_uri);
return $this->_makeIdempotentRequest(array($this->http, 'get'),
$uri, $this->retryAttempts);
}
/**
* DELETE the resource at the specified path.
*
* :param string $path: Path to the resource
* :param array $params: Query string parameters
*
* :return: The object representation of the resource
* :rtype: object
*/
public function deleteData($path, $params = array())
{
$uri = self::getRequestUri($path, $params);
return $this->_makeIdempotentRequest(array($this->http, 'delete'),
$uri, $this->retryAttempts);
}
/**
* POST to the resource at the specified path.
*
* :param string $path: Path to the resource
* :param array $params: Query string parameters
*
* :return: The object representation of the resource
* :rtype: object
*/
public function createData($path, $params = array())
{
$path = "$path.json";
$headers = array('Content-Type' => 'application/x-www-form-urlencoded');
$response = $this->http->post(
$path, $headers, self::buildQuery($params, '')
);
return $this->_processResponse($response);
}
/**
* Build a query string from query data
*
* :param array $queryData: An associative array of keys and values. The
* values can be a simple type or a list, in which case the list is
* converted to multiple query parameters with the same key.
* :param string $numericPrefix:
* :param string $queryStringStyle: Determine how to build the url
* - strict: Build a standards compliant query string without braces (can be hacked by using braces in key)
* - php: Build a PHP compatible query string with nested array syntax
* :return: The encoded query string
* :rtype: string
*/
public static function buildQuery($queryData, $numericPrefix = '') {
$query = '';
// Loop through all of the $query_data
foreach ($queryData as $key => $value) {
// If the key is an int, add the numeric_prefix to the beginning
if (is_int($key)) {
$key = $numericPrefix . $key;
}
// If the value is an array, we will end up recursing
if (is_array($value)) {
// Loop through the values
foreach ($value as $value2) {
// Add an arg_separator if needed
if ($query !== '') {
$query .= '&';
}
// Recurse
$query .= self::buildQuery(array($key => $value2), $numericPrefix);
}
} else {
// Add an arg_separator if needed
if ($query !== '') {
$query .= '&';
}
// Add the key and the urlencoded value (as a string)
$query .= $key . '=' . urlencode((string)$value);
}
}
return $query;
}
/**
* Convert the JSON encoded resource into a PHP object.
*
* :param array $response: 3-tuple containing status, headers, and body
*
* :return: PHP object decoded from JSON
* :rtype: object
* :throws: A :php:class:`Services_Twilio_RestException` if the Response is
* in the 300-500 range of status codes.
*/
private function _processResponse($response)
{
list($status, $headers, $body) = $response;
if ($status === 204) {
return true;
}
$decoded = json_decode($body);
if ($decoded === null) {
throw new Services_Twilio_RestException(
$status,
'Could not decode response body as JSON. ' .
'This likely indicates a 500 server error'
);
}
if (200 <= $status && $status < 300) {
$this->last_response = $decoded;
return $decoded;
}
throw new Services_Twilio_RestException(
$status,
isset($decoded->message) ? $decoded->message : '',
isset($decoded->code) ? $decoded->code : null,
isset($decoded->more_info) ? $decoded->more_info : null
);
}
}

View File

@@ -0,0 +1,109 @@
<?php
class Services_Twilio_AutoPagingIterator
implements Iterator
{
protected $generator;
protected $args;
protected $items;
private $_args;
public function __construct($generator, $page, $size, $filters) {
$this->generator = $generator;
$this->page = $page;
$this->size = $size;
$this->filters = $filters;
$this->items = array();
// Save a backup for rewind()
$this->_args = array(
'page' => $page,
'size' => $size,
'filters' => $filters,
);
}
public function current()
{
return current($this->items);
}
public function key()
{
return key($this->items);
}
/*
* Return the next item in the list, making another HTTP call to the next
* page of resources if necessary.
*/
public function next()
{
try {
$this->loadIfNecessary();
return next($this->items);
}
catch (Services_Twilio_RestException $e) {
// 20006 is an out of range paging error, everything else is valid
if ($e->getCode() != 20006) {
throw $e;
}
}
}
/*
* Restore everything to the way it was before we began paging. This gets
* called at the beginning of any foreach() loop
*/
public function rewind()
{
foreach ($this->_args as $arg => $val) {
$this->$arg = $val;
}
$this->items = array();
$this->next_page_uri = null;
}
public function count()
{
throw new BadMethodCallException('Not allowed');
}
public function valid()
{
try {
$this->loadIfNecessary();
return key($this->items) !== null;
}
catch (Services_Twilio_RestException $e) {
// 20006 is an out of range paging error, everything else is valid
if ($e->getCode() != 20006) {
throw $e;
}
}
return false;
}
/*
* Fill $this->items with a new page from the API, if necessary.
*/
protected function loadIfNecessary()
{
if (// Empty because it's the first time or last page was empty
empty($this->items)
// null key when the items list is iterated over completely
|| key($this->items) === null
) {
$page = call_user_func_array($this->generator, array(
$this->page,
$this->size,
$this->filters,
$this->next_page_uri,
));
$this->next_page_uri = $page->next_page_uri;
$this->items = $page->getItems();
$this->page = $this->page + 1;
}
}
}

View File

@@ -0,0 +1,346 @@
<?php
/**
* Twilio Capability Token generator
*
* @category Services
* @package Services_Twilio
* @author Jeff Lindsay <jeff.lindsay@twilio.com>
* @license http://creativecommons.org/licenses/MIT/ MIT
*/
class Services_Twilio_Capability
{
public $accountSid;
public $authToken;
public $scopes;
/**
* Create a new TwilioCapability with zero permissions. Next steps are to
* grant access to resources by configuring this token through the
* functions allowXXXX.
*
* @param $accountSid the account sid to which this token is granted access
* @param $authToken the secret key used to sign the token. Note, this auth
* token is not visible to the user of the token.
*/
public function __construct($accountSid, $authToken)
{
$this->accountSid = $accountSid;
$this->authToken = $authToken;
$this->scopes = array();
$this->clientName = false;
}
/**
* If the user of this token should be allowed to accept incoming
* connections then configure the TwilioCapability through this method and
* specify the client name.
*
* @param $clientName
*/
public function allowClientIncoming($clientName)
{
// clientName must be a non-zero length alphanumeric string
if (preg_match('/\W/', $clientName)) {
throw new InvalidArgumentException(
'Only alphanumeric characters allowed in client name.');
}
if (strlen($clientName) == 0) {
throw new InvalidArgumentException(
'Client name must not be a zero length string.');
}
$this->clientName = $clientName;
$this->allow('client', 'incoming',
array('clientName' => $clientName));
}
/**
* Allow the user of this token to make outgoing connections.
*
* @param $appSid the application to which this token grants access
* @param $appParams signed parameters that the user of this token cannot
* overwrite.
*/
public function allowClientOutgoing($appSid, array $appParams=array())
{
$this->allow('client', 'outgoing', array(
'appSid' => $appSid,
'appParams' => http_build_query($appParams, '', '&')));
}
/**
* Allow the user of this token to access their event stream.
*
* @param $filters key/value filters to apply to the event stream
*/
public function allowEventStream(array $filters=array())
{
$this->allow('stream', 'subscribe', array(
'path' => '/2010-04-01/Events',
'params' => http_build_query($filters, '', '&'),
));
}
/**
* Generates a new token based on the credentials and permissions that
* previously has been granted to this token.
*
* @param $ttl the expiration time of the token (in seconds). Default
* value is 3600 (1hr)
* @return the newly generated token that is valid for $ttl seconds
*/
public function generateToken($ttl = 3600)
{
$payload = array(
'scope' => array(),
'iss' => $this->accountSid,
'exp' => time() + $ttl,
);
$scopeStrings = array();
foreach ($this->scopes as $scope) {
if ($scope->privilege == "outgoing" && $this->clientName)
$scope->params["clientName"] = $this->clientName;
$scopeStrings[] = $scope->toString();
}
$payload['scope'] = implode(' ', $scopeStrings);
return JWT::encode($payload, $this->authToken, 'HS256');
}
protected function allow($service, $privilege, $params) {
$this->scopes[] = new ScopeURI($service, $privilege, $params);
}
}
/**
* Scope URI implementation
*
* Simple way to represent configurable privileges in an OAuth
* friendly way. For our case, they look like this:
*
* scope:<service>:<privilege>?<params>
*
* For example:
* scope:client:incoming?name=jonas
*
* @author Jeff Lindsay <jeff.lindsay@twilio.com>
*/
class ScopeURI
{
public $service;
public $privilege;
public $params;
public function __construct($service, $privilege, $params = array())
{
$this->service = $service;
$this->privilege = $privilege;
$this->params = $params;
}
public function toString()
{
$uri = "scope:{$this->service}:{$this->privilege}";
if (count($this->params)) {
$uri .= "?".http_build_query($this->params, '', '&');
}
return $uri;
}
/**
* Parse a scope URI into a ScopeURI object
*
* @param string $uri The scope URI
* @return ScopeURI The parsed scope uri
*/
public static function parse($uri)
{
if (strpos($uri, 'scope:') !== 0) {
throw new UnexpectedValueException(
'Not a scope URI according to scheme');
}
$parts = explode('?', $uri, 1);
$params = null;
if (count($parts) > 1) {
parse_str($parts[1], $params);
}
$parts = explode(':', $parts[0], 2);
if (count($parts) != 3) {
throw new UnexpectedValueException(
'Not enough parts for scope URI');
}
list($scheme, $service, $privilege) = $parts;
return new ScopeURI($service, $privilege, $params);
}
}
/**
* JSON Web Token implementation
*
* Minimum implementation used by Realtime auth, based on this spec:
* http://self-issued.info/docs/draft-jones-json-web-token-01.html.
*
* @author Neuman Vong <neuman@twilio.com>
*/
class JWT
{
/**
* @param string $jwt The JWT
* @param string|null $key The secret key
* @param bool $verify Don't skip verification process
*
* @return object The JWT's payload as a PHP object
*/
public static function decode($jwt, $key = null, $verify = true)
{
$tks = explode('.', $jwt);
if (count($tks) != 3) {
throw new UnexpectedValueException('Wrong number of segments');
}
list($headb64, $payloadb64, $cryptob64) = $tks;
if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)))
) {
throw new UnexpectedValueException('Invalid segment encoding');
}
if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($payloadb64))
) {
throw new UnexpectedValueException('Invalid segment encoding');
}
$sig = JWT::urlsafeB64Decode($cryptob64);
if ($verify) {
if (empty($header->alg)) {
throw new DomainException('Empty algorithm');
}
if ($sig != JWT::sign("$headb64.$payloadb64", $key, $header->alg)) {
throw new UnexpectedValueException('Signature verification failed');
}
}
return $payload;
}
/**
* @param object|array $payload PHP object or array
* @param string $key The secret key
* @param string $algo The signing algorithm
*
* @return string A JWT
*/
public static function encode($payload, $key, $algo = 'HS256')
{
$header = array('typ' => 'JWT', 'alg' => $algo);
$segments = array();
$segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header));
$segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload));
$signing_input = implode('.', $segments);
$signature = JWT::sign($signing_input, $key, $algo);
$segments[] = JWT::urlsafeB64Encode($signature);
return implode('.', $segments);
}
/**
* @param string $msg The message to sign
* @param string $key The secret key
* @param string $method The signing algorithm
*
* @return string An encrypted message
*/
public static function sign($msg, $key, $method = 'HS256')
{
$methods = array(
'HS256' => 'sha256',
'HS384' => 'sha384',
'HS512' => 'sha512',
);
if (empty($methods[$method])) {
throw new DomainException('Algorithm not supported');
}
return hash_hmac($methods[$method], $msg, $key, true);
}
/**
* @param string $input JSON string
*
* @return object Object representation of JSON string
*/
public static function jsonDecode($input)
{
$obj = json_decode($input);
if (function_exists('json_last_error') && $errno = json_last_error()) {
JWT::handleJsonError($errno);
}
else if ($obj === null && $input !== 'null') {
throw new DomainException('Null result with non-null input');
}
return $obj;
}
/**
* @param object|array $input A PHP object or array
*
* @return string JSON representation of the PHP object or array
*/
public static function jsonEncode($input)
{
$json = json_encode($input);
if (function_exists('json_last_error') && $errno = json_last_error()) {
JWT::handleJsonError($errno);
}
else if ($json === 'null' && $input !== null) {
throw new DomainException('Null result with non-null input');
}
return $json;
}
/**
* @param string $input A base64 encoded string
*
* @return string A decoded string
*/
public static function urlsafeB64Decode($input)
{
$padlen = 4 - strlen($input) % 4;
$input .= str_repeat('=', $padlen);
return base64_decode(strtr($input, '-_', '+/'));
}
/**
* @param string $input Anything really
*
* @return string The base64 encode of what you passed in
*/
public static function urlsafeB64Encode($input)
{
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
}
/**
* @param int $errno An error number from json_last_error()
*
* @return void
*/
private static function handleJsonError($errno)
{
$messages = array(
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON'
);
throw new DomainException(isset($messages[$errno])
? $messages[$errno]
: 'Unknown JSON error: ' . $errno
);
}
}

View File

@@ -0,0 +1,3 @@
<?php
class Services_Twilio_HttpException extends ErrorException {}

View File

@@ -0,0 +1,94 @@
<?php
/**
* HTTP Stream version of the TinyHttp Client used to connect to Twilio
* services.
*/
class Services_Twilio_HttpStreamException extends ErrorException {}
class Services_Twilio_HttpStream {
private $auth_header = null;
private $uri = null;
private $debug = false;
private static $default_options = array(
"http" => array(
"headers" => "",
"timeout" => 60,
"follow_location" => true,
"ignore_errors" => true,
),
"ssl" => array(),
);
private $options = array();
public function __construct($uri = '', $kwargs = array()) {
$this->uri = $uri;
if (isset($kwargs['debug'])) {
$this->debug = true;
}
if (isset($kwargs['http_options'])) {
$this->options = $kwargs['http_options'] + self::$default_options;
} else {
$this->options = self::$default_options;
}
}
public function __call($name, $args) {
list($res, $req_headers, $req_body) = $args + array(0, array(), '');
$request_options = $this->options;
$url = $this->uri . $res;
if (isset($req_body) && strlen($req_body) > 0) {
$request_options['http']['content'] = $req_body;
}
foreach($req_headers as $key => $value) {
$request_options['http']['header'] .= sprintf("%s: %s\r\n", $key, $value);
}
if (isset($this->auth_header)) {
$request_options['http']['header'] .= $this->auth_header;
}
$request_options['http']['method'] = strtoupper($name);
$request_options['http']['ignore_errors'] = true;
if ($this->debug) {
error_log(var_export($request_options, true));
}
$ctx = stream_context_create($request_options);
$result = file_get_contents($url, false, $ctx);
if (false === $result) {
throw new Services_Twilio_HttpStreamException(
"Unable to connect to service");
}
$status_header = array_shift($http_response_header);
if (1 !== preg_match('#HTTP/\d+\.\d+ (\d+)#', $status_header, $matches)) {
throw new Services_Twilio_HttpStreamException(
"Unable to detect the status code in the HTTP result.");
}
$status_code = intval($matches[1]);
$response_headers = array();
foreach($http_response_header as $header) {
list($key, $val) = explode(":", $header);
$response_headers[trim($key)] = trim($val);
}
return array($status_code, $response_headers, $result);
}
public function authenticate($user, $pass) {
if (isset($user) && isset($pass)) {
$this->auth_header = sprintf("Authorization: Basic %s",
base64_encode(sprintf("%s:%s", $user, $pass)));
} else {
$this->auth_header = null;
}
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* @category Services
* @package Services_Twilio
* @author Neuman Vong <neuman@twilio.com>
* @license http://creativecommons.org/licenses/MIT/ MIT
* @link http://pear.php.net/package/Services_Twilio
*/
/**
* Abstraction of an instance resource from the Twilio API.
*/
abstract class Services_Twilio_InstanceResource extends Services_Twilio_Resource {
/**
* Make a request to the API to update an instance resource
*
* :param mixed $params: An array of updates, or a property name
* :param mixed $value: A value with which to update the resource
*
* :rtype: null
* :throws: a :php:class:`RestException <Services_Twilio_RestException>` if
* the update fails.
*/
public function update($params, $value = null)
{
if (!is_array($params)) {
$params = array($params => $value);
}
$decamelizedParams = $this->client->createData($this->uri, $params);
$this->updateAttributes($decamelizedParams);
}
/*
* Add all properties from an associative array (the JSON response body) as
* properties on this instance resource, except the URI
*
* :param stdClass $params: An object containing all of the parameters of
* this instance
* :return: Nothing, this is purely side effecting
* :rtype: null
*/
public function updateAttributes($params) {
unset($params->uri);
foreach ($params as $name => $value) {
$this->$name = $value;
}
}
/**
* Get the value of a property on this resource.
*
* Instead of defining all of the properties of an object directly, we rely
* on the API to tell us which properties an object has. This method will
* query the API to retrieve a property for an object, if it is not already
* set on the object.
*
* If the call is to a subresource, eg ``$client->account->messages``, no
* request is made.
*
* To help with lazy HTTP requests, we don't actually retrieve an object
* from the API unless you really need it. Hence, this function may make API
* requests even if the property you're requesting isn't available on the
* resource.
*
* :param string $key: The property name
*
* :return mixed: Could be anything.
* :throws: a :php:class:`RestException <Services_Twilio_RestException>` if
* the update fails.
*/
public function __get($key)
{
if ($subresource = $this->getSubresources($key)) {
return $subresource;
}
if (!isset($this->$key)) {
$params = $this->client->retrieveData($this->uri);
$this->updateAttributes($params);
}
return $this->$key;
}
}

View File

@@ -0,0 +1,203 @@
<?php
/**
* @author Neuman Vong neuman@twilio.com
* @license http://creativecommons.org/licenses/MIT/ MIT
* @link http://pear.php.net/package/Services_Twilio
*/
/**
* Abstraction of a list resource from the Twilio API.
*
* The list resource implements the `IteratorAggregate
* <http://php.net/manual/en/class.iteratoraggregate.php>`_ and the `Countable
* <http://php.net/manual/en/class.countable.php>`_ interfaces.
*
*/
abstract class Services_Twilio_ListResource extends Services_Twilio_Resource
implements IteratorAggregate, Countable
{
public function __construct($client, $uri) {
$name = $this->getResourceName(true);
/*
* By default trim the 's' from the end of the list name to get the
* instance name (ex Accounts -> Account). This behavior can be
* overridden by child classes if the rule doesn't work.
*/
if (!isset($this->instance_name)) {
$this->instance_name = "Services_Twilio_Rest_" . rtrim($name, 's');
}
parent::__construct($client, $uri);
}
/**
* Gets a resource from this list.
*
* :param string $sid: The resource SID
* :return: The resource
* :rtype: :php:class:`InstanceResource <Services_Twilio_InstanceResource>`
*/
public function get($sid) {
$instance = new $this->instance_name(
$this->client, $this->uri . "/$sid"
);
// XXX check if this is actually a sid in all cases.
$instance->sid = $sid;
return $instance;
}
/**
* Construct an :php:class:`InstanceResource
* <Services_Twilio_InstanceResource>` with the specified params.
*
* :param array $params: usually a JSON HTTP response from the API
* :return: An instance with properties
* initialized to the values in the params array.
* :rtype: :php:class:`InstanceResource <Services_Twilio_InstanceResource>`
*/
public function getObjectFromJson($params, $idParam = "sid")
{
if (isset($params->{$idParam})) {
$uri = $this->uri . "/" . $params->{$idParam};
} else {
$uri = $this->uri;
}
return new $this->instance_name($this->client, $uri, $params);
}
/**
* Deletes a resource from this list.
*
* :param string $sid: The resource SID
* :rtype: null
*/
public function delete($sid, $params = array())
{
$this->client->deleteData($this->uri . '/' . $sid, $params);
}
/**
* Create a resource on the list and then return its representation as an
* InstanceResource.
*
* :param array $params: The parameters with which to create the resource
*
* :return: The created resource
* :rtype: :php:class:`InstanceResource <Services_Twilio_InstanceResource>`
*/
protected function _create($params)
{
$params = $this->client->createData($this->uri, $params);
/* Some methods like verified caller ID don't return sids. */
if (isset($params->sid)) {
$resource_uri = $this->uri . '/' . $params->sid;
} else {
$resource_uri = $this->uri;
}
return new $this->instance_name($this->client, $resource_uri, $params);
}
/**
* Returns a page of :php:class:`InstanceResources
* <Services_Twilio_InstanceResource>` from this list.
*
* :param int $page: The start page
* :param int $size: Number of items per page
* :param array $filters: Optional filters
* :param string $deep_paging_uri: if provided, the $page and $size
* parameters will be ignored and this URI will be requested directly.
*
* :return: A page of resources
* :rtype: :php:class:`Services_Twilio_Page`
*/
public function getPage(
$page = 0, $size = 50, $filters = array(), $deep_paging_uri = null
) {
$list_name = $this->getResourceName();
if ($deep_paging_uri !== null) {
$page = $this->client->retrieveData($deep_paging_uri, array(), true);
} else {
$page = $this->client->retrieveData($this->uri, array(
'Page' => $page,
'PageSize' => $size,
) + $filters);
}
/* create a new PHP object for each json obj in the api response. */
$page->$list_name = array_map(
array($this, 'getObjectFromJson'),
$page->$list_name
);
if (isset($page->next_page_uri)) {
$next_page_uri = $page->next_page_uri;
} else {
$next_page_uri = null;
}
return new Services_Twilio_Page($page, $list_name, $next_page_uri);
}
/**
* Get the total number of instances for this list.
*
* This will make one HTTP request to retrieve the total, every time this
* method is called.
*
* If the total is not set, or an Exception was thrown, returns 0
*
* :return: The total number of instance members
* :rtype: integer
*/
public function count() {
try {
$page = $this->getPage(0, 1);
return $page ? (int)$page->total : 0;
} catch (Exception $e) {
return 0;
}
}
/**
* Returns an iterable list of
* :php:class:`instance resources <Services_Twilio_InstanceResource>`.
*
* :param int $page: The start page
* :param int $size: Number of items per page
* :param array $filters: Optional filters.
* The filter array can accept full datetimes when StartTime or DateCreated
* are used. Inequalities should be within the key portion of the array and
* multiple filter parameters can be combined for more specific searches.
*
* .. code-block:: php
*
* array('DateCreated>' => '2011-07-05 08:00:00', 'DateCreated<' => '2011-08-01')
*
* .. code-block:: php
*
* array('StartTime<' => '2011-07-05 08:00:00')
*
* :return: An iterator
* :rtype: :php:class:`Services_Twilio_AutoPagingIterator`
*/
public function getIterator(
$page = 0, $size = 50, $filters = array()
) {
return new Services_Twilio_AutoPagingIterator(
array($this, 'getPageGenerator'), $page, $size, $filters
);
}
/**
* Retrieve a new page of API results, and update iterator parameters. This
* function is called by the paging iterator to retrieve a new page and
* shouldn't be called directly.
*/
public function getPageGenerator(
$page, $size, $filters = array(), $deep_paging_uri = null
) {
return $this->getPage($page, $size, $filters, $deep_paging_uri);
}
}

View File

@@ -0,0 +1,35 @@
<?php
class Services_Twilio_NumberType extends Services_Twilio_ListResource
{
public function getResourceName($camelized = false) {
$this->instance_name = 'Services_Twilio_Rest_IncomingPhoneNumber';
return $camelized ? 'IncomingPhoneNumbers' : 'incoming_phone_numbers';
}
/**
* Purchase a new phone number.
*
* Example usage:
*
* .. code-block:: php
*
* $marlosBurner = '+14105551234';
* $client->account->incoming_phone_numbers->local->purchase($marlosBurner);
*
* :param string $phone_number: The phone number to purchase
* :param array $params: An optional array of parameters to pass along with
* the request (to configure the phone number)
*/
public function purchase($phone_number, array $params = array()) {
$postParams = array(
'PhoneNumber' => $phone_number
);
return $this->create($postParams + $params);
}
public function create(array $params = array()) {
return parent::_create($params);
}
}

68
externals/twilio-php/Services/Twilio/Page.php vendored Executable file
View File

@@ -0,0 +1,68 @@
<?php
/**
* A representation of a page of resources.
*
* @category Services
* @package Services_Twilio
* @author Neuman Vong <neuman@twilio.com>
* @license http://creativecommons.org/licenses/MIT/ MIT
* @link http://pear.php.net/package/Services_Twilio
*/
class Services_Twilio_Page
implements IteratorAggregate
{
/**
* The item list.
*
* @var array $items
*/
protected $items;
/**
* Constructs a page.
*
* @param object $page The page object
* @param string $name The key of the item list
*/
public function __construct($page, $name, $next_page_uri = null)
{
$this->page = $page;
$this->items = $page->{$name};
$this->next_page_uri = $next_page_uri;
}
/**
* The item list of the page.
*
* @return array A list of instance resources
*/
public function getItems()
{
return $this->items;
}
/**
* Magic method to allow retrieving the properties of the wrapped page.
*
* @param string $prop The property name
*
* @return mixed Could be anything
*/
public function __get($prop)
{
return $this->page->$prop;
}
/**
* Implementation of IteratorAggregate::getIterator().
*
* @return Traversable
*/
public function getIterator()
{
return $this->getItems();
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* Helper class to wrap an object with a modified interface created by
* a partial application of its existing methods.
*
* @category Services
* @package Services_Twilio
* @author Neuman Vong <neuman@twilio.com>
* @license http://creativecommons.org/licenses/MIT/ MIT
* @link http://pear.php.net/package/Services_Twilio
*/
class Services_Twilio_PartialApplicationHelper
{
private $callbacks;
public function __construct()
{
$this->callbacks = array();
}
public function set($method, $callback, array $args)
{
if (!is_callable($callback)) {
return FALSE;
}
$this->callbacks[$method] = array($callback, $args);
}
public function __call($method, $args)
{
if (!isset($this->callbacks[$method])) {
throw new Exception("Method not found: $method");
}
list($callback, $cb_args) = $this->callbacks[$method];
return call_user_func_array(
$callback,
array_merge($cb_args, $args)
);
}
}

View File

@@ -0,0 +1,36 @@
<?php
class Services_Twilio_RequestValidator
{
protected $AuthToken;
function __construct($token)
{
$this->AuthToken = $token;
}
public function computeSignature($url, $data = array())
{
// sort the array by keys
ksort($data);
// append them to the data string in order
// with no delimiters
foreach($data as $key => $value)
$url .= "$key$value";
// This function calculates the HMAC hash of the data with the key
// passed in
// Note: hash_hmac requires PHP 5 >= 5.1.2 or PECL hash:1.1-1.5
// Or http://pear.php.net/package/Crypt_HMAC/
return base64_encode(hash_hmac("sha1", $url, $this->AuthToken, true));
}
public function validate($expectedSignature, $url, $data = array())
{
return $this->computeSignature($url, $data)
== $expectedSignature;
}
}

View File

@@ -0,0 +1,134 @@
<?php
/**
* Abstraction of a Twilio resource.
*
* @category Services
* @package Services_Twilio
* @author Neuman Vong <neuman@twilio.com>
* @license http://creativecommons.org/licenses/MIT/ MIT
* @link http://pear.php.net/package/Services_Twilio
*/
abstract class Services_Twilio_Resource {
protected $subresources;
public function __construct($client, $uri, $params = array())
{
$this->subresources = array();
$this->client = $client;
foreach ($params as $name => $param) {
$this->$name = $param;
}
$this->uri = $uri;
$this->init($client, $uri);
}
protected function init($client, $uri)
{
// Left empty for derived classes to implement
}
public function getSubresources($name = null) {
if (isset($name)) {
return isset($this->subresources[$name])
? $this->subresources[$name]
: null;
}
return $this->subresources;
}
protected function setupSubresources()
{
foreach (func_get_args() as $name) {
$constantized = ucfirst(self::camelize($name));
$type = "Services_Twilio_Rest_" . $constantized;
$this->subresources[$name] = new $type(
$this->client, $this->uri . "/$constantized"
);
}
}
/*
* Get the resource name from the classname
*
* Ex: Services_Twilio_Rest_Accounts -> Accounts
*
* @param boolean $camelized Whether to return camel case or not
*/
public function getResourceName($camelized = false)
{
$name = get_class($this);
$parts = explode('_', $name);
$basename = end($parts);
if ($camelized) {
return $basename;
} else {
return self::decamelize($basename);
}
}
public static function decamelize($word)
{
$callback = create_function('$matches',
'return strtolower(strlen("$matches[1]") ? "$matches[1]_$matches[2]" : "$matches[2]");');
return preg_replace_callback(
'/(^|[a-z])([A-Z])/',
$callback,
$word
);
}
/**
* Return camelized version of a word
* Examples: sms_messages => SMSMessages, calls => Calls,
* incoming_phone_numbers => IncomingPhoneNumbers
*
* @param string $word The word to camelize
* @return string
*/
public static function camelize($word) {
$callback = create_function('$matches', 'return strtoupper("$matches[2]");');
return preg_replace_callback('/(^|_)([a-z])/',
$callback,
$word);
}
/**
* Get the value of a property on this resource.
*
* @param string $key The property name
* @return mixed Could be anything.
*/
public function __get($key) {
if ($subresource = $this->getSubresources($key)) {
return $subresource;
}
return $this->$key;
}
/**
* Print a JSON representation of this object. Strips the HTTP client
* before returning.
*
* Note, this should mainly be used for debugging, and is not guaranteed
* to correspond 1:1 with the JSON API output.
*
* Note that echoing an object before an HTTP request has been made to
* "fill in" its properties may return an empty object
*/
public function __toString() {
$out = array();
foreach ($this as $key => $value) {
if ($key !== 'client' && $key !== 'subresources') {
$out[$key] = $value;
}
}
return json_encode($out, true);
}
}

View File

@@ -0,0 +1,33 @@
<?php
class Services_Twilio_Rest_Account extends Services_Twilio_InstanceResource {
protected function init($client, $uri) {
$this->setupSubresources(
'applications',
'available_phone_numbers',
'outgoing_caller_ids',
'calls',
'conferences',
'incoming_phone_numbers',
'media',
'messages',
'notifications',
'outgoing_callerids',
'recordings',
'sms_messages',
'short_codes',
'transcriptions',
'connect_apps',
'authorized_connect_apps',
'usage_records',
'usage_triggers',
'queues',
'sip'
);
$this->sandbox = new Services_Twilio_Rest_Sandbox(
$client, $uri . '/Sandbox'
);
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* For more information, see the `Account List Resource
* <http://www.twilio.com/docs/api/rest/account#list>`_ documentation.
*/
class Services_Twilio_Rest_Accounts extends Services_Twilio_ListResource {
/**
* Create a new subaccount.
*
* :param array $params: An array of parameters describing the new
* subaccount. The ``$params`` array can contain the following keys:
*
* *FriendlyName*
* A description of this account, up to 64 characters long
*
* :returns: The new subaccount
* :rtype: :php:class:`Services_Twilio_Rest_Account`
*
*/
public function create($params = array()) {
return parent::_create($params);
}
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_Application
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,12 @@
<?php
class Services_Twilio_Rest_Applications
extends Services_Twilio_ListResource
{
public function create($name, array $params = array())
{
return parent::_create(array(
'FriendlyName' => $name
) + $params);
}
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_AuthorizedConnectApp
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,10 @@
<?php
class Services_Twilio_Rest_AuthorizedConnectApps
extends Services_Twilio_ListResource
{
public function create($name, array $params = array())
{
throw new BadMethodCallException('Not allowed');
}
}

View File

@@ -0,0 +1,7 @@
<?php
class Services_Twilio_Rest_AvailablePhoneNumber
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,54 @@
<?php
class Services_Twilio_Rest_AvailablePhoneNumbers
extends Services_Twilio_ListResource
{
public function getLocal($country) {
$curried = new Services_Twilio_PartialApplicationHelper();
$curried->set(
'getList',
array($this, 'getList'),
array($country, 'Local')
);
return $curried;
}
public function getTollFree($country) {
$curried = new Services_Twilio_PartialApplicationHelper();
$curried->set(
'getList',
array($this, 'getList'),
array($country, 'TollFree')
);
return $curried;
}
public function getMobile($country)
{
$curried = new Services_Twilio_PartialApplicationHelper();
$curried->set(
'getList',
array($this, 'getList'),
array($country, 'Mobile')
);
return $curried;
}
/**
* Get a list of available phone numbers.
*
* @param string $country The 2-digit country code you'd like to search for
* numbers e.g. ('US', 'CA', 'GB')
* @param string $type The type of number ('Local', 'TollFree', or 'Mobile')
* @return object The object representation of the resource
*/
public function getList($country, $type, array $params = array())
{
return $this->client->retrieveData($this->uri . "/$country/$type", $params);
}
public function getResourceName($camelized = false) {
// You can't page through the list of available phone numbers.
$this->instance_name = 'Services_Twilio_Rest_AvailablePhoneNumber';
return $camelized ? 'Countries' : 'countries';
}
}

View File

@@ -0,0 +1,105 @@
<?php
/**
* For more information, see the `Call Instance Resource <http://www.twilio.com/docs/api/rest/call#instance>`_ documentation.
*
* .. php:attr:: sid
*
* A 34 character string that uniquely identifies this resource.
*
* .. php:attr:: parent_call_sid
*
* A 34 character string that uniquely identifies the call that created this leg.
*
* .. php:attr:: date_created
*
* The date that this resource was created, given as GMT in RFC 2822 format.
*
* .. php:attr:: date_updated
*
* The date that this resource was last updated, given as GMT in RFC 2822 format.
*
* .. php:attr:: account_sid
*
* The unique id of the Account responsible for creating this call.
*
* .. php:attr:: to
*
* The phone number that received this call. e.g., +16175551212 (E.164 format)
*
* .. php:attr:: from
*
* The phone number that made this call. e.g., +16175551212 (E.164 format)
*
* .. php:attr:: phone_number_sid
*
* If the call was inbound, this is the Sid of the IncomingPhoneNumber that
* received the call. If the call was outbound, it is the Sid of the
* OutgoingCallerId from which the call was placed.
*
* .. php:attr:: status
*
* A string representing the status of the call. May be `QUEUED`, `RINGING`,
* `IN-PROGRESS`, `COMPLETED`, `FAILED`, `BUSY` or `NO_ANSWER`.
*
* .. php:attr:: stat_time
*
* The start time of the call, given as GMT in RFC 2822 format. Empty if the call has not yet been dialed.
*
* .. php:attr:: end_time
*
* The end time of the call, given as GMT in RFC 2822 format. Empty if the call did not complete successfully.
*
* .. php:attr:: duration
*
* The length of the call in seconds. This value is empty for busy, failed, unanswered or ongoing calls.
*
* .. php:attr:: price
*
* The charge for this call in USD. Populated after the call is completed. May not be immediately available.
*
* .. php:attr:: direction
*
* A string describing the direction of the call. inbound for inbound
* calls, outbound-api for calls initiated via the REST API or
* outbound-dial for calls initiated by a <Dial> verb.
*
* .. php:attr:: answered_by
*
* If this call was initiated with answering machine detection, either human or machine. Empty otherwise.
*
* .. php:attr:: forwarded_from
*
* If this call was an incoming call forwarded from another number, the
* forwarding phone number (depends on carrier supporting forwarding).
* Empty otherwise.
*
* .. php:attr:: caller_name
*
* If this call was an incoming call from a phone number with Caller ID Lookup enabled, the caller's name. Empty otherwise.
*/
class Services_Twilio_Rest_Call extends Services_Twilio_InstanceResource {
/**
* Hang up the call
*/
public function hangup() {
$this->update('Status', 'completed');
}
/**
* Redirect the call to a new URL
*
* :param string $url: the new URL to retrieve call flow from.
*/
public function route($url) {
$this->update('Url', $url);
}
protected function init($client, $uri) {
$this->setupSubresources(
'notifications',
'recordings'
);
}
}

View File

@@ -0,0 +1,27 @@
<?php
class Services_Twilio_Rest_Calls
extends Services_Twilio_ListResource
{
public static function isApplicationSid($value)
{
return strlen($value) == 34
&& !(strpos($value, "AP") === false);
}
public function create($from, $to, $url, array $params = array())
{
$params["To"] = $to;
$params["From"] = $from;
if (self::isApplicationSid($url)) {
$params["ApplicationSid"] = $url;
} else {
$params["Url"] = $url;
}
return parent::_create($params);
}
}

View File

@@ -0,0 +1,12 @@
<?php
class Services_Twilio_Rest_Conference
extends Services_Twilio_InstanceResource
{
protected function init($client, $uri)
{
$this->setupSubresources(
'participants'
);
}
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_Conferences
extends Services_Twilio_ListResource
{
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_ConnectApp
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,10 @@
<?php
class Services_Twilio_Rest_ConnectApps
extends Services_Twilio_ListResource
{
public function create($name, array $params = array())
{
throw new BadMethodCallException('Not allowed');
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* A single Credential
*
* .. php:attr:: date_created
*
* The date the Credential was created
*
* .. php:attr:: date_updated
*
* The date the Credential was updated
*
* .. php:attr:: sid
*
* A 34 character string that identifies this object
*
* .. php:attr:: account_sid
*
* The account that created this credential
*
* .. php:attr:: username
*
* The username of this Credential object
*
* .. php:attr:: uri
*
* The uri of this Credential object
*/
class Services_Twilio_Rest_Credential extends Services_Twilio_InstanceResource { }

View File

@@ -0,0 +1,42 @@
<?php
/**
* A single CredentialList
*
* .. php:attr:: date_created
*
* The date the credential list was created
*
* .. php:attr:: date_updated
*
* The date the credential list was updated
*
* .. php:attr:: sid
*
* A 34 character string that identifies this object
*
* .. php:attr:: account_sid
*
* The account that created the credential list
*
* .. php:attr:: friendly_name
*
* The friendly name of the credential list
*
* .. php:attr:: uri
*
* The uri of the credential list
*
* .. php:attr:: subresource_uris
*
* The subresources associated with this credential list (Credentials)
*/
class Services_Twilio_Rest_CredentialList extends Services_Twilio_InstanceResource {
protected function init($client, $uri) {
$this->setupSubresources(
'credentials'
);
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* A single CredentialListMapping
*
* .. php:attr:: date_created
*
* The date this mapping was created
*
* .. php:attr:: date_updated
*
* The date this mapping was updated
*
* .. php:attr:: sid
*
* The sid of this mapping
*
* .. php:attr:: friendly_name
*
* The friendly name of this mapping
*
* .. php:attr:: uri
*
* The uri of this mapping
*
* .. php:attr:: subresource_uris
*
* The subresources associated with this mapping (Credentials)
*/
class Services_Twilio_Rest_CredentialListMapping extends Services_Twilio_InstanceResource {
protected function init($client, $uri) {
$this->setupSubresources(
'credentials'
);
}
}

View File

@@ -0,0 +1,24 @@
<?php
class Services_Twilio_Rest_CredentialListMappings extends Services_Twilio_SIPListResource {
/**
* Creates a new CredentialListMapping instance
*
* Example usage:
*
* .. code-block:: php
*
* $client->account->sip->domains->get('SDXXX')->credential_list_mappings->create("CLXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
*
* :param string $credential_list_sid: the sid of the CredentialList you're adding to this domain.
* :param array $params: a single array of parameters which is serialized and
* sent directly to the Twilio API.
*/
public function create($credential_list_sid, $params = array()) {
return parent::_create(array(
'CredentialListSid' => $credential_list_sid,
) + $params);
}
}

View File

@@ -0,0 +1,24 @@
<?php
class Services_Twilio_Rest_CredentialLists extends Services_Twilio_SIPListResource {
/**
* Creates a new CredentialList instance
*
* Example usage:
*
* .. code-block:: php
*
* $client->account->sip->credential_lists->create("MyFriendlyName");
*
* :param string $friendly_name: the friendly name of this credential list
* :param array $params: a single array of parameters which is serialized and
* sent directly to the Twilio API.
*/
public function create($friendly_name, $params = array()) {
return parent::_create(array(
'FriendlyName' => $friendly_name,
) + $params);
}
}

View File

@@ -0,0 +1,28 @@
<?php
class Services_Twilio_Rest_Credentials extends Services_Twilio_SIPListResource {
/**
* Creates a new Credential instance
*
* Example usage:
*
* .. code-block:: php
*
* $client->account->sip->credential_lists->get('CLXXX')->credentials->create(
* "AwesomeUsername", "SuperSecretPassword",
* );
*
* :param string $username: the username for the new Credential object
* :param string $password: the password for the new Credential object
* :param array $params: a single array of parameters which is serialized and
* sent directly to the Twilio API.
*/
public function create($username, $password, $params = array()) {
return parent::_create(array(
'Username' => $username,
'Password' => $password,
) + $params);
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* A single Domain
*
* .. php:attr:: date_created
*
* The date the domain was created
*
* .. php:attr:: date_updated
*
* The date the domain was updated
*
* .. php:attr:: sid
*
* A 34 character string that identifies this object
*
* .. php:attr:: account_sid
*
* The account that created the domain
*
* .. php:attr:: friendly_name
*
* The friendly name of the domain
*
* .. php:attr:: domain_name
*
* The *.sip.twilio domain for the domain
*
* .. php:attr:: auth_type
*
* The auth type used for the domain
*
* .. php:attr:: voice_url
*
* The voice url for the domain
*
* .. php:attr:: voice_fallback_url
*
* The voice fallback url for the domain
*
* .. php:attr:: voice_fallback_method
*
* The voice fallback method for the domain
*
* .. php:attr:: voice_status_callback_url
*
* The voice status callback url for the domain
*
* .. php:attr:: voice_status_callback_method
*
* The voice status_callback_method for the domain
*
* .. php:attr:: uri
*
* The uri of the domain
*
* .. php:attr:: subresource_uris
*
* The subresources associated with this domain (IpAccessControlListMappings, CredentialListMappings)
*
*/
class Services_Twilio_Rest_Domain extends Services_Twilio_InstanceResource {
protected function init($client, $uri) {
$this->setupSubresources(
'ip_access_control_list_mappings',
'credential_list_mappings'
);
}
}

View File

@@ -0,0 +1,28 @@
<?php
class Services_Twilio_Rest_Domains extends Services_Twilio_SIPListResource {
/**
* Creates a new Domain instance
*
* Example usage:
*
* .. code-block:: php
*
* $client->account->sip->domains->create(
* "MyFriendlyName", "MyDomainName"
* );
*
* :param string $friendly_name: the friendly name of this domain
* :param string $domain_name: the domain name for this domain
* :param array $params: a single array of parameters which is serialized and
* sent directly to the Twilio API.
*/
public function create($friendly_name, $domain_name, $params = array()) {
return parent::_create(array(
'FriendlyName' => $friendly_name,
'DomainName' => $domain_name,
) + $params);
}
}

View File

@@ -0,0 +1,91 @@
<?php
/**
* An object representing a single phone number. For more
* information, see the `IncomingPhoneNumber Instance Resource
* <http://www.twilio.com/docs/api/rest/incoming-phone-numbers#instance>`_
* documentation.
*
* .. php:attr:: sid
*
* A 34 character string that uniquely idetifies this resource.
*
* .. php:attr:: date_created
*
* The date that this resource was created, given as GMT RFC 2822 format.
*
* .. php:attr:: date_updated
*
* The date that this resource was last updated, given as GMT RFC 2822 format.
*
* .. php:attr:: friendly_name
*
* A human readable descriptive text for this resource, up to 64
* characters long. By default, the FriendlyName is a nicely formatted
* version of the phone number.
*
* .. php:attr:: account_sid
*
* The unique id of the Account responsible for this phone number.
*
* .. php:attr:: phone_number
*
* The incoming phone number. e.g., +16175551212 (E.164 format)
*
* .. php:attr:: api_version
*
* Calls to this phone number will start a new TwiML session with this
* API version.
*
* .. php:attr:: voice_caller_id_lookup
*
* Look up the caller's caller-ID name from the CNAM database (additional charges apply). Either true or false.
*
* .. php:attr:: voice_url
*
* The URL Twilio will request when this phone number receives a call.
*
* .. php:attr:: voice_method
*
* The HTTP method Twilio will use when requesting the above Url. Either GET or POST.
*
* .. php:attr:: voice_fallback_url
*
* The URL that Twilio will request if an error occurs retrieving or executing the TwiML requested by Url.
*
* .. php:attr:: voice_fallback_method
*
* The HTTP method Twilio will use when requesting the VoiceFallbackUrl. Either GET or POST.
*
* .. php:attr:: status_callback
*
* The URL that Twilio will request to pass status parameters (such as call ended) to your application.
*
* .. php:attr:: status_callback_method
*
* The HTTP method Twilio will use to make requests to the StatusCallback URL. Either GET or POST.
*
* .. php:attr:: sms_url
*
* The URL Twilio will request when receiving an incoming SMS message to this number.
*
* .. php:attr:: sms_method
*
* The HTTP method Twilio will use when making requests to the SmsUrl. Either GET or POST.
*
* .. php:attr:: sms_fallback_url
*
* The URL that Twilio will request if an error occurs retrieving or executing the TwiML from SmsUrl.
*
* .. php:attr:: sms_fallback_method
*
* The HTTP method Twilio will use when requesting the above URL. Either GET or POST.
*
* .. php:attr:: uri
*
* The URI for this resource, relative to https://api.twilio.com.
*/
class Services_Twilio_Rest_IncomingPhoneNumber
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* For more information, see the
* `IncomingPhoneNumbers API Resource
* <http://www.twilio.com/docs/api/rest/incoming-phone-numbers#local>`_
* documentation at twilio.com.
*/
class Services_Twilio_Rest_IncomingPhoneNumbers extends Services_Twilio_ListResource {
function init($client, $uri) {
$this->setupSubresources(
'local',
'toll_free',
'mobile'
);
}
function create(array $params = array()) {
return parent::_create($params);
}
function getList($type, array $params = array())
{
return $this->client->retrieveData($this->uri . "/$type", $params);
}
/**
* Return a phone number instance from its E.164 representation. If more
* than one number matches the search string, returns the first one.
*
* Example usage:
*
* .. code-block:: php
*
* $number = $client->account->incoming_phone_numbers->getNumber('+14105551234');
* echo $number->sid;
*
* :param string $number: The number in E.164 format, eg "+684105551234"
* :return: A :php:class:`Services_Twilio_Rest_IncomingPhoneNumber` object, or null
* :raises: a A :php:class:`Services_Twilio_RestException` if the number is
* invalid, not provided in E.164 format or for any other API exception.
*/
public function getNumber($number) {
$page = $this->getPage(0, 1, array(
'PhoneNumber' => $number
));
$items = $page->getItems();
if (is_null($items) || empty($items)) {
return null;
}
return $items[0];
}
}
class Services_Twilio_Rest_Local extends Services_Twilio_NumberType { }
class Services_Twilio_Rest_Mobile extends Services_Twilio_NumberType { }
class Services_Twilio_Rest_TollFree extends Services_Twilio_NumberType { }

View File

@@ -0,0 +1,40 @@
<?php
/**
* A single IpAccessControlList
*
* .. php:attr:: date_created
*
* The date the ip access control list was created
*
* .. php:attr:: date_updated
*
* The date the ip access control list was updated
*
* .. php:attr:: sid
*
* A 34 character string that identifies this object
*
* .. php:attr:: account_sid
*
* The account that created the ip access control list
*
* .. php:attr:: friendly_name
*
* The friendly name of the ip access control list
*
* .. php:attr:: uri
*
* The uri of the ip access control list
*
* .. php:attr:: subresource_uris
*
* The subresources associated with this ip access control list (IpAddresses)
*/
class Services_Twilio_Rest_IpAccessControlList extends Services_Twilio_InstanceResource {
protected function init($client, $uri) {
$this->setupSubresources(
'ip_addresses'
);
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* A single IpAccessControlListMapping
*
* .. php:attr:: date_created
*
* The date this mapping was created
*
* .. php:attr:: date_updated
*
* The date this mapping was updated
*
* .. php:attr:: sid
*
* The sid of this mapping
*
* .. php:attr:: friendly_name
*
* The friendly name of this mapping
*
* .. php:attr:: uri
*
* The uri of this mapping
*
* .. php:attr:: subresource_uris
*
* The subresources associated with this mapping (IpAddresses)
*/
class Services_Twilio_Rest_IpAccessControlListMapping extends Services_Twilio_InstanceResource {
protected function init($client, $uri) {
$this->setupSubresources(
'ip_addresses'
);
}
}

View File

@@ -0,0 +1,25 @@
<?php
class Services_Twilio_Rest_IpAccessControlListMappings extends Services_Twilio_SIPListResource {
/**
* Creates a new IpAccessControlListMapping instance
*
* Example usage:
*
* .. code-block:: php
*
* $client->account->sip->domains->get('SDXXX')->ip_access_control_list_mappings->create("ALXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
*
* :param string $ip_access_control_list_sid: the sid of the IpAccessControList
* you're adding to this domain.
* :param array $params: a single array of parameters which is serialized and
* sent directly to the Twilio API.
*/
public function create($ip_access_control_list_sid, $params = array()) {
return parent::_create(array(
'IpAccessControlListSid' => $ip_access_control_list_sid,
) + $params);
}
}

View File

@@ -0,0 +1,27 @@
<?php
class Services_Twilio_Rest_IpAccessControlLists extends Services_Twilio_SIPListResource {
/**
* Creates a new IpAccessControlLists instance
*
* Example usage:
*
* .. code-block:: php
*
* $client->account->sip->ip_access_control_lists->create("MyFriendlyName");
*
* :param string $friendly_name: the friendly name of this ip access control list
* :param array $params: a single array of parameters which is serialized and
* sent directly to the Twilio API.
* :return: the created list
* :rtype: :class:`Services_Twilio_Rest_IpAccessControlList`
*
*/
public function create($friendly_name, $params = array()) {
return parent::_create(array(
'FriendlyName' => $friendly_name,
) + $params);
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* A single IpAddress
*
* .. php:attr:: date_created
*
* The date the IpAddress was created
*
* .. php:attr:: date_updated
*
* The date the IpAddress was updated
*
* .. php:attr:: sid
*
* A 34 character string that identifies this object
*
* .. php:attr:: account_sid
*
* The account that created this credential
*
* .. php:attr:: friendly_name
*
* The friendly name of the IpAddress
*
* .. php:attr:: ip_address
*
* The ip address of this IpAddress object
*
* .. php:attr:: uri
*
* The uri of this IpAddress object
*/
class Services_Twilio_Rest_IpAddress extends Services_Twilio_InstanceResource { }

View File

@@ -0,0 +1,33 @@
<?php
class Services_Twilio_Rest_IpAddresses extends Services_Twilio_SIPListResource {
public function __construct($client, $uri) {
$this->instance_name = "Services_Twilio_Rest_IpAddress";
parent::__construct($client, $uri);
}
/**
* Creates a new IpAddress instance
*
* Example usage:
*
* .. code-block:: php
*
* $client->account->sip->ip_access_control_lists->get('ALXXX')->ip_addresses->create(
* "FriendlyName", "127.0.0.1"
* );
*
* :param string $friendly_name: the friendly name for the new IpAddress object
* :param string $ip_address: the ip address for the new IpAddress object
* :param array $params: a single array of parameters which is serialized and
* sent directly to the Twilio API.
*/
public function create($friendly_name, $ip_address, $params = array()) {
return parent::_create(array(
'FriendlyName' => $friendly_name,
'IpAddress' => $ip_address,
) + $params);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* A list of :class:`Media <Services_Twilio_Rest_MediaInstance>` objects.
* For the definitive reference, see the `Twilio Media List Documentation
* <https://www.twilio.com/docs/api/rest/media>`_.
*/
class Services_Twilio_Rest_Media extends Services_Twilio_ListResource {
// This is overridden because the list key in the Twilio response
// is "media_list", not "media".
public function getResourceName($camelized = false)
{
if ($camelized) {
return "MediaList";
} else {
return "media_list";
}
}
// We manually set the instance name here so that the parent
// constructor doesn't attempt to figure out it. It would do it
// incorrectly because we override getResourceName above.
public function __construct($client, $uri) {
$this->instance_name = "Services_Twilio_Rest_MediaInstance";
parent::__construct($client, $uri);
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* A single Media object. For the definitive reference, see the `Twilio Media
* Documentation <https://www.twilio.com/docs/api/rest/media>`_.
*
* .. php:attr:: sid
*
* A 34 character string that identifies this object
*
* .. php:attr:: account_sid
*
* A 34 character string representing the account that sent the message
*
* .. php:attr:: parent_sid
*
* The sid of the message that created this media.
*
* .. php:attr:: date_created
*
* The date the message was created
*
* .. php:attr:: date_updated
*
* The date the message was updated
*
* .. php:attr:: content_type
*
* The content-type of the media.
*/
class Services_Twilio_Rest_MediaInstance extends Services_Twilio_InstanceResource {
public function __construct($client, $uri) {
$uri = str_replace('MediaInstance', 'Media', $uri);
parent::__construct($client, $uri);
}
}

View File

@@ -0,0 +1,22 @@
<?php
class Services_Twilio_Rest_Member
extends Services_Twilio_InstanceResource
{
/**
* Dequeue this member
*
* @param string $url The Twiml URL to play for this member, after
* dequeueing them
* @param string $method The HTTP method to use when fetching the Twiml
* URL. Defaults to POST.
* @return Services_Twilio_Rest_Member The dequeued member
*/
public function dequeue($url, $method = 'POST') {
return self::update(array(
'Url' => $url,
'Method' => $method,
));
}
}

View File

@@ -0,0 +1,28 @@
<?php
class Services_Twilio_Rest_Members
extends Services_Twilio_ListResource
{
/**
* Return the member at the front of the queue. Note that any operations
* performed on the Member returned from this function will use the /Front
* Uri, not the Member's CallSid.
*
* @return Services_Twilio_Rest_Member The member at the front of the queue
*/
public function front() {
return new $this->instance_name($this->client, $this->uri . '/Front');
}
/* Participants are identified by CallSid, not like ME123 */
public function getObjectFromJson($params, $idParam = 'sid') {
return parent::getObjectFromJson($params, 'call_sid');
}
public function getResourceName($camelized = false)
{
// The JSON property name is atypical.
return $camelized ? 'Members' : 'queue_members';
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* A single Message
*
* .. php:attr:: date_created
*
* The date the message was created
*
* .. php:attr:: date_updated
*
* The date the message was updated
*
* .. php:attr:: sid
*
* A 34 character string that identifies this object
*
* .. php:attr:: account_sid
*
* The account that sent the message
*
* .. php:attr:: body
*
* The body of the message
*
* .. php:attr:: num_segments
*
* The number of sms messages used to deliver the body
*
* .. php:attr:: num_media
*
* The number of media that are associated with the image
*
* .. php:attr:: subresource_uris
*
* The subresources associated with this message (just Media at the moment)
*
* .. php:attr:: from
*
* The number this message was sent from
*
* .. php:attr:: to
*
* The phone number this message was sent to
*/
class Services_Twilio_Rest_Message extends Services_Twilio_InstanceResource {
protected function init($client, $uri) {
$this->setupSubresources(
'media'
);
}
}

View File

@@ -0,0 +1,73 @@
<?php
class Services_Twilio_Rest_Messages extends Services_Twilio_ListResource {
/**
* Create a new Message instance
*
* Example usage:
*
* .. code-block:: php
*
* $client->account->messages->create(array(
* "Body" => "foo",
* "From" => "+14105551234",
* "To" => "+14105556789",
* ));
*
* :param array $params: a single array of parameters which is serialized and
* sent directly to the Twilio API. You may find it easier to use the
* sendMessage helper instead of this function.
*
*/
public function create($params = array()) {
return parent::_create($params);
}
/**
* Send a message
*
* .. code-block:: php
*
* $client = new Services_Twilio('AC123', '123');
* $message = $client->account->messages->sendMessage(
* '+14105551234', // From a Twilio number in your account
* '+14105556789', // Text any number
* 'Come at the king, you best not miss.' // Message body (if any)
* array('https://demo.twilio.com/owl.png'), // An array of MediaUrls
* );
*
* :param string $from: the from number for the message, this must be a
* number you purchased from Twilio
* :param string $to: the message recipient's phone number
* :param $mediaUrls: the URLs of images to send in this MMS
* :type $mediaUrls: null (don't include media), a single URL, or an array
* of URLs to send as media with this message
* :param string $body: the text to include along with this MMS
* :param array $params: Any additional params (callback, etc) you'd like to
* send with this request, these are serialized and sent as POST
* parameters
*
* :return: The created :class:`Services_Twilio_Rest_Message`
* :raises: :class:`Services_Twilio_RestException`
* An exception if the parameters are invalid (for example, the from
* number is not a Twilio number registered to your account, or is
* unable to send MMS)
*/
public function sendMessage($from, $to, $body = null, $mediaUrls = null,
$params = array()
) {
$postParams = array(
'From' => $from,
'To' => $to,
);
// When the request is made, this will get serialized into MediaUrl=a&MediaUrl=b
if (!is_null($mediaUrls)) {
$postParams['MediaUrl'] = $mediaUrls;
}
if (!is_null($body)) {
$postParams['Body'] = $body;
}
return self::create($postParams + $params);
}
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_Notification
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_Notifications
extends Services_Twilio_ListResource
{
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_OutgoingCallerId
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,12 @@
<?php
class Services_Twilio_Rest_OutgoingCallerIds
extends Services_Twilio_ListResource
{
public function create($phoneNumber, array $params = array())
{
return parent::_create(array(
'PhoneNumber' => $phoneNumber,
) + $params);
}
}

View File

@@ -0,0 +1,10 @@
<?php
class Services_Twilio_Rest_Participant
extends Services_Twilio_InstanceResource
{
public function mute()
{
$this->update('Muted', 'true');
}
}

View File

@@ -0,0 +1,10 @@
<?php
class Services_Twilio_Rest_Participants
extends Services_Twilio_ListResource
{
/* Participants are identified by CallSid, not like PI123 */
public function getObjectFromJson($params, $idParam = "sid") {
return parent::getObjectFromJson($params, "call_sid");
}
}

View File

@@ -0,0 +1,10 @@
<?php
class Services_Twilio_Rest_Queue
extends Services_Twilio_InstanceResource {
protected function init($client, $uri) {
$this->setupSubresources('members');
}
}

View File

@@ -0,0 +1,19 @@
<?php
class Services_Twilio_Rest_Queues
extends Services_Twilio_ListResource
{
/**
* Create a new Queue
*
* @param string $friendly_name The name of this queue
* @param array $params A list of optional parameters, and their values
* @return Services_Twilio_Rest_Queue The created Queue
*/
function create($friendly_name, array $params = array()) {
return parent::_create(array(
'FriendlyName' => $friendly_name,
) + $params);
}
}

View File

@@ -0,0 +1,9 @@
<?php
class Services_Twilio_Rest_Recording
extends Services_Twilio_InstanceResource
{
protected function init($client, $uri) {
$this->setupSubresources('transcriptions');
}
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_Recordings
extends Services_Twilio_ListResource
{
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_Sandbox
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_ShortCode
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,10 @@
<?php
class Services_Twilio_Rest_ShortCodes
extends Services_Twilio_ListResource
{
public function __construct($client, $uri) {
$uri = preg_replace("#ShortCodes#", "SMS/ShortCodes", $uri);
parent::__construct($client, $uri);
}
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* For Linux filename compatibility, this file needs to be named Sip.php, or
* camelize() needs to be special cased in setupSubresources
*/
class Services_Twilio_Rest_SIP extends Services_Twilio_InstanceResource {
protected function init($client, $uri) {
$this->setupSubresources(
'domains',
'ip_access_control_lists',
'credential_lists'
);
}
public function getResourceName($camelized = false) {
return "SIP";
}
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_SmsMessage
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,18 @@
<?php
class Services_Twilio_Rest_SmsMessages
extends Services_Twilio_ListResource
{
public function __construct($client, $uri) {
$uri = preg_replace("#SmsMessages#", "SMS/Messages", $uri);
parent::__construct($client, $uri);
}
function create($from, $to, $body, array $params = array()) {
return parent::_create(array(
'From' => $from,
'To' => $to,
'Body' => $body
) + $params);
}
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_Transcription
extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_Transcriptions
extends Services_Twilio_ListResource
{
}

View File

@@ -0,0 +1,6 @@
<?php
class Services_Twilio_Rest_UsageRecord extends Services_Twilio_InstanceResource
{
}

View File

@@ -0,0 +1,33 @@
<?php
class Services_Twilio_Rest_UsageRecords extends Services_Twilio_TimeRangeResource {
public function init($client, $uri) {
$this->setupSubresources(
'today',
'yesterday',
'all_time',
'this_month',
'last_month',
'daily',
'monthly',
'yearly'
);
}
}
class Services_Twilio_Rest_Today extends Services_Twilio_TimeRangeResource { }
class Services_Twilio_Rest_Yesterday extends Services_Twilio_TimeRangeResource { }
class Services_Twilio_Rest_LastMonth extends Services_Twilio_TimeRangeResource { }
class Services_Twilio_Rest_ThisMonth extends Services_Twilio_TimeRangeResource { }
class Services_Twilio_Rest_AllTime extends Services_Twilio_TimeRangeResource { }
class Services_Twilio_Rest_Daily extends Services_Twilio_UsageResource { }
class Services_Twilio_Rest_Monthly extends Services_Twilio_UsageResource { }
class Services_Twilio_Rest_Yearly extends Services_Twilio_UsageResource { }

View File

@@ -0,0 +1,5 @@
<?php
class Services_Twilio_Rest_UsageTrigger
extends Services_Twilio_InstanceResource { }

View File

@@ -0,0 +1,27 @@
<?php
class Services_Twilio_Rest_UsageTriggers extends Services_Twilio_ListResource {
public function __construct($client, $uri) {
$uri = preg_replace("#UsageTriggers#", "Usage/Triggers", $uri);
parent::__construct($client, $uri);
}
/**
* Create a new UsageTrigger
* @param string $category The category of usage to fire a trigger for. A full list of categories can be found in the `Usage Categories documentation <http://www.twilio.com/docs/api/rest/usage-records#usage-categories>`_.
* @param string $value Fire the trigger when usage crosses this value.
* @param string $url The URL to request when the trigger fires.
* @param array $params Optional parameters for this trigger. A full list of parameters can be found in the `Usage Trigger documentation <http://www.twilio.com/docs/api/rest/usage-triggers#list-post-optional-parameters>`_.
* @return Services_Twilio_Rest_UsageTrigger The created trigger
*/
function create($category, $value, $url, array $params = array()) {
return parent::_create(array(
'UsageCategory' => $category,
'TriggerValue' => $value,
'CallbackUrl' => $url,
) + $params);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* An exception talking to the Twilio API. This is thrown whenever the Twilio
* API returns a 400 or 500-level exception.
*
* :param int $status: the HTTP status for the exception
* :param string $message: a human-readable error message for the exception
* :param int $code: a Twilio-specific error code for the exception
* :param string $info: a link to more information
*/
class Services_Twilio_RestException extends Exception {
/**
* The HTTP status for the exception.
*/
protected $status;
/**
* A URL to get more information about the error. This is not always
* available
*/
protected $info;
public function __construct($status, $message, $code = 0, $info = '') {
$this->status = $status;
$this->info = $info;
parent::__construct($message, $code);
}
/**
* Get the HTTP status code
*/
public function getStatus() {
return $this->status;
}
/**
* Get a link to more information
*/
public function getInfo() {
return $this->info;
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* This subclass of ListResource is used solely to update
* the URI for sip resources.
*/
abstract class Services_Twilio_SIPListResource extends Services_Twilio_ListResource {
public function __construct($client, $uri) {
// Rename all /Sip/ uris to /SIP/
$uri = preg_replace("#/Sip#", "/SIP", $uri);
parent::__construct($client, $uri);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* Parent class for usage resources that expose a single date, eg 'Today', 'ThisMonth', etc
* @author Kevin Burke <kevin@twilio.com>
* @license http://creativecommons.org/licenses/MIT/ MIT
* @link http://pear.php.net/package/Services_Twilio
*/
class Services_Twilio_TimeRangeResource extends Services_Twilio_UsageResource {
/**
* Return a UsageRecord corresponding to the given category.
*
* @param string $category The category of usage to retrieve. For a full
* list of valid categories, please see the documentation at
* http://www.twilio.com/docs/api/rest/usage-records#usage-all-categories
* @return Services_Twilio_Rest_UsageRecord
* @throws Services_Twilio_RestException
*/
public function getCategory($category) {
$page = $this->getPage(0, 1, array(
'Category' => $category,
));
$items = $page->getItems();
if (!is_array($items) || count($items) === 0) {
throw new Services_Twilio_RestException(
400, "Usage record data is unformattable.");
}
return $items[0];
}
}

View File

@@ -0,0 +1,126 @@
<?php
/**
* Based on TinyHttp from https://gist.github.com/618157.
* Copyright 2011, Neuman Vong. BSD License.
*/
class Services_Twilio_TinyHttpException extends ErrorException {}
/**
* An HTTP client that makes requests
*
* :param string $uri: The base uri to use for requests
* :param array $kwargs: An array of additional arguments to pass to the
* library. Accepted arguments are:
*
* - **debug** - Print the HTTP request before making it to Twilio
* - **curlopts** - An array of keys and values that are passed to
* ``curl_setopt_array``.
*
* Here's an example. This is the default HTTP client used by the library.
*
* .. code-block:: php
*
* $_http = new Services_Twilio_TinyHttp(
* "https://api.twilio.com",
* array("curlopts" => array(
* CURLOPT_USERAGENT => self::USER_AGENT,
* CURLOPT_HTTPHEADER => array('Accept-Charset: utf-8'),
* CURLOPT_CAINFO => dirname(__FILE__) . '/cacert.pem',
* ))
* );
*/
class Services_Twilio_TinyHttp {
var $user, $pass, $scheme, $host, $port, $debug, $curlopts;
public function __construct($uri = '', $kwargs = array()) {
foreach (parse_url($uri) as $name => $value) $this->$name = $value;
$this->debug = isset($kwargs['debug']) ? !!$kwargs['debug'] : NULL;
$this->curlopts = isset($kwargs['curlopts']) ? $kwargs['curlopts'] : array();
}
public function __call($name, $args) {
list($res, $req_headers, $req_body) = $args + array(0, array(), '');
$opts = $this->curlopts + array(
CURLOPT_URL => "$this->scheme://$this->host$res",
CURLOPT_HEADER => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_INFILESIZE => -1,
CURLOPT_POSTFIELDS => NULL,
CURLOPT_TIMEOUT => 60,
);
foreach ($req_headers as $k => $v) $opts[CURLOPT_HTTPHEADER][] = "$k: $v";
if ($this->port) $opts[CURLOPT_PORT] = $this->port;
if ($this->debug) $opts[CURLINFO_HEADER_OUT] = TRUE;
if ($this->user && $this->pass) $opts[CURLOPT_USERPWD] = "$this->user:$this->pass";
switch ($name) {
case 'get':
$opts[CURLOPT_HTTPGET] = TRUE;
break;
case 'post':
$opts[CURLOPT_POST] = TRUE;
$opts[CURLOPT_POSTFIELDS] = $req_body;
break;
case 'put':
$opts[CURLOPT_PUT] = TRUE;
if (strlen($req_body)) {
if ($buf = fopen('php://memory', 'w+')) {
fwrite($buf, $req_body);
fseek($buf, 0);
$opts[CURLOPT_INFILE] = $buf;
$opts[CURLOPT_INFILESIZE] = strlen($req_body);
} else throw new Services_Twilio_TinyHttpException('unable to open temporary file');
}
break;
case 'head':
$opts[CURLOPT_NOBODY] = TRUE;
break;
default:
$opts[CURLOPT_CUSTOMREQUEST] = strtoupper($name);
break;
}
try {
if ($curl = curl_init()) {
if (curl_setopt_array($curl, $opts)) {
if ($response = curl_exec($curl)) {
$parts = explode("\r\n\r\n", $response, 3);
list($head, $body) = ($parts[0] == 'HTTP/1.1 100 Continue')
? array($parts[1], $parts[2])
: array($parts[0], $parts[1]);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($this->debug) {
error_log(
curl_getinfo($curl, CURLINFO_HEADER_OUT) .
$req_body
);
}
$header_lines = explode("\r\n", $head);
array_shift($header_lines);
foreach ($header_lines as $line) {
list($key, $value) = explode(":", $line, 2);
$headers[$key] = trim($value);
}
curl_close($curl);
if (isset($buf) && is_resource($buf)) {
fclose($buf);
}
return array($status, $headers, $body);
} else {
throw new Services_Twilio_TinyHttpException(curl_error($curl));
}
} else throw new Services_Twilio_TinyHttpException(curl_error($curl));
} else throw new Services_Twilio_TinyHttpException('unable to initialize cURL');
} catch (ErrorException $e) {
if (is_resource($curl)) curl_close($curl);
if (isset($buf) && is_resource($buf)) fclose($buf);
throw $e;
}
}
public function authenticate($user, $pass) {
$this->user = $user;
$this->pass = $pass;
}
}

137
externals/twilio-php/Services/Twilio/Twiml.php vendored Executable file
View File

@@ -0,0 +1,137 @@
<?php
/**
* Exception class for Services_Twilio_Twiml.
*/
class Services_Twilio_TwimlException extends Exception {}
/**
* Twiml response generator.
*
* Author: Neuman Vong <neuman at ashmoremusic dot com>
* License: http://creativecommons.org/licenses/MIT/ MIT
*/
class Services_Twilio_Twiml {
protected $element;
/**
* Constructs a Twiml response.
*
* :param SimpleXmlElement|array $arg: Can be any of
*
* - the element to wrap
* - attributes to add to the element
* - if null, initialize an empty element named 'Response'
*/
public function __construct($arg = null) {
switch (true) {
case $arg instanceof SimpleXmlElement:
$this->element = $arg;
break;
case $arg === null:
$this->element = new SimpleXmlElement('<Response/>');
break;
case is_array($arg):
$this->element = new SimpleXmlElement('<Response/>');
foreach ($arg as $name => $value) {
$this->element->addAttribute($name, $value);
}
break;
default:
throw new TwimlException('Invalid argument');
}
}
/**
* Converts method calls into Twiml verbs.
*
* A basic example:
*
* .. code-block:: php
*
* php> print $this->say('hello');
* <Say>hello</Say>
*
* An example with attributes:
*
* .. code-block:: php
*
* print $this->say('hello', array('voice' => 'woman'));
* <Say voice="woman">hello</Say>
*
* You could even just pass in an attributes array, omitting the noun:
*
* .. code-block:: php
*
* print $this->gather(array('timeout' => '20'));
* <Gather timeout="20"/>
*
* :param string $verb: The Twiml verb.
* :param array $args:
* - (noun string)
* - (noun string, attributes array)
* - (attributes array)
*
* :return: A SimpleXmlElement
* :rtype: SimpleXmlElement
*/
public function __call($verb, array $args)
{
list($noun, $attrs) = $args + array('', array());
if (is_array($noun)) {
list($attrs, $noun) = array($noun, '');
}
/* addChild does not escape XML, while addAttribute does. This means if
* you pass unescaped ampersands ("&") to addChild, you will generate
* an error.
*
* Some inexperienced developers will pass in unescaped ampersands, and
* we want to make their code work, by escaping the ampersands for them
* before passing the string to addChild. (with htmlentities)
*
* However other people will know what to do, and their code
* already escapes ampersands before passing them to addChild. We don't
* want to break their existing code by turning their &amp;'s into
* &amp;amp;
*
* We also want to use numeric entities, not named entities so that we
* are fully compatible with XML
*
* The following lines accomplish the desired behavior.
*/
$decoded = html_entity_decode($noun, ENT_COMPAT, 'UTF-8');
$normalized = htmlspecialchars($decoded, ENT_COMPAT, 'UTF-8', false);
$child = empty($noun)
? $this->element->addChild(ucfirst($verb))
: $this->element->addChild(ucfirst($verb), $normalized);
foreach ($attrs as $name => $value) {
/* Note that addAttribute escapes raw ampersands by default, so we
* haven't touched its implementation. So this is the matrix for
* addAttribute:
*
* & turns into &amp;
* &amp; turns into &amp;amp;
*/
if (is_bool($value)) {
$value = ($value === true) ? 'true' : 'false';
}
$child->addAttribute($name, $value);
}
return new static($child);
}
/**
* Returns the object as XML.
*
* :return: The response as an XML string
* :rtype: string
*/
public function __toString()
{
$xml = $this->element->asXml();
return str_replace(
'<?xml version="1.0"?>',
'<?xml version="1.0" encoding="UTF-8"?>', $xml);
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Parent class for all UsageRecord subclasses
* @author Kevin Burke <kevin@twilio.com>
* @license http://creativecommons.org/licenses/MIT/ MIT
* @link http://pear.php.net/package/Services_Twilio
*/
class Services_Twilio_UsageResource extends Services_Twilio_ListResource {
public function getResourceName($camelized = false) {
$this->instance_name = 'Services_Twilio_Rest_UsageRecord';
return $camelized ? 'UsageRecords' : 'usage_records';
}
public function __construct($client, $uri) {
$uri = preg_replace("#UsageRecords#", "Usage/Records", $uri);
parent::__construct($client, $uri);
}
}

3849
externals/twilio-php/Services/cacert.pem vendored Executable file

File diff suppressed because it is too large Load Diff