functions.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <?php
  2. namespace Aws;
  3. use Psr\Http\Message\RequestInterface;
  4. use GuzzleHttp\ClientInterface;
  5. use GuzzleHttp\Promise\FulfilledPromise;
  6. //-----------------------------------------------------------------------------
  7. // Functional functions
  8. //-----------------------------------------------------------------------------
  9. /**
  10. * Returns a function that always returns the same value;
  11. *
  12. * @param mixed $value Value to return.
  13. *
  14. * @return callable
  15. */
  16. function constantly($value)
  17. {
  18. return function () use ($value) { return $value; };
  19. }
  20. /**
  21. * Filters values that do not satisfy the predicate function $pred.
  22. *
  23. * @param mixed $iterable Iterable sequence of data.
  24. * @param callable $pred Function that accepts a value and returns true/false
  25. *
  26. * @return \Generator
  27. */
  28. function filter($iterable, callable $pred)
  29. {
  30. foreach ($iterable as $value) {
  31. if ($pred($value)) {
  32. yield $value;
  33. }
  34. }
  35. }
  36. /**
  37. * Applies a map function $f to each value in a collection.
  38. *
  39. * @param mixed $iterable Iterable sequence of data.
  40. * @param callable $f Map function to apply.
  41. *
  42. * @return \Generator
  43. */
  44. function map($iterable, callable $f)
  45. {
  46. foreach ($iterable as $value) {
  47. yield $f($value);
  48. }
  49. }
  50. /**
  51. * Creates a generator that iterates over a sequence, then iterates over each
  52. * value in the sequence and yields the application of the map function to each
  53. * value.
  54. *
  55. * @param mixed $iterable Iterable sequence of data.
  56. * @param callable $f Map function to apply.
  57. *
  58. * @return \Generator
  59. */
  60. function flatmap($iterable, callable $f)
  61. {
  62. foreach (map($iterable, $f) as $outer) {
  63. foreach ($outer as $inner) {
  64. yield $inner;
  65. }
  66. }
  67. }
  68. /**
  69. * Partitions the input sequence into partitions of the specified size.
  70. *
  71. * @param mixed $iterable Iterable sequence of data.
  72. * @param int $size Size to make each partition (except possibly the last chunk)
  73. *
  74. * @return \Generator
  75. */
  76. function partition($iterable, $size)
  77. {
  78. $buffer = [];
  79. foreach ($iterable as $value) {
  80. $buffer[] = $value;
  81. if (count($buffer) === $size) {
  82. yield $buffer;
  83. $buffer = [];
  84. }
  85. }
  86. if ($buffer) {
  87. yield $buffer;
  88. }
  89. }
  90. /**
  91. * Returns a function that invokes the provided variadic functions one
  92. * after the other until one of the functions returns a non-null value.
  93. * The return function will call each passed function with any arguments it
  94. * is provided.
  95. *
  96. * $a = function ($x, $y) { return null; };
  97. * $b = function ($x, $y) { return $x + $y; };
  98. * $fn = \Aws\or_chain($a, $b);
  99. * echo $fn(1, 2); // 3
  100. *
  101. * @return callable
  102. */
  103. function or_chain()
  104. {
  105. $fns = func_get_args();
  106. return function () use ($fns) {
  107. $args = func_get_args();
  108. foreach ($fns as $fn) {
  109. $result = $args ? call_user_func_array($fn, $args) : $fn();
  110. if ($result) {
  111. return $result;
  112. }
  113. }
  114. return null;
  115. };
  116. }
  117. //-----------------------------------------------------------------------------
  118. // JSON compiler and loading functions
  119. //-----------------------------------------------------------------------------
  120. /**
  121. * Loads a compiled JSON file from a PHP file.
  122. *
  123. * If the JSON file has not been cached to disk as a PHP file, it will be loaded
  124. * from the JSON source file and returned.
  125. *
  126. * @param string $path Path to the JSON file on disk
  127. *
  128. * @return mixed Returns the JSON decoded data. Note that JSON objects are
  129. * decoded as associative arrays.
  130. */
  131. function load_compiled_json($path)
  132. {
  133. $compiledFilepath = "{$path}.php";
  134. if (is_readable($compiledFilepath)) {
  135. return include($compiledFilepath);
  136. }
  137. if (!file_exists($path)) {
  138. throw new \InvalidArgumentException(
  139. sprintf("File not found: %s", $path)
  140. );
  141. }
  142. return json_decode(file_get_contents($path), true);
  143. }
  144. /**
  145. * No-op
  146. */
  147. function clear_compiled_json()
  148. {
  149. // pass
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Directory iterator functions.
  153. //-----------------------------------------------------------------------------
  154. /**
  155. * Iterates over the files in a directory and works with custom wrappers.
  156. *
  157. * @param string $path Path to open (e.g., "s3://foo/bar").
  158. * @param resource $context Stream wrapper context.
  159. *
  160. * @return \Generator Yields relative filename strings.
  161. */
  162. function dir_iterator($path, $context = null)
  163. {
  164. $dh = $context ? opendir($path, $context) : opendir($path);
  165. if (!$dh) {
  166. throw new \InvalidArgumentException('File not found: ' . $path);
  167. }
  168. while (($file = readdir($dh)) !== false) {
  169. yield $file;
  170. }
  171. closedir($dh);
  172. }
  173. /**
  174. * Returns a recursive directory iterator that yields absolute filenames.
  175. *
  176. * This iterator is not broken like PHP's built-in DirectoryIterator (which
  177. * will read the first file from a stream wrapper, then rewind, then read
  178. * it again).
  179. *
  180. * @param string $path Path to traverse (e.g., s3://bucket/key, /tmp)
  181. * @param resource $context Stream context options.
  182. *
  183. * @return \Generator Yields absolute filenames.
  184. */
  185. function recursive_dir_iterator($path, $context = null)
  186. {
  187. $invalid = ['.' => true, '..' => true];
  188. $pathLen = strlen($path) + 1;
  189. $iterator = dir_iterator($path, $context);
  190. $queue = [];
  191. do {
  192. while ($iterator->valid()) {
  193. $file = $iterator->current();
  194. $iterator->next();
  195. if (isset($invalid[basename($file)])) {
  196. continue;
  197. }
  198. $fullPath = "{$path}/{$file}";
  199. yield $fullPath;
  200. if (is_dir($fullPath)) {
  201. $queue[] = $iterator;
  202. $iterator = map(
  203. dir_iterator($fullPath, $context),
  204. function ($file) use ($fullPath, $pathLen) {
  205. return substr("{$fullPath}/{$file}", $pathLen);
  206. }
  207. );
  208. continue;
  209. }
  210. }
  211. $iterator = array_pop($queue);
  212. } while ($iterator);
  213. }
  214. //-----------------------------------------------------------------------------
  215. // Misc. functions.
  216. //-----------------------------------------------------------------------------
  217. /**
  218. * Debug function used to describe the provided value type and class.
  219. *
  220. * @param mixed $input
  221. *
  222. * @return string Returns a string containing the type of the variable and
  223. * if a class is provided, the class name.
  224. */
  225. function describe_type($input)
  226. {
  227. switch (gettype($input)) {
  228. case 'object':
  229. return 'object(' . get_class($input) . ')';
  230. case 'array':
  231. return 'array(' . count($input) . ')';
  232. default:
  233. ob_start();
  234. var_dump($input);
  235. // normalize float vs double
  236. return str_replace('double(', 'float(', rtrim(ob_get_clean()));
  237. }
  238. }
  239. /**
  240. * Creates a default HTTP handler based on the available clients.
  241. *
  242. * @return callable
  243. */
  244. function default_http_handler()
  245. {
  246. $version = (string) ClientInterface::VERSION;
  247. if ($version[0] === '5') {
  248. return new \Aws\Handler\GuzzleV5\GuzzleHandler();
  249. }
  250. if ($version[0] === '6') {
  251. return new \Aws\Handler\GuzzleV6\GuzzleHandler();
  252. }
  253. throw new \RuntimeException('Unknown Guzzle version: ' . $version);
  254. }
  255. /**
  256. * Gets the default user agent string depending on the Guzzle version
  257. *
  258. * @return string
  259. */
  260. function default_user_agent()
  261. {
  262. $version = (string) ClientInterface::VERSION;
  263. if ($version[0] === '5') {
  264. return \GuzzleHttp\Client::getDefaultUserAgent();
  265. }
  266. if ($version[0] === '6') {
  267. return \GuzzleHttp\default_user_agent();
  268. }
  269. throw new \RuntimeException('Unknown Guzzle version: ' . $version);
  270. }
  271. /**
  272. * Serialize a request for a command but do not send it.
  273. *
  274. * Returns a promise that is fulfilled with the serialized request.
  275. *
  276. * @param CommandInterface $command Command to serialize.
  277. *
  278. * @return RequestInterface
  279. * @throws \RuntimeException
  280. */
  281. function serialize(CommandInterface $command)
  282. {
  283. $request = null;
  284. $handlerList = $command->getHandlerList();
  285. // Return a mock result.
  286. $handlerList->setHandler(
  287. function (CommandInterface $_, RequestInterface $r) use (&$request) {
  288. $request = $r;
  289. return new FulfilledPromise(new Result([]));
  290. }
  291. );
  292. call_user_func($handlerList->resolve(), $command)->wait();
  293. if (!$request instanceof RequestInterface) {
  294. throw new \RuntimeException(
  295. 'Calling handler did not serialize request'
  296. );
  297. }
  298. return $request;
  299. }
  300. /**
  301. * Retrieves data for a service from the SDK's service manifest file.
  302. *
  303. * Manifest data is stored statically, so it does not need to be loaded more
  304. * than once per process. The JSON data is also cached in opcache.
  305. *
  306. * @param string $service Case-insensitive namespace or endpoint prefix of the
  307. * service for which you are retrieving manifest data.
  308. *
  309. * @return array
  310. * @throws \InvalidArgumentException if the service is not supported.
  311. */
  312. function manifest($service = null)
  313. {
  314. // Load the manifest and create aliases for lowercased namespaces
  315. static $manifest = [];
  316. static $aliases = [];
  317. if (empty($manifest)) {
  318. $manifest = load_compiled_json(__DIR__ . '/data/manifest.json');
  319. foreach ($manifest as $endpoint => $info) {
  320. $alias = strtolower($info['namespace']);
  321. if ($alias !== $endpoint) {
  322. $aliases[$alias] = $endpoint;
  323. }
  324. }
  325. }
  326. // If no service specified, then return the whole manifest.
  327. if ($service === null) {
  328. return $manifest;
  329. }
  330. // Look up the service's info in the manifest data.
  331. $service = strtolower($service);
  332. if (isset($manifest[$service])) {
  333. return $manifest[$service] + ['endpoint' => $service];
  334. }
  335. if (isset($aliases[$service])) {
  336. return manifest($aliases[$service]);
  337. }
  338. throw new \InvalidArgumentException(
  339. "The service \"{$service}\" is not provided by the AWS SDK for PHP."
  340. );
  341. }
  342. /**
  343. * Checks if supplied parameter is a valid hostname
  344. *
  345. * @param string $hostname
  346. * @return bool
  347. */
  348. function is_valid_hostname($hostname)
  349. {
  350. return (
  351. preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*\.?$/i", $hostname)
  352. && preg_match("/^.{1,253}$/", $hostname)
  353. && preg_match("/^[^\.]{1,63}(\.[^\.]{0,63})*$/", $hostname)
  354. );
  355. }