581 lines
18 KiB
PHP
581 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* Zend Framework
|
|
*
|
|
* LICENSE
|
|
*
|
|
* This source file is subject to the new BSD license that is bundled
|
|
* with this package in the file LICENSE.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* http://framework.zend.com/license/new-bsd
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@zend.com so we can send you a copy immediately.
|
|
*
|
|
* @category Zend
|
|
* @package Zend_Service_Amazon
|
|
* @subpackage SimpleDb
|
|
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
*/
|
|
|
|
/**
|
|
* @see Zend_Service_Amazon_Abstract
|
|
*/
|
|
require_once 'Zend/Service/Amazon/Abstract.php';
|
|
|
|
/**
|
|
* @see Zend_Service_Amazon_SimpleDb_Response
|
|
*/
|
|
require_once 'Zend/Service/Amazon/SimpleDb/Response.php';
|
|
|
|
/**
|
|
* @see Zend_Service_Amazon_SimpleDb_Page
|
|
*/
|
|
require_once 'Zend/Service/Amazon/SimpleDb/Page.php';
|
|
|
|
/**
|
|
* @see Zend_Service_Amazon_SimpleDb_Attribute
|
|
*/
|
|
require_once 'Zend/Service/Amazon/SimpleDb/Attribute.php';
|
|
|
|
/**
|
|
* @see Zend_Service_Amazon_SimpleDb_Exception
|
|
*/
|
|
require_once 'Zend/Service/Amazon/SimpleDb/Exception.php';
|
|
|
|
/**
|
|
* @see Zend_Crypt_Hmac
|
|
*/
|
|
require_once 'Zend/Crypt/Hmac.php';
|
|
|
|
/**
|
|
* @category Zend
|
|
* @package Zend_Service_Amazon
|
|
* @subpackage SimpleDb
|
|
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
*/
|
|
class Zend_Service_Amazon_SimpleDb extends Zend_Service_Amazon_Abstract
|
|
{
|
|
/* Notes */
|
|
// TODO SSL is required
|
|
|
|
/**
|
|
* The HTTP query server
|
|
*/
|
|
protected $_sdbEndpoint = 'sdb.amazonaws.com/';
|
|
|
|
/**
|
|
* Period after which HTTP request will timeout in seconds
|
|
*/
|
|
protected $_httpTimeout = 10;
|
|
|
|
/**
|
|
* The API version to use
|
|
*/
|
|
protected $_sdbApiVersion = '2009-04-15';
|
|
|
|
/**
|
|
* Signature Version
|
|
*/
|
|
protected $_signatureVersion = '2';
|
|
|
|
/**
|
|
* Signature Encoding Method
|
|
*/
|
|
protected $_signatureMethod = 'HmacSHA256';
|
|
|
|
/**
|
|
* Create Amazon SimpleDB client.
|
|
*
|
|
* @param string $access_key Override the default Access Key
|
|
* @param string $secret_key Override the default Secret Key
|
|
* @param string $region Sets the AWS Region
|
|
* @return void
|
|
*/
|
|
public function __construct($accessKey, $secretKey)
|
|
{
|
|
parent::__construct($accessKey, $secretKey);
|
|
$this->setEndpoint("https://" . $this->_sdbEndpoint);
|
|
}
|
|
|
|
/**
|
|
* Set SimpleDB endpoint to use
|
|
*
|
|
* @param string|Zend_Uri_Http $endpoint
|
|
* @return Zend_Service_Amazon_SimpleDb
|
|
*/
|
|
public function setEndpoint($endpoint)
|
|
{
|
|
if(!($endpoint instanceof Zend_Uri_Http)) {
|
|
$endpoint = Zend_Uri::factory($endpoint);
|
|
}
|
|
if(!$endpoint->valid()) {
|
|
require_once 'Zend/Service/Amazon/SimpleDb/Exception.php';
|
|
throw new Zend_Service_Amazon_SimpleDb_Exception("Invalid endpoint supplied");
|
|
}
|
|
$this->_endpoint = $endpoint;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get SimpleDB endpoint
|
|
*
|
|
* @return Zend_Uri_Http
|
|
*/
|
|
public function getEndpoint()
|
|
{
|
|
return $this->_endpoint;
|
|
}
|
|
|
|
/**
|
|
* Get attributes API method
|
|
*
|
|
* @param string $domainName Domain name within database
|
|
* @param string
|
|
*/
|
|
public function getAttributes(
|
|
$domainName, $itemName, $attributeName = null
|
|
) {
|
|
$params = array();
|
|
$params['Action'] = 'GetAttributes';
|
|
$params['DomainName'] = $domainName;
|
|
$params['ItemName'] = $itemName;
|
|
|
|
if (isset($attributeName)) {
|
|
$params['AttributeName'] = $attributeName;
|
|
}
|
|
|
|
$response = $this->_sendRequest($params);
|
|
$document = $response->getSimpleXMLDocument();
|
|
|
|
$attributeNodes = $document->GetAttributesResult->Attribute;
|
|
|
|
// Return an array of arrays
|
|
$attributes = array();
|
|
foreach($attributeNodes as $attributeNode) {
|
|
$name = (string)$attributeNode->Name;
|
|
$valueNodes = $attributeNode->Value;
|
|
$data = null;
|
|
if (is_array($valueNodes) && !empty($valueNodes)) {
|
|
$data = array();
|
|
foreach($valueNodes as $valueNode) {
|
|
$data[] = (string)$valueNode;
|
|
}
|
|
} elseif (isset($valueNodes)) {
|
|
$data = (string)$valueNodes;
|
|
}
|
|
if (isset($attributes[$name])) {
|
|
$attributes[$name]->addValue($data);
|
|
} else {
|
|
$attributes[$name] = new Zend_Service_Amazon_SimpleDb_Attribute($itemName, $name, $data);
|
|
}
|
|
}
|
|
return $attributes;
|
|
}
|
|
|
|
/**
|
|
* Push attributes
|
|
*
|
|
* @param string $domainName
|
|
* @param string $itemName
|
|
* @param array|Traverable $attributes
|
|
* @param array $replace
|
|
* @return void
|
|
*/
|
|
public function putAttributes(
|
|
$domainName, $itemName, $attributes, $replace = array()
|
|
) {
|
|
$params = array();
|
|
$params['Action'] = 'PutAttributes';
|
|
$params['DomainName'] = $domainName;
|
|
$params['ItemName'] = $itemName;
|
|
|
|
$index = 0;
|
|
foreach ($attributes as $attribute) {
|
|
$attributeName = $attribute->getName();
|
|
foreach ($attribute->getValues() as $value) {
|
|
$params['Attribute.' . $index . '.Name'] = $attributeName;
|
|
$params['Attribute.' . $index . '.Value'] = $value;
|
|
|
|
// Check if it should be replaced
|
|
if(array_key_exists($attributeName, $replace) && $replace[$attributeName]) {
|
|
$params['Attribute.' . $index . '.Replace'] = 'true';
|
|
}
|
|
$index++;
|
|
}
|
|
}
|
|
|
|
// Exception should get thrown if there's an error
|
|
$response = $this->_sendRequest($params);
|
|
}
|
|
|
|
/**
|
|
* Add many attributes at once
|
|
*
|
|
* @param array $items
|
|
* @param string $domainName
|
|
* @param array $replace
|
|
* @return void
|
|
*/
|
|
public function batchPutAttributes($items, $domainName, array $replace = array())
|
|
{
|
|
|
|
$params = array();
|
|
$params['Action'] = 'BatchPutAttributes';
|
|
$params['DomainName'] = $domainName;
|
|
|
|
$itemIndex = 0;
|
|
foreach ($items as $name => $attributes) {
|
|
$params['Item.' . $itemIndex . '.ItemName'] = $name;
|
|
$attributeIndex = 0;
|
|
foreach ($attributes as $attribute) {
|
|
// attribute value cannot be array, so when several items are passed
|
|
// they are treated as separate values with the same attribute name
|
|
foreach($attribute->getValues() as $value) {
|
|
$params['Item.' . $itemIndex . '.Attribute.' . $attributeIndex . '.Name'] = $attribute->getName();
|
|
$params['Item.' . $itemIndex . '.Attribute.' . $attributeIndex . '.Value'] = $value;
|
|
if (isset($replace[$name])
|
|
&& isset($replace[$name][$attribute->getName()])
|
|
&& $replace[$name][$attribute->getName()]
|
|
) {
|
|
$params['Item.' . $itemIndex . '.Attribute.' . $attributeIndex . '.Replace'] = 'true';
|
|
}
|
|
$attributeIndex++;
|
|
}
|
|
}
|
|
$itemIndex++;
|
|
}
|
|
|
|
$response = $this->_sendRequest($params);
|
|
}
|
|
|
|
/**
|
|
* Delete attributes
|
|
*
|
|
* @param string $domainName
|
|
* @param string $itemName
|
|
* @param array $attributes
|
|
* @return void
|
|
*/
|
|
public function deleteAttributes($domainName, $itemName, array $attributes = array())
|
|
{
|
|
$params = array();
|
|
$params['Action'] = 'DeleteAttributes';
|
|
$params['DomainName'] = $domainName;
|
|
$params['ItemName'] = $itemName;
|
|
|
|
$attributeIndex = 0;
|
|
foreach ($attributes as $attribute) {
|
|
foreach ($attribute->getValues() as $value) {
|
|
$params['Attribute.' . $attributeIndex . '.Name'] = $attribute->getName();
|
|
$params['Attribute.' . $attributeIndex . '.Value'] = $value;
|
|
$attributeIndex++;
|
|
}
|
|
}
|
|
|
|
$response = $this->_sendRequest($params);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* List domains
|
|
*
|
|
* @param int $maxNumberOfDomains
|
|
* @param int $nextToken
|
|
* @return array 0 or more domain names
|
|
*/
|
|
public function listDomains($maxNumberOfDomains = 100, $nextToken = null)
|
|
{
|
|
$params = array();
|
|
$params['Action'] = 'ListDomains';
|
|
$params['MaxNumberOfDomains'] = $maxNumberOfDomains;
|
|
|
|
if (null !== $nextToken) {
|
|
$params['NextToken'] = $nextToken;
|
|
}
|
|
$response = $this->_sendRequest($params);
|
|
|
|
$domainNodes = $response->getSimpleXMLDocument()->ListDomainsResult->DomainName;
|
|
|
|
$data = array();
|
|
foreach ($domainNodes as $domain) {
|
|
$data[] = (string)$domain;
|
|
}
|
|
|
|
$nextTokenNode = $response->getSimpleXMLDocument()->ListDomainsResult->NextToken;
|
|
$nextToken = (string)$nextTokenNode;
|
|
$nextToken = (trim($nextToken) === '') ? null : $nextToken;
|
|
|
|
return new Zend_Service_Amazon_SimpleDb_Page($data, $nextToken);
|
|
}
|
|
|
|
/**
|
|
* Retrieve domain metadata
|
|
*
|
|
* @param string $domainName Name of the domain for which metadata will be requested
|
|
* @return array Key/value array of metadatum names and values.
|
|
*/
|
|
public function domainMetadata($domainName)
|
|
{
|
|
$params = array();
|
|
$params['Action'] = 'DomainMetadata';
|
|
$params['DomainName'] = $domainName;
|
|
$response = $this->_sendRequest($params);
|
|
|
|
$document = $response->getSimpleXMLDocument();
|
|
|
|
$metadataNodes = $document->DomainMetadataResult->children();
|
|
$metadata = array();
|
|
foreach ($metadataNodes as $metadataNode) {
|
|
$name = $metadataNode->getName();
|
|
$metadata[$name] = (string)$metadataNode;
|
|
}
|
|
|
|
return $metadata;
|
|
}
|
|
|
|
/**
|
|
* Create a new domain
|
|
*
|
|
* @param string $domainName Valid domain name of the domain to create
|
|
* @return boolean True if successful, false if not
|
|
*/
|
|
public function createDomain($domainName)
|
|
{
|
|
$params = array();
|
|
$params['Action'] = 'CreateDomain';
|
|
$params['DomainName'] = $domainName;
|
|
$response = $this->_sendRequest($params);
|
|
return $response->getHttpResponse()->isSuccessful();
|
|
}
|
|
|
|
/**
|
|
* Delete a domain
|
|
*
|
|
* @param string $domainName Valid domain name of the domain to delete
|
|
* @return boolean True if successful, false if not
|
|
*/
|
|
public function deleteDomain($domainName)
|
|
{
|
|
$params = array();
|
|
$params['Action'] = 'DeleteDomain';
|
|
$params['DomainName'] = $domainName;
|
|
$response = $this->_sendRequest($params);
|
|
return $response->getHttpResponse()->isSuccessful();
|
|
}
|
|
|
|
/**
|
|
* Select items from the database
|
|
*
|
|
* @param string $selectExpression
|
|
* @param null|string $nextToken
|
|
* @return Zend_Service_Amazon_SimpleDb_Page
|
|
*/
|
|
public function select($selectExpression, $nextToken = null)
|
|
{
|
|
$params = array();
|
|
$params['Action'] = 'Select';
|
|
$params['SelectExpression'] = $selectExpression;
|
|
|
|
if (null !== $nextToken) {
|
|
$params['NextToken'] = $nextToken;
|
|
}
|
|
|
|
$response = $this->_sendRequest($params);
|
|
$xml = $response->getSimpleXMLDocument();
|
|
|
|
$attributes = array();
|
|
foreach ($xml->SelectResult->Item as $item) {
|
|
$itemName = (string)$item->Name;
|
|
|
|
foreach ($item->Attribute as $attribute) {
|
|
$attributeName = (string)$attribute->Name;
|
|
|
|
$values = array();
|
|
foreach ($attribute->Value as $value) {
|
|
$values[] = (string)$value;
|
|
}
|
|
$attributes[$itemName][$attributeName] = new Zend_Service_Amazon_SimpleDb_Attribute($itemName, $attributeName, $values);
|
|
}
|
|
}
|
|
|
|
$nextToken = (string)$xml->NextToken;
|
|
|
|
return new Zend_Service_Amazon_SimpleDb_Page($attributes, $nextToken);
|
|
}
|
|
|
|
/**
|
|
* Quote SDB value
|
|
*
|
|
* Wraps it in ''
|
|
*
|
|
* @param string $value
|
|
* @return string
|
|
*/
|
|
public function quote($value)
|
|
{
|
|
// wrap in single quotes and convert each ' inside to ''
|
|
return "'" . str_replace("'", "''", $value) . "'";
|
|
}
|
|
|
|
/**
|
|
* Quote SDB column or table name
|
|
*
|
|
* Wraps it in ``
|
|
* @param string $name
|
|
* @return string
|
|
*/
|
|
public function quoteName($name)
|
|
{
|
|
if (preg_match('/^[a-z_$][a-z0-9_$-]*$/i', $name) == false) {
|
|
throw new Zend_Service_Amazon_SimpleDb_Exception("Invalid name: can contain only alphanumeric characters, \$ and _");
|
|
}
|
|
return "`$name`";
|
|
}
|
|
|
|
/**
|
|
* Sends a HTTP request to the SimpleDB service using Zend_Http_Client
|
|
*
|
|
* @param array $params List of parameters to send with the request
|
|
* @return Zend_Service_Amazon_SimpleDb_Response
|
|
* @throws Zend_Service_Amazon_SimpleDb_Exception
|
|
*/
|
|
protected function _sendRequest(array $params = array())
|
|
{
|
|
// UTF-8 encode all parameters and replace '+' characters
|
|
foreach ($params as $name => $value) {
|
|
unset($params[$name]);
|
|
$params[utf8_encode($name)] = $value;
|
|
}
|
|
|
|
$params = $this->_addRequiredParameters($params);
|
|
|
|
try {
|
|
/* @var $request Zend_Http_Client */
|
|
$request = self::getHttpClient();
|
|
$request->resetParameters();
|
|
|
|
$request->setConfig(array(
|
|
'timeout' => $this->_httpTimeout
|
|
));
|
|
|
|
|
|
$request->setUri($this->getEndpoint());
|
|
$request->setMethod(Zend_Http_Client::POST);
|
|
foreach ($params as $key => $value) {
|
|
$params_out[] = rawurlencode($key)."=".rawurlencode($value);
|
|
}
|
|
$request->setRawData(join('&', $params_out), Zend_Http_Client::ENC_URLENCODED);
|
|
$httpResponse = $request->request();
|
|
} catch (Zend_Http_Client_Exception $zhce) {
|
|
$message = 'Error in request to AWS service: ' . $zhce->getMessage();
|
|
throw new Zend_Service_Amazon_SimpleDb_Exception($message, $zhce->getCode());
|
|
}
|
|
$response = new Zend_Service_Amazon_SimpleDb_Response($httpResponse);
|
|
$this->_checkForErrors($response);
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Adds required authentication and version parameters to an array of
|
|
* parameters
|
|
*
|
|
* The required parameters are:
|
|
* - AWSAccessKey
|
|
* - SignatureVersion
|
|
* - Timestamp
|
|
* - Version and
|
|
* - Signature
|
|
*
|
|
* If a required parameter is already set in the <tt>$parameters</tt> array,
|
|
* it is overwritten.
|
|
*
|
|
* @param array $parameters the array to which to add the required
|
|
* parameters.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function _addRequiredParameters(array $parameters)
|
|
{
|
|
$parameters['AWSAccessKeyId'] = $this->_getAccessKey();
|
|
$parameters['SignatureVersion'] = $this->_signatureVersion;
|
|
$parameters['Timestamp'] = gmdate('c');
|
|
$parameters['Version'] = $this->_sdbApiVersion;
|
|
$parameters['SignatureMethod'] = $this->_signatureMethod;
|
|
$parameters['Signature'] = $this->_signParameters($parameters);
|
|
|
|
return $parameters;
|
|
}
|
|
|
|
/**
|
|
* Computes the RFC 2104-compliant HMAC signature for request parameters
|
|
*
|
|
* This implements the Amazon Web Services signature, as per the following
|
|
* specification:
|
|
*
|
|
* 1. Sort all request parameters (including <tt>SignatureVersion</tt> and
|
|
* excluding <tt>Signature</tt>, the value of which is being created),
|
|
* ignoring case.
|
|
*
|
|
* 2. Iterate over the sorted list and append the parameter name (in its
|
|
* original case) and then its value. Do not URL-encode the parameter
|
|
* values before constructing this string. Do not use any separator
|
|
* characters when appending strings.
|
|
*
|
|
* @param array $parameters the parameters for which to get the signature.
|
|
* @param string $secretKey the secret key to use to sign the parameters.
|
|
*
|
|
* @return string the signed data.
|
|
*/
|
|
protected function _signParameters(array $paramaters)
|
|
{
|
|
$data = "POST\n";
|
|
$data .= $this->getEndpoint()->getHost() . "\n";
|
|
$data .= "/\n";
|
|
|
|
uksort($paramaters, 'strcmp');
|
|
unset($paramaters['Signature']);
|
|
|
|
$arrData = array();
|
|
foreach ($paramaters as $key => $value) {
|
|
$value = urlencode($value);
|
|
$value = str_replace("%7E", "~", $value);
|
|
$value = str_replace("+", "%20", $value);
|
|
$arrData[] = urlencode($key) . '=' . $value;
|
|
}
|
|
|
|
$data .= implode('&', $arrData);
|
|
|
|
require_once 'Zend/Crypt/Hmac.php';
|
|
$hmac = Zend_Crypt_Hmac::compute($this->_getSecretKey(), 'SHA256', $data, Zend_Crypt_Hmac::BINARY);
|
|
|
|
return base64_encode($hmac);
|
|
}
|
|
|
|
/**
|
|
* Checks for errors responses from Amazon
|
|
*
|
|
* @param Zend_Service_Amazon_SimpleDb_Response $response the response object to
|
|
* check.
|
|
*
|
|
* @return void
|
|
*
|
|
* @throws Zend_Service_Amazon_SimpleDb_Exception if one or more errors are
|
|
* returned from Amazon.
|
|
*/
|
|
private function _checkForErrors(Zend_Service_Amazon_SimpleDb_Response $response)
|
|
{
|
|
$xpath = new DOMXPath($response->getDocument());
|
|
$list = $xpath->query('//Error');
|
|
if ($list->length > 0) {
|
|
$node = $list->item(0);
|
|
$code = $xpath->evaluate('string(Code/text())', $node);
|
|
$message = $xpath->evaluate('string(Message/text())', $node);
|
|
throw new Zend_Service_Amazon_SimpleDb_Exception($message, 0, $code);
|
|
}
|
|
}
|
|
}
|