Skip to content

Commit 1f04649

Browse files
committed
AbstractSchemaInnerParse
1 parent 9303455 commit 1f04649

18 files changed

Lines changed: 588 additions & 507 deletions

src/Schema/AbstractObjectSchema.php

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use Chubbyphp\Parsing\Errors;
99
use Chubbyphp\Parsing\ErrorsException;
1010

11-
abstract class AbstractObjectSchema extends AbstractSchema implements ObjectSchemaInterface
11+
abstract class AbstractObjectSchema extends AbstractSchemaInnerParse implements ObjectSchemaInterface
1212
{
1313
public const string ERROR_TYPE_CODE = 'abstract_object.type';
1414
public const string ERROR_TYPE_TEMPLATE = 'Type should be "array|\stdClass|\Traversable", {{given}} given';
@@ -64,54 +64,18 @@ public function __construct(array $fieldToSchema)
6464
}
6565

6666
$this->fieldToSchema = $typeCheckedFieldToSchema;
67-
}
68-
69-
final public function parse(mixed $input): mixed
70-
{
71-
if ($input instanceof \stdClass || $input instanceof \Traversable) {
72-
$input = (array) $input;
73-
}
74-
75-
if ($input instanceof \JsonSerializable) {
76-
$input = $input->jsonSerialize();
77-
}
78-
79-
try {
80-
$input = $this->dispatchPreParses($input);
8167

82-
if (null === $input && $this->nullable) {
83-
return null;
68+
$this->preParses[] = static function (mixed $input) {
69+
if ($input instanceof \stdClass || $input instanceof \Traversable) {
70+
return (array) $input;
8471
}
8572

86-
if (!\is_array($input)) {
87-
throw new ErrorsException(
88-
new Error(
89-
static::ERROR_TYPE_CODE,
90-
static::ERROR_TYPE_TEMPLATE,
91-
['given' => $this->getDataType($input)]
92-
)
93-
);
94-
}
95-
96-
/** @var array<string, mixed> $input */
97-
$childrenErrors = new Errors();
98-
99-
$this->unknownFields($input, $childrenErrors);
100-
101-
$output = $this->parseFields($input, $childrenErrors);
102-
103-
if ($childrenErrors->has()) {
104-
throw new ErrorsException($childrenErrors);
73+
if ($input instanceof \JsonSerializable) {
74+
return $input->jsonSerialize();
10575
}
10676

107-
return $this->dispatchPostParses($output);
108-
} catch (ErrorsException $e) {
109-
if ($this->catch) {
110-
return ($this->catch)($input, $e);
111-
}
112-
113-
throw $e;
114-
}
77+
return $input;
78+
};
11579
}
11680

11781
/**
@@ -146,6 +110,32 @@ final public function optional(array $optional = []): static
146110
return $clone;
147111
}
148112

113+
protected function innerParse(mixed $input): mixed
114+
{
115+
if (!\is_array($input)) {
116+
throw new ErrorsException(
117+
new Error(
118+
static::ERROR_TYPE_CODE,
119+
static::ERROR_TYPE_TEMPLATE,
120+
['given' => $this->getDataType($input)]
121+
)
122+
);
123+
}
124+
125+
/** @var array<string, mixed> $input */
126+
$childrenErrors = new Errors();
127+
128+
$this->unknownFields($input, $childrenErrors);
129+
130+
$output = $this->parseFields($input, $childrenErrors);
131+
132+
if ($childrenErrors->has()) {
133+
throw new ErrorsException($childrenErrors);
134+
}
135+
136+
return $output;
137+
}
138+
149139
/**
150140
* @param array<string, mixed> $input
151141
*/

src/Schema/AbstractSchema.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
use Chubbyphp\Parsing\ErrorsException;
88
use Chubbyphp\Parsing\Result;
99

10+
/**
11+
* @deprecated use Chubbyphp\Parsing\Schem\AbstractSchemaInnerParse
12+
*/
1013
abstract class AbstractSchema implements SchemaInterface
1114
{
1215
protected bool $nullable = false;
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Chubbyphp\Parsing\Schema;
6+
7+
use Chubbyphp\Parsing\ErrorsException;
8+
use Chubbyphp\Parsing\Result;
9+
10+
abstract class AbstractSchemaInnerParse implements SchemaInterface
11+
{
12+
protected bool $nullable = false;
13+
14+
/**
15+
* @var array<\Closure(mixed): mixed>
16+
*/
17+
protected array $preParses = [];
18+
19+
/**
20+
* @var array<\Closure>
21+
*/
22+
protected array $postParses = [];
23+
24+
/**
25+
* @var \Closure(mixed, ErrorsException): mixed
26+
*/
27+
protected ?\Closure $catch = null;
28+
29+
final public function nullable(bool $nullable = true): static
30+
{
31+
$clone = clone $this;
32+
$clone->nullable = $nullable;
33+
34+
return $clone;
35+
}
36+
37+
final public function default(mixed $default): static
38+
{
39+
return $this->preParse(static fn (mixed $input) => $input ?? $default);
40+
}
41+
42+
/**
43+
* @param \Closure(mixed $input): mixed $preParse
44+
*/
45+
final public function preParse(\Closure $preParse): static
46+
{
47+
$clone = clone $this;
48+
$clone->preParses[] = $preParse;
49+
50+
return $clone;
51+
}
52+
53+
final public function postParse(\Closure $postParse): static
54+
{
55+
$clone = clone $this;
56+
$clone->postParses[] = $postParse;
57+
58+
return $clone;
59+
}
60+
61+
final public function parse(mixed $input): mixed
62+
{
63+
try {
64+
$input = $this->dispatchPreParses($input);
65+
66+
if (null === $input && $this->nullable) {
67+
return null;
68+
}
69+
70+
$output = $this->innerParse($input);
71+
72+
return $this->dispatchPostParses($output);
73+
} catch (ErrorsException $e) {
74+
if ($this->catch) {
75+
return ($this->catch)($input, $e);
76+
}
77+
78+
throw $e;
79+
}
80+
}
81+
82+
final public function safeParse(mixed $input): Result
83+
{
84+
try {
85+
return new Result($this->parse($input), null);
86+
} catch (ErrorsException $e) {
87+
return new Result(null, $e);
88+
}
89+
}
90+
91+
/**
92+
* @param \Closure(mixed $input, ErrorsException $e): mixed $catch
93+
*/
94+
final public function catch(\Closure $catch): static
95+
{
96+
$clone = clone $this;
97+
$clone->catch = $catch;
98+
99+
return $clone;
100+
}
101+
102+
abstract protected function innerParse(mixed $input): mixed;
103+
104+
final protected function dispatchPreParses(mixed $data): mixed
105+
{
106+
return array_reduce(
107+
$this->preParses,
108+
static fn (mixed $currentData, \Closure $preParse) => $preParse($currentData),
109+
$data
110+
);
111+
}
112+
113+
final protected function dispatchPostParses(mixed $data): mixed
114+
{
115+
return array_reduce(
116+
$this->postParses,
117+
static fn (mixed $currentData, \Closure $postParse) => $postParse($currentData),
118+
$data
119+
);
120+
}
121+
122+
final protected function getDataType(mixed $input): string
123+
{
124+
return \is_object($input) ? $input::class : \gettype($input);
125+
}
126+
}

src/Schema/ArraySchema.php

Lines changed: 32 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use Chubbyphp\Parsing\Errors;
99
use Chubbyphp\Parsing\ErrorsException;
1010

11-
final class ArraySchema extends AbstractSchema implements SchemaInterface
11+
final class ArraySchema extends AbstractSchemaInnerParse implements SchemaInterface
1212
{
1313
public const string ERROR_TYPE_CODE = 'array.type';
1414
public const string ERROR_TYPE_TEMPLATE = 'Type should be "array", {{given}} given';
@@ -27,51 +27,6 @@ final class ArraySchema extends AbstractSchema implements SchemaInterface
2727

2828
public function __construct(private SchemaInterface $itemSchema) {}
2929

30-
public function parse(mixed $input): mixed
31-
{
32-
try {
33-
$input = $this->dispatchPreParses($input);
34-
35-
if (null === $input && $this->nullable) {
36-
return null;
37-
}
38-
39-
if (!\is_array($input)) {
40-
throw new ErrorsException(
41-
new Error(
42-
self::ERROR_TYPE_CODE,
43-
self::ERROR_TYPE_TEMPLATE,
44-
['given' => $this->getDataType($input)]
45-
)
46-
);
47-
}
48-
49-
$array = [];
50-
51-
$childrenErrors = new Errors();
52-
53-
foreach ($input as $i => $item) {
54-
try {
55-
$array[$i] = $this->itemSchema->parse($item);
56-
} catch (ErrorsException $e) {
57-
$childrenErrors->add($e->errors, (string) $i);
58-
}
59-
}
60-
61-
if ($childrenErrors->has()) {
62-
throw new ErrorsException($childrenErrors);
63-
}
64-
65-
return $this->dispatchPostParses($array);
66-
} catch (ErrorsException $e) {
67-
if ($this->catch) {
68-
return ($this->catch)($input, $e);
69-
}
70-
71-
throw $e;
72-
}
73-
}
74-
7530
public function length(int $length): static
7631
{
7732
return $this->postParse(static function (array $array) use ($length) {
@@ -187,4 +142,35 @@ public function reduce(\Closure $reduce, mixed $initial = null): static
187142
{
188143
return $this->postParse(static fn (array $array) => array_reduce($array, $reduce, $initial));
189144
}
145+
146+
protected function innerParse(mixed $input): mixed
147+
{
148+
if (!\is_array($input)) {
149+
throw new ErrorsException(
150+
new Error(
151+
self::ERROR_TYPE_CODE,
152+
self::ERROR_TYPE_TEMPLATE,
153+
['given' => $this->getDataType($input)]
154+
)
155+
);
156+
}
157+
158+
$output = [];
159+
160+
$childrenErrors = new Errors();
161+
162+
foreach ($input as $i => $item) {
163+
try {
164+
$output[$i] = $this->itemSchema->parse($item);
165+
} catch (ErrorsException $e) {
166+
$childrenErrors->add($e->errors, (string) $i);
167+
}
168+
}
169+
170+
if ($childrenErrors->has()) {
171+
throw new ErrorsException($childrenErrors);
172+
}
173+
174+
return $output;
175+
}
190176
}

0 commit comments

Comments
 (0)