functions.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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. if ($compiled = @include("$path.php")) {
  134. return $compiled;
  135. }
  136. if (!file_exists($path)) {
  137. throw new \InvalidArgumentException(
  138. sprintf("File not found: %s", $path)
  139. );
  140. }
  141. return json_decode(file_get_contents($path), true);
  142. }
  143. /**
  144. * No-op
  145. */
  146. function clear_compiled_json()
  147. {
  148. // pass
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Directory iterator functions.
  152. //-----------------------------------------------------------------------------
  153. /**
  154. * Iterates over the files in a directory and works with custom wrappers.
  155. *
  156. * @param string $path Path to open (e.g., "s3://foo/bar").
  157. * @param resource $context Stream wrapper context.
  158. *
  159. * @return \Generator Yields relative filename strings.
  160. */
  161. function dir_iterator($path, $context = null)
  162. {
  163. $dh = $context ? opendir($path, $context) : opendir($path);
  164. if (!$dh) {
  165. throw new \InvalidArgumentException('File not found: ' . $path);
  166. }
  167. while (($file = readdir($dh)) !== false) {
  168. yield $file;
  169. }
  170. closedir($dh);
  171. }
  172. /**
  173. * Returns a recursive directory iterator that yields absolute filenames.
  174. *
  175. * This iterator is not broken like PHP's built-in DirectoryIterator (which
  176. * will read the first file from a stream wrapper, then rewind, then read
  177. * it again).
  178. *
  179. * @param string $path Path to traverse (e.g., s3://bucket/key, /tmp)
  180. * @param resource $context Stream context options.
  181. *
  182. * @return \Generator Yields absolute filenames.
  183. */
  184. function recursive_dir_iterator($path, $context = null)
  185. {
  186. $invalid = ['.' => true, '..' => true];
  187. $pathLen = strlen($path) + 1;
  188. $iterator = dir_iterator($path, $context);
  189. $queue = [];
  190. do {
  191. while ($iterator->valid()) {
  192. $file = $iterator->current();
  193. $iterator->next();
  194. if (isset($invalid[basename($file)])) {
  195. continue;
  196. }
  197. $fullPath = "{$path}/{$file}";
  198. yield $fullPath;
  199. if (is_dir($fullPath)) {
  200. $queue[] = $iterator;
  201. $iterator = map(
  202. dir_iterator($fullPath, $context),
  203. function ($file) use ($fullPath, $pathLen) {
  204. return substr("{$fullPath}/{$file}", $pathLen);
  205. }
  206. );
  207. continue;
  208. }
  209. }
  210. $iterator = array_pop($queue);
  211. } while ($iterator);
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Misc. functions.
  215. //-----------------------------------------------------------------------------
  216. /**
  217. * Debug function used to describe the provided value type and class.
  218. *
  219. * @param mixed $input
  220. *
  221. * @return string Returns a string containing the type of the variable and
  222. * if a class is provided, the class name.
  223. */
  224. function describe_type($input)
  225. {
  226. switch (gettype($input)) {
  227. case 'object':
  228. return 'object(' . get_class($input) . ')';
  229. case 'array':
  230. return 'array(' . count($input) . ')';
  231. default:
  232. ob_start();
  233. var_dump($input);
  234. // normalize float vs double
  235. return str_replace('double(', 'float(', rtrim(ob_get_clean()));
  236. }
  237. }
  238. /**
  239. * Creates a default HTTP handler based on the available clients.
  240. *
  241. * @return callable
  242. */
  243. function default_http_handler()
  244. {
  245. $version = (string) ClientInterface::VERSION;
  246. if ($version[0] === '5') {
  247. return new \Aws\Handler\GuzzleV5\GuzzleHandler();
  248. } elseif ($version[0] === '6') {
  249. return new \Aws\Handler\GuzzleV6\GuzzleHandler();
  250. } else {
  251. throw new \RuntimeException('Unknown Guzzle version: ' . $version);
  252. }
  253. }
  254. /**
  255. * Serialize a request for a command but do not send it.
  256. *
  257. * Returns a promise that is fulfilled with the serialized request.
  258. *
  259. * @param CommandInterface $command Command to serialize.
  260. *
  261. * @return RequestInterface
  262. * @throws \RuntimeException
  263. */
  264. function serialize(CommandInterface $command)
  265. {
  266. $request = null;
  267. $handlerList = $command->getHandlerList();
  268. // Return a mock result.
  269. $handlerList->setHandler(
  270. function (CommandInterface $_, RequestInterface $r) use (&$request) {
  271. $request = $r;
  272. return new FulfilledPromise(new Result([]));
  273. }
  274. );
  275. call_user_func($handlerList->resolve(), $command)->wait();
  276. if (!$request instanceof RequestInterface) {
  277. throw new \RuntimeException(
  278. 'Calling handler did not serialize request'
  279. );
  280. }
  281. return $request;
  282. }
  283. /**
  284. * Retrieves data for a service from the SDK's service manifest file.
  285. *
  286. * Manifest data is stored statically, so it does not need to be loaded more
  287. * than once per process. The JSON data is also cached in opcache.
  288. *
  289. * @param string $service Case-insensitive namespace or endpoint prefix of the
  290. * service for which you are retrieving manifest data.
  291. *
  292. * @return array
  293. * @throws \InvalidArgumentException if the service is not supported.
  294. */
  295. function manifest($service = null)
  296. {
  297. // Load the manifest and create aliases for lowercased namespaces
  298. static $manifest = [];
  299. static $aliases = [];
  300. if (empty($manifest)) {
  301. $manifest = load_compiled_json(__DIR__ . '/data/manifest.json');
  302. foreach ($manifest as $endpoint => $info) {
  303. $alias = strtolower($info['namespace']);
  304. if ($alias !== $endpoint) {
  305. $aliases[$alias] = $endpoint;
  306. }
  307. }
  308. }
  309. // If no service specified, then return the whole manifest.
  310. if ($service === null) {
  311. return $manifest;
  312. }
  313. // Look up the service's info in the manifest data.
  314. $service = strtolower($service);
  315. if (isset($manifest[$service])) {
  316. return $manifest[$service] + ['endpoint' => $service];
  317. } elseif (isset($aliases[$service])) {
  318. return manifest($aliases[$service]);
  319. } else {
  320. throw new \InvalidArgumentException(
  321. "The service \"{$service}\" is not provided by the AWS SDK for PHP."
  322. );
  323. }
  324. }