W3TCCdnEngine_Azure

Windows Azure Storage CDN engine.

Defined (1)

The class is defined in the following location(s).

/CdnEngine_Azure.php  
  1. class CdnEngine_Azure extends CdnEngine_Base { 
  2. /** 
  3. * Storage client object 
  4. * @var Microsoft_WindowsAzure_Storage_Blob 
  5. */ 
  6. var $_client = null; 
  7.  
  8. /** 
  9. * PHP5 Constructor 
  10. * @param array $config 
  11. */ 
  12. function __construct( $config = array() ) { 
  13. $config = array_merge( array( 
  14. 'user' => '',  
  15. 'key' => '',  
  16. 'container' => '',  
  17. 'cname' => array(),  
  18. ), $config ); 
  19.  
  20. parent::__construct( $config ); 
  21.  
  22. require_once W3TC_LIB_DIR . DIRECTORY_SEPARATOR . 'Azure' . 
  23. DIRECTORY_SEPARATOR . 'loader.php'; 
  24.  
  25. /** 
  26. * Inits storage client object 
  27. * @param string $error 
  28. * @return boolean 
  29. */ 
  30. function _init( &$error ) { 
  31. if ( empty( $this->_config['user'] ) ) { 
  32. $error = 'Empty account name.'; 
  33. return false; 
  34.  
  35. if ( empty( $this->_config['key'] ) ) { 
  36. $error = 'Empty account key.'; 
  37.  
  38. return false; 
  39.  
  40. if ( empty( $this->_config['container'] ) ) { 
  41. $error = 'Empty container name.'; 
  42.  
  43. return false; 
  44.  
  45. try { 
  46. $connectionString = 'DefaultEndpointsProtocol=https;AccountName=' . 
  47. $this->_config['user'] . 
  48. ';AccountKey=' . $this->_config['key']; 
  49.  
  50. $this->_client = \MicrosoftAzure\Storage\Common\ServicesBuilder::getInstance()->createBlobService( 
  51. $connectionString); 
  52. } catch ( \Exception $ex ) { 
  53. $error = $ex->getMessage(); 
  54. return false; 
  55.  
  56.  
  57. return true; 
  58.  
  59. /** 
  60. * Uploads files to S3 
  61. * @param array $files 
  62. * @param array $results 
  63. * @param boolean $force_rewrite 
  64. * @return boolean 
  65. */ 
  66. function upload( $files, &$results, $force_rewrite = false,  
  67. $timeout_time = NULL ) { 
  68. $error = null; 
  69.  
  70. if ( !$this->_init( $error ) ) { 
  71. $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $error ); 
  72.  
  73. return false; 
  74.  
  75. foreach ( $files as $file ) { 
  76. if ( !is_null( $timeout_time ) && time() > $timeout_time ) 
  77. break; 
  78.  
  79. $remote_path = $file['remote_path']; 
  80.  
  81. $results[] = $this->_upload( $file, $force_rewrite ); 
  82.  
  83. return !$this->_is_error( $results ); 
  84.  
  85. /** 
  86. * Uploads file 
  87. * @param string $local_path 
  88. * @param string $remote_path 
  89. * @param bool $force_rewrite 
  90. * @return array 
  91. */ 
  92. function _upload( $file, $force_rewrite = false ) { 
  93. $local_path = $file['local_path']; 
  94. $remote_path = $file['remote_path']; 
  95.  
  96. if ( !file_exists( $local_path ) ) { 
  97. return $this->_get_result( $local_path, $remote_path,  
  98. W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file ); 
  99.  
  100. $contents = @file_get_contents( $local_path ); 
  101. $md5 = md5( $contents ); // @md5_file( $local_path ); 
  102. $content_md5 = $this->_get_content_md5( $md5 ); 
  103.  
  104. if ( !$force_rewrite ) { 
  105. try { 
  106. $propertiesResult = $this->_client->getBlobProperties( $this->_config['container'], $remote_path ); 
  107. $p = $propertiesResult->getProperties(); 
  108.  
  109. $local_size = @filesize( $local_path ); 
  110.  
  111. if ( $local_size == $p->getContentLength() && $content_md5 === $p->getContentMD5() ) { 
  112. return $this->_get_result( $local_path, $remote_path,  
  113. W3TC_CDN_RESULT_OK, 'File up-to-date.', $file ); 
  114. } catch ( \Exception $exception ) { 
  115.  
  116. $headers = $this->_get_headers( $file ); 
  117.  
  118. try { 
  119. // $headers 
  120. $options = new \MicrosoftAzure\Storage\Blob\Models\CreateBlobOptions(); 
  121. $options->setBlobContentMD5( $content_md5 ); 
  122. if ( isset( $headers['Content-Length'] ) ) 
  123. $options->setBlobContentLength( $headers['Content-Length'] ); 
  124. if ( isset( $headers['Content-Type'] ) ) 
  125. $options->setBlobContentType( $headers['Content-Type'] ); 
  126. if ( isset( $headers['Content-Encoding'] ) ) 
  127. $options->setBlobContentEncoding( $headers['Content-Encoding'] ); 
  128. if ( isset( $headers['Content-Language'] ) ) 
  129. $options->setBlobContentLanguage( $headers['Content-Language'] ); 
  130. if ( isset( $headers['Cache-Control'] ) ) 
  131. $options->setBlobCacheControl( $headers['Cache-Control'] ); 
  132.  
  133. $this->_client->createBlockBlob( $this->_config['container'],  
  134. $remote_path, $contents, $options ); 
  135. } catch ( \Exception $exception ) { 
  136. return $this->_get_result( $local_path, $remote_path,  
  137. W3TC_CDN_RESULT_ERROR,  
  138. sprintf( 'Unable to put blob (%s).', $exception->getMessage() ),  
  139. $file ); 
  140.  
  141. return $this->_get_result( $local_path, $remote_path, W3TC_CDN_RESULT_OK,  
  142. 'OK', $file ); 
  143.  
  144. /** 
  145. * Deletes files from storage 
  146. * @param array $files 
  147. * @param array $results 
  148. * @return boolean 
  149. */ 
  150. function delete( $files, &$results ) { 
  151. $error = null; 
  152.  
  153. if ( !$this->_init( $error ) ) { 
  154. $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $error ); 
  155.  
  156. return false; 
  157.  
  158. foreach ( $files as $file ) { 
  159. $local_path = $file['local_path']; 
  160. $remote_path = $file['remote_path']; 
  161.  
  162. try { 
  163. $r = $this->_client->deleteBlob( $this->_config['container'], $remote_path ); 
  164. $results[] = $this->_get_result( $local_path, $remote_path,  
  165. W3TC_CDN_RESULT_OK, 'OK', $file ); 
  166. } catch ( \Exception $exception ) { 
  167. $results[] = $this->_get_result( $local_path, $remote_path,  
  168. W3TC_CDN_RESULT_ERROR,  
  169. sprintf( 'Unable to delete blob (%s).', $exception->getMessage() ),  
  170. $file ); 
  171.  
  172. return !$this->_is_error( $results ); 
  173.  
  174. /** 
  175. * Tests S3 
  176. * @param string $error 
  177. * @return boolean 
  178. */ 
  179. function test( &$error ) { 
  180. if ( !parent::test( $error ) ) { 
  181. return false; 
  182.  
  183. $string = 'test_azure_' . md5( time() ); 
  184.  
  185. if ( !$this->_init( $error ) ) { 
  186. return false; 
  187.  
  188. try { 
  189. $containers = $this->_client->listContainers(); 
  190. } catch ( \Exception $exception ) { 
  191. $error = sprintf( 'Unable to list containers (%s).', $exception->getMessage() ); 
  192.  
  193. return false; 
  194.  
  195. $container = null; 
  196.  
  197. foreach ( $containers->getContainers() as $_container ) { 
  198. if ( $_container->getName() == $this->_config['container'] ) { 
  199. $container = $_container; 
  200. break; 
  201.  
  202. if ( !$container ) { 
  203. $error = sprintf( 'Container doesn\'t exist: %s.', $this->_config['container'] ); 
  204.  
  205. return false; 
  206.  
  207. try { 
  208. $this->_client->createBlockBlob( $this->_config['container'], $string, $string ); 
  209. } catch ( \Exception $exception ) { 
  210. $error = sprintf( 'Unable to create blob (%s).', $exception->getMessage() ); 
  211. return false; 
  212.  
  213. try { 
  214. $propertiesResult = $this->_client->getBlobProperties( $this->_config['container'], $string ); 
  215. $p = $propertiesResult->getProperties(); 
  216. $size = $p->getContentLength(); 
  217. $md5 = $p->getContentMD5(); 
  218. } catch ( \Exception $exception ) { 
  219. $error = sprintf( 'Unable to get blob properties (%s).', $exception->getMessage() ); 
  220. return false; 
  221.  
  222. if ( $size != strlen( $string ) || $this->_get_content_md5( md5( $string ) ) != $md5 ) { 
  223. try { 
  224. $this->_client->deleteBlob( $this->_config['container'], $string ); 
  225. } catch ( \Exception $exception ) { 
  226.  
  227. $error = 'Blob data properties are not equal.'; 
  228. return false; 
  229.  
  230. try { 
  231. $getBlob = $this->_client->getBlob( $this->_config['container'], $string ); 
  232. $dataStream = $getBlob->getContentStream(); 
  233. $data = stream_get_contents( $dataStream ); 
  234. } catch ( \Exception $exception ) { 
  235. $error = sprintf( 'Unable to get blob data (%s).', $exception->getMessage() ); 
  236. return false; 
  237.  
  238.  
  239. if ( $data != $string ) { 
  240. try { 
  241. $this->_client->deleteBlob( $this->_config['container'], $string ); 
  242. } catch ( \Exception $exception ) { 
  243.  
  244. $error = 'Blob datas are not equal.'; 
  245. return false; 
  246.  
  247. try { 
  248. $this->_client->deleteBlob( $this->_config['container'], $string ); 
  249. } catch ( \Exception $exception ) { 
  250. $error = sprintf( 'Unable to delete blob (%s).', $exception->getMessage() ); 
  251.  
  252. return false; 
  253.  
  254. return true; 
  255.  
  256. /** 
  257. * Returns CDN domain 
  258. * @return array 
  259. */ 
  260. function get_domains() { 
  261. if ( !empty( $this->_config['cname'] ) ) { 
  262. return (array) $this->_config['cname']; 
  263. } elseif ( !empty( $this->_config['user'] ) ) { 
  264. $domain = sprintf( '%s.blob.core.windows.net', $this->_config['user'] ); 
  265.  
  266. return array( 
  267. $domain 
  268. ); 
  269.  
  270. return array(); 
  271.  
  272. /** 
  273. * Returns via string 
  274. * @return string 
  275. */ 
  276. function get_via() { 
  277. return sprintf( 'Windows Azure Storage: %s', parent::get_via() ); 
  278.  
  279. /** 
  280. * Creates bucket 
  281. * @param string $container_id 
  282. * @param string $error 
  283. * @return boolean 
  284. */ 
  285. function create_container( &$container_id, &$error ) { 
  286. if ( !$this->_init( $error ) ) { 
  287. return false; 
  288.  
  289. try { 
  290. $containers = $this->_client->listContainers(); 
  291. } catch ( \Exception $exception ) { 
  292. $error = sprintf( 'Unable to list containers (%s).', $exception->getMessage() ); 
  293.  
  294. return false; 
  295.  
  296. if ( in_array( $this->_config['container'], (array) $containers ) ) { 
  297. $error = sprintf( 'Container already exists: %s.', $this->_config['container'] ); 
  298.  
  299. return false; 
  300.  
  301. try { 
  302. $createContainerOptions = new \MicrosoftAzure\Storage\Blob\Models\CreateContainerOptions(); 
  303. $createContainerOptions->setPublicAccess( 
  304. \MicrosoftAzure\Storage\Blob\Models\PublicAccessType::CONTAINER_AND_BLOBS ); 
  305.  
  306. $this->_client->createContainer( $this->_config['container'], $createContainerOptions ); 
  307. } catch ( \Exception $exception ) { 
  308. $error = sprintf( 'Unable to create container: %s (%s)', $this->_config['container'], $exception->getMessage() ); 
  309.  
  310. return false; 
  311.  
  312. return true; 
  313.  
  314. /** 
  315. * Returns Content-MD5 header value 
  316. * @param string $string 
  317. * @return string 
  318. */ 
  319. function _get_content_md5( $md5 ) { 
  320. return base64_encode( pack( 'H*', $md5 ) ); 
  321.  
  322. /** 
  323. * Formats object URL 
  324. * @param string $path 
  325. * @return string 
  326. */ 
  327. function _format_url( $path ) { 
  328. $domain = $this->get_domain( $path ); 
  329.  
  330. if ( $domain && !empty( $this->_config['container'] ) ) { 
  331. $scheme = $this->_get_scheme(); 
  332. $url = sprintf( '%s://%s/%s/%s', $scheme, $domain, $this->_config['container'], $path ); 
  333.  
  334. return $url; 
  335.  
  336. return false; 
  337.  
  338. /** 
  339. * How and if headers should be set 
  340. * @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE, W3TC_CDN_HEADER_MIRRORING 
  341. */ 
  342. function headers_support() { 
  343. return W3TC_CDN_HEADER_UPLOADABLE; 
  344.  
  345. function get_prepend_path( $path ) { 
  346. $path = parent::get_prepend_path( $path ); 
  347. $path = $this->_config['container'] ? trim( $path, '/' ) . '/' . trim( $this->_config['container'], '/' ): $path; 
  348. return $path;