Skip to content

Commit a6a307f

Browse files
committed
Add a new HTMLType
1 parent 5c7b37c commit a6a307f

19 files changed

Lines changed: 474 additions & 74 deletions

File tree

rector.tests.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Flow\ETL\FlowContext;
2828
use Flow\Types\Type\Logical\DateTimeType;
2929
use Flow\Types\Type\Logical\DateType;
30+
use Flow\Types\Type\Logical\HTMLType;
3031
use Flow\Types\Type\Logical\JsonType;
3132
use Flow\Types\Type\Logical\ListType;
3233
use Flow\Types\Type\Logical\MapType;
@@ -165,6 +166,7 @@
165166
new NewObjectToFunction(UuidType::class, 'Flow\ETL\DSL\type_uuid'),
166167
new NewObjectToFunction(XMLElementType::class, 'Flow\ETL\DSL\type_xml_element'),
167168
new NewObjectToFunction(XMLType::class, 'Flow\ETL\DSL\type_xml'),
169+
new NewObjectToFunction(HTMLType::class, 'Flow\ETL\DSL\type_html'),
168170

169171
// Extractors
170172
new NewObjectToFunction(CacheExtractor::class, 'from_cache'),

src/core/etl/src/Flow/ETL/Row/EntryFactory.php

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@ enum_entry,
2020
uuid_entry,
2121
xml_element_entry,
2222
xml_entry};
23-
use function Flow\Types\DSL\{type_date, type_datetime, type_json, type_optional, type_string, type_time, type_uuid, type_xml, type_xml_element};
23+
use function Flow\Types\DSL\{
24+
type_html,
25+
type_json,
26+
type_optional,
27+
type_string,
28+
type_uuid,
29+
type_xml
30+
};
2431
use Flow\ETL\Exception\{InvalidArgumentException,
2532
RuntimeException,
2633
SchemaDefinitionNotFoundException};
@@ -54,7 +61,6 @@ enum_entry,
5461
};
5562
use Flow\Types\Type\Native\String\StringTypeChecker;
5663
use Flow\Types\Type\TypeDetector;
57-
use Flow\Types\Value\Uuid;
5864

5965
final readonly class EntryFactory
6066
{
@@ -91,41 +97,15 @@ public function create(string $entryName, mixed $value, Schema|Definition|null $
9197

9298
if ($stringChecker->isJson()) {
9399
$valueType = type_json();
94-
}
95-
96-
if ($stringChecker->isUuid()) {
100+
} elseif ($stringChecker->isUuid()) {
97101
$valueType = type_uuid();
98-
}
99-
100-
if ($stringChecker->isXML()) {
102+
} elseif ($stringChecker->isHTML()) {
103+
$valueType = type_html();
104+
} elseif ($stringChecker->isXML()) {
101105
$valueType = type_xml();
102106
}
103107
}
104108

105-
if ($valueType instanceof InstanceOfType) {
106-
if ($valueType->class === \DOMDocument::class) {
107-
$valueType = type_xml();
108-
} elseif ($valueType->class === \DOMElement::class) {
109-
$valueType = type_xml_element();
110-
} elseif ($valueType->class === \DateInterval::class) {
111-
$valueType = type_time();
112-
} elseif (\in_array($valueType->class, [\DateTimeImmutable::class, \DateTimeInterface::class, \DateTime::class], true)) {
113-
if ($value instanceof \DateTimeInterface && $value->format('H:i:s') === '00:00:00') {
114-
$valueType = type_date();
115-
} else {
116-
$valueType = type_datetime();
117-
}
118-
} else {
119-
foreach (['Ramsey\Uuid\UuidInterface', Uuid::class, 'Symfony\Component\Uid\Uuid'] as $uuidClass) {
120-
if (\is_a($valueType->class, $uuidClass, true)) {
121-
$valueType = type_uuid();
122-
123-
break;
124-
}
125-
}
126-
}
127-
}
128-
129109
return $this->createAs($entryName, $value, $valueType);
130110
}
131111

src/core/etl/src/Flow/ETL/Schema/Definition.php

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,35 @@
55
namespace Flow\ETL\Schema;
66

77
use function Flow\ETL\DSL\is_nullable;
8-
use function Flow\Types\DSL\{type_array, type_boolean, type_date, type_datetime, type_enum, type_equals, type_float, type_integer, type_is, type_is_any, type_json, type_list, type_map, type_mixed, type_optional, type_string, type_structure, type_time, type_uuid, type_xml, type_xml_element, types};
8+
use function Flow\Types\DSL\{type_array,
9+
type_boolean,
10+
type_date,
11+
type_datetime,
12+
type_enum,
13+
type_equals,
14+
type_float,
15+
type_html,
16+
type_integer,
17+
type_is,
18+
type_is_any,
19+
type_json,
20+
type_list,
21+
type_map,
22+
type_mixed,
23+
type_optional,
24+
type_string,
25+
type_structure,
26+
type_time,
27+
type_uuid,
28+
type_xml,
29+
type_xml_element,
30+
types};
931
use Flow\ETL\Exception\{InvalidArgumentException, RuntimeException};
1032
use Flow\ETL\Row\{Entry, EntryReference, Reference};
1133
use Flow\Types\Type;
1234
use Flow\Types\Type\Logical\{ListType, MapType, OptionalType, StructureType};
1335
use Flow\Types\Type\{Native\FloatType, Native\IntegerType, Native\UnionType, TypeFactory};
14-
use Flow\Types\Value\Uuid;
36+
use Flow\Types\Value\{HTMLDocument, Uuid};
1537

1638
/**
1739
* @template-covariant T
@@ -117,6 +139,14 @@ public static function fromArray(array $definition) : self
117139
);
118140
}
119141

142+
/**
143+
* @return Definition<HTMLDocument>
144+
*/
145+
public static function html(string|Reference $entry, bool $nullable = false, ?Metadata $metadata = null) : self
146+
{
147+
return new self($entry, type_html(), $nullable, $metadata);
148+
}
149+
120150
/**
121151
* @return Definition<int>
122152
*/

src/core/etl/src/Flow/ETL/Schema/Formatter/PHPSchemaFormatter.php

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
UuidType,
2121
XMLElementType,
2222
XMLType};
23+
use Flow\Types\Type\Logical\HTMLType;
2324
use Flow\Types\Type\Native\{BooleanType, EnumType, FloatType, IntegerType, StringType};
2425

