1<?php
2/*
3** Zabbix
4** Copyright (C) 2001-2021 Zabbix SIA
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19**/
20
21
22class CSchemaValidator extends CValidator {
23
24	/**
25	 * Array of required field names.
26	 *
27	 * @var array
28	 */
29	public $required = [];
30
31	/**
32	 * Error message if a required field is missing.
33	 *
34	 * @var string
35	 */
36	public $messageRequired;
37
38	/**
39	 * Error message if an unsupported field is given.
40	 *
41	 * @var string
42	 */
43	public $messageUnsupported;
44
45	/**
46	 * Array of validators where keys are object field names and values are either CValidator objects or nulls.
47	 *
48	 * If the value is set to null, it will not be validated, but no $messageUnsupported error will be triggered.
49	 *
50	 * @var array
51	 */
52	protected $validators = [];
53
54	/**
55	 * Array of validators to validate the whole object.
56	 *
57	 * @var array
58	 */
59	protected $postValidators = [];
60
61	public function __construct(array $options = []) {
62		// set validators via the public setter method
63		if (isset($options['validators'])) {
64			foreach ($options['validators'] as $field => $validator) {
65				$this->setValidator($field, $validator);
66			}
67		}
68		unset($options['validator']);
69
70		// set post validators via the public setter method
71		if (isset($options['postValidators'])) {
72			foreach ($options['postValidators'] as $validator) {
73				$this->addPostValidator($validator);
74			}
75		}
76		unset($options['validator']);
77
78		parent::__construct($options);
79	}
80
81	/**
82	 * Checks each object field against the given validator, and then the whole object against the post validators.
83	 *
84	 * @param array $array
85	 *
86	 * @return bool
87	 */
88	public function validate($array) {
89		$required = array_flip($this->required);
90		$unvalidatedFields = array_flip(array_keys($array));
91
92		// field validators
93		foreach ($this->validators as $field => $validator) {
94			unset($unvalidatedFields[$field]);
95
96			// if the value is present
97			if (isset($array[$field])) {
98				// validate it if a validator is given, skip it otherwise
99				if ($validator && !$validator->validate($array[$field])) {
100					$this->setError($validator->getError());
101
102					return false;
103				}
104			}
105			// if no value is given, check if it's required
106			elseif (isset($required[$field])) {
107				$this->error($this->messageRequired, $field);
108
109				return false;
110			}
111		}
112
113		// check if any unsupported fields remain
114		if ($unvalidatedFields) {
115			reset($unvalidatedFields);
116			$field = key($unvalidatedFields);
117			$this->error($this->messageUnsupported, $field);
118
119			return false;
120		}
121
122		// post validators
123		foreach ($this->postValidators as $validator) {
124			if (!$validator->validate($array)) {
125				$this->setError($validator->getError());
126
127				return false;
128			}
129		}
130
131		return true;
132	}
133
134	/**
135	 * Set a validator for a field.
136	 *
137	 * @param $field
138	 * @param CValidator $validator
139	 */
140	public function setValidator($field, CValidator $validator = null) {
141		$this->validators[$field] = $validator;
142	}
143
144	/**
145	 * Add a post validator.
146	 *
147	 * @param CValidator $validator
148	 */
149	public function addPostValidator(CValidator $validator) {
150		$this->postValidators[] = $validator;
151	}
152
153	/**
154	 * Set the object name for the current validator and all included validators.
155	 *
156	 * @param string $name
157	 */
158	public function setObjectName($name) {
159		parent::setObjectName($name);
160
161		foreach ($this->validators as $validator) {
162			if ($validator) {
163				$validator->setObjectName($name);
164			}
165		}
166
167		foreach ($this->postValidators as $validator) {
168			$validator->setObjectName($name);
169		}
170	}
171
172}
173