text:
<?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
 * @subpackage Amazon_Sqs
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Sqs.php 24470 2011-09-26 16:26:44Z ezimuel $
 */

/**
 * @see Zend_Service_Amazon_Abstract
 */
require_once 'Zend/Service/Amazon/Abstract.php';

/**
 * @see Zend_Crypt_Hmac
 */
require_once 'Zend/Crypt/Hmac.php';

/**
 * Class for connecting to the Amazon Simple Queue Service (SQS)
 *
 * @category   Zend
 * @package    Zend_Service
 * @subpackage Amazon_Sqs
 * @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        http://aws.amazon.com/sqs/ Amazon Simple Queue Service
 */
class Zend_Service_Amazon_Sqs extends Zend_Service_Amazon_Abstract
{
    /**
     * Default timeout for createQueue() function
     */
    const CREATE_TIMEOUT_DEFAULT = 30;

    /**
     * HTTP end point for the Amazon SQS service
     */
    protected $_sqsEndpoint = 'queue.amazonaws.com';

    /**
     * The API version to use
     */
    protected $_sqsApiVersion = '2009-02-01';

    /**
     * Signature Version
     */
    protected $_sqsSignatureVersion = '2';

    /**
     * Signature Encoding Method
     */
    protected $_sqsSignatureMethod = 'HmacSHA256';

    protected $_sqsEndpoints = array('us-east-1' => 'sqs.us-east-1.amazonaws.com',
                                     'us-west-1' => 'sqs.us-west-1.amazonaws.com',
                                     'eu-west-1' => 'sqs.eu-west-1.amazonaws.com',
                                     'ap-southeast-1' => 'sqs.ap-southeast-1.amazonaws.com',
                                     'ap-northeast-1' => 'sqs.ap-northeast-1.amazonaws.com');
    /**
     * Constructor
     *
     * The default region is us-east-1. Use the region to set it to one of the regions that is build-in into ZF.
     * To add a new AWS region use the setEndpoint() method.
     *
     * @param string $accessKey
     * @param string $secretKey
     * @param string $region
     */
    public function __construct($accessKey = null, $secretKey = null, $region = null)
    {
        parent::__construct($accessKey, $secretKey, $region);

        if (null !== $region) {
            $this->_setEndpoint($region);
        }
    }

    /**
     * Set SQS endpoint
     *
     * Checks and sets endpoint if region exists in $_sqsEndpoints. If a new SQS region is added by amazon,
     * please use the setEndpoint function to set it.
     *
     * @param  string  $region region
     * @throws Zend_Service_Amazon_Sqs_Exception
     */
    protected function _setEndpoint($region)
    {
        if (array_key_exists($region, $this->_sqsEndpoints)) {
            $this->_sqsEndpoint = $this->_sqsEndpoints[$region];
        } else {
            throw new Zend_Service_Amazon_Sqs_Exception('Invalid SQS region specified.');
        }
    }

    /**
     * Set SQS endpoint
     *
     * You can set SQS to on of the build-in regions. If the region does not exsist it will be added.
     *
     * @param  string  $region region
     * @throws Zend_Service_Amazon_Sqs_Exception
     */
    public function setEndpoint($region)
    {
        if (!empty($region)) {
            if (array_key_exists($region, $this->_sqsEndpoints)) {
                $this->_sqsEndpoint = $this->_sqsEndpoints[$region];
            } else {
                $this->_sqsEndpoints[$region] = "sqs.$region.amazonaws.com";
                $this->_sqsEndpoint = $this->_sqsEndpoints[$region];
            }
        } else {
            throw new Zend_Service_Amazon_Sqs_Exception('Empty region specified.');
        }
    }

    /**
     * Get the SQS endpoint
     *
     * @return string
     */
    public function getEndpoint()
    {
        return $this->_sqsEndpoint;
    }

    /**
     * Get possible SQS endpoints
     *
     * Since there is not an SQS webserive to get all possible endpoints, a hardcoded list is available.
     * For the actual region list please check:
     * http://docs.amazonwebservices.com/AWSSimpleQueueService/2009-02-01/APIReference/index.html?QueueServiceWsdlArticle.html
     *
     * @param  string  $region region
     * @return array
     */
    public function getEndpoints()
    {
        return $this->_sqsEndpoints;
    }

    /**
     * Create a new queue
     *
     * Visibility timeout is how long a message is left in the queue "invisible"
     * to other readers.  If the message is acknowleged (deleted) before the
     * timeout, then the message is deleted.  However, if the timeout expires
     * then the message will be made available to other queue readers.
     *
     * @param  string  $queue_name queue name
     * @param  integer $timeout    default visibility timeout
     * @return string|boolean
     * @throws Zend_Service_Amazon_Sqs_Exception
     */
    public function create($queue_name, $timeout = null)
    {
        $params = array();
        $params['QueueName'] = $queue_name;
        $timeout = ($timeout === null) ? self::CREATE_TIMEOUT_DEFAULT : (int)$timeout;
        $params['DefaultVisibilityTimeout'] = $timeout;

        $retry_count = 0;

        do {
            $retry  = false;
            $result = $this->_makeRequest(null, 'CreateQueue', $params);

            if (!isset($result->CreateQueueResult->QueueUrl)
                || empty($result->CreateQueueResult->QueueUrl)
            ) {
                if ($result->Error->Code == 'AWS.SimpleQueueService.QueueNameExists') {
                    return false;
                } elseif ($result->Error->Code == 'AWS.SimpleQueueService.QueueDeletedRecently') {
                    // Must sleep for 60 seconds, then try re-creating the queue
                    sleep(60);
                    $retry = true;
                    $retry_count++;
                } else {
                    require_once 'Zend/Service/Amazon/Sqs/Exception.php';
                    throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
                }
            } else {
                return (string) $result->CreateQueueResult->QueueUrl;
            }

        } while ($retry);

        return false;
    }

    /**
     * Delete a queue and all of it's messages
     *
     * Returns false if the queue is not found, true if the queue exists
     *
     * @param  string  $queue_url queue URL
     * @return boolean
     * @throws Zend_Service_Amazon_Sqs_Exception
     */
    public function delete($queue_url)
    {
        $result = $this->_makeRequest($queue_url, 'DeleteQueue');

        if ($result->Error->Code !== null) {
            require_once 'Zend/Service/Amazon/Sqs/Exception.php';
            throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
        }

        return true;
    }

    /**
     * Get an array of all available queues
     *
     * @return array
     * @throws Zend_Service_Amazon_Sqs_Exception
     */
    public function getQueues()
    {
        $result = $this->_makeRequest(null, 'ListQueues');

        if (isset($result->Error)) {
            require_once 'Zend/Service/Amazon/Sqs/Exception.php';
            throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
        }

        if (!isset($result->ListQueuesResult->QueueUrl)
            || empty($result->ListQueuesResult->QueueUrl)
        ) {
            return array();
        }

        $queues = array();
        foreach ($result->ListQueuesResult->QueueUrl as $queue_url) {
            $queues[] = (string)$queue_url;
        }

        return $queues;
    }

    /**
     * Return the approximate number of messages in the queue
     *
     * @param  string  $queue_url Queue URL
     * @return integer
     * @throws Zend_Service_Amazon_Sqs_Exception
     */
    public function count($queue_url)
    {
        return (int)$this->getAttribute($queue_url, 'ApproximateNumberOfMessages');
    }

    /**
     * Send a message to the queue
     *
     * @param  string $queue_url Queue URL
     * @param  string $message   Message to send to the queue
     * @return string            Message ID
     * @throws Zend_Service_Amazon_Sqs_Exception
     */
    public function send($queue_url, $message)
    {
        $params = array();
        $params['MessageBody'] = urlencode($message);

        $checksum = md5($params['MessageBody']);

        $result = $this->_makeRequest($queue_url, 'SendMessage', $params);

        if (!isset($result->SendMessageResult->MessageId)
            || empty($result->SendMessageResult->MessageId)
        ) {
            require_once 'Zend/Service/Amazon/Sqs/Exception.php';
            throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
        } else if ((string) $result->SendMessageResult->MD5OfMessageBody != $checksum) {
            require_once 'Zend/Service/Amazon/Sqs/Exception.php';
            throw new Zend_Service_Amazon_Sqs_Exception('MD5 of body does not match message sent');
        }

        return (string) $result->SendMessageResult->MessageId;
    }

    /**
     * Get messages in the queue
     *
     * @param  string  $queue_url    Queue name
     * @param  integer $max_messages Maximum number of messages to return
     * @param  integer $timeout      Visibility timeout for these messages
     * @return array
     * @throws Zend_Service_Amazon_Sqs_Exception
     */
    public function receive($queue_url, $max_messages = null, $timeout = null)
    {
        $params = array();

        // If not set, the visibility timeout on the queue is used
        if ($timeout !== null) {
            $params['VisibilityTimeout'] = (int)$timeout;
        }

        // SQS will default to only returning one message
        if ($max_messages !== null) {
            $params['MaxNumberOfMessages'] = (int)$max_messages;
        }

        $result = $this->_makeRequest($queue_url, 'ReceiveMessage', $params);

        if (isset($result->Error)) {
            require_once 'Zend/Service/Amazon/Sqs/Exception.php';
            throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
        }

        if (!isset($result->ReceiveMessageResult->Message)
            || empty($result->ReceiveMessageResult->Message)
        ) {
            // no messages found
            return array();
        }

        $data = array();
        foreach ($result->ReceiveMessageResult->Message as $message) {
            $data[] = array(
                'message_id' => (string)$message->MessageId,
                'handle'     => (string)$message->ReceiptHandle,
                'md5'        => (string)$message->MD5OfBody,
                'body'       => urldecode((string)$message->Body),
            );
        }

        return $data;
    }

