sdk.class.php 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580
  1. <?php
  2. /*
  3. * Copyright 2010-2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License").
  6. * You may not use this file except in compliance with the License.
  7. * A copy of the License is located at
  8. *
  9. * http://aws.amazon.com/apache2.0
  10. *
  11. * or in the "license" file accompanying this file. This file is distributed
  12. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  13. * express or implied. See the License for the specific language governing
  14. * permissions and limitations under the License.
  15. */
  16. /*%******************************************************************************************%*/
  17. // CORE DEPENDENCIES
  18. // Look for include file in the same directory (e.g. `./config.inc.php`).
  19. if (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php'))
  20. {
  21. include_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php';
  22. }
  23. // Fallback to `~/.aws/sdk/config.inc.php`
  24. elseif (getenv('HOME') && file_exists(getenv('HOME') . DIRECTORY_SEPARATOR . '.aws' . DIRECTORY_SEPARATOR . 'sdk' . DIRECTORY_SEPARATOR . 'config.inc.php'))
  25. {
  26. include_once getenv('HOME') . DIRECTORY_SEPARATOR . '.aws' . DIRECTORY_SEPARATOR . 'sdk' . DIRECTORY_SEPARATOR . 'config.inc.php';
  27. }
  28. /*%******************************************************************************************%*/
  29. // EXCEPTIONS
  30. /**
  31. * Default CFRuntime Exception.
  32. */
  33. class CFRuntime_Exception extends Exception {}
  34. /*%******************************************************************************************%*/
  35. // DETERMINE WHAT ENVIRONMENT DATA TO ADD TO THE USERAGENT FOR METRIC TRACKING
  36. /*
  37. Define a temporary callback function for this calculation. Get the PHP version and any
  38. required/optional extensions that are leveraged.
  39. Tracking this data gives Amazon better metrics about what configurations are being used
  40. so that forward-looking plans for the code can be made with more certainty (e.g. What
  41. version of PHP are most people running? Do they tend to have the latest PCRE?).
  42. */
  43. function __aws_sdk_ua_callback()
  44. {
  45. $ua_append = '';
  46. $extensions = get_loaded_extensions();
  47. $sorted_extensions = array();
  48. if ($extensions)
  49. {
  50. foreach ($extensions as $extension)
  51. {
  52. if ($extension === 'curl' && function_exists('curl_version'))
  53. {
  54. $curl_version = curl_version();
  55. $sorted_extensions[strtolower($extension)] = $curl_version['version'];
  56. }
  57. elseif ($extension === 'pcre' && defined('PCRE_VERSION'))
  58. {
  59. $pcre_version = explode(' ', PCRE_VERSION);
  60. $sorted_extensions[strtolower($extension)] = $pcre_version[0];
  61. }
  62. elseif ($extension === 'openssl' && defined('OPENSSL_VERSION_TEXT'))
  63. {
  64. $openssl_version = explode(' ', OPENSSL_VERSION_TEXT);
  65. $sorted_extensions[strtolower($extension)] = $openssl_version[1];
  66. }
  67. else
  68. {
  69. $sorted_extensions[strtolower($extension)] = phpversion($extension);
  70. }
  71. }
  72. }
  73. foreach (array('simplexml', 'json', 'pcre', 'spl', 'curl', 'openssl', 'apc', 'xcache', 'memcache', 'memcached', 'pdo', 'pdo_sqlite', 'sqlite', 'sqlite3', 'zlib', 'xdebug') as $ua_ext)
  74. {
  75. if (isset($sorted_extensions[$ua_ext]) && $sorted_extensions[$ua_ext])
  76. {
  77. $ua_append .= ' ' . $ua_ext . '/' . $sorted_extensions[$ua_ext];
  78. }
  79. elseif (isset($sorted_extensions[$ua_ext]))
  80. {
  81. $ua_append .= ' ' . $ua_ext . '/0';
  82. }
  83. }
  84. foreach (array('memory_limit', 'date.timezone', 'open_basedir', 'safe_mode', 'zend.enable_gc') as $cfg)
  85. {
  86. $cfg_value = get_cfg_var($cfg);
  87. if (in_array($cfg, array('memory_limit', 'date.timezone'), true))
  88. {
  89. $ua_append .= ' ' . $cfg . '/' . str_replace('/', '.', $cfg_value);
  90. }
  91. elseif (in_array($cfg, array('open_basedir', 'safe_mode', 'zend.enable_gc'), true))
  92. {
  93. if ($cfg_value === false || $cfg_value === '' || $cfg_value === 0)
  94. {
  95. $cfg_value = 'off';
  96. }
  97. elseif ($cfg_value === true || $cfg_value === '1' || $cfg_value === 1)
  98. {
  99. $cfg_value = 'on';
  100. }
  101. $ua_append .= ' ' . $cfg . '/' . $cfg_value;
  102. }
  103. }
  104. return $ua_append;
  105. }
  106. /*%******************************************************************************************%*/
  107. // INTERMEDIARY CONSTANTS
  108. define('CFRUNTIME_NAME', 'aws-sdk-php');
  109. define('CFRUNTIME_VERSION', '1.4.1');
  110. // define('CFRUNTIME_BUILD', gmdate('YmdHis', filemtime(__FILE__))); // @todo: Hardcode for release.
  111. define('CFRUNTIME_BUILD', '20110823204245');
  112. 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());
  113. /*%******************************************************************************************%*/
  114. // CLASS
  115. /**
  116. * Core functionality and default settings shared across all SDK classes. All methods and properties in this
  117. * class are inherited by the service-specific classes.
  118. *
  119. * @version 2011.07.28
  120. * @license See the included NOTICE.md file for more information.
  121. * @copyright See the included NOTICE.md file for more information.
  122. * @link http://aws.amazon.com/php/ PHP Developer Center
  123. */
  124. class CFRuntime
  125. {
  126. /*%******************************************************************************************%*/
  127. // CONSTANTS
  128. /**
  129. * Name of the software.
  130. */
  131. const NAME = CFRUNTIME_NAME;
  132. /**
  133. * Version of the software.
  134. */
  135. const VERSION = CFRUNTIME_VERSION;
  136. /**
  137. * Build ID of the software.
  138. */
  139. const BUILD = CFRUNTIME_BUILD;
  140. /**
  141. * User agent string used to identify the software.
  142. */
  143. const USERAGENT = CFRUNTIME_USERAGENT;
  144. /*%******************************************************************************************%*/
  145. // PROPERTIES
  146. /**
  147. * The Amazon API Key.
  148. */
  149. public $key;
  150. /**
  151. * The Amazon API Secret Key.
  152. */
  153. public $secret_key;
  154. /**
  155. * The Amazon Authentication Token.
  156. */
  157. public $auth_token;
  158. /**
  159. * The Amazon Account ID, without hyphens.
  160. */
  161. public $account_id;
  162. /**
  163. * The Amazon Associates ID.
  164. */
  165. public $assoc_id;
  166. /**
  167. * Handle for the utility functions.
  168. */
  169. public $util;
  170. /**
  171. * An identifier for the current AWS service.
  172. */
  173. public $service = null;
  174. /**
  175. * The supported API version.
  176. */
  177. public $api_version = null;
  178. /**
  179. * The state of whether auth should be handled as AWS Query.
  180. */
  181. public $use_aws_query = true;
  182. /**
  183. * The default class to use for utilities (defaults to <CFUtilities>).
  184. */
  185. public $utilities_class = 'CFUtilities';
  186. /**
  187. * The default class to use for HTTP requests (defaults to <CFRequest>).
  188. */
  189. public $request_class = 'CFRequest';
  190. /**
  191. * The default class to use for HTTP responses (defaults to <CFResponse>).
  192. */
  193. public $response_class = 'CFResponse';
  194. /**
  195. * The default class to use for parsing XML (defaults to <CFSimpleXML>).
  196. */
  197. public $parser_class = 'CFSimpleXML';
  198. /**
  199. * The default class to use for handling batch requests (defaults to <CFBatchRequest>).
  200. */
  201. public $batch_class = 'CFBatchRequest';
  202. /**
  203. * The number of seconds to adjust the request timestamp by (defaults to 0).
  204. */
  205. public $adjust_offset = 0;
  206. /**
  207. * The state of SSL/HTTPS use.
  208. */
  209. public $use_ssl = true;
  210. /**
  211. * The state of SSL certificate verification.
  212. */
  213. public $ssl_verification = true;
  214. /**
  215. * The proxy to use for connecting.
  216. */
  217. public $proxy = null;
  218. /**
  219. * The alternate hostname to use, if any.
  220. */
  221. public $hostname = null;
  222. /**
  223. * The state of the capability to override the hostname with <set_hostname()>.
  224. */
  225. public $override_hostname = true;
  226. /**
  227. * The alternate port number to use, if any.
  228. */
  229. public $port_number = null;
  230. /**
  231. * The alternate resource prefix to use, if any.
  232. */
  233. public $resource_prefix = null;
  234. /**
  235. * The state of cache flow usage.
  236. */
  237. public $use_cache_flow = false;
  238. /**
  239. * The caching class to use.
  240. */
  241. public $cache_class = null;
  242. /**
  243. * The caching location to use.
  244. */
  245. public $cache_location = null;
  246. /**
  247. * When the cache should be considered stale.
  248. */
  249. public $cache_expires = null;
  250. /**
  251. * The state of cache compression.
  252. */
  253. public $cache_compress = null;
  254. /**
  255. * The current instantiated cache object.
  256. */
  257. public $cache_object = null;
  258. /**
  259. * The current instantiated batch request object.
  260. */
  261. public $batch_object = null;
  262. /**
  263. * The internally instantiated batch request object.
  264. */
  265. public $internal_batch_object = null;
  266. /**
  267. * The state of batch flow usage.
  268. */
  269. public $use_batch_flow = false;
  270. /**
  271. * The state of the cache deletion setting.
  272. */
  273. public $delete_cache = false;
  274. /**
  275. * The state of the debug mode setting.
  276. */
  277. public $debug_mode = false;
  278. /**
  279. * The number of times to retry failed requests.
  280. */
  281. public $max_retries = 3;
  282. /**
  283. * The user-defined callback function to call when a stream is read from.
  284. */
  285. public $registered_streaming_read_callback = null;
  286. /**
  287. * The user-defined callback function to call when a stream is written to.
  288. */
  289. public $registered_streaming_write_callback = null;
  290. /*%******************************************************************************************%*/
  291. // CONSTRUCTOR
  292. /**
  293. * The constructor. You would not normally instantiate this class directly. Rather, you would instantiate
  294. * a service-specific class.
  295. *
  296. * @param string $key (Optional) Your AWS key, or a session key. If blank, it will look for the <code>AWS_KEY</code> constant.
  297. * @param string $secret_key (Optional) Your AWS secret key, or a session secret key. If blank, it will look for the <code>AWS_SECRET_KEY</code> constant.
  298. * @param string $token (optional) An AWS session token. If blank, a request will be made to the AWS Secure Token Service to fetch a set of session credentials.
  299. * @return boolean A value of `false` if no valid values are set, otherwise `true`.
  300. */
  301. public function __construct($key = null, $secret_key = null, $token = null)
  302. {
  303. // Instantiate the utilities class.
  304. $this->util = new $this->utilities_class();
  305. // Determine the current service.
  306. $this->service = get_class($this);
  307. // Set default values
  308. $this->key = null;
  309. $this->secret_key = null;
  310. // If both a key and secret key are passed in, use those.
  311. if ($key && $secret_key)
  312. {
  313. $this->key = $key;
  314. $this->secret_key = $secret_key;
  315. return true;
  316. }
  317. // If neither are passed in, look for the constants instead.
  318. elseif (defined('AWS_KEY') && defined('AWS_SECRET_KEY'))
  319. {
  320. $this->key = AWS_KEY;
  321. $this->secret_key = AWS_SECRET_KEY;
  322. return true;
  323. }
  324. // Otherwise set the values to blank and return false.
  325. else
  326. {
  327. throw new CFRuntime_Exception('No valid credentials were used to authenticate with AWS.');
  328. }
  329. }
  330. /**
  331. * Handle session-based authentication for services that support it.
  332. *
  333. * @param string $key (Optional) Your AWS key, or a session key. If blank, it will look for the <code>AWS_KEY</code> constant.
  334. * @param string $secret_key (Optional) Your AWS secret key, or a session secret key. If blank, it will look for the <code>AWS_SECRET_KEY</code> constant.
  335. * @param string $token (optional) An AWS session token. If blank, a request will be made to the AWS Secure Token Service to fetch a set of session credentials.
  336. * @return boolean A value of `false` if no valid values are set, otherwise `true`.
  337. */
  338. public function session_based_auth($key = null, $secret_key = null, $token = null)
  339. {
  340. // Instantiate the utilities class.
  341. $this->util = new $this->utilities_class();
  342. // Use 'em if we've got 'em
  343. if ($key && $secret_key && $token)
  344. {
  345. $this->key = $key;
  346. $this->secret_key = $secret_key;
  347. $this->auth_token = $token;
  348. return true;
  349. }
  350. else
  351. {
  352. if (!$key && !defined('AWS_KEY'))
  353. {
  354. // @codeCoverageIgnoreStart
  355. throw new CFRuntime_Exception('No account key was passed into the constructor, nor was it set in the AWS_KEY constant.');
  356. // @codeCoverageIgnoreEnd
  357. }
  358. if (!$secret_key && !defined('AWS_SECRET_KEY'))
  359. {
  360. // @codeCoverageIgnoreStart
  361. throw new CFRuntime_Exception('No account secret was passed into the constructor, nor was it set in the AWS_SECRET_KEY constant.');
  362. // @codeCoverageIgnoreEnd
  363. }
  364. // If both a key and secret key are passed in, use those.
  365. if ($key && $secret_key)
  366. {
  367. $this->key = $key;
  368. $this->secret_key = $secret_key;
  369. }
  370. // If neither are passed in, look for the constants instead.
  371. elseif (defined('AWS_KEY') && defined('AWS_SECRET_KEY'))
  372. {
  373. $this->key = AWS_KEY;
  374. $this->secret_key = AWS_SECRET_KEY;
  375. }
  376. // Determine storage type.
  377. $this->set_cache_config(AWS_DEFAULT_CACHE_CONFIG);
  378. $cache_class = $this->cache_class;
  379. $cache_object = new $cache_class('aws_active_session_credentials_' . get_class($this) . '_' . $this->key, AWS_DEFAULT_CACHE_CONFIG, 3600); // AWS_DEFAULT_CACHE_CONFIG only matters if it's a file system path.
  380. // Fetch session credentials
  381. $session_credentials = $cache_object->response_manager(array($this, 'cache_token'), array($this->key, $this->secret_key));
  382. $this->auth_token = $session_credentials['SessionToken'];
  383. // If both a key and secret key are passed in, use those.
  384. if (isset($session_credentials['AccessKeyId']) && isset($session_credentials['SecretAccessKey']))
  385. {
  386. $this->key = $session_credentials['AccessKeyId'];
  387. $this->secret_key = $session_credentials['SecretAccessKey'];
  388. return true;
  389. }
  390. // Otherwise set the values to blank and return false.
  391. else
  392. {
  393. throw new CFRuntime_Exception('No valid credentials were used to authenticate with AWS.');
  394. }
  395. }
  396. }
  397. /**
  398. * The callback function that is executed while caching the session credentials.
  399. *
  400. * @param string $key (Optional) Your AWS key, or a session key. If blank, it will look for the <code>AWS_KEY</code> constant.
  401. * @param string $secret_key (Optional) Your AWS secret key, or a session secret key. If blank, it will look for the <code>AWS_SECRET_KEY</code> constant.
  402. * @return mixed The data to be cached or null.
  403. */
  404. public function cache_token($key, $secret_key)
  405. {
  406. $token = new AmazonSTS($key, $secret_key);
  407. $response = $token->get_session_token();
  408. if ($response->isOK())
  409. {
  410. /*
  411. Array
  412. (
  413. [AccessKeyId] => ******
  414. [Expiration] => ******
  415. [SecretAccessKey] => ******
  416. [SessionToken] => ******
  417. )
  418. */
  419. return $response->body->GetSessionTokenResult->Credentials->to_array()->getArrayCopy();
  420. }
  421. return null;
  422. }
  423. /**
  424. * Alternate approach to constructing a new instance. Supports chaining.
  425. *
  426. * @param string $key (Optional) Your AWS key, or a session key. If blank, it will look for the <code>AWS_KEY</code> constant.
  427. * @param string $secret_key (Optional) Your AWS secret key, or a session secret key. If blank, it will look for the <code>AWS_SECRET_KEY</code> constant.
  428. * @param string $token (optional) An AWS session token. If blank, a request will be made to the AWS Secure Token Service to fetch a set of session credentials.
  429. * @return boolean A value of `false` if no valid values are set, otherwise `true`.
  430. */
  431. public static function init($key = null, $secret_key = null, $token = null)
  432. {
  433. if (version_compare(PHP_VERSION, '5.3.0', '<'))
  434. {
  435. throw new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::init().');
  436. }
  437. $self = get_called_class();
  438. return new $self($key, $secret_key, $token);
  439. }
  440. /*%******************************************************************************************%*/
  441. // MAGIC METHODS
  442. /**
  443. * A magic method that allows `camelCase` method names to be translated into `snake_case` names.
  444. *
  445. * @param string $name (Required) The name of the method.
  446. * @param array $arguments (Required) The arguments passed to the method.
  447. * @return mixed The results of the intended method.
  448. */
  449. public function __call($name, $arguments)
  450. {
  451. // Convert camelCase method calls to snake_case.
  452. $method_name = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name));
  453. if (method_exists($this, $method_name))
  454. {
  455. return call_user_func_array(array($this, $method_name), $arguments);
  456. }
  457. throw new CFRuntime_Exception('The method ' . $name . '() is undefined. Attempted to map to ' . $method_name . '() which is also undefined. Error occurred');
  458. }
  459. /*%******************************************************************************************%*/
  460. // SET CUSTOM SETTINGS
  461. /**
  462. * Adjusts the current time. Use this method for occasions when a server is out of sync with Amazon
  463. * servers.
  464. *
  465. * @param integer $seconds (Required) The number of seconds to adjust the sent timestamp by.
  466. * @return $this A reference to the current instance.
  467. */
  468. public function adjust_offset($seconds)
  469. {
  470. $this->adjust_offset = $seconds;
  471. return $this;
  472. }
  473. /**
  474. * Set the proxy settings to use.
  475. *
  476. * @param string $proxy (Required) Accepts proxy credentials in the following format: `proxy://user:pass@hostname:port`
  477. * @return $this A reference to the current instance.
  478. */
  479. public function set_proxy($proxy)
  480. {
  481. $this->proxy = $proxy;
  482. return $this;
  483. }
  484. /**
  485. * Set the hostname to connect to. This is useful for alternate services that are API-compatible with
  486. * AWS, but run from a different hostname.
  487. *
  488. * @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.
  489. * @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.
  490. * @return $this A reference to the current instance.
  491. */
  492. public function set_hostname($hostname, $port_number = null)
  493. {
  494. if ($this->override_hostname)
  495. {
  496. $this->hostname = $hostname;
  497. if ($port_number)
  498. {
  499. $this->port_number = $port_number;
  500. $this->hostname .= ':' . (string) $this->port_number;
  501. }
  502. }
  503. return $this;
  504. }
  505. /**
  506. * Set the resource prefix to use. This method is useful for alternate services that are API-compatible
  507. * with AWS.
  508. *
  509. * @param string $prefix (Required) An alternate prefix to prepend to the resource path. Useful for mock or test applications.
  510. * @return $this A reference to the current instance.
  511. */
  512. public function set_resource_prefix($prefix)
  513. {
  514. $this->resource_prefix = $prefix;
  515. return $this;
  516. }
  517. /**
  518. * Disables any subsequent use of the <set_hostname()> method.
  519. *
  520. * @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`.
  521. * @return $this A reference to the current instance.
  522. */
  523. public function allow_hostname_override($override = true)
  524. {
  525. $this->override_hostname = $override;
  526. return $this;
  527. }
  528. /**
  529. * Disables SSL/HTTPS connections for hosts that don't support them. Some services, however, still
  530. * require SSL support.
  531. *
  532. * This method will throw a user warning when invoked, which can be hidden by changing your
  533. * <php:error_reporting()> settings.
  534. *
  535. * @return $this A reference to the current instance.
  536. */
  537. public function disable_ssl()
  538. {
  539. trigger_error('Disabling SSL connections is potentially unsafe and highly discouraged.', E_USER_WARNING);
  540. $this->use_ssl = false;
  541. return $this;
  542. }
  543. /**
  544. * Disables the verification of the SSL Certificate Authority. Doing so can enable an attacker to carry
  545. * out a man-in-the-middle attack.
  546. *
  547. * https://secure.wikimedia.org/wikipedia/en/wiki/Man-in-the-middle_attack
  548. *
  549. * This method will throw a user warning when invoked, which can be hidden by changing your
  550. * <php:error_reporting()> settings.
  551. *
  552. * @return $this A reference to the current instance.
  553. */
  554. public function disable_ssl_verification($ssl_verification = false)
  555. {
  556. 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);
  557. $this->ssl_verification = $ssl_verification;
  558. return $this;
  559. }
  560. /**
  561. * Enables HTTP request/response header logging to `STDERR`.
  562. *
  563. * @param boolean $enabled (Optional) Whether or not to enable debug mode. Defaults to `true`.
  564. * @return $this A reference to the current instance.
  565. */
  566. public function enable_debug_mode($enabled = true)
  567. {
  568. $this->debug_mode = $enabled;
  569. return $this;
  570. }
  571. /**
  572. * Sets the maximum number of times to retry failed requests.
  573. *
  574. * @param integer $retries (Optional) The maximum number of times to retry failed requests. Defaults to `3`.
  575. * @return $this A reference to the current instance.
  576. */
  577. public function set_max_retries($retries = 3)
  578. {
  579. $this->max_retries = $retries;
  580. return $this;
  581. }
  582. /**
  583. * Set the caching configuration to use for response caching.
  584. *
  585. * @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>
  586. * @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`.
  587. * @return $this A reference to the current instance.
  588. */
  589. public function set_cache_config($location, $gzip = true)
  590. {
  591. // If we have an array, we're probably passing in Memcached servers and ports.
  592. if (is_array($location))
  593. {
  594. $this->cache_class = 'CacheMC';
  595. }
  596. else
  597. {
  598. // I would expect locations like `/tmp/cache`, `pdo.mysql://user:pass@hostname:port`, `pdo.sqlite:memory:`, and `apc`.
  599. $type = strtolower(substr($location, 0, 3));
  600. switch ($type)
  601. {
  602. case 'apc':
  603. $this->cache_class = 'CacheAPC';
  604. break;
  605. case 'xca': // First three letters of `xcache`
  606. $this->cache_class = 'CacheXCache';
  607. break;
  608. case 'pdo':
  609. $this->cache_class = 'CachePDO';
  610. $location = substr($location, 4);
  611. break;
  612. default:
  613. $this->cache_class = 'CacheFile';
  614. break;
  615. }
  616. }
  617. // Set the remaining cache information.
  618. $this->cache_location = $location;
  619. $this->cache_compress = $gzip;
  620. return $this;
  621. }
  622. /**
  623. * Register a callback function to execute whenever a data stream is read from using
  624. * <CFRequest::streaming_read_callback()>.
  625. *
  626. * The user-defined callback function should accept three arguments:
  627. *
  628. * <ul>
  629. * <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
  630. * <li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>
  631. * <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
  632. * </ul>
  633. *
  634. * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
  635. * <li>The name of a global function to execute, passed as a string.</li>
  636. * <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
  637. * <li>An anonymous function (PHP 5.3+).</li></ul>
  638. * @return $this A reference to the current instance.
  639. */
  640. public function register_streaming_read_callback($callback)
  641. {
  642. $this->registered_streaming_read_callback = $callback;
  643. return $this;
  644. }
  645. /**
  646. * Register a callback function to execute whenever a data stream is written to using
  647. * <CFRequest::streaming_write_callback()>.
  648. *
  649. * The user-defined callback function should accept two arguments:
  650. *
  651. * <ul>
  652. * <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
  653. * <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
  654. * </ul>
  655. *
  656. * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
  657. * <li>The name of a global function to execute, passed as a string.</li>
  658. * <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
  659. * <li>An anonymous function (PHP 5.3+).</li></ul>
  660. * @return $this A reference to the current instance.
  661. */
  662. public function register_streaming_write_callback($callback)
  663. {
  664. $this->registered_streaming_write_callback = $callback;
  665. return $this;
  666. }
  667. /*%******************************************************************************************%*/
  668. // SET CUSTOM CLASSES
  669. /**
  670. * Set a custom class for this functionality. Use this method when extending/overriding existing classes
  671. * with new functionality.
  672. *
  673. * The replacement class must extend from <CFUtilities>.
  674. *
  675. * @param string $class (Optional) The name of the new class to use for this functionality.
  676. * @return $this A reference to the current instance.
  677. */
  678. public function set_utilities_class($class = 'CFUtilities')
  679. {
  680. $this->utilities_class = $class;
  681. $this->util = new $this->utilities_class();
  682. return $this;
  683. }
  684. /**
  685. * Set a custom class for this functionality. Use this method when extending/overriding existing classes
  686. * with new functionality.
  687. *
  688. * The replacement class must extend from <CFRequest>.
  689. *
  690. * @param string $class (Optional) The name of the new class to use for this functionality.
  691. * @param $this A reference to the current instance.
  692. */
  693. public function set_request_class($class = 'CFRequest')
  694. {
  695. $this->request_class = $class;
  696. return $this;
  697. }
  698. /**
  699. * Set a custom class for this functionality. Use this method when extending/overriding existing classes
  700. * with new functionality.
  701. *
  702. * The replacement class must extend from <CFResponse>.
  703. *
  704. * @param string $class (Optional) The name of the new class to use for this functionality.
  705. * @return $this A reference to the current instance.
  706. */
  707. public function set_response_class($class = 'CFResponse')
  708. {
  709. $this->response_class = $class;
  710. return $this;
  711. }
  712. /**
  713. * Set a custom class for this functionality. Use this method when extending/overriding existing classes
  714. * with new functionality.
  715. *
  716. * The replacement class must extend from <CFSimpleXML>.
  717. *
  718. * @param string $class (Optional) The name of the new class to use for this functionality.
  719. * @return $this A reference to the current instance.
  720. */
  721. public function set_parser_class($class = 'CFSimpleXML')
  722. {
  723. $this->parser_class = $class;
  724. return $this;
  725. }
  726. /**
  727. * Set a custom class for this functionality. Use this method when extending/overriding existing classes
  728. * with new functionality.
  729. *
  730. * The replacement class must extend from <CFBatchRequest>.
  731. *
  732. * @param string $class (Optional) The name of the new class to use for this functionality.
  733. * @return $this A reference to the current instance.
  734. */
  735. public function set_batch_class($class = 'CFBatchRequest')
  736. {
  737. $this->batch_class = $class;
  738. return $this;
  739. }
  740. /*%******************************************************************************************%*/
  741. // AUTHENTICATION
  742. /**
  743. * Default, shared method for authenticating a connection to AWS. Overridden on a class-by-class basis
  744. * as necessary.
  745. *
  746. * @param string $action (Required) Indicates the action to perform.
  747. * @param array $opt (Optional) An associative array of parameters for authenticating. See the individual methods for allowed keys.
  748. * @param string $domain (Optional) The URL of the queue to perform the action on.
  749. * @param integer $signature_version (Optional) The signature version to use. Defaults to 2.
  750. * @param integer $redirects (Do Not Use) Used internally by this function on occasions when Amazon S3 returns a redirect code and it needs to call itself recursively.
  751. * @return CFResponse Object containing a parsed HTTP response.
  752. */
  753. public function authenticate($action, $opt = null, $domain = null, $signature_version = 2, $redirects = 0)
  754. {
  755. // Handle nulls
  756. if (is_null($signature_version))
  757. {
  758. $signature_version = 2;
  759. }
  760. $method_arguments = func_get_args();
  761. $headers = array();
  762. $signed_headers = array();
  763. // Use the caching flow to determine if we need to do a round-trip to the server.
  764. if ($this->use_cache_flow)
  765. {
  766. // Generate an identifier specific to this particular set of arguments.
  767. $cache_id = $this->key . '_' . get_class($this) . '_' . $action . '_' . sha1(serialize($method_arguments));
  768. // Instantiate the appropriate caching object.
  769. $this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
  770. if ($this->delete_cache)
  771. {
  772. $this->use_cache_flow = false;
  773. $this->delete_cache = false;
  774. return $this->cache_object->delete();
  775. }
  776. // Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
  777. $data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments);
  778. // Parse the XML body
  779. $data = $this->parse_callback($data);
  780. // End!
  781. return $data;
  782. }
  783. $return_curl_handle = false;
  784. $x_amz_target = null;
  785. // Do we have a custom resource prefix?
  786. if ($this->resource_prefix)
  787. {
  788. $domain .= $this->resource_prefix;
  789. }
  790. // Determine signing values
  791. $current_time = time() + $this->adjust_offset;
  792. $date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, $current_time);
  793. $timestamp = gmdate(CFUtilities::DATE_FORMAT_ISO8601, $current_time);
  794. $nonce = $this->util->generate_guid();
  795. // Do we have an authentication token?
  796. if ($this->auth_token)
  797. {
  798. $headers['X-Amz-Security-Token'] = $this->auth_token;
  799. $query['SecurityToken'] = $this->auth_token;
  800. }
  801. // Manage the key-value pairs that are used in the query.
  802. if (stripos($action, 'x-amz-target') !== false)
  803. {
  804. $x_amz_target = trim(str_ireplace('x-amz-target:', '', $action));
  805. }
  806. else
  807. {
  808. $query['Action'] = $action;
  809. }
  810. // Only add it if it exists.
  811. if ($this->api_version)
  812. {
  813. $query['Version'] = $this->api_version;
  814. }
  815. // Only Signature v2
  816. if ($signature_version === 2)
  817. {
  818. $query['AWSAccessKeyId'] = $this->key;
  819. $query['SignatureMethod'] = 'HmacSHA256';
  820. $query['SignatureVersion'] = 2;
  821. $query['Timestamp'] = $timestamp;
  822. }
  823. $curlopts = array();
  824. // Set custom CURLOPT settings
  825. if (is_array($opt) && isset($opt['curlopts']))
  826. {
  827. $curlopts = $opt['curlopts'];
  828. unset($opt['curlopts']);
  829. }
  830. // Merge in any options that were passed in
  831. if (is_array($opt))
  832. {
  833. $query = array_merge($query, $opt);
  834. }
  835. $return_curl_handle = isset($query['returnCurlHandle']) ? $query['returnCurlHandle'] : false;
  836. unset($query['returnCurlHandle']);
  837. // Do a case-sensitive, natural order sort on the array keys.
  838. uksort($query, 'strcmp');
  839. // Normalize JSON input
  840. if (isset($query['body']) && $query['body'] === '[]')
  841. {
  842. $query['body'] = '{}';
  843. }
  844. if ($this->use_aws_query)
  845. {
  846. // Create the string that needs to be hashed.
  847. $canonical_query_string = $this->util->to_signable_string($query);
  848. }
  849. else
  850. {
  851. // Create the string that needs to be hashed.
  852. $canonical_query_string = $this->util->encode_signature2($query['body']);
  853. }
  854. // Remove the default scheme from the domain.
  855. $domain = str_replace(array('http://', 'https://'), '', $domain);
  856. // Parse our request.
  857. $parsed_url = parse_url('http://' . $domain);
  858. // Set the proper host header.
  859. if (isset($parsed_url['port']) && (integer) $parsed_url['port'] !== 80 && (integer) $parsed_url['port'] !== 443)
  860. {
  861. $host_header = strtolower($parsed_url['host']) . ':' . $parsed_url['port'];
  862. }
  863. else
  864. {
  865. $host_header = strtolower($parsed_url['host']);
  866. }
  867. // Set the proper request URI.
  868. $request_uri = isset($parsed_url['path']) ? $parsed_url['path'] : '/';
  869. if ($signature_version === 2)
  870. {
  871. // Prepare the string to sign
  872. $string_to_sign = "POST\n$host_header\n$request_uri\n$canonical_query_string";
  873. // Hash the AWS secret key and generate a signature for the request.
  874. $query['Signature'] = base64_encode(hash_hmac('sha256', $string_to_sign, $this->secret_key, true));
  875. }
  876. // Generate the querystring from $query
  877. $querystring = $this->util->to_query_string($query);
  878. // Gather information to pass along to other classes.
  879. $helpers = array(
  880. 'utilities' => $this->utilities_class,
  881. 'request' => $this->request_class,
  882. 'response' => $this->response_class,
  883. );
  884. // Compose the request.
  885. $request_url = ($this->use_ssl ? 'https://' : 'http://') . $domain;
  886. $request_url .= !isset($parsed_url['path']) ? '/' : '';
  887. // Instantiate the request class
  888. $request = new $this->request_class($request_url, $this->proxy, $helpers);
  889. $request->set_method('POST');
  890. $request->set_body($querystring);
  891. $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  892. // Signing using X-Amz-Target is handled differently.
  893. if ($signature_version === 3 && $x_amz_target)
  894. {
  895. $headers['X-Amz-Target'] = $x_amz_target;
  896. $headers['Content-Type'] = 'application/json; amzn-1.0';
  897. $headers['Content-Encoding'] = 'amz-1.0';
  898. $request->set_body($query['body']);
  899. $querystring = $query['body'];
  900. }
  901. // Pass along registered stream callbacks
  902. if ($this->registered_streaming_read_callback)
  903. {
  904. $request->register_streaming_read_callback($this->registered_streaming_read_callback);
  905. }
  906. if ($this->registered_streaming_write_callback)
  907. {
  908. $request->register_streaming_write_callback($this->registered_streaming_write_callback);
  909. }
  910. // Add authentication headers
  911. if ($signature_version === 3)
  912. {
  913. $headers['X-Amz-Nonce'] = $nonce;
  914. $headers['Date'] = $date;
  915. $headers['Content-Length'] = strlen($querystring);
  916. $headers['Content-MD5'] = $this->util->hex_to_base64(md5($querystring));
  917. $headers['Host'] = $host_header;
  918. }
  919. // Sort headers
  920. uksort($headers, 'strnatcasecmp');
  921. if ($signature_version === 3 && $this->use_ssl)
  922. {
  923. // Prepare the string to sign (HTTPS)
  924. $string_to_sign = $date . $nonce;
  925. }
  926. elseif ($signature_version === 3 && !$this->use_ssl)
  927. {
  928. // Prepare the string to sign (HTTP)
  929. $string_to_sign = "POST\n$request_uri\n\n";
  930. }
  931. // Add headers to request and compute the string to sign
  932. foreach ($headers as $header_key => $header_value)
  933. {
  934. // Strip linebreaks from header values as they're illegal and can allow for security issues
  935. $header_value = str_replace(array("\r", "\n"), '', $header_value);
  936. // Add the header if it has a value
  937. if ($header_value !== '')
  938. {
  939. $request->add_header($header_key, $header_value);
  940. }
  941. // Signature v3 over HTTP
  942. if ($signature_version === 3 && !$this->use_ssl)
  943. {
  944. // Generate the string to sign
  945. if (
  946. substr(strtolower($header_key), 0, 8) === 'content-' ||
  947. strtolower($header_key) === 'date' ||
  948. strtolower($header_key) === 'expires' ||
  949. strtolower($header_key) === 'host' ||
  950. substr(strtolower($header_key), 0, 6) === 'x-amz-'
  951. )
  952. {
  953. $string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n";
  954. $signed_headers[] = $header_key;
  955. }
  956. }
  957. }
  958. if ($signature_version === 3)
  959. {
  960. if (!$this->use_ssl)
  961. {
  962. $string_to_sign .= "\n";
  963. if (isset($query['body']) && $query['body'] !== '')
  964. {
  965. $string_to_sign .= $query['body'];
  966. }
  967. // Convert from string-to-sign to bytes-to-sign
  968. $bytes_to_sign = hash('sha256', $string_to_sign, true);
  969. // Hash the AWS secret key and generate a signature for the request.
  970. $signature = base64_encode(hash_hmac('sha256', $bytes_to_sign, $this->secret_key, true));
  971. }
  972. else
  973. {
  974. // Hash the AWS secret key and generate a signature for the request.
  975. $signature = base64_encode(hash_hmac('sha256', $string_to_sign, $this->secret_key, true));
  976. }
  977. $headers['X-Amzn-Authorization'] = 'AWS3' . ($this->use_ssl ? '-HTTPS' : '')
  978. . ' AWSAccessKeyId=' . $this->key
  979. . ',Algorithm=HmacSHA256'
  980. . ',SignedHeaders=' . implode(';', $signed_headers)
  981. . ',Signature=' . $signature;
  982. $request->add_header('X-Amzn-Authorization', $headers['X-Amzn-Authorization']);
  983. }
  984. // Update RequestCore settings
  985. $request->request_class = $this->request_class;
  986. $request->response_class = $this->response_class;
  987. $request->ssl_verification = $this->ssl_verification;
  988. // Debug mode
  989. if ($this->debug_mode)
  990. {
  991. $request->debug_mode = $this->debug_mode;
  992. }
  993. if (count($curlopts))
  994. {
  995. $request->set_curlopts($curlopts);
  996. }
  997. // Manage the (newer) batch request API or the (older) returnCurlHandle setting.
  998. if ($this->use_batch_flow)
  999. {
  1000. $handle = $request->prep_request();
  1001. $this->batch_object->add($handle);
  1002. $this->use_batch_flow = false;
  1003. return $handle;
  1004. }
  1005. elseif ($return_curl_handle)
  1006. {
  1007. return $request->prep_request();
  1008. }
  1009. // Send!
  1010. $request->send_request();
  1011. $request_headers = $headers;
  1012. // Prepare the response.
  1013. $headers = $request->get_response_header();
  1014. $headers['x-aws-stringtosign'] = $string_to_sign;
  1015. $headers['x-aws-request-headers'] = $request_headers;
  1016. $headers['x-aws-body'] = $querystring;
  1017. $data = new $this->response_class($headers, $this->parse_callback($request->get_response_body(), $headers), $request->get_response_code());
  1018. // Was it Amazon's fault the request failed? Retry the request until we reach $max_retries.
  1019. if ((integer) $request->get_response_code() === 500 || (integer) $request->get_response_code() === 503)
  1020. {
  1021. if ($redirects <= $this->max_retries)
  1022. {
  1023. // Exponential backoff
  1024. $delay = (integer) (pow(4, $redirects) * 100000);
  1025. usleep($delay);
  1026. $data = $this->authenticate($action, $opt, $domain, $signature_version, ++$redirects);
  1027. }
  1028. }
  1029. return $data;
  1030. }
  1031. /*%******************************************************************************************%*/
  1032. // BATCH REQUEST LAYER
  1033. /**
  1034. * Specifies that the intended request should be queued for a later batch request.
  1035. *
  1036. * @param CFBatchRequest $queue (Optional) The <CFBatchRequest> instance to use for managing batch requests. If not available, it generates a new instance of <CFBatchRequest>.
  1037. * @return $this A reference to the current instance.
  1038. */
  1039. public function batch(CFBatchRequest &$queue = null)
  1040. {
  1041. if ($queue)
  1042. {
  1043. $this->batch_object = $queue;
  1044. }
  1045. elseif ($this->internal_batch_object)
  1046. {
  1047. $this->batch_object = &$this->internal_batch_object;
  1048. }
  1049. else
  1050. {
  1051. $this->internal_batch_object = new $this->batch_class();
  1052. $this->batch_object = &$this->internal_batch_object;
  1053. }
  1054. $this->use_batch_flow = true;
  1055. return $this;
  1056. }
  1057. /**
  1058. * Executes the batch request queue by sending all queued requests.
  1059. *
  1060. * @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.
  1061. * @return array An array of <CFResponse> objects.
  1062. */
  1063. public function send($clear_after_send = true)
  1064. {
  1065. if ($this->use_batch_flow)
  1066. {
  1067. // When we send the request, disable batch flow.
  1068. $this->use_batch_flow = false;
  1069. // If we're not caching, simply send the request.
  1070. if (!$this->use_cache_flow)
  1071. {
  1072. $response = $this->batch_object->send();
  1073. $parsed_data = array_map(array($this, 'parse_callback'), $response);
  1074. $parsed_data = new CFArray($parsed_data);
  1075. // Clear the queue
  1076. if ($clear_after_send)
  1077. {
  1078. $this->batch_object->queue = array();
  1079. }
  1080. return $parsed_data;
  1081. }
  1082. // Generate an identifier specific to this particular set of arguments.
  1083. $cache_id = $this->key . '_' . get_class($this) . '_' . sha1(serialize($this->batch_object));
  1084. // Instantiate the appropriate caching object.
  1085. $this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
  1086. if ($this->delete_cache)
  1087. {
  1088. $this->use_cache_flow = false;
  1089. $this->delete_cache = false;
  1090. return $this->cache_object->delete();
  1091. }
  1092. // Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
  1093. $data_set = $this->cache_object->response_manager(array($this, 'cache_callback_batch'), array($this->batch_object));
  1094. $parsed_data = array_map(array($this, 'parse_callback'), $data_set);
  1095. $parsed_data = new CFArray($parsed_data);
  1096. // Clear the queue
  1097. if ($clear_after_send)
  1098. {
  1099. $this->batch_object->queue = array();
  1100. }
  1101. // End!
  1102. return $parsed_data;
  1103. }
  1104. // Load the class
  1105. $null = new CFBatchRequest();
  1106. unset($null);
  1107. throw new CFBatchRequest_Exception('You must use $object->batch()->send()');
  1108. }
  1109. /**
  1110. * Parses a response body into a PHP object if appropriate.
  1111. *
  1112. * @param CFResponse|string $response (Required) The <CFResponse> object to parse, or an XML string that would otherwise be a response body.
  1113. * @param string $content_type (Optional) The content-type to use when determining how to parse the content.
  1114. * @return CFResponse|string A parsed <CFResponse> object, or parsed XML.
  1115. */
  1116. public function parse_callback($response, $headers = null)
  1117. {
  1118. // Shorten this so we have a (mostly) single code path
  1119. if (isset($response->body))
  1120. {
  1121. if (is_string($response->body))
  1122. {
  1123. $body = $response->body;
  1124. }
  1125. else
  1126. {
  1127. return $response;
  1128. }
  1129. }
  1130. elseif (is_string($response))
  1131. {
  1132. $body = $response;
  1133. }
  1134. else
  1135. {
  1136. return $response;
  1137. }
  1138. // Decompress gzipped content
  1139. if (isset($headers['content-encoding']))
  1140. {
  1141. switch (strtolower(trim($headers['content-encoding'], "\x09\x0A\x0D\x20")))
  1142. {
  1143. case 'gzip':
  1144. case 'x-gzip':
  1145. if (strpos($headers['_info']['url'], 'monitoring.') !== false)
  1146. {
  1147. // CloudWatch incorrectly uses the deflate algorithm when they say gzip.
  1148. if (($uncompressed = gzuncompress($body)) !== false)
  1149. {
  1150. $body = $uncompressed;
  1151. }
  1152. elseif (($uncompressed = gzinflate($body)) !== false)
  1153. {
  1154. $body = $uncompressed;
  1155. }
  1156. break;
  1157. }
  1158. else
  1159. {
  1160. // Everyone else uses gzip correctly.
  1161. $decoder = new CFGzipDecode($body);
  1162. if ($decoder->parse())
  1163. {
  1164. $body = $decoder->data;
  1165. }
  1166. break;
  1167. }
  1168. case 'deflate':
  1169. if (strpos($headers['_info']['url'], 'monitoring.') !== false)
  1170. {
  1171. // CloudWatchWatch incorrectly does nothing when they say deflate.
  1172. continue;
  1173. }
  1174. else
  1175. {
  1176. // Everyone else uses deflate correctly.
  1177. if (($uncompressed = gzuncompress($body)) !== false)
  1178. {
  1179. $body = $uncompressed;
  1180. }
  1181. elseif (($uncompressed = gzinflate($body)) !== false)
  1182. {
  1183. $body = $uncompressed;
  1184. }
  1185. }
  1186. break;
  1187. }
  1188. }
  1189. // Look for XML cues
  1190. if (
  1191. (isset($headers['content-type']) && ($headers['content-type'] === 'text/xml' || $headers['content-type'] === 'application/xml')) || // We know it's XML
  1192. (!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
  1193. )
  1194. {
  1195. // Strip the default XML namespace to simplify XPath expressions
  1196. $body = str_replace("xmlns=", "ns=", $body);
  1197. // Parse the XML body
  1198. $body = new $this->parser_class($body);
  1199. }
  1200. // Look for JSON cues
  1201. elseif (
  1202. (isset($headers['content-type']) && $headers['content-type'] === 'application/json') || // We know it's JSON
  1203. (!isset($headers['content-type']) && $this->util->is_json($body)) // Sniff for JSON
  1204. )
  1205. {
  1206. // Normalize JSON to a CFSimpleXML object
  1207. $body = CFJSON::to_xml($body);
  1208. }
  1209. // Put the parsed data back where it goes
  1210. if (isset($response->body))
  1211. {
  1212. $response->body = $body;
  1213. }
  1214. else
  1215. {
  1216. $response = $body;
  1217. }
  1218. return $response;
  1219. }
  1220. /*%******************************************************************************************%*/
  1221. // CACHING LAYER
  1222. /**
  1223. * Specifies that the resulting <CFResponse> object should be cached according to the settings from
  1224. * <set_cache_config()>.
  1225. *
  1226. * @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").
  1227. * @param $this A reference to the current instance.
  1228. * @return $this
  1229. */
  1230. public function cache($expires)
  1231. {
  1232. // Die if they haven't used set_cache_config().
  1233. if (!$this->cache_class)
  1234. {
  1235. throw new CFRuntime_Exception('Must call set_cache_config() before using cache()');
  1236. }
  1237. if (is_string($expires))
  1238. {
  1239. $expires = strtotime($expires);
  1240. $this->cache_expires = $expires - time();
  1241. }
  1242. elseif (is_int($expires))
  1243. {
  1244. $this->cache_expires = $expires;
  1245. }
  1246. $this->use_cache_flow = true;
  1247. return $this;
  1248. }
  1249. /**
  1250. * The callback function that is executed when the cache doesn't exist or has expired. The response of
  1251. * this method is cached. Accepts identical parameters as the <authenticate()> method. Never call this
  1252. * method directly -- it is used internally by the caching system.
  1253. *
  1254. * @param string $action (Required) Indicates the action to perform.
  1255. * @param array $opt (Optional) An associative array of parameters for authenticating. See the individual methods for allowed keys.
  1256. * @param string $domain (Optional) The URL of the queue to perform the action on.
  1257. * @param integer $signature_version (Optional) The signature version to use. Defaults to 2.
  1258. * @return CFResponse A parsed HTTP response.
  1259. */
  1260. public function cache_callback($action, $opt = null, $domain = null, $signature_version = 2)
  1261. {
  1262. // Disable the cache flow since it's already been handled.
  1263. $this->use_cache_flow = false;
  1264. // Make the request
  1265. $response = $this->authenticate($action, $opt, $domain, $signature_version);
  1266. // If this is an XML document, convert it back to a string.
  1267. if (isset($response->body) && ($response->body instanceof SimpleXMLElement))
  1268. {
  1269. $response->body = $response->body->asXML();
  1270. }
  1271. return $response;
  1272. }
  1273. /**
  1274. * Used for caching the results of a batch request. Never call this method directly; it is used
  1275. * internally by the caching system.
  1276. *
  1277. * @param CFBatchRequest $batch (Required) The batch request object to send.
  1278. * @return CFResponse A parsed HTTP response.
  1279. */
  1280. public function cache_callback_batch(CFBatchRequest $batch)
  1281. {
  1282. return $batch->send();
  1283. }
  1284. /**
  1285. * Deletes a cached <CFResponse> object using the specified cache storage type.
  1286. *
  1287. * @return boolean A value of `true` if cached object exists and is successfully deleted, otherwise `false`.
  1288. */
  1289. public function delete_cache()
  1290. {
  1291. $this->use_cache_flow = true;
  1292. $this->delete_cache = true;
  1293. return $this;
  1294. }
  1295. }
  1296. /**
  1297. * Contains the functionality for auto-loading service classes.
  1298. */
  1299. class CFLoader
  1300. {
  1301. /*%******************************************************************************************%*/
  1302. // AUTO-LOADER
  1303. /**
  1304. * Automatically load classes that aren't included.
  1305. *
  1306. * @param string $class (Required) The classname to load.
  1307. * @return void
  1308. */
  1309. public static function autoloader($class)
  1310. {
  1311. $path = dirname(__FILE__) . DIRECTORY_SEPARATOR;
  1312. // Amazon SDK classes
  1313. if (strstr($class, 'Amazon'))
  1314. {
  1315. $path .= 'services' . DIRECTORY_SEPARATOR . str_ireplace('Amazon', '', strtolower($class)) . '.class.php';
  1316. }
  1317. // Utility classes
  1318. elseif (strstr($class, 'CF'))
  1319. {
  1320. $path .= 'utilities' . DIRECTORY_SEPARATOR . str_ireplace('CF', '', strtolower($class)) . '.class.php';
  1321. }
  1322. // Load CacheCore
  1323. elseif (strstr($class, 'Cache'))
  1324. {
  1325. if (file_exists($ipath = 'lib' . DIRECTORY_SEPARATOR . 'cachecore' . DIRECTORY_SEPARATOR . 'icachecore.interface.php'))
  1326. {
  1327. require_once($ipath);
  1328. }
  1329. $path .= 'lib' . DIRECTORY_SEPARATOR . 'cachecore' . DIRECTORY_SEPARATOR . strtolower($class) . '.class.php';
  1330. }
  1331. // Load RequestCore
  1332. elseif (strstr($class, 'RequestCore') || strstr($class, 'ResponseCore'))
  1333. {
  1334. $path .= 'lib' . DIRECTORY_SEPARATOR . 'requestcore' . DIRECTORY_SEPARATOR . 'requestcore.class.php';
  1335. }
  1336. // Load Symfony YAML classes
  1337. elseif (strstr($class, 'sfYaml'))
  1338. {
  1339. $path .= 'lib' . DIRECTORY_SEPARATOR . 'yaml' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'sfYaml.php';
  1340. }
  1341. // Fall back to the 'extensions' directory.
  1342. elseif (defined('AWS_ENABLE_EXTENSIONS') && AWS_ENABLE_EXTENSIONS)
  1343. {
  1344. $path .= 'extensions' . DIRECTORY_SEPARATOR . strtolower($class) . '.class.php';
  1345. }
  1346. if (file_exists($path) && !is_dir($path))
  1347. {
  1348. require_once($path);
  1349. }
  1350. }
  1351. }
  1352. // Register the autoloader.
  1353. spl_autoload_register(array('CFLoader', 'autoloader'));