3 * Amazon S3 REST API Implementation
5 * This a generic PHP class that can hook-in to Amazon's S3 Simple Storage Service
7 * Contributions and/or donations are welcome.
9 * Author: Geoffrey P. Gaudreault
10 * http://www.neurofuzzy.net
12 * This code is free, provided AS-IS with no warranty expressed or implied. Use at your own risk.
13 * If you find errors or bugs in this code, please contact me at interested@zanpo.com
14 * If you enhance this code in any way, please send me an update. Thank you!
17 * Last Updated: 9/09/2006
19 * NOTE: ENTER YOUR API ID AND SECRET KEY BELOW!!!
25 // The API access point URL
26 var $S3_URL = "http://s3.amazonaws.com/";
28 // list of valid actions (validation not implemented yet)
29 var $verbs = array("GET"=>1, "DELETE"=>1, "PUT"=>1);
31 // set to true to echo debug info
34 // -----------------------------------------
35 // -----------------------------------------
38 // your API Secret Key
40 // -----------------------------------------
41 // -----------------------------------------
47 var $_acl = "private";
49 // default content type
50 var $_contentType = "application/octet-stream";
52 // default response content type
53 var $_responseContentType = "text/xml";
55 // bucket object name prefix
58 // bucket list marker (useful for pagination)
61 // number of keys to retrieve in a list
67 // your default bucket name
68 var $bucketname = "slideroll_photos";
70 // your current object name
71 var $objectname = "temp";
75 * Constructor: Amazon S3 REST API implementation
77 function s3($options = NULL) {
79 define('DATE_RFC822', 'D, d M Y H:i:s T');
80 $this->httpDate
= gmdate(DATE_RFC822
);
82 $available_options = array("acl", "contentType");
84 if (is_array($options)) {
86 foreach ($options as $key => $value) {
88 $this->debug_text("Option: $key");
90 if (in_array($key, $available_options) ) {
92 $this->debug_text("Valid Config options: $key");
94 $this->$property = $value;
95 $this->debug_text("Setting $property to $value");
99 $this->debug_text("ERROR: Config option: $key is not a valid option");
107 // REQUIRES PEAR PACKAGE
108 // get with "pear install Crypt_HMAC"
109 require_once 'Crypt/HMAC.php';
111 $this->hasher
=& new Crypt_HMAC($this->secretKey
, "sha1");
113 // REQUIRES PEAR PACKAGE
114 // get with "pear install --onlyreqdeps HTTP_Request"
118 // Note that version HTTP_Request 1.3.0 has a BUG in it! Change line
120 // (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_postData) && empty($this->_postFiles))) {
122 // (HTTP_REQUEST_METHOD_POST == $this->_method && empty($this->_postData) && empty($this->_postFiles))) {
124 // Without this change PUTs with non-empty content-type will fail!
126 require_once 'HTTP/Request.php';
132 * Method: setBucketName
133 * Sets the name of the default bucket
135 function setBucketName ($bucket) {
136 $this->bucketname
= $bucket;
141 * Method: getBucketName
142 * Gets the name of the default bucket
144 function getBucketName () {
145 return $this->bucketname
;
150 * Method: setBucketName
151 * Sets the name of the default bucket
153 function setObjectName ($object) {
154 $this->objectname
= $object;
159 * Method: getObjectName
160 * Gets the name of the current object
162 function getObjectName () {
163 return $this->objectname
;
168 * Method: setContentType
169 * Sets the content type of the object
171 function setContentType ($ct) {
172 $this->_contentType
= $ct;
177 * Method: getContentType
178 * Gets the content type of the object
180 function getContentType () {
181 return $this->_contentType
;
186 * Method: getResponseContentType
187 * Gets the content type of the response
189 function getResponseContentType () {
190 return $this->_responseContentType
;
196 * sets the acces control policy for the current object
198 function setAcl ($acl) {
205 * gets the acces control policy for the current object
213 * Method: sendRequest
214 * Sends the request to S3
217 * resource - the name of the resource to act upon
218 * verb - the action to apply to the resource (GET, PUT, DELETE, HEAD)
219 * objectdata - the source data (body) of the resource (only applies to objects)
220 * acl - the access control policy for the resource
221 * contentType - the contentType of the resource (only applies to objects)
222 * metadata - any metadata you want to save in the header of the object
224 function sendRequest ($resource, $verb = NULL, $objectdata = NULL, $acl = NULL, $contentType = NULL, $metadata = NULL) {
233 $aclstring = "x-amz-acl:$acl\n";
236 $contenttypestring = "";
238 if ($contentType != NULL && ($verb == "PUT") && ($objectdata != NULL) && ($objectdata != "")) {
239 $contenttypestring = "$contentType";
242 // update date / time on each request
243 $this->httpDate
= gmdate(DATE_RFC822
);
244 // $this->httpDate = str_replace("0000","000",str_replace("+",".",gmdate(DATE_ISO8601))) . "Z";
246 $httpDate = $this->httpDate
;
251 if (strlen($this->prefix
)) {
253 $paramstring .= $delim."prefix=".urlencode($this->prefix
);
258 if (strlen($this->marker
)) {
260 $paramstring .= $delim."marker=".urlencode($this->marker
);
265 if (strlen($this->max_keys
)) {
267 $paramstring .= $delim."max-keys=".$this->max_keys
;
272 if (strlen($this->delimiter
)) {
274 $paramstring .= $delim."delimiter=".urlencode($this->delimiter
);
279 $this->debug_text("HTTP Request sent to: " . $this->S3_URL
. $resource . $paramstring);
281 $req =& new HTTP_Request($this->S3_URL
. $resource . $paramstring);
282 $req->setMethod($verb);
284 if (($objectdata != NULL) && ($objectdata != "")) {
286 $contentMd5 = $this->hex2b64(md5($objectdata));
287 $req->addHeader("CONTENT-MD5", $contentMd5);
288 $this->debug_text("MD5 HASH OF DATA: " . $contentMd5);
290 $contentmd5string = $contentMd5;
294 $contentmd5string = "";
298 if (strlen($contenttypestring)) {
299 $this->debug_text("Setting content type to $contentType");
300 $req->addHeader("CONTENT-TYPE", $contentType);
303 $req->addHeader("DATE", $httpDate);
305 if (strlen($aclstring)) {
306 $this->debug_text("Setting acl string to $acl");
307 $req->addHeader("x-amz-acl", $acl);
310 $metadatastring = "";
312 if (is_array($metadata)) {
316 $this->debug_text("Metadata found.");
318 foreach ($metadata as $key => $value) {
320 $metadatastring .= "x-amz-meta-".$key.":".trim($value)."\n";
322 $req->addHeader("x-amz-meta-".$key, trim($value));
324 $this->debug_text("Setting x-amz-meta-$key to '$value'");
330 if (($objectdata != NULL) && ($objectdata != "")) {
332 $req->setBody($objectdata);
336 $stringToSign = "$verb\n$contentmd5string\n$contenttypestring\n$httpDate\n$aclstring$metadatastring/$resource";
337 // $stringToSign = "$verb\n$contentmd5string\n$httpDate\n$aclstring$metadatastring/$resource";
338 $this->debug_text("Signing String: $stringToSign");
339 $signature = $this->hex2b64($this->hasher
->hash($stringToSign));
340 $this->debug_text("Signed string: $signature");
341 $req->addHeader("Authorization", "AWS " . $this->keyId
. ":" . $signature);
345 $this->_responseContentType
= $req->getResponseHeader("content-type");
347 if (strlen($req->getResponseBody())) {
349 $this->debug_text($req->getResponseBody());
350 return $req->getResponseBody();
354 $this->debug_text($req->getResponseHeader());
355 return $req->getResponseHeader();
364 * Returns a list of all buckets
366 function getBuckets () {
367 return $this->sendRequest("","GET");
373 * Gets a list of all objects in the default bucket
375 function getBucket ($bucketname = NULL) {
377 if ($bucketname == NULL) {
379 return $this->sendRequest($this->bucketname
,"GET");
383 return $this->sendRequest($bucketname,"GET");
392 * Gets a list of all objects in the specified bucket
395 * prefix - (optional) Limits the response to keys which begin with the indicated prefix. You can use prefixes to separate a bucket into different sets of keys in a way similar to how a file system uses folders.
396 * marker - (optional) Indicates where in the bucket to begin listing. The list will only include keys that occur lexicographically after marker. This is convenient for pagination: To get the next page of results use the last key of the current page as the marker.
397 * max-keys - (optional) The maximum number of keys you'd like to see in the response body. The server may return fewer than this many keys, but will not return more.
399 function getObjects ($bucketname, $prefix = NULL, $marker = NULL, $max_keys = NULL, $delimiter = NULL) {
401 if ($prefix != NULL) {
403 $this->prefix
= $prefix;
411 if ($marker != NULL) {
413 $this->marker
= $marker;
421 if ($max_keys != NULL) {
423 $this->max_keys
= $max_keys;
427 $this->max_keys
= "";
431 if ($delimiter != NULL) {
433 $this->delimiter
= $delimiter;
437 $this->delimiter
= "";
441 if ($bucketname != NULL) {
443 return $this->sendRequest($bucketname,"GET");
455 * Method: getObjectInfo
456 * Get header information about the object. The HEAD operation is used to retrieve information about a specific object,
457 * without actually fetching the object itself
460 * objectname - The name of the object to get information about
461 * bucketname - (optional) the name of the bucket containing the object. If none is supplied, the default bucket is used
463 function getObjectInfo ($objectname, $bucketname = NULL) {
464 if ($bucketname == NULL) {
465 $bucketname = $this->bucketname
;
467 return $this->sendRequest($bucketname."/".$objectname,"HEAD");
473 * Gets an object from S3
476 * objectname - the name of the object to get
477 * bucketname - (optional) the name of the bucket containing the object. If none is supplied, the default bucket is used
479 function getObject ($objectname, $bucketname = NULL) {
480 if ($bucketname == NULL) {
481 $bucketname = $this->bucketname
;
483 return $this->sendRequest($bucketname."/".$objectname,"GET");
489 * Creates a new bucket in S3
492 * bucketname - the name of the bucket. It must be unique. No other S3 users may have this bucket name
494 function putBucket ($bucketname) {
495 return $this->sendRequest($bucketname,"PUT");
501 * Puts an object into S3
504 * objectname - the name of the object to put
505 * objectdata - the source data (body) of the resource (only applies to objects)
506 * bucketname - (optional) the name of the bucket containing the object. If none is supplied, the default bucket is used
507 * acl - the access control policy for the resource
508 * contentType - the contentType of the resource (only applies to objects)
509 * metadata - any metadata you want to save in the header of the object
511 function putObject ($objectname, $objectdata, $bucketname = NULL, $acl = NULL, $contentType = NULL, $metadata = NULL) {
513 if ($bucketname == NULL) {
514 $bucketname = $this->bucketname
;
517 if ($acl == NULL ||
$acl == "") {
521 if ($contentType == NULL ||
$contentType == "") {
522 $contentType = $this->_contentType
;
525 if ($objectdata != NULL) {
526 return $this->sendRequest($bucketname."/".$objectname, "PUT", $objectdata, $acl, $contentType, $metadata);
535 * Method: deleteBucket
536 * Deletes bucket in S3. The bucket name will fall into the public domain.
538 function deleteBucket ($bucketname) {
539 return $this->sendRequest($bucketname, "DELETE");
544 * Method: deleteObject
545 * Deletes an object from S3
548 * objectname - the name of the object to delete
549 * bucketname - (optional) the name of the bucket containing the object. If none is supplied, the default bucket is used
551 function deleteObject ($objectname, $bucketname = NULL) {
553 if ($bucketname == NULL) {
555 $bucketname = $this->bucketname
;
559 return $this->sendRequest($bucketname."/".$objectname, "DELETE");
566 * Utility function for constructing signatures
568 function hex2b64($str) {
571 for ($i=0; $i < strlen($str); $i+
=2) {
572 $raw .= chr(hexdec(substr($str, $i, 2)));
574 return base64_encode($raw);
581 * Echoes debug information to the browser. Set this->debug to false for production use
583 function debug_text($text) {
588 echo("<br><br>\n\n");