    /**
     * Delete a message from the queue
     *
     * Returns true if the message is deleted, false if the deletion is
     * unsuccessful.
     *
     * @param  string $queue_url  Queue URL
     * @param  string $handle     Message handle as returned by SQS
     * @return boolean
     * @throws Zend_Service_Amazon_Sqs_Exception
     */
    public function deleteMessage($queue_url, $handle)
    {
        $params = array();
        $params['ReceiptHandle'] = (string)$handle;

        $result = $this->_makeRequest($queue_url, 'DeleteMessage', $params);

        if (isset($result->Error->Code)
            && !empty($result->Error->Code)
        ) {
            return false;
        }

        // Will always return true unless ReceiptHandle is malformed
        return true;
    }

    /**
     * Get the attributes for the queue
     *
     * @param  string $queue_url  Queue URL
     * @param  string $attribute
     * @return string
     * @throws Zend_Service_Amazon_Sqs_Exception
     */
    public function getAttribute($queue_url, $attribute = 'All')
    {
        $params = array();
        $params['AttributeName'] = $attribute;

        $result = $this->_makeRequest($queue_url, 'GetQueueAttributes', $params);

        if (!isset($result->GetQueueAttributesResult->Attribute)
            || empty($result->GetQueueAttributesResult->Attribute)
        ) {
            require_once 'Zend/Service/Amazon/Sqs/Exception.php';
            throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
        }

        if(count($result->GetQueueAttributesResult->Attribute) > 1) {
            $attr_result = array();
            foreach($result->GetQueueAttributesResult->Attribute as $attribute) {
                $attr_result[(string)$attribute->Name] = (string)$attribute->Value;
            }
            return $attr_result;
        } else {
            return (string) $result->GetQueueAttributesResult->Attribute->Value;
        }
    }

    /**
     * Make a request to Amazon SQS
     *
     * @param  string           $queue  Queue Name
     * @param  string           $action SQS action
     * @param  array            $params
     * @return SimpleXMLElement
     */
    private function _makeRequest($queue_url, $action, $params = array())
    {
        $params['Action'] = $action;
        $params = $this->addRequiredParameters($queue_url, $params);

        if ($queue_url === null) {
            $queue_url = '/';
        }

        $client = self::getHttpClient();

        switch ($action) {
            case 'ListQueues':
            case 'CreateQueue':
                $client->setUri('http://'.$this->_sqsEndpoint);
                break;
            default:
                $client->setUri($queue_url);
                break;
        }

        $retry_count = 0;

        do {
            $retry = false;

            $client->resetParameters();
            $client->setParameterGet($params);

            $response = $client->request('GET');

            $response_code = $response->getStatus();

            // Some 5xx errors are expected, so retry automatically
            if ($response_code >= 500 && $response_code < 600 && $retry_count <= 5) {
                $retry = true;
                $retry_count++;
                sleep($retry_count / 4 * $retry_count);
            }
        } while ($retry);

        unset($client);

        return new SimpleXMLElement($response->getBody());
    }

