Skip to content

Commit 7329ac6

Browse files
committed
Add a new HTMLType
1 parent e9b025b commit 7329ac6

9 files changed

Lines changed: 113 additions & 101 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: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,14 @@ enum_entry,
2020
uuid_entry,
2121
xml_element_entry,
2222
xml_entry};
23-
use function Flow\Types\DSL\{type_date,
24-
type_datetime,
23+
use function Flow\Types\DSL\{
2524
type_html,
2625
type_json,
2726
type_optional,
2827
type_string,
29-
type_time,
3028
type_uuid,
31-
type_xml,
32-
type_xml_element};
29+
type_xml
30+
};
3331
use Flow\ETL\Exception\{InvalidArgumentException,
3432
RuntimeException,
3533
SchemaDefinitionNotFoundException};
@@ -63,7 +61,6 @@ enum_entry,
6361
};
6462
use Flow\Types\Type\Native\String\StringTypeChecker;
6563
use Flow\Types\Type\TypeDetector;
66-
use Flow\Types\Value\Uuid;
6764

6865
final readonly class EntryFactory
6966
{
@@ -109,30 +106,6 @@ public function create(string $entryName, mixed $value, Schema|Definition|null $
109106
}
110107
}
111108

112-
if ($valueType instanceof InstanceOfType) {
113-
if ($valueType->class === \DOMDocument::class) {
114-
$valueType = type_xml();
115-
} elseif ($valueType->class === \DOMElement::class) {
116-
$valueType = type_xml_element();
117-
} elseif ($valueType->class === \DateInterval::class) {
118-
$valueType = type_time();
119-
} elseif (\in_array($valueType->class, [\DateTimeImmutable::class, \DateTimeInterface::class, \DateTime::class], true)) {
120-
if ($value instanceof \DateTimeInterface && $value->format('H:i:s') === '00:00:00') {
121-
$valueType = type_date();
122-
} else {
123-
$valueType = type_datetime();
124-
}
125-
} else {
126-
foreach (['Ramsey\Uuid\UuidInterface', Uuid::class, 'Symfony\Component\Uid\Uuid'] as $uuidClass) {
127-
if (\is_a($valueType->class, $uuidClass, true)) {
128-
$valueType = type_uuid();
129-
130-
break;
131-
}
132-
}
133-
}
134-
}
135-
136109
return $this->createAs($entryName, $value, $valueType);
137110
}
138111

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/Transformer/AutoCastTransformerTest.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@ public function test_transforming_row() : void
1515
{
1616
$transformer = new AutoCastTransformer(new AutoCaster());
1717

18-
$xml = new \DOMDocument();
19-
$xml->loadXML($xmlString = '<root><foo>bar</foo></root>');
20-
21-
$html = new \DOMDocument();
22-
$html->loadXML($htmlString = '<html><body><div><span>1</span></div></body></html>');
23-
2418
$rows = array_to_rows([
2519
[
2620
'integer' => '1',
@@ -30,8 +24,6 @@ public function test_transforming_row() : void
3024
'datetime' => '2021-01-01 00:00:00',
3125
'null' => 'null',
3226
'nil' => 'nil',
33-
'xml' => $xmlString,
34-
'html' => $htmlString,
3527
],
3628
]);
3729

@@ -45,8 +37,6 @@ public function test_transforming_row() : void
4537
'datetime' => new \DateTimeImmutable('2021-01-01 00:00:00'),
4638
'null' => null,
4739
'nil' => null,
48-
'xml' => $xml,
49-
'html' => $html,
5040
],
5141
],
5242
$transformer->transform($rows, flow_context())->toArray()

src/lib/types/src/Flow/Types/Type/AutoCaster.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@
99
type_date,
1010
type_datetime,
1111
type_float,
12-
type_html,
1312
type_integer,
1413
type_json,
15-
type_uuid,
16-
type_xml};
14+
type_uuid};
1715
use Flow\Types\Type\Native\String\StringTypeChecker;
1816

1917
final readonly class AutoCaster
@@ -97,14 +95,6 @@ private function castToString(string $value) : mixed
9795
return type_datetime()->cast($value);
9896
}
9997

100-
if ($typeChecker->isHTML()) {
101-
return type_html()->cast($value);
102-
}
103-
104-
if ($typeChecker->isXML()) {
105-
return type_xml()->cast($value);
106-
}
107-
10898
return $value;
10999
}
110100
}

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

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44

55
namespace Flow\Types\Type\Logical;
66

7-
use function Flow\Types\DSL\type_string;
87
use Flow\Types\Exception\{CastingException, InvalidTypeException};
98
use Flow\Types\Type;
9+
use Flow\Types\Value\HTMLDocument;
1010

1111
/**
12-
* @implements Type<\DOMDocument>
12+
* @implements Type<HTMLDocument>
1313
*/
1414
final readonly class HTMLType implements Type
1515
{
16-
public function assert(mixed $value) : \DOMDocument
16+
public function assert(mixed $value) : HTMLDocument
1717
{
1818
if ($this->isValid($value)) {
1919
return $value;
@@ -22,42 +22,30 @@ public function assert(mixed $value) : \DOMDocument
2222
throw InvalidTypeException::value($value, $this);
2323
}
2424

25-
public function cast(mixed $value) : \DOMDocument
25+
public function cast(mixed $value) : HTMLDocument
2626
{
2727
if ($this->isValid($value)) {
2828
return $value;
2929
}
3030

31-
$document = new \DOMDocument();
32-
3331
if (\is_string($value)) {
34-
if (!$this->isHtml($document, $value)) {
35-
throw new CastingException($value, $this);
36-
}
37-
38-
return $document;
32+
return new HTMLDocument($value);
3933
}
4034

41-
try {
42-
$stringValue = type_string()->cast($value);
43-
44-
if (!$this->isHtml($document, $stringValue)) {
45-
throw new CastingException($stringValue, $this);
46-
}
35+
if ($value instanceof \DOMDocument) {
36+
return new HTMLDocument($value);
37+
}
4738

48-
return $document;
49-
} catch (CastingException $e) {
50-
throw new CastingException($value, $this, $e);
39+
if (\is_object($value) && \is_a($value, 'Dom\HTMLDocument')) {
40+
return new HTMLDocument($value);
5141
}
42+
43+
throw new CastingException($value, $this);
5244
}
5345

5446
public function isValid(mixed $value) : bool
5547
{
56-
if ($value instanceof \DOMDocument) {
57-
return $this->isHtml($value, (string) $value->saveHTML());
58-
}
59-
60-
return false;
48+
return $value instanceof HTMLDocument;
6149
}
6250

6351
public function normalize() : array
@@ -71,13 +59,4 @@ public function toString() : string
7159
{
7260
return 'html';
7361
}
74-
75-
private function isHtml(\DOMDocument $document, string $value) : bool
76-
{
77-
if (\preg_match('/(<!doctype(.+?)>)?<html(.+?)>(.+?)<\/html>/im', $value) !== 1) {
78-
return false;
79-
}
80-
81-
return (bool) @$document->loadHTML($value, \LIBXML_HTML_NOIMPLIED | \LIBXML_HTML_NODEFDTD);
82-
}
8362
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Types\Value;
6+
7+
final class HTMLDocument implements \Stringable
8+
{
9+
private string $value;
10+
11+
public function __construct(string|\DOMDocument|\Dom\HTMLDocument $value)
12+
{
13+
if (\is_string($value)) {
14+
if (\class_exists(\Dom\HTMLDocument::class)) {
15+
$document = \Dom\HTMLDocument::createFromString($value);
16+
17+
$this->value = $document->saveHTML();
18+
} else {
19+
$document = new \DOMDocument();
20+
$document->loadHTML($value, \LIBXML_HTML_NOIMPLIED | \LIBXML_HTML_NODEFDTD);
21+
22+
$this->value = $document->saveHTML();
23+
}
24+
} else {
25+
$this->value = $value->saveHtml();
26+
}
27+
}
28+
29+
public static function fromString(string $value) : self
30+
{
31+
return new self($value);
32+
}
33+
34+
public function __toString() : string
35+
{
36+
return '';
37+
}
38+
39+
public function isEqual(self $type) : bool
40+
{
41+
return $this->toString() === $type->toString();
42+
}
43+
44+
public function toString() : string
45+
{
46+
return $this->value;
47+
}
48+
}

src/lib/types/tests/Flow/Types/Tests/Unit/Type/TypeDetectorTest.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
use Flow\Types\Type\Logical\HTMLType;
2222
use Flow\Types\Type\Native\{ArrayType, EnumType, NullType, StringType};
2323
use Flow\Types\Type\TypeDetector;
24-
use Flow\Types\Value\Uuid;
24+
use Flow\Types\Value\{HTMLDocument, Uuid};
2525
use PHPUnit\Framework\Attributes\DataProvider;
2626
use PHPUnit\Framework\TestCase;
2727

@@ -85,10 +85,8 @@ public static function provide_logical_types_data() : \Generator
8585
'xml_element',
8686
];
8787

88-
$html = new \DOMDocument();
89-
$html->loadHTML('<div><span>1</span></div>');
9088
yield 'html' => [
91-
$html,
89+
HTMLDocument::fromString('<html><div><span>1</span></div></html>'),
9290
HTMLType::class,
9391
'html',
9492
];

0 commit comments

Comments
 (0)