S3ClientTrait.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <?php
  2. namespace Aws\S3;
  3. use Aws\Api\Parser\PayloadParserTrait;
  4. use Aws\CommandInterface;
  5. use Aws\Exception\AwsException;
  6. use Aws\HandlerList;
  7. use Aws\ResultInterface;
  8. use Aws\S3\Exception\S3Exception;
  9. use GuzzleHttp\Promise\PromiseInterface;
  10. use GuzzleHttp\Promise\RejectedPromise;
  11. use Psr\Http\Message\ResponseInterface;
  12. /**
  13. * A trait providing S3-specific functionality. This is meant to be used in
  14. * classes implementing \Aws\S3\S3ClientInterface
  15. */
  16. trait S3ClientTrait
  17. {
  18. use PayloadParserTrait;
  19. /**
  20. * @see S3ClientInterface::upload()
  21. */
  22. public function upload(
  23. $bucket,
  24. $key,
  25. $body,
  26. $acl = 'private',
  27. array $options = []
  28. ) {
  29. return $this
  30. ->uploadAsync($bucket, $key, $body, $acl, $options)
  31. ->wait();
  32. }
  33. /**
  34. * @see S3ClientInterface::uploadAsync()
  35. */
  36. public function uploadAsync(
  37. $bucket,
  38. $key,
  39. $body,
  40. $acl = 'private',
  41. array $options = []
  42. ) {
  43. return (new ObjectUploader($this, $bucket, $key, $body, $acl, $options))
  44. ->promise();
  45. }
  46. /**
  47. * @see S3ClientInterface::copy()
  48. */
  49. public function copy(
  50. $fromB,
  51. $fromK,
  52. $destB,
  53. $destK,
  54. $acl = 'private',
  55. array $opts = []
  56. ) {
  57. return $this->copyAsync($fromB, $fromK, $destB, $destK, $acl, $opts)
  58. ->wait();
  59. }
  60. /**
  61. * @see S3ClientInterface::copyAsync()
  62. */
  63. public function copyAsync(
  64. $fromB,
  65. $fromK,
  66. $destB,
  67. $destK,
  68. $acl = 'private',
  69. array $opts = []
  70. ) {
  71. $source = [
  72. 'Bucket' => $fromB,
  73. 'Key' => $fromK,
  74. ];
  75. if (isset($opts['version_id'])) {
  76. $source['VersionId'] = $opts['version_id'];
  77. }
  78. $destination = [
  79. 'Bucket' => $destB,
  80. 'Key' => $destK
  81. ];
  82. return (new ObjectCopier($this, $source, $destination, $acl, $opts))
  83. ->promise();
  84. }
  85. /**
  86. * @see S3ClientInterface::registerStreamWrapper()
  87. */
  88. public function registerStreamWrapper()
  89. {
  90. StreamWrapper::register($this);
  91. }
  92. /**
  93. * @see S3ClientInterface::deleteMatchingObjects()
  94. */
  95. public function deleteMatchingObjects(
  96. $bucket,
  97. $prefix = '',
  98. $regex = '',
  99. array $options = []
  100. ) {
  101. $this->deleteMatchingObjectsAsync($bucket, $prefix, $regex, $options)
  102. ->wait();
  103. }
  104. /**
  105. * @see S3ClientInterface::deleteMatchingObjectsAsync()
  106. */
  107. public function deleteMatchingObjectsAsync(
  108. $bucket,
  109. $prefix = '',
  110. $regex = '',
  111. array $options = []
  112. ) {
  113. if (!$prefix && !$regex) {
  114. return new RejectedPromise(
  115. new \RuntimeException('A prefix or regex is required.')
  116. );
  117. }
  118. $params = ['Bucket' => $bucket, 'Prefix' => $prefix];
  119. $iter = $this->getIterator('ListObjects', $params);
  120. if ($regex) {
  121. $iter = \Aws\filter($iter, function ($c) use ($regex) {
  122. return preg_match($regex, $c['Key']);
  123. });
  124. }
  125. return BatchDelete::fromIterator($this, $bucket, $iter, $options)
  126. ->promise();
  127. }
  128. /**
  129. * @see S3ClientInterface::uploadDirectory()
  130. */
  131. public function uploadDirectory(
  132. $directory,
  133. $bucket,
  134. $keyPrefix = null,
  135. array $options = []
  136. ) {
  137. $this->uploadDirectoryAsync($directory, $bucket, $keyPrefix, $options)
  138. ->wait();
  139. }
  140. /**
  141. * @see S3ClientInterface::uploadDirectoryAsync()
  142. */
  143. public function uploadDirectoryAsync(
  144. $directory,
  145. $bucket,
  146. $keyPrefix = null,
  147. array $options = []
  148. ) {
  149. $d = "s3://$bucket" . ($keyPrefix ? '/' . ltrim($keyPrefix, '/') : '');
  150. return (new Transfer($this, $directory, $d, $options))->promise();
  151. }
  152. /**
  153. * @see S3ClientInterface::downloadBucket()
  154. */
  155. public function downloadBucket(
  156. $directory,
  157. $bucket,
  158. $keyPrefix = '',
  159. array $options = []
  160. ) {
  161. $this->downloadBucketAsync($directory, $bucket, $keyPrefix, $options)
  162. ->wait();
  163. }
  164. /**
  165. * @see S3ClientInterface::downloadBucketAsync()
  166. */
  167. public function downloadBucketAsync(
  168. $directory,
  169. $bucket,
  170. $keyPrefix = '',
  171. array $options = []
  172. ) {
  173. $s = "s3://$bucket" . ($keyPrefix ? '/' . ltrim($keyPrefix, '/') : '');
  174. return (new Transfer($this, $s, $directory, $options))->promise();
  175. }
  176. /**
  177. * @see S3ClientInterface::determineBucketRegion()
  178. */
  179. public function determineBucketRegion($bucketName)
  180. {
  181. return $this->determineBucketRegionAsync($bucketName)->wait();
  182. }
  183. /**
  184. * @see S3ClientInterface::determineBucketRegionAsync()
  185. *
  186. * @param string $bucketName
  187. *
  188. * @return PromiseInterface
  189. */
  190. public function determineBucketRegionAsync($bucketName)
  191. {
  192. $command = $this->getCommand('HeadBucket', ['Bucket' => $bucketName]);
  193. $handlerList = clone $this->getHandlerList();
  194. $handlerList->remove('s3.permanent_redirect');
  195. $handlerList->remove('signer');
  196. $handler = $handlerList->resolve();
  197. return $handler($command)
  198. ->then(static function (ResultInterface $result) {
  199. return $result['@metadata']['headers']['x-amz-bucket-region'];
  200. }, function (AwsException $e) {
  201. $response = $e->getResponse();
  202. if ($response === null) {
  203. throw $e;
  204. }
  205. if ($e->getAwsErrorCode() === 'AuthorizationHeaderMalformed') {
  206. $region = $this->determineBucketRegionFromExceptionBody(
  207. $response
  208. );
  209. if (!empty($region)) {
  210. return $region;
  211. }
  212. throw $e;
  213. }
  214. return $response->getHeaderLine('x-amz-bucket-region');
  215. });
  216. }
  217. private function determineBucketRegionFromExceptionBody(ResponseInterface $response)
  218. {
  219. try {
  220. $element = $this->parseXml($response->getBody(), $response);
  221. if (!empty($element->Region)) {
  222. return (string)$element->Region;
  223. }
  224. } catch (\Exception $e) {
  225. // Fallthrough on exceptions from parsing
  226. }
  227. return false;
  228. }
  229. /**
  230. * @see S3ClientInterface::doesBucketExist()
  231. */
  232. public function doesBucketExist($bucket)
  233. {
  234. return $this->checkExistenceWithCommand(
  235. $this->getCommand('HeadBucket', ['Bucket' => $bucket])
  236. );
  237. }
  238. /**
  239. * @see S3ClientInterface::doesObjectExist()
  240. */
  241. public function doesObjectExist($bucket, $key, array $options = [])
  242. {
  243. return $this->checkExistenceWithCommand(
  244. $this->getCommand('HeadObject', [
  245. 'Bucket' => $bucket,
  246. 'Key' => $key
  247. ] + $options)
  248. );
  249. }
  250. /**
  251. * Determines whether or not a resource exists using a command
  252. *
  253. * @param CommandInterface $command Command used to poll for the resource
  254. *
  255. * @return bool
  256. * @throws S3Exception|\Exception if there is an unhandled exception
  257. */
  258. private function checkExistenceWithCommand(CommandInterface $command)
  259. {
  260. try {
  261. $this->execute($command);
  262. return true;
  263. } catch (S3Exception $e) {
  264. if ($e->getAwsErrorCode() == 'AccessDenied') {
  265. return true;
  266. }
  267. if ($e->getStatusCode() >= 500) {
  268. throw $e;
  269. }
  270. return false;
  271. }
  272. }
  273. /**
  274. * @see S3ClientInterface::execute()
  275. */
  276. abstract public function execute(CommandInterface $command);
  277. /**
  278. * @see S3ClientInterface::getCommand()
  279. */
  280. abstract public function getCommand($name, array $args = []);
  281. /**
  282. * @see S3ClientInterface::getHandlerList()
  283. *
  284. * @return HandlerList
  285. */
  286. abstract public function getHandlerList();
  287. /**
  288. * @see S3ClientInterface::getIterator()
  289. *
  290. * @return \Iterator
  291. */
  292. abstract public function getIterator($name, array $args = []);
  293. }