Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 142
0.00% covered (danger)
0.00%
0 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
JCValidators
0.00% covered (danger)
0.00%
0 / 142
0.00% covered (danger)
0.00%
0 / 18
3660
0.00% covered (danger)
0.00%
0 / 1
 run
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
 isBool
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 isString
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 isStringLine
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 isInt
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 isNumber
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
30
 isList
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 isDictionary
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 isUrl
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 useDefault
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 deleteField
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 stringToList
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 uniqueSortStrList
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 isLocalizedString
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
42
 isHeaderString
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 noExtraValues
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 checkListSize
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 validateDataType
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2namespace JsonConfig;
3
4use Closure;
5
6/**
7 * Class JCValidators contains various static validation functions
8 * @package JsonConfig
9 */
10class JCValidators {
11
12    /** Call one or more validator functions with the given parameters.
13     * Validator parameters:  function ( JCValue $jcv, string $fieldPath, JCContent $content )
14     * Validator should update $jcv object with any errors it finds by using error() function.
15     * Validator may also change the value or set default/same-as-default flags.
16     * Setting status to JCValue::MISSING will delete this value (but not its parent)
17     * @param array $validators an array of validator function closures
18     * @param JCValue $value value to validate, modify, and change status of
19     * @param array $path path to the field, needed by the error messages
20     * @param JCContent $content
21     */
22    public static function run(
23        array $validators, JCValue $value, array $path, JCContent $content
24    ) {
25        if ( $validators ) {
26            foreach ( $validators as $validator ) {
27                if ( !$validator( $value, $path, $content ) ) {
28                    break;
29                }
30            }
31        }
32    }
33
34    /** Returns a validator function to check if the value is a valid boolean (true/false)
35     * @param bool $nullable if true, null becomes a valid value
36     * @return callable
37     */
38    public static function isBool( $nullable = false ) {
39        return static function ( JCValue $v, array $path ) use ( $nullable ) {
40            $value = $v->getValue();
41            if ( is_bool( $value ) || ( $nullable && $value === null ) ) {
42                return true;
43            }
44            $v->error( 'jsonconfig-err-bool', $path );
45            return false;
46        };
47    }
48
49    /** Returns a validator function to check if the value is a valid string
50     * @return callable
51     */
52    public static function isString() {
53        return static function ( JCValue $v, array $path ) {
54            if ( !is_string( $v->getValue() ) ) {
55                $v->error( 'jsonconfig-err-string', $path );
56                return false;
57            }
58            return true;
59        };
60    }
61
62    /** Returns a validator function to check if the value is a valid single line string
63     * @param bool $nullable if true, null becomes a valid value
64     * @param int $maxlength maximum allowed string size
65     * @return callable
66     */
67    public static function isStringLine( $nullable = false, $maxlength = 400 ) {
68        return static function ( JCValue $v, array $path ) use ( $nullable, $maxlength ) {
69            $value = $v->getValue();
70            if ( JCUtils::isValidLineString( $value, $maxlength ) ||
71                 ( $nullable && $value === null )
72            ) {
73                return true;
74            }
75            $v->error( 'jsonconfig-err-stringline', $path, $maxlength );
76            return false;
77        };
78    }
79
80    /** Returns a validator function to check if the value is a valid integer
81     * @return callable
82     */
83    public static function isInt() {
84        return static function ( JCValue $v, array $path ) {
85            if ( !is_int( $v->getValue() ) ) {
86                $v->error( 'jsonconfig-err-integer', $path );
87                return false;
88            }
89            return true;
90        };
91    }
92
93    /** Returns a validator function to check if the value is a valid integer
94     * @param bool $nullable if true, null becomes a valid value
95     * @return callable
96     */
97    public static function isNumber( $nullable = false ) {
98        return static function ( JCValue $v, array $path ) use ( $nullable ) {
99            $value = $v->getValue();
100            if ( is_float( $value ) || is_int( $value ) || ( $nullable && $value === null ) ) {
101                return true;
102            }
103            $v->error( 'jsonconfig-err-number', $path );
104            return false;
105        };
106    }
107
108    /** Returns a validator function to check if the value is an non-associative array (list)
109     * @return callable
110     */
111    public static function isList() {
112        return static function ( JCValue $v, array $path ) {
113            if ( !JCUtils::isList( $v->getValue() ) ) {
114                $v->error( 'jsonconfig-err-array', $path );
115                return false;
116            }
117            return true;
118        };
119    }
120
121    /** Returns a validator function to check if the value is an associative array
122     * @return callable
123     */
124    public static function isDictionary() {
125        return static function ( JCValue $v, array $path ) {
126            if ( !is_object( $v->getValue() ) ) {
127                $v->error( 'jsonconfig-err-assoc-array', $path );
128                return false;
129            }
130            return true;
131        };
132    }
133
134    /** Returns a validator function to check if the value is an associative array
135     * @return callable
136     */
137    public static function isUrl() {
138        return static function ( JCValue $v, array $path ) {
139            if ( filter_var( $v->getValue(), FILTER_VALIDATE_URL ) === false ) {
140                $v->error( 'jsonconfig-err-url', $path );
141                return false;
142            }
143            return true;
144        };
145    }
146
147    /** Returns a validator function that will substitute missing value with default
148     * @param mixed $default value to use in case field is not present, or a closure function to
149     *   generate that value
150     * @param bool $validateDefault if true, the default value will be verified by the validators
151     * @return callable
152     */
153    public static function useDefault( $default, $validateDefault = true ) {
154        return static function ( JCValue $v ) use ( $default, $validateDefault ) {
155            if ( $v->isMissing() ) {
156                if ( is_object( $default ) && ( $default instanceof Closure ) ) {
157                    $default = $default();
158                }
159                $v->setValue( $default );
160                return $validateDefault;
161            }
162            return true;
163        };
164    }
165
166    /** Returns a validator function that informs that this field should be deleted
167     * @return callable
168     */
169    public static function deleteField() {
170        return static function ( JCValue $v ) {
171            $v->status( JCValue::MISSING );
172            // continue executing validators - there could be a custom one that changes it further
173            return true;
174        };
175    }
176
177    /** Returns a validator function that will wrap a string value into an array
178     * @return callable
179     */
180    public static function stringToList() {
181        return static function ( JCValue $v ) {
182            if ( is_string( $v->getValue() ) ) {
183                $v->setValue( [ $v->getValue() ] );
184            }
185            return true;
186        };
187    }
188
189    /** Returns a validator function that will ensure the list is sorted and each value is unique
190     * @return callable
191     */
192    public static function uniqueSortStrList() {
193        return static function ( JCValue $v ) {
194            if ( !$v->isMissing() ) {
195                $arr = array_unique( $v->getValue() );
196                sort( $arr );
197                $v->setValue( $arr );
198            }
199            return true;
200        };
201    }
202
203    /** Returns a validator function that will ensure that the given value is a non-empty object,
204     * with each key being an allowed language code, and each value being a single line string.
205     * @param bool $nullable if true, null becomes a valid value
206     * @param int $maxlength
207     * @return Closure
208     */
209    public static function isLocalizedString( $nullable = false, $maxlength = 400 ) {
210        return static function ( JCValue $jcv, array $path ) use ( $nullable, $maxlength ) {
211            if ( !$jcv->isMissing() ) {
212                $v = $jcv->getValue();
213                if ( $nullable && $v === null ) {
214                    return true;
215                }
216                if ( is_object( $v ) ) {
217                    $v = (array)$v;
218                }
219                if ( JCUtils::isLocalizedArray( $v, $maxlength ) ) {
220                    // Sort array so that the values are sorted alphabetically
221                    ksort( $v );
222                    $jcv->setValue( (object)$v );
223                    return true;
224                }
225            }
226            $jcv->error( 'jsonconfig-err-localized', $path );
227            return false;
228        };
229    }
230
231    /**
232     * Returns a validator function to check if the value is a valid header string
233     * @param array &$allHeaders
234     * @return callable
235     */
236    public static function isHeaderString( &$allHeaders ) {
237        return static function ( JCValue $v, array $path ) use ( &$allHeaders ) {
238            $value = $v->getValue();
239            // must be a string, begins with a letter or '_', and only has letters/digits/'_'
240            if ( !is_string( $value ) || !preg_match( '/^[\pL_][\pL\pN_]*$/ui', $value ) ) {
241                $v->error( 'jsonconfig-err-bad-header-string', $path );
242            } elseif ( in_array( $value, $allHeaders ) ) {
243                $v->error( 'jsonconfig-err-duplicate-header', $path, $value );
244            } else {
245                $allHeaders[] = $value;
246                return true;
247            }
248            return false;
249        };
250    }
251
252    /** Returns a validator function to check if the dictionary value contains any unexpected vals
253     * This should be called after all values inside an object have already been tested
254     * @return callable
255     */
256    public static function noExtraValues() {
257        return static function ( JCValue $v, array $path ) {
258            $value = $v->getValue();
259            if ( is_object( $value ) ) {
260                foreach ( $value as $key => $subVal ) {
261                    if ( !( $subVal instanceof JCValue ) ) {
262                        $v->error( 'jsonconfig-err-unexpected-key', $path, $key );
263                        return false;
264                    }
265                }
266            }
267            return true;
268        };
269    }
270
271    /** Returns a validator function to check if value is a list of a given size
272     * @param int $count
273     * @param string $field
274     * @return callable
275     */
276    public static function checkListSize( $count, $field ) {
277        return static function ( JCValue $v, array $path ) use ( $count, $field ) {
278            $list = $v->getValue();
279            if ( is_array( $list ) && count( $list ) !== $count ) {
280                $v->error( 'jsonconfig-err-array-count', $path, count( $list ), $count, $field );
281                return false;
282            }
283            return true;
284        };
285    }
286
287    /** Returns a validator function asserting a string to be one of the valid data types.
288     * Additionally, a validator function for that data type is appended to the $validators array.
289     * @param array &$validators
290     * @return Closure
291     */
292    public static function validateDataType( &$validators ) {
293        return static function ( JCValue $v, array $path ) use ( &$validators ) {
294            $value = $v->getValue();
295            $validator = false;
296            if ( is_string( $value ) ) {
297                switch ( $value ) {
298                    case 'string':
299                        $validator = JCValidators::isStringLine( true );
300                        break;
301                    case 'boolean':
302                        $validator = JCValidators::isBool( true );
303                        break;
304                    case 'number':
305                        $validator = JCValidators::isNumber( true );
306                        break;
307                    case 'localized':
308                        $validator = JCValidators::isLocalizedString( true );
309                        break;
310                }
311            }
312            if ( $validator === false ) {
313                $v->error( 'jsonconfig-err-bad-type', $path );
314                return false;
315            }
316            $validators[] = $validator;
317            return true;
318        };
319    }
320
321}