🆙 Add cms i using 🆙

This commit is contained in:
Remco
2025-11-25 22:42:56 +01:00
parent 94704e0925
commit d44196149e
35591 changed files with 3601123 additions and 0 deletions
@@ -0,0 +1,40 @@
<?php
$finder = Symfony\Component\Finder\Finder::create()
->in([
__DIR__ . '/src',
__DIR__ . '/tests',
])
->name('*.php')
->notName('*.blade.php')
->ignoreDotFiles(true)
->ignoreVCS(true);
return (new PhpCsFixer\Config())
->setRules([
'@PSR12' => true,
'array_syntax' => ['syntax' => 'short'],
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
'not_operator_with_successor_space' => true,
'trailing_comma_in_multiline' => true,
'phpdoc_scalar' => true,
'unary_operator_spaces' => true,
'binary_operator_spaces' => true,
'blank_line_before_statement' => [
'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
],
'phpdoc_single_line_var_spacing' => true,
'phpdoc_var_without_name' => true,
'class_attributes_separation' => [
'elements' => [
'method' => 'one',
],
],
'method_argument_space' => [
'on_multiline' => 'ensure_fully_multiline',
'keep_multiple_spaces_after_comma' => true,
],
'single_trait_insert_per_statement' => true,
])
->setFinder($finder);
@@ -0,0 +1,753 @@
# Changelog
All notable changes to `ray` will be documented in this file
## 1.44.0 - 2025-11-20
### What's Changed
* Provide backwards compatibility with PHPStan 1.x. by @pfrenssen in https://github.com/spatie/ray/pull/952
* Update Dependencies by @sweptsquash in https://github.com/spatie/ray/pull/955
* Bump dependabot/fetch-metadata from 2.2.0 to 2.3.0 by @dependabot[bot] in https://github.com/spatie/ray/pull/961
* Laravel 12.x Compatibility by @laravel-shift in https://github.com/spatie/ray/pull/966
* Use static:: instead of self:: for late static binding by @TumTum in https://github.com/spatie/ray/pull/970
* Add offset and limit support to trace by @dimitri-koenig in https://github.com/spatie/ray/pull/974
* Bump dependabot/fetch-metadata from 2.3.0 to 2.4.0 by @dependabot[bot] in https://github.com/spatie/ray/pull/979
* Update issue template by @AlexVanderbist in https://github.com/spatie/ray/pull/986
* Fix PHP 8.5 deprecations by @IonBazan in https://github.com/spatie/ray/pull/989
* Bump stefanzweifel/git-auto-commit-action from 5 to 7 by @dependabot[bot] in https://github.com/spatie/ray/pull/988
* Bump actions/checkout from 4 to 5 by @dependabot[bot] in https://github.com/spatie/ray/pull/984
* Fix searchConfigFilesOnDisk to handle empty string parameter by @bjo3rnf in https://github.com/spatie/ray/pull/992
### New Contributors
* @pfrenssen made their first contribution in https://github.com/spatie/ray/pull/952
* @laravel-shift made their first contribution in https://github.com/spatie/ray/pull/966
* @TumTum made their first contribution in https://github.com/spatie/ray/pull/970
* @dimitri-koenig made their first contribution in https://github.com/spatie/ray/pull/974
* @IonBazan made their first contribution in https://github.com/spatie/ray/pull/989
* @bjo3rnf made their first contribution in https://github.com/spatie/ray/pull/992
**Full Changelog**: https://github.com/spatie/ray/compare/1.41.3...1.44.0
## 1.43.1 - 2025-10-29
### What's Changed
* Fix searchConfigFilesOnDisk to handle empty string parameter by @bjo3rnf in https://github.com/spatie/ray/pull/992
### New Contributors
* @bjo3rnf made their first contribution in https://github.com/spatie/ray/pull/992
**Full Changelog**: https://github.com/spatie/ray/compare/1.43.0...1.43.1
## 1.43.0 - 2025-10-16
### What's Changed
* Bump dependabot/fetch-metadata from 2.3.0 to 2.4.0 by @dependabot[bot] in https://github.com/spatie/ray/pull/979
* Update issue template by @AlexVanderbist in https://github.com/spatie/ray/pull/986
* Fix PHP 8.5 deprecations by @IonBazan in https://github.com/spatie/ray/pull/989
* Bump stefanzweifel/git-auto-commit-action from 5 to 7 by @dependabot[bot] in https://github.com/spatie/ray/pull/988
* Bump actions/checkout from 4 to 5 by @dependabot[bot] in https://github.com/spatie/ray/pull/984
### New Contributors
* @IonBazan made their first contribution in https://github.com/spatie/ray/pull/989
**Full Changelog**: https://github.com/spatie/ray/compare/1.42.0...1.43.0
## 1.42.0 - 2025-04-18
### What's Changed
* Add offset and limit support to trace by @dimitri-koenig in https://github.com/spatie/ray/pull/974
### New Contributors
* @dimitri-koenig made their first contribution in https://github.com/spatie/ray/pull/974
**Full Changelog**: https://github.com/spatie/ray/compare/1.41.6...1.42.0
## 1.41.6 - 2025-03-21
### What's Changed
* Use static:: instead of self:: for late static binding by @TumTum in https://github.com/spatie/ray/pull/970
### New Contributors
* @TumTum made their first contribution in https://github.com/spatie/ray/pull/970
**Full Changelog**: https://github.com/spatie/ray/compare/1.41.5...1.41.6
## 1.41.5 - 2025-02-14
### What's Changed
* Update Dependencies by @sweptsquash in https://github.com/spatie/ray/pull/955
* Bump dependabot/fetch-metadata from 2.2.0 to 2.3.0 by @dependabot in https://github.com/spatie/ray/pull/961
* Laravel 12.x Compatibility by @laravel-shift in https://github.com/spatie/ray/pull/966
### New Contributors
* @laravel-shift made their first contribution in https://github.com/spatie/ray/pull/966
**Full Changelog**: https://github.com/spatie/ray/compare/1.41.4...1.41.5
## 1.41.4 - 2024-12-09
### What's Changed
* Provide backwards compatibility with PHPStan 1.x. by @pfrenssen in https://github.com/spatie/ray/pull/952
### New Contributors
* @pfrenssen made their first contribution in https://github.com/spatie/ray/pull/952
**Full Changelog**: https://github.com/spatie/ray/compare/1.41.3...1.41.4
## 1.41.3 - 2024-12-02
### What's Changed
* Fixes an issue where some complex variables would not be shown in Ray.
* Bump dependabot/fetch-metadata from 2.0.0 to 2.1.0 by @dependabot in https://github.com/spatie/ray/pull/909
* Bump dependabot/fetch-metadata from 2.1.0 to 2.2.0 by @dependabot in https://github.com/spatie/ray/pull/928
* Add support for PHPStan 2.0 by @sweptsquash in https://github.com/spatie/ray/pull/946
### New Contributors
* @sweptsquash made their first contribution in https://github.com/spatie/ray/pull/946
**Full Changelog**: https://github.com/spatie/ray/compare/1.41.2...1.41.3
## 1.41.2 - 2024-04-24
### What's Changed
* Bump dependabot/fetch-metadata from 1.6.0 to 2.0.0 by @dependabot in https://github.com/spatie/ray/pull/900
* Fixes `Typed property Symfony\Component\VarDumper\Dumper\CliDumper::$colors must not be accessed before initialization` by @crynobone in https://github.com/spatie/ray/pull/885
* Make implicit nullable param to explicit (PHP 8.4 compatibility) by @GromNaN in https://github.com/spatie/ray/pull/903
* Add branch-alias for version 1.x by @GromNaN in https://github.com/spatie/ray/pull/905
* Remove trailing comma from JSON by @GromNaN in https://github.com/spatie/ray/pull/906
* Last explicit nullable param (PHP 8.4 compatibility) by @GromNaN in https://github.com/spatie/ray/pull/908
### New Contributors
* @GromNaN made their first contribution in https://github.com/spatie/ray/pull/903
**Full Changelog**: https://github.com/spatie/ray/compare/1.41.1...1.41.2
## 1.41.1 - 2024-01-25
**Full Changelog**: https://github.com/spatie/ray/compare/1.41.0...1.41.1
## 1.41.0 - 2024-01-24
### What's Changed
* Allow symfony 7.x by @thecaliskan in https://github.com/spatie/ray/pull/881
* docs(app): docker add step to open gateway for remote by @ValentinGaudin in https://github.com/spatie/ray/pull/882
* Add rector rule to remove ray calls from a codebase by @timvandijck in https://github.com/spatie/ray/pull/884
* Bump actions/cache from 3 to 4 by @dependabot in https://github.com/spatie/ray/pull/888
### New Contributors
* @thecaliskan made their first contribution in https://github.com/spatie/ray/pull/881
* @ValentinGaudin made their first contribution in https://github.com/spatie/ray/pull/882
**Full Changelog**: https://github.com/spatie/ray/compare/1.40.1...1.41.0
## 1.40.1 - 2023-11-20
### What's Changed
- Add showDuplicateQueries() to reference page by @AndreasHerss in https://github.com/spatie/ray/pull/865
- Implement cURL Handle Caching by @JSlush611 in https://github.com/spatie/ray/pull/864
### New Contributors
- @AndreasHerss made their first contribution in https://github.com/spatie/ray/pull/865
- @JSlush611 made their first contribution in https://github.com/spatie/ray/pull/864
**Full Changelog**: https://github.com/spatie/ray/compare/1.40.0...1.40.1
## 1.40.0 - 2023-11-07
### What's Changed
- Add support for calling closures before doing a ray request by @SebastiaanKloos in https://github.com/spatie/ray/pull/859
- Bump stefanzweifel/git-auto-commit-action from 4 to 5 by @dependabot in https://github.com/spatie/ray/pull/850
**Full Changelog**: https://github.com/spatie/ray/compare/1.39.0...1.40.0
## 1.39.0 - 2023-09-18
- add `expandAll()`
## 1.38.0 - 2023-09-12
### What's Changed
- Add expand payload by @freekmurze in https://github.com/spatie/ray/pull/843
**Full Changelog**: https://github.com/spatie/ray/compare/1.37.7...1.38.0
## 1.37.7 - 2023-09-08
- Fix issue with outputing arrays for copying.
**Full Changelog**: https://github.com/spatie/ray/compare/1.37.6...1.37.7
## 1.37.6 - 2023-09-08
### What's Changed
- Fix/issue 832 by @timvandijck in https://github.com/spatie/ray/pull/839
**Full Changelog**: https://github.com/spatie/ray/compare/1.37.5...1.37.6
## 1.37.5 - 2023-09-06
### What's Changed
- Add details about returning the closure return value by @grantholle in https://github.com/spatie/ray/pull/834
- Check for recursiveness before creating clipboard data.
### New Contributors
- @grantholle made their first contribution in https://github.com/spatie/ray/pull/834
**Full Changelog**: https://github.com/spatie/ray/compare/1.37.4...1.37.5
## 1.37.4 - 2023-09-04
- Fix a bug with Livewire 3 components being rendered instead of sent to Ray as a class
**Full Changelog**: https://github.com/spatie/ray/compare/1.37.3...1.37.4
## 1.37.3 - 2023-09-01
### What's Changed
- Bump dependabot/fetch-metadata from 1.4.0 to 1.5.1 by @dependabot in https://github.com/spatie/ray/pull/800
- Bump dependabot/fetch-metadata from 1.5.1 to 1.6.0 by @dependabot in https://github.com/spatie/ray/pull/806
- Feature/clipboard by @timvandijck in https://github.com/spatie/ray/pull/829
### New Contributors
- @timvandijck made their first contribution in https://github.com/spatie/ray/pull/829
**Full Changelog**: https://github.com/spatie/ray/compare/1.37.2...1.37.3
## 1.37.2 - 2023-05-17
- improve handling of Pest test screen names
## 1.37.1 - 2023-03-06
### What's Changed
- [1.x] Add phpstan rule to detect ray calls by @abenerd in https://github.com/spatie/ray/pull/781
**Full Changelog**: https://github.com/spatie/ray/compare/1.37.0...1.37.1
## 1.37.0 - 2023-03-03
- add `invade` method
## 1.36.2 - 2023-02-10
- do not send context if there is none
**Full Changelog**: https://github.com/spatie/ray/compare/1.36.1...1.36.2
## 1.36.1 - 2023-02-09
- support log context
## 1.36.0 - 2022-08-11
- preparation for upcoming feature
## 1.35.0 - 2022-08-09
### What's Changed
- Sync Laravel sample config `ray.php` with the latest in `laravel-ray` by @squatto in https://github.com/spatie/ray/pull/706
- Added createFromArray method on SettingsFactory by @doekenorg in https://github.com/spatie/ray/pull/708
- Update reference.md by @WouterBrouwers in https://github.com/spatie/ray/pull/712
- Fixed typo showQueries to showDuplicateQueries by @xitox97 in https://github.com/spatie/ray/pull/714
- Add slow_query_threshold to laravel docs by @fullstackfool in https://github.com/spatie/ray/pull/718
### New Contributors
- @doekenorg made their first contribution in https://github.com/spatie/ray/pull/708
- @xitox97 made their first contribution in https://github.com/spatie/ray/pull/714
- @fullstackfool made their first contribution in https://github.com/spatie/ray/pull/718
**Full Changelog**: https://github.com/spatie/ray/compare/1.34.5...1.35.0
## 1.34.5 - 2022-06-03
### What's Changed
- Bug/702 long integers by @Nielsvanpach in https://github.com/spatie/ray/pull/704
**Full Changelog**: https://github.com/spatie/ray/compare/1.34.4...1.34.5
## 1.34.4 - 2022-06-03
- allow v2 of macroable
## 1.34.3 - 2022-05-31
### What's Changed
- Fix small JavaScript related documentations by @Yago in https://github.com/spatie/ray/pull/687
- Fix tiny typo by @jrmajor in https://github.com/spatie/ray/pull/700
- Revert version of spatie/macroable to ^1.0 by @SebKay in https://github.com/spatie/ray/pull/703
### New Contributors
- @Yago made their first contribution in https://github.com/spatie/ray/pull/687
- @jrmajor made their first contribution in https://github.com/spatie/ray/pull/700
- @SebKay made their first contribution in https://github.com/spatie/ray/pull/703
**Full Changelog**: https://github.com/spatie/ray/compare/1.34.2...1.34.3
## 1.34.2 - 2022-04-08
## What's Changed
- Fix typo by @jeffreyvr in https://github.com/spatie/ray/pull/647
- Update alpinejs.md by @peterfox in https://github.com/spatie/ray/pull/649
- Update documentation for Alpine.js integration by @patinthehat in https://github.com/spatie/ray/pull/650
- Minor Documentation Update by @patinthehat in https://github.com/spatie/ray/pull/651
- Update Javascript Documentation by @patinthehat in https://github.com/spatie/ray/pull/652
- Add docs for express.js integration by @patinthehat in https://github.com/spatie/ray/pull/654
- Fix class import outside of code block by @rostockahoi in https://github.com/spatie/ray/pull/663
- Added a verification that the Yii class exists by @FR6 in https://github.com/spatie/ray/pull/675
## New Contributors
- @jeffreyvr made their first contribution in https://github.com/spatie/ray/pull/647
- @peterfox made their first contribution in https://github.com/spatie/ray/pull/649
- @rostockahoi made their first contribution in https://github.com/spatie/ray/pull/663
**Full Changelog**: https://github.com/spatie/ray/compare/1.34.1...1.34.2
## 1.34.1 - 2022-03-03
- remove stray `print_r` call
## 1.34.0 - 2022-03-03
- add support for global Ray
## 1.33.2 - 2022-02-02
## What's Changed
- Return false if success variable not defined by @cmgmyr in https://github.com/spatie/ray/pull/635
## New Contributors
- @cmgmyr made their first contribution in https://github.com/spatie/ray/pull/635
**Full Changelog**: https://github.com/spatie/ray/compare/1.33.1...1.33.2
## 1.33.1 - 2022-01-17
## What's Changed
- Ignore docs folder on package install by @fschirinzi in https://github.com/spatie/ray/pull/625
## New Contributors
- @fschirinzi made their first contribution in https://github.com/spatie/ray/pull/625
**Full Changelog**: https://github.com/spatie/ray/compare/1.33.0...1.33.1
## 1.33.0 - 2022-01-13
1.33.0
- add support for screen colors
- add support for project names
- send hostname along with request
## 1.33.0 - 2022-01-13
- add support for screen colors
- add support for project names
- send hostname along with request
## 1.32.3 - 2022-01-09
- allow Laravel 9
## 1.32.2 - 2021-12-20
- allow symfony 6
## 1.32.1 - 2021-11-30
- fix deprecation warning in PHP 8.1
## 1.32.0 - 2021-11-26
## What's Changed
- Add separator payload by @freekmurze in https://github.com/spatie/ray/pull/599
**Full Changelog**: https://github.com/spatie/ray/compare/1.31.0...1.32.0
## 1.31.0 - 2021-11-17
## What's Changed
- Docs for duplicate queries logging by @masterix21 in https://github.com/spatie/ray/pull/560
- Add context to ApplicationLog by @keithbrink in https://github.com/spatie/ray/pull/562
## New Contributors
- @masterix21 made their first contribution in https://github.com/spatie/ray/pull/560
- @keithbrink made their first contribution in https://github.com/spatie/ray/pull/562
**Full Changelog**: https://github.com/spatie/ray/compare/1.30.4...1.31.0
## 1.30.4 - 2021-11-11
## What's Changed
- Point to "servers" instead of singular "server" by @jmslbam in https://github.com/spatie/ray/pull/583
- Fix for sending "value" (by @freekmurze)
**Full Changelog**: https://github.com/spatie/ray/compare/1.30.3...1.30.4
## 1.30.3 - 2021-10-08
- Bug/567 global functions (#573)
## 1.30.2 - 2021-09-10
- align carbon call argument type with carbon payload (#556)
## 1.30.1 - 2021-09-07
- support PHP 8.1
## 1.30.0 - 2021-08-20
- add `catch` method
## 1.29.2 - 2021-08-15
- revert curl check
## 1.29.1 - 2021-08-14
- fix curl check
## 1.29.0 - 2021-08-02
- add `label` method
## 1.28.0 - 2021-07-04
- add support for base64-encoded images (#499)
## 1.27.1 - 2021-06-24
- remove typehint to allow override
## 1.27.0 - 2021-06-23
- add `once()` (#481)
## 1.26.0 - 2021-06-10
- add rate limiter
## 1.25.0 - 2021-06-07
- add `if` method
## 1.24.0 - 2021-06-04
- add limit method (#464)
## 1.23.0 - 2021-05-29
- add `text` method (#460)
## 1.22.1 - 2021-04-28
- allow Throwables to be logged
## 1.22.0 - 2021-04-28
- access named counter values (#437)
## 1.21.4 - 2021-04-17
- color exceptions red by default
## 1.21.3 - 2021-04-14
- allow spatie/macroable v2 [#426](https://github.com/spatie/ray/pull/426)
## 1.21.2 - 2021-03-04
- fix hostname for other ray packages
## 1.21.1 - 2021-03-03
- do not require hostname
## 1.21.0 - 2021-03-03
- add `hostname` in the origin section of a payload
## 1.20.1 - 2021-02-26
- fix config loading priorities in other packages
## 1.20.0 - 2021-02-22
- add `exception` method
## 1.19.5 - 2021-02-17
- allow instances of `CarbonInterface` to be used for `CarbonPayload` (#316)
## 1.19.4 - 2021-02-11
- fix enabled status (#301)
## 1.19.3 - 2021-02-09
- fix Client cache fingerprint initialization (#292)
## 1.19.2 - 2021-02-09
- add curl throttling after failed connection (#286)
## 1.19.1 - 2021-02-08
- allow symfony/stopwatch 4.0 (#284)
## 1.19.0 - 2021-02-03
- send XML payloads (#272)
## 1.18.0 - 2021-02-03
- add `enable` and `disable` methods
## 1.17.4 - 2021-02-03
- fix: remote_path/local_path replacements (#269)
## 1.17.3 - 2021-02-02
- use http v1.1 instead of 1.0 (#267)
## 1.17.2 - 2021-02-02
- cache config file
## 1.17.1 - 2021-01-27
- add support for PHP 7.3
## 1.17.0 - 2021-01-25
- add `showApp` and `hideApp`
## 1.16.0 - 2021-01-22
- add `phpinfo` method
## 1.15.0 - 2021-01-22
- add `table` method
## 1.14.1 - 2021-01-22
- fix bug when `remote_path` is also in `filePath` (#227)
## 1.14.0 - 2021-01-20
- Add support for CraftRay
## 1.13.0 - 2021-01-18
- the package will now select the best payload type when passing something to `ray()`
- added `html` method
- added `NullPayload`
- added `BoolPayload`
## 1.12.0 - 2021-01-18
- add `carbon`
## 1.11.1 - 2021-01-17
- lower deps
## 1.11.0 - 2021-01-15
- add `image()`
## 1.10.0 - 2021-01-15
- add `clearAll`
## 1.9.2 - 2021-01-15
- fix bugs around settings
## 1.9.1 - 2021-01-15
- improve helper functions
## 1.9.0 - 2021-01-15
- add `count`
## 1.8.0 - 2021-01-14
- add a check for YiiRay's instance
## 1.7.2 - 2021-01-13
- when passing `null`, let argument convertor return `null`
## 1.7.1 - 2021-01-13
- improve return type of ray function
## 1.7.0 - 2021-01-13
- support multiple arguments to `toJson()` and `json()` (#148)
## 1.6.1 - 2021-01-13
- prevent possible memory leak (#143)
## 1.6.0 - 2021-01-13
- add `file` function (#134)
## 1.5.10 - 2021-01-13
- allow better compatibility with WordPress
## 1.5.9 - 2021-01-13
- ignore package version errors
## 1.5.8 - 2021-01-13
- ignore package check errors
## 1.5.7 - 2021-01-13
- remove unneeded symfony/console dependency
## 1.5.6 - 2021-01-13
- allow lower dependencies
## 1.5.5 - 2021-01-11
- split origin factory in overridable functions
## 1.5.4 - 2021-01-11
- support WordPressRay
## 1.5.3 - 2021-01-10
- fix for traces of WordPress
## 1.5.2 - 2021-01-10
- colorize app frames
## 1.5.1 - 2021-01-10
- polish json functions
## 1.5.0 - 2021-01-09
- add `json` function
## 1.4.0 - 2021-01-09
- add `rd` function
## 1.3.7 - 2021-01-09
- add `vendor_frame` attribute to frames
## 1.3.6 - 2021-01-09
- allow older version of uuid package
## 1.3.5 - 2021-01-09
- fix search for `$indexOfRay` to include calls from the parent directory (#80)
## 1.3.4 - 2021-01-08
- prevent warning if `open_basedir` is enabled
## 1.3.3 - 2021-01-08
- do not require Composer 2
## 1.3.2 - 2021-01-08
- prevent ray from blowing up when there is no config file
## 1.3.1 - 2021-01-08
- do not blow up when the Ray app is not running
## 1.3.0 - 2021-01-08
- add support for `remote_path` and `local_path` config values
## 1.2.0 - 2021-01-08
- add `pass` function
## 1.1.3 - 2021-01-08
- prevent exception when installing in an Orchestra powered testsuite
## 1.1.2 - 2021-01-08
- enforce Composer 2 requirement
### 1.1.1 - 2021-01-08
- fix for repeated calls to `ray()` throwing an exception (#30)
## 1.1.0 - 2021-01-07
- add `makePathOsSafe`
- fix tests
## 1.0.1 - 2021-01-07
- fix default settings
## 1.0.0 - 2021-01-07
- initial release
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Spatie bvba <info@spatie.be>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,48 @@
# Debug with Ray to fix problems faster
[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/ray.svg?style=flat-square)](https://packagist.org/packages/spatie/ray)
![Tests](https://github.com/spatie/ray/workflows/Tests/badge.svg)
[![Total Downloads](https://img.shields.io/packagist/dt/spatie/laravel-ray.svg?style=flat-square)](https://packagist.org/packages/spatie/ray)
This package can be installed in any PHP application to send messages to [the Ray app](https://myray.app).
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/ray.jpg?t=2" width="419px" />](https://spatie.be/github-ad-click/ray)
The desktop app:
- can be used in WordPress, Laravel, PHP, JavaScript function
- shows you models, mails, queries, ... IN Laravel
- helps you to debug locally or via SSH
- lets you measure performance & set breakpoints
## Documentation
You can find the full documentation on [our documentation site](https://spatie.be/docs/ray).
## Testing
```bash
composer test
```
## Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
## Contributing
Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.
## Security Vulnerabilities
Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
## Credits
- [Freek Van der Herten](https://github.com/freekmurze)
- [All Contributors](../../contributors)
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
@@ -0,0 +1,10 @@
#!/bin/bash
# Check if the argument is provided
if [ -z "$1" ]; then
echo "Usage: $0 <target>"
exit 1
fi
# Execute the Rector command with the provided target
vendor/bin/rector process "$1" --config vendor/spatie/ray/remove-ray-rector.php
@@ -0,0 +1,83 @@
{
"name": "spatie/ray",
"description": "Debug with Ray to fix problems faster",
"license": "MIT",
"keywords": [
"spatie",
"ray"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"homepage": "https://github.com/spatie/ray",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/spatie"
},
{
"type": "other",
"url": "https://spatie.be/open-source/support-us"
}
],
"require": {
"php": "^7.4|^8.0",
"ext-curl": "*",
"ext-json": "*",
"ramsey/uuid": "^3.0|^4.1",
"spatie/backtrace": "^1.7.1",
"spatie/macroable": "^1.0|^2.0",
"symfony/stopwatch": "^4.2|^5.1|^6.0|^7.0|^8.0",
"symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0.3|^8.0"
},
"require-dev": {
"illuminate/support": "^7.20|^8.18|^9.0|^10.0|^11.0|^12.0",
"nesbot/carbon": "^2.63|^3.8.4",
"pestphp/pest": "^1.22",
"phpstan/phpstan": "^1.10.57|^2.0.3",
"phpunit/phpunit": "^9.5",
"rector/rector": "^0.19.2|^1.0.1|^2.0.0",
"spatie/phpunit-snapshot-assertions": "^4.2",
"spatie/test-time": "^1.2"
},
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
"psr-4": {
"Spatie\\Ray\\": "src"
},
"files": [
"src/helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"Spatie\\Ray\\Tests\\": "tests"
}
},
"bin": [
"bin/remove-ray.sh"
],
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true
},
"sort-packages": true
},
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"scripts": {
"format": "vendor/bin/php-cs-fixer fix --allow-risky=yes",
"phpstan": "vendor/bin/phpstan analyse",
"test": "vendor/bin/pest",
"test-coverage": "vendor/bin/pest --coverage"
}
}
@@ -0,0 +1,16 @@
parameters:
ignoreErrors:
-
message: "#^Function app not found\\.$#"
count: 1
path: src/helpers.php
-
message: "#^Instantiated class Spatie\\\\RayBundle\\\\Ray not found\\.$#"
count: 1
path: src/helpers.php
-
message: "#^Instantiated class Spatie\\\\WordPressRay\\\\Ray not found\\.$#"
count: 1
path: src/helpers.php
@@ -0,0 +1,14 @@
includes:
- phpstan-baseline.neon
parameters:
level: 1
paths:
- src
- tests
ignoreErrors:
- '#Unsafe usage of new static\(\)#'
-
message: '#Undefined variable\: \$this#'
path: tests
@@ -0,0 +1,8 @@
<?php
use Rector\Config\RectorConfig;
use Spatie\Ray\Rector\RemoveRayCallRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(RemoveRayCallRector::class);
};
@@ -0,0 +1,36 @@
<?php
namespace Spatie\Ray;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
class ArgumentConverter
{
public static function convertToPrimitive($argument)
{
if (is_null($argument)) {
return null;
}
if (is_string($argument)) {
return $argument;
}
if (is_int($argument)) {
return $argument;
}
if (is_bool($argument)) {
return $argument;
}
$cloner = new VarCloner();
$dumper = new HtmlDumper();
$clonedArgument = $cloner->cloneVar($argument);
return $dumper->dump($clonedArgument, true);
}
}
@@ -0,0 +1,176 @@
<?php
namespace Spatie\Ray;
use Exception;
use Spatie\Ray\Exceptions\StopExecutionRequested;
use Spatie\Ray\Origin\Hostname;
class Client
{
protected static $cache = [];
/** @var int */
protected $portNumber;
/** @var string */
protected $host;
/** @var string */
protected $fingerprint;
/** @var mixed */
protected $curlHandle = null;
public function __construct(int $portNumber = 23517, string $host = 'localhost')
{
$this->fingerprint = $host . ':' . $portNumber;
$this->portNumber = $portNumber;
$this->host = $host;
}
public function __destruct()
{
if ($this->curlHandle) {
if (version_compare(PHP_VERSION, '8.5.0', '<')) {
curl_close($this->curlHandle);
}
$this->curlHandle = null;
}
}
public function serverIsAvailable(): bool
{
// purge expired entries from the cache
static::$cache = array_filter(static::$cache, function ($data) {
return microtime(true) < $data[1];
});
if (! isset(static::$cache[$this->fingerprint])) {
$this->performAvailabilityCheck();
}
return static::$cache[$this->fingerprint][0] ?? true;
}
public function performAvailabilityCheck(): bool
{
try {
$curlHandle = $this->getCurlHandleForUrl('get', '_availability_check');
curl_exec($curlHandle);
$success = curl_errno($curlHandle) === CURLE_HTTP_NOT_FOUND;
// expire the cache entry after 30 sec
$expiresAt = microtime(true) + 30.0;
static::$cache[$this->fingerprint] = [$success, $expiresAt];
} finally {
return $success ?? false;
}
}
public function send(Request $request): void
{
if (! $this->serverIsAvailable()) {
return;
}
$curlHandle = $this->getCurlHandleForUrl('get', '');
$curlError = null;
curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, $request->toJson());
curl_exec($curlHandle);
if (curl_errno($curlHandle)) {
$curlError = curl_error($curlHandle);
}
if ($curlError) {
// do nothing for now
}
}
public function lockExists(string $lockName): bool
{
if (! $this->serverIsAvailable()) {
return false;
}
$queryString = http_build_query([
'hostname' => Hostname::get(),
'project_name' => Ray::$projectName,
]);
$curlHandle = $this->getCurlHandleForUrl('get', "locks/{$lockName}?{$queryString}");
$curlError = null;
try {
$curlResult = curl_exec($curlHandle);
if (curl_errno($curlHandle)) {
$curlError = curl_error($curlHandle);
}
if ($curlError) {
throw new Exception();
}
if (! $curlResult) {
return false;
}
$response = json_decode($curlResult, true);
if ($response['stop_execution'] ?? false) {
throw StopExecutionRequested::make();
}
return $response['active'] ?? false;
} catch (Exception $exception) {
if ($exception instanceof StopExecutionRequested) {
throw $exception;
}
}
return false;
}
protected function getCurlHandleForUrl(string $method, string $url)
{
return $this->getCurlHandle($method, "http://{$this->host}:{$this->portNumber}/{$url}");
}
protected function getCurlHandle(string $method, string $fullUrl)
{
if (! $this->curlHandle) {
$this->curlHandle = curl_init();
}
curl_reset($this->curlHandle);
curl_setopt($this->curlHandle, CURLOPT_URL, $fullUrl);
curl_setopt($this->curlHandle, CURLOPT_HTTPHEADER, array_merge([
'Accept: application/json',
'Content-Type: application/json',
]));
curl_setopt($this->curlHandle, CURLOPT_USERAGENT, 'Ray 1.0');
curl_setopt($this->curlHandle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->curlHandle, CURLOPT_TIMEOUT, 2);
curl_setopt($this->curlHandle, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($this->curlHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($this->curlHandle, CURLOPT_ENCODING, '');
curl_setopt($this->curlHandle, CURLINFO_HEADER_OUT, true);
curl_setopt($this->curlHandle, CURLOPT_FAILONERROR, true);
if ($method === 'post') {
curl_setopt($this->curlHandle, CURLOPT_POST, true);
}
return $this->curlHandle;
}
}
@@ -0,0 +1,37 @@
<?php
namespace Spatie\Ray\Concerns;
/** @mixin \Spatie\Ray\Ray */
trait RayColors
{
public function green(): self
{
return $this->color('green');
}
public function orange(): self
{
return $this->color('orange');
}
public function red(): self
{
return $this->color('red');
}
public function purple(): self
{
return $this->color('purple');
}
public function blue(): self
{
return $this->color('blue');
}
public function gray(): self
{
return $this->color('gray');
}
}
@@ -0,0 +1,37 @@
<?php
namespace Spatie\Ray\Concerns;
/** @mixin \Spatie\Ray\Ray */
trait RayScreenColors
{
public function screenGreen(): self
{
return $this->screenColor('green');
}
public function screenOrange(): self
{
return $this->screenColor('orange');
}
public function screenRed(): self
{
return $this->screenColor('red');
}
public function screenPurple(): self
{
return $this->screenColor('purple');
}
public function screenBlue(): self
{
return $this->screenColor('blue');
}
public function screenGray(): self
{
return $this->screenColor('gray');
}
}
@@ -0,0 +1,17 @@
<?php
namespace Spatie\Ray\Concerns;
/** @mixin \Spatie\Ray\Ray */
trait RaySizes
{
public function small(): self
{
return $this->size('sm');
}
public function large(): self
{
return $this->size('lg');
}
}
@@ -0,0 +1,37 @@
<?php
namespace Spatie\Ray\Concerns;
use Spatie\Backtrace\Frame;
trait RemovesRayFrames
{
protected function removeRayFrames(array $frames): array
{
$frames = array_filter($frames, function (Frame $frame) {
return ! $this->isRayFrame($frame);
});
return array_values($frames);
}
protected function isRayFrame(Frame $frame): bool
{
foreach ($this->rayNamespaces() as $rayNamespace) {
if (substr((string)$frame->class, 0, strlen($rayNamespace)) === $rayNamespace) {
return true;
}
}
return false;
}
protected function rayNamespaces(): array
{
return [
'Spatie\Ray',
'Spatie\LaravelRay',
'Spatie\WordPressRay',
];
}
}
@@ -0,0 +1,13 @@
<?php
namespace Spatie\Ray\Exceptions;
use Exception;
class CouldNotConnectToRay extends Exception
{
public static function make(string $host, int $portNumber): self
{
return new static("Couldn't connect to Ray It doesn't seem to be running at {$host}:{$portNumber}");
}
}
@@ -0,0 +1,13 @@
<?php
namespace Spatie\Ray\Exceptions;
use Exception;
class StopExecutionRequested extends Exception
{
public static function make(): self
{
return new static("This exception is thrown because you requested to stop the execution in Ray.");
}
}
@@ -0,0 +1,113 @@
<?php
namespace Spatie\Ray\Origin;
use Spatie\Backtrace\Backtrace;
use Spatie\Backtrace\Frame;
use Spatie\Ray\Ray;
class DefaultOriginFactory implements OriginFactory
{
public function getOrigin(): Origin
{
$frame = $this->getFrame();
return new Origin(
$frame ? $frame->file : null,
$frame ? $frame->lineNumber : null,
Hostname::get()
);
}
/**
* @return \Spatie\Backtrace\Frame|null
*/
protected function getFrame()
{
$frames = $this->getAllFrames();
$indexOfRay = $this->getIndexOfRayFrame($frames);
return $frames[$indexOfRay] ?? null;
}
protected function getAllFrames(): array
{
$frames = Backtrace::create()->frames();
return array_reverse($frames, true);
}
/**
* @param array $frames
*
* @return int|null
*/
protected function getIndexOfRayFrame(array $frames)
{
if ($this->isUsingGlobalRay($frames)) {
return $this->getIndexOfGlobalRayFrame($frames) + 1;
}
$index = $this->search(function (Frame $frame) {
if ($frame->class === Ray::class) {
return true;
}
if ($this->startsWith($frame->file, dirname(__DIR__))) {
return true;
}
return false;
}, $frames);
return $index + 1;
}
public function startsWith(string $hayStack, string $needle): bool
{
return strpos($hayStack, $needle) === 0;
}
public function isUsingGlobalRay(array $frames)
{
return $this->getIndexOfGlobalRayFrame($frames) !== null;
}
/**
* @param array<int, Frame> $frames
*
* @return int|null
*/
protected function getIndexOfGlobalRayFrame(array $frames)
{
return $this->search(function (Frame $frame) {
if (! $this->startsWith($frame->file, 'phar:')) {
return false;
}
if (strpos($frame->file, 'global-ray/ray-phars') === false) {
return false;
}
return true;
}, $frames);
}
/**
* @param callable $callable
* @param array $items
*
* @return int|null
*/
protected function search(callable $callable, array $items)
{
foreach ($items as $key => $item) {
if ($callable($item, $key)) {
return $key;
}
}
return null;
}
}
@@ -0,0 +1,18 @@
<?php
namespace Spatie\Ray\Origin;
class Hostname
{
protected static $hostname = null;
public static function get(): string
{
return static::$hostname ?? gethostname();
}
public static function set(string $hostname)
{
static::$hostname = $hostname;
}
}
@@ -0,0 +1,43 @@
<?php
namespace Spatie\Ray\Origin;
class Origin
{
/** @var string|null */
public $file;
/** @var string|null */
public $lineNumber;
/** @var string|null */
public $hostname;
/**
* @param string|null $file
* @param int|null $lineNumber
* @param string|null $hostname
*/
public function __construct($file, $lineNumber, $hostname = null)
{
$this->file = $file;
$this->lineNumber = $lineNumber;
$this->hostname = $hostname ?? Hostname::get();
}
public function toArray(): array
{
return [
'file' => $this->file,
'line_number' => $this->lineNumber,
'hostname' => $this->hostname,
];
}
public function fingerPrint(): string
{
return md5(print_r($this->toArray(), true));
}
}
@@ -0,0 +1,8 @@
<?php
namespace Spatie\Ray\Origin;
interface OriginFactory
{
public function getOrigin(): Origin;
}
@@ -0,0 +1,39 @@
<?php
namespace Spatie\Ray\PHPStan;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
class RemainingRayCallRule implements Rule
{
public function getNodeType(): string
{
return Node\Expr\FuncCall::class;
}
public function processNode(Node $node, Scope $scope): array
{
if (! $node instanceof Node\Expr\FuncCall) {
return [];
}
// Backwards compatibility with PHPStan 1.x.
if (! method_exists($node->name, 'getParts')) {
if ($node->name->parts[0] !== 'ray') {
return [];
}
} else {
if ($node->name->getParts()[0] !== 'ray') {
return [];
}
}
return [
RuleErrorBuilder::message('Remaining ray call in application code')
->build(),
];
}
}
@@ -0,0 +1,66 @@
<?php
namespace Spatie\Ray;
use Carbon\CarbonInterface;
use Spatie\Ray\Payloads\BoolPayload;
use Spatie\Ray\Payloads\CarbonPayload;
use Spatie\Ray\Payloads\LogPayload;
use Spatie\Ray\Payloads\NullPayload;
use Spatie\Ray\Payloads\Payload;
class PayloadFactory
{
/** @var array */
protected $values;
/** @var \Closure|null */
protected static $payloadFinder = null;
public static function createForValues(array $arguments): array
{
return (new static($arguments))->getPayloads();
}
public static function registerPayloadFinder(callable $callable)
{
self::$payloadFinder = $callable;
}
public function __construct(array $values)
{
$this->values = $values;
}
public function getPayloads(): array
{
return array_map(function ($value) {
return $this->getPayload($value);
}, $this->values);
}
protected function getPayload($value): Payload
{
if (self::$payloadFinder) {
if ($payload = (static::$payloadFinder)($value)) {
return $payload;
}
}
if (is_bool($value)) {
return new BoolPayload($value);
}
if (is_null($value)) {
return new NullPayload();
}
if ($value instanceof CarbonInterface) {
return new CarbonPayload($value);
}
$primitiveValue = ArgumentConverter::convertToPrimitive($value);
return new LogPayload($primitiveValue, $value);
}
}
@@ -0,0 +1,38 @@
<?php
namespace Spatie\Ray\Payloads;
use Spatie\Ray\ArgumentConverter;
class ApplicationLogPayload extends Payload
{
/** @var string */
protected $value;
/** @var array */
protected $context;
public function __construct(string $value, array $context = [])
{
$this->value = $value;
$this->context = $context;
}
public function getType(): string
{
return 'application_log';
}
public function getContent(): array
{
$content = [
'value' => $this->value,
];
if (count($this->context)) {
$content['context'] = ArgumentConverter::convertToPrimitive($this->context);
}
return $content;
}
}
@@ -0,0 +1,27 @@
<?php
namespace Spatie\Ray\Payloads;
class BoolPayload extends Payload
{
/** @var bool */
protected $bool;
public function __construct(bool $bool)
{
$this->bool = $bool;
}
public function getType(): string
{
return 'custom';
}
public function getContent(): array
{
return [
'content' => $this->bool,
'label' => 'Boolean',
];
}
}
@@ -0,0 +1,42 @@
<?php
namespace Spatie\Ray\Payloads;
use Spatie\Backtrace\Frame;
use Spatie\Ray\Concerns\RemovesRayFrames;
class CallerPayload extends Payload
{
use RemovesRayFrames;
/** @var array */
protected $frames;
public function __construct(array $frames)
{
$this->frames = $this->removeRayFrames($frames);
}
public function getType(): string
{
return 'caller';
}
public function getContent(): array
{
$frames = array_slice($this->frames, 1, 1);
/** @var Frame $frame */
$frame = array_values($frames)[0];
return [
'frame' => [
'file_name' => $this->replaceRemotePathWithLocalPath($frame->file),
'line_number' => $frame->lineNumber,
'class' => $frame->class,
'method' => $frame->method,
'vendor_frame' => ! $frame->applicationFrame,
],
];
}
}
@@ -0,0 +1,35 @@
<?php
namespace Spatie\Ray\Payloads;
use Carbon\CarbonInterface;
class CarbonPayload extends Payload
{
/** @var \Carbon\CarbonInterface|null */
protected $carbon;
/** @var string */
protected $format;
public function __construct(?CarbonInterface $carbon, string $format = 'Y-m-d H:i:s')
{
$this->carbon = $carbon;
$this->format = $format;
}
public function getType(): string
{
return 'carbon';
}
public function getContent(): array
{
return [
'formatted' => $this->carbon ? $this->carbon->format($this->format) : null,
'timestamp' => $this->carbon ? $this->carbon->timestamp : null,
'timezone' => $this->carbon ? $this->carbon->timezone->getName() : null,
];
}
}
@@ -0,0 +1,11 @@
<?php
namespace Spatie\Ray\Payloads;
class ClearAllPayload extends Payload
{
public function getType(): string
{
return 'clear_all';
}
}
@@ -0,0 +1,26 @@
<?php
namespace Spatie\Ray\Payloads;
class ColorPayload extends Payload
{
/** @var mixed */
protected $color;
public function __construct(string $color)
{
$this->color = $color;
}
public function getType(): string
{
return 'color';
}
public function getContent(): array
{
return [
'color' => $this->color,
];
}
}
@@ -0,0 +1,16 @@
<?php
namespace Spatie\Ray\Payloads;
class ConfettiPayload extends Payload
{
public function getType(): string
{
return 'confetti';
}
public function getContent(): array
{
return [];
}
}
@@ -0,0 +1,26 @@
<?php
namespace Spatie\Ray\Payloads;
class CreateLockPayload extends Payload
{
/** @var string */
protected $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function getType(): string
{
return 'create_lock';
}
public function getContent(): array
{
return [
'name' => $this->name,
];
}
}
@@ -0,0 +1,32 @@
<?php
namespace Spatie\Ray\Payloads;
class CustomPayload extends Payload
{
/** @var string */
protected $content;
/** @var string */
protected $label;
public function __construct(string $content, string $label = '')
{
$this->content = $content;
$this->label = $label;
}
public function getType(): string
{
return 'custom';
}
public function getContent(): array
{
return [
'content' => $this->content,
'label' => $this->label,
];
}
}
@@ -0,0 +1,31 @@
<?php
namespace Spatie\Ray\Payloads;
use Spatie\Ray\ArgumentConverter;
class DecodedJsonPayload extends Payload
{
/** @var string */
protected $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function getType(): string
{
return 'custom';
}
public function getContent(): array
{
$decodedJson = json_decode($this->value, true);
return [
'content' => ArgumentConverter::convertToPrimitive($decodedJson),
'label' => '',
];
}
}
@@ -0,0 +1,56 @@
<?php
namespace Spatie\Ray\Payloads;
use Spatie\Backtrace\Backtrace;
use Spatie\Backtrace\Frame;
use Throwable;
class ExceptionPayload extends Payload
{
/** @var \Throwable */
protected $exception;
/** @var array */
protected $meta = [];
public function __construct(Throwable $exception, array $meta = [])
{
$this->exception = $exception;
$this->meta = $meta;
}
public function getType(): string
{
return 'exception';
}
public function getContent(): array
{
Backtrace::createForThrowable($this->exception);
return [
'class' => get_class($this->exception),
'message' => $this->exception->getMessage(),
'frames' => $this->getFrames(),
'meta' => $this->meta,
];
}
protected function getFrames(): array
{
$frames = Backtrace::createForThrowable($this->exception)->frames();
return array_map(function (Frame $frame) {
return [
'file_name' => $this->replaceRemotePathWithLocalPath($frame->file),
'line_number' => $frame->lineNumber,
'class' => $frame->class,
'method' => $frame->method,
'vendor_frame' => ! $frame->applicationFrame,
'snippet' => $frame->getSnippetProperties(12),
];
}, $frames);
}
}
@@ -0,0 +1,38 @@
<?php
namespace Spatie\Ray\Payloads;
class ExpandPayload extends Payload
{
/** @var array */
protected $keys = [];
/** @var int|null */
protected $level = null;
public function __construct(array $values = [])
{
foreach ($values as $value) {
if (is_numeric($value)) {
$this->level = max($this->level, $value);
continue;
}
$this->keys[] = $value;
}
}
public function getType(): string
{
return 'expand';
}
public function getContent(): array
{
return [
'keys' => $this->keys,
'level' => $this->level,
];
}
}
@@ -0,0 +1,44 @@
<?php
namespace Spatie\Ray\Payloads;
class FileContentsPayload extends Payload
{
/** @var string */
protected $file;
public function __construct(string $file)
{
$this->file = $file;
}
public function getType(): string
{
return 'custom';
}
public function getContent(): array
{
if (! file_exists($this->file)) {
return [
'content' => "File not found: '{$this->file}'",
'label' => 'File',
];
}
$contents = file_get_contents($this->file);
return [
'content' => $this->encodeContent($contents),
'label' => basename($this->file),
];
}
protected function encodeContent(string $content): string
{
$result = htmlentities($content, ENT_QUOTES | ENT_SUBSTITUTE);
// using nl2br() causes tests to fail on Windows, so use <br> only
return str_replace(PHP_EOL, '<br />', $result);
}
}
@@ -0,0 +1,11 @@
<?php
namespace Spatie\Ray\Payloads;
class HideAppPayload extends Payload
{
public function getType(): string
{
return 'hide_app';
}
}
@@ -0,0 +1,11 @@
<?php
namespace Spatie\Ray\Payloads;
class HidePayload extends Payload
{
public function getType(): string
{
return 'hide';
}
}
@@ -0,0 +1,27 @@
<?php
namespace Spatie\Ray\Payloads;
class HtmlPayload extends Payload
{
/** @var string */
protected $html;
public function __construct(string $html = '')
{
$this->html = $html;
}
public function getType(): string
{
return 'custom';
}
public function getContent(): array
{
return [
'content' => $this->html,
'label' => 'HTML',
];
}
}
@@ -0,0 +1,54 @@
<?php
namespace Spatie\Ray\Payloads;
class ImagePayload extends Payload
{
/** @var string */
protected $location;
public function __construct(string $location)
{
$this->location = $location;
}
public function getType(): string
{
return 'custom';
}
public function getContent(): array
{
if (file_exists($this->location)) {
$this->location = 'file://' . $this->location;
}
if ($this->hasBase64Data()) {
$this->location = $this->getLocationForBase64Data();
}
$location = str_replace('"', '', $this->location);
return [
'content' => "<img src=\"{$location}\" alt=\"\" />",
'label' => 'Image',
];
}
protected function stripDataPrefix(string $data): string
{
return preg_replace('~^data:image/[a-z]+;base64,~', '', $data);
}
protected function hasBase64Data(): bool
{
$data = $this->stripDataPrefix($this->location);
return base64_encode(base64_decode($data, true)) === $data;
}
protected function getLocationForBase64Data(): string
{
return 'data:image/png;base64,' . $this->stripDataPrefix($this->location);
}
}
@@ -0,0 +1,26 @@
<?php
namespace Spatie\Ray\Payloads;
class JsonStringPayload extends Payload
{
/** @var mixed */
protected $value;
public function __construct($value)
{
$this->value = $value;
}
public function getType(): string
{
return 'json_string';
}
public function getContent(): array
{
return [
'value' => json_encode($this->value),
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace Spatie\Ray\Payloads;
class LabelPayload extends Payload
{
/** @var string */
protected $label;
public function __construct(string $label)
{
$this->label = $label;
}
public function getType(): string
{
return 'label';
}
public function getContent(): array
{
return [
'label' => $this->label,
];
}
}
@@ -0,0 +1,70 @@
<?php
namespace Spatie\Ray\Payloads;
use Exception;
use Spatie\Ray\ArgumentConverter;
use Spatie\Ray\Support\PlainTextDumper;
class LogPayload extends Payload
{
/** @var array */
protected $values;
/** @var array */
protected $meta = [];
public static function createForArguments(array $arguments): Payload
{
$dumpedArguments = array_map(function ($argument) {
return ArgumentConverter::convertToPrimitive($argument);
}, $arguments);
return new static($dumpedArguments);
}
public function __construct($values, $rawValues = [])
{
if (! is_array($values)) {
if (is_int($values) && $values >= 11111111111111111) {
$values = (string) $values;
}
$values = [$values];
}
$this->meta = [
[
'clipboard_data' => substr($this->getClipboardData($rawValues), 0, 20000),
],
];
$this->values = $values;
}
public function getType(): string
{
return 'log';
}
public function getContent(): array
{
return [
'values' => $this->values,
'meta' => $this->meta,
];
}
protected function getClipboardData($value): string
{
if (is_string($value) || is_numeric($value)) {
return (string) $value;
}
try {
return PlainTextDumper::dump($value);
} catch (Exception $ex) {
return '';
}
}
}
@@ -0,0 +1,71 @@
<?php
namespace Spatie\Ray\Payloads;
use Symfony\Component\Stopwatch\StopwatchEvent;
class MeasurePayload extends Payload
{
/** @var string */
protected $name;
/** @var bool */
protected $isNewTimer = false;
/** @var float|int */
protected $totalTime = 0;
/** @var int */
protected $maxMemoryUsageDuringTotalTime = 0;
/** @var float|int */
protected $timeSinceLastCall = 0;
/** @var int */
protected $maxMemoryUsageSinceLastCall = 0;
public function __construct(string $name, StopwatchEvent $stopwatchEvent)
{
$this->name = $name;
$this->totalTime = $stopwatchEvent->getDuration();
$this->maxMemoryUsageDuringTotalTime = $stopwatchEvent->getMemory();
$periods = $stopwatchEvent->getPeriods();
if ($lastPeriod = end($periods)) {
$this->timeSinceLastCall = $lastPeriod->getDuration() ;
$this->maxMemoryUsageSinceLastCall = $lastPeriod->getMemory();
}
}
public function concernsNewTimer(): self
{
$this->isNewTimer = true;
$this->totalTime = 0;
$this->maxMemoryUsageDuringTotalTime = 0;
$this->timeSinceLastCall = 0;
$this->maxMemoryUsageSinceLastCall = 0;
return $this;
}
public function getType(): string
{
return 'measure';
}
public function getContent(): array
{
return [
'name' => $this->name,
'is_new_timer' => $this->isNewTimer,
'total_time' => $this->totalTime,
'max_memory_usage_during_total_time' => $this->maxMemoryUsageDuringTotalTime,
'time_since_last_call' => $this->timeSinceLastCall,
'max_memory_usage_since_last_call' => $this->maxMemoryUsageSinceLastCall,
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace Spatie\Ray\Payloads;
class NewScreenPayload extends Payload
{
/** @var mixed */
protected $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function getType(): string
{
return 'new_screen';
}
public function getContent(): array
{
return [
'name' => $this->name,
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace Spatie\Ray\Payloads;
class NotifyPayload extends Payload
{
/** @var string */
protected $text;
public function __construct(string $text)
{
$this->text = $text;
}
public function getType(): string
{
return 'notify';
}
public function getContent(): array
{
return [
'value' => $this->text,
];
}
}
@@ -0,0 +1,22 @@
<?php
namespace Spatie\Ray\Payloads;
class NullPayload extends Payload
{
/** @var bool */
protected $bool;
public function getType(): string
{
return 'custom';
}
public function getContent(): array
{
return [
'content' => null,
'label' => 'Null',
];
}
}
@@ -0,0 +1,62 @@
<?php
namespace Spatie\Ray\Payloads;
use Spatie\Ray\Origin\DefaultOriginFactory;
use Spatie\Ray\Origin\Origin;
abstract class Payload
{
/** @var string */
public static $originFactoryClass = DefaultOriginFactory::class;
abstract public function getType(): string;
/** @var string|null */
public $remotePath = null;
/** @var string|null */
public $localPath = null;
public function replaceRemotePathWithLocalPath(string $filePath): string
{
if (is_null($this->remotePath) || is_null($this->localPath)) {
return $filePath;
}
$pattern = '~^' . preg_quote($this->remotePath, '~') . '~';
return preg_replace($pattern, $this->localPath, $filePath);
}
public function getContent(): array
{
return [];
}
public function toArray(): array
{
return [
'type' => $this->getType(),
'content' => $this->getContent(),
'origin' => $this->getOrigin()->toArray(),
];
}
public function toJson(): string
{
return json_encode($this->toArray());
}
protected function getOrigin(): Origin
{
/** @var \Spatie\Ray\Origin\OriginFactory $originFactory */
$originFactory = new static::$originFactoryClass();
$origin = $originFactory->getOrigin();
$origin->file = $this->replaceRemotePathWithLocalPath($origin->file);
return $origin;
}
}
@@ -0,0 +1,45 @@
<?php
namespace Spatie\Ray\Payloads;
class PhpInfoPayload extends Payload
{
/** @var array */
protected $properties = [];
public function __construct(string ...$properties)
{
$this->properties = $properties;
}
public function getType(): string
{
return 'table';
}
public function getContent(): array
{
$values = array_flip($this->properties);
foreach ($values as $property => $value) {
$values[$property] = ini_get($property);
}
if (empty($values)) {
$values = [
'PHP version' => phpversion(),
'Memory limit' => ini_get('memory_limit'),
'Max file upload size' => ini_get('max_file_uploads'),
'Max post size' => ini_get('post_max_size'),
'PHP ini file' => php_ini_loaded_file(),
"PHP scanned ini file" => php_ini_scanned_files(),
'Extensions' => implode(', ', get_loaded_extensions()),
];
}
return [
'values' => $values,
'label' => 'PHPInfo',
];
}
}
@@ -0,0 +1,11 @@
<?php
namespace Spatie\Ray\Payloads;
class RemovePayload extends Payload
{
public function getType(): string
{
return 'remove';
}
}
@@ -0,0 +1,26 @@
<?php
namespace Spatie\Ray\Payloads;
class ScreenColorPayload extends Payload
{
/** @var string */
protected $color;
public function __construct(string $color)
{
$this->color = $color;
}
public function getType(): string
{
return 'screen_color';
}
public function getContent(): array
{
return [
'color' => $this->color,
];
}
}
@@ -0,0 +1,11 @@
<?php
namespace Spatie\Ray\Payloads;
class SeparatorPayload extends Payload
{
public function getType(): string
{
return 'separator';
}
}
@@ -0,0 +1,11 @@
<?php
namespace Spatie\Ray\Payloads;
class ShowAppPayload extends Payload
{
public function getType(): string
{
return 'show_app';
}
}
@@ -0,0 +1,26 @@
<?php
namespace Spatie\Ray\Payloads;
class SizePayload extends Payload
{
/** @var mixed */
protected $size;
public function __construct(string $size)
{
$this->size = $size;
}
public function getType(): string
{
return 'size';
}
public function getContent(): array
{
return [
'size' => $this->size,
];
}
}
@@ -0,0 +1,38 @@
<?php
namespace Spatie\Ray\Payloads;
use Spatie\Ray\ArgumentConverter;
class TablePayload extends Payload
{
/** @var array */
protected $values;
/** @var string */
protected $label;
public function __construct(array $values, string $label = 'Table')
{
$this->values = $values;
$this->label = $label;
}
public function getType(): string
{
return 'table';
}
public function getContent(): array
{
$values = array_map(function ($value) {
return ArgumentConverter::convertToPrimitive($value);
}, $this->values);
return [
'values' => $values,
'label' => $this->label,
];
}
}
@@ -0,0 +1,34 @@
<?php
namespace Spatie\Ray\Payloads;
class TextPayload extends Payload
{
/** @var string */
protected $text;
public function __construct(string $text = '')
{
$this->text = $text;
}
public function getType(): string
{
return 'custom';
}
public function getContent(): array
{
return [
'content' => $this->formatContent(),
'label' => 'Text',
];
}
protected function formatContent(): string
{
$result = htmlspecialchars($this->text, ENT_QUOTES | ENT_HTML5);
return str_replace([' ', PHP_EOL], ['&nbsp;', '<br>'], $result);
}
}
@@ -0,0 +1,63 @@
<?php
namespace Spatie\Ray\Payloads;
use Spatie\Backtrace\Frame;
use Spatie\Ray\Concerns\RemovesRayFrames;
class TracePayload extends Payload
{
use RemovesRayFrames;
/** @var array */
protected $frames;
/** @var int|null */
protected $startFromIndex = null;
/** @var int|null */
protected $limit = null;
public function __construct(array $frames)
{
$this->frames = $this->removeRayFrames($frames);
}
public function startFromIndex(int $index): self
{
$this->startFromIndex = $index;
return $this;
}
public function limit(int $limit): self
{
$this->limit = $limit;
return $this;
}
public function getType(): string
{
return 'trace';
}
public function getContent(): array
{
$frames = array_map(function (Frame $frame) {
return [
'file_name' => $this->replaceRemotePathWithLocalPath($frame->file),
'line_number' => $frame->lineNumber,
'class' => $frame->class,
'method' => $frame->method,
'vendor_frame' => ! $frame->applicationFrame,
];
}, $this->frames);
if (! is_null($this->limit)) {
$frames = array_slice($frames, $this->startFromIndex ?? 0, $this->limit);
}
return compact('frames');
}
}
@@ -0,0 +1,62 @@
<?php
namespace Spatie\Ray\Payloads;
class XmlPayload extends Payload
{
/** @var string */
protected $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function getType(): string
{
return 'custom';
}
public function getContent(): array
{
$content = $this->formatXmlForDisplay($this->value);
return [
'content' => $content,
'label' => 'XML',
];
}
protected function formatXmlForDisplay(string $xml): string
{
$content = $this->formatAndIndentXml($xml);
return $this->encodeXml(trim($content));
}
protected function encodeXml(string $xml): string
{
$result = htmlentities($xml);
return str_replace([PHP_EOL, "\n", ' '], ['<br>', '<br>', '&nbsp;'], $result);
}
protected function formatAndIndentXml(string $xml): string
{
if (! class_exists(\DOMDocument::class)) {
return $xml;
}
$dom = new \DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->strictErrorChecking = false;
$dom->formatOutput = true;
if (! $dom->loadXML($xml, LIBXML_NOERROR | LIBXML_NOWARNING)) {
return $xml;
}
return $dom->saveXML();
}
}
@@ -0,0 +1,874 @@
<?php
namespace Spatie\Ray;
use Carbon\CarbonInterface;
use Closure;
use Composer\InstalledVersions;
use Exception;
use Illuminate\Support\Str;
use Ramsey\Uuid\Uuid;
use Spatie\Backtrace\Backtrace;
use Spatie\LaravelRay\Ray as LaravelRay;
use Spatie\Macroable\Macroable;
use Spatie\Ray\Concerns\RayColors;
use Spatie\Ray\Concerns\RayScreenColors;
use Spatie\Ray\Concerns\RaySizes;
use Spatie\Ray\Origin\DefaultOriginFactory;
use Spatie\Ray\Payloads\CallerPayload;
use Spatie\Ray\Payloads\CarbonPayload;
use Spatie\Ray\Payloads\ClearAllPayload;
use Spatie\Ray\Payloads\ColorPayload;
use Spatie\Ray\Payloads\ConfettiPayload;
use Spatie\Ray\Payloads\CreateLockPayload;
use Spatie\Ray\Payloads\CustomPayload;
use Spatie\Ray\Payloads\DecodedJsonPayload;
use Spatie\Ray\Payloads\ExceptionPayload;
use Spatie\Ray\Payloads\ExpandPayload;
use Spatie\Ray\Payloads\FileContentsPayload;
use Spatie\Ray\Payloads\HideAppPayload;
use Spatie\Ray\Payloads\HidePayload;
use Spatie\Ray\Payloads\HtmlPayload;
use Spatie\Ray\Payloads\ImagePayload;
use Spatie\Ray\Payloads\JsonStringPayload;
use Spatie\Ray\Payloads\LabelPayload;
use Spatie\Ray\Payloads\LogPayload;
use Spatie\Ray\Payloads\MeasurePayload;
use Spatie\Ray\Payloads\NewScreenPayload;
use Spatie\Ray\Payloads\NotifyPayload;
use Spatie\Ray\Payloads\PhpInfoPayload;
use Spatie\Ray\Payloads\RemovePayload;
use Spatie\Ray\Payloads\ScreenColorPayload;
use Spatie\Ray\Payloads\SeparatorPayload;
use Spatie\Ray\Payloads\ShowAppPayload;
use Spatie\Ray\Payloads\SizePayload;
use Spatie\Ray\Payloads\TablePayload;
use Spatie\Ray\Payloads\TextPayload;
use Spatie\Ray\Payloads\TracePayload;
use Spatie\Ray\Payloads\XmlPayload;
use Spatie\Ray\Settings\Settings;
use Spatie\Ray\Settings\SettingsFactory;
use Spatie\Ray\Support\Counters;
use Spatie\Ray\Support\ExceptionHandler;
use Spatie\Ray\Support\IgnoredValue;
use Spatie\Ray\Support\Invador;
use Spatie\Ray\Support\Limiters;
use Spatie\Ray\Support\RateLimiter;
use Symfony\Component\Stopwatch\Stopwatch;
use Throwable;
use TypeError;
class Ray
{
use RayColors;
use RayScreenColors;
use RaySizes;
use Macroable;
/** @var \Spatie\Ray\Settings\Settings */
public $settings;
/** @var \Spatie\Ray\Client */
protected static $client;
/** @var \Spatie\Ray\Support\Counters */
public static $counters;
/** @var \Spatie\Ray\Support\Limiters */
public static $limiters;
/** @var string */
public static $fakeUuid;
/** @var \Spatie\Ray\Origin\Origin|null */
public $limitOrigin = null;
/** @var string */
public $uuid = '';
/** @var bool */
public $canSendPayload = true;
/** @var array|\Exception[] */
public static $caughtExceptions = [];
/** @var \Symfony\Component\Stopwatch\Stopwatch[] */
public static $stopWatches = [];
/** @var bool|null */
public static $enabled = null;
/** @var RateLimiter */
public static $rateLimiter;
/** @var string */
public static $projectName = '';
/** @var Closure|null */
public static $beforeSendRequest = null;
public static function create(?Client $client = null, ?string $uuid = null): self
{
$settings = SettingsFactory::createFromConfigFile();
return new static($settings, $client, $uuid);
}
public function __construct(Settings $settings, ?Client $client = null, ?string $uuid = null)
{
$this->settings = $settings;
self::$client = $client ?? self::$client ?? new Client($settings->port, $settings->host);
self::$counters = self::$counters ?? new Counters();
self::$limiters = self::$limiters ?? new Limiters();
$this->uuid = $uuid ?? static::$fakeUuid ?? Uuid::uuid4()->toString();
static::$enabled = static::$enabled ?? $this->settings->enable ?? true;
static::$rateLimiter = static::$rateLimiter ?? RateLimiter::disabled();
}
/**
* @param string $projectName
*
* @return $this
*/
public function project($projectName): self
{
static::$projectName = $projectName;
return $this;
}
public function enable(): self
{
static::$enabled = true;
return $this;
}
public function disable(): self
{
static::$enabled = false;
return $this;
}
public function enabled(): bool
{
return static::$enabled || static::$enabled === null;
}
public function disabled(): bool
{
return static::$enabled === false;
}
public static function useClient(Client $client): void
{
self::$client = $client;
}
public function newScreen(string $name = ''): self
{
$name = $this->sanitizeNewScreenName($name);
$payload = new NewScreenPayload($name);
return $this->sendRequest($payload);
}
protected function sanitizeNewScreenName(string $name): string
{
if (strpos($name, '__pest_evaluable_') === 0) {
$name = substr($name, 17);
$name = str_replace('_', ' ', $name);
}
return $name;
}
public function clearAll(): self
{
$payload = new ClearAllPayload();
return $this->sendRequest($payload);
}
public function clearScreen(): self
{
return $this->newScreen();
}
public function color(string $color): self
{
$payload = new ColorPayload($color);
return $this->sendRequest($payload);
}
public function screenColor(string $color): self
{
$payload = new ScreenColorPayload($color);
return $this->sendRequest($payload);
}
public function label(string $label): self
{
$payload = new LabelPayload($label);
return $this->sendRequest($payload);
}
public function size(string $size): self
{
$payload = new SizePayload($size);
return $this->sendRequest($payload);
}
public function remove(): self
{
$payload = new RemovePayload();
return $this->sendRequest($payload);
}
public function hide(): self
{
$payload = new HidePayload();
return $this->sendRequest($payload);
}
/**
* @param string|callable $stopwatchName
*
* @return $this
*/
public function measure($stopwatchName = 'default'): self
{
if ($stopwatchName instanceof Closure) {
return $this->measureClosure($stopwatchName);
}
if (! isset(static::$stopWatches[$stopwatchName])) {
$stopwatch = new Stopwatch(true);
static::$stopWatches[$stopwatchName] = $stopwatch;
$event = $stopwatch->start($stopwatchName);
$payload = new MeasurePayload($stopwatchName, $event);
$payload->concernsNewTimer();
return $this->sendRequest($payload);
}
$stopwatch = static::$stopWatches[$stopwatchName];
$event = $stopwatch->lap($stopwatchName);
$payload = new MeasurePayload($stopwatchName, $event);
return $this->sendRequest($payload);
}
public function trace(?Closure $startingFromFrame = null, ?int $offset = null, ?int $limit = null): self
{
$backtrace = Backtrace::create();
if (class_exists(LaravelRay::class) && function_exists('base_path')) {
$backtrace->applicationPath(base_path());
}
if ($startingFromFrame) {
$backtrace->startingFromFrame($startingFromFrame);
}
if ($offset) {
$backtrace->offset($offset);
}
if ($limit) {
$backtrace->limit($limit);
}
$payload = new TracePayload($backtrace->frames());
return $this->sendRequest($payload);
}
public function backtrace(?Closure $startingFromFrame = null, ?int $offset = null, ?int $limit = null): self
{
return $this->trace($startingFromFrame, $offset, $limit);
}
public function caller(): self
{
$backtrace = Backtrace::create();
$payload = (new CallerPayload($backtrace->frames()));
return $this->sendRequest($payload);
}
protected function measureClosure(Closure $closure): self
{
$stopwatch = new Stopwatch(true);
$stopwatch->start('closure');
$closure();
$event = $stopwatch->stop('closure');
$payload = new MeasurePayload('closure', $event);
return $this->sendRequest($payload);
}
public function expand(...$levelOrKey): self
{
if (empty($levelOrKey)) {
$levelOrKey = [1];
}
$payload = new ExpandPayload($levelOrKey);
return $this->sendRequest($payload);
}
public function expandAll(): self
{
return $this->expand(999);
}
public function stopTime(string $stopwatchName = ''): self
{
if ($stopwatchName === '') {
static::$stopWatches = [];
return $this;
}
if (isset(static::$stopWatches[$stopwatchName])) {
unset(static::$stopWatches[$stopwatchName]);
return $this;
}
return $this;
}
public function notify(string $text): self
{
$payload = new NotifyPayload($text);
return $this->sendRequest($payload);
}
/**
* Sends the provided value(s) encoded as a JSON string using json_encode().
*/
public function toJson(...$values): self
{
$payloads = array_map(function ($value) {
return new JsonStringPayload($value);
}, $values);
return $this->sendRequest($payloads);
}
/**
* Sends the provided JSON string(s) decoded using json_decode().
*/
public function json(string ...$jsons): self
{
$payloads = array_map(function ($json) {
return new DecodedJsonPayload($json);
}, $jsons);
return $this->sendRequest($payloads);
}
public function file(string $filename): self
{
$payload = new FileContentsPayload($filename);
return $this->sendRequest($payload);
}
public function image(string $location): self
{
$payload = new ImagePayload($location);
return $this->sendRequest($payload);
}
public function die($status = ''): void
{
die($status);
}
public function className(object $object): self
{
return $this->send(get_class($object));
}
public function phpinfo(string ...$properties): self
{
$payload = new PhpInfoPayload(...$properties);
return $this->sendRequest($payload);
}
public function if($boolOrCallable, ?callable $callback = null): self
{
if (is_callable($boolOrCallable)) {
$boolOrCallable = (bool)$boolOrCallable();
}
if ($boolOrCallable && $callback !== null) {
$callback($this);
}
if ($callback === null) {
$this->canSendPayload = $boolOrCallable;
}
return $this;
}
/**
* @deprecated Use `if` instead of this method
*/
public function showWhen($boolOrCallable): self
{
if (is_callable($boolOrCallable)) {
$boolOrCallable = (bool)$boolOrCallable();
}
if (! $boolOrCallable) {
$this->remove();
}
return $this;
}
/**
* @deprecated Use `if` instead of this method
*/
public function showIf($boolOrCallable): self
{
return $this->showWhen($boolOrCallable);
}
/**
* @deprecated Use `if` instead of this method
*/
public function removeWhen($boolOrCallable): self
{
if (is_callable($boolOrCallable)) {
$boolOrCallable = (bool)$boolOrCallable();
}
if ($boolOrCallable) {
$this->remove();
}
return $this;
}
/**
* @deprecated Use `if` instead of this method
*/
public function removeIf($boolOrCallable): self
{
return $this->removeWhen($boolOrCallable);
}
public function carbon(?CarbonInterface $carbon): self
{
$payload = new CarbonPayload($carbon);
$this->sendRequest($payload);
return $this;
}
public function ban(): self
{
return $this->send('🕶');
}
public function charles(): self
{
return $this->send('🎶 🎹 🎷 🕺');
}
public function table(array $values, $label = 'Table'): self
{
$payload = new TablePayload($values, $label);
return $this->sendRequest($payload);
}
public function count(?string $name = null): self
{
$fingerPrint = (new DefaultOriginFactory())->getOrigin()->fingerPrint();
[$ray, $times] = self::$counters->increment($name ?? $fingerPrint);
$message = "Called ";
if ($name) {
$message .= "`{$name}` ";
}
$message .= "{$times} ";
$message .= $times === 1
? 'time'
: 'times';
$message .= '.';
$ray->sendCustom($message, 'Count');
return $ray;
}
public function clearCounters(): self
{
self::$counters->clear();
return $this;
}
public function counterValue(string $name): int
{
return self::$counters->get($name);
}
public function pause(): self
{
$lockName = md5(time());
$payload = new CreateLockPayload($lockName);
$this->sendRequest($payload);
do {
sleep(1);
} while (self::$client->lockExists($lockName));
return $this;
}
public function separator(): self
{
$payload = new SeparatorPayload();
return $this->sendRequest($payload);
}
public function url(string $url, string $label = ''): self
{
if (! Str::startsWith($url, 'http')) {
$url = "https://{$url}";
}
if (empty($label)) {
$label = $url;
}
$link = "<a href='{$url}'>{$label}</a>";
return $this->html($link);
}
public function link(string $url, string $label = '')
{
return $this->url($url, $label);
}
public function html(string $html = ''): self
{
$payload = new HtmlPayload($html);
return $this->sendRequest($payload);
}
public function confetti(): self
{
return $this->sendRequest(new ConfettiPayload());
}
public function exception(Throwable $exception, array $meta = [])
{
$payload = new ExceptionPayload($exception, $meta);
$this->sendRequest($payload);
$this->red();
return $this;
}
public function xml(string $xml): self
{
$payload = new XmlPayload($xml);
return $this->sendRequest($payload);
}
public function text(string $text): self
{
$payload = new TextPayload($text);
return $this->sendRequest($payload);
}
public function raw(...$arguments): self
{
if (! count($arguments)) {
return $this;
}
$payloads = array_map(function ($argument) {
return LogPayload::createForArguments([$argument]);
}, $arguments);
return $this->sendRequest($payloads);
}
public function limit(int $count): self
{
$this->limitOrigin = (new DefaultOriginFactory())->getOrigin();
self::$limiters->initialize($this->limitOrigin, $count);
return $this;
}
public function once(...$arguments): self
{
$this->limitOrigin = (new DefaultOriginFactory())->getOrigin();
self::$limiters->initialize($this->limitOrigin, 1);
if (! empty($arguments)) {
return $this->send(...$arguments);
}
return $this;
}
/**
* @param callable|string|null $callback
* @return \Spatie\Ray\Ray
*/
public function catch($callback = null): self
{
$result = (new ExceptionHandler())->catch($this, $callback);
if ($result instanceof Ray) {
return $result;
}
return $this;
}
public function throwExceptions(): self
{
while (! empty(self::$caughtExceptions)) {
throw array_shift(self::$caughtExceptions);
}
return $this;
}
public function invade($object): Invador
{
return new Invador($object, $this);
}
public function send(...$arguments): self
{
if (! count($arguments)) {
return $this;
}
if ($this->settings->always_send_raw_values) {
return $this->raw(...$arguments);
}
$arguments = array_map(function ($argument) {
if (is_string($argument)) {
return $argument;
}
if (! $argument instanceof Closure) {
return $argument;
}
try {
$result = $argument($this);
// use a specific class we can filter out instead of null so that null
// payloads can still be sent.
return $result instanceof Ray ? IgnoredValue::make() : $result;
} catch (Exception $exception) {
self::$caughtExceptions[] = $exception;
return IgnoredValue::make();
} catch (TypeError $error) {
return $argument;
}
}, $arguments);
$arguments = array_filter($arguments, function ($argument) {
return ! $argument instanceof IgnoredValue;
});
if (empty($arguments)) {
return $this;
}
$payloads = PayloadFactory::createForValues($arguments);
return $this->sendRequest($payloads);
}
public function pass($argument)
{
$this->send($argument);
return $argument;
}
public function showApp(): self
{
$payload = new ShowAppPayload();
return $this->sendRequest($payload);
}
public function hideApp(): self
{
$payload = new HideAppPayload();
return $this->sendRequest($payload);
}
public function sendCustom(string $content, string $label = ''): self
{
$customPayload = new CustomPayload($content, $label);
return $this->sendRequest($customPayload);
}
/**
* @param \Spatie\Ray\Payloads\Payload|\Spatie\Ray\Payloads\Payload[] $payloads
* @param array $meta
*
* @return $this
* @throws \Exception
*/
public function sendRequest($payloads, array $meta = []): self
{
if (! $this->enabled()) {
return $this;
}
if (empty($payloads)) {
return $this;
}
if (! $this->canSendPayload) {
return $this;
}
if (! empty($this->limitOrigin)) {
if (! self::$limiters->canSendPayload($this->limitOrigin)) {
return $this;
}
self::$limiters->increment($this->limitOrigin);
}
if (! is_array($payloads)) {
$payloads = [$payloads];
}
try {
if (class_exists(InstalledVersions::class)) {
$meta['ray_package_version'] = InstalledVersions::getVersion('spatie/ray');
}
} catch (Exception $e) {
// In WordPress this entire package will be rewritten
}
if (self::rateLimiter()->isMaxReached() ||
self::rateLimiter()->isMaxPerSecondReached()) {
$this->notifyWhenRateLimitReached();
return $this;
}
$allMeta = array_merge([
'php_version' => phpversion(),
'php_version_id' => PHP_VERSION_ID,
'project_name' => static::$projectName,
], $meta);
if ($closure = static::$beforeSendRequest) {
$closure($payloads, $allMeta);
}
foreach ($payloads as $payload) {
$payload->remotePath = $this->settings->remote_path;
$payload->localPath = $this->settings->local_path;
}
$request = new Request($this->uuid, $payloads, $allMeta);
self::$client->send($request);
self::rateLimiter()->hit();
return $this;
}
public static function makePathOsSafe(string $path): string
{
return str_replace('/', DIRECTORY_SEPARATOR, $path);
}
public static function rateLimiter(): RateLimiter
{
return self::$rateLimiter;
}
protected function notifyWhenRateLimitReached(): void
{
if (self::rateLimiter()->isNotified()) {
return;
}
$customPayload = new CustomPayload('Rate limit has been reached...', 'Rate limit');
$request = new Request($this->uuid, [$customPayload], []);
self::$client->send($request);
self::rateLimiter()->notify();
}
public static function beforeSendRequest(?Closure $closure = null): void
{
static::$beforeSendRequest = $closure;
}
}
@@ -0,0 +1,63 @@
<?php
namespace Spatie\Ray\Rector;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Expression;
use PhpParser\NodeTraverser;
use Rector\Contract\Rector\RectorInterface;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
class RemoveRayCallRector extends AbstractRector implements RectorInterface
{
public function getRuleDefinition(): \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
{
return new RuleDefinition('Remove Ray calls', [new ConfiguredCodeSample(<<<'CODE_SAMPLE'
$x = 'something';
ray($x);
ray()->label('debug');
ray($data)->red()->small();
CODE_SAMPLE
, <<<'CODE_SAMPLE'
$x = 'something';
CODE_SAMPLE
, ['ray'])]);
}
public function getNodeTypes(): array
{
return [Expression::class];
}
public function refactor(Node $node): ?int
{
$expr = $node->expr;
if (! $expr instanceof FuncCall && ! $expr instanceof MethodCall) {
return null;
}
if ($this->isRayCall($expr)) {
return NodeTraverser::REMOVE_NODE;
}
return null;
}
private function isRayCall(Node $node): bool
{
if ($node instanceof FuncCall && $this->isName($node->name, 'ray')) {
return true;
}
if ($node instanceof MethodCall) {
return $this->isRayCall($node->var);
}
return false;
}
}
@@ -0,0 +1,44 @@
<?php
namespace Spatie\Ray;
use Spatie\Ray\Payloads\Payload;
class Request
{
/** @var string */
protected $uuid;
/** @var array */
protected $payloads;
/** @var array */
protected $meta;
public function __construct(string $uuid, array $payloads, array $meta = [])
{
$this->uuid = $uuid;
$this->payloads = $payloads;
$this->meta = $meta;
}
public function toArray(): array
{
$payloads = array_map(function (Payload $payload) {
return $payload->toArray();
}, $this->payloads);
return [
'uuid' => $this->uuid,
'payloads' => $payloads,
'meta' => $this->meta,
];
}
public function toJson(): string
{
return json_encode($this->toArray());
}
}
@@ -0,0 +1,68 @@
<?php
namespace Spatie\Ray\Settings;
class Settings
{
/** @var array */
protected $settings = [];
/** @var bool */
protected $loadedUsingSettingsFile = false;
/** @var array */
protected $defaultSettings = [
'enable' => true,
'host' => 'localhost',
'port' => 23517,
'remote_path' => null,
'local_path' => null,
'always_send_raw_values' => false,
];
public function __construct(array $settings)
{
$this->settings = array_merge($this->defaultSettings, $settings);
}
public function markAsLoadedUsingSettingsFile()
{
$this->loadedUsingSettingsFile = true;
return $this;
}
public function setDefaultSettings(array $defaults)
{
foreach ($defaults as $name => $value) {
if ($this->wasLoadedUsingConfigFile($name)) {
$this->settings[$name] = $value;
}
}
return $this;
}
protected function wasLoadedUsingConfigFile($name)
{
if (! array_key_exists($name, $this->settings)) {
return true;
}
if (! $this->loadedUsingSettingsFile) {
return true;
}
return false;
}
public function __set(string $name, $value)
{
$this->settings[$name] = $value;
}
public function __get(string $name)
{
return $this->settings[$name] ?? null;
}
}
@@ -0,0 +1,80 @@
<?php
namespace Spatie\Ray\Settings;
class SettingsFactory
{
public static $cache = [];
public static function createFromArray(array $settings = []): Settings
{
return new Settings($settings);
}
public static function createFromConfigFile(?string $configDirectory = null): Settings
{
$settingValues = (new static())->getSettingsFromConfigFile($configDirectory);
$settings = static::createFromArray($settingValues);
if (count($settingValues)) {
$settings->markAsLoadedUsingSettingsFile();
}
return $settings;
}
public function getSettingsFromConfigFile(?string $configDirectory = null): array
{
$configFilePath = $this->searchConfigFiles($configDirectory);
if (! file_exists($configFilePath)) {
return [];
}
$options = include $configFilePath;
return $options ?? [];
}
protected function searchConfigFiles(?string $configDirectory = null): string
{
$configDirectory ??= '';
if (! isset(self::$cache[$configDirectory])) {
self::$cache[$configDirectory] = $this->searchConfigFilesOnDisk($configDirectory);
}
return self::$cache[$configDirectory];
}
protected function searchConfigFilesOnDisk(?string $configDirectory = null): string
{
$configNames = [
'ray.php',
];
$configDirectory = $configDirectory ?: getcwd();
while (@is_dir($configDirectory)) {
foreach ($configNames as $configName) {
$configFullPath = $configDirectory.DIRECTORY_SEPARATOR.$configName;
if (file_exists($configFullPath)) {
return $configFullPath;
}
}
$parentDirectory = dirname($configDirectory);
// We do a direct comparison here since there's a difference between
// the root directories on windows / *nix systems which does not
// let us compare it against the DIRECTORY_SEPARATOR directly
if ($parentDirectory === $configDirectory) {
return '';
}
$configDirectory = $parentDirectory;
}
return '';
}
}
@@ -0,0 +1,61 @@
<?php
namespace Spatie\Ray\Support;
class CacheStore
{
/** @var array */
protected $store = [];
/** @var Clock */
protected $clock;
public function __construct(Clock $clock)
{
$this->clock = $clock;
}
public function hit(): self
{
$this->store[] = $this->clock->now();
return $this;
}
public function clear(): self
{
$this->store = [];
return $this;
}
public function count(): int
{
return count($this->store);
}
public function countLastSecond(): int
{
$amount = 0;
$lastSecond = $this->clock->now()->modify('-1 second');
foreach ($this->store as $key => $item) {
if ($this->isBetween(
$item->getTimestamp(),
$lastSecond->getTimestamp(),
$this->clock->now()->getTimestamp()
)
) {
$amount++;
}
}
return $amount;
}
protected function isBetween($toCheck, $start, $end): bool
{
return $toCheck >= $start && $toCheck <= $end;
}
}
@@ -0,0 +1,10 @@
<?php
namespace Spatie\Ray\Support;
use DateTimeImmutable;
interface Clock
{
public function now(): DateTimeImmutable;
}
@@ -0,0 +1,45 @@
<?php
namespace Spatie\Ray\Support;
use Spatie\Ray\Ray;
class Counters
{
/** @var array */
protected $counters = [];
public function increment(string $name): array
{
if (! isset($this->counters[$name])) {
$this->counters[$name] = [ray(), 0];
}
[$ray, $times] = $this->counters[$name];
$newTimes = $times + 1;
$this->counters[$name] = [$ray, $newTimes];
return [$ray, $newTimes];
}
public function get(string $name): int
{
if (! isset($this->counters[$name])) {
return 0;
}
return $this->counters[$name][1];
}
public function clear(): void
{
$this->counters = [];
}
public function setRay(string $name, Ray $ray): void
{
$this->counters[$name][0] = $ray;
}
}
@@ -0,0 +1,148 @@
<?php
namespace Spatie\Ray\Support;
use Exception;
use ReflectionFunction;
use Spatie\Ray\Ray;
class ExceptionHandler
{
public function catch(Ray $ray, $callback): Ray
{
$this->executeExceptionHandlerCallback($ray, $callback);
if (! empty(Ray::$caughtExceptions)) {
throw array_shift(Ray::$caughtExceptions);
}
return $ray;
}
protected function executeCallableExceptionHandler(Ray $ray, $callback, $rethrow = true): Ray
{
$paramType = $this->getParamType(new ReflectionFunction($callback));
$expectedClasses = $this->getExpectedClasses($paramType);
if (count($expectedClasses)) {
$isExpected = false;
foreach ($expectedClasses as $class) {
$isExpected = $this->isExpectedExceptionClass($class);
if ($isExpected) {
break;
}
}
if (! $isExpected && ! $rethrow) {
return $ray;
}
if (! $isExpected && $rethrow) {
throw array_shift(Ray::$caughtExceptions);
}
}
$exception = array_shift(Ray::$caughtExceptions);
$callbackResult = $callback($exception, $ray);
return $callbackResult instanceof Ray ? $callbackResult : $ray;
}
protected function isExpectedExceptionClass($expectedClass): bool
{
foreach (Ray::$caughtExceptions as $caughtException) {
if (is_a($caughtException, $expectedClass, true)) {
return true;
}
}
return false;
}
protected function sendExceptionPayload(Ray $ray): Ray
{
$exception = array_shift(Ray::$caughtExceptions);
return $ray->exception($exception);
}
protected function executeExceptionHandlerCallback(Ray $ray, $callback, $rethrow = true): Ray
{
if (empty(Ray::$caughtExceptions)) {
return $ray;
}
if (is_callable($callback)) {
return $this->executeCallableExceptionHandler($ray, $callback, $rethrow);
}
// support arrays of both class names and callables
if (is_array($callback)) {
return $this->executeArrayOfExceptionHandlers($ray, $callback) ?? $ray;
}
return $this->sendCallbackExceptionPayload($ray, $callback);
;
}
protected function executeArrayOfExceptionHandlers(Ray $ray, array $callbacks): ?Ray
{
foreach ($callbacks as $item) {
$result = $this->executeExceptionHandlerCallback($ray, $item, false);
// the array item handled the exception
if (empty(Ray::$caughtExceptions)) {
return $result instanceof Ray ? $result : $ray;
}
}
return $ray;
}
protected function sendCallbackExceptionPayload(Ray $ray, $callback): Ray
{
if (! $callback) {
return $this->sendExceptionPayload($ray);
}
// handle class names
foreach (Ray::$caughtExceptions as $caughtException) {
if (is_string($callback) && is_a($caughtException, $callback, true)) {
return $this->sendExceptionPayload($ray);
}
}
return $ray;
}
protected function getExpectedClasses($paramType): array
{
if (! $paramType) {
return [Exception::class];
}
$result = is_a($paramType, '\\ReflectionUnionType') ? $paramType->getTypes() : [$paramType->getName()];
return array_map(function ($type) {
if (is_string($type)) {
return $type;
}
return method_exists($type, 'getName') ? $type->getName() : get_class($type);
}, $result);
}
protected function getParamType(ReflectionFunction $reflection)
{
$paramType = null;
if ($reflection->getNumberOfParameters() > 0) {
$paramType = $reflection->getParameters()[0]->getType();
}
return $paramType;
}
}
@@ -0,0 +1,11 @@
<?php
namespace Spatie\Ray\Support;
class IgnoredValue
{
public static function make(): self
{
return new static();
}
}
@@ -0,0 +1,46 @@
<?php
namespace Spatie\Ray\Support;
use ReflectionClass;
use Spatie\Ray\Ray;
class Invador
{
public $obj;
public $reflected;
public $ray;
public function __construct(object $obj, Ray $ray)
{
$this->obj = $obj;
$this->reflected = new ReflectionClass($obj);
$this->ray = $ray;
}
public function __get(string $name): Ray
{
$property = $this->reflected->getProperty($name);
if (PHP_VERSION_ID < 80100) {
$property->setAccessible(true);
}
$value = $property->getValue($this->obj);
return $this->ray->send($value);
}
public function __call(string $name, array $params = []): Ray
{
$method = $this->reflected->getMethod($name);
if (PHP_VERSION_ID < 80100) {
$method->setAccessible(true);
}
$result = $method->invoke($this->obj, ...$params);
return $this->ray->send($result);
}
}
@@ -0,0 +1,50 @@
<?php
namespace Spatie\Ray\Support;
use Spatie\Ray\Origin\Origin;
class Limiters
{
/** @var array */
protected $counters = [];
public function initialize(Origin $origin, int $limit): array
{
if (! isset($this->counters[$origin->fingerPrint()])) {
$this->counters[$origin->fingerPrint()] = [0, $limit];
}
return $this->counters[$origin->fingerPrint()];
}
public function increment(Origin $origin): array
{
$name = $origin->fingerPrint();
if (! isset($this->counters[$name])) {
return [false, false];
}
[$times, $limit] = $this->counters[$name];
$newTimes = $times + 1;
$this->counters[$name] = [$newTimes, $limit];
return [$newTimes, $limit];
}
public function canSendPayload(Origin $origin): bool
{
$name = $origin->fingerPrint();
if (! isset($this->counters[$name])) {
return true;
}
[$times, $limit] = $this->counters[$name];
return $times < $limit || $limit <= 0;
}
}
@@ -0,0 +1,118 @@
<?php
namespace Spatie\Ray\Support;
use Exception;
class PlainTextDumper
{
private static $objects;
private static $output;
private static $depth;
/**
* Converts a variable into a string representation.
* @param mixed $var variable to be dumped
* @param int $depth maximum depth that the dumper should go into the variable. Defaults to 10.
* @return string the string representation of the variable
* @throws Exception
*/
public static function dump($var, int $depth = 5): string
{
self::$output = '';
self::$objects = [];
self::$depth = $depth;
self::dumpInternal($var, 0);
return self::$output;
}
/**
* @throws Exception
*/
private static function dumpInternal($var, $level): void
{
switch (gettype($var)) {
case 'boolean':
self::$output .= $var ? 'true' : 'false';
break;
case 'double':
case 'integer':
self::$output .= "$var";
break;
case 'string':
self::$output .= "'" . addslashes($var) . "'";
break;
case 'resource':
self::$output .= '{resource}';
break;
case 'NULL':
self::$output .= "null";
break;
case 'unknown type':
self::$output .= '{unknown}';
break;
case 'array':
if (self::$depth <= $level) {
self::$output .= '[...],';
} elseif (empty($var)) {
self::$output .= '[],';
} else {
$keys = array_keys($var);
$spaces = str_repeat(' ', $level * 4);
self::$output .= "[" . $spaces;
foreach ($keys as $key) {
self::$output .= "\n" . $spaces . ' ';
self::dumpInternal($key, 0);
self::$output .= ' => ';
self::dumpInternal($var[$key], $level + 1);
self::$output .= ',';
}
self::$output .= "\n" . $spaces . ']';
}
break;
case 'object':
if (($id = array_search($var, self::$objects, true)) !== false) {
self::$output .= get_class($var) . '#' . ($id + 1) . '(...)';
} elseif (self::$depth <= $level) {
self::$output .= get_class($var) . '(...)';
} else {
$id = array_push(self::$objects, $var);
$className = get_class($var);
$spaces = str_repeat(' ', $level * 4);
self::$output .= "$className#$id\n" . $spaces . '(';
if ('__PHP_Incomplete_Class' !== get_class($var) && method_exists($var, '__debugInfo')) {
$members = $var->__debugInfo();
if (! is_array($members)) {
throw new Exception('vardumper_not_array');
}
} else {
$members = (array) $var;
}
foreach ($members as $key => $value) {
$keyDisplay = strtr(trim($key), ["\0" => ':']);
self::$output .= "\n" . $spaces . " [$keyDisplay] => ";
self::dumpInternal($value, $level + 1);
}
self::$output .= "\n" . $spaces . ')';
}
break;
}
}
}
@@ -0,0 +1,106 @@
<?php
namespace Spatie\Ray\Support;
class RateLimiter
{
/** @var int|null */
protected $maxCalls;
/** @var int|null */
protected $maxPerSecond;
/** @var CacheStore */
protected $cache;
/** @var bool */
protected $notified;
private function __construct(?int $maxCalls, ?int $maxPerSecond)
{
$this->maxCalls = $maxCalls;
$this->maxPerSecond = $maxPerSecond;
$this->cache = new CacheStore(new SystemClock());
}
public static function disabled(): self
{
return new self(null, null);
}
public function hit(): self
{
$this->cache()->hit();
return $this;
}
public function max(?int $maxCalls): self
{
$this->maxCalls = $maxCalls;
return $this;
}
public function perSecond(?int $callsPerSecond): self
{
$this->maxPerSecond = $callsPerSecond;
return $this;
}
public function isMaxReached(): bool
{
if ($this->maxCalls === null) {
return false;
}
$reached = $this->cache()->count() >= $this->maxCalls;
if ($reached === false) {
$this->notified = false;
}
return $reached;
}
public function isMaxPerSecondReached(): bool
{
if ($this->maxPerSecond === null) {
return false;
}
$reached = $this->cache()->countLastSecond() >= $this->maxPerSecond;
if ($reached === false) {
$this->notified = false;
}
return $reached;
}
public function clear(): self
{
$this->maxCalls = null;
$this->maxPerSecond = null;
$this->cache()->clear();
return $this;
}
public function isNotified(): bool
{
return $this->notified;
}
public function notify(): void
{
$this->notified = true;
}
public function cache(): CacheStore
{
return $this->cache;
}
}
@@ -0,0 +1,13 @@
<?php
namespace Spatie\Ray\Support;
use DateTimeImmutable;
class SystemClock implements Clock
{
public function now(): DateTimeImmutable
{
return new DateTimeImmutable();
}
}
@@ -0,0 +1,63 @@
<?php
use Illuminate\Contracts\Container\BindingResolutionException;
use Spatie\CraftRay\Ray as CraftRay;
use Spatie\LaravelRay\Ray as LaravelRay;
use Spatie\Ray\Ray;
use Spatie\Ray\Settings\SettingsFactory;
use Spatie\RayBundle\Ray as SymfonyRay;
use Spatie\WordPressRay\Ray as WordPressRay;
use Spatie\YiiRay\Ray as YiiRay;
if (! function_exists('ray')) {
/**
* @param mixed ...$args
*
* @return \Spatie\Ray\Ray|LaravelRay|WordPressRay|YiiRay|SymfonyRay
*/
function ray(...$args)
{
if (class_exists(LaravelRay::class)) {
try {
return app(LaravelRay::class)->send(...$args);
} catch (BindingResolutionException $exception) {
// this exception can occur when requiring spatie/ray in an Orchestra powered
// testsuite without spatie/laravel-ray's service provider being registered
// in `getPackageProviders` of the base test suite
}
}
if (class_exists(CraftRay::class) && class_exists(Yii::class)) {
return Yii::$container->get(CraftRay::class)->send(...$args);
}
if (class_exists(YiiRay::class) && class_exists(Yii::class)) {
return Yii::$container->get(YiiRay::class)->send(...$args);
}
$rayClass = Ray::class;
if (class_exists(WordPressRay::class)) {
$rayClass = WordPressRay::class;
}
if (class_exists(SymfonyRay::class)) {
$rayClass = SymfonyRay::class;
}
$settings = SettingsFactory::createFromConfigFile();
return (new $rayClass($settings))->send(...$args);
}
register_shutdown_function(function () {
ray()->throwExceptions();
});
}
if (! function_exists('rd')) {
function rd(...$args)
{
ray(...$args)->die();
}
}