    /**
     * 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  string $queue_url  Queue URL
     * @param  array  $parameters the array to which to add the required
     *                            parameters.
     * @return array
     */
    protected function addRequiredParameters($queue_url, array $parameters)
    {
        $parameters['AWSAccessKeyId']   = $this->_getAccessKey();
        $parameters['SignatureVersion'] = $this->_sqsSignatureVersion;
        $parameters['Timestamp']        = gmdate('Y-m-d\TH:i:s\Z', time()+10);
        $parameters['Version']          = $this->_sqsApiVersion;
        $parameters['SignatureMethod']  = $this->_sqsSignatureMethod;
        $parameters['Signature']        = $this->_signParameters($queue_url, $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  string $queue_url  Queue URL
     * @param  array  $parameters the parameters for which to get the signature.
     *
     * @return string the signed data.
     */
    protected function _signParameters($queue_url, array $paramaters)
    {
        $data = "GET\n";
        $data .= $this->_sqsEndpoint . "\n";
        if ($queue_url !== null) {
            $data .= parse_url($queue_url, PHP_URL_PATH);
        }
        else {
            $data .= '/';
        }
        $data .= "\n";

        uksort($paramaters, 'strcmp');
        unset($paramaters['Signature']);

        $arrData = array();
        foreach($paramaters as $key => $value) {
            $arrData[] = $key . '=' . str_replace('%7E', '~', urlencode($value));
        }

        $data .= implode('&', $arrData);

        $hmac = Zend_Crypt_Hmac::compute($this->_getSecretKey(), 'SHA256', $data, Zend_Crypt_Hmac::BINARY);

        return base64_encode($hmac);
    }
}


text:
<?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
 * @subpackage Amazon
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Item.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * @category   Zend
 * @package    Zend_Service
 * @subpackage Amazon
 * @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_Item
{
    /**
     * @var string
     */
    public $ASIN;

    /**
     * @var string
     */
    public $DetailPageURL;

    /**
     * @var int
     */
    public $SalesRank;

    /**
     * @var int
     */
    public $TotalReviews;

    /**
     * @var int
     */
    public $AverageRating;

    /**
     * @var string
     */
    public $SmallImage;

    /**
     * @var string
     */
    public $MediumImage;

    /**
     * @var string
     */
    public $LargeImage;

    /**
     * @var string
     */
    public $Subjects;

    /**
     * @var Zend_Service_Amazon_OfferSet
     */
    public $Offers;

    /**
     * @var Zend_Service_Amazon_CustomerReview[]
     */
    public $CustomerReviews = array();

    /**
     * @var Zend_Service_Amazon_SimilarProducts[]
     */
    public $SimilarProducts = array();

    /**
     * @var Zend_Service_Amazon_Accessories[]
     */
    public $Accessories = array();

    /**
     * @var array
     */
    public $Tracks = array();

    /**
     * @var Zend_Service_Amazon_ListmaniaLists[]
     */
    public $ListmaniaLists = array();

    protected $_dom;

    /**
     * Parse the given <Item> element
     *
     * @param  null|DOMElement $dom
     * @return void
     * @throws Zend_Service_Amazon_Exception
     *
     * @group ZF-9547
     */
    public function __construct($dom)
    {
        if (null === $dom) {
            require_once 'Zend/Service/Amazon/Exception.php';
            throw new Zend_Service_Amazon_Exception('Item element is empty');
        }
        if (!$dom instanceof DOMElement) {
            require_once 'Zend/Service/Amazon/Exception.php';
            throw new Zend_Service_Amazon_Exception('Item is not a valid DOM element');
        }
        $xpath = new DOMXPath($dom->ownerDocument);
        $xpath->registerNamespace('az', 'http://webservices.amazon.com/AWSECommerceService/2005-10-05');
        $this->ASIN = $xpath->query('./az:ASIN/text()', $dom)->item(0)->data;

        $result = $xpath->query('./az:DetailPageURL/text()', $dom);
        if ($result->length == 1) {
            $this->DetailPageURL = $result->item(0)->data;
        }

        if ($xpath->query('./az:ItemAttributes/az:ListPrice', $dom)->length >= 1) {
            $this->CurrencyCode = (string) $xpath->query('./az:ItemAttributes/az:ListPrice/az:CurrencyCode/text()', $dom)->item(0)->data;
            $this->Amount = (int) $xpath->query('./az:ItemAttributes/az:ListPrice/az:Amount/text()', $dom)->item(0)->data;
            $this->FormattedPrice = (string) $xpath->query('./az:ItemAttributes/az:ListPrice/az:FormattedPrice/text()', $dom)->item(0)->data;
        }

        $result = $xpath->query('./az:ItemAttributes/az:*/text()', $dom);
        if ($result->length >= 1) {
            foreach ($result as $v) {
                if (isset($this->{$v->parentNode->tagName})) {
                    if (is_array($this->{$v->parentNode->tagName})) {
                        array_push($this->{$v->parentNode->tagName}, (string) $v->data);
                    } else {
                        $this->{$v->parentNode->tagName} = array($this->{$v->parentNode->tagName}, (string) $v->data);
                    }
                } else {
                    $this->{$v->parentNode->tagName} = (string) $v->data;
                }
            }
        }

        foreach (array('SmallImage', 'MediumImage', 'LargeImage') as $im) {
            $result = $xpath->query("./az:ImageSets/az:ImageSet[position() = 1]/az:$im", $dom);
            if ($result->length == 1) {
                /**
                 * @see Zend_Service_Amazon_Image
                 */
                require_once 'Zend/Service/Amazon/Image.php';
                $this->$im = new Zend_Service_Amazon_Image($result->item(0));
            }
        }

        $result = $xpath->query('./az:SalesRank/text()', $dom);
        if ($result->length == 1) {
            $this->SalesRank = (int) $result->item(0)->data;
        }

        $result = $xpath->query('./az:CustomerReviews/az:Review', $dom);
        if ($result->length >= 1) {
            /**
             * @see Zend_Service_Amazon_CustomerReview
             */
            require_once 'Zend/Service/Amazon/CustomerReview.php';
            foreach ($result as $review) {
                $this->CustomerReviews[] = new Zend_Service_Amazon_CustomerReview($review);
            }
            $this->AverageRating = (float) $xpath->query('./az:CustomerReviews/az:AverageRating/text()', $dom)->item(0)->data;
            $this->TotalReviews = (int) $xpath->query('./az:CustomerReviews/az:TotalReviews/text()', $dom)->item(0)->data;
        }

        $result = $xpath->query('./az:EditorialReviews/az:*', $dom);
        if ($result->length >= 1) {
            /**
             * @see Zend_Service_Amazon_EditorialReview
             */
            require_once 'Zend/Service/Amazon/EditorialReview.php';
            foreach ($result as $r) {
                $this->EditorialReviews[] = new Zend_Service_Amazon_EditorialReview($r);
            }
        }

        $result = $xpath->query('./az:SimilarProducts/az:*', $dom);
        if ($result->length >= 1) {
            /**
             * @see Zend_Service_Amazon_SimilarProduct
             */
            require_once 'Zend/Service/Amazon/SimilarProduct.php';
            foreach ($result as $r) {
                $this->SimilarProducts[] = new Zend_Service_Amazon_SimilarProduct($r);
            }
        }

        $result = $xpath->query('./az:ListmaniaLists/*', $dom);
        if ($result->length >= 1) {
            /**
             * @see Zend_Service_Amazon_ListmaniaList
             */
            require_once 'Zend/Service/Amazon/ListmaniaList.php';
            foreach ($result as $r) {
                $this->ListmaniaLists[] = new Zend_Service_Amazon_ListmaniaList($r);
            }
        }

        $result = $xpath->query('./az:Tracks/az:Disc', $dom);
        if ($result->length > 1) {
            foreach ($result as $disk) {
                foreach ($xpath->query('./*/text()', $disk) as $t) {
                    // TODO: For consistency in a bugfix all tracks are appended to one single array
                    // Erroreous line: $this->Tracks[$disk->getAttribute('number')] = (string) $t->data;
                    $this->Tracks[] = (string) $t->data;
                }
            }
        } else if ($result->length == 1) {
            foreach ($xpath->query('./*/text()', $result->item(0)) as $t) {
                $this->Tracks[] = (string) $t->data;
            }
        }

        $result = $xpath->query('./az:Offers', $dom);
        $resultSummary = $xpath->query('./az:OfferSummary', $dom);
        if ($result->length > 1 || $resultSummary->length == 1) {
            /**
             * @see Zend_Service_Amazon_OfferSet
             */
            require_once 'Zend/Service/Amazon/OfferSet.php';
            $this->Offers = new Zend_Service_Amazon_OfferSet($dom);
        }

        $result = $xpath->query('./az:Accessories/*', $dom);
        if ($result->length > 1) {
            /**
             * @see Zend_Service_Amazon_Accessories
             */
            require_once 'Zend/Service/Amazon/Accessories.php';
            foreach ($result as $r) {
                $this->Accessories[] = new Zend_Service_Amazon_Accessories($r);
            }
        }

        $this->_dom = $dom;
    }

    /**
     * Returns the item's original XML
     *
     * @return string
     */
    public function asXml()
    {
        return $this->_dom->ownerDocument->saveXML($this->_dom);
    }
}

repo_name: michael-romer/zf-boilerplate

repo_language: PHP

mime_type: text/html


text:
<?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 Ec2
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Exception.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * @see Zend_Service_Amazon_Exception
 */
require_once 'Zend/Service/Amazon/Exception.php';

/**
 * The Custom Exception class that allows you to have access to the AWS Error Code.
 *
 * @category   Zend
 * @package    Zend_Service_Amazon
 * @subpackage Ec2
 * @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_Ec2_Exception extends Zend_Service_Amazon_Exception
{
    private $awsErrorCode = '';

    public function __construct($message, $code = 0, $awsErrorCode = '')
    {
        parent::__construct($message, $code);
        $this->awsErrorCode = $awsErrorCode;
    }

    public function getErrorCode()
    {
        return $this->awsErrorCode;
    }
}


text:
<?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 Ec2
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: CloudWatch.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * @see Zend_Service_Amazon_Ec2_Abstract
 */
require_once 'Zend/Service/Amazon/Ec2/Abstract.php';

/**
 * An Amazon EC2 interface that allows yout to run, terminate, reboot and describe Amazon
 * Ec2 Instances.
 *
 * @category   Zend
 * @package    Zend_Service_Amazon
 * @subpackage Ec2
 * @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_Ec2_CloudWatch extends Zend_Service_Amazon_Ec2_Abstract
{
    /**
     * The HTTP query server
     */
    protected $_ec2Endpoint = 'monitoring.amazonaws.com';

    /**
     * The API version to use
     */
    protected $_ec2ApiVersion = '2009-05-15';

    /**
     * XML Namespace for the CloudWatch Stuff
     */
    protected $_xmlNamespace = 'http://monitoring.amazonaws.com/doc/2009-05-15/';

    /**
     * The following metrics are available from each EC2 instance.
     *
     * CPUUtilization: The percentage of allocated EC2 compute units that are
     *  currently in use on the instance. This metric identifies the processing
     *  power required to run an application upon a selected instance.
     *
     * NetworkIn: The number of bytes received on all network interfaces by
     *  the instance. This metric identifies the volume of incoming network
     *  traffic to an application on a single instance.
     *
     * NetworkOut: The number of bytes sent out on all network interfaces
     *  by the instance. This metric identifies the volume of outgoing network
     *  traffic to an application on a single instance.
     *
     * DiskWriteOps: Completed write operations to all hard disks available to
     *  the instance. This metric identifies the rate at which an application
     *  writes to a hard disk. This can be used to determine the speed in which
     *  an application saves data to a hard disk.
     *
     * DiskReadBytes: Bytes read from all disks available to the instance. This
     *  metric is used to determine the volume of the data the application reads
     *  from the hard disk of the instance. This can be used to determine the
     *  speed of the application for the customer.
     *
     * DiskReadOps: Completed read operations from all disks available to the
     *  instances. This metric identifies the rate at which an application reads
     *  a disk. This can be used to determine the speed in which an application
     *  reads data from a hard disk.
     *
     * DiskWriteBytes: Bytes written to all disks available to the instance. This
     *  metric is used to determine the volume of the data the application writes
     *  onto the hard disk of the instance. This can be used to determine the speed
     *  of the application for the customer.
     *
     * Latency: Time taken between a request and the corresponding response as seen
     *  by the load balancer.
     *
     * RequestCount: The number of requests processed by the LoadBalancer.
     *
     * HealthyHostCount: The number of healthy instances. Both Load Balancing dimensions,
     *  LoadBalancerName and AvailabilityZone, should be specified when retreiving
     *  HealthyHostCount.
     *
     * UnHealthyHostCount: The number of unhealthy instances. Both Load Balancing dimensions,
     *  LoadBalancerName and AvailabilityZone, should be specified when retreiving
     *  UnHealthyHostCount.
     *
     * Amazon CloudWatch data for a new EC2 instance becomes available typically
     * within one minute of the end of the first aggregation period for the new
     * instance. You can use the currently available dimensions for EC2 instances
     * along with these metrics in order to refine the slice of data you want returned,
     * such as metric CPUUtilization and dimension ImageId to get all CPUUtilization
     * data for instances using the specified AMI.
     *
     * @var array
     */
    protected $_validMetrics = array('CPUUtilization', 'NetworkIn', 'NetworkOut',
                                    'DiskWriteOps', 'DiskReadBytes', 'DiskReadOps',
                                    'DiskWriteBytes', 'Latency', 'RequestCount',
                                    'HealthyHostCount', 'UnHealthyHostCount');

    /**
     * Amazon CloudWatch not only aggregates the raw data coming in, it also computes
     * several statistics on the data. The following table lists the statistics that you can request:
     *
     * Minimum: The lowest value observed during the specified period. This can be used to
     *  determine low volumes of activity for your application.
     *
     * Maximum: The highest value observed during the specified period. You can use this to
     *  determine high volumes of activity for your application.
     *
     * Sum: The sum of all values received (if appropriate, for example a rate would not be
     *  summed, but a number of items would be). This statistic is useful for determining
     *  the total volume of a metric.
     *
     * Average: The Average of all values received during the specified period. By comparing
     *  this statistic with the minimum and maximum statistics, you can determine the full
     *  scope of a metric and how close the average use is to the minimum and the maximum.
     *  This will allow you to increase or decrease your resources as needed.
     *
     * Samples: The count (number) of measures used. This statistic is always returned to
     *  show the user the size of the dataset collected. This will allow the user to properly
     *  weight the data.
     *
     * Statistics are computed within a period you specify, such as all CPUUtilization within a
     * five minute period. At a minimum, all data is aggregated into one minute intervals. This
     * is the minimum resolution of the data. It is this data that can be aggregated into larger
     * periods of time that you request.
     *
     * Aggregate data is generally available from the service within one minute from the end of the
     * aggregation period. Delays in data propagation might cause late or partially late data in
     * some cases. If your data is delayed, you should check the service’s Health Dashboard for
     * any current operational issues with either Amazon CloudWatch or the services collecting
     * the data, such as EC2 or Elastic Load Balancing.
     *
     * @var array
     */
    protected $_validStatistics = array('Average', 'Maximum', 'Minimum', 'Samples', 'Sum');

    /**
     * Valid Dimention Keys for getMetricStatistics
     *
     * ImageId: This dimension filters the data you request for all instances running
     *  this EC2 Amazon Machine Image (AMI).
     *
     * AvailabilityZone: This dimension filters the data you request for all instances
     *  running in that EC2 Availability Zone.
     *
     * AutoScalingGroupName: This dimension filters the data you request for all instances
     *  in a specified capacity group. An AutoScalingGroup is a collection of instances
     *  defined by customers of the Auto Scaling service. This dimension is only available
     *  for EC2 metrics when the instances are in such an AutoScalingGroup.
     *
     * InstanceId: This dimension filters the data you request for only the identified
     *  instance. This allows a user to pinpoint an exact instance from which to monitor data.
     *
     * InstanceType: This dimension filters the data you request for all instances running
     *  with this specified instance type. This allows a user to catagorize his data by the
     *  type of instance running. For example, a user might compare data from an m1.small instance
     *  and an m1.large instance to determine which has the better business value for his application.
     *
     * LoadBalancerName: This dimension filters the data you request for the specified LoadBalancer
     *  name. A LoadBalancer is represented by a DNS name and provides the single destination to
     *  which all requests intended for your application should be directed. This metric allows
     *  you to examine data from all instances connected to a single LoadBalancer.
     *
     * @var array
     */
    protected $_validDimensionsKeys = array('ImageId', 'AvailabilityZone', 'AutoScalingGroupName',
                                            'InstanceId', 'InstanceType', 'LoadBalancerName');

    /**
     * Returns data for one or more statistics of given a metric
     *
     * Note:
     * The maximum number of datapoints that the Amazon CloudWatch service will
     * return in a single GetMetricStatistics request is 1,440. If a request is
     * made that would generate more datapoints than this amount, Amazon CloudWatch
     * will return an error. You can alter your request by narrowing the time range
     * (StartTime, EndTime) or increasing the Period in your single request. You may
     * also get all of the data at the granularity you originally asked for by making
     * multiple requests with adjacent time ranges.
     *
     * @param array $options            The options you want to get statistics for:
     *                                  ** Required **
     *                                  MeasureName: The measure name that corresponds to
     *                                      the measure for the gathered metric. Valid EC2 Values are
     *                                      CPUUtilization, NetworkIn, NetworkOut, DiskWriteOps
     *                                      DiskReadBytes, DiskReadOps, DiskWriteBytes. Valid Elastic
     *                                      Load Balancing Metrics are Latency, RequestCount, HealthyHostCount
     *                                      UnHealthyHostCount
     *                                  Statistics: The statistics to be returned for the given metric. Valid
     *                                      values are Average, Maximum, Minimum, Samples, Sum.  You can specify
     *                                      this as a string or as an array of values.  If you don't specify one
     *                                      it will default to Average instead of failing out.  If you specify an incorrect
     *                                      option it will just skip it.
     *                                  ** Optional **
     *                                  Dimensions: Amazon CloudWatch allows you to specify one Dimension to further filter
     *                                      metric data on. If you don't specify a dimension, the service returns the aggregate
     *                                      of all the measures with the given measure name and time range.
     *                                  Unit: The standard unit of Measurement for a given Measure. Valid Values: Seconds,
     *                                      Percent, Bytes, Bits, Count, Bytes/Second, Bits/Second, Count/Second, and None
     *                                      Constraints: When using count/second as the unit, you should use Sum as the statistic
     *                                      instead of Average. Otherwise, the sample returns as equal to the number of requests
     *                                      instead of the number of 60-second intervals. This will cause the Average to
     *                                      always equals one when the unit is count/second.
     *                                  StartTime: The timestamp of the first datapoint to return, inclusive. For example,
     *                                      2008-02-26T19:00:00+00:00. We round your value down to the nearest minute.
     *                                      You can set your start time for more than two weeks in the past. However,
     *                                      you will only get data for the past two weeks. (in ISO 8601 format)
     *                                      Constraints: Must be before EndTime
     *                                  EndTime: The timestamp to use for determining the last datapoint to return. This is
     *                                      the last datapoint to fetch, exclusive. For example, 2008-02-26T20:00:00+00:00.
     *                                      (in ISO 8601 format)
     */
    public function getMetricStatistics(array $options)
    {
        $_usedStatistics = array();

        $params = array();
        $params['Action'] = 'GetMetricStatistics';

        if (!isset($options['Period'])) {
            $options['Period'] = 60;
        }
        if (!isset($options['Namespace'])) {
            $options['Namespace'] = 'AWS/EC2';
        }

        if (!isset($options['MeasureName']) || !in_array($options['MeasureName'], $this->_validMetrics, true)) {
            throw new Zend_Service_Amazon_Ec2_Exception('Invalid Metric Type: ' . $options['MeasureName']);
        }

        if(!isset($options['Statistics'])) {
            $options['Statistics'][] = 'Average';
        } elseif(!is_array($options['Statistics'])) {
            $options['Statistics'][] = $options['Statistics'];
        }

        foreach($options['Statistics'] as $k=>$s) {
            if(!in_array($s, $this->_validStatistics, true)) continue;
            $options['Statistics.member.' . ($k+1)] = $s;
            $_usedStatistics[] = $s;
        }
        unset($options['Statistics']);

        if(isset($options['StartTime'])) {
            if(!is_numeric($options['StartTime'])) $options['StartTime'] = strtotime($options['StartTime']);
            $options['StartTime'] = gmdate('c', $options['StartTime']);
        } else {
            $options['StartTime'] = gmdate('c', strtotime('-1 hour'));
        }

        if(isset($options['EndTime'])) {
            if(!is_numeric($options['EndTime'])) $options['EndTime'] = strtotime($options['EndTime']);
            $options['EndTime'] = gmdate('c', $options['EndTime']);
        } else {
            $options['EndTime'] = gmdate('c');
        }

        if(isset($options['Dimensions'])) {
            $x = 1;
            foreach($options['Dimensions'] as $dimKey=>$dimVal) {
                if(!in_array($dimKey, $this->_validDimensionsKeys, true)) continue;
                $options['Dimensions.member.' . $x . '.Name'] = $dimKey;
                $options['Dimensions.member.' . $x . '.Value'] = $dimVal;
                $x++;
            }

            unset($options['Dimensions']);
        }

        $params = array_merge($params, $options);

        $response = $this->sendRequest($params);
        $response->setNamespace($this->_xmlNamespace);

        $xpath = $response->getXPath();
        $nodes = $xpath->query('//ec2:GetMetricStatisticsResult/ec2:Datapoints/ec2:member');

        $return = array();
        $return['label'] = $xpath->evaluate('string(//ec2:GetMetricStatisticsResult/ec2:Label/text())');
        foreach ( $nodes as $node ) {
            $item = array();

            $item['Timestamp'] = $xpath->evaluate('string(ec2:Timestamp/text())', $node);
            $item['Unit'] = $xpath->evaluate('string(ec2:Unit/text())', $node);
            $item['Samples'] = $xpath->evaluate('string(ec2:Samples/text())', $node);
            foreach($_usedStatistics as $us) {
                $item[$us] = $xpath->evaluate('string(ec2:' . $us . '/text())', $node);
            }

            $return['datapoints'][] = $item;
            unset($item, $node);
        }

        return $return;

    }

    /**
     * Return the Metrics that are aviable for your current monitored instances
     *
     * @param string $nextToken     The NextToken parameter is an optional parameter
     *                                 that allows you to retrieve the next set of results
     *                                 for your ListMetrics query.
     * @return array
     */
    public function listMetrics($nextToken = null)
    {
        $params = array();
        $params['Action'] = 'ListMetrics';
        if (!empty($nextToken)) {
            $params['NextToken'] = $nextToken;
        }

        $response = $this->sendRequest($params);
        $response->setNamespace($this->_xmlNamespace);

        $xpath = $response->getXPath();
        $nodes = $xpath->query('//ec2:ListMetricsResult/ec2:Metrics/ec2:member');

        $return = array();
        foreach ( $nodes as $node ) {
            $item = array();

            $item['MeasureName'] = $xpath->evaluate('string(ec2:MeasureName/text())', $node);
            $item['Namespace'] = $xpath->evaluate('string(ec2:Namespace/text())', $node);
            $item['Deminsions']['name'] = $xpath->evaluate('string(ec2:Dimensions/ec2:member/ec2:Name/text())', $node);
            $item['Deminsions']['value'] = $xpath->evaluate('string(ec2:Dimensions/ec2:member/ec2:Value/text())', $node);

            if (empty($item['Deminsions']['name'])) {
                $item['Deminsions'] = array();
            }

            $return[] = $item;
            unset($item, $node);
        }

        return $return;
    }
}


text:
<?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 Ec2
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Ebs.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * @see Zend_Service_Amazon_Ec2_Abstract
 */
require_once 'Zend/Service/Amazon/Ec2/Abstract.php';

/**
 * An Amazon EC2 interface to create, describe, attach, detach and delete Elastic Block
 * Storage Volumes and Snaphsots.
 *
 * @category   Zend
 * @package    Zend_Service_Amazon
 * @subpackage Ec2
 * @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_Ec2_Ebs extends Zend_Service_Amazon_Ec2_Abstract
{
    /**
     * Creates a new Amazon EBS volume that you can mount from any Amazon EC2 instance.
     *
     * You must specify an availability zone when creating a volume. The volume and
     * any instance to which it attaches must be in the same availability zone.
     *
     * @param string $size                  The size of the volume, in GiB.
     * @param string $availabilityZone      The availability zone in which to create the new volume.
     * @return array
     */
    public function createNewVolume($size, $availabilityZone)
    {
        $params = array();
        $params['Action'] = 'CreateVolume';
        $params['AvailabilityZone'] = $availabilityZone;
        $params['Size'] = $size;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();

        $return = array();
        $return['volumeId']             = $xpath->evaluate('string(//ec2:volumeId/text())');
        $return['size']                 = $xpath->evaluate('string(//ec2:size/text())');
        $return['status']               = $xpath->evaluate('string(//ec2:status/text())');
        $return['createTime']           = $xpath->evaluate('string(//ec2:createTime/text())');
        $return['availabilityZone']     = $xpath->evaluate('string(//ec2:availabilityZone/text())');

        return $return;
    }

