PageRenderTime 97ms CodeModel.GetById 53ms app.highlight 22ms RepoModel.GetById 17ms app.codeStats 0ms

/zf/library/Zend/Service/WindowsAzure/Storage.php

http://github.com/eryx/php-framework-benchmark
PHP | 573 lines | 254 code | 67 blank | 252 comment | 36 complexity | 0f2507a9060468ecc43fac09b98d3118 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, Apache-2.0, LGPL-2.1, LGPL-3.0, BSD-2-Clause
  1<?php
  2/**
  3 * Zend Framework
  4 *
  5 * LICENSE
  6 *
  7 * This source file is subject to the new BSD license that is bundled
  8 * with this package in the file LICENSE.txt.
  9 * It is also available through the world-wide-web at this URL:
 10 * http://framework.zend.com/license/new-bsd
 11 * If you did not receive a copy of the license and are unable to
 12 * obtain it through the world-wide-web, please send an email
 13 * to license@zend.com so we can send you a copy immediately.
 14 *
 15 * @category   Zend
 16 * @package    Zend_Service_WindowsAzure
 17 * @subpackage Storage
 18 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 19 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 20 * @version    $Id: Storage.php 23775 2011-03-01 17:25:24Z ralph $
 21 */
 22
 23/**
 24 * @see Zend_Service_WindowsAzure_Credentials_CredentialsAbstract
 25 */
 26require_once 'Zend/Service/WindowsAzure/Credentials/CredentialsAbstract.php';
 27
 28/**
 29 * @see Zend_Service_WindowsAzure_Credentials_SharedKey
 30 */
 31require_once 'Zend/Service/WindowsAzure/Credentials/SharedKey.php';
 32
 33/**
 34 * @see Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract
 35 */
 36require_once 'Zend/Service/WindowsAzure/RetryPolicy/RetryPolicyAbstract.php';
 37
 38/**
 39 * @see Zend_Service_WindowsAzure_Exception
 40 */
 41require_once 'Zend/Service/WindowsAzure/Exception.php';
 42
 43/**
 44 * @see Zend_Http_Client
 45 */
 46require_once 'Zend/Http/Client.php';
 47
 48/**
 49 * @see Zend_Http_Response
 50 */
 51require_once 'Zend/Http/Response.php';
 52
 53/**
 54 * @category   Zend
 55 * @package    Zend_Service_WindowsAzure
 56 * @subpackage Storage
 57 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 58 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 59 */
 60class Zend_Service_WindowsAzure_Storage
 61{
 62	/**
 63	 * Development storage URLS
 64	 */
 65	const URL_DEV_BLOB      = "127.0.0.1:10000";
 66	const URL_DEV_QUEUE     = "127.0.0.1:10001";
 67	const URL_DEV_TABLE     = "127.0.0.1:10002";
 68	
 69	/**
 70	 * Live storage URLS
 71	 */
 72	const URL_CLOUD_BLOB    = "blob.core.windows.net";
 73	const URL_CLOUD_QUEUE   = "queue.core.windows.net";
 74	const URL_CLOUD_TABLE   = "table.core.windows.net";
 75	
 76	/**
 77	 * Resource types
 78	 */
 79	const RESOURCE_UNKNOWN     = "unknown";
 80	const RESOURCE_CONTAINER   = "c";
 81	const RESOURCE_BLOB        = "b";
 82	const RESOURCE_TABLE       = "t";
 83	const RESOURCE_ENTITY      = "e";
 84	const RESOURCE_QUEUE       = "q";
 85	
 86	/**
 87	 * HTTP header prefixes
 88	 */
 89	const PREFIX_PROPERTIES      = "x-ms-prop-";
 90	const PREFIX_METADATA        = "x-ms-meta-";
 91	const PREFIX_STORAGE_HEADER  = "x-ms-";
 92	
 93	/**
 94	 * Current API version
 95	 *
 96	 * @var string
 97	 */
 98	protected $_apiVersion = '2009-09-19';
 99	
100	/**
101	 * Storage host name
102	 *
103	 * @var string
104	 */
105	protected $_host = '';
106	
107	/**
108	 * Account name for Windows Azure
109	 *
110	 * @var string
111	 */
112	protected $_accountName = '';
113	
114	/**
115	 * Account key for Windows Azure
116	 *
117	 * @var string
118	 */
119	protected $_accountKey = '';
120	
121	/**
122	 * Use path-style URI's
123	 *
124	 * @var boolean
125	 */
126	protected $_usePathStyleUri = false;
127	
128	/**
129	 * Zend_Service_WindowsAzure_Credentials_CredentialsAbstract instance
130	 *
131	 * @var Zend_Service_WindowsAzure_Credentials_CredentialsAbstract
132	 */
133	protected $_credentials = null;
134	
135	/**
136	 * Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract instance
137	 *
138	 * @var Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract
139	 */
140	protected $_retryPolicy = null;
141	
142	/**
143	 * Zend_Http_Client channel used for communication with REST services
144	 *
145	 * @var Zend_Http_Client
146	 */
147	protected $_httpClientChannel = null;
148	
149	/**
150	 * Use proxy?
151	 *
152	 * @var boolean
153	 */
154	protected $_useProxy = false;
155	
156	/**
157	 * Proxy url
158	 *
159	 * @var string
160	 */
161	protected $_proxyUrl = '';
162	
163	/**
164	 * Proxy port
165	 *
166	 * @var int
167	 */
168	protected $_proxyPort = 80;
169	
170	/**
171	 * Proxy credentials
172	 *
173	 * @var string
174	 */
175	protected $_proxyCredentials = '';
176	
177	/**
178	 * Creates a new Zend_Service_WindowsAzure_Storage instance
179	 *
180	 * @param string $host Storage host name
181	 * @param string $accountName Account name for Windows Azure
182	 * @param string $accountKey Account key for Windows Azure
183	 * @param boolean $usePathStyleUri Use path-style URI's
184	 * @param Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy Retry policy to use when making requests
185	 */
186	public function __construct(
187		$host = self::URL_DEV_BLOB,
188		$accountName = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::DEVSTORE_ACCOUNT,
189		$accountKey = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::DEVSTORE_KEY,
190		$usePathStyleUri = false,
191		Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy = null
192	) {
193		$this->_host = $host;
194		$this->_accountName = $accountName;
195		$this->_accountKey = $accountKey;
196		$this->_usePathStyleUri = $usePathStyleUri;
197		
198		// Using local storage?
199		if (!$this->_usePathStyleUri
200			&& ($this->_host == self::URL_DEV_BLOB
201				|| $this->_host == self::URL_DEV_QUEUE
202				|| $this->_host == self::URL_DEV_TABLE)
203		) {
204			// Local storage
205			$this->_usePathStyleUri = true;
206		}
207		
208		if ($this->_credentials === null) {
209		    $this->_credentials = new Zend_Service_WindowsAzure_Credentials_SharedKey(
210		    	$this->_accountName, $this->_accountKey, $this->_usePathStyleUri);
211		}
212		
213		$this->_retryPolicy = $retryPolicy;
214		if ($this->_retryPolicy === null) {
215		    $this->_retryPolicy = Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract::noRetry();
216		}
217		
218		// Setup default Zend_Http_Client channel
219		$options = array(
220			'adapter' => 'Zend_Http_Client_Adapter_Proxy'
221		);
222		if (function_exists('curl_init')) {
223			// Set cURL options if cURL is used afterwards
224			$options['curloptions'] = array(
225					CURLOPT_FOLLOWLOCATION => true,
226					CURLOPT_TIMEOUT => 120,
227			);
228		}
229		$this->_httpClientChannel = new Zend_Http_Client(null, $options);
230	}
231	
232	/**
233	 * Set the HTTP client channel to use
234	 *
235	 * @param Zend_Http_Client_Adapter_Interface|string $adapterInstance Adapter instance or adapter class name.
236	 */
237	public function setHttpClientChannel($adapterInstance = 'Zend_Http_Client_Adapter_Proxy')
238	{
239		$this->_httpClientChannel->setAdapter($adapterInstance);
240	}
241
242    /**
243     * Retrieve HTTP client channel
244     *
245     * @return Zend_Http_Client_Adapter_Interface
246     */
247    public function getHttpClientChannel()
248    {
249        return $this->_httpClientChannel;
250    }
251	
252	/**
253	 * Set retry policy to use when making requests
254	 *
255	 * @param Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy Retry policy to use when making requests
256	 */
257	public function setRetryPolicy(Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy = null)
258	{
259		$this->_retryPolicy = $retryPolicy;
260		if ($this->_retryPolicy === null) {
261		    $this->_retryPolicy = Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract::noRetry();
262		}
263	}
264	
265	/**
266	 * Set proxy
267	 *
268	 * @param boolean $useProxy         Use proxy?
269	 * @param string  $proxyUrl         Proxy URL
270	 * @param int     $proxyPort        Proxy port
271	 * @param string  $proxyCredentials Proxy credentials
272	 */
273	public function setProxy($useProxy = false, $proxyUrl = '', $proxyPort = 80, $proxyCredentials = '')
274	{
275	    $this->_useProxy         = $useProxy;
276	    $this->_proxyUrl         = $proxyUrl;
277	    $this->_proxyPort        = $proxyPort;
278	    $this->_proxyCredentials = $proxyCredentials;
279	
280	    if ($this->_useProxy) {
281	    	$credentials = explode(':', $this->_proxyCredentials);
282	    	
283	    	$this->_httpClientChannel->setConfig(array(
284				'proxy_host' => $this->_proxyUrl,
285	    		'proxy_port' => $this->_proxyPort,
286	    		'proxy_user' => $credentials[0],
287	    		'proxy_pass' => $credentials[1],
288	    	));
289	    } else {
290			$this->_httpClientChannel->setConfig(array(
291				'proxy_host' => '',
292	    		'proxy_port' => 8080,
293	    		'proxy_user' => '',
294	    		'proxy_pass' => '',
295	    	));
296	    }
297	}
298	
299	/**
300	 * Returns the Windows Azure account name
301	 *
302	 * @return string
303	 */
304	public function getAccountName()
305	{
306		return $this->_accountName;
307	}
308	
309	/**
310	 * Get base URL for creating requests
311	 *
312	 * @return string
313	 */
314	public function getBaseUrl()
315	{
316		if ($this->_usePathStyleUri) {
317			return 'http://' . $this->_host . '/' . $this->_accountName;
318		} else {
319			return 'http://' . $this->_accountName . '.' . $this->_host;
320		}
321	}
322	
323	/**
324	 * Set Zend_Service_WindowsAzure_Credentials_CredentialsAbstract instance
325	 *
326	 * @param Zend_Service_WindowsAzure_Credentials_CredentialsAbstract $credentials Zend_Service_WindowsAzure_Credentials_CredentialsAbstract instance to use for request signing.
327	 */
328	public function setCredentials(Zend_Service_WindowsAzure_Credentials_CredentialsAbstract $credentials)
329	{
330	    $this->_credentials = $credentials;
331	    $this->_credentials->setAccountName($this->_accountName);
332	    $this->_credentials->setAccountkey($this->_accountKey);
333	    $this->_credentials->setUsePathStyleUri($this->_usePathStyleUri);
334	}
335	
336	/**
337	 * Get Zend_Service_WindowsAzure_Credentials_CredentialsAbstract instance
338	 *
339	 * @return Zend_Service_WindowsAzure_Credentials_CredentialsAbstract
340	 */
341	public function getCredentials()
342	{
343	    return $this->_credentials;
344	}
345	
346	/**
347	 * Perform request using Zend_Http_Client channel
348	 *
349	 * @param string $path Path
350	 * @param string $queryString Query string
351	 * @param string $httpVerb HTTP verb the request will use
352	 * @param array $headers x-ms headers to add
353	 * @param boolean $forTableStorage Is the request for table storage?
354	 * @param mixed $rawData Optional RAW HTTP data to be sent over the wire
355	 * @param string $resourceType Resource type
356	 * @param string $requiredPermission Required permission
357	 * @return Zend_Http_Response
358	 */
359	protected function _performRequest(
360		$path = '/',
361		$queryString = '',
362		$httpVerb = Zend_Http_Client::GET,
363		$headers = array(),
364		$forTableStorage = false,
365		$rawData = null,
366		$resourceType = Zend_Service_WindowsAzure_Storage::RESOURCE_UNKNOWN,
367		$requiredPermission = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ
368	) {
369	    // Clean path
370		if (strpos($path, '/') !== 0) {
371			$path = '/' . $path;
372		}
373			
374		// Clean headers
375		if ($headers === null) {
376		    $headers = array();
377		}
378		
379		// Ensure cUrl will also work correctly:
380		//  - disable Content-Type if required
381		//  - disable Expect: 100 Continue
382		if (!isset($headers["Content-Type"])) {
383			$headers["Content-Type"] = '';
384		}
385		$headers["Expect"]= '';
386
387		// Add version header
388		$headers['x-ms-version'] = $this->_apiVersion;
389		
390		// URL encoding
391		$path           = self::urlencode($path);
392		$queryString    = self::urlencode($queryString);
393
394        // Generate URL and sign request
395        $requestUrl     = $this->_credentials
396                          ->signRequestUrl($this->getBaseUrl() . $path . $queryString, $resourceType, $requiredPermission);
397        $requestHeaders = $this->_credentials
398                          ->signRequestHeaders($httpVerb, $path, $queryString, $headers, $forTableStorage, $resourceType, $requiredPermission, $rawData);
399
400		// Prepare request
401		$this->_httpClientChannel->resetParameters(true);
402		$this->_httpClientChannel->setUri($requestUrl);
403		$this->_httpClientChannel->setHeaders($requestHeaders);
404		$this->_httpClientChannel->setRawData($rawData);
405				
406		// Execute request
407		$response = $this->_retryPolicy->execute(
408		    array($this->_httpClientChannel, 'request'),
409		    array($httpVerb)
410		);
411		
412		return $response;
413	}
414	
415	/**
416	 * Parse result from Zend_Http_Response
417	 *
418	 * @param Zend_Http_Response $response Response from HTTP call
419	 * @return object
420	 * @throws Zend_Service_WindowsAzure_Exception
421	 */
422	protected function _parseResponse(Zend_Http_Response $response = null)
423	{
424		if ($response === null) {
425			throw new Zend_Service_WindowsAzure_Exception('Response should not be null.');
426		}
427		
428        $xml = @simplexml_load_string($response->getBody());
429
430        if ($xml !== false) {
431            // Fetch all namespaces
432            $namespaces = array_merge($xml->getNamespaces(true), $xml->getDocNamespaces(true));
433
434            // Register all namespace prefixes
435            foreach ($namespaces as $prefix => $ns) {
436                if ($prefix != '') {
437                    $xml->registerXPathNamespace($prefix, $ns);
438                }
439            }
440        }
441
442        return $xml;
443	}
444	
445	/**
446	 * Generate metadata headers
447	 *
448	 * @param array $metadata
449	 * @return HTTP headers containing metadata
450	 */
451	protected function _generateMetadataHeaders($metadata = array())
452	{
453		// Validate
454		if (!is_array($metadata)) {
455			return array();
456		}
457		
458		// Return headers
459		$headers = array();
460		foreach ($metadata as $key => $value) {
461			if (strpos($value, "\r") !== false || strpos($value, "\n") !== false) {
462				throw new Zend_Service_WindowsAzure_Exception('Metadata cannot contain newline characters.');
463			}
464			
465			if (!self::isValidMetadataName($key)) {
466		    	throw new Zend_Service_WindowsAzure_Exception('Metadata name does not adhere to metadata naming conventions. See http://msdn.microsoft.com/en-us/library/aa664670(VS.71).aspx for more information.');
467			}
468			
469		    $headers["x-ms-meta-" . strtolower($key)] = $value;
470		}
471		return $headers;
472	}
473	
474	/**
475	 * Parse metadata headers
476	 *
477	 * @param array $headers HTTP headers containing metadata
478	 * @return array
479	 */
480	protected function _parseMetadataHeaders($headers = array())
481	{
482		// Validate
483		if (!is_array($headers)) {
484			return array();
485		}
486		
487		// Return metadata
488		$metadata = array();
489		foreach ($headers as $key => $value) {
490		    if (substr(strtolower($key), 0, 10) == "x-ms-meta-") {
491		        $metadata[str_replace("x-ms-meta-", '', strtolower($key))] = $value;
492		    }
493		}
494		return $metadata;
495	}
496	
497	/**
498	 * Parse metadata XML
499	 *
500	 * @param SimpleXMLElement $parentElement Element containing the Metadata element.
501	 * @return array
502	 */
503	protected function _parseMetadataElement($element = null)
504	{
505		// Metadata present?
506		if ($element !== null && isset($element->Metadata) && $element->Metadata !== null) {
507			return get_object_vars($element->Metadata);
508		}
509
510		return array();
511	}
512	
513	/**
514	 * Generate ISO 8601 compliant date string in UTC time zone
515	 *
516	 * @param int $timestamp
517	 * @return string
518	 */
519	public function isoDate($timestamp = null)
520	{
521	    $tz = @date_default_timezone_get();
522	    @date_default_timezone_set('UTC');
523	
524	    if ($timestamp === null) {
525	        $timestamp = time();
526	    }
527	
528	    $returnValue = str_replace('+00:00', '.0000000Z', @date('c', $timestamp));
529	    @date_default_timezone_set($tz);
530	    return $returnValue;
531	}
532	
533	/**
534	 * URL encode function
535	 *
536	 * @param  string $value Value to encode
537	 * @return string        Encoded value
538	 */
539	public static function urlencode($value)
540	{
541	    return str_replace(' ', '%20', $value);
542	}
543	
544	/**
545	 * Is valid metadata name?
546	 *
547	 * @param string $metadataName Metadata name
548	 * @return boolean
549	 */
550    public static function isValidMetadataName($metadataName = '')
551    {
552        if (preg_match("/^[a-zA-Z0-9_@][a-zA-Z0-9_]*$/", $metadataName) === 0) {
553            return false;
554        }
555
556        if ($metadataName == '') {
557            return false;
558        }
559
560        return true;
561    }
562
563    /**
564     * Builds a query string from an array of elements
565     *
566     * @param array     Array of elements
567     * @return string   Assembled query string
568     */
569    public static function createQueryStringFromArray($queryString)
570    {
571        return count($queryString) > 0 ? '?' . implode('&', $queryString) : '';
572    }    
573}