Skip to content

Instantly share code, notes, and snippets.

@thrashr888
Created October 2, 2012 08:31
Show Gist options
  • Save thrashr888/3817340 to your computer and use it in GitHub Desktop.
Save thrashr888/3817340 to your computer and use it in GitHub Desktop.
s3copy copies files and streams to a public S3 file and copies the url to your clipboard.
#!/usr/local/bin/php
<?php
error_reporting(-1);
define('CFRUNTIME_NAME', 'aws-sdk-php');
define('CFRUNTIME_VERSION', 'Panther');
define('CFRUNTIME_BUILD', '20120926163000');
define('CFRUNTIME_USERAGENT', CFRUNTIME_NAME . '/' . CFRUNTIME_VERSION . ' PHP/' . PHP_VERSION . ' ' . str_replace(' ', '_', php_uname('s')) . '/' . str_replace(' ', '_', php_uname('r')) . ' Arch/' . php_uname('m') . ' SAPI/' . php_sapi_name() . ' Integer/' . PHP_INT_MAX . ' Build/' . CFRUNTIME_BUILD . __aws_sdk_ua_callback());
/*
* Copyright 2010-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*%******************************************************************************************%*/
// CLASS
/**
* Simplifies the process of looking up the content-types for a variety of file extensions.
*
* @version 2010.07.20
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/php/ PHP Developer Center
*/
class CFMimeTypes
{
/**
* Map of the extension-to-mime-types that we support.
*/
public static $mime_types = array(
'3gp' => 'video/3gpp',
'ai' => 'application/postscript',
'aif' => 'audio/x-aiff',
'aifc' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'asc' => 'text/plain',
'atom' => 'application/atom+xml',
'au' => 'audio/basic',
'avi' => 'video/x-msvideo',
'bcpio' => 'application/x-bcpio',
'bin' => 'application/octet-stream',
'bmp' => 'image/bmp',
'cdf' => 'application/x-netcdf',
'cgm' => 'image/cgm',
'class' => 'application/octet-stream',
'cpio' => 'application/x-cpio',
'cpt' => 'application/mac-compactpro',
'csh' => 'application/x-csh',
'css' => 'text/css',
'dcr' => 'application/x-director',
'dif' => 'video/x-dv',
'dir' => 'application/x-director',
'djv' => 'image/vnd.djvu',
'djvu' => 'image/vnd.djvu',
'dll' => 'application/octet-stream',
'dmg' => 'application/octet-stream',
'dms' => 'application/octet-stream',
'doc' => 'application/msword',
'dtd' => 'application/xml-dtd',
'dv' => 'video/x-dv',
'dvi' => 'application/x-dvi',
'dxr' => 'application/x-director',
'eps' => 'application/postscript',
'etx' => 'text/x-setext',
'exe' => 'application/octet-stream',
'ez' => 'application/andrew-inset',
'flv' => 'video/x-flv',
'gif' => 'image/gif',
'gram' => 'application/srgs',
'grxml' => 'application/srgs+xml',
'gtar' => 'application/x-gtar',
'gz' => 'application/x-gzip',
'hdf' => 'application/x-hdf',
'hqx' => 'application/mac-binhex40',
'htm' => 'text/html',
'html' => 'text/html',
'ice' => 'x-conference/x-cooltalk',
'ico' => 'image/x-icon',
'ics' => 'text/calendar',
'ief' => 'image/ief',
'ifb' => 'text/calendar',
'iges' => 'model/iges',
'igs' => 'model/iges',
'jnlp' => 'application/x-java-jnlp-file',
'jp2' => 'image/jp2',
'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'js' => 'application/x-javascript',
'kar' => 'audio/midi',
'latex' => 'application/x-latex',
'lha' => 'application/octet-stream',
'lzh' => 'application/octet-stream',
'm3u' => 'audio/x-mpegurl',
'm4a' => 'audio/mp4a-latm',
'm4p' => 'audio/mp4a-latm',
'm4u' => 'video/vnd.mpegurl',
'm4v' => 'video/x-m4v',
'mac' => 'image/x-macpaint',
'man' => 'application/x-troff-man',
'mathml' => 'application/mathml+xml',
'me' => 'application/x-troff-me',
'mesh' => 'model/mesh',
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mif' => 'application/vnd.mif',
'mov' => 'video/quicktime',
'movie' => 'video/x-sgi-movie',
'mp2' => 'audio/mpeg',
'mp3' => 'audio/mpeg',
'mp4' => 'video/mp4',
'mpe' => 'video/mpeg',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpga' => 'audio/mpeg',
'ms' => 'application/x-troff-ms',
'msh' => 'model/mesh',
'mxu' => 'video/vnd.mpegurl',
'nc' => 'application/x-netcdf',
'oda' => 'application/oda',
'ogg' => 'application/ogg',
'ogv' => 'video/ogv',
'pbm' => 'image/x-portable-bitmap',
'pct' => 'image/pict',
'pdb' => 'chemical/x-pdb',
'pdf' => 'application/pdf',
'pgm' => 'image/x-portable-graymap',
'pgn' => 'application/x-chess-pgn',
'pic' => 'image/pict',
'pict' => 'image/pict',
'png' => 'image/png',
'pnm' => 'image/x-portable-anymap',
'pnt' => 'image/x-macpaint',
'pntg' => 'image/x-macpaint',
'ppm' => 'image/x-portable-pixmap',
'ppt' => 'application/vnd.ms-powerpoint',
'ps' => 'application/postscript',
'qt' => 'video/quicktime',
'qti' => 'image/x-quicktime',
'qtif' => 'image/x-quicktime',
'ra' => 'audio/x-pn-realaudio',
'ram' => 'audio/x-pn-realaudio',
'ras' => 'image/x-cmu-raster',
'rdf' => 'application/rdf+xml',
'rgb' => 'image/x-rgb',
'rm' => 'application/vnd.rn-realmedia',
'roff' => 'application/x-troff',
'rtf' => 'text/rtf',
'rtx' => 'text/richtext',
'sgm' => 'text/sgml',
'sgml' => 'text/sgml',
'sh' => 'application/x-sh',
'shar' => 'application/x-shar',
'silo' => 'model/mesh',
'sit' => 'application/x-stuffit',
'skd' => 'application/x-koan',
'skm' => 'application/x-koan',
'skp' => 'application/x-koan',
'skt' => 'application/x-koan',
'smi' => 'application/smil',
'smil' => 'application/smil',
'snd' => 'audio/basic',
'so' => 'application/octet-stream',
'spl' => 'application/x-futuresplash',
'src' => 'application/x-wais-source',
'sv4cpio' => 'application/x-sv4cpio',
'sv4crc' => 'application/x-sv4crc',
'svg' => 'image/svg+xml',
'swf' => 'application/x-shockwave-flash',
't' => 'application/x-troff',
'tar' => 'application/x-tar',
'tcl' => 'application/x-tcl',
'tex' => 'application/x-tex',
'texi' => 'application/x-texinfo',
'texinfo' => 'application/x-texinfo',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'tr' => 'application/x-troff',
'tsv' => 'text/tab-separated-values',
'txt' => 'text/plain',
'ustar' => 'application/x-ustar',
'vcd' => 'application/x-cdlink',
'vrml' => 'model/vrml',
'vxml' => 'application/voicexml+xml',
'wav' => 'audio/x-wav',
'wbmp' => 'image/vnd.wap.wbmp',
'wbxml' => 'application/vnd.wap.wbxml',
'webm' => 'video/webm',
'wml' => 'text/vnd.wap.wml',
'wmlc' => 'application/vnd.wap.wmlc',
'wmls' => 'text/vnd.wap.wmlscript',
'wmlsc' => 'application/vnd.wap.wmlscriptc',
'wmv' => 'video/x-ms-wmv',
'wrl' => 'model/vrml',
'xbm' => 'image/x-xbitmap',
'xht' => 'application/xhtml+xml',
'xhtml' => 'application/xhtml+xml',
'xls' => 'application/vnd.ms-excel',
'xml' => 'application/xml',
'xpm' => 'image/x-xpixmap',
'xsl' => 'application/xml',
'xslt' => 'application/xslt+xml',
'xul' => 'application/vnd.mozilla.xul+xml',
'xwd' => 'image/x-xwindowdump',
'xyz' => 'chemical/x-xyz',
'zip' => 'application/zip',
);
/**
* Attempt to match the file extension to a known mime-type.
*
* @param string $ext (Required) The file extension to attempt to map.
* @return string The mime-type to use for the file extension.
*/
public static function get_mimetype($ext)
{
$ext = strtolower($ext); // Make sure the passed in extension is lowercase
return isset(self::$mime_types[$ext]) ? self::$mime_types[$ext] : 'application/octet-stream';
}
}
/*
* Copyright 2010-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*%******************************************************************************************%*/
// CLASS
/**
* Wraps the underlying `ResponseCore` class with some AWS-specific customizations. Response
* bodies are typically represented as CFSimpleXML objects.
*
* @version 2010.10.11
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/php/ PHP Developer Center
*/
class CFResponse extends ResponseCore {}
/**
* Handles all HTTP requests using cURL and manages the responses.
*
* @version 2012.01.17
* @copyright 2006-2011 Ryan Parman
* @copyright 2006-2010 Foleeo Inc.
* @copyright 2010-2011 Amazon.com, Inc. or its affiliates.
* @copyright 2008-2011 Contributors
* @license http://opensource.org/licenses/bsd-license.php Simplified BSD License
*/
class RequestCore
{
/**
* The URL being requested.
*/
public $request_url;
/**
* The headers being sent in the request.
*/
public $request_headers;
/**
* The body being sent in the request.
*/
public $request_body;
/**
* The response returned by the request.
*/
public $response;
/**
* The headers returned by the request.
*/
public $response_headers;
/**
* The body returned by the request.
*/
public $response_body;
/**
* The HTTP status code returned by the request.
*/
public $response_code;
/**
* Additional response data.
*/
public $response_info;
/**
* The handle for the cURL object.
*/
public $curl_handle;
/**
* The method by which the request is being made.
*/
public $method;
/**
* Stores the proxy settings to use for the request.
*/
public $proxy = null;
/**
* The username to use for the request.
*/
public $username = null;
/**
* The password to use for the request.
*/
public $password = null;
/**
* Custom CURLOPT settings.
*/
public $curlopts = null;
/**
* The state of debug mode.
*/
public $debug_mode = false;
/**
* The default class to use for HTTP Requests (defaults to <RequestCore>).
*/
public $request_class = 'RequestCore';
/**
* The default class to use for HTTP Responses (defaults to <ResponseCore>).
*/
public $response_class = 'ResponseCore';
/**
* Default useragent string to use.
*/
public $useragent = 'RequestCore/1.4.4';
/**
* File to read from while streaming up.
*/
public $read_file = null;
/**
* The resource to read from while streaming up.
*/
public $read_stream = null;
/**
* The size of the stream to read from.
*/
public $read_stream_size = null;
/**
* The length already read from the stream.
*/
public $read_stream_read = 0;
/**
* File to write to while streaming down.
*/
public $write_file = null;
/**
* The resource to write to while streaming down.
*/
public $write_stream = null;
/**
* Stores the intended starting seek position.
*/
public $seek_position = null;
/**
* The location of the cacert.pem file to use.
*/
public $cacert_location = false;
/**
* The state of SSL certificate verification.
*/
public $ssl_verification = true;
/**
* The user-defined callback function to call when a stream is read from.
*/
public $registered_streaming_read_callback = null;
/**
* The user-defined callback function to call when a stream is written to.
*/
public $registered_streaming_write_callback = null;
/**
* Whether or not the set_time_limit function should be called.
*/
public $allow_set_time_limit = true;
/*%******************************************************************************************%*/
// CONSTANTS
/**
* GET HTTP Method
*/
const HTTP_GET = 'GET';
/**
* POST HTTP Method
*/
const HTTP_POST = 'POST';
/**
* PUT HTTP Method
*/
const HTTP_PUT = 'PUT';
/**
* DELETE HTTP Method
*/
const HTTP_DELETE = 'DELETE';
/**
* HEAD HTTP Method
*/
const HTTP_HEAD = 'HEAD';
/*%******************************************************************************************%*/
// CONSTRUCTOR/DESTRUCTOR
/**
* Constructs a new instance of this class.
*
* @param string $url (Optional) The URL to request or service endpoint to query.
* @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
* @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class.
* @return $this A reference to the current instance.
*/
public function __construct($url = null, $proxy = null, $helpers = null)
{
// Set some default values.
$this->request_url = $url;
$this->method = self::HTTP_GET;
$this->request_headers = array();
$this->request_body = '';
// Determine if set_time_limit can be called
if (strpos(ini_get('disable_functions'), 'set_time_limit') !== false)
{
$this->allow_set_time_limit = false;
}
// Set a new Request class if one was set.
if (isset($helpers['request']) && !empty($helpers['request']))
{
$this->request_class = $helpers['request'];
}
// Set a new Request class if one was set.
if (isset($helpers['response']) && !empty($helpers['response']))
{
$this->response_class = $helpers['response'];
}
if ($proxy)
{
$this->set_proxy($proxy);
}
return $this;
}
/**
* Destructs the instance. Closes opened file handles.
*
* @return $this A reference to the current instance.
*/
public function __destruct()
{
if (isset($this->read_file) && isset($this->read_stream))
{
fclose($this->read_stream);
}
if (isset($this->write_file) && isset($this->write_stream))
{
fclose($this->write_stream);
}
return $this;
}
/*%******************************************************************************************%*/
// REQUEST METHODS
/**
* Sets the credentials to use for authentication.
*
* @param string $user (Required) The username to authenticate with.
* @param string $pass (Required) The password to authenticate with.
* @return $this A reference to the current instance.
*/
public function set_credentials($user, $pass)
{
$this->username = $user;
$this->password = $pass;
return $this;
}
/**
* Adds a custom HTTP header to the cURL request.
*
* @param string $key (Required) The custom HTTP header to set.
* @param mixed $value (Required) The value to assign to the custom HTTP header.
* @return $this A reference to the current instance.
*/
public function add_header($key, $value)
{
$this->request_headers[$key] = $value;
return $this;
}
/**
* Removes an HTTP header from the cURL request.
*
* @param string $key (Required) The custom HTTP header to set.
* @return $this A reference to the current instance.
*/
public function remove_header($key)
{
if (isset($this->request_headers[$key]))
{
unset($this->request_headers[$key]);
}
return $this;
}
/**
* Set the method type for the request.
*
* @param string $method (Required) One of the following constants: <HTTP_GET>, <HTTP_POST>, <HTTP_PUT>, <HTTP_HEAD>, <HTTP_DELETE>.
* @return $this A reference to the current instance.
*/
public function set_method($method)
{
$this->method = strtoupper($method);
return $this;
}
/**
* Sets a custom useragent string for the class.
*
* @param string $ua (Required) The useragent string to use.
* @return $this A reference to the current instance.
*/
public function set_useragent($ua)
{
$this->useragent = $ua;
return $this;
}
/**
* Set the body to send in the request.
*
* @param string $body (Required) The textual content to send along in the body of the request.
* @return $this A reference to the current instance.
*/
public function set_body($body)
{
$this->request_body = $body;
return $this;
}
/**
* Set the URL to make the request to.
*
* @param string $url (Required) The URL to make the request to.
* @return $this A reference to the current instance.
*/
public function set_request_url($url)
{
$this->request_url = $url;
return $this;
}
/**
* Set additional CURLOPT settings. These will merge with the default settings, and override if
* there is a duplicate.
*
* @param array $curlopts (Optional) A set of key-value pairs that set `CURLOPT` options. These will merge with the existing CURLOPTs, and ones passed here will override the defaults. Keys should be the `CURLOPT_*` constants, not strings.
* @return $this A reference to the current instance.
*/
public function set_curlopts($curlopts)
{
$this->curlopts = $curlopts;
return $this;
}
/**
* Sets the length in bytes to read from the stream while streaming up.
*
* @param integer $size (Required) The length in bytes to read from the stream.
* @return $this A reference to the current instance.
*/
public function set_read_stream_size($size)
{
$this->read_stream_size = $size;
return $this;
}
/**
* Sets the resource to read from while streaming up. Reads the stream from its current position until
* EOF or `$size` bytes have been read. If `$size` is not given it will be determined by <php:fstat()> and
* <php:ftell()>.
*
* @param resource $resource (Required) The readable resource to read from.
* @param integer $size (Optional) The size of the stream to read.
* @return $this A reference to the current instance.
*/
public function set_read_stream($resource, $size = null)
{
if (!isset($size) || $size < 0)
{
$stats = fstat($resource);
if ($stats && $stats['size'] >= 0)
{
$position = ftell($resource);
if ($position !== false && $position >= 0)
{
$size = $stats['size'] - $position;
}
}
}
$this->read_stream = $resource;
return $this->set_read_stream_size($size);
}
/**
* Sets the file to read from while streaming up.
*
* @param string $location (Required) The readable location to read from.
* @return $this A reference to the current instance.
*/
public function set_read_file($location)
{
$this->read_file = $location;
$read_file_handle = fopen($location, 'r');
return $this->set_read_stream($read_file_handle);
}
/**
* Sets the resource to write to while streaming down.
*
* @param resource $resource (Required) The writeable resource to write to.
* @return $this A reference to the current instance.
*/
public function set_write_stream($resource)
{
$this->write_stream = $resource;
return $this;
}
/**
* Sets the file to write to while streaming down.
*
* @param string $location (Required) The writeable location to write to.
* @return $this A reference to the current instance.
*/
public function set_write_file($location)
{
$this->write_file = $location;
$write_file_handle = fopen($location, 'w');
return $this->set_write_stream($write_file_handle);
}
/**
* Set the proxy to use for making requests.
*
* @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
* @return $this A reference to the current instance.
*/
public function set_proxy($proxy)
{
$proxy = parse_url($proxy);
$proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null;
$proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null;
$proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null;
$this->proxy = $proxy;
return $this;
}
/**
* Set the intended starting seek position.
*
* @param integer $position (Required) The byte-position of the stream to begin reading from.
* @return $this A reference to the current instance.
*/
public function set_seek_position($position)
{
$this->seek_position = isset($position) ? (integer) $position : null;
return $this;
}
/**
* Register a callback function to execute whenever a data stream is read from using
* <CFRequest::streaming_read_callback()>.
*
* The user-defined callback function should accept three arguments:
*
* <ul>
* <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
* <li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>
* <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
* </ul>
*
* @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
* <li>The name of a global function to execute, passed as a string.</li>
* <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
* <li>An anonymous function (PHP 5.3+).</li></ul>
* @return $this A reference to the current instance.
*/
public function register_streaming_read_callback($callback)
{
$this->registered_streaming_read_callback = $callback;
return $this;
}
/**
* Register a callback function to execute whenever a data stream is written to using
* <CFRequest::streaming_write_callback()>.
*
* The user-defined callback function should accept two arguments:
*
* <ul>
* <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
* <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
* </ul>
*
* @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
* <li>The name of a global function to execute, passed as a string.</li>
* <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
* <li>An anonymous function (PHP 5.3+).</li></ul>
* @return $this A reference to the current instance.
*/
public function register_streaming_write_callback($callback)
{
$this->registered_streaming_write_callback = $callback;
return $this;
}
/*%******************************************************************************************%*/
// PREPARE, SEND, AND PROCESS REQUEST
/**
* A callback function that is invoked by cURL for streaming up.
*
* @param resource $curl_handle (Required) The cURL handle for the request.
* @param resource $file_handle (Required) The open file handle resource.
* @param integer $length (Required) The maximum number of bytes to read.
* @return binary Binary data from a stream.
*/
public function streaming_read_callback($curl_handle, $file_handle, $length)
{
// Once we've sent as much as we're supposed to send...
if ($this->read_stream_read >= $this->read_stream_size)
{
// Send EOF
return '';
}
// If we're at the beginning of an upload and need to seek...
if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream))
{
if (fseek($this->read_stream, $this->seek_position) !== 0)
{
throw new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.');
}
}
$read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size
$this->read_stream_read += strlen($read);
$out = $read === false ? '' : $read;
// Execute callback function
if ($this->registered_streaming_read_callback)
{
call_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out);
}
return $out;
}
/**
* A callback function that is invoked by cURL for streaming down.
*
* @param resource $curl_handle (Required) The cURL handle for the request.
* @param binary $data (Required) The data to write.
* @return integer The number of bytes written.
*/
public function streaming_write_callback($curl_handle, $data)
{
$length = strlen($data);
$written_total = 0;
$written_last = 0;
while ($written_total < $length)
{
$written_last = fwrite($this->write_stream, substr($data, $written_total));
if ($written_last === false)
{
return $written_total;
}
$written_total += $written_last;
}
// Execute callback function
if ($this->registered_streaming_write_callback)
{
call_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total);
}
return $written_total;
}
/**
* Prepares and adds the details of the cURL request. This can be passed along to a <php:curl_multi_exec()>
* function.
*
* @return resource The handle for the cURL object.
*/
public function prep_request()
{
$curl_handle = curl_init();
// Set default options.
curl_setopt($curl_handle, CURLOPT_URL, $this->request_url);
curl_setopt($curl_handle, CURLOPT_FILETIME, true);
curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false);
curl_setopt($curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5);
curl_setopt($curl_handle, CURLOPT_HEADER, true);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl_handle, CURLOPT_TIMEOUT, 5184000);
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 120);
curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true);
curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url);
curl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent);
curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback'));
// Verification of the SSL cert
if ($this->ssl_verification)
{
curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, 2);
}
else
{
curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false);
}
// chmod the file as 0755
if ($this->cacert_location === true)
{
curl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
}
elseif (is_string($this->cacert_location))
{
curl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location);
}
// Debug mode
if ($this->debug_mode)
{
curl_setopt($curl_handle, CURLOPT_VERBOSE, true);
}
// Handle open_basedir & safe mode
if (!ini_get('safe_mode') && !ini_get('open_basedir'))
{
curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
}
// Enable a proxy connection if requested.
if ($this->proxy)
{
curl_setopt($curl_handle, CURLOPT_HTTPPROXYTUNNEL, true);
$host = $this->proxy['host'];
$host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : '';
curl_setopt($curl_handle, CURLOPT_PROXY, $host);
if (isset($this->proxy['user']) && isset($this->proxy['pass']))
{
curl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']);
}
}
// Set credentials for HTTP Basic/Digest Authentication.
if ($this->username && $this->password)
{
curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password);
}
// Handle the encoding if we can.
if (extension_loaded('zlib'))
{
curl_setopt($curl_handle, CURLOPT_ENCODING, 'gzip, deflate');
}
// Process custom headers
if (isset($this->request_headers) && count($this->request_headers))
{
$temp_headers = array();
if (!array_key_exists('Expect', $this->request_headers))
{
$this->request_headers['Expect'] = '';
}
foreach ($this->request_headers as $k => $v)
{
$temp_headers[] = $k . ': ' . $v;
}
curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers);
}
switch ($this->method)
{
case self::HTTP_PUT:
curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT');
if (isset($this->read_stream))
{
if (!isset($this->read_stream_size) || $this->read_stream_size < 0)
{
throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.');
}
curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size);
curl_setopt($curl_handle, CURLOPT_UPLOAD, true);
}
else
{
curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
}
break;
case self::HTTP_POST:
curl_setopt($curl_handle, CURLOPT_POST, true);
curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
break;
case self::HTTP_HEAD:
curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD);
curl_setopt($curl_handle, CURLOPT_NOBODY, 1);
break;
default: // Assumed GET
curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method);
if (isset($this->write_stream))
{
curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback'));
curl_setopt($curl_handle, CURLOPT_HEADER, false);
}
else
{
curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
}
break;
}
// Merge in the CURLOPTs
if (isset($this->curlopts) && sizeof($this->curlopts) > 0)
{
foreach ($this->curlopts as $k => $v)
{
curl_setopt($curl_handle, $k, $v);
}
}
return $curl_handle;
}
/**
* Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the
* data stored in the `curl_handle` and `response` properties unless replacement data is passed in via
* parameters.
*
* @param resource $curl_handle (Optional) The reference to the already executed cURL request.
* @param string $response (Optional) The actual response content itself that needs to be parsed.
* @return ResponseCore A <ResponseCore> object containing a parsed HTTP response.
*/
public function process_response($curl_handle = null, $response = null)
{
// Accept a custom one if it's passed.
if ($curl_handle && $response)
{
$this->curl_handle = $curl_handle;
$this->response = $response;
}
// As long as this came back as a valid resource...
if (is_resource($this->curl_handle))
{
// Determine what's what.
$header_size = curl_getinfo($this->curl_handle, CURLINFO_HEADER_SIZE);
$this->response_headers = substr($this->response, 0, $header_size);
$this->response_body = substr($this->response, $header_size);
$this->response_code = curl_getinfo($this->curl_handle, CURLINFO_HTTP_CODE);
$this->response_info = curl_getinfo($this->curl_handle);
// Parse out the headers
$this->response_headers = explode("\r\n\r\n", trim($this->response_headers));
$this->response_headers = array_pop($this->response_headers);
$this->response_headers = explode("\r\n", $this->response_headers);
array_shift($this->response_headers);
// Loop through and split up the headers.
$header_assoc = array();
foreach ($this->response_headers as $header)
{
$kv = explode(': ', $header);
$header_assoc[strtolower($kv[0])] = $kv[1];
}
// Reset the headers to the appropriate property.
$this->response_headers = $header_assoc;
$this->response_headers['_info'] = $this->response_info;
$this->response_headers['_info']['method'] = $this->method;
if ($curl_handle && $response)
{
return new $this->response_class($this->response_headers, $this->response_body, $this->response_code, $this->curl_handle);
}
}
// Return false
return false;
}
/**
* Sends the request, calling necessary utility functions to update built-in properties.
*
* @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not.
* @return string The resulting unparsed data from the request.
*/
public function send_request($parse = false)
{
if ($this->allow_set_time_limit)
{
set_time_limit(0);
}
$curl_handle = $this->prep_request();
$this->response = curl_exec($curl_handle);
if ($this->response === false)
{
throw new cURL_Exception('cURL resource: ' . (string) $curl_handle . '; cURL error: ' . curl_error($curl_handle) . ' (cURL error code ' . curl_errno($curl_handle) . '). See http://curl.haxx.se/libcurl/c/libcurl-errors.html for an explanation of error codes.');
}
$parsed_response = $this->process_response($curl_handle, $this->response);
curl_close($curl_handle);
if ($parse)
{
return $parsed_response;
}
return $this->response;
}
/**
* Sends the request using <php:curl_multi_exec()>, enabling parallel requests. Uses the "rolling" method.
*
* @param array $handles (Required) An indexed array of cURL handles to process simultaneously.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>callback</code> - <code>string|array</code> - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the <code>[0]</code> index is the class and the <code>[1]</code> index is the method name.</li>
* <li><code>limit</code> - <code>integer</code> - Optional - The number of simultaneous requests to make. This can be useful for scaling around slow server responses. Defaults to trusting cURLs judgement as to how many to use.</li></ul>
* @return array Post-processed cURL responses.
*/
public function send_multi_request($handles, $opt = null)
{
if ($this->allow_set_time_limit)
{
set_time_limit(0);
}
// Skip everything if there are no handles to process.
if (count($handles) === 0) return array();
if (!$opt) $opt = array();
// Initialize any missing options
$limit = isset($opt['limit']) ? $opt['limit'] : -1;
// Initialize
$handle_list = $handles;
$http = new $this->request_class();
$multi_handle = curl_multi_init();
$handles_post = array();
$added = count($handles);
$last_handle = null;
$count = 0;
$i = 0;
// Loop through the cURL handles and add as many as it set by the limit parameter.
while ($i < $added)
{
if ($limit > 0 && $i >= $limit) break;
curl_multi_add_handle($multi_handle, array_shift($handles));
$i++;
}
do
{
$active = false;
// Start executing and wait for a response.
while (($status = curl_multi_exec($multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM)
{
// Start looking for possible responses immediately when we have to add more handles
if (count($handles) > 0) break;
}
// Figure out which requests finished.
$to_process = array();
while ($done = curl_multi_info_read($multi_handle))
{
// Since curl_errno() isn't reliable for handles that were in multirequests, we check the 'result' of the info read, which contains the curl error number, (listed here http://curl.haxx.se/libcurl/c/libcurl-errors.html )
if ($done['result'] > 0)
{
throw new cURL_Multi_Exception('cURL resource: ' . (string) $done['handle'] . '; cURL error: ' . curl_error($done['handle']) . ' (cURL error code ' . $done['result'] . '). See http://curl.haxx.se/libcurl/c/libcurl-errors.html for an explanation of error codes.');
}
// Because curl_multi_info_read() might return more than one message about a request, we check to see if this request is already in our array of completed requests
elseif (!isset($to_process[(int) $done['handle']]))
{
$to_process[(int) $done['handle']] = $done;
}
}
// Actually deal with the request
foreach ($to_process as $pkey => $done)
{
$response = $http->process_response($done['handle'], curl_multi_getcontent($done['handle']));
$key = array_search($done['handle'], $handle_list, true);
$handles_post[$key] = $response;
if (count($handles) > 0)
{
curl_multi_add_handle($multi_handle, array_shift($handles));
}
curl_multi_remove_handle($multi_handle, $done['handle']);
curl_close($done['handle']);
}
}
while ($active || count($handles_post) < $added);
curl_multi_close($multi_handle);
ksort($handles_post, SORT_NUMERIC);
return $handles_post;
}
/*%******************************************************************************************%*/
// RESPONSE METHODS
/**
* Get the HTTP response headers from the request.
*
* @param string $header (Optional) A specific header value to return. Defaults to all headers.
* @return string|array All or selected header values.
*/
public function get_response_header($header = null)
{
if ($header)
{
return $this->response_headers[strtolower($header)];
}
return $this->response_headers;
}
/**
* Get the HTTP response body from the request.
*
* @return string The response body.
*/
public function get_response_body()
{
return $this->response_body;
}
/**
* Get the HTTP response code from the request.
*
* @return string The HTTP response code.
*/
public function get_response_code()
{
return $this->response_code;
}
}
/**
* Container for all response-related methods.
*/
class ResponseCore
{
/**
* Stores the HTTP header information.
*/
public $header;
/**
* Stores the SimpleXML response.
*/
public $body;
/**
* Stores the HTTP response code.
*/
public $status;
/**
* Constructs a new instance of this class.
*
* @param array $header (Required) Associative array of HTTP headers (typically returned by <RequestCore::get_response_header()>).
* @param string $body (Required) XML-formatted response from AWS.
* @param integer $status (Optional) HTTP response status code from the request.
* @return object Contains an <php:array> `header` property (HTTP headers as an associative array), a <php:SimpleXMLElement> or <php:string> `body` property, and an <php:integer> `status` code.
*/
public function __construct($header, $body, $status = null)
{
$this->header = $header;
$this->body = $body;
$this->status = $status;
return $this;
}
/**
* Did we receive the status code we expected?
*
* @param integer|array $codes (Optional) The status code(s) to expect. Pass an <php:integer> for a single acceptable value, or an <php:array> of integers for multiple acceptable values.
* @return boolean Whether we received the expected status code or not.
*/
public function isOK($codes = array(200, 201, 204, 206))
{
if (is_array($codes))
{
return in_array($this->status, $codes);
}
return $this->status === $codes;
}
}
class cURL_Exception extends Exception {}
class cURL_Multi_Exception extends cURL_Exception {}
class RequestCore_Exception extends Exception {}
/*
* Copyright 2010-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*%******************************************************************************************%*/
// CLASS
/**
* Wraps the underlying `RequestCore` class with some AWS-specific customizations.
*
* @version 2011.12.02
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/php/ PHP Developer Center
*/
class CFRequest extends RequestCore
{
/**
* The default class to use for HTTP Requests (defaults to <CFRequest>).
*/
public $request_class = 'CFRequest';
/**
* The default class to use for HTTP Responses (defaults to <CFResponse>).
*/
public $response_class = 'CFResponse';
/**
* The active credential set.
*/
public $credentials;
/*%******************************************************************************************%*/
// CONSTRUCTOR
/**
* Constructs a new instance of this class.
*
* @param string $url (Optional) The URL to request or service endpoint to query.
* @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
* @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class.
* @param CFCredential $credentials (Required) The credentials to use for signing and making requests.
* @return $this A reference to the current instance.
*/
public function __construct($url = null, $proxy = null, $helpers = null, CFCredential $credentials = null)
{
parent::__construct($url, $proxy, $helpers);
// Standard settings for all requests
$this->set_useragent(CFRUNTIME_USERAGENT);
$this->credentials = $credentials;
$this->cacert_location = ($this->credentials['certificate_authority'] ? $this->credentials['certificate_authority'] : false);
return $this;
}
}
/*
* Copyright 2010-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*%******************************************************************************************%*/
// CLASS
/**
* The <CFCredentials> class enables developers to easily switch between multiple sets of credentials.
*
* @version 2012.07.13
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/php/ PHP Developer Center
*/
class CFCredentials
{
/**
* The key used to specify the default credential set
*/
const DEFAULT_KEY = '@default';
/**
* The key used to identify inherited credentials
*/
const INHERIT_KEY = '@inherit';
/**
* Stores the credentials
*/
protected static $credentials = array();
/**
* Prevents this class from being constructed
*/
final private function __construct() {}
/**
* Stores the credentials for re-use.
*
* @param array $credential_sets (Required) The named credential sets that should be made available to the application.
* @return void
*/
public static function set(array $credential_sets)
{
// Make sure a default credential set is specified or can be inferred
if (count($credential_sets) === 1)
{
$credential_sets[self::DEFAULT_KEY] = reset($credential_sets);
}
// Resolve any @inherit tags
foreach ($credential_sets as $credential_name => &$credential_set)
{
if (is_array($credential_set))
{
foreach ($credential_set as $credential_key => &$credential_value)
{
if ($credential_key === self::INHERIT_KEY)
{
if (!isset($credential_sets[$credential_value]))
{
throw new CFCredentials_Exception('The credential set, "' . $credential_value . '", does not exist and cannot be inherited.');
}
$credential_set = array_merge($credential_sets[$credential_value], $credential_set);
unset($credential_set[self::INHERIT_KEY]);
}
}
}
}
// Normalize the value of the @default credential set
if (isset($credential_sets[self::DEFAULT_KEY]))
{
$default = $credential_sets[self::DEFAULT_KEY];
if (is_string($default))
{
if (!isset($credential_sets[$default]))
{
throw new CFCredentials_Exception('The credential set, "' . $default . '", does not exist and cannot be used as the default credential set.');
}
$credential_sets[self::DEFAULT_KEY] = $credential_sets[$default];
}
}
// Store the credentials
self::$credentials = $credential_sets;
}
/**
* Retrieves the requested credentials from the internal credential store.
*
* @param string $credential_set (Optional) The name of the credential set to retrieve. The default value is set in DEFAULT_KEY.
* @return stdClass A stdClass object where the properties represent the keys that were provided.
*/
public static function get($credential_name = self::DEFAULT_KEY)
{
// Make sure the credential set exists
if (!isset(self::$credentials[$credential_name]))
{
throw new CFCredentials_Exception('The credential set, "' . $credential_name . '", does not exist and cannot be retrieved.');
}
// Return the credential set as an object
return new CFCredential(self::$credentials[$credential_name]);
}
/**
* Retrieves a list of all available credential set names.
*
* @return CFArray A list of all available credential set names.
*/
public static function list_sets()
{
return new CFArray(array_keys(self::$credentials));
}
}
class CFCredentials_Exception extends Exception {}
/*
* Copyright 2010-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*%******************************************************************************************%*/
// CLASS
/**
* The <CFCredential> class represents an individual credential set.
*
* @version 2011.11.15
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/php/ PHP Developer Center
*/
class CFCredential implements ArrayAccess
{
/**
* Stores the internal <php:ArrayObject> representation of the collection.
*/
private $collection;
/**
* Default getter. Enables syntax such as $object->method->chained_method();. Also supports
* $object->key. Matching methods are prioritized over matching keys.
*
* @param string $name (Required) The name of the method to execute or key to retrieve.
* @return mixed The results of calling the function <code>$name()</code>, or the value of the key <code>$object[$name]</code>.
*/
public function __get($name)
{
return $this[$name];
}
/**
* Default setter.
*
* @param string $name (Required) The name of the method to execute.
* @param string $value (Required) The value to pass to the method.
* @return mixed The results of calling the function, <code>$name</code>.
*/
public function __set($name, $value)
{
$this[$name] = $value;
return $this;
}
/**
* Create a clone of the object.
*
* @return CFCredential A clone of the current instance.
*/
public function __clone()
{
$this->collection = clone $this->collection;
}
/*%******************************************************************************************%*/
// CONSTRUCTOR
/**
* Constructs a new instance of the <CFCredential> class.
*/
public function __construct($value = array())
{
$this->collection = new ArrayObject($value, ArrayObject::ARRAY_AS_PROPS);
}
/**
* Check whether or not a specific offset exists.
*
* @param integer $offset (Required) The location in the collection to verify the existence of.
* @return boolean A value of <code>true</code> indicates that the collection offset exists. A value of <code>false</code> indicates that it does not.
*/
public function offsetExists($offset)
{
return $this->collection->offsetExists($offset);
}
/**
* Get the value for a specific offset.
*
* @param integer $offset (Required) The location in the collection to retrieve the value for.
* @return mixed The value of the collection offset. <code>NULL</code> is returned if the offset does not exist.
*/
public function offsetGet($offset)
{
if ($this->collection->offsetExists($offset))
{
return $this->collection->offsetGet($offset);
}
return null;
}
/**
* Set the value for a specific offset.
*
* @param integer $offset (Required) The location in the collection to set a new value for.
* @param mixed $value (Required) The new value for the collection location.
* @return CFCredential A reference to the current collection.
*/
public function offsetSet($offset, $value)
{
$this->collection->offsetSet($offset, $value);
return $this;
}
/**
* Unset the value for a specific offset.
*
* @param integer $offset (Required) The location in the collection to unset.
* @return CFCredential A reference to the current collection.
*/
public function offsetUnset($offset)
{
$this->collection->offsetUnset($offset);
return $this;
}
/**
* Merge another instance of <CFCredential> onto this one.
*
* @param CFCredential $credential (Required) Another instance of <CFCredential>.
* @return CFCredential A reference to the current collection.
*/
public function merge(CFCredential $credential)
{
$merged = array_merge($this->to_array(), $credential->to_array());
$this->collection->exchangeArray($merged);
return $this;
}
/**
* Retrieves the data as a standard array.
*
* @return array The data as an array.
*/
public function to_array()
{
return $this->collection->getArrayCopy();
}
}
/*
* Copyright 2010-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*%******************************************************************************************%*/
// CLASS
/**
* Contains a set of utility methods for connecting to, and working with, AWS.
*
* @version 2010.09.30
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/php/ PHP Developer Center
*/
class CFUtilities
{
/*%******************************************************************************************%*/
// CONSTANTS
/**
* Define the RFC 2616-compliant date format.
*/
const DATE_FORMAT_RFC2616 = 'D, d M Y H:i:s \G\M\T';
/**
* Define the ISO-8601-compliant date format.
*/
const DATE_FORMAT_ISO8601 = 'Y-m-d\TH:i:s\Z';
/**
* Define the MySQL-compliant date format.
*/
const DATE_FORMAT_MYSQL = 'Y-m-d H:i:s';
/**
* Define the Signature v4 date format.
*/
const DATE_FORMAT_SIGV4 = 'Ymd\THis\Z';
/*%******************************************************************************************%*/
// METHODS
/**
* Constructs a new instance of this class.
*
* @return $this A reference to the current instance.
*/
public function __construct()
{
return $this;
}
/**
* Retrieves the value of a class constant, while avoiding the `T_PAAMAYIM_NEKUDOTAYIM` error. Misspelled because `const` is a reserved word.
*
* @param object $class (Required) An instance of the class containing the constant.
* @param string $const (Required) The name of the constant to retrieve.
* @return mixed The value of the class constant.
*/
public function konst($class, $const)
{
if (is_string($class))
{
$ref = new ReflectionClass($class);
}
else
{
$ref = new ReflectionObject($class);
}
return $ref->getConstant($const);
}
/**
* Convert a HEX value to Base64.
*
* @param string $str (Required) Value to convert.
* @return string Base64-encoded string.
*/
public function hex_to_base64($str)
{
$raw = '';
for ($i = 0; $i < strlen($str); $i += 2)
{
$raw .= chr(hexdec(substr($str, $i, 2)));
}
return base64_encode($raw);
}
/**
* Convert an associative array into a query string.
*
* @param array $array (Required) Array to convert.
* @return string URL-friendly query string.
*/
public function to_query_string($array)
{
$temp = array();
foreach ($array as $key => $value)
{
if (is_string($key) && !is_array($value))
{
$temp[] = rawurlencode($key) . '=' . rawurlencode($value);
}
}
return implode('&', $temp);
}
/**
* Convert an associative array into a sign-able string.
*
* @param array $array (Required) Array to convert.
* @return string URL-friendly sign-able string.
*/
public function to_signable_string($array)
{
$t = array();
foreach ($array as $k => $v)
{
$t[] = $this->encode_signature2($k) . '=' . $this->encode_signature2($v);
}
return implode('&', $t);
}
/**
* Encode the value according to RFC 3986.
*
* @param string $string (Required) String to convert.
* @return string URL-friendly sign-able string.
*/
public function encode_signature2($string)
{
$string = rawurlencode($string);
return str_replace('%7E', '~', $string);
}
/**
* Convert a query string into an associative array. Multiple, identical keys will become an indexed array.
*
* @param string $qs (Required) Query string to convert.
* @return array Associative array of keys and values.
*/
public function query_to_array($qs)
{
$query = explode('&', $qs);
$data = array();
foreach ($query as $q)
{
$q = explode('=', $q);
if (isset($data[$q[0]]) && is_array($data[$q[0]]))
{
$data[$q[0]][] = urldecode($q[1]);
}
else if (isset($data[$q[0]]) && !is_array($data[$q[0]]))
{
$data[$q[0]] = array($data[$q[0]]);
$data[$q[0]][] = urldecode($q[1]);
}
else
{
$data[urldecode($q[0])] = urldecode($q[1]);
}
}
return $data;
}
/**
* Return human readable file sizes.
*
* @author Aidan Lister <[email protected]>
* @author Ryan Parman <[email protected]>
* @license http://www.php.net/license/3_01.txt PHP License
* @param integer $size (Required) Filesize in bytes.
* @param string $unit (Optional) The maximum unit to use. Defaults to the largest appropriate unit.
* @param string $default (Optional) The format for the return string. Defaults to `%01.2f %s`.
* @return string The human-readable file size.
* @link http://aidanlister.com/repos/v/function.size_readable.php Original Function
*/
public function size_readable($size, $unit = null, $default = null)
{
// Units
$sizes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB');
$mod = 1024;
$ii = count($sizes) - 1;
// Max unit
$unit = array_search((string) $unit, $sizes);
if ($unit === null || $unit === false)
{
$unit = $ii;
}
// Return string
if ($default === null)
{
$default = '%01.2f %s';
}
// Loop
$i = 0;
while ($unit != $i && $size >= 1024 && $i < $ii)
{
$size /= $mod;
$i++;
}
return sprintf($default, $size, $sizes[$i]);
}
/**
* Convert a number of seconds into Hours:Minutes:Seconds.
*
* @param integer $seconds (Required) The number of seconds to convert.
* @return string The formatted time.
*/
public function time_hms($seconds)
{
$time = '';
// First pass
$hours = (int) ($seconds / 3600);
$seconds = $seconds % 3600;
$minutes = (int) ($seconds / 60);
$seconds = $seconds % 60;
// Cleanup
$time .= ($hours) ? $hours . ':' : '';
$time .= ($minutes < 10 && $hours > 0) ? '0' . $minutes : $minutes;
$time .= ':';
$time .= ($seconds < 10) ? '0' . $seconds : $seconds;
return $time;
}
/**
* Returns the first value that is set. Based on [Try.these()](http://api.prototypejs.org/language/Try/these/) from [Prototype](http://prototypejs.org).
*
* @param array $attrs (Required) The attributes to test, as strings. Intended for testing properties of the $base object, but also works with variables if you place an @ symbol at the beginning of the command.
* @param object $base (Optional) The base object to use, if any.
* @param mixed $default (Optional) What to return if there are no matches. Defaults to `null`.
* @return mixed Either a matching property of a given object, boolean `false`, or any other data type you might choose.
*/
public function try_these($attrs, $base = null, $default = null)
{
if ($base)
{
foreach ($attrs as $attr)
{
if (isset($base->$attr))
{
return $base->$attr;
}
}
}
else
{
foreach ($attrs as $attr)
{
if (isset($attr))
{
return $attr;
}
}
}
return $default;
}
/**
* Can be removed once all calls are updated.
*
* @deprecated Use <php:json_encode()> instead.
* @param mixed $obj (Required) The PHP object to convert into a JSON string.
* @return string A JSON string.
*/
public function json_encode($obj)
{
return json_encode($obj);
}
/**
* Converts a SimpleXML response to an array structure.
*
* @param ResponseCore $response (Required) A response value.
* @return array The response value as a standard, multi-dimensional array.
*/
public function convert_response_to_array(ResponseCore $response)
{
return json_decode(json_encode($response), true);
}
/**
* Checks to see if a date stamp is ISO-8601 formatted, and if not, makes it so.
*
* @param string $datestamp (Required) A date stamp, or a string that can be parsed into a date stamp.
* @return string An ISO-8601 formatted date stamp.
*/
public function convert_date_to_iso8601($datestamp)
{
if (!preg_match('/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}((\+|-)\d{2}:\d{2}|Z)/m', $datestamp))
{
return gmdate(self::DATE_FORMAT_ISO8601, strtotime($datestamp));
}
return $datestamp;
}
/**
* Determines whether the data is Base64 encoded or not.
*
* @license http://us.php.net/manual/en/function.base64-decode.php#81425 PHP License
* @param string $s (Required) The string to test.
* @return boolean Whether the string is Base64 encoded or not.
*/
public function is_base64($s)
{
return (bool) preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $s);
}
/**
* Determines whether the data is a JSON string or not.
*
* @param string $s (Required) The string to test.
* @return boolean Whether the string is a valid JSON object or not.
*/
public function is_json($s)
{
return !!(json_decode($s) instanceof stdClass);
}
/**
* Decodes `\uXXXX` entities into their real unicode character equivalents.
*
* @param string $s (Required) The string to decode.
* @return string The decoded string.
*/
public function decode_uhex($s)
{
preg_match_all('/\\\u([0-9a-f]{4})/i', $s, $matches);
$matches = $matches[count($matches) - 1];
$map = array();
foreach ($matches as $match)
{
if (!isset($map[$match]))
{
$map['\u' . $match] = html_entity_decode('&#' . hexdec($match) . ';', ENT_NOQUOTES, 'UTF-8');
}
}
return str_replace(array_keys($map), $map, $s);
}
/**
* Generates a random GUID.
*
* @author Alix Axel <http://www.php.net/manual/en/function.com-create-guid.php#99425>
* @license http://www.php.net/license/3_01.txt PHP License
* @return string A random GUID.
*/
public function generate_guid()
{
return sprintf(
'%04X%04X-%04X-%04X-%04X-%04X%04X%04X',
mt_rand(0, 65535),
mt_rand(0, 65535),
mt_rand(0, 65535),
mt_rand(16384, 20479),
mt_rand(32768, 49151),
mt_rand(0, 65535),
mt_rand(0, 65535),
mt_rand(0, 65535)
);
}
}
/*
* Copyright 2010-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*%******************************************************************************************%*/
// EXCEPTIONS
/**
* Default CFRuntime Exception.
*/
class CFRuntime_Exception extends Exception {}
/**
* Parsing Exception.
*/
class Parser_Exception extends Exception {}
/*%******************************************************************************************%*/
// DETERMINE WHAT ENVIRONMENT DATA TO ADD TO THE USERAGENT FOR METRIC TRACKING
/*
Define a temporary callback function for this calculation. Get the PHP version and any
required/optional extensions that are leveraged.
Tracking this data gives Amazon better metrics about what configurations are being used
so that forward-looking plans for the code can be made with more certainty (e.g. What
version of PHP are most people running? Do they tend to have the latest PCRE?).
*/
function __aws_sdk_ua_callback()
{
$ua_append = '';
$extensions = get_loaded_extensions();
$sorted_extensions = array();
if ($extensions)
{
foreach ($extensions as $extension)
{
if ($extension === 'curl' && function_exists('curl_version'))
{
$curl_version = curl_version();
$sorted_extensions[strtolower($extension)] = $curl_version['version'];
}
elseif ($extension === 'pcre' && defined('PCRE_VERSION'))
{
$pcre_version = explode(' ', PCRE_VERSION);
$sorted_extensions[strtolower($extension)] = $pcre_version[0];
}
elseif ($extension === 'openssl' && defined('OPENSSL_VERSION_TEXT'))
{
$openssl_version = explode(' ', OPENSSL_VERSION_TEXT);
$sorted_extensions[strtolower($extension)] = $openssl_version[1];
}
else
{
$sorted_extensions[strtolower($extension)] = phpversion($extension);
}
}
}
foreach (array('simplexml', 'json', 'pcre', 'spl', 'curl', 'openssl', 'apc', 'xcache', 'memcache', 'memcached', 'pdo', 'pdo_sqlite', 'sqlite', 'sqlite3', 'zlib', 'xdebug') as $ua_ext)
{
if (isset($sorted_extensions[$ua_ext]) && $sorted_extensions[$ua_ext])
{
$ua_append .= ' ' . $ua_ext . '/' . $sorted_extensions[$ua_ext];
}
elseif (isset($sorted_extensions[$ua_ext]))
{
$ua_append .= ' ' . $ua_ext . '/0';
}
}
foreach (array('memory_limit', 'date.timezone', 'open_basedir', 'safe_mode', 'zend.enable_gc') as $cfg)
{
$cfg_value = ini_get($cfg);
if (in_array($cfg, array('memory_limit', 'date.timezone'), true))
{
$ua_append .= ' ' . $cfg . '/' . str_replace('/', '.', $cfg_value);
}
elseif (in_array($cfg, array('open_basedir', 'safe_mode', 'zend.enable_gc'), true))
{
if ($cfg_value === false || $cfg_value === '' || $cfg_value === 0)
{
$cfg_value = 'off';
}
elseif ($cfg_value === true || $cfg_value === '1' || $cfg_value === 1)
{
$cfg_value = 'on';
}
$ua_append .= ' ' . $cfg . '/' . $cfg_value;
}
}
return $ua_append;
}
/*%******************************************************************************************%*/
// CLASS
/**
* Core functionality and default settings shared across all SDK classes. All methods and properties in this
* class are inherited by the service-specific classes.
*
* @version 2012.05.25
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/php/ PHP Developer Center
*/
class CFRuntime
{
/*%******************************************************************************************%*/
// CONSTANTS
/**
* Name of the software.
*/
const NAME = CFRUNTIME_NAME;
/**
* Version of the software.
*/
const VERSION = CFRUNTIME_VERSION;
/**
* Build ID of the software.
*/
const BUILD = CFRUNTIME_BUILD;
/**
* User agent string used to identify the software.
*/
const USERAGENT = CFRUNTIME_USERAGENT;
/*%******************************************************************************************%*/
// PROPERTIES
/**
* The Amazon API Key.
*/
public $key;
/**
* The Amazon API Secret Key.
*/
public $secret_key;
/**
* The Amazon Authentication Token.
*/
public $auth_token;
/**
* Handle for the utility functions.
*/
public $util;
/**
* An identifier for the current AWS service.
*/
public $service = null;
/**
* The supported API version.
*/
public $api_version = null;
/**
* The state of whether auth should be handled as AWS Query.
*/
public $use_aws_query = true;
/**
* The default class to use for utilities (defaults to <CFUtilities>).
*/
public $utilities_class = 'CFUtilities';
/**
* The default class to use for HTTP requests (defaults to <CFRequest>).
*/
public $request_class = 'CFRequest';
/**
* The default class to use for HTTP responses (defaults to <CFResponse>).
*/
public $response_class = 'CFResponse';
/**
* The default class to use for parsing XML (defaults to <CFSimpleXML>).
*/
public $parser_class = 'CFSimpleXML';
/**
* The default class to use for handling batch requests (defaults to <CFBatchRequest>).
*/
public $batch_class = 'CFBatchRequest';
/**
* The state of SSL/HTTPS use.
*/
public $use_ssl = true;
/**
* The state of SSL certificate verification.
*/
public $ssl_verification = true;
/**
* The proxy to use for connecting.
*/
public $proxy = null;
/**
* The alternate hostname to use, if any.
*/
public $hostname = null;
/**
* The state of the capability to override the hostname with <set_hostname()>.
*/
public $override_hostname = true;
/**
* The alternate port number to use, if any.
*/
public $port_number = null;
/**
* The alternate resource prefix to use, if any.
*/
public $resource_prefix = null;
/**
* The state of cache flow usage.
*/
public $use_cache_flow = false;
/**
* The caching class to use.
*/
public $cache_class = null;
/**
* The caching location to use.
*/
public $cache_location = null;
/**
* When the cache should be considered stale.
*/
public $cache_expires = null;
/**
* The state of cache compression.
*/
public $cache_compress = null;
/**
* The current instantiated cache object.
*/
public $cache_object = null;
/**
* The current instantiated batch request object.
*/
public $batch_object = null;
/**
* The internally instantiated batch request object.
*/
public $internal_batch_object = null;
/**
* The state of batch flow usage.
*/
public $use_batch_flow = false;
/**
* The state of the cache deletion setting.
*/
public $delete_cache = false;
/**
* The state of the debug mode setting.
*/
public $debug_mode = false;
/**
* The number of times to retry failed requests.
*/
public $max_retries = 3;
/**
* The user-defined callback function to call when a stream is read from.
*/
public $registered_streaming_read_callback = null;
/**
* The user-defined callback function to call when a stream is written to.
*/
public $registered_streaming_write_callback = null;
/**
* The credentials to use for authentication.
*/
public $credentials = array();
/**
* The authentication class to use.
*/
public $auth_class = null;
/**
* The operation to execute.
*/
public $operation = null;
/**
* The payload to send.
*/
public $payload = array();
/**
* The string prefix to prepend to the operation name.
*/
public $operation_prefix = '';
/**
* The number of times a request has been retried.
*/
public $redirects = 0;
/**
* The state of whether the response should be parsed or not.
*/
public $parse_the_response = true;
/*%******************************************************************************************%*/
// CONSTRUCTOR
/**
* The constructor. This class should not be instantiated directly. Rather, a service-specific class
* should be instantiated.
*
* @param array $options (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>certificate_authority</code> - <code>boolean</code> - Optional - Determines which Cerificate Authority file to use. A value of boolean <code>false</code> will use the Certificate Authority file available on the system. A value of boolean <code>true</code> will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to <code>0755</code>) will use that. Leave this set to <code>false</code> if you're not sure.</li>
* <li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>
* <li><code>default_cache_config</code> - <code>string</code> - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the <set_cache_config()> method. Valid values are: <code>apc</code>, <code>xcache</code>, or a file system path such as <code>./cache</code> or <code>/tmp/cache/</code>.</li>
* <li><code>key</code> - <code>string</code> - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.</li>
* <li><code>instance_profile_timeout</code> - <code>integer</code> - Optional - When retrieving IAM instance profile credentials, there is a hard connection timeout that defaults to 2 seconds to prevent unnecessary on non-EC2 systems. This setting allows you to change that timeout if needed.</li>
* <li><code>secret</code> - <code>string</code> - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.</li>
* <li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li>
* <li><code>use_instance_profile_credentials</code> - <code>boolean</code> - Optional - Forces the use of IAM Instance Profile credentials, even when regular credentials are provided.</li></ul>
* @return void
*/
public function __construct(array $options = array())
{
// Instantiate the utilities class.
$this->util = new $this->utilities_class();
// Determine the current service.
$this->service = get_class($this);
// Create credentials based on the options
$runtime_credentials = new CFCredential($options);
$credentials_provided = false;
// Retrieve a credential set from config.inc.php if it exists
if (isset($options['credentials']))
{
// Use a specific credential set and merge with the runtime credentials
$this->credentials = CFCredentials::get($options['credentials'])
->merge($runtime_credentials);
}
else
{
try
{
// Use the default credential set and merge with the runtime credentials
$this->credentials = CFCredentials::get(CFCredentials::DEFAULT_KEY)
->merge($runtime_credentials);
}
catch (CFCredentials_Exception $e)
{
// Only the runtime credentials were provided
$this->credentials = $runtime_credentials;
}
}
// Check if keys were actually provided
if (isset($this->credentials['key']) && isset($this->credentials['secret']))
{
$credentials_provided = true;
}
// Check for an instance profile credentials override
if (isset($this->credentials['use_instance_profile_credentials']) && $this->credentials['use_instance_profile_credentials'])
{
$credentials_provided = false;
}
// Automatically enable whichever caching mechanism is set to default.
$this->set_cache_config($this->credentials->default_cache_config);
// If no credentials were provided, try to get them from the EC2 instance profile
if (!$credentials_provided)
{
// Default caching mechanism is required
if (!$this->credentials->default_cache_config)
{
// @codeCoverageIgnoreStart
throw new CFCredentials_Exception('No credentials were provided. The SDK attempts to retrieve Instance '
. 'Profile credentials from the EC2 Instance Metadata Service, but doing this requires the '
. '"default_cache_config" option to be set in the config.inc.php file or constructor. In order to '
. 'cache the retrieved credentials.');
// @codeCoverageIgnoreEnd
}
// Instantiate and invoke the cache for instance profile credentials
$cache = new $this->cache_class('instance_profile_credentials', $this->cache_location, 0, $this->cache_compress);
if ($data = $cache->read())
{
$cache->expire_in((strtotime($data['expires']) - time()) * 0.85);
}
$instance_profile_credentials = $cache->response_manager(array($this, 'cache_instance_profile_credentials'), array($cache, $options));
$this->credentials->key = $instance_profile_credentials['key'];
$this->credentials->secret = $instance_profile_credentials['secret'];
$this->credentials->token = $instance_profile_credentials['token'];
}
// Set internal credentials after they are resolved
$this->key = $this->credentials->key;
$this->secret_key = $this->credentials->secret;
$this->auth_token = $this->credentials->token;
}
/**
* Alternate approach to constructing a new instance. Supports chaining.
*
* @param array $options (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>certificate_authority</code> - <code>boolean</code> - Optional - Determines which Cerificate Authority file to use. A value of boolean <code>false</code> will use the Certificate Authority file available on the system. A value of boolean <code>true</code> will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to <code>0755</code>) will use that. Leave this set to <code>false</code> if you're not sure.</li>
* <li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>
* <li><code>default_cache_config</code> - <code>string</code> - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the <set_cache_config()> method. Valid values are: <code>apc</code>, <code>xcache</code>, or a file system path such as <code>./cache</code> or <code>/tmp/cache/</code>.</li>
* <li><code>key</code> - <code>string</code> - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.</li>
* <li><code>secret</code> - <code>string</code> - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.</li>
* <li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li></ul>
* @return void
*/
public static function factory(array $options = array())
{
if (version_compare(PHP_VERSION, '5.3.0', '<'))
{
throw new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::factory().');
}
$self = get_called_class();
return new $self($options);
}
/*%******************************************************************************************%*/
// MAGIC METHODS
/**
* A magic method that allows `camelCase` method names to be translated into `snake_case` names.
*
* @param string $name (Required) The name of the method.
* @param array $arguments (Required) The arguments passed to the method.
* @return mixed The results of the intended method.
*/
public function __call($name, $arguments)
{
// Convert camelCase method calls to snake_case.
$method_name = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name));
if (method_exists($this, $method_name))
{
return call_user_func_array(array($this, $method_name), $arguments);
}
throw new CFRuntime_Exception('The method ' . $name . '() is undefined. Attempted to map to ' . $method_name . '() which is also undefined. Error occurred');
}
/*%******************************************************************************************%*/
// SET CUSTOM SETTINGS
/**
* Set the proxy settings to use.
*
* @param string $proxy (Required) Accepts proxy credentials in the following format: `proxy://user:pass@hostname:port`
* @return $this A reference to the current instance.
*/
public function set_proxy($proxy)
{
$this->proxy = $proxy;
return $this;
}
/**
* Set the hostname to connect to. This is useful for alternate services that are API-compatible with
* AWS, but run from a different hostname.
*
* @param string $hostname (Required) The alternate hostname to use in place of the default one. Useful for mock or test applications living on different hostnames.
* @param integer $port_number (Optional) The alternate port number to use in place of the default one. Useful for mock or test applications living on different port numbers.
* @return $this A reference to the current instance.
*/
public function set_hostname($hostname, $port_number = null)
{
if ($this->override_hostname)
{
$this->hostname = $hostname;
if ($port_number)
{
$this->port_number = $port_number;
$this->hostname .= ':' . (string) $this->port_number;
}
}
return $this;
}
/**
* Set the resource prefix to use. This method is useful for alternate services that are API-compatible
* with AWS.
*
* @param string $prefix (Required) An alternate prefix to prepend to the resource path. Useful for mock or test applications.
* @return $this A reference to the current instance.
*/
public function set_resource_prefix($prefix)
{
$this->resource_prefix = $prefix;
return $this;
}
/**
* Disables any subsequent use of the <set_hostname()> method.
*
* @param boolean $override (Optional) Whether or not subsequent calls to <set_hostname()> should be obeyed. A `false` value disables the further effectiveness of <set_hostname()>. Defaults to `true`.
* @return $this A reference to the current instance.
*/
public function allow_hostname_override($override = true)
{
$this->override_hostname = $override;
return $this;
}
/**
* Disables SSL/HTTPS connections for hosts that don't support them. Some services, however, still
* require SSL support.
*
* This method will throw a user warning when invoked, which can be hidden by changing your
* <php:error_reporting()> settings.
*
* @return $this A reference to the current instance.
*/
public function disable_ssl()
{
trigger_error('Disabling SSL connections is potentially unsafe and highly discouraged.', E_USER_WARNING);
$this->use_ssl = false;
return $this;
}
/**
* Disables the verification of the SSL Certificate Authority. Doing so can enable an attacker to carry
* out a man-in-the-middle attack.
*
* https://secure.wikimedia.org/wikipedia/en/wiki/Man-in-the-middle_attack
*
* This method will throw a user warning when invoked, which can be hidden by changing your
* <php:error_reporting()> settings.
*
* @return $this A reference to the current instance.
*/
public function disable_ssl_verification($ssl_verification = false)
{
trigger_error('Disabling the verification of SSL certificates can lead to man-in-the-middle attacks. It is potentially unsafe and highly discouraged.', E_USER_WARNING);
$this->ssl_verification = $ssl_verification;
return $this;
}
/**
* Enables HTTP request/response header logging to `STDERR`.
*
* @param boolean $enabled (Optional) Whether or not to enable debug mode. Defaults to `true`.
* @return $this A reference to the current instance.
*/
public function enable_debug_mode($enabled = true)
{
$this->debug_mode = $enabled;
return $this;
}
/**
* Sets the maximum number of times to retry failed requests.
*
* @param integer $retries (Optional) The maximum number of times to retry failed requests. Defaults to `3`.
* @return $this A reference to the current instance.
*/
public function set_max_retries($retries = 3)
{
$this->max_retries = $retries;
return $this;
}
/**
* Set the caching configuration to use for response caching.
*
* @param string $location (Required) <p>The location to store the cache object in. This may vary by cache method.</p><ul><li>File - The local file system paths such as <code>./cache</code> (relative) or <code>/tmp/cache/</code> (absolute). The location must be server-writable.</li><li>APC - Pass in <code>apc</code> to use this lightweight cache. You must have the <a href="http://php.net/apc">APC extension</a> installed.</li><li>XCache - Pass in <code>xcache</code> to use this lightweight cache. You must have the <a href="http://xcache.lighttpd.net">XCache</a> extension installed.</li><li>Memcached - Pass in an indexed array of associative arrays. Each associative array should have a <code>host</code> and a <code>port</code> value representing a <a href="http://php.net/memcached">Memcached</a> server to connect to.</li><li>PDO - A URL-style string (e.g. <code>pdo.mysql://user:pass@localhost/cache</code>) or a standard DSN-style string (e.g. <code>pdo.sqlite:/sqlite/cache.db</code>). MUST be prefixed with <code>pdo.</code>. See <code>CachePDO</code> and <a href="http://php.net/pdo">PDO</a> for more details.</li></ul>
* @param boolean $gzip (Optional) Whether or not data should be gzipped before being stored. A value of `true` will compress the contents before caching them. A value of `false` will leave the contents uncompressed. Defaults to `true`.
* @return $this A reference to the current instance.
*/
public function set_cache_config($location, $gzip = true)
{
// If location is empty, don't do anything.
if (empty($location))
{
return $this;
}
// If we have an array, we're probably passing in Memcached servers and ports.
if (is_array($location))
{
$this->cache_class = 'CacheMC';
}
else
{
// I would expect locations like `/tmp/cache`, `pdo.mysql://user:pass@hostname:port`, `pdo.sqlite:memory:`, and `apc`.
$type = strtolower(substr($location, 0, 3));
switch ($type)
{
case 'apc':
$this->cache_class = 'CacheAPC';
break;
case 'xca': // First three letters of `xcache`
$this->cache_class = 'CacheXCache';
break;
case 'pdo':
$this->cache_class = 'CachePDO';
$location = substr($location, 4);
break;
default:
$this->cache_class = 'CacheFile';
break;
}
}
// Set the remaining cache information.
$this->cache_location = $location;
$this->cache_compress = $gzip;
return $this;
}
/**
* Register a callback function to execute whenever a data stream is read from using
* <CFRequest::streaming_read_callback()>.
*
* The user-defined callback function should accept three arguments:
*
* <ul>
* <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
* <li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>
* <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
* </ul>
*
* @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
* <li>The name of a global function to execute, passed as a string.</li>
* <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
* <li>An anonymous function (PHP 5.3+).</li></ul>
* @return $this A reference to the current instance.
*/
public function register_streaming_read_callback($callback)
{
$this->registered_streaming_read_callback = $callback;
return $this;
}
/**
* Register a callback function to execute whenever a data stream is written to using
* <CFRequest::streaming_write_callback()>.
*
* The user-defined callback function should accept two arguments:
*
* <ul>
* <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
* <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
* </ul>
*
* @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
* <li>The name of a global function to execute, passed as a string.</li>
* <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
* <li>An anonymous function (PHP 5.3+).</li></ul>
* @return $this A reference to the current instance.
*/
public function register_streaming_write_callback($callback)
{
$this->registered_streaming_write_callback = $callback;
return $this;
}
/**
* Fetches and caches STS credentials. This is meant to be used by the constructor, and is not to be
* manually invoked.
*
* @param CacheCore $cache (Required) The a reference to the cache object that is being used to handle the caching.
* @param array $options (Required) The options that were passed into the constructor.
* @return mixed The data to be cached, or NULL.
*/
public function cache_sts_credentials($cache, $options)
{
$token = new AmazonSTS($options);
$response = $token->get_session_token();
if ($response->isOK())
{
// Update the expiration
$expiration_time = strtotime((string) $response->body->GetSessionTokenResult->Credentials->Expiration);
$expiration_duration = round(($expiration_time - time()) * 0.85);
$cache->expire_in($expiration_duration);
// Return the important data
$credentials = $response->body->GetSessionTokenResult->Credentials;
return array(
'key' => (string) $credentials->AccessKeyId,
'secret' => (string) $credentials->SecretAccessKey,
'token' => (string) $credentials->SessionToken,
'expires' => (string) $credentials->Expiration,
);
}
// @codeCoverageIgnoreStart
throw new STS_Exception('Temporary credentials from the AWS Security '
. 'Token Service could not be retrieved using the provided long '
. 'term credentials. It\'s possible that the provided long term '
. 'credentials were invalid.');
// @codeCoverageIgnoreEnd
}
/**
* Fetches and caches EC2 instance profile credentials. This is meant to be used by the constructor, and is not to
* be manually invoked.
*
* @param CacheCore $cache (Required) The a reference to the cache object that is being used to handle the caching.
* @param array $options (Required) The options that were passed into the constructor.
* @return mixed The data to be cached, or NULL.
*/
public function cache_instance_profile_credentials($cache, $options)
{
$instance_profile_url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/';
$connect_timeout = isset($options['instance_profile_timeout']) ? $options['instance_profile_timeout'] : 2;
try
{
// Make a call to the EC2 Metadata Service to find the available instance profile
$request = new RequestCore($instance_profile_url);
$request->set_curlopts(array(CURLOPT_CONNECTTIMEOUT => $connect_timeout));
$response = $request->send_request(true);
if ($response->isOK())
{
// Get the instance profile name
$profile = (string) $response->body;
// Make a call to the EC2 Metadata Service to get the instance profile credentials
$request = new RequestCore($instance_profile_url . $profile);
$request->set_curlopts(array(CURLOPT_CONNECTTIMEOUT => $connect_timeout));
$response = $request->send_request(true);
if ($response->isOK())
{
// Get the credentials
$credentials = json_decode($response->body, true);
if ($credentials['Code'] === 'Success')
{
// Determine the expiration time
$expiration_time = strtotime((string) $credentials['Expiration']);
$expiration_duration = round(($expiration_time - time()) * 0.85);
$cache->expire_in($expiration_duration);
// Return the credential information
return array(
'key' => $credentials['AccessKeyId'],
'secret' => $credentials['SecretAccessKey'],
'token' => $credentials['Token'],
'expires' => $credentials['Expiration'],
);
}
}
}
}
catch (cURL_Exception $e)
{
// The EC2 Metadata Service does not exist or had timed out.
// An exception will be thrown on the next line.
}
// @codeCoverageIgnoreStart
throw new CFCredentials_Exception('No credentials were provided. The SDK attempted to retrieve Instance '
. 'Profile credentials from the EC2 Instance Metadata Service, but failed to do so. Instance profile '
. 'credentials are only accessible on EC2 instances configured with a specific IAM role.');
// @codeCoverageIgnoreEnd
}
/*%******************************************************************************************%*/
// SET CUSTOM CLASSES
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from <CFUtilities>.
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_utilities_class($class = 'CFUtilities')
{
$this->utilities_class = $class;
$this->util = new $this->utilities_class();
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from <CFRequest>.
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @param $this A reference to the current instance.
*/
public function set_request_class($class = 'CFRequest')
{
$this->request_class = $class;
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from <CFResponse>.
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_response_class($class = 'CFResponse')
{
$this->response_class = $class;
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from <CFSimpleXML>.
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_parser_class($class = 'CFSimpleXML')
{
$this->parser_class = $class;
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from <CFBatchRequest>.
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_batch_class($class = 'CFBatchRequest')
{
$this->batch_class = $class;
return $this;
}
/*%******************************************************************************************%*/
// AUTHENTICATION
/**
* Default, shared method for authenticating a connection to AWS.
*
* @param string $operation (Required) Indicates the operation to perform.
* @param array $payload (Required) An associative array of parameters for authenticating. See the individual methods for allowed keys.
* @return CFResponse Object containing a parsed HTTP response.
*/
public function authenticate($operation, $payload)
{
$original_payload = $payload;
$method_arguments = func_get_args();
$curlopts = array();
$return_curl_handle = false;
if (substr($operation, 0, strlen($this->operation_prefix)) !== $this->operation_prefix)
{
$operation = $this->operation_prefix . $operation;
}
// Extract the custom CURLOPT settings from the payload
if (is_array($payload) && isset($payload['curlopts']))
{
$curlopts = $payload['curlopts'];
unset($payload['curlopts']);
}
// Determine whether the response or curl handle should be returned
if (is_array($payload) && isset($payload['returnCurlHandle']))
{
$return_curl_handle = isset($payload['returnCurlHandle']) ? $payload['returnCurlHandle'] : false;
unset($payload['returnCurlHandle']);
}
// Use the caching flow to determine if we need to do a round-trip to the server.
if ($this->use_cache_flow)
{
// Generate an identifier specific to this particular set of arguments.
$cache_id = $this->key . '_' . get_class($this) . '_' . $operation . '_' . sha1(serialize($method_arguments));
// Instantiate the appropriate caching object.
$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
if ($this->delete_cache)
{
$this->use_cache_flow = false;
$this->delete_cache = false;
return $this->cache_object->delete();
}
// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
$data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments);
// Parse the XML body
$data = $this->parse_callback($data);
// End!
return $data;
}
/*%******************************************************************************************%*/
// Signer
$signer = new $this->auth_class($this->hostname, $operation, $payload, $this->credentials);
$signer->key = $this->key;
$signer->secret_key = $this->secret_key;
$signer->auth_token = $this->auth_token;
$signer->api_version = $this->api_version;
$signer->utilities_class = $this->utilities_class;
$signer->request_class = $this->request_class;
$signer->response_class = $this->response_class;
$signer->use_ssl = $this->use_ssl;
$signer->proxy = $this->proxy;
$signer->util = $this->util;
$signer->registered_streaming_read_callback = $this->registered_streaming_read_callback;
$signer->registered_streaming_write_callback = $this->registered_streaming_write_callback;
$request = $signer->authenticate();
// Update RequestCore settings
$request->request_class = $this->request_class;
$request->response_class = $this->response_class;
$request->ssl_verification = $this->ssl_verification;
/*%******************************************************************************************%*/
// Debug mode
if ($this->debug_mode)
{
$request->debug_mode = $this->debug_mode;
}
// Set custom CURLOPT settings
if (count($curlopts))
{
$request->set_curlopts($curlopts);
}
// Manage the (newer) batch request API or the (older) returnCurlHandle setting.
if ($this->use_batch_flow)
{
$handle = $request->prep_request();
$this->batch_object->add($handle);
$this->use_batch_flow = false;
return $handle;
}
elseif ($return_curl_handle)
{
return $request->prep_request();
}
// Send!
$request->send_request();
// Prepare the response.
$headers = $request->get_response_header();
$headers['x-aws-stringtosign'] = $signer->string_to_sign;
if (isset($signer->canonical_request))
{
$headers['x-aws-canonicalrequest'] = $signer->canonical_request;
}
$headers['x-aws-request-headers'] = $request->request_headers;
$headers['x-aws-body'] = $signer->querystring;
$data = new $this->response_class($headers, ($this->parse_the_response === true) ? $this->parse_callback($request->get_response_body()) : $request->get_response_body(), $request->get_response_code());
// Was it Amazon's fault the request failed? Retry the request until we reach $max_retries.
if (
(integer) $request->get_response_code() === 500 || // Internal Error (presumably transient)
(integer) $request->get_response_code() === 503) // Service Unavailable (presumably transient)
{
if ($this->redirects <= $this->max_retries)
{
// Exponential backoff
$delay = (integer) (pow(4, $this->redirects) * 100000);
usleep($delay);
$this->redirects++;
$data = $this->authenticate($operation, $original_payload);
}
}
// DynamoDB has custom logic
elseif (
(integer) $request->get_response_code() === 400 &&
stripos((string) $request->get_response_body(), 'com.amazonaws.dynamodb.') !== false && (
stripos((string) $request->get_response_body(), 'ProvisionedThroughputExceededException') !== false
)
)
{
if ($this->redirects === 0)
{
$this->redirects++;
$data = $this->authenticate($operation, $original_payload);
}
elseif ($this->redirects <= max($this->max_retries, 10))
{
// Exponential backoff
$delay = (integer) (pow(2, ($this->redirects - 1)) * 50000);
usleep($delay);
$this->redirects++;
$data = $this->authenticate($operation, $original_payload);
}
}
$this->redirects = 0;
return $data;
}
/*%******************************************************************************************%*/
// BATCH REQUEST LAYER
/**
* Specifies that the intended request should be queued for a later batch request.
*
* @param CFBatchRequest $queue (Optional) The <CFBatchRequest> instance to use for managing batch requests. If not available, it generates a new instance of <CFBatchRequest>.
* @return $this A reference to the current instance.
*/
public function batch(CFBatchRequest &$queue = null)
{
if ($queue)
{
$this->batch_object = $queue;
}
elseif ($this->internal_batch_object)
{
$this->batch_object = &$this->internal_batch_object;
}
else
{
$this->internal_batch_object = new $this->batch_class();
$this->batch_object = &$this->internal_batch_object;
}
$this->use_batch_flow = true;
return $this;
}
/**
* Executes the batch request queue by sending all queued requests.
*
* @param boolean $clear_after_send (Optional) Whether or not to clear the batch queue after sending a request. Defaults to `true`. Set this to `false` if you are caching batch responses and want to retrieve results later.
* @return array An array of <CFResponse> objects.
*/
public function send($clear_after_send = true)
{
if ($this->use_batch_flow)
{
// When we send the request, disable batch flow.
$this->use_batch_flow = false;
// If we're not caching, simply send the request.
if (!$this->use_cache_flow)
{
$response = $this->batch_object->send();
$parsed_data = array_map(array($this, 'parse_callback'), $response);
$parsed_data = new CFArray($parsed_data);
// Clear the queue
if ($clear_after_send)
{
$this->batch_object->queue = array();
}
return $parsed_data;
}
// Generate an identifier specific to this particular set of arguments.
$cache_id = $this->key . '_' . get_class($this) . '_' . sha1(serialize($this->batch_object));
// Instantiate the appropriate caching object.
$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
if ($this->delete_cache)
{
$this->use_cache_flow = false;
$this->delete_cache = false;
return $this->cache_object->delete();
}
// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
$data_set = $this->cache_object->response_manager(array($this, 'cache_callback_batch'), array($this->batch_object));
$parsed_data = array_map(array($this, 'parse_callback'), $data_set);
$parsed_data = new CFArray($parsed_data);
// Clear the queue
if ($clear_after_send)
{
$this->batch_object->queue = array();
}
// End!
return $parsed_data;
}
// Load the class
$null = new CFBatchRequest();
unset($null);
throw new CFBatchRequest_Exception('You must use $object->batch()->send()');
}
/**
* Parses a response body into a PHP object if appropriate.
*
* @param CFResponse|string $response (Required) The <CFResponse> object to parse, or an XML string that would otherwise be a response body.
* @param string $content_type (Optional) The content-type to use when determining how to parse the content.
* @return CFResponse|string A parsed <CFResponse> object, or parsed XML.
*/
public function parse_callback($response, $headers = null)
{
// Bail out
if (!$this->parse_the_response) return $response;
// Shorten this so we have a (mostly) single code path
if (isset($response->body))
{
if (is_string($response->body))
{
$body = $response->body;
}
else
{
return $response;
}
}
elseif (is_string($response))
{
$body = $response;
}
else
{
return $response;
}
// Decompress gzipped content
if (isset($headers['content-encoding']))
{
switch (strtolower(trim($headers['content-encoding'], "\x09\x0A\x0D\x20")))
{
case 'gzip':
case 'x-gzip':
$decoder = new CFGzipDecode($body);
if ($decoder->parse())
{
$body = $decoder->data;
}
break;
case 'deflate':
if (($uncompressed = gzuncompress($body)) !== false)
{
$body = $uncompressed;
}
elseif (($uncompressed = gzinflate($body)) !== false)
{
$body = $uncompressed;
}
break;
}
}
// Look for XML cues
if (
(isset($headers['content-type']) && ($headers['content-type'] === 'text/xml' || $headers['content-type'] === 'application/xml')) || // We know it's XML
(!isset($headers['content-type']) && (stripos($body, '<?xml') === 0 || strpos($body, '<Error>') === 0) || preg_match('/^<(\w*) xmlns="http(s?):\/\/(\w*).amazon(aws)?.com/im', $body)) // Sniff for XML
)
{
// Strip the default XML namespace to simplify XPath expressions
$body = str_replace("xmlns=", "ns=", $body);
try {
// Parse the XML body
$body = new $this->parser_class($body);
}
catch (Exception $e)
{
throw new Parser_Exception($e->getMessage());
}
}
// Look for JSON cues
elseif (
(isset($headers['content-type']) && ($headers['content-type'] === 'application/json') || $headers['content-type'] === 'application/x-amz-json-1.0') || // We know it's JSON
(!isset($headers['content-type']) && $this->util->is_json($body)) // Sniff for JSON
)
{
// Normalize JSON to a CFSimpleXML object
$body = CFJSON::to_xml($body, $this->parser_class);
}
// Put the parsed data back where it goes
if (isset($response->body))
{
$response->body = $body;
}
else
{
$response = $body;
}
return $response;
}
/*%******************************************************************************************%*/
// CACHING LAYER
/**
* Specifies that the resulting <CFResponse> object should be cached according to the settings from
* <set_cache_config()>.
*
* @param string|integer $expires (Required) The time the cache is to expire. Accepts a number of seconds as an integer, or an amount of time, as a string, that is understood by <php:strtotime()> (e.g. "1 hour").
* @param $this A reference to the current instance.
* @return $this
*/
public function cache($expires)
{
// Die if they haven't used set_cache_config().
if (!$this->cache_class)
{
throw new CFRuntime_Exception('Must call set_cache_config() before using cache()');
}
if (is_string($expires))
{
$expires = strtotime($expires);
$this->cache_expires = $expires - time();
}
elseif (is_int($expires))
{
$this->cache_expires = $expires;
}
$this->use_cache_flow = true;
return $this;
}
/**
* The callback function that is executed when the cache doesn't exist or has expired. The response of
* this method is cached. Accepts identical parameters as the <authenticate()> method. Never call this
* method directly -- it is used internally by the caching system.
*
* @param string $operation (Required) Indicates the operation to perform.
* @param array $payload (Required) An associative array of parameters for authenticating. See the individual methods for allowed keys.
* @return CFResponse A parsed HTTP response.
*/
public function cache_callback($operation, $payload)
{
// Disable the cache flow since it's already been handled.
$this->use_cache_flow = false;
// Make the request
$response = $this->authenticate($operation, $payload);
// If this is an XML document, convert it back to a string.
if (isset($response->body) && ($response->body instanceof SimpleXMLElement))
{
$response->body = $response->body->asXML();
}
return $response;
}
/**
* Used for caching the results of a batch request. Never call this method directly; it is used
* internally by the caching system.
*
* @param CFBatchRequest $batch (Required) The batch request object to send.
* @return CFResponse A parsed HTTP response.
*/
public function cache_callback_batch(CFBatchRequest $batch)
{
return $batch->send();
}
/**
* Deletes a cached <CFResponse> object using the specified cache storage type.
*
* @return boolean A value of `true` if cached object exists and is successfully deleted, otherwise `false`.
*/
public function delete_cache()
{
$this->use_cache_flow = true;
$this->delete_cache = true;
return $this;
}
}
/**
* Contains the functionality for auto-loading service classes.
*/
class CFLoader
{
/*%******************************************************************************************%*/
// AUTO-LOADER
/**
* Automatically load classes that aren't included.
*
* @param string $class (Required) The classname to load.
* @return boolean Whether or not the file was successfully loaded.
*/
public static function autoloader($class)
{
$path = dirname(__FILE__) . DIRECTORY_SEPARATOR;
// Amazon SDK classes
if (strstr($class, 'Amazon'))
{
if (file_exists($require_this = $path . 'services' . DIRECTORY_SEPARATOR . str_ireplace('Amazon', '', strtolower($class)) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Utility classes
elseif (strstr($class, 'CF'))
{
if (file_exists($require_this = $path . 'utilities' . DIRECTORY_SEPARATOR . str_ireplace('CF', '', strtolower($class)) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load CacheCore
elseif (strstr($class, 'Cache'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'cachecore' . DIRECTORY_SEPARATOR . strtolower($class) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load RequestCore
elseif (strstr($class, 'RequestCore') || strstr($class, 'ResponseCore'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'requestcore' . DIRECTORY_SEPARATOR . 'requestcore.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Transmogrifier
elseif (strstr($class, 'Transmogrifier'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'dom' . DIRECTORY_SEPARATOR . 'Transmogrifier.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Authentication Signers
elseif (strstr($class, 'Auth'))
{
if (file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . str_replace('auth', 'signature_', strtolower($class)) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Signer interface
elseif ($class === 'Signer')
{
if (!interface_exists('Signable', false) &&
file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . 'signable.interface.php'))
{
require_once $require_this;
}
if (file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . 'signer.abstract.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Symfony YAML classes
elseif (strstr($class, 'sfYaml'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'yaml' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'sfYaml.php'))
{
require_once $require_this;
return true;
}
return false;
}
return false;
}
}
// Register the autoloader.
spl_autoload_register(array('CFLoader', 'autoloader'));
/*%******************************************************************************************%*/
// CONFIGURATION
// Look for include file in the same directory (e.g. `./config.inc.php`).
if (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php'))
{
include_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php';
}
// Fallback to `~/.aws/sdk/config.inc.php`
else
{
if (!isset($_ENV['HOME']) && isset($_SERVER['HOME']))
{
$_ENV['HOME'] = $_SERVER['HOME'];
}
elseif (!isset($_ENV['HOME']) && !isset($_SERVER['HOME']))
{
$os = strtolower(PHP_OS);
if (in_array($os, array('windows', 'winnt', 'win32')))
{
$_ENV['HOME'] = false;
}
else
{
$dir = exec('(cd ~ && pwd) 2>&1', $out, $exit);
if ($exit === 0)
{
$_ENV['HOME'] = trim($dir);
}
else
{
error_log('Failed to determine HOME directory after trying "' . $dir . '" (exit code ' . $exit . ')');
$_ENV['HOME'] = false;
}
}
if (!$_ENV['HOME'])
{
switch ($os)
{
case 'darwin':
$_ENV['HOME'] = '/Users/' . get_current_user();
break;
case 'windows':
case 'winnt':
case 'win32':
$_ENV['HOME'] = 'c:' . DIRECTORY_SEPARATOR . 'Documents and Settings' . DIRECTORY_SEPARATOR . get_current_user();
break;
default:
$_ENV['HOME'] = '/home/' . get_current_user();
break;
}
}
}
$path = DIRECTORY_SEPARATOR . '.aws' . DIRECTORY_SEPARATOR . 'sdk' . DIRECTORY_SEPARATOR . 'config.inc.php';
if (isset($_ENV['HOME']) && file_exists($_ENV['HOME'] . $path))
{
include_once $_ENV['HOME'] . $path;
}
unset($os, $dir, $out, $exit, $path);
}
/*
* Copyright 2010-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*%******************************************************************************************%*/
// EXCEPTIONS
/**
* Default S3 Exception.
*/
class S3_Exception extends Exception {}
/*%******************************************************************************************%*/
// MAIN CLASS
/**
* Amazon S3 is a web service that enables you to store data in the cloud. You can then download the data
* or use the data with other AWS services, such as Amazon Elastic Cloud Computer (EC2).
*
* Amazon Simple Storage Service (Amazon S3) is storage for the Internet. You can use Amazon S3 to store
* and retrieve any amount of data at any time, from anywhere on the web. You can accomplish these tasks
* using the AWS Management Console, which is a simple and intuitive web interface.
*
* To get the most out of Amazon S3, you need to understand a few simple concepts. Amazon S3 stores data
* as objects in buckets. An object is comprised of a file and optionally any metadata that describes
* that file.
*
* To store an object in Amazon S3, you upload the file you want to store to a bucket. When you upload a
* file, you can set permissions on the object as well as any metadata.
*
* Buckets are the containers for objects. You can have one or more buckets. For each bucket, you can control
* access to the bucket (who can create, delete, and list objects in the bucket), view access logs for the
* bucket and its objects, and choose the geographical region where Amazon S3 will store the bucket and its
* contents.
*
* Visit <http://aws.amazon.com/s3/> for more information.
*
* @version 2012.08.28
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/s3/ Amazon Simple Storage Service
* @link http://aws.amazon.com/documentation/s3/ Amazon Simple Storage Service documentation
*/
class AmazonS3 extends CFRuntime
{
/*%******************************************************************************************%*/
// REGIONAL ENDPOINTS
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.
*/
const REGION_US_E1 = 's3.amazonaws.com';
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.
*/
const REGION_VIRGINIA = self::REGION_US_E1;
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.
*/
const REGION_US_STANDARD = self::REGION_US_E1;
/**
* Specify the queue URL for the US-West 1 (Northern California) Region.
*/
const REGION_US_W1 = 's3-us-west-1.amazonaws.com';
/**
* Specify the queue URL for the US-West 1 (Northern California) Region.
*/
const REGION_CALIFORNIA = self::REGION_US_W1;
/**
* Specify the queue URL for the US-West 2 (Oregon) Region.
*/
const REGION_US_W2 = 's3-us-west-2.amazonaws.com';
/**
* Specify the queue URL for the US-West 2 (Oregon) Region.
*/
const REGION_OREGON = self::REGION_US_W2;
/**
* Specify the queue URL for the EU (Ireland) Region.
*/
const REGION_EU_W1 = 's3-eu-west-1.amazonaws.com';
/**
* Specify the queue URL for the EU (Ireland) Region.
*/
const REGION_IRELAND = self::REGION_EU_W1;
/**
* Specify the queue URL for the Asia Pacific (Singapore) Region.
*/
const REGION_APAC_SE1 = 's3-ap-southeast-1.amazonaws.com';
/**
* Specify the queue URL for the Asia Pacific (Singapore) Region.
*/
const REGION_SINGAPORE = self::REGION_APAC_SE1;
/**
* Specify the queue URL for the Asia Pacific (Japan) Region.
*/
const REGION_APAC_NE1 = 's3-ap-northeast-1.amazonaws.com';
/**
* Specify the queue URL for the Asia Pacific (Japan) Region.
*/
const REGION_TOKYO = self::REGION_APAC_NE1;
/**
* Specify the queue URL for the South America (Sao Paulo) Region.
*/
const REGION_SA_E1 = 's3-sa-east-1.amazonaws.com';
/**
* Specify the queue URL for the South America (Sao Paulo) Region.
*/
const REGION_SAO_PAULO = self::REGION_SA_E1;
/**
* Specify the queue URL for the United States GovCloud Region.
*/
const REGION_US_GOV1 = 's3-us-gov-west-1.amazonaws.com';
/**
* Specify the queue URL for the United States GovCloud FIPS 140-2 Region.
*/
const REGION_US_GOV1_FIPS = 's3-fips-us-gov-west-1.amazonaws.com';
/**
* The default endpoint.
*/
const DEFAULT_URL = self::REGION_US_E1;
/*%******************************************************************************************%*/
// REGIONAL WEBSITE ENDPOINTS
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.
*/
const REGION_US_E1_WEBSITE = 's3-website-us-east-1.amazonaws.com';
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.
*/
const REGION_VIRGINIA_WEBSITE = self::REGION_US_E1_WEBSITE;
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.
*/
const REGION_US_STANDARD_WEBSITE = self::REGION_US_E1_WEBSITE;
/**
* Specify the queue URL for the US-West 1 (Northern California) Website Region.
*/
const REGION_US_W1_WEBSITE = 's3-website-us-west-1.amazonaws.com';
/**
* Specify the queue URL for the US-West 1 (Northern California) Website Region.
*/
const REGION_CALIFORNIA_WEBSITE = self::REGION_US_W1_WEBSITE;
/**
* Specify the queue URL for the US-West 2 (Oregon) Website Region.
*/
const REGION_US_W2_WEBSITE = 's3-website-us-west-2.amazonaws.com';
/**
* Specify the queue URL for the US-West 2 (Oregon) Website Region.
*/
const REGION_OREGON_WEBSITE = self::REGION_US_W2_WEBSITE;
/**
* Specify the queue URL for the EU (Ireland) Website Region.
*/
const REGION_EU_W1_WEBSITE = 's3-website-eu-west-1.amazonaws.com';
/**
* Specify the queue URL for the EU (Ireland) Website Region.
*/
const REGION_IRELAND_WEBSITE = self::REGION_EU_W1_WEBSITE;
/**
* Specify the queue URL for the Asia Pacific (Singapore) Website Region.
*/
const REGION_APAC_SE1_WEBSITE = 's3-website-ap-southeast-1.amazonaws.com';
/**
* Specify the queue URL for the Asia Pacific (Singapore) Website Region.
*/
const REGION_SINGAPORE_WEBSITE = self::REGION_APAC_SE1_WEBSITE;
/**
* Specify the queue URL for the Asia Pacific (Japan) Website Region.
*/
const REGION_APAC_NE1_WEBSITE = 's3-website-ap-northeast-1.amazonaws.com';
/**
* Specify the queue URL for the Asia Pacific (Japan) Website Region.
*/
const REGION_TOKYO_WEBSITE = self::REGION_APAC_NE1_WEBSITE;
/**
* Specify the queue URL for the South America (Sao Paulo) Website Region.
*/
const REGION_SA_E1_WEBSITE = 's3-website-sa-east-1.amazonaws.com';
/**
* Specify the queue URL for the South America (Sao Paulo) Website Region.
*/
const REGION_SAO_PAULO_WEBSITE = self::REGION_SA_E1_WEBSITE;
/**
* Specify the queue URL for the United States GovCloud Website Region.
*/
const REGION_US_GOV1_WEBSITE = 's3-website-us-gov-west-1.amazonaws.com';
/*%******************************************************************************************%*/
// ACL
/**
* ACL: Owner-only read/write.
*/
const ACL_PRIVATE = 'private';
/**
* ACL: Owner read/write, public read.
*/
const ACL_PUBLIC = 'public-read';
/**
* ACL: Public read/write.
*/
const ACL_OPEN = 'public-read-write';
/**
* ACL: Owner read/write, authenticated read.
*/
const ACL_AUTH_READ = 'authenticated-read';
/**
* ACL: Bucket owner read.
*/
const ACL_OWNER_READ = 'bucket-owner-read';
/**
* ACL: Bucket owner full control.
*/
const ACL_OWNER_FULL_CONTROL = 'bucket-owner-full-control';
/*%******************************************************************************************%*/
// GRANTS
/**
* When applied to a bucket, grants permission to list the bucket. When applied to an object, this
* grants permission to read the object data and/or metadata.
*/
const GRANT_READ = 'READ';
/**
* When applied to a bucket, grants permission to create, overwrite, and delete any object in the
* bucket. This permission is not supported for objects.
*/
const GRANT_WRITE = 'WRITE';
/**
* Grants permission to read the ACL for the applicable bucket or object. The owner of a bucket or
* object always has this permission implicitly.
*/
const GRANT_READ_ACP = 'READ_ACP';
/**
* Gives permission to overwrite the ACP for the applicable bucket or object. The owner of a bucket
* or object always has this permission implicitly. Granting this permission is equivalent to granting
* FULL_CONTROL because the grant recipient can make any changes to the ACP.
*/
const GRANT_WRITE_ACP = 'WRITE_ACP';
/**
* Provides READ, WRITE, READ_ACP, and WRITE_ACP permissions. It does not convey additional rights and
* is provided only for convenience.
*/
const GRANT_FULL_CONTROL = 'FULL_CONTROL';
/*%******************************************************************************************%*/
// USERS
/**
* The "AuthenticatedUsers" group for access control policies.
*/
const USERS_AUTH = 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers';
/**
* The "AllUsers" group for access control policies.
*/
const USERS_ALL = 'http://acs.amazonaws.com/groups/global/AllUsers';
/**
* The "LogDelivery" group for access control policies.
*/
const USERS_LOGGING = 'http://acs.amazonaws.com/groups/s3/LogDelivery';
/*%******************************************************************************************%*/
// PATTERNS
/**
* PCRE: Match all items
*/
const PCRE_ALL = '/.*/i';
/*%******************************************************************************************%*/
// STORAGE
/**
* Standard storage redundancy.
*/
const STORAGE_STANDARD = 'STANDARD';
/**
* Reduced storage redundancy.
*/
const STORAGE_REDUCED = 'REDUCED_REDUNDANCY';
/*%******************************************************************************************%*/
// PROPERTIES
/**
* The request URL.
*/
public $request_url;
/**
* The virtual host setting.
*/
public $vhost;
/**
* The base XML elements to use for access control policy methods.
*/
public $base_acp_xml;
/**
* The base XML elements to use for creating buckets in regions.
*/
public $base_location_constraint;
/**
* The base XML elements to use for logging methods.
*/
public $base_logging_xml;
/**
* The base XML elements to use for notifications.
*/
public $base_notification_xml;
/**
* The base XML elements to use for versioning.
*/
public $base_versioning_xml;
/**
* The base XML elements to use for completing a multipart upload.
*/
public $complete_mpu_xml;
/**
* The base XML elements to use for website support.
*/
public $website_config_xml;
/**
* The base XML elements to use for multi-object delete support.
*/
public $multi_object_delete_xml;
/**
* The base XML elements to use for object expiration support.
*/
public $object_expiration_xml;
/**
* The base XML elements to use for bucket tagging.
*/
public $bucket_tagging_xml;
/**
* The base XML elements to use for CORS support.
*/
public $cors_config_xml;
/**
* The DNS vs. Path-style setting.
*/
public $path_style = false;
/**
* The state of whether the prefix change is temporary or permanent.
*/
public $temporary_prefix = false;
/*%******************************************************************************************%*/
// CONSTRUCTOR
/**
* Constructs a new instance of <AmazonS3>.
*
* @param array $options (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>certificate_authority</code> - <code>boolean</code> - Optional - Determines which Cerificate Authority file to use. A value of boolean <code>false</code> will use the Certificate Authority file available on the system. A value of boolean <code>true</code> will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to <code>0755</code>) will use that. Leave this set to <code>false</code> if you're not sure.</li>
* <li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>
* <li><code>default_cache_config</code> - <code>string</code> - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the <set_cache_config()> method. Valid values are: <code>apc</code>, <code>xcache</code>, or a file system path such as <code>./cache</code> or <code>/tmp/cache/</code>.</li>
* <li><code>key</code> - <code>string</code> - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.</li>
* <li><code>secret</code> - <code>string</code> - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.</li>
* <li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li></ul>
* @return void
*/
public function __construct(array $options = array())
{
$this->vhost = null;
$this->api_version = '2006-03-01';
$this->hostname = self::DEFAULT_URL;
$this->base_acp_xml = '<?xml version="1.0" encoding="UTF-8"?><AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/latest/"/>';
$this->base_location_constraint = '<?xml version="1.0" encoding="UTF-8"?><CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '/"><LocationConstraint/></CreateBucketConfiguration>';
$this->base_logging_xml = '<?xml version="1.0" encoding="utf-8"?><BucketLoggingStatus xmlns="http://doc.s3.amazonaws.com/' . $this->api_version . '"/>';
$this->base_notification_xml = '<?xml version="1.0" encoding="utf-8"?><NotificationConfiguration/>';
$this->base_versioning_xml = '<?xml version="1.0" encoding="utf-8"?><VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '/"/>';
$this->complete_mpu_xml = '<?xml version="1.0" encoding="utf-8"?><CompleteMultipartUpload/>';
$this->website_config_xml = '<?xml version="1.0" encoding="utf-8"?><WebsiteConfiguration xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '/"><IndexDocument><Suffix>index.html</Suffix></IndexDocument><ErrorDocument><Key>error.html</Key></ErrorDocument></WebsiteConfiguration>';
$this->multi_object_delete_xml = '<?xml version="1.0" encoding="utf-8"?><Delete/>';
$this->object_expiration_xml = '<?xml version="1.0" encoding="utf-8"?><LifecycleConfiguration/>';
$this->bucket_tagging_xml = '<?xml version="1.0" encoding="utf-8"?><Tagging><TagSet/></Tagging>';
$this->cors_config_xml = '<?xml version="1.0" encoding="utf-8"?><CORSConfiguration />';
parent::__construct($options);
}
/*%******************************************************************************************%*/
// AUTHENTICATION
/**
* Authenticates a connection to Amazon S3. Do not use directly unless implementing custom methods for
* this class.
*
* @param string $operation (Required) The name of the bucket to operate on (S3 Only).
* @param array $payload (Required) An associative array of parameters for authenticating. See inline comments for allowed keys.
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_Authentication.html REST authentication
*/
public function authenticate($operation, $payload)
{
/*
* Overriding or extending this class? You can pass the following "magic" keys into $opt.
*
* ## verb, resource, sub_resource and query_string ##
* <verb> /<resource>?<sub_resource>&<query_string>
* GET /filename.txt?versions&prefix=abc&max-items=1
*
* ## versionId, uploadId, partNumber, response-* ##
* These don't follow the same rules as above, in that the they needs to be signed, while
* other query_string values do not.
*
* ## curlopts ##
* These values get passed directly to the cURL methods in RequestCore.
*
* ## fileUpload, fileDownload, seekTo ##
* These are slightly modified and then passed to the cURL methods in RequestCore.
*
* ## headers ##
* $opt['headers'] is an array, whose keys are HTTP headers to be sent.
*
* ## body ##
* This is the request body that is sent to the server via PUT/POST.
*
* ## preauth ##
* This is a hook that tells authenticate() to generate a pre-authenticated URL.
*
* ## returnCurlHandle ##
* Tells authenticate() to return the cURL handle for the request instead of executing it.
*/
// Rename variables (to overcome inheritence issues)
$bucket = $operation;
$opt = $payload;
// Validate the S3 bucket name
if (!$this->validate_bucketname_support($bucket))
{
// @codeCoverageIgnoreStart
throw new S3_Exception('S3 does not support "' . $bucket . '" as a valid bucket name. Review "Bucket Restrictions and Limitations" in the S3 Developer Guide for more information.');
// @codeCoverageIgnoreEnd
}
// Die if $opt isn't set.
if (!$opt) return false;
$method_arguments = func_get_args();
// Use the caching flow to determine if we need to do a round-trip to the server.
if ($this->use_cache_flow)
{
// Generate an identifier specific to this particular set of arguments.
$cache_id = $this->key . '_' . get_class($this) . '_' . $bucket . '_' . sha1(serialize($method_arguments));
// Instantiate the appropriate caching object.
$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
if ($this->delete_cache)
{
$this->use_cache_flow = false;
$this->delete_cache = false;
return $this->cache_object->delete();
}
// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
$data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments);
if ($this->parse_the_response)
{
// Parse the XML body
$data = $this->parse_callback($data);
}
// End!
return $data;
}
// If we haven't already set a resource prefix and the bucket name isn't DNS-valid...
if ((!$this->resource_prefix && !$this->validate_bucketname_create($bucket)) || $this->path_style)
{
// Fall back to the older path-style URI
$this->set_resource_prefix('/' . $bucket);
$this->temporary_prefix = true;
}
// If the bucket name has periods and we are using SSL, we need to switch to path style URLs
$bucket_name_may_cause_ssl_wildcard_failures = false;
if ($this->use_ssl && strpos($bucket, '.') !== false)
{
$bucket_name_may_cause_ssl_wildcard_failures = true;
}
// Determine hostname
$scheme = $this->use_ssl ? 'https://' : 'http://';
if ($bucket_name_may_cause_ssl_wildcard_failures || $this->resource_prefix || $this->path_style)
{
// Use bucket-in-path method
$hostname = $this->hostname . $this->resource_prefix . (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket));
}
else
{
$hostname = $this->vhost ? $this->vhost : (($bucket === '') ? $this->hostname : ($bucket . '.') . $this->hostname);
}
// Get the UTC timestamp in RFC 2616 format
$date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, time());
// Storage for request parameters.
$resource = '';
$sub_resource = '';
$querystringparams = array();
$signable_querystringparams = array();
$string_to_sign = '';
$headers = array(
'Content-MD5' => '',
'Content-Type' => 'application/x-www-form-urlencoded',
'Date' => $date
);
/*%******************************************************************************************%*/
// Do we have an authentication token?
if ($this->auth_token)
{
$headers['X-Amz-Security-Token'] = $this->auth_token;
}
// Handle specific resources
if (isset($opt['resource']))
{
$resource .= $opt['resource'];
}
// Merge query string values
if (isset($opt['query_string']))
{
$querystringparams = array_merge($querystringparams, $opt['query_string']);
}
$query_string = $this->util->to_query_string($querystringparams);
// Merge the signable query string values. Must be alphabetical.
$signable_list = array(
'partNumber',
'response-cache-control',
'response-content-disposition',
'response-content-encoding',
'response-content-language',
'response-content-type',
'response-expires',
'uploadId',
'versionId'
);
foreach ($signable_list as $item)
{
if (isset($opt[$item]))
{
$signable_querystringparams[$item] = $opt[$item];
}
}
$signable_query_string = $this->util->to_query_string($signable_querystringparams);
// Merge the HTTP headers
if (isset($opt['headers']))
{
$headers = array_merge($headers, $opt['headers']);
}
// Compile the URI to request
$conjunction = '?';
$signable_resource = '/' . str_replace('%2F', '/', rawurlencode($resource));
$non_signable_resource = '';
if (isset($opt['sub_resource']))
{
$signable_resource .= $conjunction . rawurlencode($opt['sub_resource']);
$conjunction = '&';
}
if ($signable_query_string !== '')
{
$signable_query_string = $conjunction . $signable_query_string;
$conjunction = '&';
}
if ($query_string !== '')
{
$non_signable_resource .= $conjunction . $query_string;
$conjunction = '&';
}
if (substr($hostname, -1) === substr($signable_resource, 0, 1))
{
$signable_resource = ltrim($signable_resource, '/');
}
$this->request_url = $scheme . $hostname . $signable_resource . $signable_query_string . $non_signable_resource;
if (isset($opt['location']))
{
$this->request_url = $opt['location'];
}
// Gather information to pass along to other classes.
$helpers = array(
'utilities' => $this->utilities_class,
'request' => $this->request_class,
'response' => $this->response_class,
);
// Instantiate the request class
$request = new $this->request_class($this->request_url, $this->proxy, $helpers, $this->credentials);
// Update RequestCore settings
$request->request_class = $this->request_class;
$request->response_class = $this->response_class;
$request->ssl_verification = $this->ssl_verification;
// Pass along registered stream callbacks
if ($this->registered_streaming_read_callback)
{
$request->register_streaming_read_callback($this->registered_streaming_read_callback);
}
if ($this->registered_streaming_write_callback)
{
$request->register_streaming_write_callback($this->registered_streaming_write_callback);
}
// Streaming uploads
if (isset($opt['fileUpload']))
{
if (is_resource($opt['fileUpload']))
{
// Determine the length to read from the stream
$length = null; // From current position until EOF by default, size determined by set_read_stream()
if (isset($headers['Content-Length']))
{
$length = $headers['Content-Length'];
}
elseif (isset($opt['seekTo']))
{
// Read from seekTo until EOF by default
$stats = fstat($opt['fileUpload']);
if ($stats && $stats['size'] >= 0)
{
$length = $stats['size'] - (integer) $opt['seekTo'];
}
}
$request->set_read_stream($opt['fileUpload'], $length);
if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
{
$headers['Content-Type'] = 'application/octet-stream';
}
}
else
{
$request->set_read_file($opt['fileUpload']);
// Determine the length to read from the file
$length = $request->read_stream_size; // The file size by default
if (isset($headers['Content-Length']))
{
$length = $headers['Content-Length'];
}
elseif (isset($opt['seekTo']) && isset($length))
{
// Read from seekTo until EOF by default
$length -= (integer) $opt['seekTo'];
}
$request->set_read_stream_size($length);
// Attempt to guess the correct mime-type
if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
{
$extension = explode('.', $opt['fileUpload']);
$extension = array_pop($extension);
$mime_type = CFMimeTypes::get_mimetype($extension);
$headers['Content-Type'] = $mime_type;
}
}
$headers['Content-Length'] = $request->read_stream_size;
$headers['Content-MD5'] = '';
}
// Handle streaming file offsets
if (isset($opt['seekTo']))
{
// Pass the seek position to RequestCore
$request->set_seek_position((integer) $opt['seekTo']);
}
// Streaming downloads
if (isset($opt['fileDownload']))
{
if (is_resource($opt['fileDownload']))
{
$request->set_write_stream($opt['fileDownload']);
}
else
{
$request->set_write_file($opt['fileDownload']);
}
}
$curlopts = array();
// Set custom CURLOPT settings
if (isset($opt['curlopts']))
{
$curlopts = $opt['curlopts'];
}
// Debug mode
if ($this->debug_mode)
{
$curlopts[CURLOPT_VERBOSE] = true;
}
// Set the curl options.
if (count($curlopts))
{
$request->set_curlopts($curlopts);
}
// Do we have a verb?
if (isset($opt['verb']))
{
$request->set_method($opt['verb']);
$string_to_sign .= $opt['verb'] . "\n";
}
// Add headers and content when we have a body
if (isset($opt['body']))
{
$request->set_body($opt['body']);
$headers['Content-Length'] = strlen($opt['body']);
if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
{
$headers['Content-Type'] = 'application/octet-stream';
}
if (!isset($opt['NoContentMD5']) || $opt['NoContentMD5'] !== true)
{
$headers['Content-MD5'] = $this->util->hex_to_base64(md5($opt['body']));
}
}
// Handle query-string authentication
if (isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
{
unset($headers['Date']);
$headers['Content-Type'] = '';
$headers['Expires'] = is_int($opt['preauth']) ? $opt['preauth'] : strtotime($opt['preauth']);
}
// Sort headers
uksort($headers, 'strnatcasecmp');
// Add headers to request and compute the string to sign
foreach ($headers as $header_key => $header_value)
{
// Strip linebreaks from header values as they're illegal and can allow for security issues
$header_value = str_replace(array("\r", "\n"), '', $header_value);
// Add the header if it has a value
if ($header_value !== '')
{
$request->add_header($header_key, $header_value);
}
// Generate the string to sign
if (
strtolower($header_key) === 'content-md5' ||
strtolower($header_key) === 'content-type' ||
strtolower($header_key) === 'date' ||
(strtolower($header_key) === 'expires' && isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
)
{
$string_to_sign .= $header_value . "\n";
}
elseif (substr(strtolower($header_key), 0, 6) === 'x-amz-')
{
$string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n";
}
}
// Add the signable resource location
$string_to_sign .= ($this->resource_prefix ? $this->resource_prefix : '');
$string_to_sign .= (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket)) . $signable_resource . urldecode($signable_query_string);
// Hash the AWS secret key and generate a signature for the request.
$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->secret_key, true));
$request->add_header('Authorization', 'AWS ' . $this->key . ':' . $signature);
// If we're generating a URL, return the URL to the calling method.
if (isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
{
$query_params = array(
'AWSAccessKeyId' => $this->key,
'Expires' => $headers['Expires'],
'Signature' => $signature,
);
// If using short-term credentials, add the token to the query string
if ($this->auth_token)
{
$query_params['x-amz-security-token'] = $this->auth_token;
}
return $this->request_url . $conjunction . http_build_query($query_params, '', '&');
}
elseif (isset($opt['preauth']))
{
return $this->request_url;
}
/*%******************************************************************************************%*/
// If our changes were temporary, reset them.
if ($this->temporary_prefix)
{
$this->temporary_prefix = false;
$this->resource_prefix = null;
}
// Manage the (newer) batch request API or the (older) returnCurlHandle setting.
if ($this->use_batch_flow)
{
$handle = $request->prep_request();
$this->batch_object->add($handle);
$this->use_batch_flow = false;
return $handle;
}
elseif (isset($opt['returnCurlHandle']) && $opt['returnCurlHandle'] === true)
{
return $request->prep_request();
}
// Send!
$request->send_request();
// Prepare the response
$headers = $request->get_response_header();
$headers['x-aws-request-url'] = $this->request_url;
$headers['x-aws-redirects'] = $this->redirects;
$headers['x-aws-stringtosign'] = $string_to_sign;
$headers['x-aws-requestheaders'] = $request->request_headers;
// Did we have a request body?
if (isset($opt['body']))
{
$headers['x-aws-requestbody'] = $opt['body'];
}
$data = new $this->response_class($headers, $this->parse_callback($request->get_response_body()), $request->get_response_code());
// Did Amazon tell us to redirect? Typically happens for multiple rapid requests EU datacenters.
// @see: http://docs.amazonwebservices.com/AmazonS3/latest/dev/Redirects.html
// @codeCoverageIgnoreStart
if ((integer) $request->get_response_code() === 307) // Temporary redirect to new endpoint.
{
$this->redirects++;
$opt['location'] = $headers['location'];
$data = $this->authenticate($bucket, $opt);
}
// Was it Amazon's fault the request failed? Retry the request until we reach $max_retries.
elseif ((integer) $request->get_response_code() === 500 || (integer) $request->get_response_code() === 503)
{
if ($this->redirects <= $this->max_retries)
{
// Exponential backoff
$delay = (integer) (pow(4, $this->redirects) * 100000);
usleep($delay);
$this->redirects++;
$data = $this->authenticate($bucket, $opt);
}
}
// @codeCoverageIgnoreEnd
// Return!
$this->redirects = 0;
return $data;
}
/**
* Validates whether or not the specified Amazon S3 bucket name is valid for DNS-style access. This
* method is leveraged by any method that creates buckets.
*
* @param string $bucket (Required) The name of the bucket to validate.
* @return boolean Whether or not the specified Amazon S3 bucket name is valid for DNS-style access. A value of <code>true</code> means that the bucket name is valid. A value of <code>false</code> means that the bucket name is invalid.
*/
public function validate_bucketname_create($bucket)
{
// list_buckets() uses this. Let it pass.
if ($bucket === '') return true;
if (
($bucket === null || $bucket === false) || // Must not be null or false
preg_match('/[^(a-z0-9\-\.)]/', $bucket) || // Must be in the lowercase Roman alphabet, period or hyphen
!preg_match('/^([a-z]|\d)/', $bucket) || // Must start with a number or letter
!(strlen($bucket) >= 3 && strlen($bucket) <= 63) || // Must be between 3 and 63 characters long
(strpos($bucket, '..') !== false) || // Bucket names cannot contain two, adjacent periods
(strpos($bucket, '-.') !== false) || // Bucket names cannot contain dashes next to periods
(strpos($bucket, '.-') !== false) || // Bucket names cannot contain dashes next to periods
preg_match('/(-|\.)$/', $bucket) || // Bucket names should not end with a dash or period
preg_match('/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/', $bucket) // Must not be formatted as an IP address
) return false;
return true;
}
/**
* Validates whether or not the specified Amazon S3 bucket name is valid for path-style access. This
* method is leveraged by any method that reads from buckets.
*
* @param string $bucket (Required) The name of the bucket to validate.
* @return boolean Whether or not the bucket name is valid. A value of <code>true</code> means that the bucket name is valid. A value of <code>false</code> means that the bucket name is invalid.
*/
public function validate_bucketname_support($bucket)
{
// list_buckets() uses this. Let it pass.
if ($bucket === '') return true;
// Validate
if (
($bucket === null || $bucket === false) || // Must not be null or false
preg_match('/[^(a-z0-9_\-\.)]/i', $bucket) || // Must be in the Roman alphabet, period, hyphen or underscore
!preg_match('/^([a-z]|\d)/i', $bucket) || // Must start with a number or letter
!(strlen($bucket) >= 3 && strlen($bucket) <= 255) || // Must be between 3 and 255 characters long
preg_match('/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/', $bucket) // Must not be formatted as an IP address
) return false;
return true;
}
/*%******************************************************************************************%*/
// SETTERS
/**
* Sets the region to use for subsequent Amazon S3 operations. This will also reset any prior use of
* <enable_path_style()>.
*
* @param string $region (Required) The region to use for subsequent Amazon S3 operations. For a complete list of REGION constants, see the <code>AmazonS3</code> Constants page in the API reference.
* @return $this A reference to the current instance.
*/
public function set_region($region)
{
// @codeCoverageIgnoreStart
$this->set_hostname($region);
switch ($region)
{
case self::REGION_US_E1: // Northern Virginia
$this->enable_path_style(false);
break;
case self::REGION_EU_W1: // Ireland
$this->enable_path_style(); // Always use path-style access for EU endpoint.
break;
default:
$this->enable_path_style(false);
break;
}
// @codeCoverageIgnoreEnd
return $this;
}
/**
* Sets the virtual host to use in place of the default `bucket.s3.amazonaws.com` domain.
*
* @param string $vhost (Required) The virtual host to use in place of the default `bucket.s3.amazonaws.com` domain.
* @return $this A reference to the current instance.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/VirtualHosting.html Virtual Hosting of Buckets
*/
public function set_vhost($vhost)
{
$this->vhost = $vhost;
return $this;
}
/**
* Enables the use of the older path-style URI access for all requests.
*
* @param string $style (Optional) Whether or not to enable path-style URI access for all requests. The default value is <code>true</code>.
* @return $this A reference to the current instance.
*/
public function enable_path_style($style = true)
{
$this->path_style = $style;
return $this;
}
/*%******************************************************************************************%*/
// BUCKET METHODS
/**
* Creates an Amazon S3 bucket.
*
* Every object stored in Amazon S3 is contained in a bucket. Buckets partition the namespace of
* objects stored in Amazon S3 at the top level. in a bucket, any name can be used for objects.
* However, bucket names must be unique across all of Amazon S3.
*
* @param string $bucket (Required) The name of the bucket to create.
* @param string $region (Required) The preferred geographical location for the bucket. [Allowed values: `AmazonS3::REGION_US_E1 `, `AmazonS3::REGION_US_W1`, `AmazonS3::REGION_EU_W1`, `AmazonS3::REGION_APAC_SE1`, `AmazonS3::REGION_APAC_NE1`]
* @param string $acl (Optional) The ACL settings for the specified object. Accepts any of the following constants: [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. Alternatively, an array of associative arrays. Each associative array contains an <code>id</code> and a <code>permission</code> key. The default value is <code>ACL_PRIVATE</code>.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/UsingBucket.html Working with Amazon S3 Buckets
*/
public function create_bucket($bucket, $region, $acl = self::ACL_PRIVATE, $opt = null)
{
// If the bucket contains uppercase letters...
if (preg_match('/[A-Z]/', $bucket))
{
// Throw a warning
trigger_error('Since DNS-valid bucket names cannot contain uppercase characters, "' . $bucket . '" has been automatically converted to "' . strtolower($bucket) . '"', E_USER_WARNING);
// Force the bucketname to lowercase
$bucket = strtolower($bucket);
}
// Validate the S3 bucket name for creation
if (!$this->validate_bucketname_create($bucket))
{
// @codeCoverageIgnoreStart
throw new S3_Exception('"' . $bucket . '" is not DNS-valid (i.e., <bucketname>.s3.amazonaws.com), and cannot be used as an S3 bucket name. Review "Bucket Restrictions and Limitations" in the S3 Developer Guide for more information.');
// @codeCoverageIgnoreEnd
}
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
// Handle Access Control Lists. Can also be passed as an HTTP header.
if (isset($acl))
{
if (is_array($acl))
{
$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($acl));
}
else
{
$opt['headers']['x-amz-acl'] = $acl;
}
}
// Defaults
$this->set_region($region); // Also sets path-style
$xml = simplexml_load_string($this->base_location_constraint);
switch ($region)
{
case self::REGION_US_E1: // Northern Virginia
$opt['body'] = '';
break;
case self::REGION_EU_W1: // Ireland
$xml->LocationConstraint = 'EU';
$opt['body'] = $xml->asXML();
break;
default:
$xml->LocationConstraint = str_replace(array('s3-', '.amazonaws.com'), '', $region);
$opt['body'] = $xml->asXML();
break;
}
$response = $this->authenticate($bucket, $opt);
// Make sure we're set back to DNS-style URLs
$this->enable_path_style(false);
return $response;
}
/**
* Gets the region in which the specified Amazon S3 bucket is located.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function get_bucket_region($bucket, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'location';
// Authenticate to S3
$response = $this->authenticate($bucket, $opt);
if ($response->isOK())
{
// Handle body
$response->body = (string) $response->body;
}
return $response;
}
/**
* Gets the HTTP headers for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function get_bucket_headers($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'HEAD';
return $this->authenticate($bucket, $opt);
}
/**
* Deletes a bucket from an Amazon S3 account. A bucket must be empty before the bucket itself can be deleted.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param boolean $force (Optional) Whether to force-delete the bucket and all of its contents. The default value is <code>false</code>.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return mixed A <CFResponse> object if the bucket was deleted successfully. Returns boolean <code>false</code> if otherwise.
*/
public function delete_bucket($bucket, $force = false, $opt = null)
{
// Set default value
$success = true;
if ($force)
{
// Delete all of the items from the bucket.
$success = $this->delete_all_object_versions($bucket);
}
// As long as we were successful...
if ($success)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
return $this->authenticate($bucket, $opt);
}
// @codeCoverageIgnoreStart
return false;
// @codeCoverageIgnoreEnd
}
/**
* Gets a list of all buckets contained in the caller's Amazon S3 account.
*
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function list_buckets($opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
return $this->authenticate('', $opt);
}
/**
* Gets the access control list (ACL) settings for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function get_bucket_acl($bucket, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'acl';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Sets the access control list (ACL) settings for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $acl (Optional) The ACL settings for the specified bucket. [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. Alternatively, an array of associative arrays. Each associative array contains an `id` and a `permission` key. The default value is <ACL_PRIVATE>.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function set_bucket_acl($bucket, $acl = self::ACL_PRIVATE, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'acl';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
// Make sure these are defined.
// @codeCoverageIgnoreStart
if (!$this->credentials->canonical_id || !$this->credentials->canonical_name)
{
// Fetch the data live.
$canonical = $this->get_canonical_user_id();
$this->credentials->canonical_id = $canonical['id'];
$this->credentials->canonical_name = $canonical['display_name'];
}
// @codeCoverageIgnoreEnd
if (is_array($acl))
{
$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($acl));
}
else
{
$opt['body'] = '';
$opt['headers']['x-amz-acl'] = $acl;
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// OBJECT METHODS
/**
* Creates an Amazon S3 object. After an Amazon S3 bucket is created, objects can be stored in it.
*
* Each standard object can hold up to 5 GB of data. When an object is stored in Amazon S3, the data is streamed
* to multiple storage servers in multiple data centers. This ensures the data remains available in the
* event of internal network or hardware failure.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>acl</code> - <code>string</code> - Optional - The ACL settings for the specified object. Accepts any of the following constants: [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. Alternatively, an array of associative arrays. Each associative array contains an <code>id</code> and a <code>permission</code> key. The default value is <code>ACL_PRIVATE</code>.</li>
* <li><code>body</code> - <code>string</code> - Required; Conditional - The data to be stored in the object. Either this parameter or <code>fileUpload</code> must be specified.</li>
* <li><code>contentType</code> - <code>string</code> - Optional - The type of content that is being sent in the body. If a file is being uploaded via <code>fileUpload</code> as a file system path, it will attempt to determine the correct mime-type based on the file extension. The default value is <code>application/octet-stream</code>.</li>
* <li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>
* <li><code>fileUpload</code> - <code>string|resource</code> - Required; Conditional - The URL/path for the file to upload, or an open resource. Either this parameter or <code>body</code> is required.</li>
* <li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
* <li><code>length</code> - <code>integer</code> - Optional - The size of the object in bytes. For more information, see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13">RFC 2616, section 14.13</a>. The value can also be passed to the <code>header</code> option as <code>Content-Length</code>.</li>
* <li><code>meta</code> - <code>array</code> - Optional - An associative array of key-value pairs. Represented by <code>x-amz-meta-:</code>. Any header starting with this prefix is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.</li>
* <li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes within the file/stream to upload from.</li>
* <li><code>storage</code> - <code>string</code> - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: <code>AmazonS3::STORAGE_STANDARD</code>, <code>AmazonS3::STORAGE_REDUCED</code>]. The default value is <code>STORAGE_STANDARD</code>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function create_object($bucket, $filename, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['resource'] = $filename;
// Handle content length. Can also be passed as an HTTP header.
if (isset($opt['length']))
{
$opt['headers']['Content-Length'] = $opt['length'];
unset($opt['length']);
}
// Handle content type. Can also be passed as an HTTP header.
if (isset($opt['contentType']))
{
$opt['headers']['Content-Type'] = $opt['contentType'];
unset($opt['contentType']);
}
// Handle Access Control Lists. Can also be passed as an HTTP header.
if (isset($opt['acl']))
{
if (is_array($opt['acl']))
{
$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($opt['acl']));
}
else
{
$opt['headers']['x-amz-acl'] = $opt['acl'];
}
}
// Handle storage settings. Can also be passed as an HTTP header.
if (isset($opt['storage']))
{
$opt['headers']['x-amz-storage-class'] = $opt['storage'];
unset($opt['storage']);
}
// Handle encryption settings. Can also be passed as an HTTP header.
if (isset($opt['encryption']))
{
$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];
unset($opt['encryption']);
}
// Handle meta tags. Can also be passed as an HTTP header.
if (isset($opt['meta']))
{
foreach ($opt['meta'] as $meta_key => $meta_value)
{
// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.
$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;
}
unset($opt['meta']);
}
$opt['headers']['Expect'] = '100-continue';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets the contents of an Amazon S3 object in the specified bucket.
*
* The MD5 value for an object can be retrieved from the ETag HTTP header for any object that was uploaded
* with a normal PUT/POST. This value is incorrect for multipart uploads.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>etag</code> - <code>string</code> - Optional - The <code>ETag</code> header passed in from a previous request. If specified, request <code>LastModified</code> option must be specified as well. Will trigger a <code>304 Not Modified</code> status code if the file hasn't changed.</li>
* <li><code>fileDownload</code> - <code>string|resource</code> - Optional - The file system location to download the file to, or an open file resource. Must be a server-writable location.</li>
* <li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
* <li><code>lastmodified</code> - <code>string</code> - Optional - The <code>LastModified</code> header passed in from a previous request. If specified, request <code>ETag</code> option must be specified as well. Will trigger a <code>304 Not Modified</code> status code if the file hasn't changed.</li>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>range</code> - <code>string</code> - Optional - The range of bytes to fetch from the object. Specify this parameter when downloading partial bits or completing incomplete object downloads. The specified range must be notated with a hyphen (e.g., 0-10485759). Defaults to the byte range of the complete Amazon S3 object.</li>
* <li><code>response</code> - <code>array</code> - Optional - Allows adjustments to specific response headers. Pass an associative array where each key is one of the following: <code>cache-control</code>, <code>content-disposition</code>, <code>content-encoding</code>, <code>content-language</code>, <code>content-type</code>, <code>expires</code>. The <code>expires</code> value should use <php:gmdate()> and be formatted with the <code>DATE_RFC2822</code> constant.</li>
* <li><code>versionId</code> - <code>string</code> - Optional - The version of the object to retrieve. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function get_object($bucket, $filename, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'GET';
$opt['resource'] = $filename;
if (!isset($opt['headers']) || !is_array($opt['headers']))
{
$opt['headers'] = array();
}
if (isset($opt['lastmodified']))
{
$opt['headers']['If-Modified-Since'] = $opt['lastmodified'];
}
if (isset($opt['etag']))
{
$opt['headers']['If-None-Match'] = $opt['etag'];
}
// Partial content range
if (isset($opt['range']))
{
$opt['headers']['Range'] = 'bytes=' . $opt['range'];
}
// GET responses
if (isset($opt['response']))
{
foreach ($opt['response'] as $key => $value)
{
$opt['response-' . $key] = $value;
unset($opt['response'][$key]);
}
}
// Authenticate to S3
$this->parse_the_response = false;
$response = $this->authenticate($bucket, $opt);
$this->parse_the_response = true;
return $response;
}
/**
* Gets the HTTP headers for the specified Amazon S3 object.
*
* The MD5 value for an object can be retrieved from the ETag HTTP header for any object that was uploaded
* with a normal PUT/POST. This value is incorrect for multipart uploads.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>versionId</code> - <code>string</code> - Optional - The version of the object to retrieve. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function get_object_headers($bucket, $filename, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'HEAD';
$opt['resource'] = $filename;
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Deletes an Amazon S3 object from the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>versionId</code> - <code>string</code> - Optional - The version of the object to delete. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
* <li><code>MFASerial</code> - <code>string</code> - Optional - The serial number on the back of the Gemalto device. <code>MFASerial</code> and <code>MFAToken</code> must both be set for MFA to work.</li>
* <li><code>MFAToken</code> - <code>string</code> - Optional - The current token displayed on the Gemalto device. <code>MFASerial</code> and <code>MFAToken</code> must both be set for MFA to work.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
*/
public function delete_object($bucket, $filename, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['resource'] = $filename;
// Enable MFA delete?
// @codeCoverageIgnoreStart
if (isset($opt['MFASerial']) && isset($opt['MFAToken']))
{
$opt['headers'] = array(
'x-amz-mfa' => ($opt['MFASerial'] . ' ' . $opt['MFAToken'])
);
}
// @codeCoverageIgnoreEnd
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Deletes one or more specified Amazon S3 objects from the specified bucket.
*
* Since `delete_object()` is designed for deleting a single object, this method is intended to be used
* when there are two or more objects to delete.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>objects</code> - <code>array</code> - Required - The object references to delete from the bucket. <ul>
* <li><code>key</code> - <code>string</code> - Required - The name of the object (e.g., the "key") to delete. This should include the entire file path including all "subdirectories".</li>
* <li><code>version_id</code> - <code>string</code> - Optional - If the object is versioned, include the version ID to delete.</li>
* </ul></li>
* <li><code>quiet</code> - <code>boolean</code> - Optional - Whether or not Amazon S3 should use "Quiet" mode for this operation. A value of <code>true</code> will enable Quiet mode. A value of <code>false</code> will use Verbose mode. The default value is <code>false</code>.</li>
* <li><code>MFASerial</code> - <code>string</code> - Optional - The serial number on the back of the Gemalto device. <code>MFASerial</code> and <code>MFAToken</code> must both be set for MFA to work.</li>
* <li><code>MFAToken</code> - <code>string</code> - Optional - The current token displayed on the Gemalto device. <code>MFASerial</code> and <code>MFAToken</code> must both be set for MFA to work.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
*/
public function delete_objects($bucket, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'POST';
$opt['sub_resource'] = 'delete';
$opt['body'] = '';
// Bail out
if (!isset($opt['objects']) || !is_array($opt['objects']))
{
throw new S3_Exception('The ' . __FUNCTION__ . ' method requires the "objects" option to be set as an array.');
}
$xml = new SimpleXMLElement($this->multi_object_delete_xml);
// Add the objects
foreach ($opt['objects'] as $object)
{
$xobject = $xml->addChild('Object');
$node = $xobject->addChild('Key');
$node[0] = $object['key'];
if (isset($object['version_id']))
{
$xobject->addChild('VersionId', $object['version_id']);
}
}
// Quiet mode?
if (isset($opt['quiet']))
{
$quiet = 'false';
if (is_bool($opt['quiet'])) // Boolean
{
$quiet = $opt['quiet'] ? 'true' : 'false';
}
elseif (is_string($opt['quiet'])) // String
{
$quiet = ($opt['quiet'] === 'true') ? 'true' : 'false';
}
$xml->addChild('Quiet', $quiet);
}
// Enable MFA delete?
// @codeCoverageIgnoreStart
if (isset($opt['MFASerial']) && isset($opt['MFAToken']))
{
$opt['headers'] = array(
'x-amz-mfa' => ($opt['MFASerial'] . ' ' . $opt['MFAToken'])
);
}
// @codeCoverageIgnoreEnd
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets a list of all Amazon S3 objects in the specified bucket.
*
* NOTE: <strong>This method is paginated</strong>, and will not return more than <code>max-keys</code> keys. If you want to retrieve a list of all keys, you will need to make multiple calls to this function using the <code>marker</code> option to specify the pagination offset (the key of the last processed key--lexically ordered) and the <code>IsTruncated</code> response key to detect when all results have been processed. See: <a href="http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?RESTBucketGET.html">the S3 REST documentation for get_bucket</a> for more information.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>delimiter</code> - <code>string</code> - Optional - Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.</li>
* <li><code>marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the marker.</li>
* <li><code>max-keys</code> - <code>string</code> - Optional - The maximum number of results returned by the method call. The returned list will contain no more results than the specified value, but may return fewer. The default value is 1000.</li>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>prefix</code> - <code>string</code> - Optional - Restricts the response to contain results that begin only with the specified prefix.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function list_objects($bucket, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'GET';
foreach (array('delimiter', 'marker', 'max-keys', 'prefix') as $param)
{
if (isset($opt[$param]))
{
$opt['query_string'][$param] = $opt[$param];
unset($opt[$param]);
}
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Copies an Amazon S3 object to a new location, whether in the same Amazon S3 region, bucket, or otherwise.
*
* @param array $source (Required) The bucket and file name to copy from. The following keys must be set: <ul>
* <li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket containing the source object.</li>
* <li><code>filename</code> - <code>string</code> - Required - Specifies the file name of the source object to copy.</li></ul>
* @param array $dest (Required) The bucket and file name to copy to. The following keys must be set: <ul>
* <li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket to copy the object to.</li>
* <li><code>filename</code> - <code>string</code> - Required - Specifies the file name to copy the object to.</li></ul>
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>acl</code> - <code>string</code> - Optional - The ACL settings for the specified object. [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. Alternatively, an array of associative arrays. Each associative array contains an <code>id</code> and a <code>permission</code> key. The default value is <code>ACL_PRIVATE</code>.</li>
* <li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>
* <li><code>storage</code> - <code>string</code> - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: <code>AmazonS3::STORAGE_STANDARD</code>, <code>AmazonS3::STORAGE_REDUCED</code>]. The default value is <code>STORAGE_STANDARD</code>.</li>
* <li><code>versionId</code> - <code>string</code> - Optional - The version of the object to copy. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
* <li><code>ifMatch</code> - <code>string</code> - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) matches the specified tag; otherwise, the request returns a <code>412</code> HTTP status code error (precondition failed). Used in conjunction with <code>ifUnmodifiedSince</code>.</li>
* <li><code>ifUnmodifiedSince</code> - <code>string</code> - Optional - The LastModified header from a previous request. Copies the object if it hasn't been modified since the specified time; otherwise, the request returns a <code>412</code> HTTP status code error (precondition failed). Used in conjunction with <code>ifMatch</code>.</li>
* <li><code>ifNoneMatch</code> - <code>string</code> - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) is different than the specified ETag; otherwise, the request returns a <code>412</code> HTTP status code error (failed condition). Used in conjunction with <code>ifModifiedSince</code>.</li>
* <li><code>ifModifiedSince</code> - <code>string</code> - Optional - The LastModified header from a previous request. Copies the object if it has been modified since the specified time; otherwise, the request returns a <code>412</code> HTTP status code error (failed condition). Used in conjunction with <code>ifNoneMatch</code>.</li>
* <li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
* <li><code>meta</code> - <code>array</code> - Optional - Associative array of key-value pairs. Represented by <code>x-amz-meta-:</code> Any header starting with this prefix is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.</li>
* <li><code>metadataDirective</code> - <code>string</code> - Optional - Accepts either COPY or REPLACE. You will likely never need to use this, as it manages itself with no issues.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/API/RESTObjectCOPY.html Copying Amazon S3 Objects
*/
public function copy_object($source, $dest, $opt = null)
{
if (!$opt) $opt = array();
$batch = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['resource'] = $dest['filename'];
$opt['body'] = '';
// Handle copy source
if (isset($source['bucket']) && isset($source['filename']))
{
$opt['headers']['x-amz-copy-source'] = '/' . $source['bucket'] . '/' . rawurlencode($source['filename'])
. (isset($opt['versionId']) ? ('?' . 'versionId=' . rawurlencode($opt['versionId'])) : ''); // Append the versionId to copy, if available
unset($opt['versionId']);
}
// Handle metadata directive
$opt['headers']['x-amz-metadata-directive'] = 'COPY';
if ($source['bucket'] === $dest['bucket'] && $source['filename'] === $dest['filename'])
{
$opt['headers']['x-amz-metadata-directive'] = 'REPLACE';
}
if (isset($opt['metadataDirective']))
{
$opt['headers']['x-amz-metadata-directive'] = $opt['metadataDirective'];
unset($opt['metadataDirective']);
}
// Handle Access Control Lists. Can also pass canned ACLs as an HTTP header.
if (isset($opt['acl']) && is_array($opt['acl']))
{
$batch[] = $this->set_object_acl($dest['bucket'], $dest['filename'], $opt['acl'], array(
'returnCurlHandle' => true
));
unset($opt['acl']);
}
elseif (isset($opt['acl']))
{
$opt['headers']['x-amz-acl'] = $opt['acl'];
unset($opt['acl']);
}
// Handle storage settings. Can also be passed as an HTTP header.
if (isset($opt['storage']))
{
$opt['headers']['x-amz-storage-class'] = $opt['storage'];
unset($opt['storage']);
}
// Handle encryption settings. Can also be passed as an HTTP header.
if (isset($opt['encryption']))
{
$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];
unset($opt['encryption']);
}
// Handle conditional-copy parameters
if (isset($opt['ifMatch']))
{
$opt['headers']['x-amz-copy-source-if-match'] = $opt['ifMatch'];
unset($opt['ifMatch']);
}
if (isset($opt['ifNoneMatch']))
{
$opt['headers']['x-amz-copy-source-if-none-match'] = $opt['ifNoneMatch'];
unset($opt['ifNoneMatch']);
}
if (isset($opt['ifUnmodifiedSince']))
{
$opt['headers']['x-amz-copy-source-if-unmodified-since'] = $opt['ifUnmodifiedSince'];
unset($opt['ifUnmodifiedSince']);
}
if (isset($opt['ifModifiedSince']))
{
$opt['headers']['x-amz-copy-source-if-modified-since'] = $opt['ifModifiedSince'];
unset($opt['ifModifiedSince']);
}
// Handle meta tags. Can also be passed as an HTTP header.
if (isset($opt['meta']))
{
foreach ($opt['meta'] as $meta_key => $meta_value)
{
// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.
$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;
}
unset($opt['meta']);
}
// Authenticate to S3
$response = $this->authenticate($dest['bucket'], $opt);
// Attempt to reset ACLs
$http = new CFRequest();
$http->send_multi_request($batch);
return $response;
}
/**
* Updates an Amazon S3 object with new headers or other metadata. To replace the content of the
* specified Amazon S3 object, call <create_object()> with the same bucket and file name parameters.
*
* @param string $bucket (Required) The name of the bucket that contains the source file.
* @param string $filename (Required) The source file name that you want to update.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>acl</code> - <code>string</code> - Optional - The ACL settings for the specified object. [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. The default value is <ACL_PRIVATE>.</li>
* <li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
* <li><code>meta</code> - <code>array</code> - Optional - An associative array of key-value pairs. Any header with the <code>x-amz-meta-</code> prefix is considered user metadata and is stored with the Amazon S3 object. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/API/RESTObjectCOPY.html Copying Amazon S3 Objects
*/
public function update_object($bucket, $filename, $opt = null)
{
if (!$opt) $opt = array();
$opt['metadataDirective'] = 'REPLACE';
// Retrieve the original metadata
if ($metadata = $this->get_object_metadata($bucket, $filename))
{
if (isset($metadata['ACL']))
{
$opt['acl'] = isset($opt['acl']) ? $opt['acl'] : $metadata['ACL'];
}
if (isset($metadata['StorageClass']))
{
$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];
}
if (isset($metadata['ContentType']))
{
$opt['headers']['Content-Type'] = $metadata['ContentType'];
}
}
// Remove a header
unset($metadata['Headers']['date']);
// Merge headers
$opt['headers'] = array_merge($opt['headers'], $metadata['Headers']);
// Authenticate to S3
return $this->copy_object(
array('bucket' => $bucket, 'filename' => $filename),
array('bucket' => $bucket, 'filename' => $filename),
$opt
);
}
/*%******************************************************************************************%*/
// ACCESS CONTROL LISTS
/**
* Gets the access control list (ACL) settings for the specified Amazon S3 object.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>versionId</code> - <code>string</code> - Optional - The version of the object to retrieve. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function get_object_acl($bucket, $filename, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['resource'] = $filename;
$opt['sub_resource'] = 'acl';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Sets the access control list (ACL) settings for the specified Amazon S3 object.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $acl (Optional) The ACL settings for the specified object. Accepts any of the following constants: [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. Alternatively, an array of associative arrays. Each associative array contains an <code>id</code> and a <code>permission</code> key. The default value is <code>ACL_PRIVATE</code>.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function set_object_acl($bucket, $filename, $acl = self::ACL_PRIVATE, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['resource'] = $filename;
$opt['sub_resource'] = 'acl';
// Retrieve the original metadata
$metadata = $this->get_object_metadata($bucket, $filename);
if ($metadata && $metadata['ContentType'])
{
$opt['headers']['Content-Type'] = $metadata['ContentType'];
}
if ($metadata && $metadata['StorageClass'])
{
$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];
}
// Make sure these are defined.
// @codeCoverageIgnoreStart
if (!$this->credentials->canonical_id || !$this->credentials->canonical_name)
{
// Fetch the data live.
$canonical = $this->get_canonical_user_id();
$this->credentials->canonical_id = $canonical['id'];
$this->credentials->canonical_name = $canonical['display_name'];
}
// @codeCoverageIgnoreEnd
if (is_array($acl))
{
$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($acl));
}
else
{
$opt['body'] = '';
$opt['headers']['x-amz-acl'] = $acl;
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Generates the XML to be used for the Access Control Policy.
*
* @param string $canonical_id (Required) The canonical ID for the bucket owner. This is provided as the `id` return value from <get_canonical_user_id()>.
* @param string $canonical_name (Required) The canonical display name for the bucket owner. This is provided as the `display_name` value from <get_canonical_user_id()>.
* @param array $users (Optional) An array of associative arrays. Each associative array contains an `id` value and a `permission` value.
* @return string Access Control Policy XML.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ACLOverview.html Access Control Lists
*/
public function generate_access_policy($canonical_id, $canonical_name, $users)
{
$xml = simplexml_load_string($this->base_acp_xml);
$owner = $xml->addChild('Owner');
$owner->addChild('ID', $canonical_id);
$owner->addChild('DisplayName', $canonical_name);
$acl = $xml->addChild('AccessControlList');
foreach ($users as $user)
{
$grant = $acl->addChild('Grant');
$grantee = $grant->addChild('Grantee');
switch ($user['id'])
{
// Authorized Users
case self::USERS_AUTH:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_AUTH);
break;
// All Users
case self::USERS_ALL:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_ALL);
break;
// The Logging User
case self::USERS_LOGGING:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_LOGGING);
break;
// Email Address or Canonical Id
default:
if (strpos($user['id'], '@'))
{
$grantee->addAttribute('xsi:type', 'AmazonCustomerByEmail', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('EmailAddress', $user['id']);
}
else
{
// Assume Canonical Id
$grantee->addAttribute('xsi:type', 'CanonicalUser', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('ID', $user['id']);
}
break;
}
$grant->addChild('Permission', $user['permission']);
}
return $xml->asXML();
}
/**
* Generates the HTTP headers to be used for the Access Control Policy Grants.
*
* @param array $users (Optional) An array of associative arrays. Each associative array contains an `id` value and a `permission` value.
* @return array HTTP headers to be applied to the request.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ACLOverview.html Access Control Lists
*/
public function generate_access_policy_headers($users)
{
$headers = array();
foreach ($users as $user)
{
// Determine permission. If doesn't exist, create it.
$permission = 'x-amz-grant-' . str_replace('_', '-', strtolower($user['permission']));
if (!isset($headers[$permission]))
{
$headers[$permission] = array();
}
// Handle the IDs
switch ($user['id'])
{
case self::USERS_AUTH: // Authorized Users
case self::USERS_ALL: // All Users
case self::USERS_LOGGING: // The Logging User
$headers[$permission][] = 'uri="' . $user['id'] . '"';
break;
// Email Address or Canonical Id
default:
if (strpos($user['id'], '@'))
{
// Treat as email address
$headers[$permission][] = 'emailAddress="' . $user['id'] . '"';
}
else
{
// Assume Canonical Id
$headers[$permission][] = 'id="' . $user['id'] . '"';
}
break;
}
}
foreach ($headers as &$permission)
{
$permission = implode(', ', $permission);
}
return $headers;
}
/*%******************************************************************************************%*/
// LOGGING METHODS
/**
* Gets the access logs associated with the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use. Pass a `null` value when using the <set_vhost()> method.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ServerLogs.html Server Access Logging
*/
public function get_logs($bucket, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'logging';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Enables access logging for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to enable logging for. Pass a `null` value when using the <set_vhost()> method.
* @param string $target_bucket (Required) The name of the bucket to store the logs in.
* @param string $target_prefix (Required) The prefix to give to the log file names.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>users</code> - <code>array</code> - Optional - An array of associative arrays specifying any user to give access to. Each associative array contains an <code>id</code> and <code>permission</code> value.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/LoggingAPI.html Server Access Logging Configuration API
*/
public function enable_logging($bucket, $target_bucket, $target_prefix, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'logging';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->base_logging_xml);
$LoggingEnabled = $xml->addChild('LoggingEnabled');
$LoggingEnabled->addChild('TargetBucket', $target_bucket);
$LoggingEnabled->addChild('TargetPrefix', $target_prefix);
$TargetGrants = $LoggingEnabled->addChild('TargetGrants');
if (isset($opt['users']) && is_array($opt['users']))
{
foreach ($opt['users'] as $user)
{
$grant = $TargetGrants->addChild('Grant');
$grantee = $grant->addChild('Grantee');
switch ($user['id'])
{
// Authorized Users
case self::USERS_AUTH:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_AUTH);
break;
// All Users
case self::USERS_ALL:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_ALL);
break;
// The Logging User
case self::USERS_LOGGING:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_LOGGING);
break;
// Email Address or Canonical Id
default:
if (strpos($user['id'], '@'))
{
$grantee->addAttribute('xsi:type', 'AmazonCustomerByEmail', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('EmailAddress', $user['id']);
}
else
{
// Assume Canonical Id
$grantee->addAttribute('xsi:type', 'CanonicalUser', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('ID', $user['id']);
}
break;
}
$grant->addChild('Permission', $user['permission']);
}
}
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Disables access logging for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use. Pass `null` if using <set_vhost()>.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/LoggingAPI.html Server Access Logging Configuration API
*/
public function disable_logging($bucket, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'logging';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$opt['body'] = $this->base_logging_xml;
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// CONVENIENCE METHODS
/**
* Gets whether or not the specified Amazon S3 bucket exists in Amazon S3. This includes buckets
* that do not belong to the caller.
*
* @param string $bucket (Required) The name of the bucket to use.
* @return boolean A value of <code>true</code> if the bucket exists, or a value of <code>false</code> if it does not.
*/
public function if_bucket_exists($bucket)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
$header = $this->get_bucket_headers($bucket);
return (integer) $header->status !== 404;
}
/**
* Gets whether or not the specified Amazon S3 object exists in the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @return boolean A value of <code>true</code> if the object exists, or a value of <code>false</code> if it does not.
*/
public function if_object_exists($bucket, $filename)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
$header = $this->get_object_headers($bucket, $filename);
if ($header->isOK()) { return true; }
elseif ($header->status === 404) { return false; }
// @codeCoverageIgnoreStart
return null;
// @codeCoverageIgnoreEnd
}
/**
* Gets whether or not the specified Amazon S3 bucket has a bucket policy associated with it.
*
* @param string $bucket (Required) The name of the bucket to use.
* @return boolean A value of <code>true</code> if a bucket policy exists, or a value of <code>false</code> if one does not.
*/
public function if_bucket_policy_exists($bucket)
{
if ($this->use_batch_flow)
{
// @codeCoverageIgnoreStart
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
// @codeCoverageIgnoreEnd
}
$response = $this->get_bucket_policy($bucket);
if ($response->isOK()) { return true; }
elseif ($response->status === 404) { return false; }
// @codeCoverageIgnoreStart
return null;
// @codeCoverageIgnoreEnd
}
/**
* Gets the number of Amazon S3 objects in the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @return integer The number of Amazon S3 objects in the bucket.
*/
public function get_bucket_object_count($bucket)
{
if ($this->use_batch_flow)
{
// @codeCoverageIgnoreStart
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
// @codeCoverageIgnoreEnd
}
return count($this->get_object_list($bucket));
}
/**
* Gets the cumulative file size of the contents of the Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param boolean $friendly_format (Optional) A value of <code>true</code> will format the return value to 2 decimal points using the largest possible unit (i.e., 3.42 GB). A value of <code>false</code> will format the return value as the raw number of bytes.
* @return integer|string The number of bytes as an integer, or the friendly format as a string.
*/
public function get_bucket_filesize($bucket, $friendly_format = false)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
$filesize = 0;
$list = $this->list_objects($bucket);
foreach ($list->body->Contents as $filename)
{
$filesize += (integer) $filename->Size;
}
while ((string) $list->body->IsTruncated === 'true')
{
$body = (array) $list->body;
$list = $this->list_objects($bucket, array(
'marker' => (string) end($body['Contents'])->Key
));
foreach ($list->body->Contents as $object)
{
$filesize += (integer) $object->Size;
}
}
if ($friendly_format)
{
$filesize = $this->util->size_readable($filesize);
}
return $filesize;
}
/**
* Gets the file size of the specified Amazon S3 object.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param boolean $friendly_format (Optional) A value of <code>true</code> will format the return value to 2 decimal points using the largest possible unit (i.e., 3.42 GB). A value of <code>false</code> will format the return value as the raw number of bytes.
* @return integer|string The number of bytes as an integer, or the friendly format as a string.
*/
public function get_object_filesize($bucket, $filename, $friendly_format = false)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
$object = $this->get_object_headers($bucket, $filename);
if ($object->isOK()) {
$filesize = (integer) $object->header['content-length'];
} else {
$filesize = 0;
}
if ($friendly_format)
{
$filesize = $this->util->size_readable($filesize);
}
return $filesize;
}
/**
* Changes the content type for an existing Amazon S3 object.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $contentType (Required) The content-type to apply to the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function change_content_type($bucket, $filename, $contentType, $opt = null)
{
if (!$opt) $opt = array();
// Retrieve the original metadata
$metadata = $this->get_object_metadata($bucket, $filename);
if ($metadata && isset($metadata['ACL']))
{
$opt['acl'] = $metadata['ACL'];
}
if ($metadata && isset($metadata['StorageClass']))
{
$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];
}
// Merge optional parameters
$opt = array_merge_recursive(array(
'headers' => array(
'Content-Type' => $contentType
),
'metadataDirective' => 'REPLACE'
), $opt);
return $this->copy_object(
array('bucket' => $bucket, 'filename' => $filename),
array('bucket' => $bucket, 'filename' => $filename),
$opt
);
}
/**
* Changes the storage redundancy for an existing object.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $storage (Required) The storage setting to apply to the object. [Allowed values: <code>AmazonS3::STORAGE_STANDARD</code>, <code>AmazonS3::STORAGE_REDUCED</code>]
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function change_storage_redundancy($bucket, $filename, $storage, $opt = null)
{
if (!$opt) $opt = array();
// Retrieve the original metadata
$metadata = $this->get_object_metadata($bucket, $filename);
if ($metadata && isset($metadata['ACL']))
{
$opt['acl'] = $metadata['ACL'];
}
if ($metadata && isset($metadata['StorageClass']))
{
$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];
}
// Merge optional parameters
$opt = array_merge(array(
'storage' => $storage,
'metadataDirective' => 'COPY',
), $opt);
return $this->copy_object(
array('bucket' => $bucket, 'filename' => $filename),
array('bucket' => $bucket, 'filename' => $filename),
$opt
);
}
/**
* Gets a simplified list of bucket names on an Amazon S3 account.
*
* @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the bucket names against.
* @return array The list of matching bucket names. If there are no results, the method will return an empty array.
* @link http://php.net/pcre Regular Expressions (Perl-Compatible)
*/
public function get_bucket_list($pcre = null)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
// Get a list of buckets.
$list = $this->list_buckets();
if ($list = $list->body->query('descendant-or-self::Name'))
{
$list = $list->map_string($pcre);
return $list;
}
// @codeCoverageIgnoreStart
return array();
// @codeCoverageIgnoreEnd
}
/**
* Gets a simplified list of Amazon S3 object file names contained in a bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>delimiter</code> - <code>string</code> - Optional - Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.</li>
* <li><code>marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the marker.</li>
* <li><code>max-keys</code> - <code>integer</code> - Optional - The maximum number of results returned by the method call. The returned list will contain no more results than the specified value, but may return less. A value of zero is treated as if you did not specify max-keys.</li>
* <li><code>pcre</code> - <code>string</code> - Optional - A Perl-Compatible Regular Expression (PCRE) to filter the names against. This is applied only AFTER any native Amazon S3 filtering from specified <code>prefix</code>, <code>marker</code>, <code>max-keys</code>, or <code>delimiter</code> values are applied.</li>
* <li><code>prefix</code> - <code>string</code> - Optional - Restricts the response to contain results that begin only with the specified prefix.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* @return array The list of matching object names. If there are no results, the method will return an empty array.
* @link http://php.net/pcre Regular Expressions (Perl-Compatible)
*/
public function get_object_list($bucket, $opt = null)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
if (!$opt) $opt = array();
unset($opt['returnCurlHandle']); // This would cause problems
// Set some default values
$pcre = isset($opt['pcre']) ? $opt['pcre'] : null;
$max_keys = (isset($opt['max-keys']) && is_int($opt['max-keys'])) ? $opt['max-keys'] : null;
$objects = array();
if (!$max_keys)
{
// No max-keys specified. Get everything.
do
{
$list = $this->list_objects($bucket, $opt);
if ($keys = $list->body->query('descendant-or-self::Key')->map_string($pcre))
{
$objects = array_merge($objects, $keys);
}
$body = (array) $list->body;
$opt = array_merge($opt, array(
'marker' => (isset($body['Contents']) && is_array($body['Contents'])) ?
((string) end($body['Contents'])->Key) :
((string) $list->body->Contents->Key)
));
}
while ((string) $list->body->IsTruncated === 'true');
}
else
{
// Max-keys specified. Approximate number of loops and make the requests.
$max_keys = $opt['max-keys'];
$loops = ceil($max_keys / 1000);
do
{
$list = $this->list_objects($bucket, $opt);
$keys = $list->body->query('descendant-or-self::Key')->map_string($pcre);
if ($count = count($keys))
{
$objects = array_merge($objects, $keys);
if ($count < 1000)
{
break;
}
}
if ($max_keys > 1000)
{
$max_keys -= 1000;
}
$body = (array) $list->body;
$opt = array_merge($opt, array(
'max-keys' => $max_keys,
'marker' => (isset($body['Contents']) && is_array($body['Contents'])) ?
((string) end($body['Contents'])->Key) :
((string) $list->body->Contents->Key)
));
}
while (--$loops);
}
return $objects;
}
/**
* Deletes all Amazon S3 objects inside the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. The default value is <PCRE_ALL>.
* @return boolean A value of <code>true</code> means that all objects were successfully deleted. A value of <code>false</code> means that at least one object failed to delete.
* @link http://php.net/pcre Regular Expressions (Perl-Compatible)
*/
public function delete_all_objects($bucket, $pcre = self::PCRE_ALL)
{
// Collect all matches
$list = $this->get_object_list($bucket, array('pcre' => $pcre));
// As long as we have at least one match...
if (count($list) > 0)
{
$objects = array();
foreach ($list as $object)
{
$objects[] = array('key' => $object);
}
$batch = new CFBatchRequest();
$batch->use_credentials($this->credentials);
foreach (array_chunk($objects, 1000) as $object_set)
{
$this->batch($batch)->delete_objects($bucket, array(
'objects' => $object_set
));
}
$responses = $this->batch($batch)->send();
$is_ok = true;
foreach ($responses as $response)
{
if (!$response->isOK() || isset($response->body->Error))
{
$is_ok = false;
}
}
return $is_ok;
}
// If there are no matches, return true
return true;
}
/**
* Deletes all of the versions of all Amazon S3 objects inside the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. The default value is <PCRE_ALL>.
* @return boolean A value of <code>true</code> means that all object versions were successfully deleted. A value of <code>false</code> means that at least one object/version failed to delete.
* @link http://php.net/pcre Regular Expressions (Perl-Compatible)
*/
public function delete_all_object_versions($bucket, $pcre = null)
{
// Instantiate
$versions = $this->list_bucket_object_versions($bucket);
// Gather all nodes together into a single array
if ($versions->body->DeleteMarker() && $versions->body->Version())
{
$markers = array_merge($versions->body->DeleteMarker()->getArrayCopy(), $versions->body->Version()->getArrayCopy());
}
elseif ($versions->body->DeleteMarker())
{
$markers = $versions->body->DeleteMarker()->getArrayCopy();
}
elseif ($versions->body->Version())
{
$markers = $versions->body->Version()->getArrayCopy();
}
else
{
$markers = array();
}
while ((string) $versions->body->IsTruncated === 'true')
{
$versions = $this->list_bucket_object_versions($bucket, array(
'key-marker' => (string) $versions->body->NextKeyMarker
));
// Gather all nodes together into a single array
if ($versions->body->DeleteMarker() && $versions->body->Version())
{
$markers = array_merge($markers, $versions->body->DeleteMarker()->getArrayCopy(), $versions->body->Version()->getArrayCopy());
}
elseif ($versions->body->DeleteMarker())
{
$markers = array_merge($markers, $versions->body->DeleteMarker()->getArrayCopy());
}
elseif ($versions->body->Version())
{
$markers = array_merge($markers, $versions->body->Version()->getArrayCopy());
}
}
$objects = array();
// Loop through markers
foreach ($markers as $marker)
{
if ($pcre)
{
if (preg_match($pcre, (string) $marker->Key))
{
$xx = array('key' => (string) $marker->Key);
if ((string) $marker->VersionId !== 'null')
{
$xx['version_id'] = (string) $marker->VersionId;
}
$objects[] = $xx;
unset($xx);
}
}
else
{
$xx = array('key' => (string) $marker->Key);
if ((string) $marker->VersionId !== 'null')
{
$xx['version_id'] = (string) $marker->VersionId;
}
$objects[] = $xx;
unset($xx);
}
}
$batch = new CFBatchRequest();
$batch->use_credentials($this->credentials);
foreach (array_chunk($objects, 1000) as $object_set)
{
$this->batch($batch)->delete_objects($bucket, array(
'objects' => $object_set
));
}
$responses = $this->batch($batch)->send();
$is_ok = true;
foreach ($responses as $response)
{
if (!$response->isOK() || isset($response->body->Error))
{
$is_ok = false;
}
}
return $is_ok;
}
/**
* Gets the collective metadata for the given Amazon S3 object.
*
* The MD5 value for an object can be retrieved from the ETag HTTP header for any object that was uploaded
* with a normal PUT/POST. This value is incorrect for multipart uploads.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the Amazon S3 object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>versionId</code> - <code>string</code> - Optional - The version of the object to retrieve. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return mixed If the object exists, the method returns the collective metadata for the Amazon S3 object. If the object does not exist, the method returns boolean <code>false</code>.
*/
public function get_object_metadata($bucket, $filename, $opt = null)
{
$batch = new CFBatchRequest();
$this->batch($batch)->get_object_acl($bucket, $filename); // Get ACL info
$this->batch($batch)->get_object_headers($bucket, $filename); // Get content-type
$this->batch($batch)->list_objects($bucket, array( // Get other metadata
'max-keys' => 1,
'prefix' => $filename
));
$response = $this->batch($batch)->send();
// Fail if any requests were unsuccessful
if (!$response->areOK())
{
return false;
}
$data = array(
'ACL' => array(),
'ContentType' => null,
'ETag' => null,
'Headers' => null,
'Key' => null,
'LastModified' => null,
'Owner' => array(),
'Size' => null,
'StorageClass' => null,
);
// Add the content type
$data['ContentType'] = (string) $response[1]->header['content-type'];
// Add the other metadata (including storage type)
$contents = json_decode(json_encode($response[2]->body->query('descendant-or-self::Contents')->first()), true);
$data = array_merge($data, (is_array($contents) ? $contents : array()));
// Add ACL info
$grants = $response[0]->body->query('descendant-or-self::Grant');
$max = count($grants);
// Add raw header info
$data['Headers'] = $response[1]->header;
foreach (array('_info', 'x-amz-id-2', 'x-amz-request-id', 'cneonction', 'server', 'content-length', 'content-type', 'etag') as $header)
{
unset($data['Headers'][$header]);
}
ksort($data['Headers']);
if (count($grants) > 0)
{
foreach ($grants as $grant)
{
$dgrant = array(
'id' => (string) $this->util->try_these(array('ID', 'URI'), $grant->Grantee),
'permission' => (string) $grant->Permission
);
$data['ACL'][] = $dgrant;
}
}
return $data;
}
/*%******************************************************************************************%*/
// URLS
/**
* Gets the web-accessible URL for the Amazon S3 object or generates a time-limited signed request for
* a private file.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the Amazon S3 object.
* @param integer|string $preauth (Optional) Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>https</code> - <code>boolean</code> - Optional - Set to <code>true</code> if you would like the URL be in https mode. Otherwise, the default behavior is always to use http regardless of your SSL settings.</li>
* <li><code>method</code> - <code>string</code> - Optional - The HTTP method to use for the request. Defaults to a value of <code>GET</code>.</li>
* <li><code>response</code> - <code>array</code> - Optional - Allows adjustments to specific response headers. Pass an associative array where each key is one of the following: <code>cache-control</code>, <code>content-disposition</code>, <code>content-encoding</code>, <code>content-language</code>, <code>content-type</code>, <code>expires</code>. The <code>expires</code> value should use <php:gmdate()> and be formatted with the <code>DATE_RFC2822</code> constant.</li>
* <li><code>torrent</code> - <code>boolean</code> - Optional - A value of <code>true</code> will return a URL to a torrent of the Amazon S3 object. A value of <code>false</code> will return a non-torrent URL. Defaults to <code>false</code>.</li>
* <li><code>versionId</code> - <code>string</code> - Optional - The version of the object. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return string The file URL, with authentication and/or torrent parameters if requested.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_QSAuth.html Using Query String Authentication
*/
public function get_object_url($bucket, $filename, $preauth = 0, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = isset($opt['method']) ? $opt['method'] : 'GET';
$opt['resource'] = $filename;
$opt['preauth'] = $preauth;
if (isset($opt['torrent']) && $opt['torrent'])
{
$opt['sub_resource'] = 'torrent';
unset($opt['torrent']);
}
// GET responses
if (isset($opt['response']))
{
foreach ($opt['response'] as $key => $value)
{
$opt['response-' . $key] = $value;
unset($opt['response'][$key]);
}
}
// Determine whether or not to use SSL
$use_ssl = isset($opt['https']) ? (bool) $opt['https'] : false;
unset($opt['https']);
$current_use_ssl_setting = $this->use_ssl;
// Authenticate to S3
$this->use_ssl = $use_ssl;
$response = $this->authenticate($bucket, $opt);
$this->use_ssl = $current_use_ssl_setting;
return $response;
}
/**
* Gets the web-accessible URL to a torrent of the Amazon S3 object. The Amazon S3 object's access
* control list settings (ACL) MUST be set to <ACL_PUBLIC> for a valid URL to be returned.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param integer|string $preauth (Optional) Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.
* @return string The torrent URL, with authentication parameters if requested.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?S3TorrentRetrieve.html Using BitTorrent to Retrieve Objects Stored in Amazon S3
*/
public function get_torrent_url($bucket, $filename, $preauth = 0)
{
return $this->get_object_url($bucket, $filename, $preauth, array(
'torrent' => true
));
}
/*%******************************************************************************************%*/
// VERSIONING
/**
* Enables versioning support for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>MFASerial</code> - string (Optional) The serial number on the back of the Gemalto device. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>
* <li><code>MFAToken</code> - string (Optional) The current token displayed on the Gemalto device. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>
* <li><code>MFAStatus</code> - string (Optional) The MFA Delete status. Can be <code>Enabled</code> or <code>Disabled</code>. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
*/
public function enable_versioning($bucket, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'versioning';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->base_versioning_xml);
$xml->addChild('Status', 'Enabled');
// Enable MFA delete?
// @codeCoverageIgnoreStart
if (isset($opt['MFASerial']) && isset($opt['MFAToken']) && isset($opt['MFAStatus']))
{
$xml->addChild('MfaDelete', $opt['MFAStatus']);
$opt['headers']['x-amz-mfa'] = ($opt['MFASerial'] . ' ' . $opt['MFAToken']);
}
// @codeCoverageIgnoreEnd
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Disables versioning support for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>MFASerial</code> - <code>string</code> - Optional - The serial number on the back of the Gemalto device. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>
* <li><code>MFAToken</code> - <code>string</code> - Optional - The current token displayed on the Gemalto device. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>
* <li><code>MFAStatus</code> - <code>string</code> - Optional - The MFA Delete status. Can be <code>Enabled</code> or <code>Disabled</code>. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
*/
public function disable_versioning($bucket, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'versioning';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->base_versioning_xml);
$xml->addChild('Status', 'Suspended');
// Enable MFA delete?
// @codeCoverageIgnoreStart
if (isset($opt['MFASerial']) && isset($opt['MFAToken']) && isset($opt['MFAStatus']))
{
$xml->addChild('MfaDelete', $opt['MFAStatus']);
$opt['headers']['x-amz-mfa'] = ($opt['MFASerial'] . ' ' . $opt['MFAToken']);
}
// @codeCoverageIgnoreEnd
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets an Amazon S3 bucket's versioning status.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function get_versioning_status($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'versioning';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets a list of all the versions of Amazon S3 objects in the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>delimiter</code> - <code>string</code> - Optional - Unicode string parameter. Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.</li>
* <li><code>key-marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the <code>key-marker</code>.</li>
* <li><code>max-keys</code> - <code>string</code> - Optional - Limits the number of results returned in response to your query. Will return no more than this number of results, but possibly less.</li>
* <li><code>prefix</code> - <code>string</code> - Optional - Restricts the response to only contain results that begin with the specified prefix.</li>
* <li><code>version-id-marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the <code>version-id-marker</code>.</li>
* <li><code>preauth</code> - <code>integer|string</code> - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function list_bucket_object_versions($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'versions';
foreach (array('delimiter', 'key-marker', 'max-keys', 'prefix', 'version-id-marker') as $param)
{
if (isset($opt[$param]))
{
$opt['query_string'][$param] = $opt[$param];
unset($opt[$param]);
}
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// BUCKET POLICIES
/**
* Sets the policy sub-resource for the specified Amazon S3 bucket. The specified policy replaces any
* policy the bucket already has.
*
* To perform this operation, the caller must be authorized to set a policy for the bucket and have
* PutPolicy permissions. If the caller does not have PutPolicy permissions for the bucket, Amazon S3
* returns a `403 Access Denied` error. If the caller has the correct permissions but has not been
* authorized by the bucket owner, Amazon S3 returns a `405 Method Not Allowed` error.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param CFPolicy $policy (Required) The JSON policy to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/AccessPolicyLanguage.html Appendix: The Access Policy Language
*/
public function set_bucket_policy($bucket, CFPolicy $policy, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'policy';
$opt['body'] = $policy->get_json();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets the policy of the specified Amazon S3 bucket.
*
* To use this operation, the caller must have GetPolicy permissions for the specified bucket and must be
* the bucket owner. If the caller does not have GetPolicy permissions, this method will generate a
* `403 Access Denied` error. If the caller has the correct permissions but is not the bucket owner, this
* method will generate a `405 Method Not Allowed` error. If the bucket does not have a policy defined for
* it, this method will generate a `404 Policy Not Found` error.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function get_bucket_policy($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'policy';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Deletes the bucket policy for the specified Amazon S3 bucket. To delete the policy, the caller must
* be the bucket owner and have `DeletePolicy` permissions for the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response. If you do not have `DeletePolicy` permissions, Amazon S3 returns a `403 Access Denied` error. If you have the correct permissions, but are not the bucket owner, Amazon S3 returns a `405 Method Not Allowed` error. If the bucket doesn't have a policy, Amazon S3 returns a `204 No Content` error.
*/
public function delete_bucket_policy($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['sub_resource'] = 'policy';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// BUCKET NOTIFICATIONS
/**
* Enables notifications of specified events for an Amazon S3 bucket. Currently, the
* `s3:ReducedRedundancyLostObject` event is the only event supported for notifications. The
* `s3:ReducedRedundancyLostObject` event is triggered when Amazon S3 detects that it has lost all
* copies of an Amazon S3 object and can no longer service requests for that object.
*
* If the bucket owner and Amazon SNS topic owner are the same, the bucket owner has permission to
* publish notifications to the topic by default. Otherwise, the owner of the topic must create a
* policy to enable the bucket owner to publish to the topic.
*
* By default, only the bucket owner can configure notifications on a bucket. However, bucket owners
* can use bucket policies to grant permission to other users to set this configuration with the
* `s3:PutBucketNotification` permission.
*
* After a PUT operation is called to configure notifications on a bucket, Amazon S3 publishes a test
* notification to ensure that the topic exists and that the bucket owner has permission to publish
* to the specified topic. If the notification is successfully published to the SNS topic, the PUT
* operation updates the bucket configuration and returns the 200 OK responses with a
* `x-amz-sns-test-message-id` header containing the message ID of the test notification sent to topic.
*
* @param string $bucket (Required) The name of the bucket to create bucket notifications for.
* @param string $topic_arn (Required) The SNS topic ARN to send notifications to.
* @param string $event (Required) The event type to listen for.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events
*/
public function create_bucket_notification($bucket, $topic_arn, $event, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'notification';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->base_notification_xml);
$topic_config = $xml->addChild('TopicConfiguration');
$topic_config->addChild('Topic', $topic_arn);
$topic_config->addChild('Event', $event);
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets the notification configuration of a bucket. Currently, the `s3:ReducedRedundancyLostObject` event
* is the only event supported for notifications. The `s3:ReducedRedundancyLostObject` event is triggered
* when Amazon S3 detects that it has lost all replicas of a Reduced Redundancy Storage object and can no
* longer service requests for that object.
*
* If notifications are not enabled on the bucket, the operation returns an empty
* `NotificatonConfiguration` element.
*
* By default, you must be the bucket owner to read the notification configuration of a bucket. However,
* the bucket owner can use a bucket policy to grant permission to other users to read this configuration
* with the `s3:GetBucketNotification` permission.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events
*/
public function get_bucket_notifications($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'notification';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Empties the list of SNS topics to send notifications to.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events
*/
public function delete_bucket_notification($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'notification';
$opt['body'] = $this->base_notification_xml;
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// MULTIPART UPLOAD
/**
* Calculates the correct values for sequentially reading a file for multipart upload. This method should
* be used in conjunction with <upload_part()>.
*
* @param integer $filesize (Required) The size in bytes of the entire file.
* @param integer $part_size (Required) The size in bytes of the part of the file to send.
* @return array An array containing key-value pairs. The keys are `seekTo` and `length`.
*/
public function get_multipart_counts($filesize, $part_size)
{
$i = 0;
$sizecount = $filesize;
$values = array();
while ($sizecount > 0)
{
$sizecount -= $part_size;
$values[] = array(
'seekTo' => ($part_size * $i),
'length' => (($sizecount > 0) ? $part_size : ($sizecount + $part_size)),
);
$i++;
}
return $values;
}
/**
* Initiates a multipart upload and returns an `UploadId`.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>acl</code> - <code>string</code> - Optional - The ACL settings for the specified object. Accepts any of the following constants: [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. Alternatively, an array of associative arrays. Each associative array contains an <code>id</code> and a <code>permission</code> key. The default value is <code>ACL_PRIVATE</code>.</li>
* <li><code>contentType</code> - <code>string</code> - Optional - The type of content that is being sent. The default value is <code>application/octet-stream</code>.</li>
* <li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>
* <li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
* <li><code>meta</code> - <code>array</code> - Optional - An associative array of key-value pairs. Any header starting with <code>x-amz-meta-:</code> is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.</li>
* <li><code>storage</code> - <code>string</code> - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: <code>AmazonS3::STORAGE_STANDARD</code>, <code>AmazonS3::STORAGE_REDUCED</code>]. The default value is <code>STORAGE_STANDARD</code>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function initiate_multipart_upload($bucket, $filename, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'POST';
$opt['resource'] = $filename;
$opt['sub_resource'] = 'uploads';
$opt['body'] = '';
// Handle content type. Can also be passed as an HTTP header.
if (isset($opt['contentType']))
{
$opt['headers']['Content-Type'] = $opt['contentType'];
unset($opt['contentType']);
}
// Set a default content type.
if (!isset($opt['headers']['Content-Type']))
{
$opt['headers']['Content-Type'] = 'application/octet-stream';
}
// Handle Access Control Lists. Can also be passed as an HTTP header.
if (isset($opt['acl']))
{
if (is_array($opt['acl']))
{
$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($opt['acl']));
}
else
{
$opt['headers']['x-amz-acl'] = $opt['acl'];
}
}
// Handle storage settings. Can also be passed as an HTTP header.
if (isset($opt['storage']))
{
$opt['headers']['x-amz-storage-class'] = $opt['storage'];
unset($opt['storage']);
}
// Handle encryption settings. Can also be passed as an HTTP header.
if (isset($opt['encryption']))
{
$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];
unset($opt['encryption']);
}
// Handle meta tags. Can also be passed as an HTTP header.
if (isset($opt['meta']))
{
foreach ($opt['meta'] as $meta_key => $meta_value)
{
// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.
$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;
}
unset($opt['meta']);
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Uploads a single part of a multipart upload. The part size cannot be smaller than 5 MB
* or larger than 5 TB. A multipart upload can have no more than 10,000 parts.
*
* Amazon S3 charges for storage as well as requests to the service. Smaller part sizes (and more
* requests) allow for faster failures and better upload reliability. Larger part sizes (and fewer
* requests) costs slightly less but has lower upload reliability.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to <initiate_multipart_upload()>.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>fileUpload</code> - <code>string|resource</code> - Required - The URL/path for the file to upload or an open resource.</li>
* <li><code>partNumber</code> - <code>integer</code> - Required - The part number order of the multipart upload.</li>
* <li><code>expect</code> - <code>string</code> - Optional - Specifies that the SDK not send the request body until it receives an acknowledgement. If the message is rejected based on the headers, the body of the message is not sent. For more information, see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20">RFC 2616, section 14.20</a>. The value can also be passed to the <code>header</code> option as <code>Expect</code>. [Allowed values: <code>100-continue</code>]</li>
* <li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
* <li><code>length</code> - <code>integer</code> - Optional - The size of the part in bytes. For more information, see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13">RFC 2616, section 14.13</a>. The value can also be passed to the <code>header</code> option as <code>Content-Length</code>.</li>
* <li><code>md5</code> - <code>string</code> - Optional - The base64 encoded 128-bit MD5 digest of the part data. This header can be used as a message integrity check to verify that the part data is the same data that was originally sent. Although it is optional, we recommend using this mechanism as an end-to-end integrity check. For more information, see <a href="http://www.ietf.org/rfc/rfc1864.txt">RFC 1864</a>. The value can also be passed to the <code>header</code> option as <code>Content-MD5</code>.</li>
* <li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes for the piece of the file/stream to upload.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function upload_part($bucket, $filename, $upload_id, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['resource'] = $filename;
$opt['uploadId'] = $upload_id;
if (!isset($opt['fileUpload']) || !isset($opt['partNumber']))
{
throw new S3_Exception('The `fileUpload` and `partNumber` options are both required in ' . __FUNCTION__ . '().');
}
// Handle expectation. Can also be passed as an HTTP header.
if (isset($opt['expect']))
{
$opt['headers']['Expect'] = $opt['expect'];
unset($opt['expect']);
}
// Handle content length. Can also be passed as an HTTP header.
if (isset($opt['length']))
{
$opt['headers']['Content-Length'] = $opt['length'];
unset($opt['length']);
}
// Handle content md5. Can also be passed as an HTTP header.
if (isset($opt['md5']))
{
$opt['headers']['Content-MD5'] = $opt['md5'];
unset($opt['md5']);
}
$opt['headers']['Expect'] = '100-continue';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Lists the completed parts of an in-progress multipart upload.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to <initiate_multipart_upload()>.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>max-parts</code> - <code>integer</code> - Optional - The maximum number of parts to return in the response body.</li>
* <li><code>part-number-marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur numerically after the value of the <code>part-number-marker</code>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function list_parts($bucket, $filename, $upload_id, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'GET';
$opt['resource'] = $filename;
$opt['uploadId'] = $upload_id;
$opt['query_string'] = array();
foreach (array('max-parts', 'part-number-marker') as $param)
{
if (isset($opt[$param]))
{
$opt['query_string'][$param] = $opt[$param];
unset($opt[$param]);
}
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Aborts an in-progress multipart upload. This operation cannot be reversed.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to <initiate_multipart_upload()>.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function abort_multipart_upload($bucket, $filename, $upload_id, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'DELETE';
$opt['resource'] = $filename;
$opt['uploadId'] = $upload_id;
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Completes an in-progress multipart upload. A multipart upload is completed by describing the part
* numbers and corresponding ETag values in order, and submitting that data to Amazon S3 as an XML document.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to <initiate_multipart_upload()>.
* @param string|array|SimpleXMLElement|CFResponse $parts (Required) The completion XML document. This document can be provided in multiple ways; as a string of XML, as a <php:SimpleXMLElement> object representing the XML document, as an indexed array of associative arrays where the keys are <code>PartNumber</code> and <code>ETag</code>, or as a <CFResponse> object returned by <list_parts()>.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function complete_multipart_upload($bucket, $filename, $upload_id, $parts, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'POST';
$opt['resource'] = $filename;
$opt['uploadId'] = $upload_id;
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
// Disable Content-MD5 calculation for this operation
$opt['NoContentMD5'] = true;
if (is_string($parts))
{
// Assume it's the intended XML.
$opt['body'] = $parts;
}
elseif ($parts instanceof SimpleXMLElement)
{
// Assume it's a SimpleXMLElement object representing the XML.
$opt['body'] = $parts->asXML();
}
elseif (is_array($parts) || $parts instanceof CFResponse)
{
$xml = simplexml_load_string($this->complete_mpu_xml);
if (is_array($parts))
{
// Generate the appropriate XML.
foreach ($parts as $node)
{
$part = $xml->addChild('Part');
$part->addChild('PartNumber', $node['PartNumber']);
$part->addChild('ETag', $node['ETag']);
}
}
elseif ($parts instanceof CFResponse)
{
// Assume it's a response from list_parts().
foreach ($parts->body->Part as $node)
{
$part = $xml->addChild('Part');
$part->addChild('PartNumber', (string) $node->PartNumber);
$part->addChild('ETag', (string) $node->ETag);
}
}
$opt['body'] = $xml->asXML();
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Lists the in-progress multipart uploads.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>delimiter</code> - <code>string</code> - Optional - Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.</li>
* <li><code>key-marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the <code>key-marker</code>. If used in conjunction with <code>upload-id-marker</code>, the results will be filtered to include keys whose upload ID is alphabetically after the value of <code>upload-id-marker</code>.</li>
* <li><code>upload-id-marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the <code>upload-id-marker</code>. Must be used in conjunction with <code>key-marker</code>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function list_multipart_uploads($bucket, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'uploads';
foreach (array('key-marker', 'max-uploads', 'upload-id-marker') as $param)
{
if (isset($opt[$param]))
{
$opt['query_string'][$param] = $opt[$param];
unset($opt[$param]);
}
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Since Amazon S3's standard <copy_object()> operation only supports copying objects that are smaller than
* 5 GB, the ability to copy large objects (greater than 5 GB) requires the use of "Multipart Copy".
*
* Copying large objects requires the developer to initiate a new multipart "upload", copy pieces of the
* large object (specifying a range of bytes up to 5 GB from the large source file), then complete the
* multipart "upload".
*
* NOTE: <strong>This is a synchronous operation</strong>, not an <em>asynchronous</em> operation, which means
* that Amazon S3 will not return a response for this operation until the copy has completed across the Amazon
* S3 server fleet. Copying objects within a single region will complete more quickly than copying objects
* <em>across</em> regions. The synchronous nature of this operation is different from other services where
* responses are typically returned immediately, even if the operation itself has not yet been completed on
* the server-side.
*
* @param array $source (Required) The bucket and file name to copy from. The following keys must be set: <ul>
* <li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket containing the source object.</li>
* <li><code>filename</code> - <code>string</code> - Required - Specifies the file name of the source object to copy.</li></ul>
* @param array $dest (Required) The bucket and file name to copy to. The following keys must be set: <ul>
* <li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket to copy the object to.</li>
* <li><code>filename</code> - <code>string</code> - Required - Specifies the file name to copy the object to.</li></ul>
* @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to <initiate_multipart_upload()>.
* @param integer $part_number (Required) A part number uniquely identifies a part and defines its position within the destination object. When you complete a multipart upload, a complete object is created by concatenating parts in ascending order based on part number. If you copy a new part using the same part number as a previously copied/uploaded part, the previously written part is overwritten.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>ifMatch</code> - <code>string</code> - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) matches the specified tag; otherwise, the request returns a <code>412</code> HTTP status code error (precondition failed). Used in conjunction with <code>ifUnmodifiedSince</code>.</li>
* <li><code>ifUnmodifiedSince</code> - <code>string</code> - Optional - The LastModified header from a previous request. Copies the object if it hasn't been modified since the specified time; otherwise, the request returns a <code>412</code> HTTP status code error (precondition failed). Used in conjunction with <code>ifMatch</code>.</li>
* <li><code>ifNoneMatch</code> - <code>string</code> - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) is different than the specified ETag; otherwise, the request returns a <code>412</code> HTTP status code error (failed condition). Used in conjunction with <code>ifModifiedSince</code>.</li>
* <li><code>ifModifiedSince</code> - <code>string</code> - Optional - The LastModified header from a previous request. Copies the object if it has been modified since the specified time; otherwise, the request returns a <code>412</code> HTTP status code error (failed condition). Used in conjunction with <code>ifNoneMatch</code>.</li>
* <li><code>range</code> - <code>string</code> - Optional - The range of bytes to copy from the object. Specify this parameter when copying partial bits. The specified range must be notated with a hyphen (e.g., 0-10485759). Defaults to the byte range of the complete Amazon S3 object.</li>
* <li><code>versionId</code> - <code>string</code> - Optional - The version of the object to copy. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function copy_part($source, $dest, $upload_id, $part_number, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['resource'] = $dest['filename'];
$opt['uploadId'] = $upload_id;
$opt['partNumber'] = $part_number;
// Handle copy source
if (isset($source['bucket']) && isset($source['filename']))
{
$opt['headers']['x-amz-copy-source'] = '/' . $source['bucket'] . '/' . rawurlencode($source['filename'])
. (isset($opt['versionId']) ? ('?' . 'versionId=' . rawurlencode($opt['versionId'])) : ''); // Append the versionId to copy, if available
unset($opt['versionId']);
}
// Handle conditional-copy parameters
if (isset($opt['ifMatch']))
{
$opt['headers']['x-amz-copy-source-if-match'] = $opt['ifMatch'];
unset($opt['ifMatch']);
}
if (isset($opt['ifNoneMatch']))
{
$opt['headers']['x-amz-copy-source-if-none-match'] = $opt['ifNoneMatch'];
unset($opt['ifNoneMatch']);
}
if (isset($opt['ifUnmodifiedSince']))
{
$opt['headers']['x-amz-copy-source-if-unmodified-since'] = $opt['ifUnmodifiedSince'];
unset($opt['ifUnmodifiedSince']);
}
if (isset($opt['ifModifiedSince']))
{
$opt['headers']['x-amz-copy-source-if-modified-since'] = $opt['ifModifiedSince'];
unset($opt['ifModifiedSince']);
}
// Partial content range
if (isset($opt['range']))
{
$opt['headers']['x-amz-copy-source-range'] = 'bytes=' . $opt['range'];
}
// Authenticate to S3
return $this->authenticate($dest['bucket'], $opt);
}
/**
* Creates an Amazon S3 object using the multipart upload APIs. It is analogous to <create_object()>.
*
* While each individual part of a multipart upload can hold up to 5 GB of data, this method limits the
* part size to a maximum of 500 MB. The combined size of all parts can not exceed 5 TB of data. When an
* object is stored in Amazon S3, the data is streamed to multiple storage servers in multiple data
* centers. This ensures the data remains available in the event of internal network or hardware failure.
*
* Amazon S3 charges for storage as well as requests to the service. Smaller part sizes (and more
* requests) allow for faster failures and better upload reliability. Larger part sizes (and fewer
* requests) costs slightly less but has lower upload reliability.
*
* In certain cases with large objects, it's possible for this method to attempt to open more file system
* connections than allowed by the OS. In this case, either
* <a href="https://forums.aws.amazon.com/thread.jspa?threadID=70216">increase the number of connections
* allowed</a> or increase the value of the <code>partSize</code> parameter to use a larger part size.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>fileUpload</code> - <code>string|resource</code> - Required - The URL/path for the file to upload, or an open resource.</li>
* <li><code>acl</code> - <code>string</code> - Optional - The ACL settings for the specified object. [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. The default value is <code>ACL_PRIVATE</code>.</li>
* <li><code>contentType</code> - <code>string</code> - Optional - The type of content that is being sent in the body. The default value is <code>application/octet-stream</code>.</li>
* <li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
* <li><code>length</code> - <code>integer</code> - Optional - The size of the object in bytes. For more information, see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13">RFC 2616, section 14.13</a>. The value can also be passed to the <code>header</code> option as <code>Content-Length</code>.</li>
* <li><code>limit</code> - <code>integer</code> - Optional - The maximum number of concurrent uploads done by cURL. Gets passed to <code>CFBatchRequest</code>.</li>
* <li><code>meta</code> - <code>array</code> - Optional - An associative array of key-value pairs. Any header starting with <code>x-amz-meta-:</code> is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.</li>
* <li><code>partSize</code> - <code>integer</code> - Optional - The size of an individual part. The size may not be smaller than 5 MB or larger than 500 MB. The default value is 50 MB.</li>
* <li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes for the first piece of the file/stream to upload.</li>
* <li><code>storage</code> - <code>string</code> - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: <code>AmazonS3::STORAGE_STANDARD</code>, <code>AmazonS3::STORAGE_REDUCED</code>]. The default value is <code>STORAGE_STANDARD</code>.</li>
* <li><code>uploadId</code> - <code>string</code> - Optional - An upload ID identifying an existing multipart upload to use. If this option is not set, one will be created automatically.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function create_mpu_object($bucket, $filename, $opt = null)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
if (!$opt) $opt = array();
// Handle content length. Can also be passed as an HTTP header.
if (isset($opt['length']))
{
$opt['headers']['Content-Length'] = $opt['length'];
unset($opt['length']);
}
if (!isset($opt['fileUpload']))
{
throw new S3_Exception('The `fileUpload` option is required in ' . __FUNCTION__ . '().');
}
elseif (is_resource($opt['fileUpload']))
{
$opt['limit'] = 1; // We can only read from this one resource.
$upload_position = isset($opt['seekTo']) ? (integer) $opt['seekTo'] : ftell($opt['fileUpload']);
$upload_filesize = isset($opt['headers']['Content-Length']) ? (integer) $opt['headers']['Content-Length'] : null;
if (!isset($upload_filesize) && $upload_position !== false)
{
$stats = fstat($opt['fileUpload']);
if ($stats && $stats['size'] >= 0)
{
$upload_filesize = $stats['size'] - $upload_position;
}
}
}
else
{
$upload_position = isset($opt['seekTo']) ? (integer) $opt['seekTo'] : 0;
if (isset($opt['headers']['Content-Length']))
{
$upload_filesize = (integer) $opt['headers']['Content-Length'];
}
else
{
$upload_filesize = filesize($opt['fileUpload']);
if ($upload_filesize !== false)
{
$upload_filesize -= $upload_position;
}
}
}
if ($upload_position === false || !isset($upload_filesize) || $upload_filesize === false || $upload_filesize < 0)
{
throw new S3_Exception('The size of `fileUpload` cannot be determined in ' . __FUNCTION__ . '().');
}
// Handle part size
if (isset($opt['partSize']))
{
// If less that 5 MB...
if ((integer) $opt['partSize'] < 5242880)
{
$opt['partSize'] = 5242880; // 5 MB
}
// If more than 500 MB...
elseif ((integer) $opt['partSize'] > 524288000)
{
$opt['partSize'] = 524288000; // 500 MB
}
}
else
{
$opt['partSize'] = 52428800; // 50 MB
}
// If the upload size is smaller than the piece size, failover to create_object().
if ($upload_filesize < $opt['partSize'] && !isset($opt['uploadId']))
{
return $this->create_object($bucket, $filename, $opt);
}
// Initiate multipart upload
if (isset($opt['uploadId']))
{
$upload_id = $opt['uploadId'];
}
else
{
// Compose options for initiate_multipart_upload().
$_opt = array();
foreach (array('contentType', 'acl', 'storage', 'headers', 'meta') as $param)
{
if (isset($opt[$param]))
{
$_opt[$param] = $opt[$param];
}
}
$upload = $this->initiate_multipart_upload($bucket, $filename, $_opt);
if (!$upload->isOK())
{
return $upload;
}
// Fetch the UploadId
$upload_id = (string) $upload->body->UploadId;
}
// Get the list of pieces
$pieces = $this->get_multipart_counts($upload_filesize, (integer) $opt['partSize']);
// Queue batch requests
$batch = new CFBatchRequest(isset($opt['limit']) ? (integer) $opt['limit'] : null);
foreach ($pieces as $i => $piece)
{
$this->batch($batch)->upload_part($bucket, $filename, $upload_id, array(
'expect' => '100-continue',
'fileUpload' => $opt['fileUpload'],
'partNumber' => ($i + 1),
'seekTo' => $upload_position + (integer) $piece['seekTo'],
'length' => (integer) $piece['length'],
));
}
// Send batch requests
$batch_responses = $this->batch($batch)->send();
if (!$batch_responses->areOK())
{
return $batch_responses;
}
// Compose completion XML
$parts = array();
foreach ($batch_responses as $i => $response)
{
$parts[] = array('PartNumber' => ($i + 1), 'ETag' => $response->header['etag']);
}
return $this->complete_multipart_upload($bucket, $filename, $upload_id, $parts);
}
/**
* Aborts all multipart uploads initiated before the specified date. This operation cannot be reversed.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string|integer $when (Optional) The time and date to use for comparison. Accepts any value that <php:strtotime()> understands.
* @return CFArray A <CFArray> containing a series of 0 or more <CFResponse> objects, containing a parsed HTTP response.
*/
public function abort_multipart_uploads_by_date($bucket, $when = null)
{
if ($this->use_batch_flow)
{
// @codeCoverageIgnoreStart
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
// @codeCoverageIgnoreEnd
}
$when = $when ? $when : time();
$handles = array();
$data = $this->list_multipart_uploads($bucket)->body;
$when = is_int($when) ? $when : strtotime($when);
if (!($data instanceof CFSimpleXML))
{
return false;
}
$list = $data->query('descendant-or-self::Upload/Initiated');
if (count($list) > 0)
{
foreach ($list as $node)
{
if (strtotime((string) $node) < $when)
{
$q = new CFBatchRequest();
$parent = $node->parent();
$upload_id = $parent
->query('descendant-or-self::UploadId')
->first()
->to_string();
$filename = $parent
->query('descendant-or-self::Key')
->first()
->to_string();
$handles[] = $this->abort_multipart_upload($bucket, $filename, $upload_id, array(
'returnCurlHandle' => true
));
}
}
$http = new CFRequest();
$responses = $http->send_multi_request($handles);
if (is_array($responses) && count($responses) > 0)
{
return new CFArray($responses);
}
}
return new CFArray();
}
/*%******************************************************************************************%*/
// WEBSITE CONFIGURATION
/**
* Enables and configures an Amazon S3 website using the corresponding bucket as the content source.
* The website will have one default domain name associated with it, which is the bucket name. If you
* attempt to configure an Amazon S3 website for a bucket whose name is not compatible with DNS,
* Amazon S3 returns an <code>InvalidBucketName</code> error. For more information on bucket names and DNS,
* refer to <a href="http://docs.amazonwebservices.com/AmazonS3/latest/dev/BucketRestrictions.html">Bucket Restrictions and Limitations.</a>
*
* To visit the bucket as a website a new endpoint is created in the following pattern:
* <code>http://&lt;bucketName&gt;.s3-website-&lt;region&gt;.amazonaws.com</code>. This is a sample URL
* for a bucket called <code>example-bucket</code> in the <code>us-east-1</code> region.
* (e.g., <code>http://example-bucket.s3-website-us-east-1.amazonaws.com</code>)
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>indexDocument</code> - <code>string</code> - Optional - The file path to use as the root document. The default value is <code>index.html</code>.</li>
* <li><code>errorDocument</code> - <code>string</code> - Optional - The file path to use as the error document. The default value is <code>error.html</code>.</li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function create_website_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'website';
$xml = simplexml_load_string($this->website_config_xml);
if (isset($opt['indexDocument']))
{
$xml->IndexDocument->Suffix = $opt['indexDocument'];
}
if (isset($opt['errorDocument']))
{
$xml->ErrorDocument->Key = $opt['errorDocument'];
}
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Retrieves the website configuration for a bucket. The contents of this response are identical to the
* content submitted by the user during the website creation operation. If a website configuration has
* never been set, Amazon S3 will return a 404 error.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function get_website_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'website';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Removes the website configuration for a bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function delete_website_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['sub_resource'] = 'website';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// OBJECT EXPIRATION
/**
* Enables the ability to specify an expiry period for objects when an object should be deleted,
* measured as number of days from creation time. Amazon S3 guarantees that the object will be
* deleted when the expiration time is passed.
*
* This feature is also known as "lifecycle" (e.g., in the AWS Console).
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>rules</code> - <code>string</code> - Required - The object expiration rule-sets to apply to the bucket. <ul>
* <li><code>x</code> - <code>array</code> - Required - This represents a simple array index. <ul>
* <li><code>id</code> - <code>string</code> - Optional - Unique identifier for the rule. The value cannot be longer than 255 characters.</li>
* <li><code>prefix</code> - <code>string</code> - Required - The Amazon S3 object prefix which targets the file(s) for expiration.</li>
* <li><code>expiration</code> - <code>array</code> - Required - The container for the unit of measurement by which the expiration time is calculated. <ul>
* <li><code>days</code> - <code>integer</code> - Required - The number of days until the targetted objects expire from the bucket.</li>
* </ul></li>
* <li><code>enabled</code> - <code>boolean</code> - Optional - Whether or not to enable this rule-set. A value of <code>true</code> enables the rule-set. A value of <code>false</code> disables the rule-set. The default value is <code>true</code>.</li>
* </ul></li>
* </ul></li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function create_object_expiration_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'lifecycle';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->object_expiration_xml, $this->parser_class);
if (isset($opt['rules']) && is_array($opt['rules']) && count($opt['rules']))
{
foreach ($opt['rules'] as $rule)
{
$xrule = $xml->addChild('Rule');
// ID
if (isset($rule['id']))
{
if (strlen($rule['id']) > 255)
{
throw new S3_Exception('The "id" for a rule must not be more than 255 characters in the ' . __FUNCTION__ . ' method.');
}
$xrule->addChild('ID', $rule['id']);
}
// Prefix
if (isset($rule['prefix']))
{
$xrule->addChild('Prefix', $rule['prefix']);
}
else
{
throw new S3_Exception('Each rule requires a "prefix" in the ' . __FUNCTION__ . ' method.');
}
// Status
$enabled = 'Enabled';
if (isset($rule['enabled']))
{
if (is_bool($rule['enabled'])) // Boolean
{
$enabled = $rule['enabled'] ? 'Enabled' : 'Disabled';
}
elseif (is_string($rule['enabled'])) // String
{
$enabled = (strtolower($rule['enabled']) === 'true') ? 'Enabled' : 'Disabled';
}
$xrule->addChild('Status', $enabled);
}
else
{
$xrule->addChild('Status', 'Enabled');
}
// Expiration
if (isset($rule['expiration']))
{
$xexpiration = $xrule->addChild('Expiration');
if (isset($rule['expiration']['days']))
{
$xexpiration->addChild('Days', $rule['expiration']['days']);
}
}
else
{
throw new S3_Exception('Each rule requires a "expiration" in the ' . __FUNCTION__ . ' method.');
}
}
}
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Retrieves the expiry period (i.e., lifecycle) for objects.
*
* This feature is also known as "lifecycle" (e.g., in the AWS Console).
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function get_object_expiration_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'lifecycle';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Deletes the expiry period (i.e., lifecycle) for objects.
*
* This feature is also known as "lifecycle" (e.g., in the AWS Console).
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function delete_object_expiration_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['sub_resource'] = 'lifecycle';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// BUCKET TAGS
/**
* Apply a set of tags to the specified bucket. Bucket Tags simplify the task of associating Amazon S3
* costs with specific buckets.
*
* This operation requires permission to perform <code>s3:PutBucketTagging</code> actions. By default,
* the bucket owner is permitted to perform these actions, and can grant permission to other users.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>tags</code> - <code>array</code> - Required - An associative array of custom key-value pairs. <ul>
* <li><code>[custom-key]</code> - <code>string</code> - Optional - A custom key-value pair to tag the bucket with.</li>
* </ul></li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function create_bucket_tags($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'tagging';
$xml = simplexml_load_string($this->bucket_tagging_xml);
if (isset($opt['tags']) && is_array($opt['tags']))
{
foreach ($opt['tags'] as $key => $value)
{
$xtag = $xml->TagSet->addChild('Tag');
$xtag->addChild('Key', $key);
$xtag->addChild('Value', $value);
}
}
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Retrieve all associated tags for the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function get_bucket_tags($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'tagging';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Delete all associated tags from the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function delete_bucket_tags($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['sub_resource'] = 'tagging';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// CROSS-ORIGIN RESOURCE SHARING (CORS)
/**
* Create a new CORS configuration.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>cors_rule</code> - <code>array</code> - Required - One or more rule-sets. <ul>
* <li><code>x</code> - <code>array</code> - Required - This represents a simple array index. <ul>
* <li><code>allowed_header</code> - <code>array</code> - Required - Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.</li>
* <li><code>allowed_method</code> - <code>array</code> - Required - An array of HTTP methods to allow. There must be at least one method set. [Allowed values: `GET`, `PUT`, `HEAD`, `POST`, `DELETE`]</li>
* <li><code>allowed_origin</code> - <code>array</code> - Required - An array of hostnames to allow. This could be `*` to indicate it is open to all domains. If one of them contains the string `*`, then there can be exactly one.</li>
* <li><code>expose_header</code> - <code>string</code> - Optional - Enable the browser to read this header.</li>
* <li><code>id</code> - <code>string</code> - Optional - Unique identifier for the rule. The value cannot be longer than 255 characters.</li>
* <li><code>max_age</code> - <code>integer</code> - Optional - Alter the client's caching behavior for the pre-flight request.</li>
* </ul></li>
* </ul></li>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function create_cors_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'cors';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->cors_config_xml, $this->parser_class);
if (isset($opt['cors_rule']) && is_array($opt['cors_rule']))
{
foreach ($opt['cors_rule'] as $rule_set)
{
// New rule node
$xrule = $xml->addChild('CORSRule');
// ExposeHeader node
if (isset($rule_set['expose_header']))
{
$xrule->addChild('ExposeHeader', $rule_set['expose_header']);
}
// MaxAgeSeconds node
if (isset($rule_set['max_age']))
{
$xrule->addChild('MaxAgeSeconds', $rule_set['max_age']);
}
// AllowedHeader node
if (isset($rule_set['allowed_header']))
{
if (!is_array($rule_set['allowed_header']))
{
$rule_set['allowed_header'] = array($rule_set['allowed_header']);
}
foreach ($rule_set['allowed_header'] as $method)
{
$xrule->addChild('AllowedHeader', $method);
}
}
// AllowedMethod node
if (isset($rule_set['allowed_method']))
{
if (!is_array($rule_set['allowed_method']))
{
$rule_set['allowed_method'] = array($rule_set['allowed_method']);
}
foreach ($rule_set['allowed_method'] as $method)
{
$xrule->addChild('AllowedMethod', $method);
}
}
// AllowedOrigin node
if (isset($rule_set['allowed_origin']))
{
if (!is_array($rule_set['allowed_origin']))
{
$rule_set['allowed_origin'] = array($rule_set['allowed_origin']);
}
foreach ($rule_set['allowed_origin'] as $method)
{
$xrule->addChild('AllowedOrigin', $method);
}
}
}
}
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Retrieves the CORS configuration.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function get_cors_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'cors';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Deletes the CORS configuration.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
* <li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.</li></ul>
* @return CFResponse A <CFResponse> object containing a parsed HTTP response.
*/
public function delete_cors_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['sub_resource'] = 'cors';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// MISCELLANEOUS
/**
* Gets the canonical user ID and display name from the Amazon S3 server.
*
* @return array An associative array containing the `id` and `display_name` values.
*/
public function get_canonical_user_id()
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
$id = $this->list_buckets();
return array(
'id' => (string) $id->body->Owner->ID,
'display_name' => (string) $id->body->Owner->DisplayName
);
}
/**
* Loads and registers the S3StreamWrapper class as a stream wrapper.
*
* @param string $protocol (Optional) The name of the protocol to register.
* @return boolean Whether or not the registration succeeded.
*/
public function register_stream_wrapper($protocol = 's3')
{
require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'extensions'
. DIRECTORY_SEPARATOR . 's3streamwrapper.class.php';
return S3StreamWrapper::register($this, $protocol);
}
}
// cli args
if($argc < 3) {
echo "s3copy copies files and streams to a public S3 file and copies the url to your clipboard." . PHP_EOL . PHP_EOL;
echo "Usage: s3copy bucket-name file" . PHP_EOL;
echo "Usage: echo 'example' | s3copy bucket-name filename" . PHP_EOL . PHP_EOL;
echo "Credentials must exist in ~/.awssecret" . PHP_EOL;
exit(1);
}
$bucket = isset($argv[1])?$argv[1]:die('Bucket name required.' . PHP_EOL);
$file = isset($argv[2])?$argv[2]:die('File name required.' . PHP_EOL);
$s3key_file = realpath($_ENV['HOME'].'/.awssecret');
if(!file_exists($s3key_file)) die('~/.awssecret must be present.' . PHP_EOL);
$s3key = file_get_contents($s3key_file);
$s3key = explode("\n", $s3key);
CFCredentials::set(array(
// Credentials for the development environment.
'cli' => array(
// Amazon Web Services Key. Found in the AWS Security Credentials. You can also pass
// this value as the first parameter to a service constructor.
'key' => $s3key[0],
// Amazon Web Services Secret Key. Found in the AWS Security Credentials. You can also
// pass this value as the second parameter to a service constructor.
'secret' => $s3key[1],
// This option allows you to configure a preferred storage type to use for caching by
// default. This can be changed later using the set_cache_config() method.
//
// Valid values are: `apc`, `xcache`, or a file system path such as `./cache` or
// `/tmp/cache/`.
'default_cache_config' => '/tmp/s3copy_cache/',
// Determines which Cerificate Authority file to use.
//
// A value of boolean `false` will use the Certificate Authority file available on the
// system. A value of boolean `true` will use the Certificate Authority provided by the
// SDK. Passing a file system path to a Certificate Authority file (chmodded to `0755`)
// will use that.
//
// Leave this set to `false` if you're not sure.
'certificate_authority' => false
),
// Specify a default credential set to use if there are more than one.
'@default' => 'cli'
));
// init
$s3 = new AmazonS3();
// make the bucket if it doesn't exist
if (!$s3->if_bucket_exists($bucket))
{
$response = $s3->create_bucket($bucket, AmazonS3::REGION_US_E1);
if (!$response->isOK()) die('Could not create `' . $bucket . '`.' . PHP_EOL);
}
if(is_file($file)) {
// copy a file to S3
echo "Uploading File '{$file}' to S3 Bucket '{$bucket}'..." . PHP_EOL . PHP_EOL;
$filename = basename($file);
$file = realpath($file);
} else {
// copy stdin to S3
echo "Uploading STDIN to S3 Bucket '{$bucket}'..." . PHP_EOL . PHP_EOL;
$filename = $file;
$file = fopen("php://stdin", "r");
}
// do the upload
$response = $s3->create_object($bucket, $filename, array(
'fileUpload' => $file,
'acl' => AmazonS3::ACL_PUBLIC
));
if (!$response->isOK()) die('Upload failed.');
// if it works, echo and copy url to clipboard
$url = "http://{$bucket}.s3.amazonaws.com/{$filename}";
echo "A Public Share URL for: {$filename}" . PHP_EOL;
echo $url . PHP_EOL . PHP_EOL;
// copy using pbcopy or xclip
system("[ -x `which pbcopy` ] && echo '{$url}' | pbcopy && echo 'Also in your clipboard'");
system("[ -x `which xclip` ] && echo '{$url}' | xclip && echo 'Also in your clipboard'");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment