Skip to content

Commit

Permalink
adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
tymondesigns committed Jan 15, 2025
1 parent f20a682 commit e978ba5
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@
use Cortex\JsonSchema\Types\ObjectSchema;
use Cortex\JsonSchema\Exceptions\SchemaException;

class FromClosure
class ClosureConverter
{
public static function convert(Closure $closure): ObjectSchema
public function __construct(
protected Closure $closure,
) {}

public function convert(): ObjectSchema
{
$reflection = new ReflectionFunction($closure);
$reflection = new ReflectionFunction($this->closure);
$schema = new ObjectSchema();

// TODO: handle descriptions
Expand All @@ -40,6 +44,7 @@ protected static function getSchemaFromReflectionParameter(ReflectionParameter $
{
$type = $parameter->getType();

// @phpstan-ignore argument.type
$schema = self::getSchemaFromReflectionType($type);

$schema->title($parameter->getName());
Expand All @@ -64,6 +69,7 @@ protected static function getSchemaFromReflectionParameter(ReflectionParameter $
$reflection = new ReflectionEnum($typeName);

if ($reflection->isBacked()) {
/** @var non-empty-array<array-key, \BackedEnum> */
$cases = $typeName::cases();
$schema->enum(array_map(fn($case): int|string => $case->value, $cases));
}
Expand All @@ -81,10 +87,12 @@ protected static function getSchemaFromReflectionType(
): Schema {
$schemaTypes = match (true) {
$type instanceof ReflectionUnionType, $type instanceof ReflectionIntersectionType => array_map(
// @phpstan-ignore argument.type
fn(ReflectionNamedType $t): SchemaType => self::resolveSchemaType($t),
$type->getTypes(),
),
// If the parameter is not typed or explicitly typed as mixed, we use all schema types
// TODO: use phpstan parser to get the type also
in_array($type?->getName(), ['mixed', null], true) => SchemaType::cases(),
default => [self::resolveSchemaType($type)],
};
Expand Down
4 changes: 2 additions & 2 deletions src/SchemaFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use Cortex\JsonSchema\Types\StringSchema;
use Cortex\JsonSchema\Types\BooleanSchema;
use Cortex\JsonSchema\Types\IntegerSchema;
use Cortex\JsonSchema\Converters\FromClosure;
use Cortex\JsonSchema\Converters\ClosureConverter;

class SchemaFactory
{
Expand Down Expand Up @@ -68,6 +68,6 @@ public static function mixed(?string $title = null): UnionSchema

public static function fromClosure(Closure $closure): ObjectSchema
{
return FromClosure::convert($closure);
return (new ClosureConverter($closure))->convert();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
namespace Cortex\JsonSchema\Tests\Unit\Converters;

use Cortex\JsonSchema\Types\ObjectSchema;
use Cortex\JsonSchema\Converters\FromClosure;
use Cortex\JsonSchema\Converters\ClosureConverter;

it('can create a schema from a closure', function (): void {
$closure = function (string $name, array $fooArray, ?int $age = null): void {};
$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand Down Expand Up @@ -44,7 +44,7 @@ enum MyEnum: string
}

$closure = function (MyEnum $myEnum, bool $foo = true): void {};
$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand Down Expand Up @@ -78,7 +78,7 @@ enum Status: int
}

$closure = function (Status $status): void {};
$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand All @@ -102,7 +102,7 @@ enum Status: int

it('can create a schema from a closure with a union type', function (): void {
$closure = function (int|string $foo): void {};
$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand All @@ -124,7 +124,7 @@ enum Status: int

it('can create a schema from a closure with a nullable union type', function (): void {
$closure = function (int|string|null $foo): void {};
$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand All @@ -147,7 +147,7 @@ enum Status: int

it('can create a schema from a closure with array type hints', function (): void {
$closure = function (array $items, array $tags = ['default']): void {};
$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand All @@ -170,7 +170,7 @@ enum Status: int

it('can create a schema from a closure with mixed type', function (): void {
$closure = function (mixed $data): void {};
$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand All @@ -197,7 +197,7 @@ enum Status: int

it('can create a schema from a closure with object type', function (): void {
$closure = function (object $data): void {};
$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand All @@ -216,7 +216,7 @@ enum Status: int

it('can create a schema from a closure with float type', function (): void {
$closure = function (float $amount = 0.0): void {};
$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand All @@ -240,7 +240,7 @@ enum Status: int
bool $active = true,
): void {};

$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand Down Expand Up @@ -277,7 +277,7 @@ enum Status: int

it('can create a schema from a closure with array type', function (): void {
$closure = function (array $items = ['default']): void {};
$schema = FromClosure::convert($closure);
$schema = (new ClosureConverter($closure))->convert();

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
Expand Down
29 changes: 29 additions & 0 deletions tests/Unit/SchemaFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,32 @@
expect($schemaArray)->toHaveKey('readOnly', true);
expect($schemaArray)->toHaveKey('writeOnly', true);
});

it('can create a schema from a closure', function (): void {
$closure = function (string $name, array $fooArray, ?int $age = null): void {};
$schema = Schema::fromClosure($closure);

expect($schema)->toBeInstanceOf(ObjectSchema::class);
expect($schema->toArray())->toBe([
'type' => 'object',
'$schema' => 'http://json-schema.org/draft-07/schema#',
'properties' => [
'name' => [
'type' => 'string',
],
'fooArray' => [
'type' => 'array',
],
'age' => [
'type' => [
'integer',
'null',
],
],
],
'required' => [
'name',
'fooArray',
],
]);
});

0 comments on commit e978ba5

Please sign in to comment.