mirror of
https://github.com/pestphp/pest.git
synced 2026-03-10 09:47:23 +01:00
Merge branch 'master' into better-dataset-support
# Conflicts: # src/Concerns/Testable.php
This commit is contained in:
@ -11,6 +11,8 @@ final class Arr
|
||||
{
|
||||
/**
|
||||
* Checks if the given array has the given key.
|
||||
*
|
||||
* @param array<array-key, mixed> $array
|
||||
*/
|
||||
public static function has(array $array, string|int $key): bool
|
||||
{
|
||||
@ -33,6 +35,8 @@ final class Arr
|
||||
|
||||
/**
|
||||
* Gets the given key value.
|
||||
*
|
||||
* @param array<array-key, mixed> $array
|
||||
*/
|
||||
public static function get(array $array, string|int $key, mixed $default = null): mixed
|
||||
{
|
||||
|
||||
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Pest\Support;
|
||||
|
||||
use Closure;
|
||||
use Pest\Exceptions\ShouldNotHappen;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -17,10 +18,12 @@ final class ChainableClosure
|
||||
public static function from(Closure $closure, Closure $next): Closure
|
||||
{
|
||||
return function () use ($closure, $next): void {
|
||||
/* @phpstan-ignore-next-line */
|
||||
call_user_func_array(Closure::bind($closure, $this, $this::class), func_get_args());
|
||||
/* @phpstan-ignore-next-line */
|
||||
call_user_func_array(Closure::bind($next, $this, $this::class), func_get_args());
|
||||
if (!is_object($this)) { // @phpstan-ignore-line
|
||||
throw ShouldNotHappen::fromMessage('$this not bound to chainable closure.');
|
||||
}
|
||||
|
||||
\Pest\Support\Closure::bind($closure, $this, $this::class)(...func_get_args());
|
||||
\Pest\Support\Closure::bind($next, $this, $this::class)(...func_get_args());
|
||||
};
|
||||
}
|
||||
|
||||
@ -30,10 +33,8 @@ final class ChainableClosure
|
||||
public static function fromStatic(Closure $closure, Closure $next): Closure
|
||||
{
|
||||
return static function () use ($closure, $next): void {
|
||||
/* @phpstan-ignore-next-line */
|
||||
call_user_func_array(Closure::bind($closure, null, self::class), func_get_args());
|
||||
/* @phpstan-ignore-next-line */
|
||||
call_user_func_array(Closure::bind($next, null, self::class), func_get_args());
|
||||
\Pest\Support\Closure::bind($closure, null, self::class)(...func_get_args());
|
||||
\Pest\Support\Closure::bind($next, null, self::class)(...func_get_args());
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
36
src/Support/Closure.php
Normal file
36
src/Support/Closure.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Support;
|
||||
|
||||
use Closure as BaseClosure;
|
||||
use Pest\Exceptions\ShouldNotHappen;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Closure
|
||||
{
|
||||
/**
|
||||
* Binds the given closure to the given "this".
|
||||
*
|
||||
* @return BaseClosure|never
|
||||
*
|
||||
* @throws ShouldNotHappen
|
||||
*/
|
||||
public static function bind(BaseClosure|null $closure, ?object $newThis, object|string|null $newScope = 'static'): BaseClosure
|
||||
{
|
||||
if ($closure == null) {
|
||||
throw ShouldNotHappen::fromMessage('Could not bind null closure.');
|
||||
}
|
||||
|
||||
$closure = BaseClosure::bind($closure, $newThis, $newScope);
|
||||
|
||||
if ($closure == false) {
|
||||
throw ShouldNotHappen::fromMessage('Could not bind closure.');
|
||||
}
|
||||
|
||||
return $closure;
|
||||
}
|
||||
}
|
||||
@ -35,16 +35,16 @@ final class Container
|
||||
/**
|
||||
* Gets a dependency from the container.
|
||||
*
|
||||
* @return object
|
||||
* @param class-string $id
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $id)
|
||||
{
|
||||
if (array_key_exists($id, $this->instances)) {
|
||||
return $this->instances[$id];
|
||||
if (!array_key_exists($id, $this->instances)) {
|
||||
$this->instances[$id] = $this->build($id);
|
||||
}
|
||||
|
||||
$this->instances[$id] = $this->build($id);
|
||||
|
||||
return $this->instances[$id];
|
||||
}
|
||||
|
||||
@ -60,10 +60,11 @@ final class Container
|
||||
|
||||
/**
|
||||
* Tries to build the given instance.
|
||||
*
|
||||
* @param class-string $id
|
||||
*/
|
||||
private function build(string $id): object
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
$reflectionClass = new ReflectionClass($id);
|
||||
|
||||
if ($reflectionClass->isInstantiable()) {
|
||||
@ -84,6 +85,7 @@ final class Container
|
||||
}
|
||||
}
|
||||
|
||||
//@phpstan-ignore-next-line
|
||||
return $this->get($candidate);
|
||||
},
|
||||
$constructor->getParameters()
|
||||
|
||||
@ -50,6 +50,8 @@ final class ExceptionTrace
|
||||
|
||||
$property = new ReflectionProperty($t, 'serializableTrace');
|
||||
$property->setAccessible(true);
|
||||
|
||||
/** @var array<int, array<string, string>> $trace */
|
||||
$trace = $property->getValue($t);
|
||||
|
||||
$cleanedTrace = [];
|
||||
|
||||
@ -25,13 +25,16 @@ final class HigherOrderCallables
|
||||
*
|
||||
* Create a new expectation. Callable values will be executed prior to returning the new expectation.
|
||||
*
|
||||
* @param callable|TValue $value
|
||||
* @param (Closure():TValue)|TValue $value
|
||||
*
|
||||
* @return Expectation<TValue>
|
||||
*/
|
||||
public function expect(mixed $value): Expectation
|
||||
{
|
||||
return new Expectation($value instanceof Closure ? Reflection::bindCallableWithData($value) : $value);
|
||||
/** @var TValue $value */
|
||||
$value = $value instanceof Closure ? Reflection::bindCallableWithData($value) : $value;
|
||||
|
||||
return new Expectation($value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -18,14 +18,14 @@ final class HigherOrderMessage
|
||||
/**
|
||||
* An optional condition that will determine if the message will be executed.
|
||||
*
|
||||
* @var (callable(): bool)|null
|
||||
* @var (Closure(): bool)|null
|
||||
*/
|
||||
public $condition;
|
||||
public ?Closure $condition = null;
|
||||
|
||||
/**
|
||||
* Creates a new higher order message.
|
||||
*
|
||||
* @param array<int, mixed>|null $arguments
|
||||
* @param array<int, mixed> $arguments
|
||||
*/
|
||||
public function __construct(
|
||||
public string $filename,
|
||||
@ -41,7 +41,6 @@ final class HigherOrderMessage
|
||||
*/
|
||||
public function call(object $target): mixed
|
||||
{
|
||||
/* @phpstan-ignore-next-line */
|
||||
if (is_callable($this->condition) && call_user_func(Closure::bind($this->condition, $target)) === false) {
|
||||
return $target;
|
||||
}
|
||||
@ -54,8 +53,7 @@ final class HigherOrderMessage
|
||||
try {
|
||||
return is_array($this->arguments)
|
||||
? Reflection::call($target, $this->name, $this->arguments)
|
||||
: $target->{$this->name};
|
||||
/* @phpstan-ignore-line */
|
||||
: $target->{$this->name}; /* @phpstan-ignore-line */
|
||||
} catch (Throwable $throwable) {
|
||||
Reflection::setPropertyValue($throwable, 'file', $this->filename);
|
||||
Reflection::setPropertyValue($throwable, 'line', $this->line);
|
||||
@ -79,7 +77,7 @@ final class HigherOrderMessage
|
||||
*/
|
||||
public function when(callable $condition): self
|
||||
{
|
||||
$this->condition = $condition;
|
||||
$this->condition = Closure::fromCallable($condition);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ final class HigherOrderMessageCollection
|
||||
*
|
||||
* @param array<int, mixed>|null $arguments
|
||||
*/
|
||||
public function add(string $filename, int $line, string $name, array $arguments = null): void
|
||||
public function add(string $filename, int $line, string $name, ?array $arguments): void
|
||||
{
|
||||
$this->messages[] = new HigherOrderMessage($filename, $line, $name, $arguments);
|
||||
}
|
||||
@ -29,7 +29,7 @@ final class HigherOrderMessageCollection
|
||||
*
|
||||
* @param array<int, mixed>|null $arguments
|
||||
*/
|
||||
public function addWhen(callable $condition, string $filename, int $line, string $name, array $arguments = null): void
|
||||
public function addWhen(callable $condition, string $filename, int $line, string $name, ?array $arguments): void
|
||||
{
|
||||
$this->messages[] = (new HigherOrderMessage($filename, $line, $name, $arguments))->when($condition);
|
||||
}
|
||||
@ -40,6 +40,7 @@ final class HigherOrderMessageCollection
|
||||
public function chain(object $target): void
|
||||
{
|
||||
foreach ($this->messages as $message) {
|
||||
//@phpstan-ignore-next-line
|
||||
$target = $message->call($target) ?? $target;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ use Throwable;
|
||||
*/
|
||||
final class HigherOrderTapProxy
|
||||
{
|
||||
private const UNDEFINED_PROPERTY = 'Undefined property: P\\';
|
||||
private const UNDEFINED_PROPERTY = 'Undefined property: P\\'; // @phpstan-ignore-line
|
||||
|
||||
/**
|
||||
* Create a new tap proxy instance.
|
||||
@ -31,8 +31,7 @@ final class HigherOrderTapProxy
|
||||
*/
|
||||
public function __set(string $property, $value): void
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
$this->target->{$property} = $value;
|
||||
$this->target->{$property} = $value; // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,9 +42,8 @@ final class HigherOrderTapProxy
|
||||
public function __get(string $property)
|
||||
{
|
||||
try {
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->target->{$property};
|
||||
} catch (Throwable $throwable) {
|
||||
return $this->target->{$property}; // @phpstan-ignore-line
|
||||
} catch (Throwable $throwable) { // @phpstan-ignore-line
|
||||
Reflection::setPropertyValue($throwable, 'file', Backtrace::file());
|
||||
Reflection::setPropertyValue($throwable, 'line', Backtrace::line());
|
||||
|
||||
|
||||
@ -48,4 +48,14 @@ final class Str
|
||||
|
||||
return substr($target, -$length) === $search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string evaluable by an `eval`.
|
||||
*/
|
||||
public static function evaluable(string $code): string
|
||||
{
|
||||
$code = str_replace(' ', '_', $code);
|
||||
|
||||
return (string) preg_replace('/[^A-Z_a-z0-9\\\\]/', '', $code);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user