    /**
     * Creates a new Amazon EBS volume that you can mount from any Amazon EC2 instance.
     *
     * You must specify an availability zone when creating a volume. The volume and
     * any instance to which it attaches must be in the same availability zone.
     *
     * @param string $snapshotId            The snapshot from which to create the new volume.
     * @param string $availabilityZone      The availability zone in which to create the new volume.
     * @return array
     */
    public function createVolumeFromSnapshot($snapshotId, $availabilityZone)
    {
        $params = array();
        $params['Action'] = 'CreateVolume';
        $params['AvailabilityZone'] = $availabilityZone;
        $params['SnapshotId'] = $snapshotId;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();

        $return = array();
        $return['volumeId']             = $xpath->evaluate('string(//ec2:volumeId/text())');
        $return['size']                 = $xpath->evaluate('string(//ec2:size/text())');
        $return['status']               = $xpath->evaluate('string(//ec2:status/text())');
        $return['createTime']           = $xpath->evaluate('string(//ec2:createTime/text())');
        $return['availabilityZone']     = $xpath->evaluate('string(//ec2:availabilityZone/text())');
        $return['snapshotId']           = $xpath->evaluate('string(//ec2:snapshotId/text())');

        return $return;
    }

    /**
     * Lists one or more Amazon EBS volumes that you own, If you do not
     * specify any volumes, Amazon EBS returns all volumes that you own.
     *
     * @param string|array $volumeId        The ID or array of ID's of the volume(s) to list
     * @return array
     */
    public function describeVolume($volumeId = null)
    {
        $params = array();
        $params['Action'] = 'DescribeVolumes';

        if(is_array($volumeId) && !empty($volumeId)) {
            foreach($volumeId as $k=>$name) {
                $params['VolumeId.' . ($k+1)] = $name;
            }
        } elseif($volumeId) {
            $params['VolumeId.1'] = $volumeId;
        }

        $response = $this->sendRequest($params);

        $xpath  = $response->getXPath();
        $nodes = $xpath->query('//ec2:volumeSet/ec2:item', $response->getDocument());

        $return = array();
        foreach ($nodes as $node) {
            $item = array();

            $item['volumeId']   = $xpath->evaluate('string(ec2:volumeId/text())', $node);
            $item['size']       = $xpath->evaluate('string(ec2:size/text())', $node);
            $item['status']     = $xpath->evaluate('string(ec2:status/text())', $node);
            $item['createTime'] = $xpath->evaluate('string(ec2:createTime/text())', $node);

            $attachmentSet = $xpath->query('ec2:attachmentSet/ec2:item', $node);
            if($attachmentSet->length == 1) {
                $_as = $attachmentSet->item(0);
                $as = array();
                $as['volumeId'] = $xpath->evaluate('string(ec2:volumeId/text())', $_as);
                $as['instanceId'] = $xpath->evaluate('string(ec2:instanceId/text())', $_as);
                $as['device'] = $xpath->evaluate('string(ec2:device/text())', $_as);
                $as['status'] = $xpath->evaluate('string(ec2:status/text())', $_as);
                $as['attachTime'] = $xpath->evaluate('string(ec2:attachTime/text())', $_as);
                $item['attachmentSet'] = $as;
            }

            $return[] = $item;
            unset($item, $node);
        }

        return $return;
    }

    public function describeAttachedVolumes($instanceId)
    {
        $volumes = $this->describeVolume();

        $return = array();
        foreach($volumes as $vol) {
            if(isset($vol['attachmentSet']) && $vol['attachmentSet']['instanceId'] == $instanceId) {
                $return[] = $vol;
            }
        }

        return $return;
    }

    /**
     * Attaches an Amazon EBS volume to an instance
     *
     * @param string $volumeId              The ID of the Amazon EBS volume
     * @param string $instanceId            The ID of the instance to which the volume attaches
     * @param string $device                Specifies how the device is exposed to the instance (e.g., /dev/sdh).
     * @return array
     */
    public function attachVolume($volumeId, $instanceId, $device)
    {
        $params = array();
        $params['Action']       = 'AttachVolume';
        $params['VolumeId']     = $volumeId;
        $params['InstanceId']   = $instanceId;
        $params['Device']       = $device;

        $response = $this->sendRequest($params);

        $xpath = $response->getXPath();

        $return = array();
        $return['volumeId']     = $xpath->evaluate('string(//ec2:volumeId/text())');
        $return['instanceId']   = $xpath->evaluate('string(//ec2:instanceId/text())');
        $return['device']       = $xpath->evaluate('string(//ec2:device/text())');
        $return['status']       = $xpath->evaluate('string(//ec2:status/text())');
        $return['attachTime']   = $xpath->evaluate('string(//ec2:attachTime/text())');

        return $return;
    }

    /**
     * Detaches an Amazon EBS volume from an instance
     *
     * @param string $volumeId              The ID of the Amazon EBS volume
     * @param string $instanceId            The ID of the instance from which the volume will detach
     * @param string $device                The device name
     * @param boolean $force                Forces detachment if the previous detachment attempt did not occur cleanly
     *                                      (logging into an instance, unmounting the volume, and detaching normally).
     *                                      This option can lead to data loss or a corrupted file system. Use this option
     *                                      only as a last resort to detach an instance from a failed instance. The
     *                                      instance will not have an opportunity to flush file system caches nor
     *                                      file system meta data.
     * @return array
     */
    public function detachVolume($volumeId, $instanceId = null, $device = null, $force = false)
    {
        $params = array();
        $params['Action']       = 'DetachVolume';
        $params['VolumeId']     = $volumeId;
        $params['InstanceId']   = strval($instanceId);
        $params['Device']       = strval($device);
        $params['Force']        = strval($force);

        $response = $this->sendRequest($params);

        $xpath = $response->getXPath();

        $return = array();
        $return['volumeId']     = $xpath->evaluate('string(//ec2:volumeId/text())');
        $return['instanceId']   = $xpath->evaluate('string(//ec2:instanceId/text())');
        $return['device']       = $xpath->evaluate('string(//ec2:device/text())');
        $return['status']       = $xpath->evaluate('string(//ec2:status/text())');
        $return['attachTime']   = $xpath->evaluate('string(//ec2:attachTime/text())');

        return $return;
    }

    /**
     * Deletes an Amazon EBS volume
     *
     * @param string $volumeId              The ID of the volume to delete
     * @return boolean
     */
    public function deleteVolume($volumeId)
    {
        $params = array();
        $params['Action']       = 'DeleteVolume';
        $params['VolumeId']     = $volumeId;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();

        $return = $xpath->evaluate('string(//ec2:return/text())');

        return ($return === "true");
    }

    /**
     * Creates a snapshot of an Amazon EBS volume and stores it in Amazon S3. You can use snapshots for backups,
     * to launch instances from identical snapshots, and to save data before shutting down an instance
     *
     * @param string $volumeId              The ID of the Amazon EBS volume to snapshot
     * @return array
     */
    public function createSnapshot($volumeId)
    {
        $params = array();
        $params['Action']       = 'CreateSnapshot';
        $params['VolumeId']     = $volumeId;

        $response = $this->sendRequest($params);

        $xpath = $response->getXPath();

        $return = array();
        $return['snapshotId']   = $xpath->evaluate('string(//ec2:snapshotId/text())');
        $return['volumeId']     = $xpath->evaluate('string(//ec2:volumeId/text())');
        $return['status']       = $xpath->evaluate('string(//ec2:status/text())');
        $return['startTime']    = $xpath->evaluate('string(//ec2:startTime/text())');
        $return['progress']     = $xpath->evaluate('string(//ec2:progress/text())');

        return $return;
    }

    /**
     * Describes the status of Amazon EBS snapshots
     *
     * @param string|array $snapshotId      The ID or arry of ID's of the Amazon EBS snapshot
     * @return array
     */
    public function describeSnapshot($snapshotId = null)
    {
        $params = array();
        $params['Action'] = 'DescribeSnapshots';

        if(is_array($snapshotId) && !empty($snapshotId)) {
            foreach($snapshotId as $k=>$name) {
                $params['SnapshotId.' . ($k+1)] = $name;
            }
        } elseif($snapshotId) {
            $params['SnapshotId.1'] = $snapshotId;
        }

        $response = $this->sendRequest($params);

        $xpath  = $response->getXPath();
        $nodes = $xpath->query('//ec2:snapshotSet/ec2:item', $response->getDocument());

        $return = array();
        foreach ($nodes as $node) {
            $item = array();

            $item['snapshotId'] = $xpath->evaluate('string(ec2:snapshotId/text())', $node);
            $item['volumeId']   = $xpath->evaluate('string(ec2:volumeId/text())', $node);
            $item['status']     = $xpath->evaluate('string(ec2:status/text())', $node);
            $item['startTime']  = $xpath->evaluate('string(ec2:startTime/text())', $node);
            $item['progress']   = $xpath->evaluate('string(ec2:progress/text())', $node);

            $return[] = $item;
            unset($item, $node);
        }

        return $return;
    }

