info343/lectures/json-web-services/files/books.php

<?php
   // books.php
   // Output book data from a text file in JSON format.
   // Adapted by Morgan Doocy for INFO 343, Autumn 2012
   // Originally by Marty Stepp, CSE 190 M
   
   // File to read book data from.
   $INPUT_FILE = "books.txt";
   
   // Disallow anything other than POST requests.
   if ($_SERVER["REQUEST_METHOD"] != "GET") {
      http_die(400, "Invalid Request", "ERROR 400: Invalid request - This service accepts only GET requests.");
   }
   
   // Error if unknown parameters passed.
   $unknown = array_diff(array_keys($_REQUEST), array('category', 'delay'));
   if (count($unknown)) {
      http_die(400, "Invalid Request", "ERROR 400: Invalid request - Unrecognized parameter(s) passed: " . implode(", ", $unknown));
   }
   
   // Ensure file is readable.
   if (!file_exists($INPUT_FILE) || !is_readable($INPUT_FILE)) {
      http_die(500, "Server Error", "ERROR 500: Server error - Unable to read input file: $INPUT_FILE");
   }
   
   // Delay specified number of seconds (optional).
   if (isset($_REQUEST['delay'])) {
      $delay = max(0, min(60, (int) sanitize($_REQUEST["delay"])));
      sleep($delay);
   }
   
   // Load input file.
   $books = array_map('line2book', file($INPUT_FILE, FILE_IGNORE_NEW_LINES));
   
   // Output JSON data.
   if (isset($_REQUEST['category'])) {
      // Category specified - filter out books that aren't of the specified
      // category, and output the resulting list in JSON.
      $category = sanitize($_REQUEST['category']);
      $count = count($books);
      for ($i = 0; $i < $count; $i++) {
         if ($books[$i]['category'] != $category) {
            unset($books[$i]);
         }
      }
      print_json(array_values($books));
   } else {
      // No category specified - get a list of the categories present in file,
      // and output that list in JSON.
      $categories = array();
      foreach ($books as $book) {
         if (!in_array($book['category'], $categories)) {
            array_push($categories, $book['category']);
         }
      }
      sort($categories);
      print_json($categories);
   }
   
   
   
   // === Functions ===
   
   // Output the given data as JSON, with an application/json header.
   function print_json($data) {
      header("Content-type: application/json");
      print pretty_print(json_encode($data));
   }
   
   // Convert a line from the input file into an array of properties/values.
   function line2book($line) {
      $props = array('title', 'author', 'category', 'year', 'price');
      $values = explode('|', $line);
      return array_combine($props, $values);
   }
   
   // 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($msg);
   }
   
   // Removes all characters except letters/numbers from query parameters
   function sanitize($str) {
      return preg_replace("/[^A-Za-z0-9_]*/", "", $str);
   }
   
   // Return a human-readable version the given JSON.
   // Adapted from http://www.php.net/manual/en/function.json-encode.php#80339
   function pretty_print($input) {
      $tab = "\t";
      $nl = "\n";
      $indent = 0;
      $in_string = false;
      $indented_array = 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;
   }
?>