--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/enmi/Zend/Service/Amazon/SimpleDb.php Thu Jan 20 19:30:54 2011 +0100
@@ -0,0 +1,580 @@
+<?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-2010 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-2010 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 $maxNumberOfDomains int
+ * @param $nextToken int
+ * @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 $domainName string 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 $domainName string 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 $domainName string 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);
+ }
+ }
+}