PageRenderTime 126ms CodeModel.GetById 28ms RepoModel.GetById 4ms app.codeStats 0ms

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

https://github.com/shevron/zf2
PHP | 475 lines | 227 code | 55 blank | 193 comment | 29 complexity | f6345a7e9f8a16428118bbabc21e3457 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. * @package Zend_Service
  9. */
  10. namespace Zend\Service\WindowsAzure\Storage;
  11. use Zend\Http\Client;
  12. use Zend\Http\Request;
  13. use Zend\Http\Response;
  14. use Zend\Service\WindowsAzure\Credentials;
  15. use Zend\Service\WindowsAzure\Exception\DomainException;
  16. use Zend\Service\WindowsAzure\RetryPolicy;
  17. /**
  18. * @category Zend
  19. * @package Zend_Service_WindowsAzure
  20. * @subpackage Storage
  21. */
  22. class Storage
  23. {
  24. /**
  25. * Development storage URLS
  26. */
  27. const URL_DEV_BLOB = "127.0.0.1:10000";
  28. const URL_DEV_QUEUE = "127.0.0.1:10001";
  29. const URL_DEV_TABLE = "127.0.0.1:10002";
  30. /**
  31. * Live storage URLS
  32. */
  33. const URL_CLOUD_BLOB = "blob.core.windows.net";
  34. const URL_CLOUD_QUEUE = "queue.core.windows.net";
  35. const URL_CLOUD_TABLE = "table.core.windows.net";
  36. /**
  37. * Resource types
  38. */
  39. const RESOURCE_UNKNOWN = "unknown";
  40. const RESOURCE_CONTAINER = "c";
  41. const RESOURCE_BLOB = "b";
  42. const RESOURCE_TABLE = "t";
  43. const RESOURCE_ENTITY = "e";
  44. const RESOURCE_QUEUE = "q";
  45. /**
  46. * Current API version
  47. *
  48. * @var string
  49. */
  50. protected $_apiVersion = '2009-04-14';
  51. /**
  52. * Storage host name
  53. *
  54. * @var string
  55. */
  56. protected $_host = '';
  57. /**
  58. * Account name for Windows Azure
  59. *
  60. * @var string
  61. */
  62. protected $_accountName = '';
  63. /**
  64. * Account key for Windows Azure
  65. *
  66. * @var string
  67. */
  68. protected $_accountKey = '';
  69. /**
  70. * Use path-style URI's
  71. *
  72. * @var boolean
  73. */
  74. protected $_usePathStyleUri = false;
  75. /**
  76. * Credentials\AbstractCredentials instance
  77. *
  78. * @var Credentials\AbstractCredentials
  79. */
  80. protected $_credentials = null;
  81. /**
  82. * RetryPolicy\AbstractRetryPolicy instance
  83. *
  84. * @var RetryPolicy\AbstractRetryPolicy
  85. */
  86. protected $_retryPolicy = null;
  87. /**
  88. * Client channel used for communication with REST services
  89. *
  90. * @var Client
  91. */
  92. protected $_httpClientChannel = null;
  93. /**
  94. * Use proxy?
  95. *
  96. * @var boolean
  97. */
  98. protected $_useProxy = false;
  99. /**
  100. * Proxy url
  101. *
  102. * @var string
  103. */
  104. protected $_proxyUrl = '';
  105. /**
  106. * Proxy port
  107. *
  108. * @var int
  109. */
  110. protected $_proxyPort = 80;
  111. /**
  112. * Proxy credentials
  113. *
  114. * @var string
  115. */
  116. protected $_proxyCredentials = '';
  117. /**
  118. * Creates a new Zend_Service_WindowsAzure_Storage instance
  119. *
  120. * @param string $host Storage host name
  121. * @param string $accountName Account name for Windows Azure
  122. * @param string $accountKey Account key for Windows Azure
  123. * @param boolean $usePathStyleUri Use path-style URI's
  124. * @param RetryPolicy\AbstractRetryPolicy $retryPolicy Retry policy to use when making requests
  125. */
  126. public function __construct(
  127. $host = self::URL_DEV_BLOB,
  128. $accountName = Credentials\AbstractCredentials::DEVSTORE_ACCOUNT,
  129. $accountKey = Credentials\AbstractCredentials::DEVSTORE_KEY,
  130. $usePathStyleUri = false,
  131. RetryPolicy\AbstractRetryPolicy $retryPolicy = null
  132. )
  133. {
  134. $this->_host = $host;
  135. $this->_accountName = $accountName;
  136. $this->_accountKey = $accountKey;
  137. $this->_usePathStyleUri = $usePathStyleUri;
  138. // Using local storage?
  139. if (!$this->_usePathStyleUri
  140. && ($this->_host == self::URL_DEV_BLOB
  141. || $this->_host == self::URL_DEV_QUEUE
  142. || $this->_host == self::URL_DEV_TABLE)
  143. ) {
  144. // Local storage
  145. $this->_usePathStyleUri = true;
  146. }
  147. if ($this->_credentials === null) {
  148. $this->_credentials = new Credentials\SharedKey(
  149. $this->_accountName, $this->_accountKey, $this->_usePathStyleUri);
  150. }
  151. $this->_retryPolicy = $retryPolicy;
  152. if ($this->_retryPolicy === null) {
  153. $this->_retryPolicy = RetryPolicy\AbstractRetryPolicy::noRetry();
  154. }
  155. // Setup default Client channel
  156. $options = array(
  157. 'adapter' => 'Zend\\Http\\Client\\Adapter\\Proxy'
  158. );
  159. if (function_exists('curl_init')) {
  160. // Set cURL options if cURL is used afterwards
  161. $options['curloptions'] = array(
  162. CURLOPT_FOLLOWLOCATION => true,
  163. CURLOPT_TIMEOUT => 120,
  164. );
  165. }
  166. $this->_httpClientChannel = new Client(null, $options);
  167. }
  168. /**
  169. * Set the HTTP client channel to use
  170. *
  171. * @param Client\Adapter\AdapterInterface|string $adapterInstance Adapter instance or adapter class name.
  172. */
  173. public function setHttpClientChannel($adapterInstance = 'Zend\Http\Client\Adapter\Proxy')
  174. {
  175. $this->_httpClientChannel->setAdapter($adapterInstance);
  176. }
  177. /**
  178. * Set retry policy to use when making requests
  179. *
  180. * @param RetryPolicy\AbstractRetryPolicy $retryPolicy Retry policy to use when making requests
  181. */
  182. public function setRetryPolicy(RetryPolicy\AbstractRetryPolicy $retryPolicy = null)
  183. {
  184. $this->_retryPolicy = $retryPolicy;
  185. if ($this->_retryPolicy === null) {
  186. $this->_retryPolicy = RetryPolicy\AbstractRetryPolicy::noRetry();
  187. }
  188. }
  189. /**
  190. * Set proxy
  191. *
  192. * @param boolean $useProxy Use proxy?
  193. * @param string $proxyUrl Proxy URL
  194. * @param int $proxyPort Proxy port
  195. * @param string $proxyCredentials Proxy credentials
  196. */
  197. public function setProxy($useProxy = false, $proxyUrl = '', $proxyPort = 80, $proxyCredentials = '')
  198. {
  199. $this->_useProxy = $useProxy;
  200. $this->_proxyUrl = $proxyUrl;
  201. $this->_proxyPort = $proxyPort;
  202. $this->_proxyCredentials = $proxyCredentials;
  203. if ($this->_useProxy) {
  204. $credentials = explode(':', $this->_proxyCredentials);
  205. if (!isset($credentials[1])) {
  206. $credentials[1] = '';
  207. }
  208. $this->_httpClientChannel->setOptions(array(
  209. 'proxy_host' => $this->_proxyUrl,
  210. 'proxy_port' => $this->_proxyPort,
  211. 'proxy_user' => $credentials[0],
  212. 'proxy_pass' => $credentials[1],
  213. ));
  214. } else {
  215. $this->_httpClientChannel->setOptions(array(
  216. 'proxy_host' => '',
  217. 'proxy_port' => 8080,
  218. 'proxy_user' => '',
  219. 'proxy_pass' => '',
  220. ));
  221. }
  222. }
  223. /**
  224. * Returns the Windows Azure account name
  225. *
  226. * @return string
  227. */
  228. public function getAccountName()
  229. {
  230. return $this->_accountName;
  231. }
  232. /**
  233. * Get base URL for creating requests
  234. *
  235. * @return string
  236. */
  237. public function getBaseUrl()
  238. {
  239. if ($this->_usePathStyleUri) {
  240. return 'http://' . $this->_host . '/' . $this->_accountName;
  241. } else {
  242. return 'http://' . $this->_accountName . '.' . $this->_host;
  243. }
  244. }
  245. /**
  246. * Set Credentials\AbstractCredentials instance
  247. *
  248. * @param Credentials\AbstractCredentials $credentials Credentials\AbstractCredentials instance to use for request signing.
  249. */
  250. public function setCredentials(Credentials\AbstractCredentials $credentials)
  251. {
  252. $this->_credentials = $credentials;
  253. $this->_credentials->setAccountName($this->_accountName);
  254. $this->_credentials->setAccountkey($this->_accountKey);
  255. $this->_credentials->setUsePathStyleUri($this->_usePathStyleUri);
  256. }
  257. /**
  258. * Get Credentials\AbstractCredentials instance
  259. *
  260. * @return Credentials\AbstractCredentials
  261. */
  262. public function getCredentials()
  263. {
  264. return $this->_credentials;
  265. }
  266. /**
  267. * Perform request using Client channel
  268. *
  269. * @param string $path Path
  270. * @param string $queryString Query string
  271. * @param string $httpVerb HTTP verb the request will use
  272. * @param array $headers x-ms headers to add
  273. * @param boolean $forTableStorage Is the request for table storage?
  274. * @param mixed $rawData Optional RAW HTTP data to be sent over the wire
  275. * @param string $resourceType Resource type
  276. * @param string $requiredPermission Required permission
  277. * @return Response
  278. */
  279. protected function _performRequest(
  280. $path = '/',
  281. $queryString = '',
  282. $httpVerb = Request::METHOD_GET,
  283. $headers = array(),
  284. $forTableStorage = false,
  285. $rawData = null,
  286. $resourceType = Storage::RESOURCE_UNKNOWN,
  287. $requiredPermission = Credentials\AbstractCredentials::PERMISSION_READ
  288. )
  289. {
  290. // Clean path
  291. if (strpos($path, '/') !== 0) {
  292. $path = '/' . $path;
  293. }
  294. // Clean headers
  295. if ($headers === null) {
  296. $headers = array();
  297. }
  298. // Ensure cUrl will also work correctly:
  299. // - disable Content-Type if required
  300. // - disable Expect: 100 Continue
  301. if (!isset($headers["Content-Type"])) {
  302. $headers["Content-Type"] = '';
  303. }
  304. $headers["Expect"] = '';
  305. // Add version header
  306. $headers['x-ms-version'] = $this->_apiVersion;
  307. // URL encoding
  308. $path = self::urlencode($path);
  309. $queryString = self::urlencode($queryString);
  310. // Generate URL and sign request
  311. $requestUrl = $this->_credentials
  312. ->signRequestUrl($this->getBaseUrl() . $path . $queryString, $resourceType, $requiredPermission);
  313. $requestHeaders = $this->_credentials
  314. ->signRequestHeaders($httpVerb, $path, $queryString, $headers, $forTableStorage, $resourceType,
  315. $requiredPermission);
  316. // Prepare request
  317. $this->_httpClientChannel->resetParameters(true);
  318. $this->_httpClientChannel->setUri($requestUrl);
  319. $this->_httpClientChannel->setHeaders($requestHeaders);
  320. $this->_httpClientChannel->setRawBody($rawData);
  321. // Execute request
  322. $response = $this->_retryPolicy->execute(
  323. array($this->_httpClientChannel, 'request'),
  324. array($httpVerb)
  325. );
  326. return $response;
  327. }
  328. /**
  329. * Parse result from Response
  330. *
  331. * @param Response $response Response from HTTP call
  332. * @return \SimpleXMLElement
  333. */
  334. protected function _parseResponse(Response $response)
  335. {
  336. $xml = @simplexml_load_string($response->getBody());
  337. if ($xml !== false) {
  338. // Fetch all namespaces
  339. $namespaces = array_merge($xml->getNamespaces(true), $xml->getDocNamespaces(true));
  340. // Register all namespace prefixes
  341. foreach ($namespaces as $prefix => $ns) {
  342. if ($prefix != '') {
  343. $xml->registerXPathNamespace($prefix, $ns);
  344. }
  345. }
  346. }
  347. return $xml;
  348. }
  349. /**
  350. * Generate metadata headers
  351. *
  352. * @param array $metadata
  353. * @throws DomainException
  354. * @return array HTTP headers containing metadata
  355. */
  356. protected function _generateMetadataHeaders($metadata = array())
  357. {
  358. // Validate
  359. if (!is_array($metadata)) {
  360. return array();
  361. }
  362. // Return headers
  363. $headers = array();
  364. foreach ($metadata as $key => $value) {
  365. if (strpos($value, "\r") !== false || strpos($value, "\n") !== false) {
  366. throw new DomainException('Metadata cannot contain newline characters.');
  367. }
  368. $headers['x-ms-meta-' . strtolower($key)] = $value;
  369. }
  370. return $headers;
  371. }
  372. /**
  373. * Parse metadata errors
  374. *
  375. * @param array $headers HTTP headers containing metadata
  376. * @return array
  377. */
  378. protected function _parseMetadataHeaders($headers = array())
  379. {
  380. // Validate
  381. if (!is_array($headers)) {
  382. return array();
  383. }
  384. // Return metadata
  385. $metadata = array();
  386. foreach ($headers as $key => $value) {
  387. if (substr(strtolower($key), 0, 10) == 'x-ms-meta-') {
  388. $metadata[str_replace('x-ms-meta-', '', strtolower($key))] = $value;
  389. }
  390. }
  391. return $metadata;
  392. }
  393. /**
  394. * Generate ISO 8601 compliant date string in UTC time zone
  395. *
  396. * @param int $timestamp
  397. * @return string
  398. */
  399. public function isoDate($timestamp = null)
  400. {
  401. $tz = @date_default_timezone_get();
  402. @date_default_timezone_set('UTC');
  403. if ($timestamp === null) {
  404. $timestamp = time();
  405. }
  406. $returnValue = str_replace('+00:00', '.0000000Z', @date('c', $timestamp));
  407. @date_default_timezone_set($tz);
  408. return $returnValue;
  409. }
  410. /**
  411. * URL encode function
  412. *
  413. * @param string $value Value to encode
  414. * @return string Encoded value
  415. */
  416. public static function urlencode($value)
  417. {
  418. return str_replace(' ', '%20', $value);
  419. }
  420. }