2526
final readonly class PHPSchemaFormatter implements SchemaFormatter
@@ -174,16 +175,17 @@ private function mapType(Definition $definition) : string
174175
private function simpleType(Definition $definition) : string
175176
{
176177
$reflection = match ($definition->type()::class) {
177-
StringType::class => new \ReflectionFunction("\Flow\ETL\DSL\string_schema"),
178-
IntegerType::class => new \ReflectionFunction("\Flow\ETL\DSL\integer_schema"),
179-
BooleanType::class => new \ReflectionFunction("\Flow\ETL\DSL\bool_schema"),
180-
DateType::class => new \ReflectionFunction("\Flow\ETL\DSL\date_schema"),
181-
DateTimeType::class => new \ReflectionFunction("\Flow\ETL\DSL\datetime_schema"),
182-
TimeType::class => new \ReflectionFunction("\Flow\ETL\DSL\\time_schema"),
183-
JsonType::class => new \ReflectionFunction("\Flow\ETL\DSL\\json_schema"),
184-
UuidType::class => new \ReflectionFunction("\Flow\ETL\DSL\\uuid_schema"),
185-
XMLType::class => new \ReflectionFunction("\Flow\ETL\DSL\\xml_schema"),
186-
XMLElementType::class => new \ReflectionFunction("\Flow\ETL\DSL\\xml_element_schema"),
178+
StringType::class => new \ReflectionFunction('\Flow\ETL\DSL\string_schema'),
179+
IntegerType::class => new \ReflectionFunction('\Flow\ETL\DSL\integer_schema'),
180+
BooleanType::class => new \ReflectionFunction('\Flow\ETL\DSL\bool_schema'),
181+
DateType::class => new \ReflectionFunction('\Flow\ETL\DSL\date_schema'),
182+
DateTimeType::class => new \ReflectionFunction('\Flow\ETL\DSL\datetime_schema'),
183+
TimeType::class => new \ReflectionFunction('\Flow\ETL\DSL\time_schema'),
184+
JsonType::class => new \ReflectionFunction('\Flow\ETL\DSL\json_schema'),
185+
UuidType::class => new \ReflectionFunction('\Flow\ETL\DSL\uuid_schema'),
186+
XMLType::class => new \ReflectionFunction('\Flow\ETL\DSL\xml_schema'),
187+
XMLElementType::class => new \ReflectionFunction('\Flow\ETL\DSL\xml_element_schema'),
188+
HTMLType::class => new \ReflectionFunction('\Flow\ETL\DSL\html_schema'),
187189
default => throw new RuntimeException('Type ' . $definition->type()->toString() . ' is not a simple definition'),
188190
};
189191

src/core/etl/tests/Flow/ETL/Tests/Unit/Function/CastTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public static function cast_provider() : array
4545
'xml_to_array' => [$xml, 'array', ['root' => ['foo' => ['@attributes' => ['baz' => 'buz'], '@value' => 'bar']]]],
4646
'string_to_xml' => [$xmlString, 'xml', $xml],
4747
'xml_to_string' => [$xml, 'string', '<root><foo baz="buz">bar</foo></root>'],
48+
'full_xml_to_string' => [$fullXMLString, 'string', '<root><foo baz="buz">bar</foo></root>'],
4849
'datetime' => [new \DateTimeImmutable('2023-01-01 00:00:00 UTC'), 'string', '2023-01-01T00:00:00+00:00'],
4950
'datetime_to_date' => [new \DateTimeImmutable('2023-01-01 00:01:00 UTC'), 'date', new \DateTimeImmutable('2023-01-01T00:00:00+00:00')],
5051
'string_to_timezone' => ['UTC', 'timezone', new \DateTimeZone('UTC')],

src/core/etl/tests/Flow/ETL/Tests/Unit/Row/EntryFactoryTest.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ enum_schema,
3434
use function Flow\Types\DSL\{type_datetime, type_float, type_integer, type_list, type_map, type_string, type_structure, type_time_zone};
3535
use Flow\ETL\Exception\{InvalidArgumentException, SchemaDefinitionNotFoundException};
3636
use Flow\ETL\Row\Entry\TimeEntry;
37-
use Flow\ETL\Row\EntryFactory;
37+
use Flow\ETL\Row\{Entry, EntryFactory};
3838
use Flow\ETL\Schema\Metadata;
3939
use Flow\ETL\Tests\Fixtures\Enum\BackedIntEnum;
4040
use Flow\ETL\Tests\FlowTestCase;
@@ -43,6 +43,24 @@ enum_schema,
4343

4444
final class EntryFactoryTest extends FlowTestCase
4545
{
46+
public static function provide_recognized_data() : \Generator
47+
{
48+
yield 'json' => [
49+
$json = '{"id":1}',
50+
json_entry('e', $json),
51+
];
52+
53+
yield 'xml' => [
54+
$xml = '<root><foo>1</foo><bar>2</bar><baz>3</baz></root>',
55+
xml_entry('e', $xml),
56+
];
57+
58+
yield 'uuid' => [
59+
$uuid = '00000000-0000-0000-0000-000000000000',
60+
uuid_entry('e', $uuid),
61+
];
62+
}
63+
4664
public static function provide_unrecognized_data() : \Generator
4765
{
4866
yield 'json alike' => [
@@ -360,6 +378,18 @@ public function test_object() : void
360378
(new EntryFactory())->create('e', new \ArrayIterator([1, 2]));
361379
}
362380

381+
/**
382+
* @param Entry<mixed> $entry
383+
*/
384+
#[DataProvider('provide_recognized_data')]
385+
public function test_recognized_data_set_same_as_provided(string $input, Entry $entry) : void
386+
{
387+
self::assertEquals(
388+
$entry,
389+
(new EntryFactory())->create('e', $input)
390+
);
391+
}
392+
363393
public function test_string() : void
364394
{
365395
self::assertEquals(

src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/AutoCastTransformerTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
namespace Flow\ETL\Tests\Unit\Transformer;
66

77
use function Flow\ETL\DSL\{array_to_rows, flow_context};
8-
use Flow\ETL\Tests\FlowTestCase;
98
use Flow\ETL\Transformer\AutoCastTransformer;
109
use Flow\Types\Type\AutoCaster;
10+
use PHPUnit\Framework\TestCase;
1111

12-
final class AutoCastTransformerTest extends FlowTestCase
12+
final class AutoCastTransformerTest extends TestCase
1313
{
1414
public function test_transforming_row() : void
1515
{

src/lib/types/src/Flow/Types/DSL/functions.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
UuidType,
2727
XMLElementType,
2828
XMLType};
29+
use Flow\Types\Type\Logical\HTMLType;
2930
use Flow\Types\Type\Native\{ArrayType,
3031
BooleanType,
3132
CallableType,
@@ -39,7 +40,7 @@
3940
ResourceType,
4041
StringType,
4142
UnionType};
42-
use Flow\Types\Value\Uuid;
43+
use Flow\Types\Value\{HTMLDocument, Uuid};
4344
use UnitEnum;
4445

4546
/**
@@ -435,6 +436,15 @@ function type_literal(bool|float|int|string $value) : LiteralType
435436
return new LiteralType($value);
436437
}
437438

439+
/**
440+
* @return Type<HTMLDocument>
441+
*/
442+
#[DocumentationDSL(module: Module::TYPES, type: DSLType::TYPE)]
443+
function type_html() : Type
444+
{
445+
return new HTMLType();
446+
}
447+
438448
/**
439449
* @template T
440450
*
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Types\Type\Logical;
6+
7+
use Flow\Types\Exception\{CastingException, InvalidTypeException};
8+
use Flow\Types\Type;
9+
use Flow\Types\Value\HTMLDocument;
10+
11+
/**
12+
* @implements Type<HTMLDocument>
13+
*/
14+
final readonly class HTMLType implements Type
15+
{
16+
public function assert(mixed $value) : HTMLDocument
17+
{
18+
if ($this->isValid($value)) {
19+
return $value;
20+
}
21+
22+
throw InvalidTypeException::value($value, $this);
23+
}
24+
25+
public function cast(mixed $value) : HTMLDocument
26+
{
27+
if ($this->isValid($value)) {
28+
return $value;
29+
}
30+
31+
if (\is_string($value)) {
32+
return new HTMLDocument($value);
33+
}
34+
35+
if ($value instanceof \DOMDocument) {
36+
return new HTMLDocument($value);
37+
}
38+
39+
if (\is_object($value) && \is_a($value, 'Dom\HTMLDocument')) {
40+
return new HTMLDocument($value);
41+
}
42+
43+
throw new CastingException($value, $this);
44+
}
45+
46+
public function isValid(mixed $value) : bool
47+
{
48+
return $value instanceof HTMLDocument;
49+
}
50+
51+
public function normalize() : array
52+
{
53+
return [
54+
'type' => 'html',
55+
];
56+
}
57+
58+
public function toString() : string
59+
{
60+
return 'html';
61+
}
62+
}

src/lib/types/src/Flow/Types/Type/Logical/NonEmptyStringType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function cast(mixed $value) : string
5050
}
5151

5252
if ($value instanceof \DOMDocument) {
53-
return $this->assert($value->saveXML($value->documentElement) ?: '');
53+
return $this->assert(($value->saveXML($value->documentElement) ?: $value->saveHtml($value->documentElement)) ?: '');
5454
}
5555

5656
if ($value instanceof \DOMElement) {

0 commit comments

Comments
 (0)