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.  * @category Web_Services
  33.  * @package  Services_W3C_CSSValidator
  34.  * @author   Laurent Laville <pear@laurent-laville.org>
  35.  * @license  http://www.opensource.org/licenses/bsd-license.php BSD
  36.  * @version  CVS: $id$
  37.  * @link     http://pear.php.net/package/Services_W3C_CSSValidator
  38.  * @since    File available since Release 0.1.0
  39.  */
  40.  
  41. require_once 'HTTP/Request.php';
  42.  
  43. require_once 'Services/W3C/CSSValidator/Response.php';
  44. require_once 'Services/W3C/CSSValidator/Error.php';
  45. require_once 'Services/W3C/CSSValidator/Warning.php';
  46.  
  47. /**
  48.  * Base class for utilizing the W3C CSS Validator service.
  49.  *
  50.  * @category Web_Services
  51.  * @package  Services_W3C_CSSValidator
  52.  * @author   Laurent Laville <pear@laurent-laville.org>
  53.  * @license  http://www.opensource.org/licenses/bsd-license.php BSD
  54.  * @link     http://pear.php.net/package/Services_W3C_CSSValidator
  55.  * @since    Class available since Release 0.1.0
  56.  */
  57. class Services_W3C_CSSValidator
  58. {
  59.     /**
  60.      * URI to the W3C validator.
  61.      *
  62.      * @var    string
  63.      * @since  0.1.0
  64.      * @access public
  65.      */
  66.     public $validator_uri = 'http://jigsaw.w3.org/css-validator/validator';
  67.  
  68.     /**
  69.      * The URL of the document to validate
  70.      *
  71.      * @var    string
  72.      * @since  0.1.0
  73.      * @access public
  74.      */
  75.     public $uri;
  76.  
  77.     /**
  78.      * Internally used filename of a file to upload to the validator
  79.      * POSTed as multipart/form-data
  80.      *
  81.      * @var    string
  82.      * @since  0.1.0
  83.      * @access public
  84.      */
  85.     public $uploaded_file;
  86.  
  87.     /**
  88.      * CSS fragment to validate.
  89.      *
  90.      * Full documents only. At the moment, will only work if data is sent with the
  91.      * UTF-8 encoding.
  92.      *
  93.      * @var    string
  94.      * @since  0.1.0
  95.      * @access public
  96.      */
  97.     public $fragment;
  98.  
  99.     /**
  100.      * Output format
  101.      *
  102.      * Triggers the various outputs formats of the validator. If unset, the usual
  103.      * Web html format will be sent. If set to soap12, the SOAP1.2 interface will be
  104.      * triggered. See below for the SOAP 1.2 response format description.
  105.      *
  106.      * @var    string
  107.      * @since  0.1.0
  108.      * @access public
  109.      */
  110.     public $output = 'soap12';
  111.  
  112.     /**
  113.      * Warning level
  114.      *
  115.      * Default value is '1', and value could one of these :
  116.      * <ul>
  117.      *   <li>2</li> all warning messages
  118.      *   <li>1</li> normal report
  119.      *   <li>0</li> most important warning messages
  120.      *   <li>no</li> none messages
  121.      * </ul>
  122.      *
  123.      * @var    string
  124.      * @since  0.1.0
  125.      * @access public
  126.      */
  127.     public $warning;
  128.  
  129.     /**
  130.      * Profile
  131.      *
  132.      * Default value is 'css21', and value could one of these :
  133.      * <ul>
  134.      *   <li>none</li> none profile
  135.      *   <li>css1</li> CSS level 1
  136.      *   <li>css2</li> CSS level 2
  137.      *   <li>css21</li> CSS level 2.1
  138.      *   <li>css3</li> CSS level 3
  139.      *   <li>svg</li> SVG
  140.      *   <li>svgbasic</li> SVG Basic
  141.      *   <li>svgtiny</li> SVG Tiny
  142.      *   <li>mobile</li> Mobile
  143.      *   <li>atsc-tv</li> ATSC TV
  144.      *   <li>tv</li> TV
  145.      * </ul>
  146.      *
  147.      * @var    string
  148.      * @since  0.1.0
  149.      * @access public
  150.      */
  151.     public $profile = 'css21';
  152.  
  153.     /**
  154.      * User medium
  155.      *
  156.      * Default value is 'all', and value could one of these :
  157.      * <ul>
  158.      *   <li>all</li>
  159.      *   <li>aural</li>
  160.      *   <li>braille</li>
  161.      *   <li>embossed</li>
  162.      *   <li>handheld</li>
  163.      *   <li>print</li>
  164.      *   <li>projection</li>
  165.      *   <li>screen</li>
  166.      *   <li>tty</li>
  167.      *   <li>tv</li>
  168.      *   <li>presentation</li>
  169.      * </ul>
  170.      *
  171.      * @var    string
  172.      * @since  0.1.0
  173.      * @access public
  174.      */
  175.     public $usermedium = 'all';
  176.  
  177.     /**
  178.      * Language used for response messages
  179.      *
  180.      * Default value is 'en', and value could one of these :
  181.      * en, fr, ja, es, zh-cn, nl, de
  182.      *
  183.      * @var    string
  184.      * @since  0.1.0
  185.      * @access public
  186.      */
  187.     public $lang = 'en';
  188.  
  189.     /**
  190.      * HTTP_Request object.
  191.      *
  192.      * @var    object
  193.      * @since  0.1.0
  194.      * @access protected
  195.      */
  196.     protected $request;
  197.  
  198.     /**
  199.      * Constructor for the class.
  200.      *
  201.      * @param array $options An array of options
  202.      * @since  0.1.0
  203.      * @access public
  204.      */
  205.     public function __construct($options = array())
  206.     {
  207.         $this->setOptions($options);
  208.     }
  209.  
  210.     /**
  211.      * Sets options for the class.
  212.      * Pass an associative array of options for the class.
  213.      *
  214.      * @param array $options An array of options
  215.      * @return void
  216.      * @since  0.1.0
  217.      * @access public
  218.      */
  219.     public function setOptions($options)
  220.     {
  221.         foreach ($options as $option=>$val) {
  222.             $this->$option = $val;
  223.         }
  224.     }
  225.  
  226.     /**
  227.      * Validates a given URI
  228.      *
  229.      * Executes the validator using the current parameters and returns a Response
  230.      * object on success.
  231.      *
  232.      * @param  string $uri The address to the page to validate ex: http://example.com/
  233.      * @return mixed object Services_W3C_CSSValidator_Response
  234.      *                      if web service call successfull,
  235.      *               boolean FALSE otherwise
  236.      * @since  0.1.0
  237.      * @access public
  238.      */
  239.     public function validateUri($uri)
  240.     {
  241.         $this->uri = $uri;
  242.         $this->buildRequest('uri');
  243.         if ($this->sendRequest()) {
  244.             return Services_W3C_CSSValidator::parseSOAP12Response(
  245.                 $this->request->getResponseBody());
  246.         } else {
  247.             return false;
  248.         }
  249.     }
  250.  
  251.     /**
  252.      * Validates the local file
  253.      *
  254.      * Requests validation on the local file, from an instance of the W3C validator.
  255.      * The file is posted to the W3C validator using multipart/form-data.
  256.      *
  257.      * @param  string $file file to be validated.
  258.      * @return mixed object Services_W3C_CSSValidator_Response
  259.      *                      if web service call successfull,
  260.      *               boolean FALSE otherwise
  261.      * @since  0.1.0
  262.      * @access public
  263.      */
  264.     public function validateFile($file)
  265.     {
  266.         if (file_exists($file)) {
  267.             $this->uploaded_file = $file;
  268.             $this->buildRequest('file'); //return $this->request;
  269.             if ($this->sendRequest()) {
  270.                 return Services_W3C_CSSValidator::parseSOAP12Response(
  271.                     $this->request->getResponseBody());
  272.             } else {
  273.                 return false;
  274.             }
  275.         } else {
  276.             return false;
  277.         }
  278.     }
  279.  
  280.     /**
  281.      * Validate an html string
  282.      *
  283.      * @param  string $html  Full html document fragment
  284.      * @return mixed object Services_W3C_CSSValidator_Response
  285.      *                      if web service call successfull,
  286.      *               boolean FALSE otherwise
  287.      * @since  0.1.0
  288.      * @access public
  289.      */
  290.     public function validateFragment($css)
  291.     {
  292.         $this->fragment = $css;
  293.         $this->buildRequest('fragment');
  294.         if ($this->sendRequest()) {
  295.             return Services_W3C_CSSValidator::parseSOAP12Response(
  296.                 $this->request->getResponseBody());
  297.         } else {
  298.             return false;
  299.         }
  300.     }
  301.  
  302.     /**
  303.      * Prepares a request object to send to the validator.
  304.      *
  305.      * @param  string $type  uri, file, or fragment
  306.      * @return void
  307.      * @since  0.1.0
  308.      * @access protected
  309.      */
  310.     protected function buildRequest($type = 'uri')
  311.     {
  312.         $this->request = new HTTP_Request();
  313.         $this->request->setURL($this->validator_uri);
  314.         switch ($type) {
  315.         case 'uri':
  316.         default:
  317.             $this->request->setMethod(HTTP_REQUEST_METHOD_GET);
  318.             $this->request->addQueryString('uri', $this->uri);
  319.             $method = 'addQueryString';
  320.             break;
  321.         case 'file':
  322.             $this->request->setMethod(HTTP_REQUEST_METHOD_POST);
  323.             $this->request->addFile('file',
  324.                                      $this->uploaded_file,
  325.                                      'text/css');
  326.             $method = 'addPostData';
  327.             break;
  328.         case 'fragment':
  329.             $this->request->setMethod(HTTP_REQUEST_METHOD_GET);
  330.             $this->request->addQueryString('text', $this->fragment);
  331.             $method = 'addQueryString';
  332.             break;
  333.         }
  334.  
  335.         $options = array('output', 'warning', 'profile', 'usermedium', 'lang');
  336.         foreach ($options as $option) {
  337.             if (isset($this->$option)) {
  338.                 if (is_bool($this->$option)) {
  339.                     $this->request->$method($option, intval($this->$option));
  340.                 } else {
  341.                     $this->request->$method($option, $this->$option);
  342.                 }
  343.             }
  344.         }
  345.     }
  346.  
  347.     /**
  348.      * Actually sends the request to the CSS Validator service
  349.      *
  350.      * @return bool TRUE if request was sent successfully, FALSE otherwise
  351.      * @since  0.1.0
  352.      * @access protected
  353.      */
  354.     protected function sendRequest()
  355.     {
  356.         if (PEAR::isError($this->request->sendRequest())) {
  357.             return false;
  358.         } else {
  359.             return true;
  360.         }
  361.     }
  362.  
  363.     /**
  364.      * Parse an XML response from the validator
  365.      *
  366.      * This function parses a SOAP 1.2 response xml string from the validator.
  367.      *
  368.      * @param  string $xml The raw soap12 XML response from the validator.
  369.      * @return mixed object Services_W3C_CSSValidator_Response
  370.      *                      if parsing soap12 response successfully,
  371.      *               boolean FALSE otherwise
  372.      * @since  0.1.0
  373.      * @access protected
  374.      */
  375.     protected static function parseSOAP12Response($xml)
  376.     {
  377.         $doc = new DOMDocument();
  378.         // try to load soap 1.2 xml response, and suppress warning reports if any
  379.         if (@$doc->loadXML($xml)) {
  380.             $response = new Services_W3C_CSSValidator_Response();
  381.  
  382.             // Get the standard CDATA elements
  383.             $cdata = array('uri', 'checkedby', 'csslevel', 'date');
  384.             foreach ($cdata as $var) {
  385.                 $element = $doc->getElementsByTagName($var);
  386.                 if ($element->length) {
  387.                     $response->$var = $element->item(0)->nodeValue;
  388.                 }
  389.             }
  390.             // Handle the bool element validity
  391.             $element = $doc->getElementsByTagName('validity');
  392.             if ($element->length &&
  393.                 $element->item(0)->nodeValue == 'true') {
  394.                 $response->validity = true;
  395.             } else {
  396.                 $response->validity = false;
  397.             }
  398.             if (!$response->validity) {
  399.                 $errors = $doc->getElementsByTagName('error');
  400.                 foreach ($errors as $error) {
  401.                     $response->errors[] =
  402.                         new Services_W3C_CSSValidator_Error($error);
  403.                 }
  404.             }
  405.             $warnings = $doc->getElementsByTagName('warning');
  406.             foreach ($warnings as $warning) {
  407.                 $response->warnings[] =
  408.                     new Services_W3C_CSSValidator_Warning($warning);
  409.             }
  410.             return $response;
  411.         } else {
  412.             // Could not load the XML document
  413.             return false;
  414.         }
  415.     }
  416. }
  417. ?>