PHP Classes

File: gs3.php

Recommend this page to a friend!
  Classes of Cesar D. Rodas   Amazon S3 Stream Wrapper   gs3.php   Download  
File: gs3.php
Role: Class source
Content type: text/plain
Description: Main class
Class: Amazon S3 Stream Wrapper
Stream wrapper to get and send files to Amazon S3
Author: By
Last change: * Fixing error for opendir for php4
Date: 15 years ago
Size: 24,736 bytes
 

Contents

Class file image Download
<?php /** * Class that is a PHP Stream wrapper * * * This class register a stream wapper called "s3". * With this you could write, read, delete files and also * create and delete directories (buckets) as you do with * your local filesytem. * * @category PHP Stream Wrapper * @category Web Services * @package gS3 * @author Cesar D. Rodas <saddor@gmail.com> * @copyright 2007 Cesar D. Rodas * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version 1.0 * @link http://cesars.users.phpclasses.org/gs3 */ $include_dir = dirname( __FILE__ ); require_once($include_dir."/hash.php"); require_once($include_dir."/http.php"); /** * mkdir: Only the owner could have access */ define('_PRIVATE', 1); /** * mkdir: Only the owner could write, but every one could * read. */ define('_PUBLIC_READ', 2); /** * mkdir: Any one could read or write */ define('_PUBLIC_WRITE', 3); /** * fopen: Read a file. */ define('READ', 'r'); /** * fopen: Write a file as private */ define('WRITE','w'); /** * fopen: Write a file as Public read. */ define('WRITE_PUBLIC','w+r'); /** * fopen: Write a file as Public write. */ define('WRITE_PUBLIC_WRITE','w+w'); /** * Transaction Result * * This variable is a global var that is store blank * is the transaction was OK or have the error text * * @var string * @access public */ $amazonResponse; /** * Simple Storage Service stream wrapper * * @access public * @author Cesar D. Rodas <saddor@gmail.com> * @copyright 2007 Cesar D. Rodas * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @package gS3 */ class gs3_IO { /** * HTTP Connection class * @var object * @access private **/ var $http; /** * True if this class was contructed * * @var bool * @access private */ var $contructed; /** * The opened URL * * @var string * @access private */ var $path; /** * Type of open. * @var bool True for write, false only for read * @access private */ var $tOpen; /** * Type of ACL of a opened file for write * * @var int * @access private */ var $tAcl; /** * Actual position of the file * @var int * @access private */ var $position; /** * In memory file buffer. * * @var string * @access private */ var $buffer; /** * Buffer actual size * * @var int * @access private */ var $bufSize; /** * Buffer Max Size * * @var int * @access private */ var $bufActSize; /** * Array with list of files * * @var array * @access private */ var $dirList; /** * Actual file in the directory * * @var int * @access private */ var $actualDir; /** * Actual tag, used in XML parse * * @var string * @access private */ var $actualTag; /** * Stats Variable * * @var array * @access private */ var $stat; /** * Flag for End Of file * * @var bool * @access private */ var $isEOF; /** * Save the request file path * @access private * @var string */ var $reqFile; function gs3_IO() { if ($this->contructed) return; $http=new http_class; $http->timeout=0; $http->data_timeout=0; $http->debug=0; $http->html_debug=0; $http->user_agent="Cesar D. Rodas' gS3 Class (+http://cesars.phpclasses.org/gs3)"; $http->follow_redirect=1; $http->redirection_limit=5; $http->exclude_address=""; $http->protocol_version="1.1"; $this->http = &$http; $this->contructed = true; $this->position=0; $this->buffer=""; } /** * Open * * * */ function stream_open($path, $mode, $options, &$opened_path) { if ($this->getPathNumberOfComponents($path) != 2) { trigger_error("$path is not a valid amazon s3 file path. A file *must* be inside of a bucket",E_USER_NOTICE); return false; } $rmethod='PUT'; $this->tOpen=true; switch($mode) { case WRITE: case 'wb': $acl = _PRIVATE; break; case WRITE_PUBLIC: $acl = _PUBLIC_READ; break; case WRITE_PUBLIC_WRITE: $acl = _PUBLIC_WRITE; break; case READ: case 'rb': /* thanks to Jeff Arthur */ $rmethod='GET'; $this->tOpen=false; break; default: trigger_error("$mode is not supported. Visit <a href='http://cesarodas.com/gs3/gS3/_gs3.php.html#defineREAD' target='_blank'>doc</a> for further details",E_USER_NOTICE); return false; } $this->reqFile = $path; $this->initialize($path,$rmethod,$url); $http=&$this->http; $this->path = $url; if ($this->tOpen) { /* the file was opened for read, so exit, because file is do when the file is closed */ $this->tAcl = $acl; return true; } /* The file is opened for read. */ $http->GetRequestArguments($url,$arguments); /* parse arguments */ $this->getS3AuthCode('GET',$arguments); $r = $this->Process($arguments, $headers); $this->bufActSize = $headers['content-length']; global $content_type; $content_type = $headers['content-type']; return $r; } /** * Return the actual pointer position * @return int */ function stream_tell() { return $this->position; } /** * Set a new position. * @param int $offset Number of bits to move * @param int $whence SEEK_SET, SEEK_CUR or SEEK_END * @return int|bool The new position or false. */ function stream_seek($offset, $whence) { $l= $this->bufActSize; $p=&$this->position; switch ($whence) { case SEEK_SET: $newPos = $offset; break; case SEEK_CUR: $newPos = $p + $offset; break; case SEEK_END: $newPos = $l + $offset; break; default: return false; } $ret = ($newPos >=0 && $newPos <=$l); if ($ret) $p=$newPos; return $ret; } /** * Write a $data into the buffer. * * @return int|bool Numbe of bytes written or false. */ function stream_write($data){ if (!$this->tOpen) return false; $v=&$this->buffer; $l=strlen($data); $p=&$this->position; $v = substr($v, 0, $p) . $data . substr($v, $p += $l); return $l; } /** * Read a data from the S3 object * * @return string */ function stream_read($count) { if ($this->tOpen) return false; while (!$this->isEOF && $this->position+$count > $this->bufSize && $this->bufSize != $this->bufActSize) { /* the required part is not on the buffer, so download it */ $err=$this->http->ReadReplyBody($tmp,1024); if($err!="" && strlen($tmp)==0) $this->isEOF = true; $this->buffer .= $tmp; /* buffer this! */ $this->bufSize += strlen($tmp); } $ret = substr($this->buffer, $this->position, $count); $this->position += strlen($ret); return $ret; } /** * EOF * * implements the eof() */ function stream_eof() { return $this->isEOF; } /** * Close the Connection * * Close the connecting, and if the file was opened for write * the file is sended to the S3 server. * * @return bool */ function stream_close() { $http = &$this->http; $r = true; if ($this->tOpen) { $http->GetRequestArguments($this->path,$arguments); /* parse arguments */ $arguments["Body"]=&$this->buffer; $arguments['Headers']['Content-Type'] = isset($content_type) ? $content_type : $this->getMimeOfFileType($this->path); $arguments['Headers']['Content-Length'] = strlen( $arguments["Body"] ); if ($this->tAcl) $arguments['Headers']['x-amz-acl'] = $this->accessId2String( $this->tAcl ); $this->getS3AuthCode('PUT',$arguments); $r = $this->Process($arguments, $headers); } $http->Close(); return $r; } /** * Implements the fstats * * Thanks to Jeff Arthur for ask this needed feature */ function stream_stat() { return stat( $this->reqFile ); } /** * Implements the Stats * * @return array */ function url_stat($path, $flags) { if ($this->getPathNumberOfComponents($path) != 2) { trigger_error("$path is not a valid amazon s3 file path. A file *must* be inside of a bucket",E_USER_NOTICE); return false; } $this->initialize($path,'HEAD',$url); $http=&$this->http; $http->GetRequestArguments($url,$arguments); /* parse arguments */ $this->getS3AuthCode('HEAD',$arguments); $e=$this->Process($arguments, $headers); if ($e==true) { $e = array('size'=>$headers['content-length'],'mtime'=> strtotime($headers['last-modified']), 'atime' => time() ); } return $e; } /** * Create a Directory or Bucket * * Example of usage: * * <code> *<?php * include("gs3.php"); * define('S3_KEY', '059d545s4d6554'); //fake-code * define('S3_PRIVATE','dsadsadshajkdhas') //fake-code * $e=mkdir("s3://foldername",_PRIVATE||_PUBLIC_READ||_PUBLIC_WRITE); * if ($e) echo "Done"; * else echo "Error! Amazon said: ".$amazonResponse; *?> * </code> * Nested folders could not be done!, that is a Amazon S3 Limitation * * @param string $name Bucket name * @param int $mode Permision of the bucket * @return bool true if success */ function mkdir($name, $mode=_PRIVATE) { if ($this->getPathNumberOfComponents($name) != 1) { trigger_error("$path is not a valid amazon s3 a bucket",E_USER_NOTICE); return false; } $this->initialize($name,'PUT',$url); $http=&$this->http; /* * Parse the request URL into parts that * the httpclient object could process */ $http->GetRequestArguments($url,$arguments); $arguments['Headers']['x-amz-acl'] = $this->accessId2String($mode); /* * Now get the S3 Authentication code */ $this->getS3AuthCode('PUT',$arguments); $r = $this->Process($arguments, $headers); $http->Close(); return $r; } /** * Implements the unlink referece * */ function unlink($name) { if ($this->getPathNumberOfComponents($name) != 2) { trigger_error("$path is not a valid amazon s3 file path. A file *must* be inside of a bucket",E_USER_NOTICE); return false; } $this->initialize($name,'DELETE',$url); $http=&$this->http; $http->GetRequestArguments($url,$arguments); /* parse arguments */ $this->getS3AuthCode('DELETE',$arguments); return $this->Process($arguments, $headers); } /** * Implements the unlink referece * */ function rmdir($name,$options) { if ($this->getPathNumberOfComponents($path) != 1) { trigger_error("$path is not a valid amazon s3 bucket",E_USER_NOTICE); return false; } $this->initialize($name,'DELETE',$url); $http=&$this->http; $http->GetRequestArguments($url,$arguments); /* parse arguments */ $this->getS3AuthCode('DELETE',$arguments); return $this->Process($arguments, $headers); } /** * Implementing opendir() * */ function dir_opendir($path, $options) { $this->actualDir = 0; $this->dirList = array(); if ($this->getPathNumberOfComponents($path) != 1) { trigger_error("$path is not a valid amazon s3 bucket",E_USER_NOTICE); return false; } $this->initialize($path,'GET',$url); $http=&$this->http; $http->GetRequestArguments($url,$arguments); /* parse arguments */ $this->getS3AuthCode('GET',$arguments); $e=$this->Process($arguments, $headers); if ($e==true) { $response=""; for(;;) { $error=$http->ReadReplyBody($body,1000); if($error!="" || strlen($body)==0) break; $response.=($body); } $xml = xml_parser_create(); xml_parser_set_option($xml,XML_OPTION_CASE_FOLDING,true); xml_set_element_handler($xml, array(&$this,"_dirStart"),array(&$this,"_dirEnd") ); xml_set_character_data_handler($xml,array(&$this,"_dirData") ); xml_parse($xml,$response, true); xml_parser_free($xml); $http->close(); } return $e; } /** * Readdir * */ function dir_readdir() { return (count($this->dirList) > $this->actualDir) ? $this->dirList[ $this->actualDir++ ] : false; } /** * Rewind dir * */ function dir_rewinddir() { $this->actualDir=0; } /** * close dir * */ function dir_closedir() { $this->dir_rewinddir(); $this->dirList = array(); } /** * Handle start of XML tags * */ function _dirStart(&$parser,&$name,&$attribs){ $this->actualTag = $name; } /** * Handle end of XML tags * */ function _dirEnd(&$parser,&$name){ $this->actualTag = ""; } /** * Handle data of XML tags * * Save in an array when TAG == "KEY" */ function _dirData(&$parser,&$data){ if ($this->actualTag=="KEY") $this->dirList[] = $data; } /** * Initialize a the httpclient * @param string $name file name * @param string $rmethod What to do.. PUT, GET, DELETE... * @param string &$url By reference function with get the URL * @access private */ function initialize($name,$rmethod, &$url) { /* * Call class contructor */ $this->gs3_IO(); /* * Reference the httpclient object */ $http = &$this->http; /* * The calling method for create something is PUT */ $http->request_method= $rmethod; /* * Now Create the URL for request with PUT */ $name = substr($name,5); $url = "http://s3.amazonaws.com/${name}"; } /** * Open connection and ask something * * @access private * @param array $arguments * @param array &$gHeaders */ function Process($arguments,&$gHeaders) { $http = &$this->http; /* open */ $http->Open($arguments); /* send request */ $http->SendRequest($arguments); /* get response headers */ $http->ReadReplyHeaders($tmp); $gHeaders = $tmp; /* error check */ global $amazonResponse; $amazonResponse=''; $http->response_status.=""; //convert to string if ($http->response_status[0] != 2) { /*Something were wrong*/ $amazonResponse=''; for(;;) { $error=$http->ReadReplyBody($body,1000); if($error!="" || strlen($body)==0) break; $amazonResponse.=($body); } $http->Close(); return false; } return true; } /** * Return the Authentication code * * @access private * @param string $ReqMethod the kind of Request method (PUT, DELETE, GET, POST) * @param array $args The httpclient arguments */ function getS3AuthCode($ReqMethod, &$args) { $headers = &$args['Headers']; $headers['Date'] = gmdate("D, d M Y G:i:s T"); /* * building AUTH code */ $type = isset($headers['Content-Type']) ? $headers['Content-Type'] : ""; $md5 = isset($headers['Content-MD5']) ? $headers['Content-MD5'] : ""; $access = isset($headers['x-amz-acl']) ? "x-amz-acl:".$headers['x-amz-acl']."\n" : ""; $stringToSign = $ReqMethod."\n$md5\n$type\n".$headers['Date']."\n".$access; $stringToSign.= $args['RequestURI']; //die($stringToSign); $hasher =& new Crypt_HMAC(S3_PRIVATE, "sha1"); $signature = $this->hex2b64($hasher->hash($stringToSign)); $headers['Authorization'] = " AWS ".S3_KEY.":".$signature; } /** * Return the string of the access * * @access private * @param int $access Type of access * @return string The string of access */ function accessId2String($access) { switch($access) { case _PUBLIC_READ: $s = "public-read"; break; case _PUBLIC_WRITE: $s = "public-read-write"; break; default: $s = "private"; } return $s; } /** * Encode a field for amazon auth * * @access private * @param string $str String to encode * @return string */ function hex2b64($str) { $raw = ''; for ($i=0; $i < strlen($str); $i+=2) { $raw .= chr(hexdec(substr($str, $i, 2))); } return base64_encode($raw); } /** * Return the "content/type" of a file based on the file name * * @param string $name File name * @access private * @return string mime type */ function getMimeOfFileType($name) { switch(is_integer($dot=strrpos($name,".")) ? strtolower(substr($name,$dot)) : "") { case ".xls": $content_type="application/excel"; break; case ".hqx": $content_type="application/macbinhex40"; break; case ".doc": case ".dot": case ".wrd": $content_type="application/msword"; break; case ".pdf": $content_type="application/pdf"; break; case ".pgp": $content_type="application/pgp"; break; case ".ps": case ".eps": case ".ai": $content_type="application/postscript"; break; case ".ppt": $content_type="application/powerpoint"; break; case ".rtf": $content_type="application/rtf"; break; case ".tgz": case ".gtar": $content_type="application/x-gtar"; break; case ".gz": $content_type="application/x-gzip"; break; case ".php": case ".php3": $content_type="application/x-httpd-php"; break; case ".js": $content_type="application/x-javascript"; break; case ".ppd": case ".psd": $content_type="application/x-photoshop"; break; case ".swf": case ".swc": case ".rf": $content_type="application/x-shockwave-flash"; break; case ".tar": $content_type="application/x-tar"; break; case ".zip": $content_type="application/zip"; break; case ".mid": case ".midi": case ".kar": $content_type="audio/midi"; break; case ".mp2": case ".mp3": case ".mpga": $content_type="audio/mpeg"; break; case ".ra": $content_type="audio/x-realaudio"; break; case ".wav": $content_type="audio/wav"; break; case ".bmp": $content_type="image/bitmap"; break; case ".gif": $content_type="image/gif"; break; case ".iff": $content_type="image/iff"; break; case ".jb2": $content_type="image/jb2"; break; case ".jpg": case ".jpe": case ".jpeg": $content_type="image/jpeg"; break; case ".jpx": $content_type="image/jpx"; break; case ".png": $content_type="image/png"; break; case ".tif": case ".tiff": $content_type="image/tiff"; break; case ".wbmp": $content_type="image/vnd.wap.wbmp"; break; case ".xbm": $content_type="image/xbm"; break; case ".css": $content_type="text/css"; break; case ".txt": $content_type="text/plain"; break; case ".htm": case ".html": $content_type="text/html"; break; case ".xml": $content_type="text/xml"; break; case ".mpg": case ".mpe": case ".mpeg": $content_type="video/mpeg"; break; case ".qt": case ".mov": $content_type="video/quicktime"; break; case ".avi": $content_type="video/x-ms-video"; break; case ".eml": $content_type="message/rfc822"; break; default: $content_type="application/octet-stream"; break; } return $content_type; } /** * Get the number of components of a path * * Example: * s3://path/cesar = 2 * s3://path/ = 1 * * @param string $path Path * @return int * @access private */ function getPathNumberOfComponents($path) { $p = explode("/",substr($path,5, strlen($path)-6)); return count($p); } } stream_wrapper_register("s3","gs3_IO") or die("Failed to register protocol gS3"); ?>