PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / trunk
Secure Custom Fields vtrunk
6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / vendor / justinrainbow / json-schema / src / JsonSchema / Constraints / FormatConstraint.php
secure-custom-fields / vendor / justinrainbow / json-schema / src / JsonSchema / Constraints Last commit date
TypeCheck 8 months ago BaseConstraint.php 8 months ago CollectionConstraint.php 8 months ago Constraint.php 8 months ago ConstraintInterface.php 8 months ago EnumConstraint.php 8 months ago Factory.php 8 months ago FormatConstraint.php 8 months ago NumberConstraint.php 8 months ago ObjectConstraint.php 8 months ago SchemaConstraint.php 8 months ago StringConstraint.php 8 months ago TypeConstraint.php 8 months ago UndefinedConstraint.php 8 months ago
FormatConstraint.php
215 lines
1 <?php
2
3 /*
4 * This file is part of the JsonSchema package.
5 *
6 * For the full copyright and license information, please view the LICENSE
7 * file that was distributed with this source code.
8 */
9
10 namespace JsonSchema\Constraints;
11
12 use JsonSchema\Entity\JsonPointer;
13 use JsonSchema\Rfc3339;
14
15 /**
16 * Validates against the "format" property
17 *
18 * @author Justin Rainbow <justin.rainbow@gmail.com>
19 *
20 * @see http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.23
21 */
22 class FormatConstraint extends Constraint
23 {
24 /**
25 * {@inheritdoc}
26 */
27 public function check(&$element, $schema = null, ?JsonPointer $path = null, $i = null)
28 {
29 if (!isset($schema->format) || $this->factory->getConfig(self::CHECK_MODE_DISABLE_FORMAT)) {
30 return;
31 }
32
33 switch ($schema->format) {
34 case 'date':
35 if (!$date = $this->validateDateTime($element, 'Y-m-d')) {
36 $this->addError($path, sprintf('Invalid date %s, expected format YYYY-MM-DD', json_encode($element)), 'format', array('format' => $schema->format));
37 }
38 break;
39
40 case 'time':
41 if (!$this->validateDateTime($element, 'H:i:s')) {
42 $this->addError($path, sprintf('Invalid time %s, expected format hh:mm:ss', json_encode($element)), 'format', array('format' => $schema->format));
43 }
44 break;
45
46 case 'date-time':
47 if (null === Rfc3339::createFromString($element)) {
48 $this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element)), 'format', array('format' => $schema->format));
49 }
50 break;
51
52 case 'utc-millisec':
53 if (!$this->validateDateTime($element, 'U')) {
54 $this->addError($path, sprintf('Invalid time %s, expected integer of milliseconds since Epoch', json_encode($element)), 'format', array('format' => $schema->format));
55 }
56 break;
57
58 case 'regex':
59 if (!$this->validateRegex($element)) {
60 $this->addError($path, 'Invalid regex format ' . $element, 'format', array('format' => $schema->format));
61 }
62 break;
63
64 case 'color':
65 if (!$this->validateColor($element)) {
66 $this->addError($path, 'Invalid color', 'format', array('format' => $schema->format));
67 }
68 break;
69
70 case 'style':
71 if (!$this->validateStyle($element)) {
72 $this->addError($path, 'Invalid style', 'format', array('format' => $schema->format));
73 }
74 break;
75
76 case 'phone':
77 if (!$this->validatePhone($element)) {
78 $this->addError($path, 'Invalid phone number', 'format', array('format' => $schema->format));
79 }
80 break;
81
82 case 'uri':
83 if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) {
84 $this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format));
85 }
86 break;
87
88 case 'uriref':
89 case 'uri-reference':
90 if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) {
91 // FILTER_VALIDATE_URL does not conform to RFC-3986, and cannot handle relative URLs, but
92 // the json-schema spec uses RFC-3986, so need a bit of hackery to properly validate them.
93 // See https://tools.ietf.org/html/rfc3986#section-4.2 for additional information.
94 if (substr($element, 0, 2) === '//') { // network-path reference
95 $validURL = filter_var('scheme:' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE);
96 } elseif (substr($element, 0, 1) === '/') { // absolute-path reference
97 $validURL = filter_var('scheme://host' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE);
98 } elseif (strlen($element)) { // relative-path reference
99 $pathParts = explode('/', $element, 2);
100 if (strpos($pathParts[0], ':') !== false) {
101 $validURL = null;
102 } else {
103 $validURL = filter_var('scheme://host/' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE);
104 }
105 } else {
106 $validURL = null;
107 }
108 if ($validURL === null) {
109 $this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format));
110 }
111 }
112 break;
113
114 case 'email':
115 $filterFlags = FILTER_NULL_ON_FAILURE;
116 if (defined('FILTER_FLAG_EMAIL_UNICODE')) {
117 // Only available from PHP >= 7.1.0, so ignore it for coverage checks
118 $filterFlags |= constant('FILTER_FLAG_EMAIL_UNICODE'); // @codeCoverageIgnore
119 }
120 if (null === filter_var($element, FILTER_VALIDATE_EMAIL, $filterFlags)) {
121 $this->addError($path, 'Invalid email', 'format', array('format' => $schema->format));
122 }
123 break;
124
125 case 'ip-address':
126 case 'ipv4':
127 if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV4)) {
128 $this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format));
129 }
130 break;
131
132 case 'ipv6':
133 if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV6)) {
134 $this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format));
135 }
136 break;
137
138 case 'host-name':
139 case 'hostname':
140 if (!$this->validateHostname($element)) {
141 $this->addError($path, 'Invalid hostname', 'format', array('format' => $schema->format));
142 }
143 break;
144
145 default:
146 // Empty as it should be:
147 // The value of this keyword is called a format attribute. It MUST be a string.
148 // A format attribute can generally only validate a given set of instance types.
149 // If the type of the instance to validate is not in this set, validation for
150 // this format attribute and instance SHOULD succeed.
151 // http://json-schema.org/latest/json-schema-validation.html#anchor105
152 break;
153 }
154 }
155
156 protected function validateDateTime($datetime, $format)
157 {
158 $dt = \DateTime::createFromFormat($format, $datetime);
159
160 if (!$dt) {
161 return false;
162 }
163
164 if ($datetime === $dt->format($format)) {
165 return true;
166 }
167
168 // handles the case where a non-6 digit microsecond datetime is passed
169 // which will fail the above string comparison because the passed
170 // $datetime may be '2000-05-01T12:12:12.123Z' but format() will return
171 // '2000-05-01T12:12:12.123000Z'
172 if ((strpos('u', $format) !== -1) && (preg_match('/\.\d+Z$/', $datetime))) {
173 return true;
174 }
175
176 return false;
177 }
178
179 protected function validateRegex($regex)
180 {
181 return false !== @preg_match('/' . $regex . '/u', '');
182 }
183
184 protected function validateColor($color)
185 {
186 if (in_array(strtolower($color), array('aqua', 'black', 'blue', 'fuchsia',
187 'gray', 'green', 'lime', 'maroon', 'navy', 'olive', 'orange', 'purple',
188 'red', 'silver', 'teal', 'white', 'yellow'))) {
189 return true;
190 }
191
192 return preg_match('/^#([a-f0-9]{3}|[a-f0-9]{6})$/i', $color);
193 }
194
195 protected function validateStyle($style)
196 {
197 $properties = explode(';', rtrim($style, ';'));
198 $invalidEntries = preg_grep('/^\s*[-a-z]+\s*:\s*.+$/i', $properties, PREG_GREP_INVERT);
199
200 return empty($invalidEntries);
201 }
202
203 protected function validatePhone($phone)
204 {
205 return preg_match('/^\+?(\(\d{3}\)|\d{3}) \d{3} \d{4}$/', $phone);
206 }
207
208 protected function validateHostname($host)
209 {
210 $hostnameRegex = '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/i';
211
212 return preg_match($hostnameRegex, $host);
213 }
214 }
215