r100667 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r100666‎ | r100667 | r100668 >
Date:00:10, 25 October 2011
Author:aaron
Status:deferred
Tags:swift 
Comment:
Add some quick & dirty fake cloudfiles classes for local testing. Objects are just stored to FS.
Modified paths:
  • /trunk/extensions/SwiftMedia/php-fakecloudfiles (added) (history)
  • /trunk/extensions/SwiftMedia/php-fakecloudfiles/fake_cloudfiles.php (added) (history)

Diff [purge]

Index: trunk/extensions/SwiftMedia/php-fakecloudfiles/fake_cloudfiles.php
@@ -0,0 +1,1181 @@
 2+<?php
 3+/**
 4+ * Quick & Dirty mock cloud files classes for testing.
 5+ * Doesn't actually do authentication or HTTP stuff, just calls the methods.
 6+ * Objects are just stored on the File System under the container and object name.
 7+ * $IP/fake_cloudfiles.config.php must exist and set FAKE_SWIFT_CONTAINER_PATH,
 8+ * which is the directory that holds the container directories.
 9+ */
 10+
 11+/**
 12+ */
 13+if ( file_exists( dirname( __FILE__ ) . "/../../../fake_cloudfiles.config.php" ) ) {
 14+ require_once( dirname( __FILE__ ) . "/../../../fake_cloudfiles.config.php" );
 15+} else {
 16+ die( "Nothing to see here.\n" );
 17+}
 18+require_once( dirname( __FILE__ ) . "/../php-cloudfiles/cloudfiles_exceptions.php" );
 19+define("DEFAULT_CF_API_VERSION", 1);
 20+define("MAX_CONTAINER_NAME_LEN", 256);
 21+define("MAX_OBJECT_NAME_LEN", 1024);
 22+define("MAX_OBJECT_SIZE", 5*1024*1024*1024+1);
 23+define("US_AUTHURL", "https://auth.api.rackspacecloud.com");
 24+define("UK_AUTHURL", "https://lon.auth.api.rackspacecloud.com");
 25+/**
 26+ * Class for handling Cloud Files Authentication, call it's {@link authenticate()}
 27+ * method to obtain authorized service urls and an authentication token.
 28+ *
 29+ * Example:
 30+ * <code>
 31+ * # Create the authentication instance
 32+ * #
 33+ * $auth = new CF_Authentication("username", "api_key");
 34+ *
 35+ * # NOTE: For UK Customers please specify your AuthURL Manually
 36+ * # There is a Predfined constant to use EX:
 37+ * #
 38+ * # $auth = new CF_Authentication("username, "api_key", NULL, UK_AUTHURL);
 39+ * # Using the UK_AUTHURL keyword will force the api to use the UK AuthUrl.
 40+ * # rather then the US one. The NULL Is passed for legacy purposes and must
 41+ * # be passed to function correctly.
 42+ *
 43+ * # NOTE: Some versions of cURL include an outdated certificate authority (CA)
 44+ * # file. This API ships with a newer version obtained directly from
 45+ * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle,
 46+ * # call the CF_Authentication instance's 'ssl_use_cabundle()' method.
 47+ * #
 48+ * # $auth->ssl_use_cabundle(); # bypass cURL's old CA bundle
 49+ *
 50+ * # Perform authentication request
 51+ * #
 52+ * $auth->authenticate();
 53+ * </code>
 54+ *
 55+ * @package php-cloudfiles
 56+ */
 57+class CF_Authentication
 58+{
 59+ public $dbug;
 60+ public $username;
 61+ public $api_key;
 62+ public $auth_host;
 63+ public $account;
 64+
 65+ /**
 66+ * Instance variables that are set after successful authentication
 67+ */
 68+ public $storage_url;
 69+ public $cdnm_url;
 70+ public $auth_token;
 71+
 72+ /**
 73+ * Class constructor (PHP 5 syntax)
 74+ *
 75+ * @param string $username Mosso username
 76+ * @param string $api_key Mosso API Access Key
 77+ * @param string $account <i>Account name</i>
 78+ * @param string $auth_host <i>Authentication service URI</i>
 79+ */
 80+ function __construct($username=NULL, $api_key=NULL, $account=NULL, $auth_host=US_AUTHURL)
 81+ {
 82+
 83+ $this->dbug = False;
 84+ $this->username = $username;
 85+ $this->api_key = $api_key;
 86+ $this->account_name = $account;
 87+ $this->auth_host = $auth_host;
 88+
 89+ $this->storage_url = NULL;
 90+ $this->cdnm_url = NULL;
 91+ $this->auth_token = NULL;
 92+
 93+ $this->cfs_http = NULL;
 94+ }
 95+
 96+ /**
 97+ * Attempt to validate Username/API Access Key
 98+ *
 99+ * Attempts to validate credentials with the authentication service. It
 100+ * either returns <kbd>True</kbd> or throws an Exception. Accepts a single
 101+ * (optional) argument for the storage system API version.
 102+ *
 103+ * Example:
 104+ * <code>
 105+ * # Create the authentication instance
 106+ * #
 107+ * $auth = new CF_Authentication("username", "api_key");
 108+ *
 109+ * # Perform authentication request
 110+ * #
 111+ * $auth->authenticate();
 112+ * </code>
 113+ *
 114+ * @param string $version API version for Auth service (optional)
 115+ * @return boolean <kbd>True</kbd> if successfully authenticated
 116+ * @throws AuthenticationException invalid credentials
 117+ * @throws InvalidResponseException invalid response
 118+ */
 119+ function authenticate($version=DEFAULT_CF_API_VERSION)
 120+ {
 121+ $this->storage_url = 'http://localhost/swift/storage/someuserurl';
 122+ $this->cdnm_url = 'http://localhost/swift/curl/someuserurl';
 123+ $this->auth_token = 'somerandomishtokenstring';
 124+ return True;
 125+ }
 126+ /**
 127+ * Use Cached Token and Storage URL's rather then grabbing from the Auth System
 128+ *
 129+ * Example:
 130+ * <code>
 131+ * #Create an Auth instance
 132+ * $auth = new CF_Authentication();
 133+ * #Pass Cached URL's and Token as Args
 134+ * $auth->load_cached_credentials("auth_token", "storage_url", "cdn_management_url");
 135+ * </code>
 136+ *
 137+ * @param string $auth_token A Cloud Files Auth Token (Required)
 138+ * @param string $storage_url The Cloud Files Storage URL (Required)
 139+ * @param string $cdnm_url CDN Management URL (Required)
 140+ * @return boolean <kbd>True</kbd> if successful
 141+ * @throws SyntaxException If any of the Required Arguments are missing
 142+ */
 143+ function load_cached_credentials($auth_token, $storage_url, $cdnm_url)
 144+ {
 145+ $this->storage_url = $storage_url;
 146+ $this->cdnm_url = $cdnm_url;
 147+ $this->auth_token = $auth_token;
 148+ return True;
 149+ }
 150+ /**
 151+ * Grab Cloud Files info to be Cached for later use with the load_cached_credentials method.
 152+ *
 153+ * Example:
 154+ * <code>
 155+ * #Create an Auth instance
 156+ * $auth = new CF_Authentication("UserName","API_Key");
 157+ * $auth->authenticate();
 158+ * $array = $auth->export_credentials();
 159+ * </code>
 160+ *
 161+ * @return array of url's and an auth token.
 162+ */
 163+ function export_credentials()
 164+ {
 165+ $arr = array();
 166+ $arr['storage_url'] = $this->storage_url;
 167+ $arr['cdnm_url'] = $this->cdnm_url;
 168+ $arr['auth_token'] = $this->auth_token;
 169+
 170+ return $arr;
 171+ }
 172+
 173+
 174+ /**
 175+ * Make sure the CF_Authentication instance has authenticated.
 176+ *
 177+ * Ensures that the instance variables necessary to communicate with
 178+ * Cloud Files have been set from a previous authenticate() call.
 179+ *
 180+ * @return boolean <kbd>True</kbd> if successfully authenticated
 181+ */
 182+ function authenticated()
 183+ {
 184+ if (!($this->storage_url || $this->cdnm_url) || !$this->auth_token) {
 185+ return False;
 186+ }
 187+ return True;
 188+ }
 189+
 190+ /**
 191+ * Toggle debugging - set cURL verbose flag
 192+ */
 193+ function setDebug($bool)
 194+ {
 195+ $this->dbug = $bool;
 196+ }
 197+}
 198+
 199+/**
 200+ * Class for establishing connections to the Cloud Files storage system.
 201+ * Connection instances are used to communicate with the storage system at
 202+ * the account level; listing and deleting Containers and returning Container
 203+ * instances.
 204+ *
 205+ * Example:
 206+ * <code>
 207+ * # Create the authentication instance
 208+ * #
 209+ * $auth = new CF_Authentication("username", "api_key");
 210+ *
 211+ * # Perform authentication request
 212+ * #
 213+ * $auth->authenticate();
 214+ *
 215+ * # Create a connection to the storage/cdn system(s) and pass in the
 216+ * # validated CF_Authentication instance.
 217+ * #
 218+ * $conn = new CF_Connection($auth);
 219+ *
 220+ * # NOTE: Some versions of cURL include an outdated certificate authority (CA)
 221+ * # file. This API ships with a newer version obtained directly from
 222+ * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle,
 223+ * # call the CF_Authentication instance's 'ssl_use_cabundle()' method.
 224+ * #
 225+ * # $conn->ssl_use_cabundle(); # bypass cURL's old CA bundle
 226+ * </code>
 227+ *
 228+ * @package php-cloudfiles
 229+ */
 230+class CF_Connection
 231+{
 232+ public $dbug;
 233+ public $cfs_auth;
 234+
 235+ /**
 236+ * Pass in a previously authenticated CF_Authentication instance.
 237+ *
 238+ * Example:
 239+ * <code>
 240+ * # Create the authentication instance
 241+ * #
 242+ * $auth = new CF_Authentication("username", "api_key");
 243+ *
 244+ * # Perform authentication request
 245+ * #
 246+ * $auth->authenticate();
 247+ *
 248+ * # Create a connection to the storage/cdn system(s) and pass in the
 249+ * # validated CF_Authentication instance.
 250+ * #
 251+ * $conn = new CF_Connection($auth);
 252+ *
 253+ * # If you are connecting via Rackspace servers and have access
 254+ * # to the servicenet network you can set the $servicenet to True
 255+ * # like this.
 256+ *
 257+ * $conn = new CF_Connection($auth, $servicenet=True);
 258+ *
 259+ * </code>
 260+ *
 261+ * If the environement variable RACKSPACE_SERVICENET is defined it will
 262+ * force to connect via the servicenet.
 263+ *
 264+ * @param obj $cfs_auth previously authenticated CF_Authentication instance
 265+ * @param boolean $servicenet enable/disable access via Rackspace servicenet.
 266+ * @throws AuthenticationException not authenticated
 267+ */
 268+ function __construct($cfs_auth, $servicenet=False)
 269+ {
 270+ if (isset($_ENV['RACKSPACE_SERVICENET']))
 271+ $servicenet=True;
 272+ $this->cfs_auth = $cfs_auth;
 273+ if (!$this->cfs_auth->authenticated()) {
 274+ $e = "Need to pass in a previously authenticated ";
 275+ $e .= "CF_Authentication instance.";
 276+ throw new AuthenticationException($e);
 277+ }
 278+ $this->dbug = False;
 279+ }
 280+
 281+ /**
 282+ * Toggle debugging of instance and back-end HTTP module
 283+ *
 284+ * @param boolean $bool enable/disable cURL debugging
 285+ */
 286+ function setDebug($bool)
 287+ {
 288+ $this->dbug = (boolean) $bool;
 289+ }
 290+
 291+ /**
 292+ * Close a connection
 293+ *
 294+ * Example:
 295+ * <code>
 296+ *
 297+ * $conn->close();
 298+ *
 299+ * </code>
 300+ *
 301+ * Will close all current cUrl active connections.
 302+ *
 303+ */
 304+ public function close()
 305+ {
 306+ }
 307+
 308+ /**
 309+ * Create a Container
 310+ *
 311+ * Given a Container name, return a Container instance, creating a new
 312+ * remote Container if it does not exit.
 313+ *
 314+ * Example:
 315+ * <code>
 316+ * # ... authentication code excluded (see previous examples) ...
 317+ * #
 318+ * $conn = new CF_Authentication($auth);
 319+ *
 320+ * $images = $conn->create_container("my photos");
 321+ * </code>
 322+ *
 323+ * @param string $container_name container name
 324+ * @return CF_Container
 325+ * @throws SyntaxException invalid name
 326+ * @throws InvalidResponseException unexpected response
 327+ */
 328+ function create_container($container_name=NULL)
 329+ {
 330+ if ($container_name != "0" and !isset($container_name))
 331+ throw new SyntaxException("Container name not set.");
 332+
 333+ if (!isset($container_name) or $container_name == "")
 334+ throw new SyntaxException("Container name not set.");
 335+
 336+ if (strpos($container_name, "/") !== False) {
 337+ $r = "Container name '".$container_name;
 338+ $r .= "' cannot contain a '/' character.";
 339+ throw new SyntaxException($r);
 340+ }
 341+ if (strlen($container_name) > MAX_CONTAINER_NAME_LEN) {
 342+ throw new SyntaxException(sprintf(
 343+ "Container name exeeds %d bytes.",
 344+ MAX_CONTAINER_NAME_LEN));
 345+ }
 346+
 347+ if ( !is_dir( FAKE_SWIFT_CONTAINER_PATH . "/$container_name" ) ) {
 348+ mkdir( FAKE_SWIFT_CONTAINER_PATH . "/$container_name" );
 349+ }
 350+
 351+ return new CF_Container($this->cfs_auth, $this->cfs_http, $container_name);
 352+ }
 353+
 354+ /**
 355+ * Delete a Container
 356+ *
 357+ * Given either a Container instance or name, remove the remote Container.
 358+ * The Container must be empty prior to removing it.
 359+ *
 360+ * Example:
 361+ * <code>
 362+ * # ... authentication code excluded (see previous examples) ...
 363+ * #
 364+ * $conn = new CF_Authentication($auth);
 365+ *
 366+ * $conn->delete_container("my photos");
 367+ * </code>
 368+ *
 369+ * @param string|obj $container container name or instance
 370+ * @return boolean <kbd>True</kbd> if successfully deleted
 371+ * @throws SyntaxException missing proper argument
 372+ * @throws InvalidResponseException invalid response
 373+ * @throws NonEmptyContainerException container not empty
 374+ * @throws NoSuchContainerException remote container does not exist
 375+ */
 376+ function delete_container($container=NULL)
 377+ {
 378+ $container_name = NULL;
 379+
 380+ if (is_object($container)) {
 381+ if (get_class($container) == "CF_Container") {
 382+ $container_name = $container->name;
 383+ }
 384+ }
 385+ if (is_string($container)) {
 386+ $container_name = $container;
 387+ }
 388+
 389+ if ($container_name != "0" and !isset($container_name))
 390+ throw new SyntaxException("Must specify container object or name.");
 391+
 392+ if ( !is_dir( FAKE_SWIFT_CONTAINER_PATH . "/$container_name" ) ) {
 393+ throw new NoSuchContainerException(
 394+ "Specified container did not exist to delete.");
 395+ }
 396+ if ( !rmdir( FAKE_SWIFT_CONTAINER_PATH . "/$container_name" ) ) {
 397+ throw new NonEmptyContainerException(
 398+ "Container must be empty prior to removing it.");
 399+ }
 400+ return True;
 401+ }
 402+
 403+ /**
 404+ * Return a Container instance
 405+ *
 406+ * For the given name, return a Container instance if the remote Container
 407+ * exists, otherwise throw a Not Found exception.
 408+ *
 409+ * Example:
 410+ * <code>
 411+ * # ... authentication code excluded (see previous examples) ...
 412+ * #
 413+ * $conn = new CF_Authentication($auth);
 414+ *
 415+ * $images = $conn->get_container("my photos");
 416+ * print "Number of Objects: " . $images->count . "\n";
 417+ * print "Bytes stored in container: " . $images->bytes . "\n";
 418+ * </code>
 419+ *
 420+ * @param string $container_name name of the remote Container
 421+ * @return container CF_Container instance
 422+ * @throws NoSuchContainerException thrown if no remote Container
 423+ * @throws InvalidResponseException unexpected response
 424+ */
 425+ function get_container($container_name=NULL)
 426+ {
 427+ if ( !is_dir( FAKE_SWIFT_CONTAINER_PATH . "/$container_name" ) ) {
 428+ throw new NoSuchContainerException("Container not found.");
 429+ }
 430+ $count = $bytes = -1; // not implemented
 431+ return new CF_Container($this->cfs_auth, $this->cfs_http,
 432+ $container_name, $count, $bytes);
 433+ }
 434+}
 435+
 436+/**
 437+ * Container operations
 438+ *
 439+ * Containers are storage compartments where you put your data (objects).
 440+ * A container is similar to a directory or folder on a conventional filesystem
 441+ * with the exception that they exist in a flat namespace, you can not create
 442+ * containers inside of containers.
 443+ *
 444+ * You also have the option of marking a Container as "public" so that the
 445+ * Objects stored in the Container are publicly available via the CDN.
 446+ *
 447+ * @package php-cloudfiles
 448+ */
 449+class CF_Container
 450+{
 451+ public $cfs_auth;
 452+ public $cfs_http;
 453+ public $name;
 454+ public $object_count;
 455+ public $bytes_used;
 456+
 457+ public $cdn_enabled;
 458+ public $cdn_ssl_uri;
 459+ public $cdn_uri;
 460+ public $cdn_ttl;
 461+ public $cdn_log_retention;
 462+ public $cdn_acl_user_agent;
 463+ public $cdn_acl_referrer;
 464+
 465+ /**
 466+ * Class constructor
 467+ *
 468+ * Constructor for Container
 469+ *
 470+ * @param obj $cfs_auth CF_Authentication instance
 471+ * @param obj $cfs_http HTTP connection manager
 472+ * @param string $name name of Container
 473+ * @param int $count number of Objects stored in this Container
 474+ * @param int $bytes number of bytes stored in this Container
 475+ * @throws SyntaxException invalid Container name
 476+ */
 477+ function __construct(&$cfs_auth, &$cfs_http, $name, $count=0,
 478+ $bytes=0, $docdn=True)
 479+ {
 480+ if (strlen($name) > MAX_CONTAINER_NAME_LEN) {
 481+ throw new SyntaxException("Container name exceeds "
 482+ . "maximum allowed length.");
 483+ }
 484+ if (strpos($name, "/") !== False) {
 485+ throw new SyntaxException(
 486+ "Container names cannot contain a '/' character.");
 487+ }
 488+ $this->cfs_auth = $cfs_auth;
 489+ $this->cfs_http = $cfs_http;
 490+ $this->name = $name;
 491+ $this->object_count = $count;
 492+ $this->bytes_used = $bytes;
 493+ $this->cdn_enabled = NULL;
 494+ $this->cdn_uri = NULL;
 495+ $this->cdn_ssl_uri = NULL;
 496+ $this->cdn_ttl = NULL;
 497+ $this->cdn_log_retention = NULL;
 498+ $this->cdn_acl_user_agent = NULL;
 499+ $this->cdn_acl_referrer = NULL;
 500+ }
 501+
 502+ /**
 503+ * Create a new remote storage Object
 504+ *
 505+ * Return a new Object instance. If the remote storage Object exists,
 506+ * the instance's attributes are populated.
 507+ *
 508+ * Example:
 509+ * <code>
 510+ * # ... authentication code excluded (see previous examples) ...
 511+ * #
 512+ * $conn = new CF_Authentication($auth);
 513+ *
 514+ * $public_container = $conn->get_container("public");
 515+ *
 516+ * # This creates a local instance of a storage object but only creates
 517+ * # it in the storage system when the object's write() method is called.
 518+ * #
 519+ * $pic = $public_container->create_object("baby.jpg");
 520+ * </code>
 521+ *
 522+ * @param string $obj_name name of storage Object
 523+ * @return obj CF_Object instance
 524+ */
 525+ function create_object($obj_name=NULL)
 526+ {
 527+ return new CF_Object($this, $obj_name);
 528+ }
 529+
 530+ /**
 531+ * Return an Object instance for the remote storage Object
 532+ *
 533+ * Given a name, return a Object instance representing the
 534+ * remote storage object.
 535+ *
 536+ * Example:
 537+ * <code>
 538+ * # ... authentication code excluded (see previous examples) ...
 539+ * #
 540+ * $conn = new CF_Authentication($auth);
 541+ *
 542+ * $public_container = $conn->get_container("public");
 543+ *
 544+ * # This call only fetches header information and not the content of
 545+ * # the storage object. Use the Object's read() or stream() methods
 546+ * # to obtain the object's data.
 547+ * #
 548+ * $pic = $public_container->get_object("baby.jpg");
 549+ * </code>
 550+ *
 551+ * @param string $obj_name name of storage Object
 552+ * @return obj CF_Object instance
 553+ */
 554+ function get_object($obj_name=NULL)
 555+ {
 556+ return new CF_Object($this, $obj_name, True);
 557+ }
 558+
 559+ /**
 560+ * Return a list of Objects
 561+ *
 562+ * Return an array of strings listing the Object names in this Container.
 563+ *
 564+ * Example:
 565+ * <code>
 566+ * # ... authentication code excluded (see previous examples) ...
 567+ * #
 568+ * $images = $conn->get_container("my photos");
 569+ *
 570+ * # Grab the list of all storage objects
 571+ * #
 572+ * $all_objects = $images->list_objects();
 573+ *
 574+ * # Grab subsets of all storage objects
 575+ * #
 576+ * $first_ten = $images->list_objects(10);
 577+ *
 578+ * # Note the use of the previous result's last object name being
 579+ * # used as the 'marker' parameter to fetch the next 10 objects
 580+ * #
 581+ * $next_ten = $images->list_objects(10, $first_ten[count($first_ten)-1]);
 582+ *
 583+ * # Grab images starting with "birthday_party" and default limit/marker
 584+ * # to match all photos with that prefix
 585+ * #
 586+ * $prefixed = $images->list_objects(0, NULL, "birthday");
 587+ *
 588+ * # Assuming you have created the appropriate directory marker Objects,
 589+ * # you can traverse your pseudo-hierarchical containers
 590+ * # with the "path" argument.
 591+ * #
 592+ * $animals = $images->list_objects(0,NULL,NULL,"pictures/animals");
 593+ * $dogs = $images->list_objects(0,NULL,NULL,"pictures/animals/dogs");
 594+ * </code>
 595+ *
 596+ * @param int $limit <i>optional</i> only return $limit names
 597+ * @param int $marker <i>optional</i> subset of names starting at $marker
 598+ * @param string $prefix <i>optional</i> Objects whose names begin with $prefix
 599+ * @param string $path <i>optional</i> only return results under "pathname"
 600+ * @return array array of strings
 601+ * @throws InvalidResponseException unexpected response
 602+ */
 603+ function list_objects($limit=0, $marker=NULL, $prefix=NULL, $path=NULL)
 604+ {
 605+ $names = array();
 606+ // $prefix must be a dir in mock class
 607+ $dir = FAKE_SWIFT_CONTAINER_PATH . "/{$this->name}/$prefix";
 608+ if ( is_dir( $dir ) ) {
 609+ $handle = opendir( $dir );
 610+ if ( $handle ) {
 611+ while ( false !== ( $file = readdir( $handle ) ) ) {
 612+ if ( $file { 0 } != '.' ) {
 613+ $names[] = $file;
 614+ }
 615+ }
 616+ closedir( $handle );
 617+ }
 618+ }
 619+ return $names;
 620+ }
 621+
 622+ /**
 623+ * Delete a remote storage Object
 624+ *
 625+ * Given an Object instance or name, permanently remove the remote Object
 626+ * and all associated metadata.
 627+ *
 628+ * Example:
 629+ * <code>
 630+ * # ... authentication code excluded (see previous examples) ...
 631+ * #
 632+ * $conn = new CF_Authentication($auth);
 633+ *
 634+ * $images = $conn->get_container("my photos");
 635+ *
 636+ * # Delete specific object
 637+ * #
 638+ * $images->delete_object("disco_dancing.jpg");
 639+ * </code>
 640+ *
 641+ * @param obj $obj name or instance of Object to delete
 642+ * @return boolean <kbd>True</kbd> if successfully removed
 643+ * @throws SyntaxException invalid Object name
 644+ * @throws NoSuchObjectException remote Object does not exist
 645+ * @throws InvalidResponseException unexpected response
 646+ */
 647+ function delete_object($obj)
 648+ {
 649+ $obj_name = NULL;
 650+ if (is_object($obj)) {
 651+ if (get_class($obj) == "CF_Object") {
 652+ $obj_name = $obj->name;
 653+ }
 654+ }
 655+ if (is_string($obj)) {
 656+ $obj_name = $obj;
 657+ }
 658+ if (!$obj_name) {
 659+ throw new SyntaxException("Object name not set.");
 660+ }
 661+ if ( !@unlink( $this->getObjectFSPath( $obj_name ) ) ) {
 662+ $m = "Specified object '".$this->name."/".$obj_name;
 663+ $m.= "' did not exist to delete.";
 664+ throw new NoSuchObjectException($m);
 665+ }
 666+ return True;
 667+ }
 668+
 669+ function getObjectFSPath( $file ) {
 670+ return FAKE_SWIFT_CONTAINER_PATH . "/{$this->name}/{$file}";
 671+ }
 672+}
 673+
 674+
 675+/**
 676+ * Object operations
 677+ *
 678+ * An Object is analogous to a file on a conventional filesystem. You can
 679+ * read data from, or write data to your Objects. You can also associate
 680+ * arbitrary metadata with them.
 681+ *
 682+ * @package php-cloudfiles
 683+ */
 684+class CF_Object
 685+{
 686+ public $container;
 687+ public $name;
 688+ public $last_modified;
 689+ public $content_type;
 690+ public $content_length;
 691+ public $metadata;
 692+ public $manifest;
 693+ private $etag;
 694+
 695+ /**
 696+ * Class constructor
 697+ *
 698+ * @param obj $container CF_Container instance
 699+ * @param string $name name of Object
 700+ * @param boolean $force_exists if set, throw an error if Object doesn't exist
 701+ */
 702+ function __construct(&$container, $name, $force_exists=False, $dohead=True)
 703+ {
 704+ if ($name[0] == "/") {
 705+ $r = "Object name '".$name;
 706+ $r .= "' cannot contain begin with a '/' character.";
 707+ throw new SyntaxException($r);
 708+ }
 709+ if (strlen($name) > MAX_OBJECT_NAME_LEN) {
 710+ throw new SyntaxException("Object name exceeds "
 711+ . "maximum allowed length.");
 712+ }
 713+ $this->container = $container;
 714+ $this->name = $name;
 715+ $this->etag = NULL;
 716+ $this->_etag_override = False;
 717+ $this->last_modified = NULL;
 718+ $this->content_type = NULL;
 719+ $this->content_length = 0;
 720+ $this->metadata = array();
 721+ $this->manifest = NULL;
 722+ if ($dohead) {
 723+ if (!$this->_initialize() && $force_exists) {
 724+ throw new NoSuchObjectException("No such object '".$name."'");
 725+ }
 726+ }
 727+ }
 728+
 729+ /**
 730+ * String representation of Object
 731+ *
 732+ * Pretty print the Object's location and name
 733+ *
 734+ * @return string Object information
 735+ */
 736+ function __toString()
 737+ {
 738+ return $this->container->name . "/" . $this->name;
 739+ }
 740+
 741+ /**
 742+ * Internal check to get the proper mimetype.
 743+ *
 744+ * This function would go over the available PHP methods to get
 745+ * the MIME type.
 746+ *
 747+ * By default it will try to use the PHP fileinfo library which is
 748+ * available from PHP 5.3 or as an PECL extension
 749+ * (http://pecl.php.net/package/Fileinfo).
 750+ *
 751+ * It will get the magic file by default from the system wide file
 752+ * which is usually available in /usr/share/magic on Unix or try
 753+ * to use the file specified in the source directory of the API
 754+ * (share directory).
 755+ *
 756+ * if fileinfo is not available it will try to use the internal
 757+ * mime_content_type function.
 758+ *
 759+ * @param string $handle name of file or buffer to guess the type from
 760+ * @return boolean <kbd>True</kbd> if successful
 761+ * @throws BadContentTypeException
 762+ */
 763+ function _guess_content_type($handle) {
 764+ if ($this->content_type)
 765+ return;
 766+
 767+ if (function_exists("finfo_open")) {
 768+ $local_magic = dirname(__FILE__) . "/share/magic";
 769+ $finfo = @finfo_open(FILEINFO_MIME, $local_magic);
 770+
 771+ if (!$finfo)
 772+ $finfo = @finfo_open(FILEINFO_MIME);
 773+
 774+ if ($finfo) {
 775+
 776+ if (is_file((string)$handle))
 777+ $ct = @finfo_file($finfo, $handle);
 778+ else
 779+ $ct = @finfo_buffer($finfo, $handle);
 780+
 781+ /* PHP 5.3 fileinfo display extra information like
 782+ charset so we remove everything after the ; since
 783+ we are not into that stuff */
 784+ if ($ct) {
 785+ $extra_content_type_info = strpos($ct, "; ");
 786+ if ($extra_content_type_info)
 787+ $ct = substr($ct, 0, $extra_content_type_info);
 788+ }
 789+
 790+ if ($ct && $ct != 'application/octet-stream')
 791+ $this->content_type = $ct;
 792+
 793+ @finfo_close($finfo);
 794+ }
 795+ }
 796+
 797+ if (!$this->content_type && (string)is_file($handle) && function_exists("mime_content_type")) {
 798+ $this->content_type = @mime_content_type($handle);
 799+ }
 800+
 801+ if (!$this->content_type) {
 802+ throw new BadContentTypeException("Required Content-Type not set");
 803+ }
 804+ return True;
 805+ }
 806+
 807+ function getFSPath() {
 808+ return $this->container->getObjectFSPath( $this->name );
 809+ }
 810+
 811+ /**
 812+ * Read the remote Object's data
 813+ *
 814+ * Returns the Object's data. This is useful for smaller Objects such
 815+ * as images or office documents. Object's with larger content should use
 816+ * the stream() method below.
 817+ *
 818+ * Pass in $hdrs array to set specific custom HTTP headers such as
 819+ * If-Match, If-None-Match, If-Modified-Since, Range, etc.
 820+ *
 821+ * Example:
 822+ * <code>
 823+ * # ... authentication/connection/container code excluded
 824+ * # ... see previous examples
 825+ *
 826+ * $my_docs = $conn->get_container("documents");
 827+ * $doc = $my_docs->get_object("README");
 828+ * $data = $doc->read(); # read image content into a string variable
 829+ * print $data;
 830+ *
 831+ * # Or see stream() below for a different example.
 832+ * #
 833+ * </code>
 834+ *
 835+ * @param array $hdrs user-defined headers (Range, If-Match, etc.)
 836+ * @return string Object's data
 837+ * @throws InvalidResponseException unexpected response
 838+ */
 839+ function read($hdrs=array())
 840+ {
 841+ return file_get_contents( $this->getFSPath() );
 842+ }
 843+
 844+ /**
 845+ * Streaming read of Object's data
 846+ *
 847+ * Given an open PHP resource (see PHP's fopen() method), fetch the Object's
 848+ * data and write it to the open resource handle. This is useful for
 849+ * streaming an Object's content to the browser (videos, images) or for
 850+ * fetching content to a local file.
 851+ *
 852+ * Pass in $hdrs array to set specific custom HTTP headers such as
 853+ * If-Match, If-None-Match, If-Modified-Since, Range, etc.
 854+ *
 855+ * Example:
 856+ * <code>
 857+ * # ... authentication/connection/container code excluded
 858+ * # ... see previous examples
 859+ *
 860+ * # Assuming this is a web script to display the README to the
 861+ * # user's browser:
 862+ * #
 863+ * <?php
 864+ * // grab README from storage system
 865+ * //
 866+ * $my_docs = $conn->get_container("documents");
 867+ * $doc = $my_docs->get_object("README");
 868+ *
 869+ * // Hand it back to user's browser with appropriate content-type
 870+ * //
 871+ * header("Content-Type: " . $doc->content_type);
 872+ * $output = fopen("php://output", "w");
 873+ * $doc->stream($output); # stream object content to PHP's output buffer
 874+ * fclose($output);
 875+ * ?>
 876+ *
 877+ * # See read() above for a more simple example.
 878+ * #
 879+ * </code>
 880+ *
 881+ * @param resource $fp open resource for writing data to
 882+ * @param array $hdrs user-defined headers (Range, If-Match, etc.)
 883+ * @return string Object's data
 884+ * @throws InvalidResponseException unexpected response
 885+ */
 886+ function stream(&$fp, $hdrs=array())
 887+ {
 888+ $data = file_get_contents( $this->getFSPath() );
 889+ fwrite( $fp, $data );
 890+ return True;
 891+ }
 892+
 893+ /**
 894+ * Upload Object's data to Cloud Files
 895+ *
 896+ * Write data to the remote Object. The $data argument can either be a
 897+ * PHP resource open for reading (see PHP's fopen() method) or an in-memory
 898+ * variable. If passing in a PHP resource, you must also include the $bytes
 899+ * parameter.
 900+ *
 901+ * Example:
 902+ * <code>
 903+ * # ... authentication/connection/container code excluded
 904+ * # ... see previous examples
 905+ *
 906+ * $my_docs = $conn->get_container("documents");
 907+ * $doc = $my_docs->get_object("README");
 908+ *
 909+ * # Upload placeholder text in my README
 910+ * #
 911+ * $doc->write("This is just placeholder text for now...");
 912+ * </code>
 913+ *
 914+ * @param string|resource $data string or open resource
 915+ * @param float $bytes amount of data to upload (required for resources)
 916+ * @param boolean $verify generate, send, and compare MD5 checksums
 917+ * @return boolean <kbd>True</kbd> when data uploaded successfully
 918+ * @throws SyntaxException missing required parameters
 919+ * @throws BadContentTypeException if no Content-Type was/could be set
 920+ * @throws MisMatchedChecksumException $verify is set and checksums unequal
 921+ * @throws InvalidResponseException unexpected response
 922+ */
 923+ function write($data=NULL, $bytes=0, $verify=True)
 924+ {
 925+ if (!$data && !is_string($data)) {
 926+ throw new SyntaxException("Missing data source.");
 927+ }
 928+ if ($bytes > MAX_OBJECT_SIZE) {
 929+ throw new SyntaxException("Bytes exceeds maximum object size.");
 930+ }
 931+ if ($verify) {
 932+ if (!$this->_etag_override) {
 933+ $this->etag = $this->compute_md5sum($data);
 934+ }
 935+ } else {
 936+ $this->etag = NULL;
 937+ }
 938+
 939+ $close_fh = False;
 940+ if (!is_resource($data)) {
 941+ # A hack to treat string data as a file handle. php://memory feels
 942+ # like a better option, but it seems to break on Windows so use
 943+ # a temporary file instead.
 944+ #
 945+ $fp = fopen("php://temp", "wb+");
 946+ #$fp = fopen("php://memory", "wb+");
 947+ fwrite($fp, $data, strlen($data));
 948+ rewind($fp);
 949+ $close_fh = True;
 950+ $this->content_length = (float) strlen($data);
 951+ if ($this->content_length > MAX_OBJECT_SIZE) {
 952+ throw new SyntaxException("Data exceeds maximum object size");
 953+ }
 954+ $ct_data = substr($data, 0, 64);
 955+ } else {
 956+ $this->content_length = $bytes;
 957+ $fp = $data;
 958+ $ct_data = fread($data, 64);
 959+ rewind($data);
 960+ }
 961+
 962+ $this->_guess_content_type($ct_data);
 963+
 964+ $dir = dirname( $this->getFSPath() );
 965+ if ( !file_exists( $dir ) ) {
 966+ mkdir( $dir, 0777, true );
 967+ }
 968+ file_put_contents( $this->getFSPath(), $data, LOCK_EX );
 969+
 970+ if ($close_fh) { fclose($fp); }
 971+ return True;
 972+ }
 973+
 974+ /**
 975+ * Copy one Object to another Object to Cloud Files
 976+ *
 977+ * Example:
 978+ * <code>
 979+ * # ... authentication/connection/container code excluded
 980+ * # ... see previous examples
 981+ *
 982+ * $my_docs = $conn->get_container("documents");
 983+ * $doc = $my_docs->get_object("README");
 984+ *
 985+ * # Copy README.txt on top of this object (which you must have
 986+ * already written something to).
 987+ * #
 988+ * $doc->copy("/documents/README.txt");
 989+ * </code>
 990+ *
 991+ * @param string $source Name of existing object
 992+ * @return boolean <kbd>True</kbd> when data uploaded successfully
 993+ * @throws SyntaxException missing required parameters
 994+ * @throws BadContentTypeException if no Content-Type was/could be set
 995+ * @throws MisMatchedChecksumException $verify is set and checksums unequal
 996+ * @throws InvalidResponseException unexpected response
 997+ */
 998+ function copy($source)
 999+ {
 1000+ if (!$source && !is_string($source)) {
 1001+ throw new SyntaxException("Missing data source.");
 1002+ }
 1003+ $sObj = new self( $this->container, $source );
 1004+ copy( $sObj->getFSPath(), $this->getFSPath() );
 1005+ return True;
 1006+ }
 1007+
 1008+ /**
 1009+ * Upload Object data from local filename
 1010+ *
 1011+ * This is a convenience function to upload the data from a local file. A
 1012+ * True value for $verify will cause the method to compute the Object's MD5
 1013+ * checksum prior to uploading.
 1014+ *
 1015+ * Example:
 1016+ * <code>
 1017+ * # ... authentication/connection/container code excluded
 1018+ * # ... see previous examples
 1019+ *
 1020+ * $my_docs = $conn->get_container("documents");
 1021+ * $doc = $my_docs->get_object("README");
 1022+ *
 1023+ * # Upload my local README's content
 1024+ * #
 1025+ * $doc->load_from_filename("/home/ej/cloudfiles/readme");
 1026+ * </code>
 1027+ *
 1028+ * @param string $filename full path to local file
 1029+ * @param boolean $verify enable local/remote MD5 checksum validation
 1030+ * @return boolean <kbd>True</kbd> if data uploaded successfully
 1031+ * @throws SyntaxException missing required parameters
 1032+ * @throws BadContentTypeException if no Content-Type was/could be set
 1033+ * @throws MisMatchedChecksumException $verify is set and checksums unequal
 1034+ * @throws InvalidResponseException unexpected response
 1035+ * @throws IOException error opening file
 1036+ */
 1037+ function load_from_filename($filename, $verify=True)
 1038+ {
 1039+ $fp = @fopen($filename, "r");
 1040+ if (!$fp) {
 1041+ throw new IOException("Could not open file for reading: ".$filename);
 1042+ }
 1043+
 1044+ clearstatcache();
 1045+
 1046+ $size = (float) sprintf("%u", filesize($filename));
 1047+ if ($size > MAX_OBJECT_SIZE) {
 1048+ throw new SyntaxException("File size exceeds maximum object size.");
 1049+ }
 1050+
 1051+ $this->_guess_content_type($filename);
 1052+
 1053+ $this->write($fp, $size, $verify);
 1054+ fclose($fp);
 1055+ return True;
 1056+ }
 1057+
 1058+ /**
 1059+ * Save Object's data to local filename
 1060+ *
 1061+ * Given a local filename, the Object's data will be written to the newly
 1062+ * created file.
 1063+ *
 1064+ * Example:
 1065+ * <code>
 1066+ * # ... authentication/connection/container code excluded
 1067+ * # ... see previous examples
 1068+ *
 1069+ * # Whoops! I deleted my local README, let me download/save it
 1070+ * #
 1071+ * $my_docs = $conn->get_container("documents");
 1072+ * $doc = $my_docs->get_object("README");
 1073+ *
 1074+ * $doc->save_to_filename("/home/ej/cloudfiles/readme.restored");
 1075+ * </code>
 1076+ *
 1077+ * @param string $filename name of local file to write data to
 1078+ * @return boolean <kbd>True</kbd> if successful
 1079+ * @throws IOException error opening file
 1080+ * @throws InvalidResponseException unexpected response
 1081+ */
 1082+ function save_to_filename($filename)
 1083+ {
 1084+ $fp = @fopen($filename, "wb");
 1085+ if (!$fp) {
 1086+ throw new IOException("Could not open file for writing: ".$filename);
 1087+ }
 1088+ $result = $this->stream($fp);
 1089+ fclose($fp);
 1090+ return $result;
 1091+ }
 1092+
 1093+ /**
 1094+ * Set Object's MD5 checksum
 1095+ *
 1096+ * Manually set the Object's ETag. Including the ETag is mandatory for
 1097+ * Cloud Files to perform end-to-end verification. Omitting the ETag forces
 1098+ * the user to handle any data integrity checks.
 1099+ *
 1100+ * @param string $etag MD5 checksum hexidecimal string
 1101+ */
 1102+ function set_etag($etag)
 1103+ {
 1104+ $this->etag = $etag;
 1105+ $this->_etag_override = True;
 1106+ }
 1107+
 1108+ /**
 1109+ * Object's MD5 checksum
 1110+ *
 1111+ * Accessor method for reading Object's private ETag attribute.
 1112+ *
 1113+ * @return string MD5 checksum hexidecimal string
 1114+ */
 1115+ function getETag()
 1116+ {
 1117+ return $this->etag;
 1118+ }
 1119+
 1120+ /**
 1121+ * Compute the MD5 checksum
 1122+ *
 1123+ * Calculate the MD5 checksum on either a PHP resource or data. The argument
 1124+ * may either be a local filename, open resource for reading, or a string.
 1125+ *
 1126+ * <b>WARNING:</b> if you are uploading a big file over a stream
 1127+ * it could get very slow to compute the md5 you probably want to
 1128+ * set the $verify parameter to False in the write() method and
 1129+ * compute yourself the md5 before if you have it.
 1130+ *
 1131+ * @param filename|obj|string $data filename, open resource, or string
 1132+ * @return string MD5 checksum hexidecimal string
 1133+ */
 1134+ function compute_md5sum(&$data)
 1135+ {
 1136+ if (function_exists("hash_init") && is_resource($data)) {
 1137+ $ctx = hash_init('md5');
 1138+ while (!feof($data)) {
 1139+ $buffer = fgets($data, 65536);
 1140+ hash_update($ctx, $buffer);
 1141+ }
 1142+ $md5 = hash_final($ctx, false);
 1143+ rewind($data);
 1144+ } elseif ((string)is_file($data)) {
 1145+ $md5 = md5_file($data);
 1146+ } else {
 1147+ $md5 = md5($data);
 1148+ }
 1149+ return $md5;
 1150+ }
 1151+
 1152+ /**
 1153+ * PRIVATE: fetch information about the remote Object if it exists
 1154+ */
 1155+ private function _initialize()
 1156+ {
 1157+ clearstatcache();
 1158+ $path = $this->getFSPath();
 1159+ if ( !file_exists( $path ) ) {
 1160+ return False;
 1161+ }
 1162+ // Slow...but whatever...
 1163+ $this->etag = md5_file( $path );
 1164+ $this->last_modified = strftime( '%a, %d %b %Y %H:%M:%S GMT', filemtime( $path ) );
 1165+ $this->content_type = $this->_guess_content_type( $path );
 1166+ $this->content_length = (float)filesize( $path );
 1167+ $this->metadata = NULL;
 1168+ $this->manifest = NULL;
 1169+ return True;
 1170+ }
 1171+}
 1172+
 1173+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 1174+
 1175+/*
 1176+ * Local variables:
 1177+ * tab-width: 4
 1178+ * c-basic-offset: 4
 1179+ * c-hanging-comment-ender-p: nil
 1180+ * End:
 1181+ */
 1182+?>
Property changes on: trunk/extensions/SwiftMedia/php-fakecloudfiles/fake_cloudfiles.php
___________________________________________________________________
Added: svn:eol-style
11183 + native

Status & tagging log