    /**
     * Deletes a snapshot of an Amazon EBS  volume that is stored in Amazon S3
     *
     * @param string $snapshotId            The ID of the Amazon EBS snapshot to delete
     * @return boolean
     */
    public function deleteSnapshot($snapshotId)
    {
        $params = array();
        $params['Action']       = 'DeleteSnapshot';
        $params['SnapshotId']   = $snapshotId;

        $response = $this->sendRequest($params);

        $xpath = $response->getXPath();
        $return = $xpath->evaluate('string(//ec2:return/text())');

        return ($return === "true");
    }
}


text:
<?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 Ec2
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Image.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * @see Zend_Service_Amazon_Ec2_Abstract
 */
require_once 'Zend/Service/Amazon/Ec2/Abstract.php';

/**
 * An Amazon EC2 interface to register, describe and deregister Amamzon Machine Instances (AMI)
 *
 * @category   Zend
 * @package    Zend_Service_Amazon
 * @subpackage Ec2
 * @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_Ec2_Image extends Zend_Service_Amazon_Ec2_Abstract
{
    /**
     * Registers an AMI with Amazon EC2. Images must be registered before
     * they can be launched.
     *
     * Each AMI is associated with an unique ID which is provided by the Amazon
     * EC2 service through the RegisterImage operation. During registration, Amazon
     * EC2 retrieves the specified image manifest from Amazon S3 and verifies that
     * the image is owned by the user registering the image.
     *
     * The image manifest is retrieved once and stored within the Amazon EC2.
     * Any modifications to an image in Amazon S3 invalidates this registration.
     * If you make changes to an image, deregister the previous image and register
     * the new image. For more information, see DeregisterImage.
     *
     * @param string $imageLocation         Full path to your AMI manifest in Amazon S3 storage.
     * @return string                       The ami fro the newly registred image;
     */
    public function register($imageLocation)
    {
        $params                 = array();
        $params['Action']       = 'RegisterImage';
        $params['ImageLocation']= $imageLocation;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();

        $amiId = $xpath->evaluate('string(//ec2:imageId/text())');

        return $amiId;
    }

    /**
     * Returns information about AMIs, AKIs, and ARIs available to the user.
     * Information returned includes image type, product codes, architecture,
     * and kernel and RAM disk IDs. Images available to the user include public
     * images available for any user to launch, private images owned by the user
     * making the request, and private images owned by other users for which the
     * user has explicit launch permissions.
     *
     * Launch permissions fall into three categories:
     *      public: The owner of the AMI granted launch permissions for the AMI
     *              to the all group. All users have launch permissions for these AMIs.
     *      explicit: The owner of the AMI granted launch permissions to a specific user.
     *      implicit: A user has implicit launch permissions for all AMIs he or she owns.
     *
     * The list of AMIs returned can be modified by specifying AMI IDs, AMI owners,
     * or users with launch permissions. If no options are specified, Amazon EC2 returns
     * all AMIs for which the user has launch permissions.
     *
     * If you specify one or more AMI IDs, only AMIs that have the specified IDs are returned.
     * If you specify an invalid AMI ID, a fault is returned. If you specify an AMI ID for which
     * you do not have access, it will not be included in the returned results.
     *
     * If you specify one or more AMI owners, only AMIs from the specified owners and for
     * which you have access are returned. The results can include the account IDs of the
     * specified owners, amazon for AMIs owned by Amazon or self for AMIs that you own.
     *
     * If you specify a list of executable users, only users that have launch permissions
     * for the AMIs are returned. You can specify account IDs (if you own the AMI(s)), self
     * for AMIs for which you own or have explicit permissions, or all for public AMIs.
     *
     * @param string|array $imageId             A list of image descriptions
     * @param string|array $owner               Owners of AMIs to describe.
     * @param string|array $executableBy        AMIs for which specified users have access.
     * @return array
     */
    public function describe($imageId = null, $owner = null, $executableBy = null)
    {
        $params = array();
        $params['Action'] = 'DescribeImages';

        if(is_array($imageId) && !empty($imageId)) {
            foreach($imageId as $k=>$name) {
                $params['ImageId.' . ($k+1)] = $name;
            }
        } elseif($imageId) {
            $params['ImageId.1'] = $imageId;
        }

        if(is_array($owner) && !empty($owner)) {
            foreach($owner as $k=>$name) {
                $params['Owner.' . ($k+1)] = $name;
            }
        } elseif($owner) {
            $params['Owner.1'] = $owner;
        }

        if(is_array($executableBy) && !empty($executableBy)) {
            foreach($executableBy as $k=>$name) {
                $params['ExecutableBy.' . ($k+1)] = $name;
            }
        } elseif($executableBy) {
            $params['ExecutableBy.1'] = $executableBy;
        }

        $response = $this->sendRequest($params);

        $xpath  = $response->getXPath();
        $nodes = $xpath->query('//ec2:imagesSet/ec2:item');

        $return = array();
        foreach ($nodes as $node) {
            $item = array();

            $item['imageId']        = $xpath->evaluate('string(ec2:imageId/text())', $node);
            $item['imageLocation']  = $xpath->evaluate('string(ec2:imageLocation/text())', $node);
            $item['imageState']     = $xpath->evaluate('string(ec2:imageState/text())', $node);
            $item['imageOwnerId']   = $xpath->evaluate('string(ec2:imageOwnerId/text())', $node);
            $item['isPublic']       = $xpath->evaluate('string(ec2:isPublic/text())', $node);
            $item['architecture']   = $xpath->evaluate('string(ec2:architecture/text())', $node);
            $item['imageType']      = $xpath->evaluate('string(ec2:imageType/text())', $node);
            $item['kernelId']       = $xpath->evaluate('string(ec2:kernelId/text())', $node);
            $item['ramdiskId']      = $xpath->evaluate('string(ec2:ramdiskId/text())', $node);
            $item['platform']       = $xpath->evaluate('string(ec2:platform/text())', $node);

            $return[] = $item;
            unset($item, $node);
        }

        return $return;
    }

    /**
     * Deregisters an AMI. Once deregistered, instances of the AMI can no longer be launched.
     *
     * @param string $imageId                   Unique ID of a machine image, returned by a call
     *                                          to RegisterImage or DescribeImages.
     * @return boolean
     */
    public function deregister($imageId)
    {
        $params                 = array();
        $params['Action']       = 'DeregisterImage';
        $params['ImageId']      = $imageId;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();

        $return = $xpath->evaluate('string(//ec2:return/text())');

        return ($return === "true");
    }

    /**
     * Modifies an attribute of an AMI.
     *
     * Valid Attributes:
     *       launchPermission:  Controls who has permission to launch the AMI. Launch permissions
     *                          can be granted to specific users by adding userIds.
     *                          To make the AMI public, add the all group.
     *       productCodes:      Associates a product code with AMIs. This allows developers to
     *                          charge users for using AMIs. The user must be signed up for the
     *                          product before they can launch the AMI. This is a write once attribute;
     *                          after it is set, it cannot be changed or removed.
     *
     * @param string $imageId                   AMI ID to modify.
     * @param string $attribute                 Specifies the attribute to modify. See the preceding
     *                                          attributes table for supported attributes.
     * @param string $operationType             Specifies the operation to perform on the attribute.
     *                                          See the preceding attributes table for supported operations for attributes.
     *                                          Valid Values: add | remove
     *                                          Required for launchPermssion Attribute
     *
     * @param string|array $userId              User IDs to add to or remove from the launchPermission attribute.
     *                                          Required for launchPermssion Attribute
     * @param string|array $userGroup           User groups to add to or remove from the launchPermission attribute.
     *                                          Currently, the all group is available, which will make it a public AMI.
     *                                          Required for launchPermssion Attribute
     * @param string $productCode               Attaches a product code to the AMI. Currently only one product code
     *                                          can be associated with an AMI. Once set, the product code cannot be changed or reset.
     *                                          Required for productCodes Attribute
     * @return boolean
     */
    public function modifyAttribute($imageId, $attribute, $operationType = 'add', $userId = null, $userGroup = null, $productCode = null)
    {
        $params = array();
        $params['Action'] = 'ModifyImageAttribute';
        $parmas['ImageId'] = $imageId;
        $params['Attribute'] = $attribute;

        switch($attribute) {
            case 'launchPermission':
                // break left out
            case 'launchpermission':
                $params['Attribute'] = 'launchPermission';
                $params['OperationType'] = $operationType;

                if(is_array($userId) && !empty($userId)) {
                    foreach($userId as $k=>$name) {
                        $params['UserId.' . ($k+1)] = $name;
                    }
                } elseif($userId) {
                    $params['UserId.1'] = $userId;
                }

                if(is_array($userGroup) && !empty($userGroup)) {
                    foreach($userGroup as $k=>$name) {
                        $params['UserGroup.' . ($k+1)] = $name;
                    }
                } elseif($userGroup) {
                    $params['UserGroup.1'] = $userGroup;
                }

                break;
            case 'productCodes':
                // break left out
            case 'productcodes':
                $params['Attribute'] = 'productCodes';
                $params['ProductCode.1'] = $productCode;
                break;
            default:
                require_once 'Zend/Service/Amazon/Ec2/Exception.php';
                throw new Zend_Service_Amazon_Ec2_Exception('Invalid Attribute Passed In.  Valid Image Attributes are launchPermission and productCode.');
                break;
        }

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();

        $return = $xpath->evaluate('string(//ec2:return/text())');

        return ($return === "true");
    }

    /**
     * Returns information about an attribute of an AMI. Only one attribute can be specified per call.
     *
     * @param string $imageId                   ID of the AMI for which an attribute will be described.
     * @param string $attribute                 Specifies the attribute to describe.  Valid Attributes are
     *                                          launchPermission, productCodes
     */
    public function describeAttribute($imageId, $attribute)
    {
        $params = array();
        $params['Action'] = 'DescribeImageAttribute';
        $params['ImageId'] = $imageId;
        $params['Attribute'] = $attribute;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();

        $return = array();
        $return['imageId'] = $xpath->evaluate('string(//ec2:imageId/text())');

        // check for launchPermission
        if($attribute == 'launchPermission') {
            $lPnodes = $xpath->query('//ec2:launchPermission/ec2:item');

            if($lPnodes->length > 0) {
                $return['launchPermission'] = array();
                foreach($lPnodes as $node) {
                    $return['launchPermission'][] = $xpath->evaluate('string(ec2:userId/text())', $node);
                }
            }
        }

        // check for product codes
        if($attribute == 'productCodes') {
            $pCnodes = $xpath->query('//ec2:productCodes/ec2:item');
            if($pCnodes->length > 0) {
                $return['productCodes'] = array();
                foreach($pCnodes as $node) {
                    $return['productCodes'][] = $xpath->evaluate('string(ec2:productCode/text())', $node);
                }
            }
        }

        return $return;

    }

    /**
     * Resets an attribute of an AMI to its default value.  The productCodes attribute cannot be reset
     *
     * @param string $imageId                   ID of the AMI for which an attribute will be reset.
     * @param String $attribute                 Specifies the attribute to reset. Currently, only launchPermission is supported.
     *                                          In the case of launchPermission, all public and explicit launch permissions for
     *                                          the AMI are revoked.
     * @return boolean
     */
    public function resetAttribute($imageId, $attribute)
    {
        $params = array();
        $params['Action'] = 'ResetImageAttribute';
        $params['ImageId'] = $imageId;
        $params['Attribute'] = $attribute;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();

        $return = $xpath->evaluate('string(//ec2:return/text())');

        return ($return === "true");
    }
}


