CFRuntime

Core functionality and default settings shared across all SDK classes.

Defined (1)

The class is defined in the following location(s).

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