/home/smartonegroup/www/veroserv/vendor/spatie/ray/src/Ray.php
<?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): self
{
$backtrace = Backtrace::create();
if (class_exists(LaravelRay::class) && function_exists('base_path')) {
$backtrace->applicationPath(base_path());
}
if ($startingFromFrame) {
$backtrace->startingFromFrame($startingFromFrame);
}
$payload = new TracePayload($backtrace->frames());
return $this->sendRequest($payload);
}
public function backtrace(?Closure $startingFromFrame = null): self
{
return $this->trace($startingFromFrame);
}
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;
}
}