├── .github ├── FUNDING.yml └── workflows │ └── php.yml ├── LICENSE.md ├── README.md ├── composer.json └── helpers ├── auth.php ├── common.php ├── datetime.php ├── filesystem.php ├── http.php └── objects.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Help me support this package 2 | 3 | ko_fi: DarkGhostHunter 4 | custom: ['https://paypal.me/darkghosthunter'] 5 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: true 13 | matrix: 14 | php: [8.0] 15 | laravel: [8.*] 16 | dependency-version: [prefer-lowest, prefer-stable] 17 | include: 18 | - laravel: 8.* 19 | testbench: 6.* 20 | 21 | name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - ${{ matrix.dependency-version }} 22 | 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v2 26 | 27 | - name: Setup PHP 28 | uses: shivammathur/setup-php@v2 29 | with: 30 | php-version: ${{ matrix.php }} 31 | extensions: mbstring, intl 32 | coverage: xdebug 33 | 34 | - name: Cache dependencies 35 | uses: actions/cache@v2 36 | with: 37 | path: ~/.composer/cache/files 38 | key: ${{ runner.os }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 39 | restore-keys: ${{ runner.os }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer- 40 | 41 | - name: Install dependencies 42 | run: | 43 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-progress --no-update 44 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress --no-suggest 45 | 46 | - name: Run Tests 47 | run: composer run-script test 48 | 49 | - name: Upload Coverage to Coveralls 50 | env: 51 | COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | COVERALLS_SERVICE_NAME: github 53 | run: | 54 | rm -rf composer.* vendor/ 55 | composer require php-coveralls/php-coveralls 56 | vendor/bin/php-coveralls 57 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Italo Israel Baeza Cabrera 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Regine Tholen - Unsplash (UL) #ojGvj7CE5OQ](https://images.unsplash.com/photo-1574246915327-8cf501d94757?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1280&h=400&q=80) 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/darkghosthunter/larahelp/v/stable)](https://packagist.org/packages/darkghosthunter/larahelp) [![License](https://poser.pugx.org/darkghosthunter/larahelp/license)](https://packagist.org/packages/darkghosthunter/larahelp) 4 | ![](https://img.shields.io/packagist/php-v/darkghosthunter/larahelp.svg) 5 | ![](https://github.com/DarkGhostHunter/Larahelp/workflows/PHP%20Composer/badge.svg) 6 | [![Coverage Status](https://coveralls.io/repos/github/DarkGhostHunter/Larahelp/badge.svg?branch=master)](https://coveralls.io/github/DarkGhostHunter/Larahelp?branch=master) 7 | 8 | 9 | # Larahelp 10 | 11 | Supercharge your Laravel projects with more than 25 useful global helpers. 12 | 13 | ## Requisites 14 | 15 | * Laravel 8.x or later 16 | * PHP 8.0 or later 17 | 18 | ## Installation 19 | 20 | You can install the package via composer: 21 | 22 | ```bash 23 | composer require darkghosthunter/larahelp 24 | ``` 25 | 26 | > This package is focused on the backend. If you want views helpers, I recommend you to use [custom Blade directives](https://laravel.com/docs/blade#extending-blade) instead. 27 | 28 | ## Usage 29 | 30 | This package includes helpful global helpers for your project make almost anything into beautiful _one liners_: 31 | 32 | | | | | 33 | |---|---|---| 34 | | [app_call](#app_call) | [in_development](#in_development) | [route_is](#route_is) 35 | | [call_existing](#call_existing) | [logged_in](#logged_in) | [shadow](#shadow) 36 | | [created](#created) | [methods_of](#methods_of) | [sleep_between](#sleep_between) 37 | | [data_update](#data_update) | [missing_trait](#missing_trait) | [taptap](#taptap) 38 | | [delist](#delist) | [none_of](#none_of) | [undot_path](#undot_path) 39 | | [diff](#diff) | [object_assign](#object_assign) | [until](#until) 40 | | [dot_path](#dot_path) | [ok](#ok) | [user](#user) 41 | | [enclose](#enclose) | [period](#period) | [weekend](#weekend) 42 | | [files](#files) | [period_from](#period_from) | [weekstart](#weekstart) 43 | | [has_trait](#has_trait) | [pipe](#pipe) | [which_of](#which_of) 44 | | [hashy](#hashy) | [remember](#remember) | [yesterday](#yesterday) 45 | | [in_console](#in_console) | [route_is](#route_is) | 46 | ### `app_call()` 47 | 48 | Executes a callable using the application Container. 49 | 50 | ```php 51 | $result = app_call('\App\MyService\Service@createSomething'); 52 | ``` 53 | 54 | ### `call_existing()` 55 | 56 | Calls a dynamic method or macro if it exists in the object instance. It supports macros and static methods. 57 | 58 | ```php 59 | class Something { 60 | public function foo() { 61 | return 'bar'; 62 | } 63 | } 64 | 65 | call_existing(new Something, 'bar'); // null 66 | 67 | call_existing(new Something, 'foo'); // "bar" 68 | ``` 69 | 70 | ### `created()` 71 | 72 | Return an HTTP 201 response (OK, Created), with optional content. 73 | 74 | ```php 75 | public function store(Request $request): Response 76 | { 77 | // ... 78 | 79 | return created([$post->getKeyName() => $post->getKey()]; 80 | } 81 | ``` 82 | 83 | ### `data_update()` 84 | 85 | Updates an item of an array or object using a callback that receives it. 86 | 87 | ```php 88 | $array = [ 89 | 'foo' => [ 90 | 'bar' => null 91 | ] 92 | ]; 93 | 94 | data_update($array, 'foo.bar', function ($value) { 95 | if ($value === null) { 96 | return 'baz'; 97 | } 98 | }) 99 | 100 | 101 | // [ 102 | // 'foo' => [ 103 | // 'bar' => 'baz' 104 | // ] 105 | // ]; 106 | ``` 107 | 108 | > The value will be updated regardless if the key doesn't exists. 109 | 110 | ### `delist()` 111 | 112 | Returns the values of the array, so these can be listed into variables. It accepts an optional offset to remove a given number of first keys. 113 | 114 | ```php 115 | $array = [ 116 | 'foo' => 'bar', 117 | 'baz' => 'qux', 118 | 'quux' => 'quuz', 119 | ] 120 | 121 | [$baz, $quux] = delist($array, 1); 122 | ``` 123 | 124 | ### `diff()` 125 | 126 | Returns the difference between two dates in seconds or any other given unit. 127 | 128 | ```php 129 | $seconds = diff('now', '15th may'); 130 | 131 | $minutes = diff('today', now()) 132 | ``` 133 | 134 | ### `dot_path()` 135 | 136 | Transforms a relative path into dot notation. 137 | 138 | ```php 139 | $path = dot_path('files/user_id_312/videos/'); 140 | 141 | // files.user_id_312.videos 142 | ``` 143 | 144 | > This does not validate file paths. You should append the filename to the dot-path manually. 145 | 146 | ### `enclose()` 147 | 148 | Wraps a value or callable into a Closure, if it's not already callable. 149 | 150 | ```php 151 | $enclosed = enclose('foo'); 152 | 153 | $enclosed(); 154 | 155 | // "foo" 156 | ``` 157 | 158 | ### `files()` 159 | 160 | Returns the local Filesystem helper, or a list of files in a path. 161 | 162 | ```php 163 | $content = files()->get('text.txt'); 164 | ``` 165 | 166 | ### `has_trait()` 167 | 168 | Checks recursively if the object is using a single trait. 169 | 170 | ```php 171 | trait Child { 172 | // .. 173 | } 174 | 175 | trait Parent { 176 | use Child; 177 | // .. 178 | } 179 | 180 | class Foo { 181 | use Parent; 182 | } 183 | 184 | has_trait(Foo::class, Child::class); 185 | 186 | // true 187 | ``` 188 | 189 | ### `hashy()` 190 | 191 | Creates a small BASE64 encoded MD5 hash from a string for portable checksum. 192 | 193 | This is very useful to hash large walls of texts, or even files, while compressing the 128-bit hash into a 24-character string. 194 | 195 | ```php 196 | $hash = hashy('This is a hashable string'); 197 | 198 | // "TJYa8+63dRbdN6w44shX1g==" 199 | ``` 200 | 201 | You can use the same function to compare the hashable string with a hash to note if it was modified. 202 | 203 | ```php 204 | hashy('This is a hashable string', 'TJYa8+63dRbdN6w44shX1g=='); 205 | 206 | // true 207 | 208 | hashy('This is a hashable string!', 'TJYa8+63dRbdN6w44shX1g=='); 209 | 210 | // false 211 | ``` 212 | 213 | ### `in_console()` 214 | 215 | Check if the application is running in console, like when using Artisan or PHPUnit. 216 | 217 | ```php 218 | if (in_console()) { 219 | return "We're in console"; 220 | } 221 | ``` 222 | 223 | It also accepts a callable that will be executed if the condition is true. 224 | 225 | ```php 226 | $result = in_console(fn() => 'foo'); 227 | 228 | // "foo" 229 | ``` 230 | 231 | ### `in_development()` 232 | 233 | Check if the application is running in development environments: `dev`, `development` or `local` 234 | 235 | ```php 236 | if (in_development()) { 237 | return "Do anything, it doesn't matter!"; 238 | } 239 | ``` 240 | 241 | It also accepts a callable that will be executed if the condition is true. 242 | 243 | ```php 244 | $result = in_development(fn() => 'foo'); 245 | 246 | // "foo" 247 | ``` 248 | 249 | ### `logged_in()` 250 | 251 | Executes a single callback while the user is logged in. 252 | 253 | It basically logs in and logs out an user while executing the callback, which can be useful to do on guest routes. 254 | 255 | ```php 256 | use App\Models\User; 257 | 258 | $user = User::find(1); 259 | 260 | $post = logged_in($user, function (User $user) { 261 | return $user->post()->create([ 262 | // .. 263 | ]); 264 | }); 265 | ``` 266 | 267 | > It will throw an exception if there is already a user logged in. 268 | 269 | ### `methods_of()` 270 | 271 | Returns a collection of all public methods from a given class or object. 272 | 273 | ```php 274 | use Illuminate\Support\Collection; 275 | 276 | $methods = methods_of(Collection::class) 277 | 278 | return $methods->has('make'); 279 | 280 | // true 281 | ``` 282 | 283 | ### `missing_trait()` 284 | 285 | Checks recursively if the object is not using a trait. 286 | 287 | ```php 288 | trait Parent { 289 | // .. 290 | } 291 | 292 | class Foo { 293 | use Parent; 294 | } 295 | 296 | missing_trait(Foo::class, Child::class); 297 | 298 | // true 299 | ``` 300 | 301 | ### `none_of()` 302 | 303 | Checks if none of the options compared to a subject, or called with it, returns something truthy. 304 | 305 | ```php 306 | $subject = 'foo'; 307 | 308 | none_of('foo', ['bar', 'baz', 'qux']); 309 | 310 | // false 311 | ``` 312 | 313 | Using a callable, it will receive the subject and the key being compared. 314 | 315 | ```php 316 | $subject = 'foo'; 317 | 318 | none_of('foo', ['bar', 'baz', 'qux'], fn ($subject, $compared) => $subject === $compared); 319 | 320 | // false 321 | ``` 322 | 323 | ### `object_assign()` 324 | 325 | Assigns an array of values to an object, recursively, using dot notation. 326 | 327 | ```php 328 | $object = new stdClass(); 329 | 330 | object_assign($object, ['foo' => 'bar']); 331 | 332 | echo $object->foo; // "bar" 333 | ``` 334 | 335 | ### `ok()` 336 | 337 | Returns an HTTP 204 response (OK, No Content). 338 | 339 | ```php 340 | public function send(Request $request) 341 | { 342 | // ... 343 | 344 | return ok(); 345 | } 346 | ``` 347 | 348 | ### `period()` 349 | 350 | Returns the period of a given start and end or interval. Periods are extremely useful to get multiple dates based on a start, end, a given time between each occurrence. 351 | 352 | The most common way to create a period is defining the start, the number of occurrences, and the interval amount. 353 | 354 | ```php 355 | $periods = period('now', 4, '15 minutes'); 356 | ``` 357 | 358 | Alternatively, you can specify a start, and end, and the interval of time until that end. 359 | 360 | ```php 361 | $period = period('today', 'last day of this month', '3 days'); 362 | ``` 363 | 364 | Once you do, you can iterate each moment of time with a `foreach` loop. 365 | 366 | ```php 367 | foreach (period('today', 'last day of this month', '3 days') as $datetime) { 368 | echo $datetime->toDateTimeString(); 369 | } 370 | 371 | // 2015-07-21 00:00:00 372 | // 2015-07-24 00:00:00 373 | // 2015-07-27 00:00:00 374 | // 2015-07-30 00:00:00 375 | ``` 376 | 377 | ### `period_from()` 378 | 379 | Returns the next or previous period from given date, exclusive by default. 380 | 381 | This is very handy to know when the next or previous moment from a given datetime, like the current time. 382 | 383 | ```php 384 | $period = period('2015-07-06 00:00:00', '2015-07-26 00:00:00', '1 week'); 385 | 386 | period_from($period, '2015-07-13'); 387 | 388 | // 2015-07-13 389 | ``` 390 | 391 | The function can also check the previous date by setting `$after` to `false`. 392 | 393 | ```php 394 | $period = period('2015-07-06 00:00:00', '2015-07-26 00:00:00', '1 week'); 395 | 396 | period_from($period, '2015-07-06'); 397 | 398 | // 2015-07-13 399 | ``` 400 | 401 | > Dates compared are exclusive. In include the date as inclusive, set `$inclusive` to `true`. 402 | 403 | ### `pipe()` 404 | 405 | Sends an object through a pipeline. 406 | 407 | ```php 408 | pipe(10, [ 409 | fn($integer, $next) => $next($integer + 10); 410 | fn($integer, $next) => $next($integer - 5); 411 | ]) 412 | 413 | // 15 414 | ``` 415 | 416 | ### `remember()` 417 | 418 | Retrieves an item from the cache, or stores a default value if the item doesn't exist. 419 | 420 | ```php 421 | remember('foo', 60, function() { 422 | return 'bar'; 423 | }) 424 | ``` 425 | 426 | If no `ttl` is set, and rather a callback is issued as second parameter, it will be stored forever. 427 | 428 | ```php 429 | remember('foo', function () { 430 | return 'bar'; 431 | }) 432 | ``` 433 | 434 | It supports atomic locks, which are created using the same name key. It will lock the key by a given seconds, while also waiting for the same amount of time. 435 | 436 | ```php 437 | remember('foo', 60, function() { 438 | return 'bar'; 439 | }, 20); 440 | ``` 441 | 442 | This can be useful to avoid cache data-races, where multiple processes run the same callback because the cache key is not filled yet. 443 | 444 | ### `route_is()` 445 | 446 | Determine whether the current route's name matches the given patterns. 447 | 448 | ```php 449 | if (route_is('dahsboard.*')) { 450 | return 'You are in the dashboard'; 451 | } 452 | ``` 453 | 454 | ### `shadow()` 455 | 456 | Calls a method on an object if it exists, or returns false. It supports Macros. 457 | 458 | ```php 459 | if ($rendered = shadow($mayRender, 'render')) { 460 | return $rendered; 461 | } 462 | 463 | return response((string)$mayRender); 464 | ``` 465 | 466 | ### `sleep_between()` 467 | 468 | Runs a callback while sleeping between multiple executions, returning a Collection of all results. 469 | 470 | ```php 471 | use App\Models\User; 472 | 473 | sleep_between(3, 1000, fn() => User::query()->inRandomOrder()->value('name')) 474 | 475 | // [ 476 | // 'john', 477 | // 'michel', 478 | // 'maria', 479 | // ] 480 | ``` 481 | 482 | ### `taptap()` 483 | 484 | Call the given Closure with the given value then return the value, twice. 485 | 486 | ```php 487 | use App\Models\User; 488 | use App\Notifications\Message; 489 | 490 | return taptap(User::find(1))->notify(new Message('Hello!'))->save(); 491 | ``` 492 | 493 | ### `undot_path()` 494 | 495 | Transforms a path from dot notation to a relative path. 496 | 497 | ```php 498 | $path = undot_path('files.user_id_312.videos'); 499 | 500 | // files/user_id_312/videos/ 501 | ``` 502 | 503 | ### `until()` 504 | 505 | Returns the interval from a date until the desired date. 506 | 507 | ```php 508 | until('now', 'next month')->total('days'); 509 | 510 | // 25 511 | ``` 512 | 513 | ### `user()` 514 | 515 | Returns the currently authenticated user, if any. 516 | 517 | ```php 518 | user()?->name; 519 | 520 | // "John Doe" 521 | ``` 522 | 523 | ### `weekend()` 524 | 525 | Returns the end of the week. It supports setting what day the week end. 526 | 527 | ```php 528 | weekend()->toDateTimeString(); 529 | 530 | // 2015-07-05 23:59:59 531 | 532 | weekend('now')->toDateTimeString(); 533 | 534 | // 2015-07-13 23:59:59 535 | ``` 536 | 537 | ### `weekstart()` 538 | 539 | Returns the end of the week. It supports setting what day the week starts. 540 | 541 | ```php 542 | weekstart()->toDateTimeString(); 543 | 544 | // 2015-06-28 00:00:00 545 | 546 | weekstart('now')->toDateTimeString(); 547 | 548 | // 2015-07-06 00:00:00 549 | ``` 550 | 551 | ### `which_of()` 552 | 553 | Returns the key of the option which comparison or callback returns true. 554 | 555 | ```php 556 | $which = which_of('foo', [0 => 'baz', 1 => 'bar', 2 => 'foo']) 557 | 558 | // 2 559 | ``` 560 | 561 | If the callback returns something truthy, that value will be used. 562 | 563 | ```php 564 | $which = which_of( 565 | 'foo', 566 | ['baz', 'bar', 'foo'], 567 | fn($subject, $option) => $subject === $option ? 'cougar' : false) 568 | ); 569 | 570 | // "cougar" 571 | ``` 572 | 573 | ### `yesterday()` 574 | 575 | Returns the date for yesterday. 576 | 577 | ```php 578 | // 2015-07-12 17:30:00 579 | yesterday(); 580 | 581 | // 2015-07-11 00:00:00 582 | ``` 583 | 584 | ## Missing a helper? 585 | 586 | If you have an idea for a helper, shoot it as an issue. PR with test and good code quality receive priority. 587 | 588 | ## Security 589 | 590 | If you discover any security related issues, please email darkghosthunter@gmail.com instead of using the issue tracker. 591 | 592 | ## License 593 | 594 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 595 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "darkghosthunter/larahelp", 3 | "description": "Supercharge your Laravel projects with useful global helpers.", 4 | "keywords": [ 5 | "darkghosthunter", 6 | "helpers", 7 | "laravel" 8 | ], 9 | "homepage": "https://github.com/darkghosthunter/larahelp", 10 | "license": "MIT", 11 | "type": "library", 12 | "authors": [ 13 | { 14 | "name": "Italo Israel Baeza Cabrera", 15 | "email": "darkghosthunter@gmail.com", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.0", 21 | "ext-json": "*", 22 | "illuminate/support": "^8.0" 23 | }, 24 | "require-dev": { 25 | "orchestra/testbench": "^6.0", 26 | "guzzlehttp/guzzle": "^7.0", 27 | "phpunit/phpunit": "^9.3", 28 | "mockery/mockery": "^1.4", 29 | "nesbot/carbon": "^2.41", 30 | "moneyphp/money": "^v4.0.2" 31 | }, 32 | "autoload": { 33 | "files": [ 34 | "helpers/auth.php", 35 | "helpers/common.php", 36 | "helpers/datetime.php", 37 | "helpers/filesystem.php", 38 | "helpers/http.php", 39 | "helpers/objects.php" 40 | ] 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "Tests\\": "tests" 45 | } 46 | }, 47 | "scripts": { 48 | "test": "vendor/bin/phpunit --coverage-clover build/logs/clover.xml", 49 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 50 | }, 51 | "config": { 52 | "sort-packages": true 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /helpers/auth.php: -------------------------------------------------------------------------------- 1 | user($guard); 16 | } 17 | } 18 | 19 | if (!function_exists('logged_in')) { 20 | /** 21 | * Executes a single callback while the user is logged in. 22 | * 23 | * @param \Illuminate\Contracts\Auth\Authenticatable $user 24 | * @param callable $callback 25 | * @param string|null $guard 26 | * 27 | * @return mixed 28 | * @throws \RuntimeException 29 | */ 30 | function logged_in(Authenticatable $user, callable $callback, string $guard = null): mixed 31 | { 32 | $auth = app('auth')->guard($guard); 33 | 34 | if ($auth->user()) { 35 | throw new RuntimeException("A user [{$auth->user()->getAuthIdentifier()}] was already authenticated"); 36 | } 37 | 38 | $auth->login($user); 39 | 40 | return tap($callback($user), static function () use ($auth): void { 41 | $auth->logout(); 42 | }); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /helpers/common.php: -------------------------------------------------------------------------------- 1 | $callable 15 | * 16 | * @return mixed 17 | */ 18 | function data_update(mixed &$target, string|array $key, callable $callable): mixed 19 | { 20 | return data_set($target, $key, $callable(data_get($target, $key))); 21 | } 22 | } 23 | 24 | if (!function_exists('delist')) { 25 | /** 26 | * Returns the values of the array, so these can be listed into variables. 27 | * 28 | * @param array $items 29 | * @param int $offset 30 | * 31 | * @return array 32 | */ 33 | function delist(array $items, int $offset = 0): array 34 | { 35 | return array_slice(array_values($items), $offset); 36 | } 37 | } 38 | 39 | if (!function_exists('enclose')) { 40 | /** 41 | * Wraps a value or callable into a Closure, if it's not already callable. 42 | * 43 | * @param mixed $value 44 | * 45 | * @return \Closure 46 | */ 47 | function enclose(mixed $value): Closure 48 | { 49 | if ($value instanceof Closure) { 50 | return $value; 51 | } 52 | 53 | if (is_callable($value)) { 54 | return Closure::fromCallable($value); 55 | } 56 | 57 | return static function (mixed ...$params) use ($value) { 58 | return value($value, ...$params); 59 | }; 60 | } 61 | } 62 | 63 | if (!function_exists('hashy')) { 64 | /** 65 | * Creates a small BASE64 encoded MD5 hash from a string for portable checksum. 66 | * 67 | * @param \Stringable|\Illuminate\Support\Stringable|string $hashable 68 | * @param string|null $hash The hash to compare the result. 69 | * 70 | * @return string|bool Returns a boolean if a comparable hash has been set to compare. 71 | */ 72 | function hashy(Stringable|LaravelStringable|string $hashable, string $hash = null): string|bool 73 | { 74 | $hashed = base64_encode(md5((string)$hashable, true)); 75 | 76 | return $hash ? hash_equals($hash, $hashed) : $hashed; 77 | } 78 | } 79 | 80 | if (!function_exists('in_console')) { 81 | /** 82 | * Check if the application is running in console. 83 | * 84 | * @param callable|null $callback If added, it will be run if the application is in console. 85 | * 86 | * @return bool 87 | */ 88 | function in_console(callable $callback = null): bool 89 | { 90 | if (app()->runningInConsole()) { 91 | value($callback); 92 | return true; 93 | } 94 | 95 | // @codeCoverageIgnoreStart 96 | return false; 97 | // @codeCoverageIgnoreEnd 98 | } 99 | } 100 | 101 | if (!function_exists('in_development')) { 102 | /** 103 | * Check if the application is running in development environments (no testing, staging or production). 104 | * 105 | * @param callable|null $callback 106 | * 107 | * @return bool 108 | */ 109 | function in_development(callable $callback = null): bool 110 | { 111 | if (app()->environment('dev', 'development', 'local')) { 112 | value($callback); 113 | return true; 114 | } 115 | 116 | return false; 117 | } 118 | } 119 | 120 | if (!function_exists('none_of')) { 121 | /** 122 | * Checks if none of the options compared to a subject, or called with it, returns something truthy. 123 | * 124 | * @param mixed $subject 125 | * @param iterable $options 126 | * @param callable|null $callback 127 | * 128 | * @return bool 129 | */ 130 | function none_of(mixed $subject, iterable $options, callable $callback = null): bool 131 | { 132 | return false === which_of($subject, $options, $callback); 133 | } 134 | } 135 | 136 | if (!function_exists('object_assign')) { 137 | /** 138 | * Assigns an array of values to an object, recursively, using dot notation. 139 | * 140 | * @param object $object 141 | * @param \Illuminate\Contracts\Support\Arrayable|iterable $data 142 | * @param bool $overwrite 143 | * 144 | * @return object 145 | */ 146 | function object_assign(object $object, Arrayable|iterable $data, bool $overwrite = true): object 147 | { 148 | if ($data instanceof Arrayable) { 149 | $data = $data->toArray(); 150 | } 151 | 152 | foreach ($data as $name => $value) { 153 | data_set($object, $name, $value, $overwrite); 154 | } 155 | 156 | return $object; 157 | } 158 | } 159 | 160 | if (!function_exists('pipe')) { 161 | /** 162 | * Sends an object through a pipeline. 163 | * 164 | * @param mixed $passable 165 | * @param array|object[]|callable[]|string[]|class-string[] $pipes 166 | * @param callable|null $destination 167 | * 168 | * @return mixed 169 | */ 170 | function pipe(mixed $passable, array $pipes, callable $destination = null): mixed 171 | { 172 | $destination ??= static function (mixed $result): mixed { 173 | return $result; 174 | }; 175 | 176 | return app(Pipeline::class)->send($passable)->through($pipes)->then($destination); 177 | } 178 | } 179 | 180 | if (!function_exists('remember')) { 181 | /** 182 | * Retrieves an item from the cache, or stores a default value if the item doesn't exist. 183 | * 184 | * @param string $key 185 | * @param \Closure|\DateTimeInterface|\DateInterval|int $ttl 186 | * @param \Closure|\DateTimeInterface|\DateInterval|int|null $callback 187 | * @param int|null $lock If issued, it will lock the key and wait the same amount of seconds. 188 | * 189 | * @return mixed 190 | */ 191 | function remember( 192 | string $key, 193 | Closure|DateTimeInterface|DateInterval|int $ttl, 194 | Closure|DateTimeInterface|DateInterval|int $callback = null, 195 | int $lock = null 196 | ): mixed 197 | { 198 | $cache = app('cache'); 199 | 200 | if (is_callable($ttl)) { 201 | [$ttl, $callback, $lock] = [null, $ttl, $callback]; 202 | } 203 | 204 | if ($lock) { 205 | return $cache->lock($key, $lock)->block($ttl, static fn() => $cache->remember($key, $ttl, $callback)); 206 | } 207 | 208 | return $cache->remember($key, $ttl, $callback); 209 | } 210 | } 211 | 212 | if (!function_exists('shadow')) { 213 | /** 214 | * Calls a method on an object if it exists, or returns false. 215 | * 216 | * @param object $object 217 | * @param string $method 218 | * @param mixed ...$arguments 219 | * 220 | * @return mixed 221 | */ 222 | function shadow(object $object, string $method, mixed ...$arguments): mixed 223 | { 224 | if (method_exists($object, $method) || method_exists($object, 'hasMacro') && $object::hasMacro($method)) { 225 | return $object->{$method}(...$arguments); 226 | } 227 | 228 | return false; 229 | } 230 | } 231 | 232 | if (!function_exists('sleep_between')) { 233 | /** 234 | * Runs a callback while sleeping between multiple executions. 235 | * 236 | * It returns a collection of each callback result. 237 | * 238 | * @param int $times 239 | * @param int $sleep Milliseconds to sleep. 1000 equals to 1 second. 240 | * @param callable $callback 241 | * 242 | * @return \Illuminate\Support\Collection 243 | */ 244 | function sleep_between(int $times, int $sleep, callable $callback): Collection 245 | { 246 | $sleep *= 1000; 247 | 248 | return Collection::times($times, static function ($iteration) use ($callback, $sleep, $times): mixed { 249 | $result = $callback($iteration); 250 | 251 | if ($iteration < $times) { 252 | usleep($sleep); 253 | } 254 | 255 | return $result; 256 | }); 257 | } 258 | } 259 | 260 | if (!function_exists('taptap')) { 261 | /** 262 | * Call the given Closure with the given value then return the value, twice. 263 | * 264 | * @param mixed $value 265 | * @param callable|null $callback 266 | * @return mixed 267 | */ 268 | function taptap(mixed $value, callable $callback = null): mixed 269 | { 270 | return tap(tap($value, $callback)); 271 | } 272 | } 273 | 274 | if (!function_exists('which_of')) { 275 | /** 276 | * Returns the key of the option which comparison or callback returns true. 277 | * 278 | * If the callback returns something truthy, that value will be used. 279 | * 280 | * @param mixed $subject 281 | * @param iterable $options 282 | * @param callable|null $callback 283 | * 284 | * @return mixed If no results returns truthy, `false` will be returned. 285 | */ 286 | function which_of(mixed $subject, iterable $options, callable $callback = null): mixed 287 | { 288 | $callback = $callback ?? static function (mixed $subject, mixed $option): bool { 289 | return $subject === $option; 290 | }; 291 | 292 | foreach ($options as $key => $option) { 293 | if ($result = $callback($subject, $option)) { 294 | return $result === true ? $key : $result; 295 | } 296 | } 297 | 298 | return false; 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /helpers/datetime.php: -------------------------------------------------------------------------------- 1 | total($in); 27 | } 28 | } 29 | 30 | if (!function_exists('weekstart')) { 31 | /** 32 | * Returns the start of the week. 33 | * 34 | * @param \DateTimeInterface|string|int $from 35 | * @param int $weekStartAt 36 | * 37 | * @return \Illuminate\Support\Carbon 38 | */ 39 | function weekstart(DateTimeInterface|string|int $from = 'now', int $weekStartAt = 0): Carbon 40 | { 41 | return Carbon::parse($from)->startOfWeek($weekStartAt); 42 | } 43 | } 44 | 45 | if (!function_exists('weekend')) { 46 | /** 47 | * Returns the end of the week. 48 | * 49 | * @param \DateTimeInterface|string|int $from 50 | * @param int $weekStartAt 51 | * 52 | * @return \Illuminate\Support\Carbon 53 | */ 54 | function weekend(DateTimeInterface|string|int $from = 'now', int $weekStartAt = 0): Carbon 55 | { 56 | return Carbon::parse($from)->endOfWeek($weekStartAt); 57 | } 58 | } 59 | 60 | if (!function_exists('until')) { 61 | /** 62 | * Returns the interval from a date until the desired date. 63 | * 64 | * @param \DateTimeInterface|string|int $from 65 | * @param \DateTimeInterface|string|int $until 66 | * @param bool $absolute 67 | * 68 | * @return \Carbon\CarbonInterval 69 | */ 70 | function until( 71 | DateTimeInterface|string|int $from, 72 | DateTimeInterface|string|int $until, 73 | bool $absolute = true 74 | ): CarbonInterval 75 | { 76 | return Carbon::parse($from)->diffAsCarbonInterval($until, $absolute); 77 | } 78 | } 79 | 80 | if (! function_exists('period')) { 81 | /** 82 | * Returns the period of a given start and end or interval. 83 | * 84 | * @param \DateTimeInterface|string|int $start 85 | * @param \DateTimeInterface|\DateInterval|string|int $end 86 | * @param \DateInterval|string|int|null $interval 87 | * @param string|null $unit If specified, $interval must be an integer 88 | * 89 | * @return \Carbon\CarbonPeriod 90 | */ 91 | function period( 92 | DateTimeInterface|string|int $start, 93 | DateTimeInterface|DateInterval|string|int $end, 94 | DateInterval|string|int $interval = null, 95 | string $unit = null 96 | ): CarbonPeriod 97 | { 98 | return Carbon::parse($start)->toPeriod($end, $interval, $unit); 99 | } 100 | } 101 | 102 | if (!function_exists('period_from')) { 103 | /** 104 | * Returns the next or previous period from given date, exclusive. 105 | * 106 | * @param \DatePeriod|\Carbon\CarbonPeriod $periods If it's an Interval, it will be a single period. 107 | * @param \DateTimeInterface|string|int $at 108 | * @param bool $after When `true`, it will check if it's after or equal the date. 109 | * @param bool $inclusive When `true`, dates equal to the compared datatime will be considered. 110 | * 111 | * @return \Illuminate\Support\Carbon|null Returns `null` if the next period is outside `$at`. 112 | */ 113 | function period_from( 114 | DatePeriod|CarbonPeriod $periods, 115 | DateTimeInterface|string|int $at = 'now', 116 | bool $after = true, 117 | bool $inclusive = false, 118 | ): ?Carbon 119 | { 120 | $periods = CarbonPeriod::instance($periods); 121 | 122 | $at = Carbon::parse($at); 123 | 124 | $call = $after ? 'greaterThan' : 'lessThan'; 125 | 126 | if ($inclusive) { 127 | $call .= 'OrEqualTo'; 128 | } 129 | 130 | return $after 131 | ? Collection::make($periods)->first->{$call}($at) 132 | : Collection::make($periods)->reverse()->first->{$call}($at); 133 | } 134 | } 135 | 136 | if (!function_exists('yesterday')) { 137 | /** 138 | * Returns the date for yesterday. 139 | * 140 | * @param \DateTimeZone|string|null $tz 141 | * 142 | * @return \Illuminate\Support\Carbon 143 | */ 144 | function yesterday(DateTimeZone|string $tz = null): Carbon 145 | { 146 | return Carbon::yesterday($tz); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /helpers/filesystem.php: -------------------------------------------------------------------------------- 1 | replace(['\\', '/'], '.')->trim('.'); 18 | } 19 | } 20 | 21 | if (!function_exists('files')) { 22 | /** 23 | * Returns the local Filesystem helper, or a list of files in a path. 24 | * 25 | * @param string|null $path 26 | * @param bool $recursive 27 | * 28 | * @return \Illuminate\Filesystem\Filesystem|\Illuminate\Support\Collection<\SplFileInfo>|\SplFileInfo[] 29 | */ 30 | function files(string $path = null, bool $recursive = false): Filesystem|Collection 31 | { 32 | $filesystem = app('files'); 33 | 34 | if (! $path) { 35 | return $filesystem; 36 | } 37 | 38 | $path = base_path($path); 39 | 40 | return Collection::make($recursive ? $filesystem->allFiles($path) : $filesystem->files($path)); 41 | } 42 | } 43 | 44 | if (!function_exists('undot_path')) { 45 | /** 46 | * Transforms a path from dot notation to a relative path. 47 | * 48 | * @param string $dotPath 49 | * 50 | * @return string 51 | */ 52 | function undot_path(string $dotPath): string 53 | { 54 | return (string) Str::of($dotPath)->replace('.', DIRECTORY_SEPARATOR)->trim(DIRECTORY_SEPARATOR); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /helpers/http.php: -------------------------------------------------------------------------------- 1 | noContent(204, $headers); 32 | } 33 | } 34 | 35 | if (!function_exists('route_is')) { 36 | /** 37 | * Determine whether the current route's name matches the given patterns. 38 | * 39 | * @param string ...$patterns 40 | * 41 | * @return bool It will always return `false` when outside an HTTP Request. 42 | */ 43 | function route_is(string ...$patterns): bool 44 | { 45 | return (bool) app('request')->routeIs(...$patterns); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /helpers/objects.php: -------------------------------------------------------------------------------- 1 | call($callback, $parameters); 17 | } 18 | } 19 | 20 | if (!function_exists('call_existing')) { 21 | /** 22 | * Calls a dynamic method or macro if it exists in the object instance. 23 | * 24 | * @param object|string $object $object 25 | * @param string $method 26 | * @param mixed ...$parameters 27 | * 28 | * @return mixed Returns `false` if the call wasn't executed. 29 | */ 30 | function call_existing(object|string $object, string $method, mixed ...$parameters): mixed 31 | { 32 | if (method_exists($object, $method) || (method_exists($object, 'hasMacro') && $object::hasMacro($method))) { 33 | return call_user_func_array([$object, $method], $parameters); 34 | } 35 | 36 | return false; 37 | } 38 | } 39 | 40 | if (!function_exists('has_trait')) { 41 | /** 42 | * Checks recursively if the object is using a single trait. 43 | * 44 | * @param object|string $object 45 | * @param string $trait 46 | * 47 | * @return bool 48 | */ 49 | function has_trait(object|string $object, string $trait): bool 50 | { 51 | return in_array($trait, class_uses_recursive($object), true); 52 | } 53 | } 54 | 55 | if (!function_exists('methods_of')) { 56 | /** 57 | * Returns a collection of all public methods from a given class or object. 58 | * 59 | * @param object|string $object 60 | * @param \Closure|int $filter 61 | * 62 | * @return \Illuminate\Support\Collection<\ReflectionMethod>|\ReflectionMethod[] 63 | */ 64 | function methods_of(object|string $object, Closure|int $filter = ReflectionMethod::IS_PUBLIC): Collection 65 | { 66 | $reflection = new ReflectionClass($object); 67 | 68 | $collection = $filter instanceof Closure 69 | ? Collection::make($reflection->getMethods())->filter($filter) 70 | : Collection::make($reflection->getMethods($filter)); 71 | 72 | return $collection->keyBy(static function (ReflectionMethod $method): string { 73 | return $method->name; 74 | }); 75 | } 76 | } 77 | 78 | if (!function_exists('missing_trait')) { 79 | /** 80 | * Checks recursively if the object is not using a trait. 81 | * 82 | * @param object|string $object 83 | * @param string $trait 84 | * 85 | * @return bool 86 | */ 87 | function missing_trait(object|string $object, string $trait): bool 88 | { 89 | return !has_trait($object, $trait); 90 | } 91 | } 92 | 93 | if (!function_exists('properties_of')) { 94 | /** 95 | * Returns a collection of all public properties from a given class or object. 96 | * 97 | * @param object|string $object 98 | * @param \Closure|int $filter 99 | * 100 | * @return \Illuminate\Support\Collection 101 | */ 102 | function properties_of(object|string $object, Closure|int $filter = ReflectionProperty::IS_PUBLIC): Collection 103 | { 104 | $reflection = new ReflectionClass($object); 105 | 106 | $collection = $filter instanceof Closure 107 | ? Collection::make($reflection->getProperties())->filter($filter) 108 | : Collection::make($reflection->getProperties($filter)); 109 | 110 | return $collection->keyBy(static function (ReflectionProperty $property): string { 111 | return $property->name; 112 | }); 113 | } 114 | } 115 | --------------------------------------------------------------------------------