初始化项目

This commit is contained in:
2025-01-11 14:16:20 +08:00
parent 1ed53bf65b
commit cf64159069
11206 changed files with 1446622 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Application;
use DragonCode\Support\Facades\Helpers\Str;
/**
* @see https://www.php.net/manual/ru/reserved.constants.php
*/
class OS
{
public function isUnix(): bool
{
return ! $this->isWindows() && ! $this->isUnknown();
}
public function isWindows(): bool
{
return $this->family() === 'windows';
}
public function isBSD(): bool
{
return $this->family() === 'bsd';
}
public function isDarwin(): bool
{
return $this->family() === 'darwin';
}
public function isSolaris(): bool
{
return $this->family() === 'solaris';
}
public function isLinux(): bool
{
return $this->family() === 'linux';
}
public function isUnknown(): bool
{
return $this->family() === 'unknown';
}
public function family(): string
{
return Str::lower(PHP_OS_FAMILY);
}
}

View File

@@ -0,0 +1,70 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
declare(strict_types=1);
namespace DragonCode\Support\Application;
class Version
{
public function __construct(
protected ?string $version = null
) {
}
public function of(string $version): self
{
$this->version = $version;
return $this;
}
public function lt(string $version): bool
{
return $this->is($version, 'lt');
}
public function lte(string $version): bool
{
return $this->is($version, 'le');
}
public function gt(string $version): bool
{
return $this->is($version, 'gt');
}
public function gte(string $version): bool
{
return $this->is($version, 'ge');
}
public function equalTo(string $version): bool
{
return $this->is($version, 'eq');
}
public function notEqualTo(string $version): bool
{
return $this->is($version, 'ne');
}
protected function is(string $version, string $comparator): bool
{
return version_compare($version, $this->version, $comparator);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Callbacks;
class Empties
{
public function notEmpty(): callable
{
return static fn ($value) => ! empty($value) || is_bool($value);
}
public function notEmptyBoth(): callable
{
return static fn ($value, $key) => (! empty($value) || is_bool($value)) && ! empty($key);
}
}

View File

@@ -0,0 +1,121 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Callbacks;
use DragonCode\Support\Facades\Helpers\Str;
use JetBrains\PhpStorm\Pure;
class Sorter
{
/**
* Gets an array of special characters.
*
* @return string[]
*/
public function specialChars(): array
{
return [
' ',
'*',
'-',
'_',
'—',
'=',
'\\',
'/',
'|',
'~',
'`',
'+',
':',
';',
'@',
'#',
'$',
'%',
'^',
'&',
'?',
'!',
'(',
')',
'{',
'}',
'[',
']',
'§',
'№',
'<',
'>',
'.',
',',
];
}
/**
* Gets a callback function for sorting.
*
* @return callable
*/
public function default(): callable
{
return function ($current, $next) {
$current = $this->lower($current);
$next = $this->lower($next);
if ($current === $next) {
return 0;
}
if (is_string($current) && is_numeric($next)) {
return $this->hasSpecialChar($current) ? -1 : 1;
}
if (is_numeric($current) && is_string($next)) {
return $this->hasSpecialChar($next) ? 1 : -1;
}
return $current < $next ? -1 : 1;
};
}
/**
* Prepares a value for case-insensitive sorting.
*
* @param $value
*
* @return mixed|string|null
*/
protected function lower($value): mixed
{
return is_string($value) ? Str::lower($value) : $value;
}
/**
* Determine if a value is a special character.
*
* @param $value
*
* @return bool
*/
#[Pure]
protected function hasSpecialChar($value): bool
{
return in_array($value, $this->specialChars());
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Concerns;
use DragonCode\Support\Facades\Helpers\Str;
/**
* @property array $casts
*/
trait Castable
{
protected function cast(array &$source)
{
foreach ($source as $key => &$value) {
$value = $this->castValue($key, $value);
}
}
protected function castValue(string $key, mixed $value)
{
$cast = $this->castKey($key);
$method = $this->castMethodName($cast);
return $this->{$method}($value);
}
protected function castKey(string $key): string
{
return $this->casts[$key] ?? 'default';
}
protected function castMethodName(string $key): string
{
return (string) Str::of($key)->start('castTo_')->camel();
}
protected function castToArray(mixed $value): array
{
if (empty($value)) {
return [];
}
if (is_array($value)) {
return $value;
}
parse_str($value, $output);
return $output;
}
protected function castToInteger(mixed $value): ?int
{
return empty($value) && ! is_numeric($value) ? null : $value;
}
protected function castToString(?string $value): string
{
return (string) $value;
}
protected function castToDefault(mixed $value): mixed
{
return $value;
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
declare(strict_types=1);
namespace DragonCode\Support\Concerns;
use JetBrains\PhpStorm\NoReturn;
trait Dumpable
{
/**
* Outputs the contents of a variable without terminating the application.
*
* @return $this
*/
public function dump(): static
{
dump($this->value);
return $this;
}
/**
* Outputs the contents of a variable, terminating the application.
*
* @return void
*/
#[NoReturn]
public function dd(): void
{
dd($this->value);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Concerns;
use JetBrains\PhpStorm\Pure;
trait Makeable
{
#[Pure]
public static function make(...$parameters): static
{
return new static(...$parameters);
}
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Concerns;
use DragonCode\Support\Facades\Instances\Call;
trait Resolvable
{
protected static array $resolved = [];
protected static function resolveInstance(object|string $instance, mixed ...$parameters): mixed
{
$class = self::resolveInstanceClass($instance);
if (isset(self::$resolved[$class])) {
return self::$resolved[$class];
}
return self::$resolved[$class] = is_object($instance) ? $instance : new $instance(...$parameters);
}
protected static function resolveCallback(string $value, callable $callback): mixed
{
$class = static::getSameClass();
if (isset(static::$resolved[$class][$value])) {
return static::$resolved[$class][$value];
}
return static::$resolved[$class][$value] = Call::callback($callback, $value);
}
protected static function resolveInstanceClass(object|string $instance): string
{
return is_object($instance) ? get_class($instance) : $instance;
}
protected static function getSameClass(): string
{
return static::class;
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Concerns;
use DragonCode\Support\Exceptions\ForbiddenVariableTypeException;
use DragonCode\Support\Facades\Helpers\Arr;
use DragonCode\Support\Facades\Helpers\Str;
trait Validation
{
/**
* @param mixed $haystack
* @param array|string $needles
*/
protected function validateType(mixed $haystack, mixed $needles): void
{
$type = $this->validateGetType($haystack);
$needles = $this->validateNeedles($needles);
if (! Str::contains($type, $needles)) {
throw new ForbiddenVariableTypeException($type, $needles);
}
}
protected function validateNeedles(mixed $values): array
{
return Arr::map((array) $values, static fn ($value) => Str::lower($value));
}
protected function validateGetType($haystack): string
{
return Str::lower(gettype($haystack));
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Exceptions;
use Exception;
use JetBrains\PhpStorm\Pure;
class DirectoryNotFoundException extends Exception
{
#[Pure]
public function __construct(?string $path)
{
parent::__construct('Directory "' . $path . '" does not exist.');
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Exceptions;
use Exception;
use JetBrains\PhpStorm\Pure;
class FileNotFoundException extends Exception
{
#[Pure]
public function __construct(?string $path)
{
parent::__construct('File "' . $path . '" does not exist.');
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
declare(strict_types=1);
namespace DragonCode\Support\Exceptions;
use ParseError;
class FileSyntaxErrorException extends ParseError
{
public function __construct(string $filename)
{
$path = realpath($filename);
parent::__construct("File \"$path\" has an incorrect structure or is corrupted.");
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Exceptions;
use JetBrains\PhpStorm\Pure;
use LogicException;
class ForbiddenVariableTypeException extends LogicException
{
#[Pure]
public function __construct(string $haystack, array|string $needles)
{
$needles = $this->needles($needles);
$message = $this->message($haystack, $needles);
parent::__construct($message);
}
protected function needles(array|string $needles): string
{
return is_string($needles) ? $needles : implode(', ', $needles);
}
protected function message(string $haystack, string $needles): string
{
return "Forbidden variable type: $needles needles, $haystack given.";
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
declare(strict_types=1);
namespace DragonCode\Support\Exceptions;
use Exception;
use JetBrains\PhpStorm\Pure;
class InvalidDestinationPathException extends Exception
{
#[Pure]
public function __construct(?string $path)
{
parent::__construct('The start and end paths must not be the same: ' . $path);
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Exceptions;
use Exception;
use JetBrains\PhpStorm\Pure;
class NotValidUrlException extends Exception
{
#[Pure]
public function __construct(?string $url)
{
$value = $this->value($url);
$message = $this->message($value);
parent::__construct($message, 412);
}
protected function value(?string $url): string
{
return ! empty($url) ? 'The "' . $url . '"' : 'Empty string';
}
protected function message(string $value): string
{
return $value . ' is not a valid URL.';
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Exceptions;
use Exception;
use JetBrains\PhpStorm\Pure;
class UnhandledFileExtensionException extends Exception
{
#[Pure]
public function __construct(?string $path)
{
parent::__construct('Unhandled file extension: ' . $path);
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Exceptions;
use Exception;
use JetBrains\PhpStorm\Pure;
class UnknownStubFileException extends Exception
{
#[Pure]
public function __construct(?string $filename)
{
parent::__construct('Unknown stub file: "' . $filename . '"', 400);
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Exceptions;
use JetBrains\PhpStorm\Pure;
use LogicException;
class UnknownUrlComponentIndexException extends LogicException
{
#[Pure]
public function __construct(int $component)
{
parent::__construct('Unknown URL component index: ' . $component);
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Application;
use DragonCode\Support\Application\OS as Helper;
use DragonCode\Support\Facades\Facade;
/**
* @method static bool isBSD()
* @method static bool isDarwin()
* @method static bool isLinux()
* @method static bool isSolaris()
* @method static bool isUnix()
* @method static bool isUnknown()
* @method static bool isWindows()
* @method static string family()
*/
class OS extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
declare(strict_types=1);
namespace DragonCode\Support\Facades\Application;
use DragonCode\Support\Application\Version as Helper;
use DragonCode\Support\Facades\Facade;
/**
* @method static Helper of(string $version)
*/
class Version extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Callbacks;
use DragonCode\Support\Callbacks\Empties as Callback;
use DragonCode\Support\Facades\Facade;
/**
* @method static callable notEmpty()
* @method static callable notEmptyBoth()
*/
class Empties extends Facade
{
protected static function getFacadeAccessor(): string
{
return Callback::class;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Callbacks;
use DragonCode\Support\Callbacks\Sorter as Callback;
use DragonCode\Support\Facades\Facade;
/**
* @method static array specialChars()
* @method static callable default()
*/
class Sorter extends Facade
{
protected static function getFacadeAccessor(): string
{
return Callback::class;
}
}

View File

@@ -0,0 +1,49 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades;
use DragonCode\Support\Concerns\Resolvable;
use RuntimeException;
abstract class Facade
{
use Resolvable;
public static function __callStatic($method, $args): mixed
{
if ($instance = self::getFacadeRoot()) {
return $instance->{$method}(...$args);
}
throw new RuntimeException('A facade root has not been set.');
}
public static function getFacadeRoot(): object
{
return self::resolveInstance(static::getFacadeAccessor());
}
public static function clearResolvedInstances(): void
{
self::$resolved = [];
}
protected static function getFacadeAccessor(): mixed
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Filesystem;
use DirectoryIterator;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Filesystem\Directory as Helper;
use SplFileInfo;
/**
* @method static array allPaths(string $path, ?callable $callback = null, bool $recursive = false)
* @method static array names(string $path, callable|null $callback = null, bool $recursive = false)
* @method static bool doesntExist(string $path)
* @method static bool exists(string $path)
* @method static bool isDirectory(DirectoryIterator|SplFileInfo|string $value)
* @method static bool make(string $path, int $mode = 0755)
* @method static DirectoryIterator|DirectoryIterator[] all(string $path)
* @method static string validated(DirectoryIterator|SplFileInfo|string $path)
* @method static void copy(string $source, string $target)
* @method static void delete(array|string $path)
* @method static void ensureDelete(array|string $path)
* @method static void ensureDirectory(string $path, int $mode = 0755, bool $can_delete = false)
* @method static void move(string $source, string $target)
* @method static void validate(DirectoryIterator|SplFileInfo|string $path)
*/
class Directory extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Filesystem;
use DirectoryIterator;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Filesystem\File as Helper;
use SplFileInfo;
/**
* @method static array allPaths(string $path, ?callable $callback = null, bool $recursive = false)
* @method static array load(string $path)
* @method static array names(string $path, callable|null $callback = null, bool $recursive = false)
* @method static bool exists(string $path)
* @method static bool isFile(DirectoryIterator|SplFileInfo|string $value)
* @method static string store(string $path, string $content, int $mode = 0755)
* @method static string validated(DirectoryIterator|SplFileInfo|string $path)
* @method static void copy(string $source, string $target, int $mode = 0755)
* @method static void delete(string|string[] $paths)
* @method static void ensureDelete(array|string $paths)
* @method static void move(string $source, string $target, int $mode = 0755)
* @method static void validate(DirectoryIterator|SplFileInfo|string $path)
*/
class File extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Filesystem;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Filesystem\Path as Helper;
/**
* @method static string basename(string $path)
* @method static string dirname(string $path)
* @method static string extension(string $path)
* @method static string filename(string $path)
*/
class Path extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Helpers;
use ArrayAccess;
use ArrayObject;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Helpers\Ables\Arrayable;
use DragonCode\Support\Helpers\Arr as Helper;
/**
* @method static Arrayable of(ArrayObject|array|null $value = [])
* @method static Arrayable ofFile(string $path)
* @method static array addUnique(array $array, mixed $values)
* @method static array combine(...$arrays)
* @method static array except(array $array, array|callable|string $keys)
* @method static array filter($array, callable $callback = null, int $mode = 0)
* @method static array flatten(array $array, bool $ignore_keys = true)
* @method static array flattenKeys(mixed $array, string $delimiter = '.', ?string $prefix = null)
* @method static array flip($array)
* @method static array keys($array)
* @method static array ksort(array $array, callable $callback = null)
* @method static array map(array|ArrayAccess $array, callable $callback, bool $recursive = false)
* @method static array mapInto(array $array, string $class)
* @method static array merge(...$arrays)
* @method static array only(array|ArrayAccess $array, array|callable|string $keys)
* @method static array push(array|ArrayAccess $array, mixed ...$values)
* @method static array remove(array|ArrayAccess $array, mixed $key)
* @method static array renameKeys(array $array, callable $callback)
* @method static array renameKeysMap(array $array, array $map)
* @method static array resolve($value = null)
* @method static array reverse(array $array, bool $preserve_keys = false)
* @method static array set(array|ArrayAccess $array, array|mixed $key, mixed $value = null)
* @method static array sort(array $array, callable $callback = null)
* @method static array sortByKeys(array $array, array $sorter)
* @method static array splice(array $array, int $offset, ?int $length = null, mixed $replacement = null)
* @method static array tap(array|ArrayAccess $array, callable $callback)
* @method static array unique(array $array, int $flags = SORT_STRING)
* @method static array values($array)
* @method static array wrap($value = null)
* @method static bool doesntEmpty($value)
* @method static bool doesntExist(array|ArrayAccess $array, $key)
* @method static bool doesntExistWithoutDot(array|ArrayAccess $array, $key)
* @method static bool exists(array|ArrayAccess $array, $key)
* @method static bool existsWithoutDot(array|ArrayAccess $array, $key)
* @method static bool isArrayable($value = null)
* @method static bool isEmpty($value)
* @method static int count(ArrayObject|array $array)
* @method static int longestStringLength(array $array)
* @method static mixed first(iterable $array, ?callable $callback = null, mixed $default = null)
* @method static mixed get(array|ArrayAccess $array, $key, $default = null)
* @method static mixed getKey(array|ArrayAccess $array, $key, $default = null)
* @method static mixed last(array $array, ?callable $callback = null, mixed $default = null)
*/
class Arr extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Helpers;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Helpers\Boolean as Helper;
/**
* @method static bool isFalse($value)
* @method static bool isTrue($value)
* @method static bool to($value)
* @method static bool|null parse($value)
* @method static string toString(bool $value)
*/
class Boolean extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Helpers;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Helpers\Digit as Helper;
/**
* @method static float rounded(float $number, int $length = 4, int $precision = 1)
* @method static int factorial(int $n = 0)
* @method static string toChars(int $number, string $chars = 'abcdefghijklmnopqrstuvwxyz')
* @method static string toShort(float $number, int $precision = 1, ?string $suffix = null)
* @method static string toString(float $value)
*/
class Digit extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2023 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Helpers;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Helpers\Ables\Stringable;
use DragonCode\Support\Helpers\Str as Helper;
/**
* @method static Stringable of(?string $value)
* @method static array|null matchAll(string $value, string $pattern)
* @method static bool contains(string $haystack, $needles)
* @method static bool doesntEmpty($value)
* @method static bool endsWith(string $haystack, string|string[] $needles)
* @method static bool is(string|array $pattern, mixed $value)
* @method static bool isEmpty($value)
* @method static bool matchContains(string $value, array|string $pattern)
* @method static bool startsWith(string $haystack, string|string[] $needles)
* @method static int count(?string $value, string $needle, int $offset = 0)
* @method static int length(?string $value, string $encoding = null)
* @method static string append(mixed $value, string $suffix)
* @method static string ascii(?string $value, string $language = 'en')
* @method static string between(?string $value, mixed $from, mixed $to, bool $trim = true)
* @method static string ltrim(?string $string, string $characters = " \t\n\r\0\x0B")
* @method static string prepend(mixed $value, string $prefix)
* @method static string random(int $length = 16)
* @method static string replace(string $template, array|string|int|float $search, array|string|int|float|null $replace = null)
* @method static string replaceFormat(string $template, array $values, string $key_format = null)
* @method static string rtrim(?string $string, string $characters = " \t\n\r\0\x0B")
* @method static string slug(string $title, string $separator = '-', ?string $language = 'en')
* @method static string toString(?string $value)
* @method static string trim(?string $string, string $characters = " \t\n\r\0\x0B")
* @method static string|null after(string $subject, string $search)
* @method static string|null before(string $subject, string $search)
* @method static string|null camel(?string $value)
* @method static string|null choice(float $number, array $choice = [], string $extra = null)
* @method static string|null de(?string $value)
* @method static string|null e(?string $value, bool $double = true)
* @method static string|null end(?string $value, string $suffix)
* @method static string|null finish(string $value, string $cap = '/')
* @method static string|null lower(?string $value)
* @method static string|null map(?string $value, callable $callback)
* @method static string|null match(string $value, string $pattern)
* @method static string|null pregReplace(?string $value, string $pattern, string $replacement)
* @method static string|null snake(?string $value, string $delimiter = '_')
* @method static string|null squish(?string $value)
* @method static string|null start(?string $value, string $prefix)
* @method static string|null studly(?string $value)
* @method static string|null substr(string $string, int $start, int $length = null)
* @method static string|null title(?string $value)
* @method static string|null upper(?string $value)
*/
class Str extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Http;
use DragonCode\Contracts\Http\Builder as BuilderContract;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Http\Builder as Support;
use Psr\Http\Message\UriInterface;
/**
* @method static array getQueryArray()
* @method static array toArray()
* @method static int|null getPort()
* @method static string getAuthority()
* @method static string getBaseUrl()
* @method static string getDomain()
* @method static string getFragment()
* @method static string getHost()
* @method static string getPassword()
* @method static string getPath()
* @method static string getQuery()
* @method static string getScheme()
* @method static string getSubDomain()
* @method static string getUser()
* @method static string getUserInfo()
* @method static string toUrl()
* @method static string|null getBaseDomain()
* @method static string|null getDomainLevel(int $level = 0)
* @method static Support fromPsr(UriInterface $uri)
* @method static Support parse(mixed $url, int $component = BuilderContract::PHP_URL_ALL)
* @method static Support parsed(array $parsed)
* @method static Support putQuery(string $key, mixed $value)
* @method static Support removeFragment()
* @method static Support removeQuery(string $key)
* @method static Support same()
* @method static Support withFragment(string $fragment)
* @method static Support withHost(string $host)
* @method static Support withPath(string $path)
* @method static Support withPort(int|null $host)
* @method static Support withQuery(array|string $path)
* @method static Support withScheme(string $scheme)
* @method static Support withUserInfo(string $user, string $password = null)
* @method static UriInterface toPsr()
*/
class Builder extends Facade
{
protected static function getFacadeAccessor(): string
{
return Support::class;
}
}

View File

@@ -0,0 +1,37 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Http;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Http\Url as Helper;
/**
* @method static \DragonCode\Support\Http\Builder parse(\DragonCode\Contracts\Http\Builder|string|null $url)
* @method static bool exists(\DragonCode\Contracts\Http\Builder|string|null $url)
* @method static bool is(\DragonCode\Contracts\Http\Builder|string|null $url)
* @method static \DragonCode\Support\Http\Builder|\DragonCode\Contracts\Http\Builder|string validated(\DragonCode\Contracts\Http\Builder|string|null $url)
* @method static string|null default(\DragonCode\Contracts\Http\Builder|string|null $url, \DragonCode\Contracts\Http\Builder|string|null $default)
* @method static void validate(\DragonCode\Contracts\Http\Builder|string|null $url)
*/
class Url extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,38 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Instances;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Instances\Call as Helper;
/**
* @method static mixed callback(mixed $callback, mixed ...$parameters)
* @method static mixed run(mixed $class, string $method, ...$parameters)
* @method static mixed runExists(mixed $class, string $method, ...$parameters)
* @method static mixed runMethods($class, $methods, ...$parameters)
* @method static mixed runOf(array $map, $value, ...$parameters)
* @method static mixed value(mixed $callback, mixed $parameters = [])
* @method static mixed when(bool $when, mixed $class, string $method, ...$parameters)
*/
class Call extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Instances;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Instances\Instance as Helper;
/**
* @method static bool exists(object|string $haystack)
* @method static bool of(object|string $haystack, string|string[] $needles)
* @method static string|null basename(object|string $class)
* @method static string|null classname(object|string $class = null)
*/
class Instance extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Instances;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Instances\Reflection as Helper;
use ReflectionClass;
/**
* @method static array getConstants(object|string $class)
* @method static bool isStaticMethod(object|ReflectionClass|string $class, string $method)
* @method static ReflectionClass resolve(object|ReflectionClass|string $class)
*/
class Reflection extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Tools;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Tools\Replace as Tool;
/**
* @method static string toFormat(string $value, string $format = null)
* @method static string toFormatArray(array $values, string $format = null)
*/
class Replace extends Facade
{
protected static function getFacadeAccessor(): string
{
return Tool::class;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Tools;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Tools\Stub as Tool;
/**
* @method static string replace(string $stub_file, array $replace)
* @method static string get(string $filename)
*/
class Stub extends Facade
{
protected static function getFacadeAccessor(): string
{
return Tool::class;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Facades\Types;
use DragonCode\Support\Facades\Facade;
use DragonCode\Support\Types\Is as Helper;
/**
* @method static bool boolean($value)
* @method static bool contract($value)
* @method static bool doesntEmpty($value)
* @method static bool error($value)
* @method static bool isEmpty($value)
* @method static bool object($value)
* @method static bool reflectionClass($value)
* @method static bool string($value)
*/
class Is extends Facade
{
protected static function getFacadeAccessor(): string
{
return Helper::class;
}
}

View File

@@ -0,0 +1,311 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Filesystem;
use DirectoryIterator;
use DragonCode\Support\Exceptions\DirectoryNotFoundException;
use DragonCode\Support\Exceptions\InvalidDestinationPathException;
use DragonCode\Support\Facades\Filesystem\File as FileHelper;
use DragonCode\Support\Facades\Helpers\Arr;
use DragonCode\Support\Facades\Helpers\Str;
use DragonCode\Support\Facades\Instances\Call;
use DragonCode\Support\Facades\Instances\Instance;
use FilesystemIterator;
use SplFileInfo;
class Directory
{
/**
* Get a list of files and folders in a directory.
*
* @param string $path
*
* @throws \DragonCode\Support\Exceptions\DirectoryNotFoundException
*
* @return DirectoryIterator|DirectoryIterator[]
*/
public function all(string $path): DirectoryIterator
{
if ($this->doesntExist($path)) {
throw new DirectoryNotFoundException($path);
}
return new DirectoryIterator($path);
}
/**
* Get a list of directory paths.
*
* @param string $path
* @param callable|null $callback
* @param bool $recursive
*
* @throws \DragonCode\Support\Exceptions\DirectoryNotFoundException
*
* @return array
*/
public function allPaths(string $path, ?callable $callback = null, bool $recursive = false): array
{
$items = [];
foreach ($this->all($path) as $directory) {
if ($directory->isDir() && ! $directory->isDot()) {
if (! is_callable($callback) || Call::callback($callback, $directory->getRealPath())) {
$items[] = $directory->getRealPath();
}
}
if ($recursive && $directory->isDir() && ! $directory->isDot()) {
$directories = $this->allPaths($directory->getRealPath(), $callback, $recursive);
$items = array_merge($items, $directories);
}
}
sort($items);
return array_values($items);
}
/**
* Get a list of directory names along a path.
*
* @param string $path
* @param callable|null $callback
* @param bool $recursive
*
* @throws \DragonCode\Support\Exceptions\DirectoryNotFoundException
*
* @return array
*/
public function names(string $path, ?callable $callback = null, bool $recursive = false): array
{
return Arr::of(
$this->allPaths($path, $callback, $recursive)
)
->map(fn (string $value) => Str::of($value)->after(realpath($path))->trim('\\/')->toString())
->toArray();
}
/**
* Create a directory at the specified path.
*
* @param string $path
* @param int $mode
*
* @return bool
*/
public function make(string $path, int $mode = 0755): bool
{
return ! $this->doesntExist($path) || mkdir($path, $mode, true);
}
/**
* Copies directory.
*
* @param string $source
* @param string $target
*
* @throws \DragonCode\Support\Exceptions\DirectoryNotFoundException
* @throws \DragonCode\Support\Exceptions\InvalidDestinationPathException
*
* @return void
*/
public function copy(string $source, string $target): void
{
$this->validate($source);
$this->comparePaths($source, $target);
$this->ensureDirectory($target);
foreach (FileHelper::names($source, recursive: true) as $file) {
FileHelper::copy(
$source . '/' . $file,
$target . '/' . $file
);
}
}
/**
* Moving a directory to a new path.
*
* @param string $source
* @param string $target
*
* @throws \DragonCode\Support\Exceptions\DirectoryNotFoundException
* @throws \DragonCode\Support\Exceptions\InvalidDestinationPathException
*
* @return void
*/
public function move(string $source, string $target): void
{
$this->copy($source, $target);
$this->ensureDelete($source);
}
/**
* Delete the directory with all contents in the specified path.
*
* @param array|string $paths
*
* @throws \DragonCode\Support\Exceptions\DirectoryNotFoundException
*
* @return void
*/
public function delete(array|string $paths): void
{
foreach ((array) $paths as $path) {
if (! $this->isDirectory($path)) {
throw new DirectoryNotFoundException($path);
}
$items = new FilesystemIterator($path);
foreach ($items as $item) {
$item->isDir() && ! $item->isLink()
? $this->delete($item->getPathname())
: FileHelper::delete($item->getPathname());
}
@rmdir($path);
}
}
/**
* Ensure the directory has been deleted.
*
* @param array|string $paths
*
* @throws \DragonCode\Support\Exceptions\DirectoryNotFoundException
*
* @return void
*/
public function ensureDelete(array|string $paths): void
{
foreach ((array) $paths as $path) {
$this->doesntExist($path) || $this->delete($path);
}
}
/**
* Ensure created directory exists.
*
* @param string $path
* @param int $mode
* @param bool $can_delete
*
* @throws \DragonCode\Support\Exceptions\DirectoryNotFoundException
*/
public function ensureDirectory(string $path, int $mode = 0755, bool $can_delete = false): void
{
if ($can_delete && $this->exists($path)) {
$this->delete($path);
}
if ($this->doesntExist($path)) {
$this->make($path, $mode);
}
}
/**
* Check if the directory exists.
*
* @param string $path
*
* @return bool
*/
public function exists(string $path): bool
{
return file_exists($path) && is_dir($path);
}
/**
* Check if the directory doesn't exists.
*
* @param string $path
*
* @return bool
*/
public function doesntExist(string $path): bool
{
return ! $this->exists($path);
}
/**
* Check if object or path is a directory.
*
* @param DirectoryIterator|SplFileInfo|string $value
*
* @return bool
*/
public function isDirectory(mixed $value): bool
{
if (Instance::of($value, [SplFileInfo::class, DirectoryIterator::class])) {
return $value->isDir();
}
return is_dir($value);
}
/**
* Comparison of start and end paths.
*
* @param string $path1
* @param string $path2
*
* @throws \DragonCode\Support\Exceptions\InvalidDestinationPathException
*
* @return void
*/
public function comparePaths(string $path1, string $path2): void
{
if ($path1 === $path2 || realpath($path1) === realpath($path2)) {
throw new InvalidDestinationPathException(realpath($path1));
}
}
/**
* Checks the existence of a directory.
*
* @param DirectoryIterator|SplFileInfo|string $path
*
* @throws \DragonCode\Support\Exceptions\DirectoryNotFoundException
*/
public function validate(string $path): void
{
if (! $this->isDirectory($path)) {
throw new DirectoryNotFoundException($path);
}
}
/**
* Checks the existence of a directory and return full path if exist.
*
* @param DirectoryIterator|SplFileInfo|string $path
*
* @throws \DragonCode\Support\Exceptions\DirectoryNotFoundException
*
* @return string
*/
public function validated(string $path): string
{
$this->validate($path);
return realpath($path);
}
}

View File

@@ -0,0 +1,256 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Filesystem;
use DirectoryIterator;
use DragonCode\Support\Exceptions\FileNotFoundException;
use DragonCode\Support\Exceptions\FileSyntaxErrorException;
use DragonCode\Support\Exceptions\UnhandledFileExtensionException;
use DragonCode\Support\Facades\Filesystem\Directory;
use DragonCode\Support\Facades\Filesystem\Path;
use DragonCode\Support\Facades\Helpers\Arr;
use DragonCode\Support\Facades\Helpers\Str;
use DragonCode\Support\Facades\Instances\Call;
use DragonCode\Support\Facades\Instances\Instance;
use SplFileInfo;
use Throwable;
class File
{
/**
* Get a list of filenames with a full paths.
*
* @param string $path
* @param callable|null $callback
* @param bool $recursive
*
* @return array
*/
public function allPaths(string $path, ?callable $callback = null, bool $recursive = false): array
{
$items = [];
foreach (Directory::all($path) as $item) {
if ($item->isFile()) {
if (! is_callable($callback) || Call::callback($callback, $item->getRealPath())) {
$items[] = $item->getRealPath();
}
}
if ($recursive && $item->isDir() && ! $item->isDot()) {
$files = $this->allPaths($item->getRealPath(), $callback, $recursive);
$items = array_merge($items, $files);
}
}
sort($items);
return array_values($items);
}
/**
* Get a list of filenames along a path.
*
* @param string $path
* @param callable|null $callback
* @param bool $recursive
*
* @return array
*/
public function names(string $path, ?callable $callback = null, bool $recursive = false): array
{
return Arr::of(
$this->allPaths($path, $callback, $recursive)
)
->map(fn (string $value) => Str::of($value)->after(realpath($path))->trim('\\/')->toString())
->toArray();
}
/**
* Save content to file.
*
* @param string $path
* @param string $content
* @param int $mode
*
* @return string returns the full path to the saved file
*/
public function store(string $path, string $content, int $mode = 0755): string
{
Directory::ensureDirectory(Path::dirname($path), $mode);
file_put_contents($path, $content);
return realpath($path);
}
/**
* Load content from the file.
*
* @param string $path
*
* @throws \DragonCode\Support\Exceptions\FileNotFoundException
*
* @return array
*/
public function load(string $path): array
{
try {
if (! $this->exists($path)) {
throw new FileNotFoundException($path);
}
return match (Path::extension($path)) {
'php' => require $path,
'json' => json_decode(file_get_contents($path), true, 512, JSON_THROW_ON_ERROR),
default => throw new UnhandledFileExtensionException($path)
};
}
catch (FileNotFoundException|UnhandledFileExtensionException $e) {
throw $e;
}
catch (Throwable) {
throw new FileSyntaxErrorException($path);
}
}
/**
* Copies file.
*
* @param string $source
* @param string $target
* @param int $mode
*/
public function copy(string $source, string $target, int $mode = 0755): void
{
Directory::ensureDirectory(Path::dirname($target), $mode);
copy($source, $target);
}
/**
* Moving a file to a new path.
*
* @param string $source
* @param string $target
* @param int $mode
*
* @throws \DragonCode\Support\Exceptions\FileNotFoundException
*/
public function move(string $source, string $target, int $mode = 0755): void
{
Directory::ensureDirectory(Path::dirname($target), $mode);
rename($source, $target);
}
/**
* Checks if the file exists.
*
* @param string $path
*
* @return bool
*/
public function exists(string $path): bool
{
return file_exists($path) && is_file($path);
}
/**
* Deletes files in the specified paths.
*
* @param string|string[] $paths
*
* @throws \DragonCode\Support\Exceptions\FileNotFoundException
*
* @return void
*/
public function delete(array|string $paths): void
{
foreach ((array) $paths as $path) {
if (! $this->exists($path)) {
throw new FileNotFoundException($path);
}
unlink($path);
}
}
/**
* Ensure the file has been deleted.
*
* @param array|string $paths
*
* @throws \DragonCode\Support\Exceptions\FileNotFoundException
*
* @return void
*/
public function ensureDelete(array|string $paths): void
{
foreach ((array) $paths as $path) {
$this->exists($path) && $this->delete($path);
}
}
/**
* Checks if an object or link is a file at the specified path.
*
* @param DirectoryIterator|SplFileInfo|string $value
*
* @return bool
*/
public function isFile(mixed $value): bool
{
if (Instance::of($value, [SplFileInfo::class, DirectoryIterator::class])) {
return $value->isFile();
}
return is_file($value);
}
/**
* Checks the existence of a file.
*
* @param DirectoryIterator|SplFileInfo|string $path
*
* @throws \DragonCode\Support\Exceptions\FileNotFoundException
*/
public function validate(mixed $path): void
{
if (! $this->isFile($path)) {
throw new FileNotFoundException($path);
}
}
/**
* Checks the existence of a file and return full path if exist.
*
* @param DirectoryIterator|SplFileInfo|string $path
*
* @throws \DragonCode\Support\Exceptions\FileNotFoundException
*
* @return string
*/
public function validated(mixed $path): string
{
$this->validate($path);
return realpath($path);
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Filesystem;
use DragonCode\Support\Facades\Helpers\Str;
class Path
{
public function dirname(string $path): string
{
return pathinfo($path, PATHINFO_DIRNAME);
}
public function basename(string $path): string
{
return pathinfo($path, PATHINFO_BASENAME);
}
public function extension(string $path): string
{
return Str::lower(pathinfo($path, PATHINFO_EXTENSION));
}
public function filename(string $path): string
{
return pathinfo($path, PATHINFO_FILENAME);
}
}

View File

@@ -0,0 +1,593 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Helpers\Ables;
use ArrayObject;
use DragonCode\Contracts\Support\Arrayable as ArrayableContract;
use DragonCode\Support\Concerns\Dumpable;
use DragonCode\Support\Facades\Helpers\Arr;
use DragonCode\Support\Facades\Instances\Call;
use DragonCode\Support\Facades\Instances\Instance;
use JetBrains\PhpStorm\Pure;
class Arrayable implements ArrayableContract
{
use Dumpable;
protected Arrayable|ArrayObject|array|null $value = [];
public function __construct(Arrayable|ArrayObject|array|null $value = [])
{
$this->of($value);
}
public function of(Arrayable|ArrayObject|array|null $value = []): self
{
$this->value = Instance::of($value, Arrayable::class) ? $value->toArray() : (array) $value;
return $this;
}
/**
* Performing an action on a condition.
*
* @param mixed $condition
* @param callable $callback
* @param mixed|null $default
*
* @return $this
*/
public function when(mixed $condition, callable $callback, mixed $default = null): self
{
if (Call::value($condition, $this)) {
$value = Call::value($callback, $this);
return new self($value);
}
return ! is_null($default) ? $this->when(true, $default) : $this;
}
/**
* Join array elements with a string.
*
* @param string $separator
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
#[Pure]
public function implode(string $separator): Stringable
{
return new Stringable(implode($separator, $this->value));
}
/**
* Returns the final array.
*
* @return array
*/
public function toArray(): array
{
return $this->value ?: [];
}
/**
* Renaming array keys.
* As the second parameter, a callback function is passed, which determines the actions for processing the value.
* The output of the function must be a string with a name.
*
* @param callable $callback
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function renameKeys(callable $callback): self
{
return new self(Arr::renameKeys($this->value, $callback));
}
/**
* Renaming array keys with map.
*
* @param array $map
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function renameKeysMap(array $map): self
{
return new self(Arr::renameKeysMap($this->value, $map));
}
/**
* Push one a unique element onto the end of array.
*
* @param mixed $values
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function addUnique(mixed $values): self
{
return new self(Arr::addUnique($this->value, $values));
}
/**
* Removes duplicate values from an array.
*
* Sorting type flags:
* SORT_REGULAR - compare items normally
* SORT_NUMERIC - compare items numerically
* SORT_STRING - compare items as strings
* SORT_LOCALE_STRING - compare items as strings, based on the current locale
*
* @see https://php.net/manual/en/function.array-unique.php
*
* @param int $flags
*
* @return $this
*/
public function unique(int $flags = SORT_STRING): self
{
return new self(Arr::unique($this->value, $flags));
}
/**
* Sort an associative array in the order specified by an array of keys.
*
* Example:
*
* $arr = ['q' => 1, 'r' => 2, 's' => 5, 'w' => 123];
*
* Arr::sortByKeys($arr, ['q', 'w', 'e']);
*
* print_r($arr);
*
* Array
* (
* [q] => 1
* [w] => 123
* [r] => 2
* [s] => 5
* )
*
* @see https://gist.github.com/Ellrion/a3145621f936aa9416f4c04987533d8d#file-helper-php
*
* @param array $sorter
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function sortByKeys(array $sorter): self
{
return new self(Arr::sortByKeys($this->value, $sorter));
}
/**
* Recursively sorting an array by values.
*
* @param callable|null $callback
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function sort(?callable $callback = null): self
{
return new self(Arr::sort($this->value, $callback));
}
/**
* Recursively sorting an array by keys.
*
* @param callable|null $callback
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function ksort(?callable $callback = null): self
{
return new self(Arr::ksort($this->value, $callback));
}
/**
* Merge one or more arrays recursively.
* Don't forget that numeric keys NOT will be renumbered!
*
* @param array ...$arrays
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function merge(array ...$arrays): self
{
return new self(Arr::merge($this->value, ...$arrays));
}
/**
* Combining arrays without preserving keys.
*
* @param ...$arrays
*
* @return $this
*/
public function combine(array ...$arrays): self
{
return new self(Arr::combine($this->value, ...$arrays));
}
/**
* Make the instance as an array recursively.
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function resolve(): self
{
return new self(Arr::resolve($this->value));
}
/**
* Get an item from an array.
*
* @param mixed $key
* @param mixed|null $default
*
* @return mixed
*/
public function get(mixed $key, mixed $default = null): mixed
{
return Arr::get($this->value, $key, $default);
}
/**
* If the element key exists, then return the name of the key, otherwise the default value.
*
* @param mixed $key
* @param mixed|null $default
*
* @return mixed
*/
public function getKey(mixed $key, mixed $default = null): mixed
{
return Arr::getKey($this->value, $key, $default);
}
/**
* Get all of the given array except for a specified array of keys.
*
* @param array|callable|string $keys
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function except(array|callable|string $keys): self
{
return new self(Arr::except($this->value, $keys));
}
/**
* Get a subset of the items from the given array.
*
* @param array|callable|string $keys
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function only(array|callable|string $keys): self
{
return new self(Arr::only($this->value, $keys));
}
/**
* Iterates over each value in the <b>array</b> passing them to the <b>callback</b> function.
* If the <b>callback</b> function returns true, the current value from <b>array</b> is returned into
* the result array. Array keys are preserved.
*
* @see https://php.net/manual/en/function.array-filter.php
*
* @param callable|null $callback
* @param int $mode
*
* @return $this
*/
public function filter(?callable $callback = null, int $mode = 0): self
{
return new self(Arr::filter($this->value, $callback, $mode));
}
/**
* Flatten a multi-dimensional array into a single level.
*
* @param bool $ignore_keys
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function flatten(bool $ignore_keys = true): self
{
return new self(Arr::flatten($this->value, $ignore_keys));
}
public function flattenKeys(string $delimiter = '.', ?string $prefix = null): self
{
return new self(Arr::flattenKeys($this->value, $delimiter, $prefix));
}
/**
* Applies the callback to the elements of the given arrays.
*
* @param callable $callback
* @param bool $recursive
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function map(callable $callback, bool $recursive = false): self
{
return new self(Arr::map($this->value, $callback, $recursive));
}
/**
* Creating a new instance of the given class by passing the value into the constructor.
*
* @param string $class
*
* @return $this
*/
public function mapInto(string $class): self
{
return new self(Arr::mapInto($this->value, $class));
}
/**
* Exchanges all keys with their associated values in an array.
*
* @see https://php.net/manual/en/function.array-flip.php
*
* @return $this
*/
public function flip(): self
{
return new self(Arr::flip($this->value));
}
/**
* Return all the keys or a subset of the keys of an array.
*
* @see https://php.net/manual/en/function.array-keys.php
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function keys(): self
{
return new self(Arr::keys($this->value));
}
/**
* Return all the values of an array.
*
* @see https://php.net/manual/en/function.array-values.php
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function values(): self
{
return new self(Arr::values($this->value));
}
/**
* Push elements onto the end of array.
*
* @see https://php.net/manual/en/function.array-push.php
*
* @param mixed ...$values
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function push(mixed ...$values): self
{
return new self(Arr::push($this->value, ...$values));
}
/**
* Assigns a value to an array key.
*
* @param string|int|float $key
* @param mixed $value
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function set(string|int|float $key, mixed $value = null): self
{
return new self(Arr::set($this->value, $key, $value));
}
/**
* Removes an array key.
*
* @param mixed $key
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function remove(string|int|float $key): self
{
return new self(Arr::remove($this->value, $key));
}
/**
* Call the given Closure with the given value then return the value.
*
* @param callable $callback
*
* @return $this
*/
public function tap(callable $callback): self
{
return new self(Arr::tap($this->value, $callback));
}
/**
* Return an array with elements in reverse order.
*
* @param bool $preserve
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function reverse(bool $preserve = false): self
{
return new self(Arr::reverse($this->value, $preserve));
}
/**
* Remove a portion of the array and replace it with something else.
*
* @see https://php.net/manual/en/function.array-splice.php
*
* @param int $offset
* @param int|null $length
* @param mixed $replacement
*
* @return $this
*/
public function splice(int $offset, ?int $length = null, mixed $replacement = null): self
{
return new self(Arr::splice($this->value, $offset, $length, $replacement));
}
/**
* Get the size of the longest text element of the array.
*
* @return int
*/
public function longestStringLength(): int
{
return Arr::longestStringLength($this->value);
}
/**
* Determine if the given key exists in the provided array.
*
* @param mixed $key
*
* @return bool
*/
public function exists(mixed $key): bool
{
return Arr::exists($this->value, $key);
}
/**
* Determine if the given key doesn't exist in the provided array.
*
* @param mixed $key
*
* @return bool
*/
public function doesntExist(mixed $key): bool
{
return Arr::doesntExist($this->value, $key);
}
/**
* Determine if the given key exists in the provided array without dot divider.
*
* @param mixed $key
*
* @return bool
*/
public function existsWithoutDot(mixed $key): bool
{
return Arr::existsWithoutDot($this->value, $key);
}
/**
* Determine if the given key doesn't exist in the provided array without dot divider.
*
* @param mixed $key
*
* @return bool
*/
public function doesntExistWithoutDot(mixed $key): bool
{
return Arr::doesntExistWithoutDot($this->value, $key);
}
/**
* Determines if the array or arrayable object is empty.
*
* @return bool
*/
public function isEmpty(): bool
{
return Arr::isEmpty($this->value);
}
/**
* Determines if the value is doesn't empty.
*
* @return bool
*/
public function doesntEmpty(): bool
{
return Arr::doesntEmpty($this->value);
}
/**
* Return the first element in an array passing a given truth test.
*
* @param callable|null $callback
* @param mixed|null $default
*
* @return mixed
*/
public function first(?callable $callback = null, mixed $default = null): mixed
{
return Arr::first($this->value, $callback, $default);
}
/**
* Return the last element in an array passing a given truth test.
*
* @param callable|null $callback
* @param mixed|null $default
*
* @return mixed
*/
public function last(?callable $callback = null, mixed $default = null): mixed
{
return Arr::last($this->value, $callback, $default);
}
/**
* Returns the number of array elements.
*
* @return int
*/
public function count(): int
{
return Arr::count($this->value);
}
/**
* Returns an object filled with the value of the array.
*
* @param string $instance
*
* @return mixed
*/
public function toInstance(string $instance): mixed
{
if (method_exists($instance, '__invoke')) {
$instance = new $instance();
call_user_func([$instance, '__invoke'], $this->value);
return $instance;
}
return new $instance($this->value);
}
}

View File

@@ -0,0 +1,551 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2023 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Helpers\Ables;
use DragonCode\Contracts\Support\Stringable as Contract;
use DragonCode\Support\Concerns\Dumpable;
use DragonCode\Support\Facades\Helpers\Arr;
use DragonCode\Support\Facades\Helpers\Str;
use DragonCode\Support\Facades\Instances\Call;
use JetBrains\PhpStorm\Pure;
class Stringable implements Contract
{
use Dumpable;
public function __construct(
protected ?string $value = null
) {
}
public function __toString(): string
{
return $this->toString();
}
public function toString(): string
{
return (string) $this->value;
}
public function of(?string $value = null): self
{
$this->value = (string) $value;
return $this;
}
/**
* Performing an action on a condition.
*
* @param mixed $condition
* @param mixed $callback
* @param mixed $default
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function when(mixed $condition, mixed $callback, mixed $default = null): self
{
if (Call::value($condition, $this)) {
$value = Call::value($callback, $this);
return new self($value);
}
return ! is_null($default) ? $this->when(true, $default) : $this;
}
/**
* Split a string by a string.
*
* @param string $separator
* @param string|null $map_into
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
#[Pure]
public function explode(string $separator, ?string $map_into = null): Arrayable
{
$array = Arr::of(explode($separator, $this->toString()));
return $map_into ? $array->mapInto($map_into) : $array;
}
/**
* Escape HTML special characters in a string.
*
* @param bool $double
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function e(bool $double = true): self
{
return new self(Str::e($this->toString(), $double));
}
/**
* Convert special HTML entities back to characters.
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function de(): self
{
return new self(Str::de($this->toString()));
}
/**
* Replacing multiple spaces with a single space.
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function squish(): self
{
return new self(Str::squish($this->toString()));
}
/**
* Begin a string with a single instance of a given value.
*
* @param string $prefix
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function start(string $prefix): self
{
return new self(Str::start($this->toString(), $prefix));
}
/**
* End a string with a single instance of a given value.
*
* @param string $suffix
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function end(string $suffix): self
{
return new self(Str::end($this->toString(), $suffix));
}
/**
* Adds a substring to the end of a string.
*
* @param mixed $suffix
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function append(mixed $suffix): self
{
return new self(Str::append($this->toString(), $suffix));
}
/**
* Adds a substring to the start of a string.
*
* @param mixed $prefix
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function prepend(mixed $prefix): self
{
return new self(Str::prepend($this->toString(), $prefix));
}
/**
* Cap a string with a single instance of a given value.
*
* @param string $cap
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function finish(string $cap = '/'): self
{
return new self(Str::finish($this->toString(), $cap));
}
/**
* Convert the given string to lower-case.
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function lower(): self
{
return new self(Str::lower($this->toString()));
}
/**
* Convert the given string to upper-case.
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function upper(): self
{
return new self(Str::upper($this->toString()));
}
/**
* Convert a value to studly caps case.
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function studly(): self
{
return new self(Str::studly($this->toString()));
}
/**
* Convert a value to camel case.
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function camel(): self
{
return new self(Str::camel($this->toString()));
}
/**
* Convert a string to snake case.
*
* @param string|null $delimiter
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function snake(?string $delimiter = '_'): self
{
return new self(Str::snake($this->toString(), $delimiter));
}
/**
* Generate a URL friendly "slug" from a given string.
*
* @param string $separator
* @param string|null $language
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function slug(string $separator = '-', ?string $language = 'en'): self
{
return new self(Str::slug($this->toString(), $separator, $language));
}
/**
* Convert the given string to title case.
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function title(): self
{
return new self(Str::title($this->toString()));
}
/**
* Returns the portion of string specified by the start and length parameters.
*
* @param int $start
* @param int|null $length
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function substr(int $start, ?int $length = null): self
{
return new self(Str::substr($this->toString(), $start, $length));
}
/**
* Replace all occurrences of the search string with the replacement string by format.
*
* @param array $values
* @param string|null $key_format
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function replaceFormat(array $values, ?string $key_format = null): self
{
return new self(Str::replaceFormat($this->toString(), $values, $key_format));
}
/**
* Replace all occurrences of the search string with the replacement string.
*
* @param array|string|string[]|int|float $search
* @param array|string|string[]|int|float $replace
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function replace(mixed $search, mixed $replace): self
{
return new self(Str::replace($this->toString(), $search, $replace));
}
/**
* Get the portion of a string before the first occurrence of a given value.
*
* @param string $search
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function before(string $search): self
{
return new self(Str::before($this->toString(), $search));
}
/**
* Return the remainder of a string after the first occurrence of a given value.
*
* @param string $search
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function after(string $search): self
{
return new self(Str::after($this->toString(), $search));
}
/**
* Generate a more truly "random" alpha-numeric string.
*
* @param int $length
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function random(int $length = 16): self
{
return new self(Str::random($length));
}
/**
* Get the string matching the given pattern.
*
* @param string $pattern
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function match(string $pattern): self
{
return new self(Str::match($this->toString(), $pattern));
}
/**
* Get the all string matching the given pattern.
*
* @param string $pattern
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function matchAll(string $pattern): Arrayable
{
return Arr::of(Str::matchAll($this->toString(), $pattern));
}
/**
* Determine if a given string contains a given substring by regex.
*
* @param array|string $pattern
*
* @return bool
*/
public function matchContains(array|string $pattern): bool
{
return Str::matchContains($this->toString(), $pattern);
}
/**
* Replace a given value in the string.
*
* @param string $pattern
* @param string $replacement
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function pregReplace(string $pattern, string $replacement): self
{
return new self(Str::pregReplace($this->toString(), $pattern, $replacement));
}
/**
* Transliterate a UTF-8 value to ASCII.
*
* @param string|null $language
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function ascii(?string $language = 'en'): self
{
return new self(Str::ascii($this->toString(), $language));
}
/**
* Using a call-back function to process a value.
*
* @param callable $callback
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function map(callable $callback): self
{
return new self(Str::map($this->toString(), $callback));
}
/**
* Get the portion of a string between two given values.
*
* @param mixed $from
* @param mixed $to
* @param bool $trim
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
#[Pure]
public function between(mixed $from, mixed $to, bool $trim = true): self
{
return new self(Str::between($this->toString(), $from, $to, $trim));
}
/**
* Strip whitespace (or other characters) from the beginning and end of a string.
*
* @param string $characters
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function trim(string $characters = " \t\n\r\0\x0B"): self
{
return new self(Str::trim($this->toString(), $characters));
}
/**
* Strip whitespace (or other characters) from the beginning of a string.
*
* @param string $characters
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function ltrim(string $characters = " \t\n\r\0\x0B"): self
{
return new self(Str::ltrim($this->toString(), $characters));
}
/**
* Strip whitespace (or other characters) from the end of a string.
*
* @param string $characters
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function rtrim(string $characters = " \t\n\r\0\x0B"): self
{
return new self(Str::rtrim($this->toString(), $characters));
}
/**
* Determine if a given string matches a given pattern.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param array|string $pattern
*
* @return bool
*/
public function is(array|string $pattern): bool
{
return Str::is($pattern, $this->toString());
}
/**
* Determine if a given string starts with a given substring.
*
* @param mixed $needles
*
* @return bool
*/
public function startsWith(mixed $needles): bool
{
return Str::startsWith($this->toString(), $needles);
}
/**
* Determine if a given string ends with a given substring.
*
* @param mixed $needles
*
* @return bool
*/
public function endsWith(mixed $needles): bool
{
return Str::endsWith($this->toString(), $needles);
}
/**
* Return the length of the given string.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $encoding
*
* @return int
*/
public function length(?string $encoding = null): int
{
return Str::length($this->toString(), $encoding);
}
/**
* Count the number of substring occurrences.
*
* @param string $needle
* @param int $offset
*
* @return int
*/
public function count(string $needle, int $offset = 0): int
{
return Str::count($this->value, $needle, $offset);
}
/**
* Determine if a given string contains a given substring.
*
* @param mixed $needles
*
* @return bool
*/
public function contains(mixed $needles): bool
{
return Str::contains($this->toString(), $needles);
}
/**
* Determines if the value is empty.
*
* @return bool
*/
public function isEmpty(): bool
{
return Str::isEmpty($this->value);
}
/**
* Determines if the value is doesn't empty.
*
* @return bool
*/
public function doesntEmpty(): bool
{
return Str::doesntEmpty($this->value);
}
}

View File

@@ -0,0 +1,895 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Helpers;
use ArrayAccess;
use ArrayObject;
use Closure;
use DragonCode\Contracts\Support\Arrayable;
use DragonCode\Support\Facades\Callbacks\Empties;
use DragonCode\Support\Facades\Callbacks\Sorter;
use DragonCode\Support\Facades\Filesystem\File;
use DragonCode\Support\Facades\Instances\Call;
use DragonCode\Support\Facades\Instances\Instance as InstanceHelper;
use DragonCode\Support\Facades\Instances\Reflection as ReflectionHelper;
use DragonCode\Support\Helpers\Ables\Arrayable as ArrayableHelper;
use Illuminate\Contracts\Support\Arrayable as ArrayableIlluminate;
class Arr
{
/**
* Get a new arrayable object from the given array.
*
* @param ArrayObject|array|null $value
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function of(ArrayObject|array|null $value = []): Ables\Arrayable
{
return new Ables\Arrayable($value);
}
/**
* Get a new arrayable object from the given array from the php or json array file.
*
* @param string $path
*
* @return \DragonCode\Support\Helpers\Ables\Arrayable
*/
public function ofFile(string $path): Ables\Arrayable
{
$content = File::load($path);
return $this->of($content);
}
/**
* Renaming array keys.
* As the second parameter, a callback function is passed, which determines the actions for processing the value.
* The output of the function must be a string with a name.
*
* @param ArrayObject|array|null $array $array
* @param callable $callback
*
* @return array
*/
public function renameKeys(ArrayObject|array|null $array, callable $callback): array
{
$result = [];
foreach ($array as $key => $value) {
$new = Call::callback($callback, $key, $value);
$result[$new] = $value;
}
return $result;
}
/**
* Renaming array keys with map.
*
* @param ArrayObject|array|null $array $array
* @param array $map
*
* @return array
*/
public function renameKeysMap(ArrayObject|array|null $array, array $map): array
{
return $this->renameKeys($array, static fn ($key) => $map[$key] ?? $key);
}
/**
* Get the size of the longest text element of the array.
*
* @param ArrayObject|array|null $array $array
*
* @return int
*/
public function longestStringLength(ArrayObject|array|null $array): int
{
return ! empty($array) ? max(array_map('mb_strlen', $array)) : 0;
}
/**
* Push one a unique element onto the end of array.
*
* @param ArrayObject|array $array $array
* @param mixed $values
*
* @return array
*/
public function addUnique(ArrayObject|array $array, mixed $values): array
{
if ($this->isArrayable($values)) {
foreach ($values as $value) {
$array = $this->addUnique($array, $value);
}
}
else {
$array[] = $values;
}
return $this->unique($array);
}
/**
* Removes duplicate values from an array.
*
* Sorting type flags:
* SORT_REGULAR - compare items normally
* SORT_NUMERIC - compare items numerically
* SORT_STRING - compare items as strings
* SORT_LOCALE_STRING - compare items as strings, based on the current locale
*
* @see https://php.net/manual/en/function.array-unique.php
*
* @param ArrayObject|array $array $array
* @param int $flags
*
* @return array
*/
public function unique(ArrayObject|array $array, int $flags = SORT_STRING): array
{
return array_unique($array, $flags);
}
/**
* Sort an associative array in the order specified by an array of keys.
*
* Example:
*
* $arr = ['q' => 1, 'r' => 2, 's' => 5, 'w' => 123];
*
* Arr::sortByKeys($arr, ['q', 'w', 'e']);
*
* print_r($arr);
*
* Array
* (
* [q] => 1
* [w] => 123
* [r] => 2
* [s] => 5
* )
*
* @see https://gist.github.com/Ellrion/a3145621f936aa9416f4c04987533d8d#file-helper-php
*
* @param ArrayObject|array $array $array
* @param array $sorter
*
* @return array
*/
public function sortByKeys(ArrayObject|array $array, array $sorter): array
{
$sorter = array_intersect($sorter, array_keys($array));
return array_merge(array_flip($sorter), $array);
}
/**
* Recursively sorting an array by values.
*
* @param ArrayObject|array $array $array
* @param callable|null $callback
*
* @return array
*/
public function sort(ArrayObject|array $array, ?callable $callback = null): array
{
$callback = $callback ?: Sorter::default();
usort($array, $callback);
foreach ($array as &$value) {
if (is_array($value)) {
$value = $this->sort($value, $callback);
}
}
return $array;
}
/**
* Recursively sorting an array by keys.
*
* @param ArrayObject|array $array $array
* @param callable|null $callback
*
* @return array
*/
public function ksort(ArrayObject|array $array, ?callable $callback = null): array
{
$callback = $callback ?: Sorter::default();
uksort($array, $callback);
foreach ($array as &$value) {
if (is_array($value)) {
$value = $this->ksort($value, $callback);
}
}
return $array;
}
/**
* Merge one or more arrays recursively.
* Don't forget that numeric keys NOT will be renumbered!
*
* @param array[] ...$arrays
*
* @return array
*/
public function merge(ArrayObject|array ...$arrays): array
{
$result = [];
foreach ($arrays as $array) {
foreach ($array as $key => $value) {
if (is_array($value)) {
$prev_value = $result[$key] ?? [];
if (gettype($prev_value) !== 'array') {
$prev_value = [];
}
$value = $this->merge($prev_value, $value);
}
$result[$key] = $value;
}
}
return $result;
}
/**
* Combining arrays without preserving keys.
*
* @param array ...$arrays
*
* @return array
*/
public function combine(ArrayObject|array ...$arrays): array
{
$result = [];
foreach ($arrays as $array) {
foreach ($array as $key => $value) {
if (is_array($value) && (! is_numeric($key) || ! isset($result[$key]))) {
$prev_value = $result[$key] ?? [];
if (gettype($prev_value) !== 'array') {
$prev_value = [];
}
$result[$key] = $this->combine($prev_value, $value);
continue;
}
if (is_array($value)) {
$value = $this->combine($value);
}
$result[] = $value;
}
}
return $result;
}
/**
* If the given value is not an array and not null, wrap it in one.
*
* @param mixed $value
*
* @return array
*/
public function wrap(mixed $value = null): array
{
if (is_array($value)) {
return $value;
}
return ! empty($value) ? [$value] : [];
}
/**
* Get the instance as an array.
*
* @param mixed $value
*
* @return array
*/
public function resolve(mixed $value): array
{
if (InstanceHelper::of($value, [ArrayObject::class, ArrayableHelper::class])) {
$value = Call::runMethods($value, ['getArrayCopy', 'resolve', 'toArray']);
}
if (is_object($value)) {
$value = method_exists($value, 'toArray') ? $value->toArray() : get_object_vars($value);
}
$array = $this->wrap($value);
foreach ($array as &$item) {
$item = $this->isArrayable($item) ? $this->resolve($item) : $item;
}
return $array;
}
/**
* Determine if the given key exists in the provided array.
*
* @param ArrayAccess|\DragonCode\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Arrayable|array $array |\ArrayAccess $array
* @param mixed $key
*
* @return bool
*/
public function exists(mixed $array, mixed $key): bool
{
if ($this->existsWithoutDot($array, $key)) {
return true;
}
if (! str_contains($key, '.')) {
return $this->existsWithoutDot($array, $key);
}
foreach (explode('.', $key) as $segment) {
if ($this->isArrayable($array) && $this->exists($array, $segment)) {
$array = $array[$segment];
}
else {
return false;
}
}
return true;
}
/**
* Determine if the given key doesn't exist in the provided array.
*
* @param ArrayAccess|\DragonCode\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Arrayable|array $array
* @param mixed $key
*
* @return bool
*/
public function doesntExist(mixed $array, mixed $key): bool
{
return ! $this->exists($array, $key);
}
/**
* Determine if the given key exists in the provided array without dot divider.
*
* @param ArrayAccess|\DragonCode\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Arrayable|array $array |\ArrayAccess $array
* @param mixed $key
*
* @return bool
*/
public function existsWithoutDot(mixed $array, mixed $key): bool
{
if ($array instanceof ArrayAccess) {
return $array->offsetExists($key);
}
return array_key_exists($key, $array);
}
/**
* Determine if the given key doesn't exist in the provided array without dot divider.
*
* @param ArrayAccess|\DragonCode\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Arrayable|array $array |\ArrayAccess $array
* @param mixed $key
*
* @return bool
*/
public function doesntExistWithoutDot(mixed $array, mixed $key): bool
{
return ! $this->existsWithoutDot($array, $key);
}
/**
* Get an item from an array.
*
* @see https://github.com/illuminate/collections/blob/master/Arr.php
*
* @param ArrayAccess|\DragonCode\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Arrayable|array $array |ArrayAccess $array
* @param mixed $key
* @param mixed|null $default
*
* @return mixed|null
*/
public function get(mixed $array, mixed $key, mixed $default = null): mixed
{
if (! $this->isArrayable($array)) {
return Call::value($default);
}
if (is_null($key)) {
return $array;
}
if ($this->existsWithoutDot($array, $key)) {
return $array[$key];
}
if (! str_contains((string) $key, '.')) {
return $array[$key] ?? Call::value($default);
}
foreach (explode('.', $key) as $segment) {
if ($this->isArrayable($array) && $this->existsWithoutDot($array, $segment)) {
$array = $array[$segment];
}
else {
return Call::value($default);
}
}
return $array;
}
/**
* If the element key exists, then return the name of the key, otherwise the default value.
*
* @param array|ArrayAccess $array
* @param mixed $key
* @param mixed $default
*
* @return mixed|null
*/
public function getKey(mixed $array, mixed $key, mixed $default = null): mixed
{
return $this->exists($array, $key) ? $key : $default;
}
/**
* Get all of the given array except for a specified array of keys.
*
* @param array|ArrayAccess $array
* @param array|callable|string $keys
*
* @return array
*/
public function except(mixed $array, mixed $keys): array
{
$callback = is_callable($keys)
? $keys
: static fn ($key) => empty($keys) || ! in_array($key, (array) $keys);
return $this->filter((array) $array, $callback, ARRAY_FILTER_USE_KEY);
}
/**
* Get a subset of the items from the given array.
*
* @param array|ArrayAccess $array
* @param array|callable|string $keys
*
* @return array
*/
public function only(mixed $array, mixed $keys): array
{
if (is_callable($keys)) {
return $this->filter($array, $keys, ARRAY_FILTER_USE_KEY);
}
$result = [];
foreach ((array) $keys as $index => $key) {
if (is_array($key) && isset($array[$index])) {
$result[$index] = $this->only($array[$index], $key);
}
elseif (is_array($key) && ! isset($array[$index])) {
continue;
}
elseif (isset($array[$key])) {
$result[$key] = $array[$key];
}
}
return $result;
}
/**
* Iterates over each value in the <b>array</b> passing them to the <b>callback</b> function.
* If the <b>callback</b> function returns true, the current value from <b>array</b> is returned into
* the result array. Array keys are preserved.
*
* @see https://php.net/manual/en/function.array-filter.php
*
* @param array|ArrayAccess $array
* @param callable|null $callback
* @param int $mode
*
* @return array
*/
public function filter(mixed $array, ?callable $callback = null, int $mode = 0): array
{
if (empty($callback)) {
$callback = $mode === ARRAY_FILTER_USE_BOTH
? Empties::notEmptyBoth()
: Empties::notEmpty();
}
return array_filter($array, $callback, $mode);
}
/**
* Return all the keys or a subset of the keys of an array.
*
* @see https://php.net/manual/en/function.array-keys.php
*
* @param mixed $array
*
* @return array
*/
public function keys(mixed $array): array
{
return array_keys($this->resolve($array));
}
/**
* Return all the values of an array.
*
* @see https://php.net/manual/en/function.array-values.php
*
* @param mixed $array
*
* @return array
*/
public function values(mixed $array): array
{
return array_values($this->resolve($array));
}
/**
* Exchanges all keys with their associated values in an array.
*
* @see https://php.net/manual/en/function.array-flip.php
*
* @param mixed $array
*
* @return array
*/
public function flip(mixed $array): array
{
return array_flip($this->resolve($array));
}
/**
* Flatten a multi-dimensional array into a single level.
*
* @param array $array
* @param bool $ignore_keys
*
* @return array
*/
public function flatten(mixed $array, bool $ignore_keys = true): array
{
$result = [];
foreach ($array as $key => $item) {
if (! $this->isArrayable($item)) {
$result = $ignore_keys
? $this->push($result, $item)
: $this->set($result, $key, $item);
continue;
}
$flatten = $this->flatten($item, $ignore_keys);
$values = $ignore_keys ? array_values($flatten) : $flatten;
$result = array_merge($result, $values);
}
return $ignore_keys ? array_values($result) : $result;
}
public function flattenKeys(mixed $array, string $delimiter = '.', ?string $prefix = null): array
{
$result = [];
foreach ($array as $key => $value) {
$new_key = ! empty($prefix) ? $prefix . $delimiter . $key : $key;
if (is_array($value)) {
$values = $this->flattenKeys($value, $delimiter, $new_key);
$result = array_merge($result, $values);
continue;
}
$result[$new_key] = $value;
}
return $result;
}
/**
* Applies the callback to the elements of the given arrays.
*
* @param array|ArrayAccess $array
* @param callable $callback
* @param bool $recursive
*
* @return array
*/
public function map(ArrayObject|array $array, callable $callback, bool $recursive = false): array
{
foreach ($array as $key => &$value) {
if ($recursive && is_array($value)) {
$value = $this->map($value, $callback, $recursive);
}
else {
$value = Call::callback($callback, $value, $key);
}
}
return $array;
}
/**
* Creating a new instance of the given class by passing the value into the constructor.
*
* @param array $array
* @param string $class
*
* @return array
*/
public function mapInto(ArrayObject|array $array, string $class): array
{
foreach ($array as &$value) {
$value = new $class($value);
}
return $array;
}
/**
* Push elements onto the end of array.
*
* @see https://php.net/manual/en/function.array-push.php
*
* @param array|ArrayAccess $array
* @param mixed ...$values
*
* @return array
*/
public function push(mixed $array, mixed ...$values): array
{
foreach ($values as $value) {
$array[] = $value;
}
return $array;
}
/**
* Assigns a value to an array key.
*
* @param array|ArrayAccess $array
* @param mixed $key
* @param mixed $value
*
* @return array
*/
public function set(mixed $array, mixed $key, mixed $value = null): array
{
if ($this->isArrayable($key)) {
$array = $this->merge($array, $key);
}
else {
$array[$key] = $value;
}
return $array;
}
/**
* Removes an array key.
*
* @param array|ArrayAccess $array
* @param mixed $key
*
* @return array
*/
public function remove(mixed $array, string|int|float $key): array
{
unset($array[$key]);
return $array;
}
/**
* Call the given Closure with the given value then return the value.
*
* @param array|ArrayAccess $array
* @param callable $callback
*
* @return array
*/
public function tap(mixed $array, callable $callback): array
{
foreach ($array as $key => $value) {
Call::callback($callback, $value, $key);
}
return $array;
}
/**
* Check if the item is an array.
*
* @param mixed $value
*
* @return bool
*/
public function isArrayable(mixed $value = null): bool
{
if (is_array($value) || is_object($value)) {
return true;
}
if (
is_string($value)
&& method_exists($value, 'toArray')
&& ! ReflectionHelper::isStaticMethod($value, 'toArray')
) {
return false;
}
if (
InstanceHelper::of($value, [
Arrayable::class,
ArrayableIlluminate::class,
ArrayableHelper::class,
ArrayObject::class,
ArrayAccess::class,
Arrayable::class,
])
) {
return true;
}
return InstanceHelper::of($value, Closure::class) && method_exists($value, 'toArray');
}
/**
* Determines if the array or arrayable object is empty.
*
* @param mixed $value
*
* @return bool
*/
public function isEmpty(mixed $value): bool
{
$value = is_object($value) && method_exists($value, 'toArray') ? $value->toArray() : $value;
$value = is_object($value) ? (array) $value : $value;
return is_array($value) && empty($value);
}
/**
* Determines if the value is doesn't empty.
*
* @param mixed $value
*
* @return bool
*/
public function doesntEmpty(mixed $value): bool
{
return ! $this->isEmpty($value);
}
/**
* Return an array with elements in reverse order.
*
* @param array $array
* @param bool $preserve_keys
*
* @return array
*/
public function reverse(ArrayObject|array $array, bool $preserve_keys = false): array
{
return array_reverse($array, $preserve_keys);
}
/**
* Return the first element in an array passing a given truth test.
*
* @param array $array
* @param callable|null $callback
* @param mixed|null $default
*
* @return mixed
*/
public function first(ArrayObject|array $array, ?callable $callback = null, mixed $default = null): mixed
{
if (is_null($callback)) {
return empty($array) ? Call::value($default) : reset($array);
}
foreach ($array as $key => $value) {
if (Call::callback($callback, $value, $key)) {
return $value;
}
}
return Call::value($default);
}
/**
* Return the last element in an array passing a given truth test.
*
* @param array $array
* @param callable|null $callback
* @param mixed|null $default
*
* @return mixed
*/
public function last(ArrayObject|array $array, ?callable $callback = null, mixed $default = null): mixed
{
if (is_null($callback)) {
return empty($array) ? Call::value($default) : end($array);
}
return $this->first(array_reverse($array, true), $callback, $default);
}
/**
* Remove a portion of the array and replace it with something else.
*
* @see https://php.net/manual/en/function.array-splice.php
*
* @param array $array
* @param int $offset
* @param int|null $length
* @param mixed $replacement
*
* @return array
*/
public function splice(ArrayObject|array $array, int $offset, ?int $length = null, mixed $replacement = null): array
{
array_splice($array, $offset, $length, $replacement);
return $array;
}
/**
* Returns the number of array elements.
*
* @param ArrayObject|array $array
*
* @return int
*/
public function count(ArrayObject|array $array): int
{
return InstanceHelper::of($array, ArrayObject::class) ? $array->count() : count($array);
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Helpers;
use JetBrains\PhpStorm\Pure;
class Boolean
{
/**
* Determines if the value is `true`, otherwise it will return `false`.
*
* @param mixed $value
*
* @return bool
*/
public function isTrue(mixed $value): bool
{
return $this->to($value) === true;
}
/**
* Determines if the value is `false`, otherwise it will return `true`.
*
* @param mixed $value
*
* @return bool
*/
public function isFalse(mixed $value): bool
{
return $this->to($value) === false;
}
/**
* Converts a value to a boolean type.
*
* @param mixed $value
*
* @return bool
*/
#[Pure]
public function to(mixed $value): bool
{
return (bool) $this->parse($value);
}
/**
* Getting a filtered value in a boolean view.
*
* @param mixed $value
*
* @return bool|null
*/
public function parse(mixed $value): ?bool
{
if (is_null($value)) {
return null;
}
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
}
/**
* Converts a boolean value to a string.
*
* @param bool $value
*
* @return string
*/
public function toString(bool $value): string
{
return $value ? 'true' : 'false';
}
}

View File

@@ -0,0 +1,128 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Helpers;
use DragonCode\Support\Facades\Helpers\Arr;
use DragonCode\Support\Facades\Helpers\Str as StrHelper;
class Digit
{
/**
* Calculating the factorial of a number.
*
* @param int $n
*
* @return int
*/
public function factorial(int $n = 0): int
{
if ($n === 0) {
return 1;
}
return $n * $this->factorial($n - 1);
}
/**
* Converts a number into a short version.
* eg: 1000 >> 1K.
*
* @param float $number
* @param int $precision
* @param string|null $suffix
*
* @return string
*/
public function toShort(float $number, int $precision = 1, ?string $suffix = null): string
{
$length = strlen((string) ((int) $number));
$length = ceil($length / 3) * 3 + 1;
$suffix = $this->suffix($length, $suffix);
$value = $this->rounded($number, $length, $precision);
return $value . $suffix;
}
/**
* Create short unique identifier from number.
* Actually using in short URL.
*
* @param int $number
* @param string $chars
*
* @return string
*/
public function toChars(int $number, string $chars = 'abcdefghijklmnopqrstuvwxyz'): string
{
$length = StrHelper::length($chars);
$mod = $number % $length;
while ($mod > 0 || $number > 0) {
$result = StrHelper::of($chars)->substr($mod, 1)->append($result ?? '');
$number = ($number - $mod) / $length;
$mod = $number % $length;
}
return $result ?? StrHelper::substr($chars, $number, 1);
}
/**
* Format a number with grouped with divider.
*
* @param float|int $number
* @param int $length
* @param int $precision
*
* @return float
*/
public function rounded(float|int $number, int $length = 4, int $precision = 1): float
{
$divided = (float) bcpow(10, $length - 4, 2);
return round($number / $divided, $precision);
}
/**
* Converts a numeric value to a string.
*
* @param float|int $value
*
* @return string
*/
public function toString(float|int $value): string
{
return (string) $value;
}
protected function suffix(int $length = 0, ?string $suffix = null): string
{
$available = [
4 => '' . $suffix,
7 => 'K' . $suffix,
10 => 'M' . $suffix,
13 => 'B' . $suffix,
16 => 'T' . $suffix . '+',
];
return $available[$length] ?? Arr::last($available);
}
}

View File

@@ -0,0 +1,802 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2023 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Helpers;
use DragonCode\Support\Facades\Helpers\Arr as ArrHelper;
use DragonCode\Support\Facades\Instances\Call;
use DragonCode\Support\Facades\Instances\Call as CallHelper;
use DragonCode\Support\Facades\Tools\Replace;
use Exception;
use Illuminate\Contracts\Support\DeferringDisplayableValue;
use Illuminate\Contracts\Support\Htmlable;
use voku\helper\ASCII;
class Str
{
/**
* The cache of snake-cased words.
*
* @var array
*/
protected static array $snakeCache = [];
/**
* The cache of camel-cased words.
*
* @var array
*/
protected static array $camelCache = [];
/**
* The cache of studly-cased words.
*
* @var array
*/
protected static array $studlyCache = [];
protected array $escaping_methods = [
DeferringDisplayableValue::class => 'resolveDisplayableValue',
Htmlable::class => 'toHtml',
];
/**
* Get a new stringable object from the given string.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $value
*
* @return \DragonCode\Support\Helpers\Ables\Stringable
*/
public function of(?string $value): Ables\Stringable
{
return new Ables\Stringable($value);
}
/**
* Escape HTML special characters in a string.
*
* @param string|null $value
* @param bool $double
*
* @return string|null
*/
public function e(?string $value, bool $double = true): ?string
{
if ($escaped = CallHelper::runOf($this->escaping_methods, $value)) {
return $escaped;
}
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', $double);
}
/**
* Convert special HTML entities back to characters.
*
* @param string|null $value
*
* @return string|null
*/
public function de(?string $value): ?string
{
return htmlspecialchars_decode($value, ENT_QUOTES);
}
/**
* Replacing multiple spaces with a single space.
*
* @see https://laravel.com/docs/9.x/helpers#method-str-squish
*
* @param string|null $value
*
* @return string|null
*/
public function squish(?string $value): ?string
{
$value = $this->pregReplace($value, '~^[\s\x{FEFF}]+|[\s\x{FEFF}]+$~u', '');
return $this->pregReplace($value, '~(\s|\x{3164}|\x{1160})+~u', ' ');
}
/**
* Get a string according to an integer value.
*
* @param float $number
* @param array $choice
* @param string|null $extra
*
* @return string
*/
public function choice(float $number, array $choice = [], ?string $extra = null): string
{
$number = (int) $number;
$mod = $number % 10;
switch (true) {
case $mod === 0:
case $mod >= 5 && $mod <= 9:
case ($number % 100 >= 11) && ($number % 100 <= 20):
$result = $choice[2] ?? '';
break;
case $mod >= 2 && $mod <= 4:
$result = $choice[1] ?? '';
break;
default:
$result = $choice[0] ?? '';
}
if (empty($extra)) {
return trim($result);
}
return implode(' ', [trim($result), trim($extra)]);
}
/**
* Begin a string with a single instance of a given value.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $value
* @param string $prefix
*
* @return string
*/
public function start(?string $value, string $prefix): string
{
$quoted = preg_quote($prefix, '/');
return $prefix . preg_replace('/^(?:' . $quoted . ')+/u', '', $value);
}
/**
* End a string with a single instance of a given value.
*
* @param string|null $value
* @param string $suffix
*
* @return string
*/
public function end(?string $value, string $suffix): string
{
$quoted = preg_quote($suffix, '/');
return preg_replace('/^(?:' . $quoted . ')+/u', '', $value) . $suffix;
}
/**
* Adds a substring to the end of a string.
*
* @param mixed $value
* @param string $suffix
*
* @return string
*/
public function append(mixed $value, string $suffix): string
{
return $value . $suffix;
}
/**
* Adds a substring to the start of a string.
*
* @param mixed $value
* @param string $prefix
*
* @return string
*/
public function prepend(mixed $value, string $prefix): string
{
return $prefix . $value;
}
/**
* Cap a string with a single instance of a given value.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string $value
* @param string $cap
*
* @return string
*/
public function finish(string $value, string $cap = '/'): string
{
$quoted = preg_quote($cap, '/');
return preg_replace('/(?:' . $quoted . ')+$/u', '', $value) . $cap;
}
/**
* Determine if a given string matches a given pattern.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|array $pattern
* @param mixed $value
*
* @return bool
*/
public function is(array|string $pattern, mixed $value): bool
{
$patterns = ArrHelper::wrap($pattern);
$value = (string) $value;
if (empty($patterns)) {
return false;
}
foreach ($patterns as $pattern) {
$pattern = (string) $pattern;
// If the given value is an exact match we can of course return true right
// from the beginning. Otherwise, we will translate asterisks and do an
// actual pattern match against the two strings to see if they match.
if ($pattern == $value) {
return true;
}
$pattern = preg_quote($pattern, '#');
// Asterisks are translated into zero-or-more regular expression wildcards
// to make it convenient to check if the strings starts with the given
// pattern such as "library/*", making any string check convenient.
$pattern = str_replace('\*', '.*', $pattern);
if (preg_match('#^' . $pattern . '\z#u', $value) === 1) {
return true;
}
}
return false;
}
/**
* Determine if a given string starts with a given substring.
*
* @param string $haystack
* @param string|string[] $needles
*
* @return bool
*/
public function startsWith(string $haystack, mixed $needles): bool
{
foreach ((array) $needles as $needle) {
if ((string) $needle !== '' && str_starts_with($haystack, $needle)) {
return true;
}
}
return false;
}
/**
* Determine if a given string ends with a given substring.
*
* @param string $haystack
* @param string|string[] $needles
*
* @return bool
*/
public function endsWith(string $haystack, mixed $needles): bool
{
foreach ((array) $needles as $needle) {
if ((string) $needle !== '' && str_ends_with($haystack, $needle)) {
return true;
}
}
return false;
}
/**
* Convert the given string to lower-case.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $value
*
* @return string
*/
public function lower(?string $value): string
{
return mb_strtolower($value, 'UTF-8');
}
/**
* Convert the given string to upper-case.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $value
*
* @return string
*/
public function upper(?string $value): ?string
{
return mb_strtoupper($value, 'UTF-8');
}
/**
* Convert a value to studly caps case.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $value
*
* @return string|null
*/
public function studly(?string $value): ?string
{
$key = $value;
if (isset(self::$studlyCache[$key])) {
return self::$studlyCache[$key];
}
$value = ucwords(str_replace(['-', '_'], ' ', $value));
return self::$studlyCache[$key] = str_replace(' ', '', $value);
}
/**
* Convert a value to camel case.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $value
*
* @return string|null
*/
public function camel(?string $value): ?string
{
if (isset(self::$camelCache[$value])) {
return self::$camelCache[$value];
}
return self::$camelCache[$value] = lcfirst($this->studly($value));
}
/**
* Convert a string to snake case.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $value
* @param string $delimiter
*
* @return string|null
*/
public function snake(?string $value, string $delimiter = '_'): ?string
{
$key = $value;
if (isset(self::$snakeCache[$key][$delimiter])) {
return self::$snakeCache[$key][$delimiter];
}
if (! ctype_lower($value)) {
$value = preg_replace('/\s+/u', '', ucwords($value));
$value = $this->lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
}
return self::$snakeCache[$key][$delimiter] = $value;
}
/**
* Generate a URL friendly "slug" from a given string.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string $title
* @param string $separator
* @param string|null $language
*
* @return string
*/
public function slug(string $title, string $separator = '-', ?string $language = 'en'): string
{
$title = $language ? $this->ascii($title, $language) : $title;
// Convert all dashes/underscores into separator
$flip = $separator === '-' ? '_' : '-';
$title = preg_replace('![' . preg_quote($flip) . ']+!u', $separator, $title);
// Replace @ with the word 'at'
$title = str_replace('@', $separator . 'at' . $separator, $title);
// Remove all characters that are not the separator, letters, numbers, or whitespace.
$title = preg_replace('![^' . preg_quote($separator) . '\pL\pN\s]+!u', '', $this->lower($title));
// Replace all separator characters and whitespace by a single separator
$title = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $title);
return trim($title, $separator);
}
/**
* Convert the given string to title case.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $value
*
* @return string|null
*/
public function title(?string $value): ?string
{
if (is_numeric($value)) {
return $value;
}
return mb_convert_case((string) $value, MB_CASE_TITLE, 'UTF-8') ?: null;
}
/**
* Return the length of the given string.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $value
* @param string|null $encoding
*
* @return int
*/
public function length(?string $value, ?string $encoding = null): int
{
return $encoding
? mb_strlen($value, $encoding)
: mb_strlen($value);
}
/**
* Count the number of substring occurrences.
*
* @param string|null $value
* @param string $needle
* @param int $offset
*
* @return int
*/
public function count(?string $value, string $needle, int $offset = 0): int
{
return substr_count((string) $value, $needle, $offset);
}
/**
* Returns the portion of string specified by the start and length parameters.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string $string
* @param int $start
* @param int|null $length
*
* @return string|null
*/
public function substr(string $string, int $start, ?int $length = null): ?string
{
return mb_substr($string, $start, $length, 'UTF-8');
}
/**
* Replace all occurrences of the search string with the replacement string by format.
*
* @param string $template
* @param array $values
* @param string|null $key_format
*
* @return string
*/
public function replaceFormat(string $template, array $values, ?string $key_format = null): string
{
$keys = Replace::toFormatArray(array_keys($values), $key_format);
return $this->replace($template, $keys, array_values($values));
}
/**
* Replace all occurrences of the search string with the replacement string.
*
* @param string|null $value
* @param array|string|string[]|int|float $search
* @param array|string|string[]|int|float|null $replace
*
* @return string
*/
public function replace(?string $value, mixed $search, mixed $replace = null): string
{
if (is_null($replace) && is_array($search)) {
$replace = array_values($search);
$search = array_keys($search);
}
return str_replace($search, $replace, (string) $value);
}
/**
* Get the portion of a string before the first occurrence of a given value.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string $subject
* @param string $search
*
* @return string
*/
public function before(string $subject, string $search): ?string
{
return ! empty($search) ? explode($search, $subject)[0] : null;
}
/**
* Return the remainder of a string after the first occurrence of a given value.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string $subject
* @param string $search
*
* @return string
*/
public function after(string $subject, string $search): ?string
{
return ! empty($search) ? array_reverse(explode($search, $subject, 2))[0] : null;
}
/**
* Determine if a given string contains a given substring.
*
* @param string $haystack
* @param string|string[] $needles
*
* @return bool
*/
public function contains(string $haystack, mixed $needles): bool
{
foreach ((array) $needles as $needle) {
if ((string) $needle !== '' && str_contains($haystack, $needle)) {
return true;
}
}
return false;
}
/**
* Generate a more truly "random" alpha-numeric string.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param int $length
*
* @throws Exception
*
* @return string
*/
public function random(int $length = 16): string
{
$string = '';
while (($len = strlen($string)) < $length) {
$size = $length - $len;
$bytes = random_bytes($size);
$string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
}
return $string;
}
/**
* Get the string matching the given pattern.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string $value
* @param string $pattern
*
* @return string|null
*/
public function match(string $value, string $pattern): ?string
{
preg_match($pattern, $value, $matches);
return ! $matches ? null : ($matches[1] ?? $matches[0]);
}
/**
* Get the all string matching the given pattern.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string $value
* @param string $pattern
*
* @return array|null
*/
public function matchAll(string $value, string $pattern): ?array
{
preg_match_all($pattern, $value, $matches);
return empty($matches[0]) ? null : ($matches[1] ?? $matches[0]);
}
/**
* Determine if a given string contains a given substring by regex.
*
* @param string $value
* @param array|string $pattern
*
* @return bool
*/
public function matchContains(string $value, array|string $pattern): bool
{
foreach ((array) $pattern as $item) {
if ($this->match($value, $item) !== null) {
return true;
}
}
return false;
}
/**
* Replace a given value in the string.
*
* @param string|null $value
* @param string $pattern
* @param string $replacement
*
* @return string|null
*/
public function pregReplace(?string $value, string $pattern, string $replacement): ?string
{
return preg_replace($pattern, $replacement, (string) $value);
}
/**
* Determines if the value is empty.
*
* @param mixed $value
*
* @return bool
*/
public function isEmpty(mixed $value): bool
{
$value = is_string($value) ? trim($value) : $value;
return empty($value) && ! is_numeric($value) && (is_string($value) || is_null($value));
}
/**
* Determines if the value is doesn't empty.
*
* @param mixed $value
*
* @return bool
*/
public function doesntEmpty(mixed $value): bool
{
return ! $this->isEmpty($value);
}
/**
* Transliterate a UTF-8 value to ASCII.
*
* @see https://github.com/illuminate/support/blob/master/Str.php
*
* @param string|null $value
* @param string|null $language
*
* @return string
*/
public function ascii(?string $value, ?string $language = 'en'): string
{
return ASCII::to_ascii((string) $value, $language);
}
/**
* Converts a value to a string.
*
* @param string|null $value
*
* @return string
*/
public function toString(?string $value): string
{
return is_null($value) ? 'null' : $value;
}
/**
* Using a call-back function to process a value.
*
* @param string|null $value
* @param callable $callback
*
* @return string|null
*/
public function map(?string $value, callable $callback): ?string
{
return Call::callback($callback, $value);
}
/**
* Get the portion of a string between two given values.
*
* @param string|null $value
* @param mixed $from
* @param mixed $to
* @param bool $trim
*
* @return string
*/
public function between(?string $value, mixed $from, mixed $to, bool $trim = true): string
{
return $this->of($value)
->before($to)
->after($from)
->when($trim, fn ($value) => $this->trim($value))
->toString();
}
/**
* Strip whitespace (or other characters) from the beginning and end of a string.
*
* @param string|null $string
* @param string $characters
*
* @return string
*/
public function trim(?string $string, string $characters = " \t\n\r\0\x0B"): string
{
return trim((string) $string, $characters);
}
/**
* Strip whitespace (or other characters) from the beginning of a string.
*
* @param string|null $string
* @param string $characters
*
* @return string
*/
public function ltrim(?string $string, string $characters = " \t\n\r\0\x0B"): string
{
return ltrim((string) $string, $characters);
}
/**
* Strip whitespace (or other characters) from the end of a string.
*
* @param string|null $string
* @param string $characters
*
* @return string
*/
public function rtrim(?string $string, string $characters = " \t\n\r\0\x0B"): string
{
return rtrim((string) $string, $characters);
}
}

View File

@@ -0,0 +1,624 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Http;
use DragonCode\Contracts\Http\Builder as BuilderContract;
use DragonCode\Support\Concerns\Castable;
use DragonCode\Support\Concerns\Validation;
use DragonCode\Support\Exceptions\UnknownUrlComponentIndexException;
use DragonCode\Support\Facades\Helpers\Arr;
use DragonCode\Support\Facades\Helpers\Str;
use DragonCode\Support\Facades\Http\Url as UrlHelper;
use DragonCode\Support\Helpers\Ables\Stringable;
use JetBrains\PhpStorm\Pure;
use Psr\Http\Message\UriInterface;
class Builder implements BuilderContract
{
use Castable;
use Validation;
protected array $parsed = [];
protected array $components = [
PHP_URL_SCHEME => 'scheme',
PHP_URL_HOST => 'host',
PHP_URL_PORT => 'port',
PHP_URL_USER => 'user',
PHP_URL_PASS => 'pass',
PHP_URL_QUERY => 'query',
PHP_URL_PATH => 'path',
PHP_URL_FRAGMENT => 'fragment',
];
protected array $casts = [
'query' => 'array',
'port' => 'integer',
];
protected array $validate = [
PHP_URL_SCHEME => ['null', 'string'],
PHP_URL_HOST => ['null', 'string'],
PHP_URL_PORT => ['null', 'integer'],
PHP_URL_USER => ['null', 'string'],
PHP_URL_PASS => ['null', 'string'],
PHP_URL_QUERY => ['null', 'string', 'array'],
PHP_URL_PATH => ['null', 'string'],
PHP_URL_FRAGMENT => ['null', 'string'],
];
/**
* Return the string representation as a URI reference.
*
* @return string
*/
public function __toString(): string
{
return $this->toUrl();
}
/**
* Gets the current instance of the object.
*
* @return \DragonCode\Support\Http\Builder
*/
public function same(): self
{
return $this;
}
/**
* Parse a URL.
*
* @param \Psr\Http\Message\UriInterface|string|null $url
* @param int $component
*
* @return \DragonCode\Support\Http\Builder
*/
public function parse(mixed $url, int $component = self::PHP_URL_ALL): BuilderContract
{
if ($component === self::PHP_URL_ALL) {
UrlHelper::validate($url);
}
$instance = $this->resolveSame($component);
$key = $this->componentNameByIndex($component);
return $component === self::PHP_URL_ALL || empty($key)
? $instance->parsed(parse_url($url))
: $instance->parsed([$key => parse_url($url, $component)]);
}
/**
* Populate an object with parsed data.
*
* @param array $parsed
*
* @return \DragonCode\Support\Http\Builder
*/
public function parsed(array $parsed): BuilderContract
{
$components = array_values($this->components);
$filtered = Arr::only($parsed, $components);
$this->parsed = Arr::of($this->parsed)
->merge($filtered)
->toArray();
$this->cast($this->parsed);
return $this;
}
/**
* Retrieve the domain name of the URI.
*
* @return string
*/
public function getDomain(): string
{
return $this->getHost();
}
/**
* Retrieve the domain level name of the URI.
*
* @param int $level
*
* @return string
*/
public function getDomainLevel(int $level = 0): string
{
return Str::of($this->getHost())
->explode('.')
->reverse()
->get($level - 1, '');
}
/**
* Retrieve the base domain name of the URI.
*
* @return string
*/
public function getBaseDomain(): string
{
$first = $this->getDomainLevel(1);
$second = $this->getDomainLevel(2);
if ($first && $second) {
return $second . '.' . $first;
}
return '';
}
/**
* Retrieve the subdomain name of the URI.
*
* @return string
*/
public function getSubDomain(): string
{
return Str::of($this->getHost())
->when(
fn ($host) => Str::count($host, '.') > 1,
fn (Stringable $string) => $string
->explode('.')
->splice(-2)
->implode('.')
)->toString();
}
/**
* Retrieve the scheme and host of the URI.
*
* @return string
*/
public function getBaseUrl(): string
{
return Str::of('://')
->prepend($this->getScheme())
->append($this->getHost())
->trim('://')
->toString();
}
/**
* Retrieve the scheme component of the URI.
*
* @return string
*/
public function getScheme(): string
{
return (string) $this->get(PHP_URL_SCHEME);
}
/**
* Retrieve the authority component of the URI.
*
* @return string
*/
public function getAuthority(): string
{
$auth = $this->getUserInfo();
$host = $this->getHost();
$port = $this->getPort();
return (string) Str::of("$auth@$host:$port")->trim('@:');
}
/**
* Retrieve the user information component of the URI.
*
* @return string
*/
public function getUserInfo(): string
{
$user = $this->getUser();
$pass = $this->getPassword();
return (string) Str::of("$user:$pass")->trim(':');
}
/**
* Retrieve the user name component of the URI.
*
* @return string
*/
public function getUser(): string
{
return (string) $this->get(PHP_URL_USER);
}
/**
* Retrieve the user password component of the URI.
*
* @return string
*/
public function getPassword(): string
{
return (string) $this->get(PHP_URL_PASS);
}
/**
* Retrieve the host component of the URI.
*
* @return string
*/
public function getHost(): string
{
return (string) $this->get(PHP_URL_HOST);
}
/**
* Retrieve the port component of the URI.
*
* @return int|null
*/
public function getPort(): ?int
{
return $this->get(PHP_URL_PORT);
}
/**
* Retrieve the path component of the URI.
*
* @return string
*/
public function getPath(): string
{
$value = $this->get(PHP_URL_PATH);
return Str::of($value)
->trim('/')
->start('/')
->when(
fn (Stringable $path) => $path->toString() !== '/',
fn (Stringable $path) => $path,
''
);
}
/**
* Retrieve the query string of the URI.
*
* @return string
*/
public function getQuery(): string
{
if ($value = $this->get(PHP_URL_QUERY)) {
return is_string($value) ? $value : http_build_query($value);
}
return '';
}
/**
* Retrieve the query array of the URI.
*
* @return array
*/
public function getQueryArray(): array
{
if ($value = $this->get(PHP_URL_QUERY)) {
return $value;
}
return [];
}
/**
* Retrieve the fragment component of the URI.
*
* @return string
*/
public function getFragment(): string
{
return (string) $this->get(PHP_URL_FRAGMENT);
}
/**
* Remove the fragment component from the URI.
*
* @return \DragonCode\Support\Http\Builder
*/
public function removeFragment(): BuilderContract
{
return $this->set(PHP_URL_FRAGMENT, null);
}
/**
* Return an instance with the specified scheme.
*
* @param string $scheme
*
* @return \DragonCode\Support\Http\Builder
*/
public function withScheme($scheme): self
{
return $this->set(PHP_URL_SCHEME, $scheme);
}
/**
* Return an instance with the specified user information.
*
* @param string $user
* @param string|null $password
*
* @return \DragonCode\Support\Http\Builder
*/
public function withUserInfo($user, $password = null): self
{
return $this
->set(PHP_URL_USER, $user)
->set(PHP_URL_PASS, $password);
}
/**
* Return an instance with the specified host.
*
* @param string $host
*
* @return \DragonCode\Support\Http\Builder
*/
public function withHost($host): self
{
return $this->set(PHP_URL_HOST, $host);
}
/**
* Return an instance with the specified port.
*
* @param int|null $port
*
* @return \DragonCode\Support\Http\Builder
*/
public function withPort($port): self
{
return $this->set(PHP_URL_PORT, $port);
}
/**
* Return an instance with the specified path.
*
* @param string $path
*
* @return \DragonCode\Support\Http\Builder
*/
public function withPath($path): self
{
return $this->set(PHP_URL_PATH, $path);
}
/**
* Return an instance with the specified query string.
*
* @param array|string $query
*
* @return \DragonCode\Support\Http\Builder
*/
public function withQuery($query): self
{
return $this->set(PHP_URL_QUERY, $query);
}
/**
* Return an instance with the specified query object.
*
* @param string $key
* @param mixed $value
*
* @return \DragonCode\Support\Http\Builder
*/
public function putQuery(string $key, $value): BuilderContract
{
$query = $this->get(PHP_URL_QUERY);
if (empty($value)) {
$value = is_array($value) ? []
: (! is_numeric($value) ? null : $value);
}
$query = Arr::set($query, $key, $value);
return $this->set(PHP_URL_QUERY, $query);
}
/**
* Return an instance with the specified query object.
*
* @param string $key
*
* @return \DragonCode\Support\Http\Builder
*/
public function removeQuery(string $key): BuilderContract
{
$query = $this->get(PHP_URL_QUERY);
unset($query[$key]);
return $this->set(PHP_URL_QUERY, $query);
}
/**
* Return an instance with the specified URI fragment.
*
* @param string $fragment
*
* @return \DragonCode\Support\Http\Builder
*/
public function withFragment($fragment): self
{
return $this->set(PHP_URL_FRAGMENT, $fragment);
}
/**
* Return an instance with the specified `UriInterface`.
*
* @param \Psr\Http\Message\UriInterface $uri
*
* @return \DragonCode\Support\Http\Builder
*/
public function fromPsr(UriInterface $uri): BuilderContract
{
$this->parsed = [];
$this->withScheme($uri->getScheme());
$this->withHost($uri->getHost());
$this->withPort($uri->getPort());
$this->withPath($uri->getPath());
$this->withQuery($uri->getQuery());
$this->withFragment($uri->getFragment());
$auth = explode(':', $uri->getUserInfo());
$this->withUserInfo(
$auth[0] ?? null,
$auth[1] ?? null
);
$this->cast($this->parsed);
return $this;
}
/**
* Return the string representation as a URI reference.
*
* @return \Psr\Http\Message\UriInterface
*/
public function toPsr(): UriInterface
{
return $this->same();
}
/**
* Returns parsed data.
*
* @return null[]|string[]
*/
public function toArray(): array
{
return [
'scheme' => $this->getScheme(),
'user' => $this->getUser(),
'pass' => $this->getPassword(),
'host' => $this->getHost(),
'port' => $this->getPort(),
'path' => $this->getPath(),
'query' => $this->getQuery(),
'fragment' => $this->getFragment(),
];
}
/**
* Return the string representation as a URI reference.
*
* @return string
*/
public function toUrl(): string
{
return Arr::of($this->prepare())
->map(static fn (mixed $value) => (string) $value)
->filter()
->implode('')
->when(fn (Stringable $url) => $url->toString() === '//', '', fn ($url) => $url)
->toString();
}
protected function componentNameByIndex(int $component): ?string
{
$this->validateComponentIndex($component);
return Arr::get($this->components, $component);
}
protected function validateComponentIndex(int $component): void
{
$components = array_keys($this->components);
if ($component !== self::PHP_URL_ALL && ! in_array($component, $components, true)) {
throw new UnknownUrlComponentIndexException($component);
}
}
protected function set(int $component, $value): self
{
$this->validate($component, $value);
$name = $this->componentNameByIndex($component);
$this->parsed[$name] = $this->castValue($name, $value);
return $this;
}
protected function get(int $index)
{
$name = $this->componentNameByIndex($index);
return Arr::get($this->parsed, $name);
}
protected function getValidationType(int $component): array
{
return Arr::get($this->validate, $component, []);
}
protected function validate(int $component, $value): void
{
$type = $this->getValidationType($component);
$this->validateType($value, $type);
}
/**
* Based on code by Maksim (Ellrion) Platonov.
*
* @see https://gist.github.com/Ellrion/f51ba0d40ae1d62eeae44fd1adf7b704
*
* @return array
*/
protected function prepare(): array
{
return [
BuilderPrepare::make()->of($this->getScheme())->suffix(':'),
'//',
BuilderPrepare::make()->of($this->getUser()),
BuilderPrepare::make()->of($this->getPassword())->prefix(':'),
$this->getUser() || $this->getPassword() ? '@' : '',
BuilderPrepare::make()->of($this->getHost()),
BuilderPrepare::make()->of($this->getPort())->prefix(':'),
BuilderPrepare::make()->of($this->getPath()),
BuilderPrepare::make()->of($this->getQuery())->prefix('?'),
BuilderPrepare::make()->of($this->getFragment())->prefix('#'),
];
}
#[Pure]
protected function resolveSame(int $component = self::PHP_URL_ALL): self
{
return $component === self::PHP_URL_ALL ? new self() : $this;
}
}

View File

@@ -0,0 +1,73 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Http;
use DragonCode\Contracts\Support\Stringable;
use DragonCode\Support\Concerns\Makeable;
use DragonCode\Support\Facades\Helpers\Str;
class BuilderPrepare implements Stringable
{
use Makeable;
protected string $of = '';
protected string $prefix = '';
protected string $suffix = '';
protected string $default = '';
public function __toString(): string
{
if (! empty($this->of)) {
return (string) $this->prefixed();
}
return $this->default;
}
public function of(mixed $value): self
{
$this->of = (string) $value;
return $this;
}
public function prefix(string $value): self
{
$this->prefix = $value;
return $this;
}
public function suffix(string $value): self
{
$this->suffix = $value;
return $this;
}
protected function prefixed(): ?string
{
return (string) Str::of($this->of)
->start($this->prefix)
->finish($this->suffix)
->trim();
}
}

View File

@@ -0,0 +1,204 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Http;
use DragonCode\Contracts\Http\Builder as BuilderContract;
use DragonCode\Support\Exceptions\NotValidUrlException;
use DragonCode\Support\Facades\Helpers\Str;
use DragonCode\Support\Facades\Http\Builder as UrlBuilder;
use Psr\Http\Message\UriInterface;
use Throwable;
class Url
{
protected array $chars = [
'А' => '%D0%90',
'а' => '%D0%B0',
'Б' => '%D0%91',
'б' => '%D0%B1',
'В' => '%D0%92',
'в' => '%D0%B2',
'Г' => '%D0%93',
'г' => '%D0%B3',
'Д' => '%D0%94',
'д' => '%D0%B4',
'Е' => '%D0%95',
'е' => '%D0%B5',
'Ж' => '%D0%96',
'ж' => '%D0%B6',
'З' => '%D0%97',
'з' => '%D0%B7',
'И' => '%D0%98',
'и' => '%D0%B8',
'Й' => '%D0%99',
'й' => '%D0%B9',
'К' => '%D0%9A',
'к' => '%D0%BA',
'Л' => '%D0%9B',
'л' => '%D0%BB',
'М' => '%D0%9C',
'м' => '%D0%BC',
'Н' => '%D0%9D',
'н' => '%D0%BD',
'О' => '%D0%9E',
'о' => '%D0%BE',
'П' => '%D0%9F',
'п' => '%D0%BF',
'Р' => '%D0%A0',
'р' => '%D1%80',
'С' => '%D0%A1',
'с' => '%D1%81',
'Т' => '%D0%A2',
'т' => '%D1%82',
'У' => '%D0%A3',
'у' => '%D1%83',
'Ф' => '%D0%A4',
'ф' => '%D1%84',
'Х' => '%D0%A5',
'х' => '%D1%85',
'Ц' => '%D0%A6',
'ц' => '%D1%86',
'Ч' => '%D0%A7',
'ч' => '%D1%87',
'Ш' => '%D0%A8',
'ш' => '%D1%88',
'Щ' => '%D0%A9',
'щ' => '%D1%89',
'Ъ' => '%D0%AA',
'ъ' => '%D1%8A',
'Ы' => '%D0%AB',
'ы' => '%D1%8B',
'Ь' => '%D0%AC',
'ь' => '%D1%8C',
'Э' => '%D0%AD',
'э' => '%D1%8D',
'Ю' => '%D0%AE',
'ю' => '%D1%8E',
'Я' => '%D0%AF',
'я' => '%D1%8F',
'Ё' => '%D0%81',
'ё' => '%D1%91',
];
/**
* Parsing URL into components.
*
* @param BuilderContract|string|null $url
*
* @return \DragonCode\Support\Http\Builder
*/
public function parse(UriInterface|string|null $url): Builder
{
return UrlBuilder::parse($url);
}
/**
* Check if the string is a valid URL.
*
* @param BuilderContract|string|null $url
*
* @return bool
*/
public function is(mixed $url): bool
{
try {
$url = Str::replace((string) $url, array_keys($this->chars), array_values($this->chars));
return filter_var($url, FILTER_VALIDATE_URL) !== false;
}
catch (Throwable) {
return false;
}
}
/**
* Validate if the value is a valid URL or throw an error.
*
* @param BuilderContract|string|null $url
*
* @throws \DragonCode\Support\Exceptions\NotValidUrlException
*/
public function validate(mixed $url): void
{
if (! $this->is($url)) {
throw new NotValidUrlException((string) $url);
}
}
/**
* Returns the URL after validation, or throws an error.
*
* @param BuilderContract|string|null $url
*
* @throws \DragonCode\Support\Exceptions\NotValidUrlException
*
* @return BuilderContract|\DragonCode\Support\Http\Builder|string
*/
public function validated(mixed $url): mixed
{
$this->validate($url);
return $url;
}
/**
* Check if the specified URL exists.
*
* @param BuilderContract|string|null $url
*
* @throws \DragonCode\Support\Exceptions\NotValidUrlException
*
* @return bool
*/
public function exists(UriInterface|string|null $url): bool
{
$this->validate($url);
try {
$headers = get_headers($url);
$key = array_search('HTTP/', $headers);
$value = $headers[$key] ?? null;
preg_match('/HTTP\/\d{1}\.?\d?\s[2-3]\d{2}/i', $value, $matches);
return count($matches) > 0;
}
catch (Throwable $e) {
return false;
}
}
/**
* Check the existence of the URL and return the default value if it is missing.
*
* @param BuilderContract|string $url
* @param BuilderContract|string $default
*
* @throws \DragonCode\Support\Exceptions\NotValidUrlException
*
* @return string|null
*/
public function default(UriInterface|string $url, UriInterface|string $default): string
{
$value = $this->exists($url) ? $url : $default;
return $this->validated($value);
}
}

View File

@@ -0,0 +1,205 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Instances;
use Closure;
use DragonCode\Support\Facades\Helpers\Arr;
use DragonCode\Support\Facades\Instances\Instance as InstanceHelper;
use DragonCode\Support\Facades\Instances\Reflection as ReflectionHelper;
use DragonCode\Support\Facades\Types\Is as IsHelper;
use InvalidArgumentException;
use ReflectionClass;
use ReflectionException;
class Call
{
/**
* Gets the result of executing code in the specified class.
*
* @param object|callable|string $class
* @param string $method
* @param mixed ...$parameters
*
* @throws ReflectionException
*
* @return mixed
*/
public function run(object|callable|string $class, string $method, mixed ...$parameters): mixed
{
$this->validate($class);
if ($value = $this->callback($class, $method, ...$parameters)) {
return $value;
}
$reflect = $this->reflection($class)->getMethod($method);
if (! $reflect->isStatic() && ! InstanceHelper::of($class, Closure::class)) {
$class = $this->resolve($class);
}
return call_user_func([$class, $method], ...$parameters);
}
/**
* Gets the result of executing code in the specified class if method exist.
*
* @param object|callable|string $class
* @param string $method
* @param mixed ...$parameters
*
* @throws ReflectionException
*
* @return mixed
*/
public function runExists(object|callable|string $class, string $method, mixed ...$parameters): mixed
{
$this->validate($class);
if ($value = $this->callback($class, $method, ...$parameters)) {
return $value;
}
if (method_exists($class, $method)) {
return $this->run($class, $method, ...$parameters);
}
return null;
}
/**
* Calls the object's methods one by one and returns the first non-empty value.
*
* @param object|callable|string $class
* @param array|string $methods
* @param mixed ...$parameters
*
* @throws ReflectionException
*
* @return mixed
*/
public function runMethods(object|callable|string $class, array|string $methods, mixed ...$parameters): mixed
{
if ($value = $this->callback($class, $methods, ...$parameters)) {
return $value;
}
foreach (Arr::wrap($methods) as $method) {
if ($value = $this->runExists($class, $method, ...$parameters)) {
return $value;
}
}
return null;
}
/**
* Calls a method of an object that matches a class.
*
* @param array $map
* @param mixed $value
* @param mixed ...$parameters
*
* @throws ReflectionException
*
* @return mixed|null
*/
public function runOf(array $map, mixed $value, mixed ...$parameters): mixed
{
if ($this->validated($value)) {
foreach ($map as $class => $method) {
if (InstanceHelper::of($value, $class)) {
return $this->runExists($value, $method, ...$parameters);
}
}
}
return null;
}
/**
* Gets the result of executing code in the specified class, if allowed.
*
* @param bool $when
* @param callable|Closure|string $class
* @param string $method
* @param mixed ...$parameters
*
* @throws ReflectionException
*
* @return mixed|null
*/
public function when(bool $when, object|callable|string $class, string $method, ...$parameters): mixed
{
return $when ? $this->run($class, $method, ...$parameters) : null;
}
/**
* Getting the result of a callback.
*
* @param mixed $callback
* @param mixed ...$parameters
*
* @return mixed
*/
public function callback(mixed $callback, mixed ...$parameters): mixed
{
if (is_callable($callback)) {
return $callback(...$parameters);
}
return null;
}
/**
* Execute a callback or return a value.
*
* @param mixed $callback
* @param array $parameters
*
* @return mixed
*/
public function value(mixed $callback, mixed $parameters = []): mixed
{
$parameters = Arr::wrap($parameters);
return is_callable($callback) ? $callback(...$parameters) : $callback;
}
protected function resolve(object|callable|string $class): object
{
return IsHelper::object($class) ? $class : new $class();
}
protected function reflection(object|callable|string $class): ReflectionClass
{
return ReflectionHelper::resolve($class);
}
protected function validate(mixed $class): void
{
if (! $this->validated($class)) {
throw new InvalidArgumentException('Argument #1 must be either a class reference or an instance of a class, ' . gettype($class) . ' given.');
}
}
protected function validated(mixed $class): bool
{
return is_callable($class) || InstanceHelper::exists($class) || IsHelper::object($class) || InstanceHelper::of($class, Closure::class);
}
}

View File

@@ -0,0 +1,136 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2023 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Instances;
use DragonCode\Support\Facades\Helpers\Arr as ArrHelper;
use DragonCode\Support\Facades\Instances\Reflection as ReflectionHelper;
use DragonCode\Support\Facades\Types\Is as IsHelper;
use ReflectionClass;
class Instance
{
/**
* Checks if the item being checked inherits from other objects and interfaces.
*
* @param object|callable|string $haystack
* @param string|string[] $needles
*
* @return bool
*/
public function of(mixed $haystack, mixed $needles): bool
{
if (! $this->exists($haystack)) {
return false;
}
$reflection = $this->resolve($haystack);
foreach (ArrHelper::wrap($needles) as $needle) {
if (! $this->exists($needle)) {
continue;
}
if ($this->findTrait($reflection, $needle)) {
return true;
}
if (is_a($haystack, $needle, true)) {
return true;
}
}
return false;
}
/**
* Extract the trailing name component from a file path.
*
* @param object|string $class
*
* @return string|null
*/
public function basename(object|string $class): ?string
{
$class = $this->classname($class);
return basename(str_replace('\\', '/', (string) $class)) ?: null;
}
/**
* Gets the class name of the object.
*
* @param object|string|null $class
*
* @return string|null
*/
public function classname(object|string $class = null): ?string
{
if (IsHelper::object($class)) {
return get_class($class);
}
return class_exists($class) || interface_exists($class) || enum_exists($class) ? $class : null;
}
/**
* Checks if the object exists.
*
* @param object|string $haystack
*
* @return bool
*/
public function exists(mixed $haystack): bool
{
if (IsHelper::object($haystack)) {
return true;
}
return IsHelper::string($haystack) && (class_exists($haystack) || interface_exists($haystack) || trait_exists($haystack) || enum_exists($haystack));
}
/**
* Creates a ReflectionClass object.
*
* @param object|string $class
*
* @return ReflectionClass
*/
protected function resolve(object|string $class): ReflectionClass
{
return ReflectionHelper::resolve($class);
}
protected function findTrait(ReflectionClass $haystack, object|string $needle): bool
{
if (in_array($needle, $haystack->getTraitNames(), true)) {
return true;
}
foreach ($haystack->getTraits() as $trait) {
if (in_array($needle, $trait->getTraitNames(), true)) {
return true;
}
if ($this->findTrait($trait, $needle)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Instances;
use DragonCode\Support\Facades\Types\Is as IsHelper;
use ReflectionClass;
use ReflectionException;
class Reflection
{
/**
* Creates a ReflectionClass object.
*
* @param object|ReflectionClass|string $class
*
* @throws ReflectionException
*
* @return ReflectionClass
*/
public function resolve(object|string $class): ReflectionClass
{
return IsHelper::reflectionClass($class) ? $class : new ReflectionClass($class);
}
/**
* Gets class constants.
*
* @param object|string $class
*
* @throws ReflectionException
*
* @return array
*/
public function getConstants(object|string $class): array
{
return $this->resolve($class)->getConstants();
}
public function isStaticMethod(object|string $class, string $method): bool
{
return $this->resolve($class)->getMethod($method)->isStatic();
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Tools;
class Replace
{
public function toFormat(string $value, ?string $format = null): string
{
return empty($format) || $format === '%s' ? $value : sprintf($format, $value);
}
public function toFormatArray(array $values, ?string $format = null): array
{
if (empty($format)) {
return $values;
}
return array_map(static fn ($value) => sprintf($format, $value), $values);
}
}

View File

@@ -0,0 +1,92 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Tools;
use DragonCode\Support\Exceptions\UnknownStubFileException;
use DragonCode\Support\Facades\Helpers\Str;
class Stub
{
public const PHP_ARRAY = 'php_array.stub';
public const JSON = 'json.stub';
/**
* Replace the contents of the template file.
*
* @param string $stub_file
* @param array $replace
*
* @throws \DragonCode\Support\Exceptions\UnknownStubFileException
*
* @return string
*/
public function replace(string $stub_file, array $replace): string
{
$content = $this->get($stub_file);
return Str::replaceFormat($content, $replace);
}
/**
* Receive the contents of the template file.
*
* @param string $filename
*
* @throws \DragonCode\Support\Exceptions\UnknownStubFileException
*
* @return string
*/
public function get(string $filename): string
{
if ($path = $this->path($filename)) {
return file_get_contents($path);
}
throw new UnknownStubFileException($filename);
}
/**
* Receive the path to the template file.
*
* @param string $filename
*
* @return string|null
*/
protected function path(string $filename): ?string
{
$path = $this->isCustom($filename) ? $filename : __DIR__ . '/../../resources/stubs/' . $filename;
return realpath($path);
}
/**
* Returns a link to the template file.
*
* If the file exists under the specified link, it will return it,
* otherwise it will search in the default folder.
*
* @param string $path
*
* @return bool
*/
protected function isCustom(string $path): bool
{
return file_exists($path);
}
}

View File

@@ -0,0 +1,138 @@
<?php
/*
* This file is part of the "dragon-code/support" project.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Andrey Helldar <helldar@ai-rus.com>
*
* @copyright 2022 Andrey Helldar
*
* @license MIT
*
* @see https://github.com/TheDragonCode/support
*/
namespace DragonCode\Support\Types;
use DragonCode\Support\Facades\Helpers\Arr as ArrHelper;
use DragonCode\Support\Facades\Helpers\Boolean as BooleanHelper;
use DragonCode\Support\Facades\Helpers\Str as StrHelper;
use DragonCode\Support\Facades\Instances\Instance as InstanceHelper;
use DragonCode\Support\Facades\Instances\Reflection as ReflectionHelper;
use Exception;
use ReflectionClass;
use Throwable;
class Is
{
/**
* Determines if the value is empty.
*
* @param mixed $value
*
* @return bool
*/
public function isEmpty(mixed $value): bool
{
if (is_numeric($value) || is_bool($value)) {
return false;
}
return empty($value) || StrHelper::isEmpty($value) || ArrHelper::isEmpty($value);
}
/**
* Determines if the value is doesn't empty.
*
* @param mixed $value
*
* @return bool
*/
public function doesntEmpty(mixed $value): bool
{
return ! $this->isEmpty($value);
}
/**
* Finds whether a variable is an object.
*
* @param mixed $value
*
* @return bool
*/
public function object(mixed $value): bool
{
return is_object($value);
}
/**
* Find whether the type of a variable is string.
*
* @param mixed $value
*
* @return bool
*/
public function string(mixed $value): bool
{
return is_string($value);
}
/**
* Determines if a value is boolean.
*
* @param mixed $value
*
* @return bool
*/
public function boolean(mixed $value): bool
{
$result = BooleanHelper::parse($value);
return is_bool($result);
}
/**
* Find whether the type of a variable is interface.
*
* @param mixed $value
*
* @return bool
*/
public function contract(mixed $value): bool
{
if (is_string($value)) {
$class = InstanceHelper::classname($value);
return ! empty($class) && interface_exists($class);
}
return ReflectionHelper::resolve($value)->isInterface();
}
/**
* Find whether the type of a variable is exception.
*
* @param mixed $value
*
* @return bool
*/
public function error(mixed $value): bool
{
return InstanceHelper::of($value, [Exception::class, Throwable::class]);
}
/**
* Find whether the type of a variable is ReflectionClass.
*
* @param mixed $value
*
* @return bool
*/
public function reflectionClass(mixed $value): bool
{
return $value instanceof ReflectionClass;
}
}