Completed
Pull Request — master (#1)
by Timothy
08:02
created

JsonSchema::formatError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 11
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
3
namespace Abacaphiliac\Zend\Validator;
4
5
use JsonSchema\RefResolver;
6
use JsonSchema\Uri\UriResolver;
7
use JsonSchema\Uri\UriRetriever;
8
use JsonSchema\Validator;
9
use Zend\Json\Exception\RuntimeException;
10
use Zend\Json\Json;
11
use Zend\Validator\AbstractValidator;
12
use Zend\Validator\Exception;
13
14
class JsonSchema extends AbstractValidator
15
{
16
    /** @var string */
17
    private $file;
18
    
19
    /** @var string[] */
20
    private $messages = array();
21
22
    /** @var string[] */
23
    private $errorKeys = array();
24
    
25
    /** @var string */
26
    private $messagePrefix = '[';
27
    
28
    /** @var string */
29
    private $messageSuffix = ']';
30
    
31
    /** @var string */
32
    private $messageAttributeDelimiter = '] [';
33
34
    /**
35
     * JsonSchema constructor.
36
     * @param array|null|\Traversable $options
37
     */
38
    public function __construct($options = null)
39
    {
40
        $this->errorKeys = array(
41
            'property',
42
            'constraint',
43
            'message',
44
        );
45
        
46
        parent::__construct($options);
47
    }
48
49
    /**
50
     * Returns true if and only if $value meets the validation requirements
51
     *
52
     * If $value fails validation, then this method returns false, and
53
     * getMessages() will return an array of messages that explain why the
54
     * validation failed.
55
     *
56
     * @param  mixed $value
57
     * @return bool
58
     * @throws Exception\RuntimeException If validation of $value is impossible
59
     */
60
    public function isValid($value)
61
    {
62
        $schema = $this->getSchema();
63
        
64
        try {
65
            $decoded = $this->decodeValue($value);
66
        } catch (RuntimeException $e) {
67
            $this->messages = array(
68
                $e->getMessage(),
69
            );
70
            return false;
71
        }
72
        
73
        $validator = new Validator();
74
        $validator->check($decoded, $schema);
75
76
        if ($validator->isValid()) {
77
            return true;
78
        }
79
        
80
        $this->messages = array_map(array($this, 'formatError'), $validator->getErrors());
81
        
82
        return false;
83
    }
84
85
    /**
86
     * @param mixed[] $error
87
     * @return string
88
     */
89
    public function formatError(array $error)
90
    {
91
        // Get the error properties that should be returned to the user.
92
        $properties = array_intersect_key($error, array_flip($this->errorKeys));
93
94
        // Format error properties as bracket-delimited key-value-pairs.
95
        $message = urldecode(http_build_query($properties, null, $this->messageAttributeDelimiter));
96
97
        // Validation error may be returned as JSON. Replace quotes so that the message looks nice.
98
        return str_replace('"', "'", $this->messagePrefix . $message . $this->messageSuffix);
99
    }
100
101
    /**
102
     * @return \stdClass
103
     * @throws \Zend\Validator\Exception\RuntimeException
104
     */
105
    private function getSchema()
106
    {
107
        $file = $this->file;
108
        if (!$file) {
109
            throw new Exception\RuntimeException('Validator option `file` is required and cannot be empty.');
110
        }
111
112
        $url = parse_url($file);
113
        if (!array_key_exists('scheme', $url)) {
114
            $file = 'file://' . realpath($file);
115
        }
116
117
        $refResolver = new RefResolver(new UriRetriever(), new UriResolver());
118
119
        try {
120
            return $refResolver->resolve($file);
121
        } catch (\Exception $e) {
122
            throw new Exception\RuntimeException(
123
                sprintf('Could not load JSON Schema: %s', $e->getMessage()),
124
                $e->getCode(),
125
                $e
126
            );
127
        }
128
    }
129
130
    /**
131
     * @param mixed $value
132
     * @return \stdClass
133
     * @throws \Zend\Json\Exception\RuntimeException
134
     */
135
    private function decodeValue($value)
136
    {
137
        $encoded = $value;
138
        if (!is_scalar($value)) {
139
            $encoded = Json::encode($value);
140
        }
141
142
        return Json::decode($encoded);
143
    }
144
145
    /**
146
     * @return string[]
147
     */
148
    public function getMessages()
149
    {
150
        return $this->messages;
151
    }
152
153
    /**
154
     * @return string
155
     */
156
    public function getFile()
157
    {
158
        return $this->file;
159
    }
160
161
    /**
162
     * @param string $file
163
     */
164
    public function setFile($file)
165
    {
166
        $this->file = $file;
167
    }
168
169
    /**
170
     * @return string[]
171
     */
172
    public function getErrorKeys()
173
    {
174
        return $this->errorKeys;
175
    }
176
177
    /**
178
     * @param string[] $errorKeys
179
     */
180
    public function setErrorKeys(array $errorKeys)
181
    {
182
        $this->errorKeys = $errorKeys;
183
    }
184
185
    /**
186
     * @return string
187
     */
188
    public function getMessagePrefix()
189
    {
190
        return $this->messagePrefix;
191
    }
192
193
    /**
194
     * @param string $messagePrefix
195
     */
196
    public function setMessagePrefix($messagePrefix)
197
    {
198
        $this->messagePrefix = $messagePrefix;
199
    }
200
201
    /**
202
     * @return string
203
     */
204
    public function getMessageSuffix()
205
    {
206
        return $this->messageSuffix;
207
    }
208
209
    /**
210
     * @param string $messageSuffix
211
     */
212
    public function setMessageSuffix($messageSuffix)
213
    {
214
        $this->messageSuffix = $messageSuffix;
215
    }
216
217
    /**
218
     * @return string
219
     */
220
    public function getMessageAttributeDelimiter()
221
    {
222
        return $this->messageAttributeDelimiter;
223
    }
224
225
    /**
226
     * @param string $messageAttributeDelimiter
227
     */
228
    public function setMessageAttributeDelimiter($messageAttributeDelimiter)
229
    {
230
        $this->messageAttributeDelimiter = $messageAttributeDelimiter;
231
    }
232
}
233