functions.php 13 KB

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