Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
namespace Aws\Api;
3
 
4
use Aws;
5
 
6
/**
7
 * Validates a schema against a hash of input.
8
 */
9
class Validator
10
{
11
    private $path = [];
12
    private $errors = [];
13
    private $constraints = [];
14
 
15
    private static $defaultConstraints = [
16
        'required' => true,
17
        'min'      => true,
18
        'max'      => false,
19
        'pattern'  => false
20
    ];
21
 
22
    /**
23
     * @param array $constraints Associative array of constraints to enforce.
24
     *                           Accepts the following keys: "required", "min",
25
     *                           "max", and "pattern". If a key is not
26
     *                           provided, the constraint will assume false.
27
     */
28
    public function __construct(array $constraints = null)
29
    {
30
        static $assumedFalseValues = [
31
            'required' => false,
32
            'min'      => false,
33
            'max'      => false,
34
            'pattern'  => false
35
        ];
36
        $this->constraints = empty($constraints)
37
            ? self::$defaultConstraints
38
            : $constraints + $assumedFalseValues;
39
    }
40
 
41
    /**
42
     * Validates the given input against the schema.
43
     *
44
     * @param string $name  Operation name
45
     * @param Shape  $shape Shape to validate
46
     * @param array  $input Input to validate
47
     *
48
     * @throws \InvalidArgumentException if the input is invalid.
49
     */
50
    public function validate($name, Shape $shape, array $input)
51
    {
52
        $this->dispatch($shape, $input);
53
 
54
        if ($this->errors) {
55
            $message = sprintf(
56
                "Found %d error%s while validating the input provided for the "
57
                    . "%s operation:\n%s",
58
                count($this->errors),
59
                count($this->errors) > 1 ? 's' : '',
60
                $name,
61
                implode("\n", $this->errors)
62
            );
63
            $this->errors = [];
64
 
65
            throw new \InvalidArgumentException($message);
66
        }
67
    }
68
 
69
    private function dispatch(Shape $shape, $value)
70
    {
71
        static $methods = [
72
            'structure' => 'check_structure',
73
            'list'      => 'check_list',
74
            'map'       => 'check_map',
75
            'blob'      => 'check_blob',
76
            'boolean'   => 'check_boolean',
77
            'integer'   => 'check_numeric',
78
            'float'     => 'check_numeric',
79
            'long'      => 'check_numeric',
80
            'string'    => 'check_string',
81
            'byte'      => 'check_string',
82
            'char'      => 'check_string'
83
        ];
84
 
85
        $type = $shape->getType();
86
        if (isset($methods[$type])) {
87
            $this->{$methods[$type]}($shape, $value);
88
        }
89
    }
90
 
91
    private function check_structure(StructureShape $shape, $value)
92
    {
93
        $isDocument = (isset($shape['document']) && $shape['document']);
94
        $isUnion = (isset($shape['union']) && $shape['union']);
95
        if ($isDocument) {
96
            if (!$this->checkDocumentType($value)) {
97
                $this->addError("is not a valid document type");
98
                return;
99
            }
100
        } elseif ($isUnion) {
101
            if (!$this->checkUnion($value)) {
102
                $this->addError("is a union type and must have exactly one non null value");
103
                return;
104
            }
105
        } elseif (!$this->checkAssociativeArray($value)) {
106
            return;
107
        }
108
 
109
        if ($this->constraints['required'] && $shape['required']) {
110
            foreach ($shape['required'] as $req) {
111
                if (!isset($value[$req])) {
112
                    $this->path[] = $req;
113
                    $this->addError('is missing and is a required parameter');
114
                    array_pop($this->path);
115
                }
116
            }
117
        }
118
        if (!$isDocument) {
119
            foreach ($value as $name => $v) {
120
                if ($shape->hasMember($name)) {
121
                    $this->path[] = $name;
122
                    $this->dispatch(
123
                        $shape->getMember($name),
124
                        isset($value[$name]) ? $value[$name] : null
125
                    );
126
                    array_pop($this->path);
127
                }
128
            }
129
        }
130
    }
131
 
132
    private function check_list(ListShape $shape, $value)
133
    {
134
        if (!is_array($value)) {
135
            $this->addError('must be an array. Found '
136
                . Aws\describe_type($value));
137
            return;
138
        }
139
 
140
        $this->validateRange($shape, count($value), "list element count");
141
 
142
        $items = $shape->getMember();
143
        foreach ($value as $index => $v) {
144
            $this->path[] = $index;
145
            $this->dispatch($items, $v);
146
            array_pop($this->path);
147
        }
148
    }
149
 
150
    private function check_map(MapShape $shape, $value)
151
    {
152
        if (!$this->checkAssociativeArray($value)) {
153
            return;
154
        }
155
 
156
        $values = $shape->getValue();
157
        foreach ($value as $key => $v) {
158
            $this->path[] = $key;
159
            $this->dispatch($values, $v);
160
            array_pop($this->path);
161
        }
162
    }
163
 
164
    private function check_blob(Shape $shape, $value)
165
    {
166
        static $valid = [
167
            'string' => true,
168
            'integer' => true,
169
            'double' => true,
170
            'resource' => true
171
        ];
172
 
173
        $type = gettype($value);
174
        if (!isset($valid[$type])) {
175
            if ($type != 'object' || !method_exists($value, '__toString')) {
176
                $this->addError('must be an fopen resource, a '
177
                    . 'GuzzleHttp\Stream\StreamInterface object, or something '
178
                    . 'that can be cast to a string. Found '
179
                    . Aws\describe_type($value));
180
            }
181
        }
182
    }
183
 
184
    private function check_numeric(Shape $shape, $value)
185
    {
186
        if (!is_numeric($value)) {
187
            $this->addError('must be numeric. Found '
188
                . Aws\describe_type($value));
189
            return;
190
        }
191
 
192
        $this->validateRange($shape, $value, "numeric value");
193
    }
194
 
195
    private function check_boolean(Shape $shape, $value)
196
    {
197
        if (!is_bool($value)) {
198
            $this->addError('must be a boolean. Found '
199
                . Aws\describe_type($value));
200
        }
201
    }
202
 
203
    private function check_string(Shape $shape, $value)
204
    {
205
        if ($shape['jsonvalue']) {
206
            if (!self::canJsonEncode($value)) {
207
                $this->addError('must be a value encodable with \'json_encode\'.'
208
                    . ' Found ' . Aws\describe_type($value));
209
            }
210
            return;
211
        }
212
 
213
        if (!$this->checkCanString($value)) {
214
            $this->addError('must be a string or an object that implements '
215
                . '__toString(). Found ' . Aws\describe_type($value));
216
            return;
217
        }
218
 
219
        $value = isset($value) ? $value : '';
220
        $this->validateRange($shape, strlen($value), "string length");
221
 
222
        if ($this->constraints['pattern']) {
223
            $pattern = $shape['pattern'];
224
            if ($pattern && !preg_match("/$pattern/", $value)) {
225
                $this->addError("Pattern /$pattern/ failed to match '$value'");
226
            }
227
        }
228
    }
229
 
230
    private function validateRange(Shape $shape, $length, $descriptor)
231
    {
232
        if ($this->constraints['min']) {
233
            $min = $shape['min'];
234
            if ($min && $length < $min) {
235
                $this->addError("expected $descriptor to be >= $min, but "
236
                    . "found $descriptor of $length");
237
            }
238
        }
239
 
240
        if ($this->constraints['max']) {
241
            $max = $shape['max'];
242
            if ($max && $length > $max) {
243
                $this->addError("expected $descriptor to be <= $max, but "
244
                    . "found $descriptor of $length");
245
            }
246
        }
247
    }
248
 
249
    private function checkArray($arr)
250
    {
251
        return $this->isIndexed($arr) || $this->isAssociative($arr);
252
    }
253
 
254
    private function isAssociative($arr)
255
    {
256
        return count(array_filter(array_keys($arr), "is_string")) == count($arr);
257
    }
258
 
259
    private function isIndexed(array $arr)
260
    {
261
        return $arr == array_values($arr);
262
    }
263
 
264
    private function checkCanString($value)
265
    {
266
        static $valid = [
267
            'string'  => true,
268
            'integer' => true,
269
            'double'  => true,
270
            'NULL'    => true,
271
        ];
272
 
273
        $type = gettype($value);
274
 
275
        return isset($valid[$type]) ||
276
            ($type == 'object' && method_exists($value, '__toString'));
277
    }
278
 
279
    private function checkAssociativeArray($value)
280
    {
281
        $isAssociative = false;
282
 
283
        if (is_array($value)) {
284
            $expectedIndex = 0;
285
            $key = key($value);
286
 
287
            do {
288
                $isAssociative = $key !== $expectedIndex++;
289
                next($value);
290
                $key = key($value);
291
            } while (!$isAssociative && null !== $key);
292
        }
293
 
294
        if (!$isAssociative) {
295
            $this->addError('must be an associative array. Found '
296
                . Aws\describe_type($value));
297
            return false;
298
        }
299
 
300
        return true;
301
    }
302
 
303
    private function checkDocumentType($value)
304
    {
305
        if (is_array($value)) {
306
            $typeOfFirstKey = gettype(key($value));
307
            foreach ($value as $key => $val) {
308
               if (!$this->checkDocumentType($val) || gettype($key) != $typeOfFirstKey) {
309
                   return false;
310
               }
311
            }
312
            return $this->checkArray($value);
313
        }
314
        return is_null($value)
315
            || is_numeric($value)
316
            || is_string($value)
317
            || is_bool($value);
318
    }
319
 
320
    private function checkUnion($value)
321
    {
322
        if (is_array($value)) {
323
            $nonNullCount = 0;
324
            foreach ($value as $key => $val) {
325
                if (!is_null($val) && !(strpos($key, "@") === 0)) {
326
                    $nonNullCount++;
327
                }
328
            }
329
            return $nonNullCount == 1;
330
        }
331
        return !is_null($value);
332
    }
333
 
334
    private function addError($message)
335
    {
336
        $this->errors[] =
337
            implode('', array_map(function ($s) { return "[{$s}]"; }, $this->path))
338
            . ' '
339
            . $message;
340
    }
341
 
342
    private function canJsonEncode($data)
343
    {
344
        return !is_resource($data);
345
    }
346
}