PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.2
MailPoet – Newsletters, Email Marketing, and Automation v5.34.2
5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / Validator / Validator.php
mailpoet / lib / Validator Last commit date
Schema 3 years ago Builder.php 1 year ago Schema.php 8 months ago ValidationException.php 4 years ago Validator.php 3 years ago index.php 3 years ago
Validator.php
213 lines
1 <?php declare(strict_types = 1);
2
3 namespace MailPoet\Validator;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use JsonSerializable;
9 use MailPoet\WP\Functions as WPFunctions;
10 use stdClass;
11 use WP_Error;
12
13 class Validator {
14 /** @var WPFunctions */
15 private $wp;
16
17 public function __construct(
18 WPFunctions $wp
19 ) {
20 $this->wp = $wp;
21 }
22
23 /**
24 * Strict validation & sanitization implementation.
25 * It only coerces int to float (e.g. 5 to 5.0).
26 *
27 * @param mixed $value
28 * @return mixed
29 */
30 public function validate(Schema $schema, $value, string $paramName = 'value') {
31 return $this->validateSchemaArray($schema->toArray(), $value, $paramName);
32 }
33
34 /**
35 * Strict validation & sanitization implementation.
36 * It only coerces int to float (e.g. 5 to 5.0).
37 *
38 * @param array $schema. The array must follow the format, which is returned from Schema::toArray().
39 * @param mixed $value
40 * @return mixed
41 */
42 public function validateSchemaArray(array $schema, $value, string $paramName = 'value') {
43 $result = $this->validateAndSanitizeValueFromSchema($value, $schema, $paramName);
44 if ($result instanceof WP_Error) {
45 throw ValidationException::createFromWpError($result);
46 }
47 return $result;
48 }
49
50 /**
51 * Mirrors rest_validate_value_from_schema() and rest_sanitize_value_from_schema().
52 *
53 * @param mixed $value
54 * @param array $schema
55 * @param string $paramName
56 * @return mixed|WP_Error
57 */
58 private function validateAndSanitizeValueFromSchema($value, array $schema, string $paramName) {
59 // nullable
60 $fullType = $schema['type'] ?? null;
61 if (is_array($fullType) && in_array('null', $fullType, true) && $value === null) {
62 return null;
63 }
64
65 // anyOf, oneOf
66 if (isset($schema['anyOf'])) {
67 return $this->validateAndSanitizeAnyOf($value, $schema, $paramName);
68 } elseif (isset($schema['oneOf'])) {
69 return $this->validateAndSanitizeOneOf($value, $schema, $paramName);
70 }
71
72 // make types strict
73 $type = is_array($fullType) ? $fullType[0] : $fullType;
74 switch ($type) {
75 case 'number':
76 if (!is_float($value) && !is_int($value)) {
77 return $this->getTypeError($paramName, $fullType);
78 }
79 break;
80 case 'integer':
81 if (!is_int($value)) {
82 return $this->getTypeError($paramName, $fullType);
83 }
84 break;
85 case 'boolean':
86 if (!is_bool($value)) {
87 return $this->getTypeError($paramName, $fullType);
88 }
89 break;
90 case 'array':
91 if (!is_array($value)) {
92 return $this->getTypeError($paramName, $fullType);
93 }
94
95 if (isset($schema['items'])) {
96 foreach ($value as $i => $v) {
97 $result = $this->validateAndSanitizeValueFromSchema($v, $schema['items'], $paramName . '[' . $i . ']');
98 if ($this->wp->isWpError($result)) {
99 return $result;
100 }
101 }
102 }
103 break;
104 case 'object':
105 if (!is_array($value) && !$value instanceof stdClass && !$value instanceof JsonSerializable) {
106 return $this->getTypeError($paramName, $fullType);
107 }
108
109 // ensure string keys
110 $value = (array)($value instanceof JsonSerializable ? $value->jsonSerialize() : $value);
111 if (count(array_filter(array_keys($value), 'is_string')) !== count($value)) {
112 return $this->getTypeError($paramName, $fullType);
113 }
114
115 // validate object properties
116 foreach ($value as $k => $v) {
117 if (isset($schema['properties'][$k])) {
118 $result = $this->validateAndSanitizeValueFromSchema($v, $schema['properties'][$k], $paramName . '[' . $k . ']');
119 if ($this->wp->isWpError($result)) {
120 return $result;
121 }
122 continue;
123 }
124
125 $patternPropertySchema = $this->wp->restFindMatchingPatternPropertySchema($k, $schema);
126 if ($patternPropertySchema) {
127 $result = $this->validateAndSanitizeValueFromSchema($v, $patternPropertySchema, $paramName . '[' . $k . ']');
128 if ($this->wp->isWpError($result)) {
129 return $result;
130 }
131 continue;
132 }
133
134 if (isset($schema['additionalProperties']) && is_array($schema['additionalProperties'])) {
135 $result = $this->validateAndSanitizeValueFromSchema($v, $schema['additionalProperties'], $paramName . '[' . $k . ']');
136 if ($this->wp->isWpError($result)) {
137 return $result;
138 }
139 }
140 }
141 break;
142 }
143
144 $result = $this->wp->restValidateValueFromSchema($value, $schema, $paramName);
145 if ($this->wp->isWpError($result)) {
146 return $result;
147 }
148 return $this->wp->restSanitizeValueFromSchema($value, $schema, $paramName);
149 }
150
151 /**
152 * Mirrors rest_find_any_matching_schema().
153 *
154 * @param mixed $value
155 * @return mixed|WP_Error
156 */
157 private function validateAndSanitizeAnyOf($value, array $anyOfSchema, string $paramName) {
158 $errors = [];
159 foreach ($anyOfSchema['anyOf'] as $index => $schema) {
160 $result = $this->validateAndSanitizeValueFromSchema($value, $schema, $paramName);
161 if (!$this->wp->isWpError($result)) {
162 return $result;
163 }
164 $errors[] = ['error_object' => $result, 'schema' => $schema, 'index' => $index];
165 }
166 return $this->wp->restGetCombiningOperationError($value, $paramName, $errors);
167 }
168
169 /**
170 * Mirrors rest_find_one_matching_schema().
171 *
172 * @param mixed $value
173 * @return mixed|WP_Error
174 */
175 private function validateAndSanitizeOneOf($value, array $oneOfSchema, string $paramName) {
176 $matchingSchemas = [];
177 $errors = [];
178 $data = null;
179 foreach ($oneOfSchema['oneOf'] as $index => $schema) {
180 $result = $this->validateAndSanitizeValueFromSchema($value, $schema, $paramName);
181 if ($this->wp->isWpError($result)) {
182 $errors[] = ['error_object' => $result, 'schema' => $schema, 'index' => $index];
183 } else {
184 $data = $result;
185 $matchingSchemas[$index] = $schema;
186 }
187 }
188
189 if (!$matchingSchemas) {
190 return $this->wp->restGetCombiningOperationError($value, $paramName, $errors);
191 }
192
193 if (count($matchingSchemas) > 1) {
194 // reuse WP method to generate detailed error
195 $invalidSchema = ['type' => []];
196 $oneOf = array_replace(array_fill(0, count($oneOfSchema['oneOf']), $invalidSchema), $matchingSchemas);
197 return $this->wp->restFindOneMatchingSchema($value, ['oneOf' => $oneOf], $paramName);
198 }
199 return $data;
200 }
201
202 /** @param string|string[] $type */
203 private function getTypeError(string $param, $type): WP_Error {
204 $type = is_array($type) ? $type : [$type];
205 return new WP_Error(
206 'rest_invalid_type',
207 // translators: %1$s is the current parameter and %2$s a comma-separated list of the allowed types.
208 sprintf(__('%1$s is not of type %2$s.', 'mailpoet'), $param, implode(',', $type)),
209 ['param' => $param]
210 );
211 }
212 }
213