CSE 154

Lecture 19: A PHP JSON API

Agenda

PHP Web Services with File I/O

Building and using a PHP JSON API

Q&A

Q: What is the one thing that PHP does well that JavaScript isn't well known for?

File processing

Q: From the client perspective, what is an advantage of JSON over plain text?

Easier to parse structured data, less error prone than using straight indices

PHP File I/O review

Getting information from a file

Recall: What are the functions to do PHP file I/O?

file(filename): returns lines of a file as an array ( \n at end of each item).

file(filename, FILE_IGNORE_NEW_LINES): returns lines of a file as an array with no \n at the end of each item.

file_get_contents(filename) returns entire contents of a file as a single string.

file_put_contents(filename, output) writes a string into a file, overwriting existing content

file_put_contents(filename, output, FILE_APPEND) writes a string into a file, appending the content to the existing file.

Recall: Getting information from a directory

Recall: What are the PHP functions that can get information from directories on the server?

function description
glob returns an array of all file names that match a given pattern (returns a file path and name, such as "foo/bar/myfile.txt")
scandir returns an array of all file names in a given directory (returns just the file names, such as "myfile.txt")

Can accept a general path with the * wildcard (more powerful).

glob Example

# reverse all poems in the poetry directory
$poems = glob("poetry/poem*.dat");
foreach ($poems as $poemfile) {
  $text = file_get_contents($poemfile);
  file_put_contents($poemfile, strrev($text));
  echo "I just reversed " . basename($poemfile) . "\n";
}

PHP

glob can match a wildcard path with the * character

  • glob("foo/bar/*.doc") returns all .doc files in the foo/bar subdirectory
  • glob("food*") returns all files whose names begin with "food"

The basename function strips any leading directory from a file path

  • basename("foo/bar/baz.txt") returns "baz.txt"

scandir Example

foreach (scandir("taxes/old") as $filename) {
  echo "I found a file: {$filename}\n";
}

PHP

I found a file: .

I found a file: ..

I found a file: 2007_w2.pdf

I found a file: 2006_1099.doc

output

scandir includes current directory (".") and parent ("..") in the array.

Don't need basename with scandir; returns file names only without directory

Now let's build a web service

A simple case study: JSONtest

Demo in class, look down for snippets

Example PHP code

<?php
header("Content-Type: application/json");

$output = array();
$output["name"] = "Miranda";
$output["hobbies"] = array("pottery", "softball",
                           "cycling", "watching youtube");

print(json_encode($output));
?>

Produces:

{
  "name":"Miranda",
  "hobbies":["pottery","softball"]
}

PHP

Example HTML

<h1>JSON Test</h1>
<button>Get JSON data.</button>
<h2>Response</h2>
<p id="response"></p>

HTML

Example JS Fetch handler

function loadData(data) {
  let response = $("response");
  response.innerText = data["name"];
  let p = document.createElement("p");
  p.innerText = "Hobbies";
  response.appendChild(p);
  let ul = document.createElement("ul");
  response.appendChild(ul);
  for(let i = 0; i < data["hobbies"].length; i++) {
    let li = document.createElement("li");
    li.innerText = data["hobbies"][i];
    ul.appendChild(li);
  }
}

JS

Step 1: Define your API

You first need to determine how your users interact with your API. Questions you can yourself might be:

  • What data might a user need?
  • What might be the easiest format for the user to use this data in?

Points of Interest requests

For our Points of Interest API we want three types of data:

  • All of the cities we have with at least one point of interest each.
  • A random city with the point of interest(s) for that city
  • The point(s) of interest for a city that is specified by the user

Points of Interest output

The general format of the response could look like the following for all of our cities

{
  "cities" : [
    {
      "name": "Seattle",
      "sites": [
        {
          "name": "Space Needle",
          "image": "....",
          "review": ".... "
        }
      ]
    },
    {
      "name": "New York",
      "sites": [
        {
          "name": "Ellis Island",
          "image": "...."
          "review": ".... "
        },
        {
          "name": "Statue of Liberty",
          "image": "...."
          "review": ".... "
        }
      ]
    }
    ...
  ]
}

JSON

Step 2: Write your PHP to handle the requests

Think of the conditionals you need to handle your request

Remember to check if the $_GET and $_POST indices for those requests are set (using isset), and handle the error correctly if not.

You can use conditional statements to figure out which query parameter to handle.

Remember to be careful about using $_GET vs $_POST where needed (for now we're using $_GET).

Oh and don't forget to handle other error cases as well.

Put the correct headers in each part of the conditional.... then look to see if there is any refactoring you can do!

Reminder: Setting Content Type with header

header("Content-type: type/subtype");

PHP (template)

Most of the time we'll use header("Content-type: text/plain"); or header("Content-type: application/json");

Also remember that in error cases we want to send back the Invalid Request header header("HTTP/1.1 400 Invalid Request");

Remember that you can not print or echo before a header statement.

Step 3: Write code to handle each request type

How will your data be stored on the server?

How will you convert the data into the right format for sending back to the caller

Code Quality in a Web Service

Code Quality in JS

Talk with your neighbor: Try to list at least 5 Code Quality standards we expect to achieve for your JS in this course (e.g. as specific as "line lengths < 100 characters" or "good variable descriptive identifier names")

Those may include

  • Using proper naming conventions (camelCase and ALL_CAPS) for JS
  • Good indentation and curly brace style (either stacked or K&R)
  • Using descriptive JSdoc to comment functions
  • Use the module pattern and "use strict;"
  • Localizing variables, not overusing globals
  • Using functions to capture functionality
  • Refactoring common code and sub-operations into function
  • Do not mix HTML/CSS/JS

Code Quality in PHP

These same code quality guidelines should transfer over to your PHP.

Your conditional statement that handles the request should not be in a function, but the code in the body of each condition should be fairly short. Use functions to encapsulate code.

Write your PHP with PHP specific naming conventions (like variable_names), use good indentation and curly brace style, localize variables as much as possible, ... and comment your code.