text:
<?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 Ec2
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Securitygroups.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * @see Zend_Service_Amazon_Ec2_Abstract
 */
require_once 'Zend/Service/Amazon/Ec2/Abstract.php';

/**
 * An Amazon EC2 interface to create, delete, describe, grand and revoke sercurity permissions.
 *
 * @category   Zend
 * @package    Zend_Service_Amazon
 * @subpackage Ec2
 * @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_Ec2_Securitygroups extends Zend_Service_Amazon_Ec2_Abstract
{
    /**
     * Creates a new security group.
     *
     * Every instance is launched in a security group. If no security group is specified
     * during launch, the instances are launched in the default security group. Instances
     * within the same security group have unrestricted network access to each other.
     * Instances will reject network access attempts from other instances in a different
     * security group. As the owner of instances you can grant or revoke specific permissions
     * using the {@link authorizeIp}, {@link authorizeGroup}, {@link revokeGroup} and
     * {$link revokeIp} operations.
     *
     * @param string $name          Name of the new security group.
     * @param string $description   Description of the new security group.
     * @return boolean
     */
    public function create($name, $description)
    {
        $params = array();
        $params['Action'] = 'CreateSecurityGroup';
        $params['GroupName'] = $name;
        $params['GroupDescription'] = $description;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();
        $success  = $xpath->evaluate('string(//ec2:return/text())');

        return ($success === "true");
    }

    /**
     * Returns information about security groups that you own.
     *
     * If you specify security group names, information about those security group is returned.
     * Otherwise, information for all security group is returned. If you specify a group
     * that does not exist, a fault is returned.
     *
     * @param string|array $name    List of security groups to describe
     * @return array
     */
    public function describe($name = null)
    {
        $params = array();
        $params['Action'] = 'DescribeSecurityGroups';
        if(is_array($name) && !empty($name)) {
            foreach($name as $k=>$name) {
                $params['GroupName.' . ($k+1)] = $name;
            }
        } elseif($name) {
            $params['GroupName.1'] = $name;
        }

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();

        $return = array();

        $nodes = $xpath->query('//ec2:securityGroupInfo/ec2:item');

        foreach($nodes as $node) {
            $item = array();

            $item['ownerId'] = $xpath->evaluate('string(ec2:ownerId/text())', $node);
            $item['groupName'] = $xpath->evaluate('string(ec2:groupName/text())', $node);
            $item['groupDescription'] = $xpath->evaluate('string(ec2:groupDescription/text())', $node);

            $ip_nodes = $xpath->query('ec2:ipPermissions/ec2:item', $node);

            foreach($ip_nodes as $ip_node) {
                $sItem = array();

                $sItem['ipProtocol'] = $xpath->evaluate('string(ec2:ipProtocol/text())', $ip_node);
                $sItem['fromPort'] = $xpath->evaluate('string(ec2:fromPort/text())', $ip_node);
                $sItem['toPort'] = $xpath->evaluate('string(ec2:toPort/text())', $ip_node);

                $ips = $xpath->query('ec2:ipRanges/ec2:item', $ip_node);

                $sItem['ipRanges'] = array();
                foreach($ips as $ip) {
                    $sItem['ipRanges'][] = $xpath->evaluate('string(ec2:cidrIp/text())', $ip);
                }

                if(count($sItem['ipRanges']) == 1) {
                    $sItem['ipRanges'] = $sItem['ipRanges'][0];
                }

                $item['ipPermissions'][] = $sItem;
                unset($ip_node, $sItem);
            }

            $return[] = $item;

            unset($item, $node);
        }

        return $return;
    }

    /**
     * Deletes a security group.
     *
     * If you attempt to delete a security group that contains instances, a fault is returned.
     * If you attempt to delete a security group that is referenced by another security group,
     * a fault is returned. For example, if security group B has a rule that allows access
     * from security group A, security group A cannot be deleted until the allow rule is removed.
     *
     * @param string $name          Name of the security group to delete.
     * @return boolean
     */
    public function delete($name)
    {
        $params = array();
        $params['Action'] = 'DeleteSecurityGroup';
        $params['GroupName'] = $name;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();
        $success  = $xpath->evaluate('string(//ec2:return/text())');

        return ($success === "true");
    }

    /**
     * Adds permissions to a security group
     *
     * Permissions are specified by the IP protocol (TCP, UDP or ICMP), the source of the request
     * (by IP range or an Amazon EC2 user-group pair), the source and destination port ranges
     * (for TCP and UDP), and the ICMP codes and types (for ICMP). When authorizing ICMP, -1
     * can be used as a wildcard in the type and code fields.
     *
     * Permission changes are propagated to instances within the security group as quickly as
     * possible. However, depending on the number of instances, a small delay might occur.
     *
     *
     * @param string $name                  Name of the group to modify.
     * @param string $ipProtocol            IP protocol to authorize access to when operating on a CIDR IP.
     * @param integer $fromPort             Bottom of port range to authorize access to when operating on a CIDR IP.
     *                                      This contains the ICMP type if ICMP is being authorized.
     * @param integer $toPort               Top of port range to authorize access to when operating on a CIDR IP.
     *                                      This contains the ICMP code if ICMP is being authorized.
     * @param string $cidrIp                CIDR IP range to authorize access to when operating on a CIDR IP.
     * @return boolean
     */
    public function authorizeIp($name, $ipProtocol, $fromPort, $toPort, $cidrIp)
    {
        $params = array();
        $params['Action'] = 'AuthorizeSecurityGroupIngress';
        $params['GroupName'] = $name;
        $params['IpProtocol'] = $ipProtocol;
        $params['FromPort'] = $fromPort;
        $params['ToPort'] = $toPort;
        $params['CidrIp'] = $cidrIp;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();
        $success  = $xpath->evaluate('string(//ec2:return/text())');

        return ($success === "true");

    }

    /**
     * Adds permissions to a security group
     *
     * When authorizing a user/group pair permission, GroupName, SourceSecurityGroupName and
     * SourceSecurityGroupOwnerId must be specified.
     *
     * Permission changes are propagated to instances within the security group as quickly as
     * possible. However, depending on the number of instances, a small delay might occur.
     *
     * @param string $name                  Name of the group to modify.
     * @param string $groupName             Name of security group to authorize access to when operating on a user/group pair.
     * @param string $ownerId               Owner of security group to authorize access to when operating on a user/group pair.
     * @return boolean
     */
    public function authorizeGroup($name, $groupName, $ownerId)
    {
        $params = array();
        $params['Action'] = 'AuthorizeSecurityGroupIngress';
        $params['GroupName'] = $name;
        $params['SourceSecurityGroupName'] = $groupName;
        $params['SourceSecurityGroupOwnerId'] = $ownerId;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();
        $success  = $xpath->evaluate('string(//ec2:return/text())');

        return ($success === "true");
    }

    /**
     * Revokes permissions from a security group. The permissions used to revoke must be specified
     * using the same values used to grant the permissions.
     *
     * Permissions are specified by the IP protocol (TCP, UDP or ICMP), the source of the request
     * (by IP range or an Amazon EC2 user-group pair), the source and destination port ranges
     * (for TCP and UDP), and the ICMP codes and types (for ICMP). When authorizing ICMP, -1
     * can be used as a wildcard in the type and code fields.
     *
     * Permission changes are propagated to instances within the security group as quickly as
     * possible. However, depending on the number of instances, a small delay might occur.
     *
     *
     * @param string $name                  Name of the group to modify.
     * @param string $ipProtocol            IP protocol to revoke access to when operating on a CIDR IP.
     * @param integer $fromPort             Bottom of port range to revoke access to when operating on a CIDR IP.
     *                                      This contains the ICMP type if ICMP is being revoked.
     * @param integer $toPort               Top of port range to revoked access to when operating on a CIDR IP.
     *                                      This contains the ICMP code if ICMP is being revoked.
     * @param string $cidrIp                CIDR IP range to revoke access to when operating on a CIDR IP.
     * @return boolean
     */
    public function revokeIp($name, $ipProtocol, $fromPort, $toPort, $cidrIp)
    {
        $params = array();
        $params['Action'] = 'RevokeSecurityGroupIngress';
        $params['GroupName'] = $name;
        $params['IpProtocol'] = $ipProtocol;
        $params['FromPort'] = $fromPort;
        $params['ToPort'] = $toPort;
        $params['CidrIp'] = $cidrIp;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();
        $success  = $xpath->evaluate('string(//ec2:return/text())');

        return ($success === "true");
    }

    /**
     * Revokes permissions from a security group. The permissions used to revoke must be specified
     * using the same values used to grant the permissions.
     *
     * Permission changes are propagated to instances within the security group as quickly as
     * possible. However, depending on the number of instances, a small delay might occur.
     *
     * When revoking a user/group pair permission, GroupName, SourceSecurityGroupName and
     * SourceSecurityGroupOwnerId must be specified.
     *
     * @param string $name                  Name of the group to modify.
     * @param string $groupName             Name of security group to revoke access to when operating on a user/group pair.
     * @param string $ownerId               Owner of security group to revoke access to when operating on a user/group pair.
     * @return boolean
     */
    public function revokeGroup($name, $groupName, $ownerId)
    {
        $params = array();
        $params['Action'] = 'RevokeSecurityGroupIngress';
        $params['GroupName'] = $name;
        $params['SourceSecurityGroupName'] = $groupName;
        $params['SourceSecurityGroupOwnerId'] = $ownerId;

        $response = $this->sendRequest($params);
        $xpath = $response->getXPath();
        $success  = $xpath->evaluate('string(//ec2:return/text())');

        return ($success === "true");
    }
}


