http://www.mediawiki.org/wiki/Special:Code/MediaWiki/100667
Revision: 100667 Author: aaron Date: 2011-10-25 00:10:50 +0000 (Tue, 25 Oct 2011) Log Message: ----------- Add some quick & dirty fake cloudfiles classes for local testing. Objects are just stored to FS. Added Paths: ----------- trunk/extensions/SwiftMedia/php-fakecloudfiles/ trunk/extensions/SwiftMedia/php-fakecloudfiles/fake_cloudfiles.php Added: trunk/extensions/SwiftMedia/php-fakecloudfiles/fake_cloudfiles.php =================================================================== --- trunk/extensions/SwiftMedia/php-fakecloudfiles/fake_cloudfiles.php (rev 0) +++ trunk/extensions/SwiftMedia/php-fakecloudfiles/fake_cloudfiles.php 2011-10-25 00:10:50 UTC (rev 100667) @@ -0,0 +1,1181 @@ +<?php +/** + * Quick & Dirty mock cloud files classes for testing. + * Doesn't actually do authentication or HTTP stuff, just calls the methods. + * Objects are just stored on the File System under the container and object name. + * $IP/fake_cloudfiles.config.php must exist and set FAKE_SWIFT_CONTAINER_PATH, + * which is the directory that holds the container directories. + */ + +/** + */ +if ( file_exists( dirname( __FILE__ ) . "/../../../fake_cloudfiles.config.php" ) ) { + require_once( dirname( __FILE__ ) . "/../../../fake_cloudfiles.config.php" ); +} else { + die( "Nothing to see here.\n" ); +} +require_once( dirname( __FILE__ ) . "/../php-cloudfiles/cloudfiles_exceptions.php" ); +define("DEFAULT_CF_API_VERSION", 1); +define("MAX_CONTAINER_NAME_LEN", 256); +define("MAX_OBJECT_NAME_LEN", 1024); +define("MAX_OBJECT_SIZE", 5*1024*1024*1024+1); +define("US_AUTHURL", "https://auth.api.rackspacecloud.com"); +define("UK_AUTHURL", "https://lon.auth.api.rackspacecloud.com"); +/** + * Class for handling Cloud Files Authentication, call it's {@link authenticate()} + * method to obtain authorized service urls and an authentication token. + * + * Example: + * <code> + * # Create the authentication instance + * # + * $auth = new CF_Authentication("username", "api_key"); + * + * # NOTE: For UK Customers please specify your AuthURL Manually + * # There is a Predfined constant to use EX: + * # + * # $auth = new CF_Authentication("username, "api_key", NULL, UK_AUTHURL); + * # Using the UK_AUTHURL keyword will force the api to use the UK AuthUrl. + * # rather then the US one. The NULL Is passed for legacy purposes and must + * # be passed to function correctly. + * + * # NOTE: Some versions of cURL include an outdated certificate authority (CA) + * # file. This API ships with a newer version obtained directly from + * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle, + * # call the CF_Authentication instance's 'ssl_use_cabundle()' method. + * # + * # $auth->ssl_use_cabundle(); # bypass cURL's old CA bundle + * + * # Perform authentication request + * # + * $auth->authenticate(); + * </code> + * + * @package php-cloudfiles + */ +class CF_Authentication +{ + public $dbug; + public $username; + public $api_key; + public $auth_host; + public $account; + + /** + * Instance variables that are set after successful authentication + */ + public $storage_url; + public $cdnm_url; + public $auth_token; + + /** + * Class constructor (PHP 5 syntax) + * + * @param string $username Mosso username + * @param string $api_key Mosso API Access Key + * @param string $account <i>Account name</i> + * @param string $auth_host <i>Authentication service URI</i> + */ + function __construct($username=NULL, $api_key=NULL, $account=NULL, $auth_host=US_AUTHURL) + { + + $this->dbug = False; + $this->username = $username; + $this->api_key = $api_key; + $this->account_name = $account; + $this->auth_host = $auth_host; + + $this->storage_url = NULL; + $this->cdnm_url = NULL; + $this->auth_token = NULL; + + $this->cfs_http = NULL; + } + + /** + * Attempt to validate Username/API Access Key + * + * Attempts to validate credentials with the authentication service. It + * either returns <kbd>True</kbd> or throws an Exception. Accepts a single + * (optional) argument for the storage system API version. + * + * Example: + * <code> + * # Create the authentication instance + * # + * $auth = new CF_Authentication("username", "api_key"); + * + * # Perform authentication request + * # + * $auth->authenticate(); + * </code> + * + * @param string $version API version for Auth service (optional) + * @return boolean <kbd>True</kbd> if successfully authenticated + * @throws AuthenticationException invalid credentials + * @throws InvalidResponseException invalid response + */ + function authenticate($version=DEFAULT_CF_API_VERSION) + { + $this->storage_url = 'http://localhost/swift/storage/someuserurl'; + $this->cdnm_url = 'http://localhost/swift/curl/someuserurl'; + $this->auth_token = 'somerandomishtokenstring'; + return True; + } + /** + * Use Cached Token and Storage URL's rather then grabbing from the Auth System + * + * Example: + * <code> + * #Create an Auth instance + * $auth = new CF_Authentication(); + * #Pass Cached URL's and Token as Args + * $auth->load_cached_credentials("auth_token", "storage_url", "cdn_management_url"); + * </code> + * + * @param string $auth_token A Cloud Files Auth Token (Required) + * @param string $storage_url The Cloud Files Storage URL (Required) + * @param string $cdnm_url CDN Management URL (Required) + * @return boolean <kbd>True</kbd> if successful + * @throws SyntaxException If any of the Required Arguments are missing + */ + function load_cached_credentials($auth_token, $storage_url, $cdnm_url) + { + $this->storage_url = $storage_url; + $this->cdnm_url = $cdnm_url; + $this->auth_token = $auth_token; + return True; + } + /** + * Grab Cloud Files info to be Cached for later use with the load_cached_credentials method. + * + * Example: + * <code> + * #Create an Auth instance + * $auth = new CF_Authentication("UserName","API_Key"); + * $auth->authenticate(); + * $array = $auth->export_credentials(); + * </code> + * + * @return array of url's and an auth token. + */ + function export_credentials() + { + $arr = array(); + $arr['storage_url'] = $this->storage_url; + $arr['cdnm_url'] = $this->cdnm_url; + $arr['auth_token'] = $this->auth_token; + + return $arr; + } + + + /** + * Make sure the CF_Authentication instance has authenticated. + * + * Ensures that the instance variables necessary to communicate with + * Cloud Files have been set from a previous authenticate() call. + * + * @return boolean <kbd>True</kbd> if successfully authenticated + */ + function authenticated() + { + if (!($this->storage_url || $this->cdnm_url) || !$this->auth_token) { + return False; + } + return True; + } + + /** + * Toggle debugging - set cURL verbose flag + */ + function setDebug($bool) + { + $this->dbug = $bool; + } +} + +/** + * Class for establishing connections to the Cloud Files storage system. + * Connection instances are used to communicate with the storage system at + * the account level; listing and deleting Containers and returning Container + * instances. + * + * Example: + * <code> + * # Create the authentication instance + * # + * $auth = new CF_Authentication("username", "api_key"); + * + * # Perform authentication request + * # + * $auth->authenticate(); + * + * # Create a connection to the storage/cdn system(s) and pass in the + * # validated CF_Authentication instance. + * # + * $conn = new CF_Connection($auth); + * + * # NOTE: Some versions of cURL include an outdated certificate authority (CA) + * # file. This API ships with a newer version obtained directly from + * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle, + * # call the CF_Authentication instance's 'ssl_use_cabundle()' method. + * # + * # $conn->ssl_use_cabundle(); # bypass cURL's old CA bundle + * </code> + * + * @package php-cloudfiles + */ +class CF_Connection +{ + public $dbug; + public $cfs_auth; + + /** + * Pass in a previously authenticated CF_Authentication instance. + * + * Example: + * <code> + * # Create the authentication instance + * # + * $auth = new CF_Authentication("username", "api_key"); + * + * # Perform authentication request + * # + * $auth->authenticate(); + * + * # Create a connection to the storage/cdn system(s) and pass in the + * # validated CF_Authentication instance. + * # + * $conn = new CF_Connection($auth); + * + * # If you are connecting via Rackspace servers and have access + * # to the servicenet network you can set the $servicenet to True + * # like this. + * + * $conn = new CF_Connection($auth, $servicenet=True); + * + * </code> + * + * If the environement variable RACKSPACE_SERVICENET is defined it will + * force to connect via the servicenet. + * + * @param obj $cfs_auth previously authenticated CF_Authentication instance + * @param boolean $servicenet enable/disable access via Rackspace servicenet. + * @throws AuthenticationException not authenticated + */ + function __construct($cfs_auth, $servicenet=False) + { + if (isset($_ENV['RACKSPACE_SERVICENET'])) + $servicenet=True; + $this->cfs_auth = $cfs_auth; + if (!$this->cfs_auth->authenticated()) { + $e = "Need to pass in a previously authenticated "; + $e .= "CF_Authentication instance."; + throw new AuthenticationException($e); + } + $this->dbug = False; + } + + /** + * Toggle debugging of instance and back-end HTTP module + * + * @param boolean $bool enable/disable cURL debugging + */ + function setDebug($bool) + { + $this->dbug = (boolean) $bool; + } + + /** + * Close a connection + * + * Example: + * <code> + * + * $conn->close(); + * + * </code> + * + * Will close all current cUrl active connections. + * + */ + public function close() + { + } + + /** + * Create a Container + * + * Given a Container name, return a Container instance, creating a new + * remote Container if it does not exit. + * + * Example: + * <code> + * # ... authentication code excluded (see previous examples) ... + * # + * $conn = new CF_Authentication($auth); + * + * $images = $conn->create_container("my photos"); + * </code> + * + * @param string $container_name container name + * @return CF_Container + * @throws SyntaxException invalid name + * @throws InvalidResponseException unexpected response + */ + function create_container($container_name=NULL) + { + if ($container_name != "0" and !isset($container_name)) + throw new SyntaxException("Container name not set."); + + if (!isset($container_name) or $container_name == "") + throw new SyntaxException("Container name not set."); + + if (strpos($container_name, "/") !== False) { + $r = "Container name '".$container_name; + $r .= "' cannot contain a '/' character."; + throw new SyntaxException($r); + } + if (strlen($container_name) > MAX_CONTAINER_NAME_LEN) { + throw new SyntaxException(sprintf( + "Container name exeeds %d bytes.", + MAX_CONTAINER_NAME_LEN)); + } + + if ( !is_dir( FAKE_SWIFT_CONTAINER_PATH . "/$container_name" ) ) { + mkdir( FAKE_SWIFT_CONTAINER_PATH . "/$container_name" ); + } + + return new CF_Container($this->cfs_auth, $this->cfs_http, $container_name); + } + + /** + * Delete a Container + * + * Given either a Container instance or name, remove the remote Container. + * The Container must be empty prior to removing it. + * + * Example: + * <code> + * # ... authentication code excluded (see previous examples) ... + * # + * $conn = new CF_Authentication($auth); + * + * $conn->delete_container("my photos"); + * </code> + * + * @param string|obj $container container name or instance + * @return boolean <kbd>True</kbd> if successfully deleted + * @throws SyntaxException missing proper argument + * @throws InvalidResponseException invalid response + * @throws NonEmptyContainerException container not empty + * @throws NoSuchContainerException remote container does not exist + */ + function delete_container($container=NULL) + { + $container_name = NULL; + + if (is_object($container)) { + if (get_class($container) == "CF_Container") { + $container_name = $container->name; + } + } + if (is_string($container)) { + $container_name = $container; + } + + if ($container_name != "0" and !isset($container_name)) + throw new SyntaxException("Must specify container object or name."); + + if ( !is_dir( FAKE_SWIFT_CONTAINER_PATH . "/$container_name" ) ) { + throw new NoSuchContainerException( + "Specified container did not exist to delete."); + } + if ( !rmdir( FAKE_SWIFT_CONTAINER_PATH . "/$container_name" ) ) { + throw new NonEmptyContainerException( + "Container must be empty prior to removing it."); + } + return True; + } + + /** + * Return a Container instance + * + * For the given name, return a Container instance if the remote Container + * exists, otherwise throw a Not Found exception. + * + * Example: + * <code> + * # ... authentication code excluded (see previous examples) ... + * # + * $conn = new CF_Authentication($auth); + * + * $images = $conn->get_container("my photos"); + * print "Number of Objects: " . $images->count . "\n"; + * print "Bytes stored in container: " . $images->bytes . "\n"; + * </code> + * + * @param string $container_name name of the remote Container + * @return container CF_Container instance + * @throws NoSuchContainerException thrown if no remote Container + * @throws InvalidResponseException unexpected response + */ + function get_container($container_name=NULL) + { + if ( !is_dir( FAKE_SWIFT_CONTAINER_PATH . "/$container_name" ) ) { + throw new NoSuchContainerException("Container not found."); + } + $count = $bytes = -1; // not implemented + return new CF_Container($this->cfs_auth, $this->cfs_http, + $container_name, $count, $bytes); + } +} + +/** + * Container operations + * + * Containers are storage compartments where you put your data (objects). + * A container is similar to a directory or folder on a conventional filesystem + * with the exception that they exist in a flat namespace, you can not create + * containers inside of containers. + * + * You also have the option of marking a Container as "public" so that the + * Objects stored in the Container are publicly available via the CDN. + * + * @package php-cloudfiles + */ +class CF_Container +{ + public $cfs_auth; + public $cfs_http; + public $name; + public $object_count; + public $bytes_used; + + public $cdn_enabled; + public $cdn_ssl_uri; + public $cdn_uri; + public $cdn_ttl; + public $cdn_log_retention; + public $cdn_acl_user_agent; + public $cdn_acl_referrer; + + /** + * Class constructor + * + * Constructor for Container + * + * @param obj $cfs_auth CF_Authentication instance + * @param obj $cfs_http HTTP connection manager + * @param string $name name of Container + * @param int $count number of Objects stored in this Container + * @param int $bytes number of bytes stored in this Container + * @throws SyntaxException invalid Container name + */ + function __construct(&$cfs_auth, &$cfs_http, $name, $count=0, + $bytes=0, $docdn=True) + { + if (strlen($name) > MAX_CONTAINER_NAME_LEN) { + throw new SyntaxException("Container name exceeds " + . "maximum allowed length."); + } + if (strpos($name, "/") !== False) { + throw new SyntaxException( + "Container names cannot contain a '/' character."); + } + $this->cfs_auth = $cfs_auth; + $this->cfs_http = $cfs_http; + $this->name = $name; + $this->object_count = $count; + $this->bytes_used = $bytes; + $this->cdn_enabled = NULL; + $this->cdn_uri = NULL; + $this->cdn_ssl_uri = NULL; + $this->cdn_ttl = NULL; + $this->cdn_log_retention = NULL; + $this->cdn_acl_user_agent = NULL; + $this->cdn_acl_referrer = NULL; + } + + /** + * Create a new remote storage Object + * + * Return a new Object instance. If the remote storage Object exists, + * the instance's attributes are populated. + * + * Example: + * <code> + * # ... authentication code excluded (see previous examples) ... + * # + * $conn = new CF_Authentication($auth); + * + * $public_container = $conn->get_container("public"); + * + * # This creates a local instance of a storage object but only creates + * # it in the storage system when the object's write() method is called. + * # + * $pic = $public_container->create_object("baby.jpg"); + * </code> + * + * @param string $obj_name name of storage Object + * @return obj CF_Object instance + */ + function create_object($obj_name=NULL) + { + return new CF_Object($this, $obj_name); + } + + /** + * Return an Object instance for the remote storage Object + * + * Given a name, return a Object instance representing the + * remote storage object. + * + * Example: + * <code> + * # ... authentication code excluded (see previous examples) ... + * # + * $conn = new CF_Authentication($auth); + * + * $public_container = $conn->get_container("public"); + * + * # This call only fetches header information and not the content of + * # the storage object. Use the Object's read() or stream() methods + * # to obtain the object's data. + * # + * $pic = $public_container->get_object("baby.jpg"); + * </code> + * + * @param string $obj_name name of storage Object + * @return obj CF_Object instance + */ + function get_object($obj_name=NULL) + { + return new CF_Object($this, $obj_name, True); + } + + /** + * Return a list of Objects + * + * Return an array of strings listing the Object names in this Container. + * + * Example: + * <code> + * # ... authentication code excluded (see previous examples) ... + * # + * $images = $conn->get_container("my photos"); + * + * # Grab the list of all storage objects + * # + * $all_objects = $images->list_objects(); + * + * # Grab subsets of all storage objects + * # + * $first_ten = $images->list_objects(10); + * + * # Note the use of the previous result's last object name being + * # used as the 'marker' parameter to fetch the next 10 objects + * # + * $next_ten = $images->list_objects(10, $first_ten[count($first_ten)-1]); + * + * # Grab images starting with "birthday_party" and default limit/marker + * # to match all photos with that prefix + * # + * $prefixed = $images->list_objects(0, NULL, "birthday"); + * + * # Assuming you have created the appropriate directory marker Objects, + * # you can traverse your pseudo-hierarchical containers + * # with the "path" argument. + * # + * $animals = $images->list_objects(0,NULL,NULL,"pictures/animals"); + * $dogs = $images->list_objects(0,NULL,NULL,"pictures/animals/dogs"); + * </code> + * + * @param int $limit <i>optional</i> only return $limit names + * @param int $marker <i>optional</i> subset of names starting at $marker + * @param string $prefix <i>optional</i> Objects whose names begin with $prefix + * @param string $path <i>optional</i> only return results under "pathname" + * @return array array of strings + * @throws InvalidResponseException unexpected response + */ + function list_objects($limit=0, $marker=NULL, $prefix=NULL, $path=NULL) + { + $names = array(); + // $prefix must be a dir in mock class + $dir = FAKE_SWIFT_CONTAINER_PATH . "/{$this->name}/$prefix"; + if ( is_dir( $dir ) ) { + $handle = opendir( $dir ); + if ( $handle ) { + while ( false !== ( $file = readdir( $handle ) ) ) { + if ( $file { 0 } != '.' ) { + $names[] = $file; + } + } + closedir( $handle ); + } + } + return $names; + } + + /** + * Delete a remote storage Object + * + * Given an Object instance or name, permanently remove the remote Object + * and all associated metadata. + * + * Example: + * <code> + * # ... authentication code excluded (see previous examples) ... + * # + * $conn = new CF_Authentication($auth); + * + * $images = $conn->get_container("my photos"); + * + * # Delete specific object + * # + * $images->delete_object("disco_dancing.jpg"); + * </code> + * + * @param obj $obj name or instance of Object to delete + * @return boolean <kbd>True</kbd> if successfully removed + * @throws SyntaxException invalid Object name + * @throws NoSuchObjectException remote Object does not exist + * @throws InvalidResponseException unexpected response + */ + function delete_object($obj) + { + $obj_name = NULL; + if (is_object($obj)) { + if (get_class($obj) == "CF_Object") { + $obj_name = $obj->name; + } + } + if (is_string($obj)) { + $obj_name = $obj; + } + if (!$obj_name) { + throw new SyntaxException("Object name not set."); + } + if ( !@unlink( $this->getObjectFSPath( $obj_name ) ) ) { + $m = "Specified object '".$this->name."/".$obj_name; + $m.= "' did not exist to delete."; + throw new NoSuchObjectException($m); + } + return True; + } + + function getObjectFSPath( $file ) { + return FAKE_SWIFT_CONTAINER_PATH . "/{$this->name}/{$file}"; + } +} + + +/** + * Object operations + * + * An Object is analogous to a file on a conventional filesystem. You can + * read data from, or write data to your Objects. You can also associate + * arbitrary metadata with them. + * + * @package php-cloudfiles + */ +class CF_Object +{ + public $container; + public $name; + public $last_modified; + public $content_type; + public $content_length; + public $metadata; + public $manifest; + private $etag; + + /** + * Class constructor + * + * @param obj $container CF_Container instance + * @param string $name name of Object + * @param boolean $force_exists if set, throw an error if Object doesn't exist + */ + function __construct(&$container, $name, $force_exists=False, $dohead=True) + { + if ($name[0] == "/") { + $r = "Object name '".$name; + $r .= "' cannot contain begin with a '/' character."; + throw new SyntaxException($r); + } + if (strlen($name) > MAX_OBJECT_NAME_LEN) { + throw new SyntaxException("Object name exceeds " + . "maximum allowed length."); + } + $this->container = $container; + $this->name = $name; + $this->etag = NULL; + $this->_etag_override = False; + $this->last_modified = NULL; + $this->content_type = NULL; + $this->content_length = 0; + $this->metadata = array(); + $this->manifest = NULL; + if ($dohead) { + if (!$this->_initialize() && $force_exists) { + throw new NoSuchObjectException("No such object '".$name."'"); + } + } + } + + /** + * String representation of Object + * + * Pretty print the Object's location and name + * + * @return string Object information + */ + function __toString() + { + return $this->container->name . "/" . $this->name; + } + + /** + * Internal check to get the proper mimetype. + * + * This function would go over the available PHP methods to get + * the MIME type. + * + * By default it will try to use the PHP fileinfo library which is + * available from PHP 5.3 or as an PECL extension + * (http://pecl.php.net/package/Fileinfo). + * + * It will get the magic file by default from the system wide file + * which is usually available in /usr/share/magic on Unix or try + * to use the file specified in the source directory of the API + * (share directory). + * + * if fileinfo is not available it will try to use the internal + * mime_content_type function. + * + * @param string $handle name of file or buffer to guess the type from + * @return boolean <kbd>True</kbd> if successful + * @throws BadContentTypeException + */ + function _guess_content_type($handle) { + if ($this->content_type) + return; + + if (function_exists("finfo_open")) { + $local_magic = dirname(__FILE__) . "/share/magic"; + $finfo = @finfo_open(FILEINFO_MIME, $local_magic); + + if (!$finfo) + $finfo = @finfo_open(FILEINFO_MIME); + + if ($finfo) { + + if (is_file((string)$handle)) + $ct = @finfo_file($finfo, $handle); + else + $ct = @finfo_buffer($finfo, $handle); + + /* PHP 5.3 fileinfo display extra information like + charset so we remove everything after the ; since + we are not into that stuff */ + if ($ct) { + $extra_content_type_info = strpos($ct, "; "); + if ($extra_content_type_info) + $ct = substr($ct, 0, $extra_content_type_info); + } + + if ($ct && $ct != 'application/octet-stream') + $this->content_type = $ct; + + @finfo_close($finfo); + } + } + + if (!$this->content_type && (string)is_file($handle) && function_exists("mime_content_type")) { + $this->content_type = @mime_content_type($handle); + } + + if (!$this->content_type) { + throw new BadContentTypeException("Required Content-Type not set"); + } + return True; + } + + function getFSPath() { + return $this->container->getObjectFSPath( $this->name ); + } + + /** + * Read the remote Object's data + * + * Returns the Object's data. This is useful for smaller Objects such + * as images or office documents. Object's with larger content should use + * the stream() method below. + * + * Pass in $hdrs array to set specific custom HTTP headers such as + * If-Match, If-None-Match, If-Modified-Since, Range, etc. + * + * Example: + * <code> + * # ... authentication/connection/container code excluded + * # ... see previous examples + * + * $my_docs = $conn->get_container("documents"); + * $doc = $my_docs->get_object("README"); + * $data = $doc->read(); # read image content into a string variable + * print $data; + * + * # Or see stream() below for a different example. + * # + * </code> + * + * @param array $hdrs user-defined headers (Range, If-Match, etc.) + * @return string Object's data + * @throws InvalidResponseException unexpected response + */ + function read($hdrs=array()) + { + return file_get_contents( $this->getFSPath() ); + } + + /** + * Streaming read of Object's data + * + * Given an open PHP resource (see PHP's fopen() method), fetch the Object's + * data and write it to the open resource handle. This is useful for + * streaming an Object's content to the browser (videos, images) or for + * fetching content to a local file. + * + * Pass in $hdrs array to set specific custom HTTP headers such as + * If-Match, If-None-Match, If-Modified-Since, Range, etc. + * + * Example: + * <code> + * # ... authentication/connection/container code excluded + * # ... see previous examples + * + * # Assuming this is a web script to display the README to the + * # user's browser: + * # + * <?php + * // grab README from storage system + * // + * $my_docs = $conn->get_container("documents"); + * $doc = $my_docs->get_object("README"); + * + * // Hand it back to user's browser with appropriate content-type + * // + * header("Content-Type: " . $doc->content_type); + * $output = fopen("php://output", "w"); + * $doc->stream($output); # stream object content to PHP's output buffer + * fclose($output); + * ?> + * + * # See read() above for a more simple example. + * # + * </code> + * + * @param resource $fp open resource for writing data to + * @param array $hdrs user-defined headers (Range, If-Match, etc.) + * @return string Object's data + * @throws InvalidResponseException unexpected response + */ + function stream(&$fp, $hdrs=array()) + { + $data = file_get_contents( $this->getFSPath() ); + fwrite( $fp, $data ); + return True; + } + + /** + * Upload Object's data to Cloud Files + * + * Write data to the remote Object. The $data argument can either be a + * PHP resource open for reading (see PHP's fopen() method) or an in-memory + * variable. If passing in a PHP resource, you must also include the $bytes + * parameter. + * + * Example: + * <code> + * # ... authentication/connection/container code excluded + * # ... see previous examples + * + * $my_docs = $conn->get_container("documents"); + * $doc = $my_docs->get_object("README"); + * + * # Upload placeholder text in my README + * # + * $doc->write("This is just placeholder text for now..."); + * </code> + * + * @param string|resource $data string or open resource + * @param float $bytes amount of data to upload (required for resources) + * @param boolean $verify generate, send, and compare MD5 checksums + * @return boolean <kbd>True</kbd> when data uploaded successfully + * @throws SyntaxException missing required parameters + * @throws BadContentTypeException if no Content-Type was/could be set + * @throws MisMatchedChecksumException $verify is set and checksums unequal + * @throws InvalidResponseException unexpected response + */ + function write($data=NULL, $bytes=0, $verify=True) + { + if (!$data && !is_string($data)) { + throw new SyntaxException("Missing data source."); + } + if ($bytes > MAX_OBJECT_SIZE) { + throw new SyntaxException("Bytes exceeds maximum object size."); + } + if ($verify) { + if (!$this->_etag_override) { + $this->etag = $this->compute_md5sum($data); + } + } else { + $this->etag = NULL; + } + + $close_fh = False; + if (!is_resource($data)) { + # A hack to treat string data as a file handle. php://memory feels + # like a better option, but it seems to break on Windows so use + # a temporary file instead. + # + $fp = fopen("php://temp", "wb+"); + #$fp = fopen("php://memory", "wb+"); + fwrite($fp, $data, strlen($data)); + rewind($fp); + $close_fh = True; + $this->content_length = (float) strlen($data); + if ($this->content_length > MAX_OBJECT_SIZE) { + throw new SyntaxException("Data exceeds maximum object size"); + } + $ct_data = substr($data, 0, 64); + } else { + $this->content_length = $bytes; + $fp = $data; + $ct_data = fread($data, 64); + rewind($data); + } + + $this->_guess_content_type($ct_data); + + $dir = dirname( $this->getFSPath() ); + if ( !file_exists( $dir ) ) { + mkdir( $dir, 0777, true ); + } + file_put_contents( $this->getFSPath(), $data, LOCK_EX ); + + if ($close_fh) { fclose($fp); } + return True; + } + + /** + * Copy one Object to another Object to Cloud Files + * + * Example: + * <code> + * # ... authentication/connection/container code excluded + * # ... see previous examples + * + * $my_docs = $conn->get_container("documents"); + * $doc = $my_docs->get_object("README"); + * + * # Copy README.txt on top of this object (which you must have + * already written something to). + * # + * $doc->copy("/documents/README.txt"); + * </code> + * + * @param string $source Name of existing object + * @return boolean <kbd>True</kbd> when data uploaded successfully + * @throws SyntaxException missing required parameters + * @throws BadContentTypeException if no Content-Type was/could be set + * @throws MisMatchedChecksumException $verify is set and checksums unequal + * @throws InvalidResponseException unexpected response + */ + function copy($source) + { + if (!$source && !is_string($source)) { + throw new SyntaxException("Missing data source."); + } + $sObj = new self( $this->container, $source ); + copy( $sObj->getFSPath(), $this->getFSPath() ); + return True; + } + + /** + * Upload Object data from local filename + * + * This is a convenience function to upload the data from a local file. A + * True value for $verify will cause the method to compute the Object's MD5 + * checksum prior to uploading. + * + * Example: + * <code> + * # ... authentication/connection/container code excluded + * # ... see previous examples + * + * $my_docs = $conn->get_container("documents"); + * $doc = $my_docs->get_object("README"); + * + * # Upload my local README's content + * # + * $doc->load_from_filename("/home/ej/cloudfiles/readme"); + * </code> + * + * @param string $filename full path to local file + * @param boolean $verify enable local/remote MD5 checksum validation + * @return boolean <kbd>True</kbd> if data uploaded successfully + * @throws SyntaxException missing required parameters + * @throws BadContentTypeException if no Content-Type was/could be set + * @throws MisMatchedChecksumException $verify is set and checksums unequal + * @throws InvalidResponseException unexpected response + * @throws IOException error opening file + */ + function load_from_filename($filename, $verify=True) + { + $fp = @fopen($filename, "r"); + if (!$fp) { + throw new IOException("Could not open file for reading: ".$filename); + } + + clearstatcache(); + + $size = (float) sprintf("%u", filesize($filename)); + if ($size > MAX_OBJECT_SIZE) { + throw new SyntaxException("File size exceeds maximum object size."); + } + + $this->_guess_content_type($filename); + + $this->write($fp, $size, $verify); + fclose($fp); + return True; + } + + /** + * Save Object's data to local filename + * + * Given a local filename, the Object's data will be written to the newly + * created file. + * + * Example: + * <code> + * # ... authentication/connection/container code excluded + * # ... see previous examples + * + * # Whoops! I deleted my local README, let me download/save it + * # + * $my_docs = $conn->get_container("documents"); + * $doc = $my_docs->get_object("README"); + * + * $doc->save_to_filename("/home/ej/cloudfiles/readme.restored"); + * </code> + * + * @param string $filename name of local file to write data to + * @return boolean <kbd>True</kbd> if successful + * @throws IOException error opening file + * @throws InvalidResponseException unexpected response + */ + function save_to_filename($filename) + { + $fp = @fopen($filename, "wb"); + if (!$fp) { + throw new IOException("Could not open file for writing: ".$filename); + } + $result = $this->stream($fp); + fclose($fp); + return $result; + } + + /** + * Set Object's MD5 checksum + * + * Manually set the Object's ETag. Including the ETag is mandatory for + * Cloud Files to perform end-to-end verification. Omitting the ETag forces + * the user to handle any data integrity checks. + * + * @param string $etag MD5 checksum hexidecimal string + */ + function set_etag($etag) + { + $this->etag = $etag; + $this->_etag_override = True; + } + + /** + * Object's MD5 checksum + * + * Accessor method for reading Object's private ETag attribute. + * + * @return string MD5 checksum hexidecimal string + */ + function getETag() + { + return $this->etag; + } + + /** + * Compute the MD5 checksum + * + * Calculate the MD5 checksum on either a PHP resource or data. The argument + * may either be a local filename, open resource for reading, or a string. + * + * <b>WARNING:</b> if you are uploading a big file over a stream + * it could get very slow to compute the md5 you probably want to + * set the $verify parameter to False in the write() method and + * compute yourself the md5 before if you have it. + * + * @param filename|obj|string $data filename, open resource, or string + * @return string MD5 checksum hexidecimal string + */ + function compute_md5sum(&$data) + { + if (function_exists("hash_init") && is_resource($data)) { + $ctx = hash_init('md5'); + while (!feof($data)) { + $buffer = fgets($data, 65536); + hash_update($ctx, $buffer); + } + $md5 = hash_final($ctx, false); + rewind($data); + } elseif ((string)is_file($data)) { + $md5 = md5_file($data); + } else { + $md5 = md5($data); + } + return $md5; + } + + /** + * PRIVATE: fetch information about the remote Object if it exists + */ + private function _initialize() + { + clearstatcache(); + $path = $this->getFSPath(); + if ( !file_exists( $path ) ) { + return False; + } + // Slow...but whatever... + $this->etag = md5_file( $path ); + $this->last_modified = strftime( '%a, %d %b %Y %H:%M:%S GMT', filemtime( $path ) ); + $this->content_type = $this->_guess_content_type( $path ); + $this->content_length = (float)filesize( $path ); + $this->metadata = NULL; + $this->manifest = NULL; + return True; + } +} + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> Property changes on: trunk/extensions/SwiftMedia/php-fakecloudfiles/fake_cloudfiles.php ___________________________________________________________________ Added: svn:eol-style + native _______________________________________________ MediaWiki-CVS mailing list MediaWiki-CVS@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs