Skip to content

Commit ba0edf5

Browse files
committed
Add a new HTMLType
1 parent b59fdb7 commit ba0edf5

11 files changed

Lines changed: 326 additions & 14 deletions

File tree

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,16 @@ 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\{type_date,
24+
type_datetime,
25+
type_html,
26+
type_json,
27+
type_optional,
28+
type_string,
29+
type_time,
30+
type_uuid,
31+
type_xml,
32+
type_xml_element};
2433
use Flow\ETL\Exception\{InvalidArgumentException,
2534
RuntimeException,
2635
SchemaDefinitionNotFoundException};
@@ -96,6 +105,10 @@ public function create(string $entryName, mixed $value, Schema|Definition|null $
96105
$valueType = type_uuid();
97106
}
98107

108+
if ($stringChecker->isHTML()) {
109+
$valueType = type_html();
110+
}
111+
99112
if ($stringChecker->isXML()) {
100113
$valueType = type_xml();
101114
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
UuidType,
2626
XMLElementType,
2727
XMLType};
28+
use Flow\Types\Type\Logical\HTMLType;
2829
use Flow\Types\Type\Native\{ArrayType,
2930
BooleanType,
3031
CallableType,
@@ -425,6 +426,15 @@ function type_literal(bool|float|int|string $value) : LiteralType
425426
return new LiteralType($value);
426427
}
427428

429+
/**
430+
* @return Type<\DOMDocument>
431+
*/
432+
#[DocumentationDSL(module: Module::TYPES, type: DSLType::TYPE)]
433+
function type_html() : Type
434+
{
435+
return new HTMLType();
436+
}
437+
428438
/**
429439
* @template T
430440
*

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
type_date,
1010
type_datetime,
1111
type_float,
12+
type_html,
1213
type_integer,
1314
type_json,
14-
type_uuid};
15+
type_uuid,
16+
type_xml};
1517
use Flow\Types\Type\Native\String\StringTypeChecker;
1618

1719
final readonly class AutoCaster
@@ -95,6 +97,14 @@ private function castToString(string $value) : mixed
9597
return type_datetime()->cast($value);
9698
}
9799

100+
if ($typeChecker->isHTML()) {
101+
return type_html()->cast($value);
102+
}
103+
104+
if ($typeChecker->isXML()) {
105+
return type_xml()->cast($value);
106+
}
107+
98108
return $value;
99109
}
100110
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Types\Type\Logical;
6+
7+
use function Flow\Types\DSL\type_string;
8+
use Flow\Types\Exception\{CastingException, InvalidTypeException};
9+
use Flow\Types\Type;
10+
11+
/**
12+
* @implements Type<\DOMDocument>
13+
*/
14+
final readonly class HTMLType implements Type
15+
{
16+
public function assert(mixed $value) : \DOMDocument
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) : \DOMDocument
26+
{
27+
if ($this->isValid($value)) {
28+
return $value;
29+
}
30+
31+
if (\is_string($value)) {
32+
return $this->htmlDocument($value);
33+
}
34+
35+
try {
36+
$stringValue = type_string()->cast($value);
37+
38+
return $this->htmlDocument($stringValue);
39+
} catch (CastingException $e) {
40+
throw new CastingException($value, $this, $e);
41+
}
42+
}
43+
44+
public function isValid(mixed $value) : bool
45+
{
46+
return $value instanceof \DOMDocument;
47+
}
48+
49+
public function normalize() : array
50+
{
51+
return [
52+
'type' => 'html',
53+
];
54+
}
55+
56+
public function toString() : string
57+
{
58+
return 'html';
59+
}
60+
61+
private function htmlDocument(string $value) : \DOMDocument
62+
{
63+
$doc = new \DOMDocument();
64+
65+
if (!@$doc->loadHTML($value, \LIBXML_HTML_NOIMPLIED | \LIBXML_HTML_NODEFDTD)) {
66+
throw new CastingException($value, $this);
67+
}
68+
69+
return $doc;
70+
}
71+
}

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) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace Flow\Types\Type\Logical;
66

7-
use function Flow\Types\DSL\{type_string, type_xml};
7+
use function Flow\Types\DSL\type_string;
88
use Flow\Types\Exception\{CastingException, InvalidTypeException};
99
use Flow\Types\Type;
1010

@@ -32,7 +32,7 @@ public function cast(mixed $value) : \DOMDocument
3232
$doc = new \DOMDocument();
3333

3434
if (!@$doc->loadXML($value)) {
35-
throw new CastingException($value, type_xml());
35+
throw new CastingException($value, $this);
3636
}
3737

3838
return $doc;

src/lib/types/src/Flow/Types/Type/Native/String/StringTypeChecker.php

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,36 @@ public function isFloat() : bool
108108
return \is_numeric($this->string) && \str_contains($this->string, '.');
109109
}
110110

111+
public function isHTML() : bool
112+
{
113+
if ($this->string === '') {
114+
return false;
115+
}
116+
117+
if ('<' !== $this->string[0]) {
118+
return false;
119+
}
120+
121+
if (\preg_match('/<(.+?)>(.+?)<\/(.+?)>/', $this->string) === 1) {
122+
try {
123+
\libxml_use_internal_errors(true);
124+
125+
$doc = new \DOMDocument();
126+
127+
if (!@$doc->loadHTML($this->string, \LIBXML_HTML_NOIMPLIED | \LIBXML_HTML_NODEFDTD)) {
128+
return true;
129+
}
130+
} catch (\Exception) {
131+
return false;
132+
} finally {
133+
\libxml_clear_errors(); // Clear any errors if needed
134+
\libxml_use_internal_errors(false); // Restore standard error handling
135+
}
136+
}
137+
138+
return false;
139+
}
140+
111141
public function isInteger() : bool
112142
{
113143
if ($this->string === '') {
@@ -182,16 +212,14 @@ public function isXML() : bool
182212
\libxml_use_internal_errors(true);
183213

184214
$doc = new \DOMDocument();
185-
$result = $doc->loadXML($this->string);
186-
\libxml_clear_errors(); // Clear any errors if needed
187-
\libxml_use_internal_errors(false); // Restore standard error handling
215+
$result = @$doc->loadXML($this->string);
188216

189217
return (bool) $result;
190218
} catch (\Exception) {
219+
return false;
220+
} finally {
191221
\libxml_clear_errors(); // Clear any errors if needed
192222
\libxml_use_internal_errors(false); // Restore standard error handling
193-
194-
return false;
195223
}
196224
}
197225

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,24 @@
44

55
namespace Flow\Types\Type;
66

7-
use function Flow\Types\DSL\{type_array, type_boolean, type_date, type_datetime, type_enum, type_float, type_instance_of, type_integer, type_json, type_map, type_null, type_string, type_time, type_uuid, type_xml, type_xml_element, types};
7+
use function Flow\Types\DSL\{type_array,
8+
type_boolean,
9+
type_date,
10+
type_datetime,
11+
type_enum,
12+
type_float,
13+
type_html,
14+
type_instance_of,
15+
type_integer,
16+
type_json,
17+
type_map,
18+
type_null,
19+
type_string,
20+
type_time,
21+
type_uuid,
22+
type_xml,
23+
type_xml_element,
24+
types};
825
use Flow\Types\Exception\InvalidArgumentException;
926
use Flow\Types\Type;
1027
use Flow\Types\Type\Logical\{ListType, StructureType};
@@ -46,8 +63,8 @@ public function detectType(mixed $value) : Type
4663
}
4764

4865
$detector = new ArrayContentDetector(
49-
$keyTypes = types(...\array_map($this->detectType(...), \array_keys($value)))->deduplicate(),
50-
$valueTypes = types(...\array_map($this->detectType(...), \array_values($value)))->deduplicate(),
66+
types(...\array_map($this->detectType(...), \array_keys($value)))->deduplicate(),
67+
types(...\array_map($this->detectType(...), \array_values($value)))->deduplicate(),
5168
\array_is_list($value)
5269
);
5370

@@ -94,6 +111,10 @@ public function detectType(mixed $value) : Type
94111
return type_datetime();
95112
}
96113

114+
if (type_html()->isValid($value)) {
115+
return type_html();
116+
}
117+
97118
if (type_xml()->isValid($value)) {
98119
return type_xml();
99120
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
type_date,
1111
type_datetime,
1212
type_float,
13+
type_html,
1314
type_integer,
1415
type_json,
1516
type_mixed,
@@ -79,6 +80,7 @@ public static function fromArray(array $data) : Type
7980
'scalar' => type_scalar(),
8081
'mixed' => type_mixed(),
8182
'numeric-string' => type_numeric_string(),
83+
'html' => type_html(),
8284
default => throw new InvalidArgumentException("Unknown type '" . (\is_string($data['type']) ? $data['type'] : \gettype($data['type'])) . "'"),
8385
};
8486
}

0 commit comments

Comments
 (0)