info343/labs/7/urban.php

<?php
   // urban.php
   // web service for Urban Dictionary lab
   // Morgan Doocy
   // 15 Nov 2011
   
   // Web service URLs.
   $BASE_URL = "http://www.urbandictionary.com/iphone/search";
   $DEFINITIONS_URL = "$BASE_URL/define?term=%s";
   // $RELATED_URL = "$BASE_URL/related?term=%s";
   // $NEARBY_URL = "$BASE_URL/nearby?term=%s";
   
   // Valid parameter formats.
   $PARAMS = array(
      'term' => '/^\s*\S.*$/',
      'format' => '/^(?:xml|json|txt|)$/',
      'delay' => '/^(?:[0-5][0-9]|60)$/'
   );
   
   // Validation failure error messages.
   $ERRORS = array(
      'term' => 'a non-empty string',
      'format' => "'xml', 'json', or 'txt' (default)",
      'delay' => 'an integer number of seconds between 0 and 60'
   );
   
   
   // Disallow anything other than POST requests.
   if ($_SERVER["REQUEST_METHOD"] != "GET") {
      http_die(400, "Invalid Request", "This service accepts only GET requests.");
   }
   
   // Error if unknown parameters passed.
   $unknown = array_diff(array_keys($_REQUEST), array_keys($PARAMS));
   if (count($unknown)) {
      http_die(400, "Invalid Request", "Unrecognized parameter(s) passed: " . implode(", ", $unknown));
   }
   
   // Fetch (and check) 'term' and 'format' parameters.
   $term = trim(check_param('term'));
   $format = isset($_GET['format']) ? check_param('format') : 'txt';
   
   // Delay specified number of seconds (optional).
   if (isset($_REQUEST['delay'])) {
      sleep(check_param('delay'));
   }
   
   // Fetch definitions for term.
   $definitions = get_definitions($term);
   
   // 404 if no definitions found for this term.
   if (empty($definitions)) {
      http_die(404, "Term Not Found", "No definitions found for the term '$term'.");
   }
   
   
   // Output data.
   if ($format == 'txt') {
      header("Content-type: text/plain");
      print $definitions[0]['definition'];
   } else {
      header("Content-type: application/$format");
      if ($format == 'xml') {
         print xml($term, $definitions);
      } else {
         print pretty_json(json_encode($definitions));
      }
   }
   
   
   
   // Return the list of definitions from Urban Dictionary for the given term
   // (in an associative array structure converted from JSON).
   function get_definitions($term) {
      global $DEFINITIONS_URL;
      $url = sprintf($DEFINITIONS_URL, urlencode($term));
      $data = json_decode(file_get_contents($url), true);
      $list = $data['list'];
      $are_results = count($list) && !array_key_exists('type', $list[0]);
      return $are_results ? $list : null;
   }
   
   // Return the given data in XML format.
   function xml($term, $definitions) {
      // Return the given name with underscores removed (as appropriate for XML tag name).
      function xmlfieldname($name) {
         return str_replace('_', '', $name);
      }
      
      // Return the given key/value pair formatted as XML attribute: key="value".
      function xmlattr($key, $val) {
         return "$key=\"$val\"";
      }
      
      // Return the given key/value pair formatted as XML element: <key>value</key>.
      function xmlelem($key, $val) {
         return "<$key>$val</$key>";
      }
      
      // Fields from each definition to use as attributes / elements (in order).
      $attr_fields = array('defid', 'thumbs_down', 'thumbs_up', 'current_vote', 'permalink');
      $elem_fields = array('word', 'author', 'definition', 'example');
      
      // Capture and return XML output.
      ob_start();
      print '<?xml version="1.0" encoding="UTF-8"?' . ">\n";
      ?>
<entries term="<?= htmlspecialchars($term) ?>">
<?php
foreach ($definitions as $definition) {
   // Prepare attribute string with converted attribute names and escaped values.
   $attrs = array();
   foreach ($attr_fields as $field) {
      $attrs[htmlspecialchars(xmlfieldname($field))] = htmlspecialchars($definition[$field]);
   }
   $attrstr = implode(" ", array_map('xmlattr', array_keys($attrs), array_values($attrs)));
   
   // Write entry with attribute string, and elements with converted tag names
   // and escaped values.
   ?>
   <entry <?= $attrstr ?>>
<?php foreach ($elem_fields as $field) { ?>
      <?= xmlelem(htmlspecialchars(xmlfieldname($field)), htmlspecialchars($definition[$field])) ?> 
<?php } ?>
   </entry>
<?php
}
?>
</entries>
      <?php
      return ob_get_clean();
   }
   
   // Die with the given HTTP status code and message.
   function http_die($code, $status, $msg) {
      header("HTTP/1.1 $code $status");
      header("Content-type: text/plain");
      die("ERROR $code: $status - $msg");
   }
   
   // Validate the parameter of the given name.
   function check_param($name) {
      global $PARAMS, $ERRORS;
      $value = isset($_REQUEST[$name]) ? $_REQUEST[$name] : '';
      if (!preg_match($PARAMS[$name], $value)) {
         http_die(400, "Invalid Request", "Invalid value '$value' for parameter '$name'.\n(Must be {$ERRORS[$name]}.)");
      }
      return $value;
   }
   
   // Return a human-readable version the given JSON.
   // Adapted from http://www.php.net/manual/en/function.json-encode.php#80339
   function pretty_json($input) {
      $tab = "\t";
      $nl = "\n";
      $indent = 0;
      $in_string = false;
      
      $output = "";
      for ($i = 0; $i < strlen($input); $i++) {
         $char = $input[$i];
         $before = $after = '';
         if (!$in_string) {
            switch ($char) {
               case '{':
               case '[':
                  $after = $nl . str_repeat($tab, ++$indent);
                  break;
               
               case '}':
               case ']':
                  $before = $nl . str_repeat($tab, --$indent);
                  break;
               
               case ',':
                  $after = $nl . str_repeat($tab, $indent);
                  break;
               
               case ':':
                  $after = " ";
                  break;
               
               case '"':
                  if ($i == 0 || $input[$i-1] != '\\') {
                     $in_string = true;
                  }
            }
         } else if ($char == '"' && $input[$i-1] != '\\') {
            $in_string = false;
         }
         $output .= $before . $char . $after;
      }

      return $output;
   }
?>