1<?php 2/** 3 * Copyright (c) 2007, Laurent Laville <pear@laurent-laville.org> 4 * 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * * Neither the name of the authors nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 * 32 * PHP version 5 33 * 34 * @category Web_Services 35 * @package Services_W3C_CSSValidator 36 * @author Laurent Laville <pear@laurent-laville.org> 37 * @license http://www.opensource.org/licenses/bsd-license.php BSD 38 * @version CVS: $Id$ 39 * @link http://pear.php.net/package/Services_W3C_CSSValidator 40 * @since File available since Release 0.1.0 41 */ 42 43require_once 'HTTP/Request2.php'; 44 45require_once 'Services/W3C/CSSValidator/Response.php'; 46require_once 'Services/W3C/CSSValidator/Error.php'; 47require_once 'Services/W3C/CSSValidator/Warning.php'; 48 49/** 50 * Base class for utilizing the W3C CSS Validator service. 51 * 52 * @category Web_Services 53 * @package Services_W3C_CSSValidator 54 * @author Laurent Laville <pear@laurent-laville.org> 55 * @license http://www.opensource.org/licenses/bsd-license.php BSD 56 * @link http://pear.php.net/package/Services_W3C_CSSValidator 57 * @since Class available since Release 0.1.0 58 */ 59class Services_W3C_CSSValidator 60{ 61 /** 62 * URI to the W3C validator. 63 * 64 * @var string 65 */ 66 const VALIDATOR_URI = 'http://jigsaw.w3.org/css-validator/validator'; 67 68 /** 69 * The URL of the document to validate 70 * 71 * @var string 72 */ 73 protected $uri; 74 75 /** 76 * Internally used filename of a file to upload to the validator 77 * POSTed as multipart/form-data 78 * 79 * @var string 80 */ 81 protected $uploaded_file; 82 83 /** 84 * CSS fragment to validate. 85 * 86 * Full documents only. At the moment, will only work if data is sent with the 87 * UTF-8 encoding. 88 * 89 * @var string 90 */ 91 protected $fragment; 92 93 /** 94 * Options list (available with default values) : 95 * 96 * output - Output format 97 * Triggers the various outputs formats of the validator. If unset, 98 * the usual Web html format will be sent. If set to soap12, 99 * the SOAP1.2 interface will be triggered. 100 * 101 * warning - Warning level 102 * Default value is '1', and value could one of these : 103 * <ul> 104 * <li>2</li> all warning messages 105 * <li>1</li> normal report 106 * <li>0</li> most important warning messages 107 * <li>no</li> none messages 108 * </ul> 109 * 110 * profile - Profile 111 * Default value is 'css21', and value could one of these : 112 * <ul> 113 * <li>none</li> none profile 114 * <li>css1</li> CSS level 1 115 * <li>css2</li> CSS level 2 116 * <li>css21</li> CSS level 2.1 117 * <li>css3</li> CSS level 3 118 * <li>svg</li> SVG 119 * <li>svgbasic</li> SVG Basic 120 * <li>svgtiny</li> SVG Tiny 121 * <li>mobile</li> Mobile 122 * <li>atsc-tv</li> ATSC TV 123 * <li>tv</li> TV 124 * </ul> 125 * 126 * usermedium - User medium 127 * Default value is 'all', and value could one of these : 128 * <ul> 129 * <li>all</li> 130 * <li>aural</li> 131 * <li>braille</li> 132 * <li>embossed</li> 133 * <li>handheld</li> 134 * <li>print</li> 135 * <li>projection</li> 136 * <li>screen</li> 137 * <li>tty</li> 138 * <li>tv</li> 139 * <li>presentation</li> 140 * </ul> 141 * 142 * lang - Language used for response messages 143 * Default value is 'en', and value could one of these : 144 * en, fr, ja, es, zh-cn, nl, de 145 * 146 * @var array 147 */ 148 protected $options; 149 150 /** 151 * HTTP_Request2 object. 152 * 153 * @var object 154 */ 155 protected $request; 156 157 /** 158 * Constructor for the class. 159 * 160 * @return void 161 */ 162 public function __construct(HTTP_Request2 $request = null) 163 { 164 $this->options = array('output' => 'soap12', 'warning' => '1', 165 'profile' => 'css21', 'usermedium' => 'all', 'lang' => 'en'); 166 167 if (empty($request)) { 168 $request = new HTTP_Request2(); 169 } 170 171 $this->request = $request; 172 } 173 174 /** 175 * Sets options for the class. 176 * 177 * @param string $option Name of option to set 178 * @param string $val Value of option to set 179 * 180 * @return void 181 */ 182 public function __set($option, $val) 183 { 184 // properties that can be set directly 185 $setting_allowed = array('uri'); 186 187 if (isset($this->options[$option])) { 188 $this->options[$option] = $val; 189 } elseif (property_exists($this, $option)) { 190 if (in_array($option, $setting_allowed)) { 191 $this->$option = $val; 192 } 193 } 194 } 195 196 /** 197 * Gets options for the class. 198 * 199 * @param string $option Name of option to set 200 * 201 * @return mixed 202 */ 203 public function __get($option) 204 { 205 // properties that can be get directly 206 $getting_allowed = array('uri'); 207 208 $r = null; 209 if (isset($this->options[$option])) { 210 $r = $this->options[$option]; 211 } elseif (property_exists($this, $option)) { 212 if (in_array($option, $getting_allowed)) { 213 $r = $this->$option; 214 } 215 } 216 return $r; 217 } 218 219 /** 220 * Validates a given URI 221 * 222 * Executes the validator using the current parameters and returns a Response 223 * object on success. 224 * 225 * @param string $uri The address to the page to validate ex: http://example.com/ 226 * 227 * @return mixed object Services_W3C_CSSValidator_Response 228 * if web service call successfull, 229 * boolean FALSE otherwise 230 */ 231 public function validateUri($uri) 232 { 233 $this->uri = $uri; 234 $this->buildRequest('uri'); 235 if ($response = $this->sendRequest()) { 236 return $this->parseSOAP12Response($response->getBody()); 237 } else { 238 return false; 239 } 240 } 241 242 /** 243 * Validates the local file 244 * 245 * Requests validation on the local file, from an instance of the W3C validator. 246 * The file is posted to the W3C validator using multipart/form-data. 247 * 248 * @param string $file file to be validated. 249 * 250 * @return mixed object Services_W3C_CSSValidator_Response 251 * if web service call successfull, 252 * boolean FALSE otherwise 253 */ 254 public function validateFile($file) 255 { 256 if (file_exists($file)) { 257 $this->uploaded_file = $file; 258 $this->buildRequest('file'); //return $this->request; 259 if ($response = $this->sendRequest()) { 260 return $this->parseSOAP12Response($response->getBody()); 261 } else { 262 return false; 263 } 264 } else { 265 return false; 266 } 267 } 268 269 /** 270 * Validate an html string 271 * 272 * @param string $css Full css document fragment 273 * 274 * @return mixed object Services_W3C_CSSValidator_Response 275 * if web service call successfull, 276 * boolean FALSE otherwise 277 */ 278 public function validateFragment($css) 279 { 280 $this->fragment = $css; 281 $this->buildRequest('fragment'); 282 if ($response = $this->sendRequest()) { 283 return $this->parseSOAP12Response($response->getBody()); 284 } else { 285 return false; 286 } 287 } 288 289 /** 290 * Prepares a request object to send to the validator. 291 * 292 * @param string $type uri, file, or fragment 293 * 294 * @return void 295 */ 296 protected function buildRequest($type = 'uri') 297 { 298 $this->request->setURL(self::VALIDATOR_URI); 299 switch ($type) { 300 case 'uri': 301 default: 302 $this->request->setMethod(HTTP_Request2::METHOD_GET); 303 $this->setQueryVariable('uri', $this->uri); 304 $method = 'setQueryVariable'; 305 break; 306 case 'file': 307 $this->request->setMethod(HTTP_Request2::METHOD_POST); 308 $this->request->addUpload('file', 309 $this->uploaded_file, 310 null, 311 'text/css'); 312 $method = 'addPostParameter'; 313 break; 314 case 'fragment': 315 $this->request->setMethod(HTTP_Request2::METHOD_GET); 316 $this->setQueryVariable('text', $this->fragment); 317 $method = 'setQueryVariable'; 318 break; 319 } 320 321 $options = array('output', 'warning', 'profile', 'usermedium', 'lang'); 322 foreach ($options as $option) { 323 if (isset($this->options[$option])) { 324 if (is_bool($this->options[$option])) { 325 $this->request->$method($option, 326 intval($this->options[$option])); 327 } else { 328 $this->$method($option, $this->options[$option]); 329 } 330 } 331 } 332 } 333 334 /** 335 * Set a querystring variable for the request 336 * 337 * @param string $name Name of the querystring parameter 338 * @param mixed $value Value of the parameter 339 * 340 * @return void 341 */ 342 protected function setQueryVariable($name, $value = '') 343 { 344 $url = $this->request->getURL(); 345 $url->setQueryVariable($name, $value); 346 $this->request->setURL($url); 347 } 348 349 /** 350 * Add post data to the request 351 * 352 * @param string $name Name of the post field 353 * @param mixed $value Value of the field 354 * 355 * @return void 356 */ 357 protected function addPostParameter($name, $value = '') 358 { 359 $this->request->addPostParameter($name, $value); 360 } 361 362 363 /** 364 * Actually sends the request to the CSS Validator service 365 * 366 * @return bool TRUE if request was sent successfully, FALSE otherwise 367 * @todo Raise dependency to 5.3.0 all together? (#18082) 368 */ 369 protected function sendRequest() 370 { 371 try { 372 return $this->request->send(); 373 } catch (Exception $e) { 374 if (version_compare(PHP_VERSION, '5.3.0') >= 0) { 375 throw new Exception('Error sending request', null, $e); 376 } 377 throw new Exception($e->getMessage()); 378 } 379 } 380 381 /** 382 * Parse an XML response from the validator 383 * 384 * This function parses a SOAP 1.2 response xml string from the validator. 385 * 386 * @param string $xml The raw soap12 XML response from the validator. 387 * 388 * @return mixed object Services_W3C_CSSValidator_Response 389 * if parsing soap12 response successfully, 390 * boolean FALSE otherwise 391 */ 392 protected function parseSOAP12Response($xml) 393 { 394 $doc = new DOMDocument(); 395 // try to load soap 1.2 xml response, and suppress warning reports if any 396 if (@$doc->loadXML($xml)) { 397 $response = new Services_W3C_CSSValidator_Response(); 398 399 // Get the standard CDATA elements 400 $cdata = array('uri', 'checkedby', 'csslevel', 'date'); 401 foreach ($cdata as $var) { 402 $element = $doc->getElementsByTagName($var); 403 if ($element->length) { 404 $response->$var = $element->item(0)->nodeValue; 405 } 406 } 407 // Handle the bool element validity 408 $element = $doc->getElementsByTagName('validity'); 409 if ($element->length && 410 $element->item(0)->nodeValue == 'true') { 411 $response->validity = true; 412 } else { 413 $response->validity = false; 414 } 415 if (!$response->validity) { 416 $errors = $doc->getElementsByTagName('error'); 417 foreach ($errors as $error) { 418 $response->addError(new 419 Services_W3C_CSSValidator_Error($error)); 420 } 421 } 422 $warnings = $doc->getElementsByTagName('warning'); 423 foreach ($warnings as $warning) { 424 $response->addWarning(new 425 Services_W3C_CSSValidator_Warning($warning)); 426 } 427 return $response; 428 } else { 429 // Could not load the XML document 430 return false; 431 } 432 } 433} 434?> 435