helpers.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. <?php
  2. use Illuminate\Contracts\Support\DeferringDisplayableValue;
  3. use Illuminate\Contracts\Support\Htmlable;
  4. use Illuminate\Database\Eloquent\Model;
  5. use Illuminate\Support\Arr;
  6. use Illuminate\Support\Env;
  7. use Illuminate\Support\Fluent;
  8. use Illuminate\Support\HigherOrderTapProxy;
  9. use Illuminate\Support\Once;
  10. use Illuminate\Support\Onceable;
  11. use Illuminate\Support\Optional;
  12. use Illuminate\Support\Sleep;
  13. use Illuminate\Support\Str;
  14. use Illuminate\Support\Stringable as SupportStringable;
  15. if (! function_exists('append_config')) {
  16. /**
  17. * Assign high numeric IDs to a config item to force appending.
  18. *
  19. * @param array $array
  20. */
  21. function append_config(array $array): array
  22. {
  23. $start = 9999;
  24. foreach ($array as $key => $value) {
  25. if (is_numeric($key)) {
  26. $start++;
  27. $array[$start] = Arr::pull($array, $key);
  28. }
  29. }
  30. return $array;
  31. }
  32. }
  33. if (! function_exists('blank')) {
  34. /**
  35. * Determine if the given value is "blank".
  36. *
  37. * @phpstan-assert-if-false !=null|'' $value
  38. *
  39. * @phpstan-assert-if-true !=numeric|bool $value
  40. *
  41. * @param mixed $value
  42. */
  43. function blank($value): bool
  44. {
  45. if (is_null($value)) {
  46. return true;
  47. }
  48. if (is_string($value)) {
  49. return trim($value) === '';
  50. }
  51. if (is_numeric($value) || is_bool($value)) {
  52. return false;
  53. }
  54. if ($value instanceof Model) {
  55. return false;
  56. }
  57. if ($value instanceof Countable) {
  58. return count($value) === 0;
  59. }
  60. if ($value instanceof Stringable) {
  61. return trim((string) $value) === '';
  62. }
  63. return empty($value);
  64. }
  65. }
  66. if (! function_exists('class_basename')) {
  67. /**
  68. * Get the class "basename" of the given object / class.
  69. *
  70. * @param string|object $class
  71. */
  72. function class_basename($class): string
  73. {
  74. $class = is_object($class) ? get_class($class) : $class;
  75. return basename(str_replace('\\', '/', $class));
  76. }
  77. }
  78. if (! function_exists('class_uses_recursive')) {
  79. /**
  80. * Returns all traits used by a class, its parent classes and trait of their traits.
  81. *
  82. * @param object|string $class
  83. * @return array<string, string>
  84. */
  85. function class_uses_recursive($class): array
  86. {
  87. if (is_object($class)) {
  88. $class = get_class($class);
  89. }
  90. $results = [];
  91. foreach (array_reverse(class_parents($class) ?: []) + [$class => $class] as $class) {
  92. $results += trait_uses_recursive($class);
  93. }
  94. return array_unique($results);
  95. }
  96. }
  97. if (! function_exists('e')) {
  98. /**
  99. * Encode HTML special characters in a string.
  100. *
  101. * @param \Illuminate\Contracts\Support\DeferringDisplayableValue|\Illuminate\Contracts\Support\Htmlable|\BackedEnum|string|int|float|null $value
  102. * @param bool $doubleEncode
  103. */
  104. function e($value, $doubleEncode = true): string
  105. {
  106. if ($value instanceof DeferringDisplayableValue) {
  107. $value = $value->resolveDisplayableValue();
  108. }
  109. if ($value instanceof Htmlable) {
  110. return $value->toHtml() ?? '';
  111. }
  112. if ($value instanceof BackedEnum) {
  113. $value = $value->value;
  114. }
  115. return htmlspecialchars($value ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', $doubleEncode);
  116. }
  117. }
  118. if (! function_exists('env')) {
  119. /**
  120. * Gets the value of an environment variable.
  121. *
  122. * @param string $key
  123. * @param mixed $default
  124. * @return mixed
  125. */
  126. function env($key, $default = null)
  127. {
  128. return Env::get($key, $default);
  129. }
  130. }
  131. if (! function_exists('filled')) {
  132. /**
  133. * Determine if a value is "filled".
  134. *
  135. * @phpstan-assert-if-true !=null|'' $value
  136. *
  137. * @phpstan-assert-if-false !=numeric|bool $value
  138. *
  139. * @param mixed $value
  140. */
  141. function filled($value): bool
  142. {
  143. return ! blank($value);
  144. }
  145. }
  146. if (! function_exists('fluent')) {
  147. /**
  148. * Create a Fluent object from the given value.
  149. *
  150. * @param iterable|object|null $value
  151. */
  152. function fluent($value = null): Fluent
  153. {
  154. return new Fluent($value ?? []);
  155. }
  156. }
  157. if (! function_exists('literal')) {
  158. /**
  159. * Return a new literal or anonymous object using named arguments.
  160. *
  161. * @return mixed
  162. */
  163. function literal(...$arguments)
  164. {
  165. if (count($arguments) === 1 && array_is_list($arguments)) {
  166. return $arguments[0];
  167. }
  168. return (object) $arguments;
  169. }
  170. }
  171. if (! function_exists('object_get')) {
  172. /**
  173. * Get an item from an object using "dot" notation.
  174. *
  175. * @template TValue of object
  176. *
  177. * @param TValue $object
  178. * @param string|null $key
  179. * @param mixed $default
  180. * @return ($key is empty ? TValue : mixed)
  181. */
  182. function object_get($object, $key, $default = null)
  183. {
  184. if (is_null($key) || trim($key) === '') {
  185. return $object;
  186. }
  187. foreach (explode('.', $key) as $segment) {
  188. if (! is_object($object) || ! isset($object->{$segment})) {
  189. return value($default);
  190. }
  191. $object = $object->{$segment};
  192. }
  193. return $object;
  194. }
  195. }
  196. if (! function_exists('laravel_cloud')) {
  197. /**
  198. * Determine if the application is running on Laravel Cloud.
  199. */
  200. function laravel_cloud(): bool
  201. {
  202. return ($_ENV['LARAVEL_CLOUD'] ?? false) === '1' ||
  203. ($_SERVER['LARAVEL_CLOUD'] ?? false) === '1';
  204. }
  205. }
  206. if (! function_exists('once')) {
  207. /**
  208. * Ensures a callable is only called once, and returns the result on subsequent calls.
  209. *
  210. * @template TReturnType
  211. *
  212. * @param callable(): TReturnType $callback
  213. * @return TReturnType
  214. */
  215. function once(callable $callback)
  216. {
  217. $onceable = Onceable::tryFromTrace(
  218. debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2),
  219. $callback,
  220. );
  221. return $onceable ? Once::instance()->value($onceable) : call_user_func($callback);
  222. }
  223. }
  224. if (! function_exists('optional')) {
  225. /**
  226. * Provide access to optional objects.
  227. *
  228. * @template TValue
  229. * @template TReturn
  230. *
  231. * @param TValue $value
  232. * @param (callable(TValue): TReturn)|null $callback
  233. * @return ($callback is null ? \Illuminate\Support\Optional : ($value is null ? null : TReturn))
  234. */
  235. function optional($value = null, ?callable $callback = null)
  236. {
  237. if (is_null($callback)) {
  238. return new Optional($value);
  239. } elseif (! is_null($value)) {
  240. return $callback($value);
  241. }
  242. }
  243. }
  244. if (! function_exists('preg_replace_array')) {
  245. /**
  246. * Replace a given pattern with each value in the array in sequentially.
  247. *
  248. * @param string $pattern
  249. * @param array $replacements
  250. * @param string $subject
  251. */
  252. function preg_replace_array($pattern, array $replacements, $subject): string
  253. {
  254. return preg_replace_callback($pattern, function () use (&$replacements) {
  255. foreach ($replacements as $value) {
  256. return array_shift($replacements);
  257. }
  258. }, $subject);
  259. }
  260. }
  261. if (! function_exists('retry')) {
  262. /**
  263. * Retry an operation a given number of times.
  264. *
  265. * @template TValue
  266. *
  267. * @param int|array<int, int> $times
  268. * @param callable(int): TValue $callback
  269. * @param int|\Closure(int, \Throwable): int $sleepMilliseconds
  270. * @param (callable(\Throwable): bool)|null $when
  271. * @return TValue
  272. *
  273. * @throws \Throwable
  274. */
  275. function retry($times, callable $callback, $sleepMilliseconds = 0, $when = null)
  276. {
  277. $attempts = 0;
  278. $backoff = [];
  279. if (is_array($times)) {
  280. $backoff = $times;
  281. $times = count($times) + 1;
  282. }
  283. beginning:
  284. $attempts++;
  285. $times--;
  286. try {
  287. return $callback($attempts);
  288. } catch (Throwable $e) {
  289. if ($times < 1 || ($when && ! $when($e))) {
  290. throw $e;
  291. }
  292. $sleepMilliseconds = $backoff[$attempts - 1] ?? $sleepMilliseconds;
  293. if ($sleepMilliseconds) {
  294. Sleep::usleep(value($sleepMilliseconds, $attempts, $e) * 1000);
  295. }
  296. goto beginning;
  297. }
  298. }
  299. }
  300. if (! function_exists('str')) {
  301. /**
  302. * Get a new stringable object from the given string.
  303. *
  304. * @param string|null $string
  305. * @return ($string is null ? object : \Illuminate\Support\Stringable)
  306. */
  307. function str($string = null)
  308. {
  309. if (func_num_args() === 0) {
  310. return new class
  311. {
  312. public function __call($method, $parameters)
  313. {
  314. return Str::$method(...$parameters);
  315. }
  316. public function __toString()
  317. {
  318. return '';
  319. }
  320. };
  321. }
  322. return new SupportStringable($string);
  323. }
  324. }
  325. if (! function_exists('tap')) {
  326. /**
  327. * Call the given Closure with the given value then return the value.
  328. *
  329. * @template TValue
  330. *
  331. * @param TValue $value
  332. * @param (callable(TValue): mixed)|null $callback
  333. * @return ($callback is null ? \Illuminate\Support\HigherOrderTapProxy : TValue)
  334. */
  335. function tap($value, $callback = null)
  336. {
  337. if (is_null($callback)) {
  338. return new HigherOrderTapProxy($value);
  339. }
  340. $callback($value);
  341. return $value;
  342. }
  343. }
  344. if (! function_exists('throw_if')) {
  345. /**
  346. * Throw the given exception if the given condition is true.
  347. *
  348. * @template TValue
  349. * @template TParams of mixed
  350. * @template TException of \Throwable
  351. * @template TExceptionValue of TException|class-string<TException>|string
  352. *
  353. * @param TValue $condition
  354. * @param Closure(TParams): TExceptionValue|TExceptionValue $exception
  355. * @param TParams ...$parameters
  356. * @return ($condition is true ? never : ($condition is non-empty-mixed ? never : TValue))
  357. *
  358. * @throws TException
  359. */
  360. function throw_if($condition, $exception = 'RuntimeException', ...$parameters)
  361. {
  362. if ($condition) {
  363. if ($exception instanceof Closure) {
  364. $exception = $exception(...$parameters);
  365. }
  366. if (is_string($exception) && class_exists($exception)) {
  367. $exception = new $exception(...$parameters);
  368. }
  369. throw is_string($exception) ? new RuntimeException($exception) : $exception;
  370. }
  371. return $condition;
  372. }
  373. }
  374. if (! function_exists('throw_unless')) {
  375. /**
  376. * Throw the given exception unless the given condition is true.
  377. *
  378. * @template TValue
  379. * @template TParams of mixed
  380. * @template TException of \Throwable
  381. * @template TExceptionValue of TException|class-string<TException>|string
  382. *
  383. * @param TValue $condition
  384. * @param Closure(TParams): TExceptionValue|TExceptionValue $exception
  385. * @param TParams ...$parameters
  386. * @return ($condition is false ? never : ($condition is non-empty-mixed ? TValue : never))
  387. *
  388. * @throws TException
  389. */
  390. function throw_unless($condition, $exception = 'RuntimeException', ...$parameters)
  391. {
  392. throw_if(! $condition, $exception, ...$parameters);
  393. return $condition;
  394. }
  395. }
  396. if (! function_exists('trait_uses_recursive')) {
  397. /**
  398. * Returns all traits used by a trait and its traits.
  399. *
  400. * @param object|string $trait
  401. * @return array<string, string>
  402. */
  403. function trait_uses_recursive($trait): array
  404. {
  405. $traits = class_uses($trait) ?: [];
  406. foreach ($traits as $trait) {
  407. $traits += trait_uses_recursive($trait);
  408. }
  409. return $traits;
  410. }
  411. }
  412. if (! function_exists('transform')) {
  413. /**
  414. * Transform the given value if it is present.
  415. *
  416. * @template TValue
  417. * @template TReturn
  418. * @template TDefault
  419. *
  420. * @param TValue $value
  421. * @param callable(TValue): TReturn $callback
  422. * @param TDefault|callable(TValue): TDefault $default
  423. * @return ($value is empty ? TDefault : TReturn)
  424. */
  425. function transform($value, callable $callback, $default = null)
  426. {
  427. if (filled($value)) {
  428. return $callback($value);
  429. }
  430. if (is_callable($default)) {
  431. return $default($value);
  432. }
  433. return $default;
  434. }
  435. }
  436. if (! function_exists('windows_os')) {
  437. /**
  438. * Determine whether the current environment is Windows based.
  439. */
  440. function windows_os(): bool
  441. {
  442. return PHP_OS_FAMILY === 'Windows';
  443. }
  444. }
  445. if (! function_exists('with')) {
  446. /**
  447. * Return the given value, optionally passed through the given callback.
  448. *
  449. * @template TValue
  450. * @template TReturn
  451. *
  452. * @param TValue $value
  453. * @param (callable(TValue): (TReturn))|null $callback
  454. * @return ($callback is null ? TValue : TReturn)
  455. */
  456. function with($value, ?callable $callback = null)
  457. {
  458. return is_null($callback) ? $value : $callback($value);
  459. }
  460. }