<?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;
}
?>