1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13:
14: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
15:
16: 17: 18: 19: 20: 21:
22: class cHTMLValidator
23: {
24: 25: 26:
27: protected $_doubleTags = [
28: "form",
29: "head",
30: "body",
31: "html",
32: "td",
33: "tr",
34: "table",
35: "a",
36: "tbody",
37: "title",
38: "container",
39: "span",
40: "div",
41: ];
42:
43: 44: 45:
46: public $missingNodes = [];
47:
48: 49: 50:
51: public $missingTags = [];
52:
53: 54: 55: 56:
57: public $iNodeName;
58:
59: 60: 61:
62: protected $_html;
63:
64: 65: 66:
67: protected $_nestingLevel = [];
68:
69: 70: 71:
72: protected $_nestingNodes = [];
73:
74: 75: 76:
77: protected $_existingTags = [];
78:
79: 80: 81:
82: public function validate($html)
83: {
84: $nestingLevel = 0;
85:
86:
87: $this->_html = $this->_cleanHTML($html);
88:
89: $htmlParser = new HtmlParser($this->_html);
90:
91: while ($htmlParser->parse()) {
92: $nodeName = $htmlParser->getNodeName();
93: $this->_existingTags[] = $nodeName;
94:
95:
96: if (in_array($nodeName, $this->_doubleTags)) {
97: if (!array_key_exists($nodeName, $this->_nestingLevel)) {
98: $this->_nestingLevel[$nodeName] = 0;
99: }
100:
101: if (!array_key_exists($nodeName, $this->_nestingNodes)) {
102: $this->_nestingNodes[$nodeName][intval($this->_nestingLevel[$nodeName])] = [];
103: }
104:
105:
106: if ($htmlParser->getNodeType() == HtmlParser::NODE_TYPE_ELEMENT) {
107:
108: $nestingLevel++;
109:
110: $this->_nestingNodes[$nodeName][intval($this->_nestingLevel[$nodeName])]["name"] = $htmlParser->getNodeAttributes('name');
111: $this->_nestingNodes[$nodeName][intval($this->_nestingLevel[$nodeName])]["id"] = $htmlParser->getNodeAttributes('id');
112: $this->_nestingNodes[$nodeName][intval($this->_nestingLevel[$nodeName])]["level"] = $nestingLevel;
113: $this->_nestingNodes[$nodeName][intval($this->_nestingLevel[$nodeName])]["char"] = $htmlParser->getHtmlTextIndex();
114: $this->_nestingLevel[$nodeName]++;
115: }
116:
117: if ($htmlParser->getNodeType() == HtmlParser::NODE_TYPE_ENDELEMENT) {
118:
119: if ($this->_nestingLevel[$nodeName] > 0) {
120: unset($this->_nestingNodes[$nodeName][$this->_nestingLevel[$nodeName]]);
121: $this->_nestingLevel[$nodeName]--;
122:
123:
124:
125:
126:
127: $nestingLevel--;
128: }
129: }
130: }
131: }
132:
133:
134: $this->missingNodes = [];
135:
136:
137: foreach ($this->_nestingLevel as $key => $value) {
138:
139: if ($value > 0) {
140:
141: for ($i = 0; $i < $value; $i++) {
142: $node = $this->_nestingNodes[$key][$i];
143:
144: list($line, $char) = $this->_getLineAndCharPos($node["char"]);
145: $this->missingNodes[] = [
146: "tag" => $key,
147: "id" => $node["id"],
148: "name" => $node["name"],
149: "line" => $line,
150: "char" => $char,
151: ];
152:
153: $this->missingTags[$line][$char] = true;
154: }
155: }
156: }
157: }
158:
159: 160: 161: 162: 163:
164: public function tagExists($tag)
165: {
166: return in_array($tag, $this->_existingTags);
167: }
168:
169: 170: 171: 172: 173:
174: protected function _cleanHTML($html)
175: {
176:
177: $resultingHTML = preg_replace('/<\?(php)?((.)|(\s))*?\?>/i', '', $html);
178:
179:
180: $resultingHTML = str_replace("\r\n", "\n", $resultingHTML);
181: $resultingHTML = str_replace("\r", "\n", $resultingHTML);
182:
183: return $resultingHTML;
184: }
185:
186: 187: 188: 189:
190: protected function _returnErrorMap()
191: {
192: $html = "<pre>";
193:
194: $chunks = explode("\n", $this->_html);
195:
196: foreach ($chunks as $key => $value) {
197: $html .= ($key + 1) . " ";
198:
199: for ($i = 0; $i < cString::getStringLength($value); $i++) {
200: $char = cString::getPartOfString($value, $i, 1);
201:
202: if (is_array($this->missingTags[$key + 1])) {
203: if (array_key_exists($i + 2, $this->missingTags[$key + 1])) {
204: $html .= "<u><b>" . conHtmlSpecialChars($char) . "</b></u>";
205: } else {
206: $html .= conHtmlSpecialChars($char);
207: }
208: } else {
209: $html .= conHtmlSpecialChars($char);
210: }
211: }
212:
213: $html .= "<br>";
214: }
215:
216: return $html;
217: }
218:
219: 220: 221: 222: 223:
224: protected function _getLineAndCharPos($charpos)
225: {
226: $mangled = cString::getPartOfString($this->_html, 0, $charpos);
227: $line = cString::countSubstring($mangled, "\n") + 1;
228: $char = $charpos - cString::findLastPos($mangled, "\n");
229:
230: return [$line, $char];
231: }
232: }
233: