-
Notifications
You must be signed in to change notification settings - Fork 379
Expand file tree
/
Copy pathValidationExemption.php
More file actions
223 lines (204 loc) · 6.66 KB
/
Copy pathValidationExemption.php
File metadata and controls
223 lines (204 loc) · 6.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
<?php
/**
* Class ValidationExemption.
*
* @package AmpProject\AmpWP
*/
namespace AmpProject\AmpWP;
use AmpProject\Dom\Document;
use AmpProject\Dom\Element;
use DOMAttr;
use DOMNode;
/**
* Helper functionality to deal with validation exemptions.
*
* @todo This could be made into a Service that contains a collection of nodes which were PX-verified or AMP-unvalidated. In this way, there would be no need to add attributes to DOM nodes, and non-element/attribute nodes could be marked, and it would be faster to see if there are any such exempted nodes.
*
* @package AmpProject\AmpWP
* @since 2.2
* @internal
* @see \AmpProject\DevMode
*/
final class ValidationExemption {
/**
* HTML attribute to indicate one or more attributes have been verified for PX from AMP validation.
*
* @var string
*/
const PX_VERIFIED_ATTRS_ATTRIBUTE = 'data-px-verified-attrs';
/**
* HTML attribute to indicate an tag/element has been verified for PX.
*
* The difference here with `data-amp-unvalidated-tag` is that the PX-verified means that the tag will work
* properly with CSS tree shaking.
*
* @var string
*/
const PX_VERIFIED_TAG_ATTRIBUTE = 'data-px-verified-tag';
/**
* HTML attribute to indicate one or more attributes are exempted from AMP validation.
*
* @var string
*/
const AMP_UNVALIDATED_ATTRS_ATTRIBUTE = 'data-amp-unvalidated-attrs';
/**
* HTML attribute to indicate an tag/element is exempted from AMP validation.
*
* @var string
*/
const AMP_UNVALIDATED_TAG_ATTRIBUTE = 'data-amp-unvalidated-tag';
/**
* Check whether PX is verified for node.
*
* This means that it is exempted from AMP validation, but it doesn't mean the PX is negatively impacted.
*
* @param DOMNode $node Node.
* @return bool Whether node marked as being PX-verified.
*/
public static function is_px_verified_for_node( DOMNode $node ) {
if ( $node instanceof Element ) {
return $node->hasAttribute( self::PX_VERIFIED_TAG_ATTRIBUTE );
} elseif ( $node instanceof DOMAttr ) {
return self::check_for_attribute_token_list_membership( $node, self::PX_VERIFIED_ATTRS_ATTRIBUTE );
}
return false;
}
/**
* Mark node as being PX-verified.
*
* @param DOMNode $node Node.
* @return bool Whether successful.
*/
public static function mark_node_as_px_verified( DOMNode $node ) {
return self::mark_node_with_exemption_attribute(
$node,
self::PX_VERIFIED_TAG_ATTRIBUTE,
self::PX_VERIFIED_ATTRS_ATTRIBUTE
);
}
/**
* Mark node as not being AMP-validated.
*
* @param DOMNode $node Node.
* @return bool Whether successful.
*/
public static function mark_node_as_amp_unvalidated( DOMNode $node ) {
return self::mark_node_with_exemption_attribute(
$node,
self::AMP_UNVALIDATED_TAG_ATTRIBUTE,
self::AMP_UNVALIDATED_ATTRS_ATTRIBUTE
);
}
/**
* Mark node with exemption attribute.
*
* @param DOMNode $node Node.
* @param string $tag_attribute_name Tag attribute name.
* @param string $attrs_attribute_name Attributes attribute name.
* @return bool
*/
private static function mark_node_with_exemption_attribute( DOMNode $node, $tag_attribute_name, $attrs_attribute_name ) {
if ( $node instanceof Element ) {
if ( ! $node->hasAttribute( $tag_attribute_name ) ) {
if ( null === $node->ownerDocument ) {
return false; // @codeCoverageIgnore
}
$node->setAttributeNode( $node->ownerDocument->createAttribute( $tag_attribute_name ) );
}
return true;
} elseif ( $node instanceof DOMAttr ) {
$element = $node->parentNode;
if ( ! $element instanceof Element ) {
return false; // @codeCoverageIgnore
}
$attr_value = $element->getAttribute( $attrs_attribute_name );
if ( $attr_value ) {
$attr_value .= ' ' . $node->nodeName;
} else {
$attr_value = $node->nodeName;
}
$element->setAttribute( $attrs_attribute_name, $attr_value );
return true;
}
return false;
}
/**
* Check whether AMP is unvalidated for node.
*
* This means that it is exempted from AMP validation and it may negatively impact PX.
*
* @param DOMNode $node Node.
* @return bool Whether node marked as being AMP-unvalidated.
*/
public static function is_amp_unvalidated_for_node( DOMNode $node ) {
if ( $node instanceof Element ) {
return $node->hasAttribute( self::AMP_UNVALIDATED_TAG_ATTRIBUTE );
} elseif ( $node instanceof DOMAttr ) {
return self::check_for_attribute_token_list_membership( $node, self::AMP_UNVALIDATED_ATTRS_ATTRIBUTE );
}
return false;
}
/**
* Check whether the given attribute node is mentioned among the token list of another supplied attribute.
*
* @param DOMAttr $attr Attribute node to check for.
* @param string $token_list_attr_name Attribute name that has the token list value.
*
* @return bool Whether membership is present.
*/
private static function check_for_attribute_token_list_membership( DOMAttr $attr, $token_list_attr_name ) {
$element = $attr->parentNode;
if ( ! $element instanceof Element ) {
return false; // @codeCoverageIgnore
}
if ( ! $element->hasAttribute( $token_list_attr_name ) ) {
return false;
}
return in_array(
$attr->nodeName,
preg_split( '/\s+/', $element->getAttribute( $token_list_attr_name ) ),
true
);
}
/**
* Check whether the provided document has nodes which are not AMP-validated.
*
* @param Document $document Document for which to check whether dev mode is active.
* @return bool Whether the document is in dev mode.
*/
public static function is_document_with_amp_unvalidated_nodes( Document $document ) {
return self::is_document_containing_attributes( $document, [ self::AMP_UNVALIDATED_TAG_ATTRIBUTE, self::AMP_UNVALIDATED_ATTRS_ATTRIBUTE ] );
}
/**
* Check whether the provided document has nodes which are PX-verified.
*
* @param Document $document Document for which to check whether dev mode is active.
* @return bool Whether the document is in dev mode.
*/
public static function is_document_with_px_verified_nodes( Document $document ) {
return self::is_document_containing_attributes( $document, [ self::PX_VERIFIED_TAG_ATTRIBUTE, self::PX_VERIFIED_ATTRS_ATTRIBUTE ] );
}
/**
* Check whether a document contains the given attribute names.
*
* @param Document $document Document.
* @param string[] $attribute_names Attribute names.
* @return bool Whether attributes exist in the document.
*/
private static function is_document_containing_attributes( Document $document, $attribute_names ) {
return $document->xpath->query(
sprintf(
'//*[%s]',
join(
' or ',
array_map(
static function ( $attr_name ) {
return "@{$attr_name}";
},
$attribute_names
)
)
)
)->length > 0;
}
}