1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Chromabits\Nucleus\Support;
13:
14: use Chromabits\Nucleus\Data\ArrayList;
15: use Chromabits\Nucleus\Data\ArrayMap;
16: use Chromabits\Nucleus\Data\Factories\ComplexFactory;
17: use Chromabits\Nucleus\Data\Interfaces\FoldableInterface;
18: use Chromabits\Nucleus\Data\Interfaces\LeftFoldableInterface;
19: use Chromabits\Nucleus\Exceptions\CoreException;
20: use Chromabits\Nucleus\Exceptions\LackOfCoffeeException;
21: use Chromabits\Nucleus\Foundation\StaticObject;
22: use Chromabits\Nucleus\Meditation\Arguments;
23: use Chromabits\Nucleus\Meditation\Boa;
24: use Chromabits\Nucleus\Meditation\Exceptions\InvalidArgumentException;
25: use Chromabits\Nucleus\Meditation\Exceptions\MismatchedArgumentTypesException;
26: use Chromabits\Nucleus\Meditation\Primitives\ScalarTypes;
27: use Chromabits\Nucleus\Meditation\TypeHound;
28: use Chromabits\Nucleus\Strings\Rope;
29: use Closure;
30: use Exception;
31: use ReflectionFunction;
32: use Traversable;
33:
34: 35: 36: 37: 38: 39: 40: 41:
42: class Std extends StaticObject
43: {
44: 45: 46: 47: 48: 49: 50: 51: 52: 53:
54: public static function apply(callable $function, $args)
55: {
56: Arguments::define(Boa::func(), Boa::lst())->check($function, $args);
57:
58: return static::call($function, ...$args);
59: }
60:
61: 62: 63: 64: 65: 66: 67: 68: 69: 70:
71: public static function concat($one, $other)
72: {
73: Arguments::define(
74: Boa::either(
75: Boa::lst(),
76: Boa::string()
77: ),
78: Boa::either(
79: Boa::lst(),
80: Boa::string()
81: )
82: )->check($one, $other);
83:
84: $oneType = TypeHound::fetch($one);
85: $twoType = TypeHound::fetch($other);
86:
87: if ($oneType !== $twoType) {
88: throw new MismatchedArgumentTypesException(
89: __FUNCTION__,
90: $one,
91: $other
92: );
93: }
94:
95: if ($oneType === ScalarTypes::SCALAR_STRING) {
96: return $one . $other;
97: }
98:
99: return ArrayMap::of($one)->append(ArrayMap::of($other))->toArray();
100: }
101:
102: 103: 104: 105: 106: 107: 108: 109: 110:
111: public static function truthy(...$args)
112: {
113: foreach ($args as $arg) {
114: if ($arg) {
115: return $arg;
116: }
117: }
118:
119: return false;
120: }
121:
122: 123: 124: 125: 126: 127: 128: 129: 130:
131: public static function falsy(...$args)
132: {
133: foreach ($args as $arg) {
134: if (!$arg) {
135: return $arg;
136: }
137: }
138:
139: return true;
140: }
141:
142: 143: 144: 145: 146: 147: 148:
149: public static function coalesce(...$args)
150: {
151: foreach ($args as $arg) {
152: if ($arg !== null) {
153: return $arg;
154: }
155: }
156:
157: return null;
158: }
159:
160: 161: 162: 163: 164: 165: 166:
167: public static function coalesceThunk(...$args)
168: {
169: return static::thunk(static::coalesce(...$args));
170: }
171:
172: 173: 174: 175: 176: 177: 178: 179:
180: public static function each(callable $function, $input)
181: {
182: if ($input instanceof FoldableInterface) {
183: $input->foldl(function ($acc, $x) use ($function) {
184: $function($x);
185: }, null);
186:
187: return;
188: } elseif ($input instanceof LeftFoldableInterface) {
189: $input->foldl(function ($x) use ($function) {
190: $function($x);
191: }, null);
192:
193: return;
194: }
195:
196: static::map($function, $input);
197: }
198:
199: 200: 201: 202: 203: 204: 205:
206: public static function nonempty(...$args)
207: {
208: foreach ($args as $arg) {
209: if (!empty($arg)) {
210: return $arg;
211: }
212: }
213:
214: return null;
215: }
216:
217: 218: 219: 220: 221: 222: 223: 224: 225: 226:
227: public static function within($min, $max, $value)
228: {
229: Arguments::define(
230: Boa::either(Boa::integer(), Boa::float()),
231: Boa::either(Boa::integer(), Boa::float()),
232: Boa::either(Boa::integer(), Boa::float())
233: )->check($min, $max, $value);
234:
235: if ($min > $max) {
236: throw new LackOfCoffeeException(
237: 'Max value is less than the min value.'
238: );
239: }
240:
241: return ($min <= $value && $max >= $value);
242: }
243:
244: 245: 246: 247: 248: 249: 250: 251:
252: public static function rope($string, $encoding = null)
253: {
254: Arguments::define(Boa::string(), Boa::maybe(Boa::string()))
255: ->check($string, $encoding);
256:
257: return new Rope($string, $encoding);
258: }
259:
260: 261: 262: 263: 264: 265: 266:
267: public static function esc($string)
268: {
269: Arguments::define(Boa::string())->check($string);
270:
271: return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
272: }
273:
274: 275: 276: 277: 278: 279: 280: 281: 282:
283: public static function callSetters(
284: $object,
285: array $input,
286: array $allowed = null
287: ) {
288: $filtered = ArrayMap::of($input);
289:
290: if ($allowed !== null) {
291: $filtered = $filtered->only($allowed);
292: }
293:
294: $filtered->each(function ($value, $key) use (&$object) {
295: $setterName = 'set' . Str::studly($key);
296:
297: $object->$setterName($value);
298: });
299: }
300:
301: 302: 303: 304: 305: 306: 307: 308: 309: 310:
311: public static function firstBias($biased, $one, $other)
312: {
313: Arguments::define(Boa::boolean(), Boa::any(), Boa::any())
314: ->check($biased, $one, $other);
315:
316: if ($biased) {
317: return static::thunk($one);
318: }
319:
320: return static::thunk($other);
321: }
322:
323: 324: 325: 326: 327: 328: 329:
330: public static function thunk($value)
331: {
332: return $value instanceof Closure ? $value() : $value;
333: }
334:
335: 336: 337: 338: 339: 340: 341: 342:
343: public static function value($value)
344: {
345: return static::thunk($value);
346: }
347:
348: 349: 350: 351: 352: 353: 354: 355: 356: 357:
358: public static function jsonEncode($value, $options = 0, $depth = 512)
359: {
360: return Json::encode($value, $options, $depth);
361: }
362:
363: 364: 365: 366: 367: 368: 369: 370: 371:
372: public static function foldr(callable $function, $initial, $foldable)
373: {
374: Arguments::define(Boa::func(), Boa::any(), Boa::foldable())
375: ->check($function, $initial, $foldable);
376:
377: return ComplexFactory::toFoldable($foldable)
378: ->foldr($function, $initial);
379: }
380:
381: 382: 383: 384: 385: 386: 387: 388: 389:
390: public static function reduceRight(callable $function, $initial, $list)
391: {
392: return static::foldr($function, $initial, $list);
393: }
394:
395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407:
408: public static function foldl(callable $function, $initial, $foldable)
409: {
410: Arguments::define(Boa::func(), Boa::any(), Boa::leftFoldable())
411: ->check($function, $initial, $foldable);
412:
413: return ComplexFactory::toLeftFoldable($foldable)
414: ->foldl($function, $initial);
415: }
416:
417: 418: 419: 420: 421: 422: 423: 424: 425:
426: public static function reduce(callable $function, $initial, $traversable)
427: {
428: return static::foldl($function, $initial, $traversable);
429: }
430:
431: 432: 433: 434: 435: 436: 437: 438: 439: 440:
441: public static function jsonDecode($value, $options = 0, $depth = 512)
442: {
443: return Json::decode($value, $options, $depth);
444: }
445:
446: 447: 448: 449: 450: 451: 452: 453:
454: public static function map(callable $function, $traversable)
455: {
456: $aggregation = [];
457:
458: foreach ($traversable as $key => $value) {
459: $aggregation[$key] = $function($value, $key);
460: }
461:
462: return $aggregation;
463: }
464:
465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477:
478: public static function filter(callable $function, $traversable)
479: {
480: Arguments::define(Boa::func(), Boa::traversable())
481: ->check($function, $traversable);
482:
483: $aggregation = [];
484:
485: foreach ($traversable as $key => $value) {
486: if ($function($value, $key)) {
487: $aggregation[$key] = $value;
488: }
489: }
490:
491: return $aggregation;
492: }
493:
494: 495: 496: 497: 498: 499: 500: 501:
502: public static function curry(callable $function, ...$args)
503: {
504: return static::curryArgs($function, $args);
505: }
506:
507: 508: 509: 510: 511: 512: 513: 514:
515: public static function curryArgs(callable $function, $args)
516: {
517: Arguments::define(Boa::func(), Boa::arr())->check($function, $args);
518:
519:
520: $required = function () use ($function) {
521: return (new ReflectionFunction($function))
522: ->getNumberOfRequiredParameters();
523: };
524:
525: $isFulfilled = function (callable $function, $args) use ($required) {
526: return count($args) >= $required($function);
527: };
528:
529: if ($isFulfilled($function, $args)) {
530: return static::apply($function, $args);
531: }
532:
533: return function (...$funcArgs) use (
534: $function,
535: $args,
536: $required,
537: $isFulfilled
538: ) {
539: $newArgs = ArrayList::of($args)
540: ->append(ArrayList::of($funcArgs))
541: ->toArray();
542:
543: if ($isFulfilled($function, $newArgs)) {
544: return static::apply($function, $newArgs);
545: }
546:
547: return static::curryArgs($function, $newArgs);
548: };
549: }
550:
551: 552: 553: 554: 555: 556: 557: 558:
559: public static function call(callable $function, ...$args)
560: {
561: return call_user_func($function, ...$args);
562: }
563:
564: 565: 566: 567: 568: 569: 570: 571:
572: public static function poll(callable $function, $times)
573: {
574: Arguments::define(Boa::func(), Boa::integer())
575: ->check($function, $times);
576:
577: for ($ii = 0; $ii < $times; $ii++) {
578: static::call($function, $ii);
579: }
580: }
581:
582: 583: 584: 585: 586: 587: 588: 589: 590: 591:
592: public static function retry(callable $function, $attempts)
593: {
594: Arguments::define(Boa::func(), Boa::integer())
595: ->check($function, $attempts);
596:
597: for ($ii = 0; $ii < $attempts; $ii++) {
598: try {
599: $result = static::call($function, $ii);
600:
601: return $result;
602: } catch (Exception $e) {
603: continue;
604: }
605: }
606:
607: return null;
608: }
609:
610: 611: 612: 613: 614: 615: 616: 617:
618: public static function castToBool($mixed)
619: {
620: if (is_string($mixed) || $mixed instanceof Rope) {
621: $lower = Rope::of($mixed)->toLower();
622:
623: if ($lower->equals(Rope::of('true'))) {
624: return true;
625: } elseif ($lower->equals(Rope::of('false'))) {
626: return false;
627: }
628:
629: throw new CoreException('Unable to cast into a bool.');
630: } elseif (is_int($mixed)) {
631: if ($mixed === 1) {
632: return true;
633: } elseif ($mixed === 0) {
634: return false;
635: }
636:
637: throw new CoreException('Unable to cast into a bool.');
638: } elseif (is_float($mixed)) {
639: if ($mixed === 1.0) {
640: return true;
641: } elseif ($mixed === 0.0) {
642: return false;
643: }
644:
645: throw new CoreException('Unable to cast into a bool.');
646: } elseif (is_bool($mixed)) {
647: return $mixed;
648: }
649:
650: throw new CoreException('Unable to cast into a bool.');
651: }
652: }
653: