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

https://github.com/digitalstrategyworks/Reese-WordPress · PHP · 406 lines · 325 code · 30 blank · 51 comment · 13 complexity · d638044a93462a6b3045d8928711f683 MD5 · raw file

  1. <?php
  2. /**
  3. * Amazon S3 CDN engine
  4. */
  5. require_once W3TC_LIB_W3_DIR . '/Cdn/Base.php';
  6. if (!class_exists('S3')) {
  7. require_once W3TC_LIB_DIR . '/S3.php';
  8. }
  9. /**
  10. * Class W3_Cdn_S3
  11. */
  12. class W3_Cdn_S3 extends W3_Cdn_Base {
  13. /**
  14. * S3 object
  15. *
  16. * @var S3
  17. */
  18. var $_s3 = null;
  19. /**
  20. * PHP5 Constructor
  21. *
  22. * @param array $config
  23. */
  24. function __construct($config = array()) {
  25. $config = array_merge(array(
  26. 'key' => '',
  27. 'secret' => '',
  28. 'bucket' => '',
  29. 'cname' => array(),
  30. ), $config);
  31. parent::__construct($config);
  32. }
  33. /**
  34. * PHP4 Constructor
  35. *
  36. * @param array $config
  37. */
  38. function W3_Cdn_S3($config = array()) {
  39. $this->__construct($config);
  40. }
  41. /**
  42. * Inits S3 object
  43. *
  44. * @param string $error
  45. * @return boolean
  46. */
  47. function _init(&$error) {
  48. if (empty($this->_config['key'])) {
  49. $error = 'Empty access key.';
  50. return false;
  51. }
  52. if (empty($this->_config['secret'])) {
  53. $error = 'Empty secret key.';
  54. return false;
  55. }
  56. if (empty($this->_config['bucket'])) {
  57. $error = 'Empty bucket.';
  58. return false;
  59. }
  60. $this->_s3 = & new S3($this->_config['key'], $this->_config['secret'], false);
  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 $local_path => $remote_path) {
  78. $results[] = $this->_upload($local_path, $remote_path, $force_rewrite);
  79. if ($this->_config['compression'] && $this->_may_gzip($remote_path)) {
  80. $remote_path_gzip = $remote_path . $this->_gzip_extension;
  81. $results[] = $this->_upload_gzip($local_path, $remote_path_gzip, $force_rewrite);
  82. }
  83. }
  84. return !$this->_is_error($results);
  85. }
  86. /**
  87. * Uploads single file to S3
  88. *
  89. * @param string $local_path
  90. * @param string $remote_path
  91. * @param boolean $force_rewrite
  92. * @return array
  93. */
  94. function _upload($local_path, $remote_path, $force_rewrite = false) {
  95. if (!file_exists($local_path)) {
  96. return $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_ERROR, 'Source file not found.');
  97. }
  98. if (!$force_rewrite) {
  99. $this->_set_error_handler();
  100. $info = @$this->_s3->getObjectInfo($this->_config['bucket'], $remote_path);
  101. $this->_restore_error_handler();
  102. if ($info) {
  103. $hash = @md5_file($local_path);
  104. $s3_hash = (isset($info['hash']) ? $info['hash'] : '');
  105. if ($hash === $s3_hash) {
  106. return $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_OK, 'Object up-to-date.');
  107. }
  108. }
  109. }
  110. $headers = $this->_get_headers($local_path);
  111. $this->_set_error_handler();
  112. $result = @$this->_s3->putObjectFile($local_path, $this->_config['bucket'], $remote_path, S3::ACL_PUBLIC_READ, array(), $headers);
  113. $this->_restore_error_handler();
  114. if ($result) {
  115. return $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_OK, 'OK');
  116. }
  117. return $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_ERROR, sprintf('Unable to put object (%s).', $this->_get_last_error()));
  118. }
  119. /**
  120. * Uploads gzip version of file
  121. *
  122. * @param string $local_path
  123. * @param string $remote_path
  124. * @param boolean $force_rewrite
  125. * @return array
  126. */
  127. function _upload_gzip($local_path, $remote_path, $force_rewrite = false) {
  128. if (!function_exists('gzencode')) {
  129. return $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_ERROR, "GZIP library doesn't exist.");
  130. }
  131. if (!file_exists($local_path)) {
  132. return $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_ERROR, 'Source file not found.');
  133. }
  134. $contents = @file_get_contents($local_path);
  135. if ($contents === false) {
  136. return $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_ERROR, 'Unable to read file.');
  137. }
  138. $data = gzencode($contents);
  139. if (!$force_rewrite) {
  140. $this->_set_error_handler();
  141. $info = @$this->_s3->getObjectInfo($this->_config['bucket'], $remote_path);
  142. $this->_restore_error_handler();
  143. if ($info) {
  144. $hash = md5($data);
  145. $s3_hash = (isset($info['hash']) ? $info['hash'] : '');
  146. if ($hash === $s3_hash) {
  147. return $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_OK, 'Object up-to-date.');
  148. }
  149. }
  150. }
  151. $headers = $this->_get_headers($local_path);
  152. $headers = array_merge($headers, array(
  153. 'Vary' => 'Accept-Encoding',
  154. 'Content-Encoding' => 'gzip'
  155. ));
  156. $this->_set_error_handler();
  157. $result = @$this->_s3->putObjectString($data, $this->_config['bucket'], $remote_path, S3::ACL_PUBLIC_READ, array(), $headers);
  158. $this->_restore_error_handler();
  159. if ($result) {
  160. return $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_OK, 'OK');
  161. }
  162. return $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_ERROR, sprintf('Unable to put object (%s).', $this->_get_last_error()));
  163. }
  164. /**
  165. * Deletes files from S3
  166. *
  167. * @param array $files
  168. * @param array $results
  169. * @return boolean
  170. */
  171. function delete($files, &$results) {
  172. $error = null;
  173. if (!$this->_init($error)) {
  174. $results = $this->_get_results($files, W3TC_CDN_RESULT_HALT, $error);
  175. return false;
  176. }
  177. foreach ($files as $local_path => $remote_path) {
  178. $this->_set_error_handler();
  179. $result = @$this->_s3->deleteObject($this->_config['bucket'], $remote_path);
  180. $this->_restore_error_handler();
  181. if ($result) {
  182. $results[] = $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_OK, 'OK');
  183. } else {
  184. $results[] = $this->_get_result($local_path, $remote_path, W3TC_CDN_RESULT_ERROR, sprintf('Unable to delete object (%s).', $this->_get_last_error()));
  185. }
  186. if ($this->_config['compression']) {
  187. $remote_path_gzip = $remote_path . $this->_gzip_extension;
  188. $this->_set_error_handler();
  189. $result = @$this->_s3->deleteObject($this->_config['bucket'], $remote_path_gzip);
  190. $this->_restore_error_handler();
  191. if ($result) {
  192. $results[] = $this->_get_result($local_path, $remote_path_gzip, W3TC_CDN_RESULT_OK, 'OK');
  193. } else {
  194. $results[] = $this->_get_result($local_path, $remote_path_gzip, W3TC_CDN_RESULT_ERROR, sprintf('Unable to delete object (%s).', $this->_get_last_error()));
  195. }
  196. }
  197. }
  198. return !$this->_is_error($results);
  199. }
  200. /**
  201. * Tests S3
  202. *
  203. * @param string $error
  204. * @return boolean
  205. */
  206. function test(&$error) {
  207. if (!parent::test($error)) {
  208. return false;
  209. }
  210. $string = 'test_s3_' . md5(time());
  211. if (!$this->_init($error)) {
  212. return false;
  213. }
  214. $this->_set_error_handler();
  215. $buckets = @$this->_s3->listBuckets();
  216. if ($buckets === false) {
  217. $error = sprintf('Unable to list buckets (%s).', $this->_get_last_error());
  218. $this->_restore_error_handler();
  219. return false;
  220. }
  221. if (!in_array($this->_config['bucket'], (array) $buckets)) {
  222. $error = sprintf('Bucket doesn\'t exist: %s.', $this->_config['bucket']);
  223. $this->_restore_error_handler();
  224. return false;
  225. }
  226. if (!@$this->_s3->putObjectString($string, $this->_config['bucket'], $string, S3::ACL_PUBLIC_READ)) {
  227. $error = sprintf('Unable to put object (%s).', $this->_get_last_error());
  228. $this->_restore_error_handler();
  229. return false;
  230. }
  231. if (!($object = @$this->_s3->getObject($this->_config['bucket'], $string))) {
  232. $error = sprintf('Unable to get object (%s).', $this->_get_last_error());
  233. $this->_restore_error_handler();
  234. return false;
  235. }
  236. if ($object->body != $string) {
  237. $error = 'Objects are not equal.';
  238. @$this->_s3->deleteObject($this->_config['bucket'], $string);
  239. $this->_restore_error_handler();
  240. return false;
  241. }
  242. if (!@$this->_s3->deleteObject($this->_config['bucket'], $string)) {
  243. $error = sprintf('Unable to delete object (%s).', $this->_get_last_error());
  244. $this->_restore_error_handler();
  245. return false;
  246. }
  247. $this->_restore_error_handler();
  248. return true;
  249. }
  250. /**
  251. * Returns CDN domain
  252. *
  253. * @return array
  254. */
  255. function get_domains() {
  256. if (!empty($this->_config['cname'])) {
  257. return (array) $this->_config['cname'];
  258. } elseif (!empty($this->_config['bucket'])) {
  259. $domain = sprintf('%s.s3.amazonaws.com', $this->_config['bucket']);
  260. return array(
  261. $domain
  262. );
  263. }
  264. return array();
  265. }
  266. /**
  267. * Returns via string
  268. *
  269. * @return string
  270. */
  271. function get_via() {
  272. return sprintf('Amazon Web Services: S3: %s', parent::get_via());
  273. }
  274. /**
  275. * Creates bucket
  276. *
  277. * @param string $container_id
  278. * @param string $error
  279. * @return boolean
  280. */
  281. function create_container(&$container_id, &$error) {
  282. if (!$this->_init($error)) {
  283. return false;
  284. }
  285. $this->_set_error_handler();
  286. $buckets = @$this->_s3->listBuckets();
  287. if ($buckets === false) {
  288. $error = sprintf('Unable to list buckets (%s).', $this->_get_last_error());
  289. $this->_restore_error_handler();
  290. return false;
  291. }
  292. if (in_array($this->_config['bucket'], (array) $buckets)) {
  293. $error = sprintf('Bucket already exists: %s.', $this->_config['bucket']);
  294. $this->_restore_error_handler();
  295. return false;
  296. }
  297. if (empty($this->_config['bucket_acl'])) {
  298. $this->_config['bucket_acl'] = S3::ACL_PUBLIC_READ;
  299. }
  300. if (!isset($this->_config['bucket_location'])) {
  301. $this->_config['bucket_location'] = S3::LOCATION_US;
  302. }
  303. if (!@$this->_s3->putBucket($this->_config['bucket'], $this->_config['bucket_acl'], $this->_config['bucket_location'])) {
  304. $error = sprintf('Unable to create bucket: %s (%s).', $this->_config['bucket'], $this->_get_last_error());
  305. $this->_restore_error_handler();
  306. return false;
  307. }
  308. $this->_restore_error_handler();
  309. return true;
  310. }
  311. }