PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/public/wp-content/plugins/w3-total-cache/lib/W3/Cdn/Azure.php

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