-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of Chevere. | ||
* | ||
* (c) Rodolfo Berrios <rodolfo@chevere.org> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Chevere\Action; | ||
|
||
use Chevere\Action\Interfaces\ActionInterface; | ||
use Chevere\Parameter\Cast; | ||
use Chevere\Parameter\Interfaces\CastInterface; | ||
use Chevere\Parameter\Interfaces\ParameterInterface; | ||
use Chevere\Parameter\Interfaces\ParametersInterface; | ||
use Chevere\Parameter\Interfaces\UnionParameterInterface; | ||
use LogicException; | ||
use ReflectionMethod; | ||
use ReflectionNamedType; | ||
use Throwable; | ||
use TypeError; | ||
use function Chevere\Message\message; | ||
use function Chevere\Parameter\arguments; | ||
use function Chevere\Parameter\arrayp; | ||
|
||
/** | ||
* @method mixed run() | ||
*/ | ||
abstract class Action implements ActionInterface | ||
{ | ||
public const RUN_METHOD = 'run'; | ||
|
||
protected ?ParametersInterface $parameters = null; | ||
|
||
public static function acceptResponse(): ParameterInterface | ||
{ | ||
return arrayp(); | ||
} | ||
|
||
final public static function assert(): void | ||
{ | ||
static::assertMethod(); | ||
static::assertStatic(); | ||
} | ||
|
||
final public function getResponse(mixed ...$argument): CastInterface | ||
{ | ||
static::assert(); | ||
$this->assertRuntime(); | ||
Check warning on line 54 in src/Action.php GitHub Actions / PHP 8.1 test on ubuntu-latest
Check warning on line 54 in src/Action.php GitHub Actions / PHP 8.2 test on ubuntu-latest
Check warning on line 54 in src/Action.php GitHub Actions / PHP 8.3 test on ubuntu-latest
|
||
$arguments = arguments($this->parameters(), $argument)->toArray(); | ||
$run = $this->run(...$arguments); | ||
|
||
try { | ||
static::acceptResponse()->__invoke($run); | ||
} catch (Throwable $e) { | ||
$message = message( | ||
'`%method%` → %message%', | ||
method: static::runMethodFQN(), | ||
message: $e->getMessage(), | ||
)->__toString(); | ||
|
||
throw new ($e::class)($message); | ||
} | ||
|
||
return new Cast($run); | ||
} | ||
|
||
public static function assertTypes( | ||
Check warning on line 73 in src/Action.php GitHub Actions / PHP 8.1 test on ubuntu-latest
Check warning on line 73 in src/Action.php GitHub Actions / PHP 8.2 test on ubuntu-latest
Check warning on line 73 in src/Action.php GitHub Actions / PHP 8.3 test on ubuntu-latest
|
||
ReflectionNamedType $reflection, | ||
ParameterInterface $response | ||
): void { | ||
$returnName = $reflection->getName(); | ||
$expectName = $response->type()->typeHinting(); | ||
$return = match ($returnName) { | ||
'void' => 'null', | ||
'ArrayAccess' => 'array', | ||
default => $returnName, | ||
}; | ||
$expect = []; | ||
if ($response instanceof UnionParameterInterface) { | ||
foreach ($response->parameters() as $parameter) { | ||
$expect[] = $parameter->type()->typeHinting(); | ||
} | ||
} else { | ||
$expect[] = match ($expectName) { | ||
'generic' => 'array', | ||
default => $expectName, | ||
}; | ||
} | ||
if (! in_array($return, $expect, true)) { | ||
throw new TypeError( | ||
(string) message( | ||
'Method `%method%` must declare `%type%` return type', | ||
method: static::runMethodFQN(), | ||
type: implode('|', $expect), | ||
) | ||
); | ||
} | ||
} | ||
|
||
protected static function assertMethod(): void | ||
Check warning on line 106 in src/Action.php GitHub Actions / PHP 8.1 test on ubuntu-latest
Check warning on line 106 in src/Action.php GitHub Actions / PHP 8.2 test on ubuntu-latest
Check warning on line 106 in src/Action.php GitHub Actions / PHP 8.3 test on ubuntu-latest
|
||
{ | ||
if (! method_exists(static::class, static::RUN_METHOD)) { | ||
throw new LogicException( | ||
(string) message( | ||
'Action `%action%` does not define %invoke% method', | ||
action: static::class, | ||
invoke: static::RUN_METHOD, | ||
) | ||
); | ||
} | ||
$response = static::acceptResponse(); | ||
$method = new ReflectionMethod(static::class, static::RUN_METHOD); | ||
if (! $method->hasReturnType()) { | ||
if ($response->type()->typeHinting() === 'null') { | ||
return; | ||
} | ||
|
||
throw new TypeError( | ||
(string) message( | ||
'Method `%method%` must declare `%type%` return type', | ||
method: static::runMethodFQN(), | ||
type: $response->type()->typeHinting(), | ||
) | ||
); | ||
} | ||
/** @var ReflectionNamedType $returnType */ | ||
$returnType = $method->getReturnType(); | ||
static::assertTypes($returnType, $response); | ||
} | ||
|
||
/** | ||
* Enables to define extra parameter assertion before the run method is called. | ||
* @codeCoverageIgnore | ||
*/ | ||
protected static function assertStatic(): void | ||
{ | ||
// enables extra static assertion | ||
} | ||
|
||
/** | ||
* Enables to define extra parameter assertion before the run method is called. | ||
* @codeCoverageIgnore | ||
*/ | ||
protected function assertRuntime(): void | ||
{ | ||
// enables extra runtime assertion | ||
} | ||
|
||
final protected function parameters(): ParametersInterface | ||
{ | ||
if ($this->parameters === null) { | ||
$this->parameters = getParameters(static::class); | ||
} | ||
|
||
return $this->parameters; | ||
} | ||
|
||
final protected static function runMethodFQN(): string | ||
{ | ||
return static::class . '::' . static::RUN_METHOD; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of Chevere. | ||
* | ||
* (c) Rodolfo Berrios <rodolfo@chevere.org> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Chevere\Action; | ||
|
||
use Chevere\Action\Interfaces\ControllerInterface; | ||
use Chevere\Parameter\Interfaces\StringParameterInterface; | ||
use InvalidArgumentException; | ||
use function Chevere\Message\message; | ||
|
||
abstract class Controller extends Action implements ControllerInterface | ||
{ | ||
protected static function assertStatic(): void | ||
{ | ||
$invalid = []; | ||
foreach (getParameters(static::class) as $name => $parameter) { | ||
if (! ($parameter instanceof StringParameterInterface)) { | ||
$invalid[] = $name; | ||
} | ||
} | ||
if ($invalid === []) { | ||
return; | ||
} | ||
|
||
throw new InvalidArgumentException( | ||
(string) message( | ||
'Parameter `%parameters%` must be of type **%type%** for controller **%className%**', | ||
parameters: implode(', ', $invalid), | ||
type: 'string', | ||
className: static::class | ||
) | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of Chevere. | ||
* | ||
* (c) Rodolfo Berrios <rodolfo@chevere.org> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Chevere\Action; | ||
|
||
use Chevere\Action\Interfaces\ControllerInterface; | ||
use Chevere\Action\Interfaces\ControllerNameInterface; | ||
use InvalidArgumentException; | ||
|
||
final class ControllerName implements ControllerNameInterface | ||
{ | ||
public function __construct( | ||
private string $name | ||
) { | ||
if (is_subclass_of($this->name, ControllerInterface::class)) { | ||
return; | ||
} | ||
|
||
throw new InvalidArgumentException(); | ||
} | ||
|
||
public function __toString(): string | ||
{ | ||
return $this->name; | ||
} | ||
} |