text:
<?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 Ec2
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Availabilityzones.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * @see Zend_Service_Amazon_Ec2_Abstract
 */
require_once 'Zend/Service/Amazon/Ec2/Abstract.php';

/**
 * An Amazon EC2 interface to query which Availibity Zones your account has access to.
 *
 * @category   Zend
 * @package    Zend_Service_Amazon
 * @subpackage Ec2
 * @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_Ec2_Availabilityzones extends Zend_Service_Amazon_Ec2_Abstract
{
    /**
     * Describes availability zones that are currently available to the account
     * and their states.
     *
     * @param string|array $zoneName            Name of an availability zone.
     * @return array                            An array that contains all the return items.  Keys: zoneName and zoneState.
     */
    public function describe($zoneName = null)
    {
        $params = array();
        $params['Action'] = 'DescribeAvailabilityZones';

        if(is_array($zoneName) && !empty($zoneName)) {
            foreach($zoneName as $k=>$name) {
                $params['ZoneName.' . ($k+1)] = $name;
            }
        } elseif($zoneName) {
            $params['ZoneName.1'] = $zoneName;
        }

        $response = $this->sendRequest($params);

        $xpath  = $response->getXPath();
        $nodes  = $xpath->query('//ec2:item');

        $return = array();
        foreach ($nodes as $k => $node) {
            $item = array();
            $item['zoneName']   = $xpath->evaluate('string(ec2:zoneName/text())', $node);
            $item['zoneState']  = $xpath->evaluate('string(ec2:zoneState/text())', $node);

            $return[] = $item;
            unset($item);
        }

        return $return;
    }
}


text:
<?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 Ec2
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Abstract.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * @see Zend_Service_Amazon_Abstract
 */
require_once 'Zend/Service/Amazon/Abstract.php';

/**
 * @see Zend_Service_Amazon_Ec2_Response
 */
require_once 'Zend/Service/Amazon/Ec2/Response.php';

/**
 * @see Zend_Service_Amazon_Ec2_Exception
 */
require_once 'Zend/Service/Amazon/Ec2/Exception.php';

/**
 * Provides the basic functionality to send a request to the Amazon Ec2 Query API
 *
 * @category   Zend
 * @package    Zend_Service_Amazon
 * @subpackage Ec2
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
abstract class Zend_Service_Amazon_Ec2_Abstract extends Zend_Service_Amazon_Abstract
{
    /**
     * The HTTP query server
     */
    protected $_ec2Endpoint = 'ec2.amazonaws.com';

    /**
     * The API version to use
     */
    protected $_ec2ApiVersion = '2009-04-04';

    /**
     * Signature Version
     */
    protected $_ec2SignatureVersion = '2';

    /**
     * Signature Encoding Method
     */
    protected $_ec2SignatureMethod = 'HmacSHA256';

    /**
     * Period after which HTTP request will timeout in seconds
     */
    protected $_httpTimeout = 10;

    /**
     * @var string Amazon Region
     */
    protected static $_defaultRegion = null;

    /**
     * @var string Amazon Region
     */
    protected $_region;

    /**
     * An array that contains all the valid Amazon Ec2 Regions.
     *
     * @var array
     */
    protected static $_validEc2Regions = array('eu-west-1', 'us-east-1');

    /**
     * Create Amazon 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=null, $secretKey=null, $region=null)
    {
        if(!$region) {
            $region = self::$_defaultRegion;
        } else {
            // make rue the region is valid
            if(!empty($region) && !in_array(strtolower($region), self::$_validEc2Regions, true)) {
                require_once 'Zend/Service/Amazon/Exception.php';
                throw new Zend_Service_Amazon_Exception('Invalid Amazon Ec2 Region');
            }
        }

        $this->_region = $region;

        parent::__construct($accessKey, $secretKey);
    }

    /**
     * Set which region you are working in.  It will append the
     * end point automaticly
     *
     * @param string $region
     */
    public static function setRegion($region)
    {
        if(in_array(strtolower($region), self::$_validEc2Regions, true)) {
            self::$_defaultRegion = $region;
        } else {
            require_once 'Zend/Service/Amazon/Exception.php';
            throw new Zend_Service_Amazon_Exception('Invalid Amazon Ec2 Region');
        }
    }

    /**
     * Method to fetch the AWS Region
     *
     * @return string
     */
    protected function _getRegion()
    {
        return (!empty($this->_region)) ? $this->_region . '.' : '';
    }

    /**
     * Sends a HTTP request to the queue service using Zend_Http_Client
     *
     * @param array $params         List of parameters to send with the request
     * @return Zend_Service_Amazon_Ec2_Response
     * @throws Zend_Service_Amazon_Ec2_Exception
     */
    protected function sendRequest(array $params = array())
    {
        $url = 'https://' . $this->_getRegion() . $this->_ec2Endpoint . '/';

        $params = $this->addRequiredParameters($params);

        try {
            /* @var $request Zend_Http_Client */
            $request = self::getHttpClient();
            $request->resetParameters();

            $request->setConfig(array(
                'timeout' => $this->_httpTimeout
            ));

            $request->setUri($url);
            $request->setMethod(Zend_Http_Client::POST);
            $request->setParameterPost($params);

            $httpResponse = $request->request();

        } catch (Zend_Http_Client_Exception $zhce) {
            $message = 'Error in request to AWS service: ' . $zhce->getMessage();
            throw new Zend_Service_Amazon_Ec2_Exception($message, $zhce->getCode(), $zhce);
        }
        $response = new Zend_Service_Amazon_Ec2_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->_ec2SignatureVersion;
        $parameters['Timestamp']        = gmdate('Y-m-d\TH:i:s\Z');
        $parameters['Version']          = $this->_ec2ApiVersion;
        $parameters['SignatureMethod']  = $this->_ec2SignatureMethod;
        $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->_getRegion() . $this->_ec2Endpoint . "\n";
        $data .= "/\n";

        uksort($paramaters, 'strcmp');
        unset($paramaters['Signature']);

        $arrData = array();
        foreach($paramaters as $key => $value) {
            $arrData[] = $key . '=' . str_replace("%7E", "~", rawurlencode($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_Ec2_Response $response the response object to
     *                                                   check.
     *
     * @return void
     *
     * @throws Zend_Service_Amazon_Ec2_Exception if one or more errors are
     *         returned from Amazon.
     */
    private function checkForErrors(Zend_Service_Amazon_Ec2_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_Ec2_Exception($message, 0, $code);
        }

    }
}