CSE 154

Lecture 20: More File I/O with scandir and glob

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/managing plain text

Getting information from a file

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

PHP I/O Functions

function name(s) category
file, file_get_contents, file_put_contents reading/writing entire files
basename, file_exists, filesize, fileperms, filemtime, is_dir, is_readable, is_writable, disk_free_space asking for information
copy, rename, unlink, chmod, chgrp, chown, mkdir, rmdir manipulating files and directories
glob, scandir reading directories

Returning to the Points of Interest Web Service

For our Points of Interest API on Wednesday (pointsofinterest.php), we had the goal of supporting 3 different GET requests:

  • All of the cities we have with at least one point of interest each. (city=all)
  • The Point(s) of Interest for a city that is specified by the user (city={cityname})
  • The POI for a random city (city=random)

Getting City/POI Associations

Each requires a way to associate city names and their Points of Interest. We use 2 methods to create these associative arrays:

  1. V1: get_list() hard-code the $attractions array.
  2. V2: get_list_from_file() used file processing to read cities.txt and create the $attractions array.

Initial (V1) Solution (with get_list())

function get_list() {
  $attractions = array("Seattle" => "Space Needle",
                       "New York" => "Ellis Island",
                       "Boston" => "Boston Harbor",
                       "Philadelphia" => "Valley Forge",
                       "Detroit" => "The Henry Ford Museum",
                       "Paris" => "Eiffel Tower",
                       "Vancouver BC" => "Stanley Park",
                       "Tokyo" => "Mt. Fuji",
                       "Beijing" => "Great Wall",
                       "London" => "Big Ben");
 return $attractions;
}

PHP

V2 Solution File I/O (get_list_from_file())

We used file processing to generalize our get_list() function to generate the associative array based on the contents in our cities.txt:

Seattle, Space Needle
New York, Ellis Island
Boston, Boston Harbor
Philadelphia, Valley Forge
Detroit, The Henry Ford Museum
Paris, Eiffel Tower
...
Beijing, Great Wall
London, Big Ben

cities.txt

function get_list_from_file() {
  $contents = file("cities.txt", FILE_IGNORE_NEW_LINES);
  $attractions = array();
  foreach ($contents as $line) {
    $parts = explode(",", $line);
    $city = $parts[0];
    $attraction = $parts[1];

    # could also do:
    # list($city, $attraction) = explode(",", $line);

    # e.g. { "Seattle" => "Space Needle" }
    $attractions[$city] = $attraction;
  }
  return $attractions;
}

PHP

city={cityname} (text)

http://example.com/pointsofinterest.php?city=Seattle

GET request

The best place to visit in Seattle is Space Needle.

Output

A Start to city=all (JSON)

{
    "Seattle": " Space Needle",
    "New York": " Ellis Island",
    "Boston": " Boston Harbor",
    "Philadelphia": " Valley Forge",
    "Detroit": " The Henry Ford Museum",
    "Paris": " Eiffel Tower",
    "Vancouver BC": " Stanley Park",
    "Tokyo": " Mt. Fuji",
    "San Francisco": " Fisherman's Terminal",
    "Beijing": " Great Wall",
    "London": " Big Ben"
}

JSON

Adding More Information with Directory Processing

For both solutions, we left off with each value of the array being the name of the site. But how can we add more information for each site?

Recall that for each POI in a city, we eventually wanted:

  • The name (e.g. Space Needle)
  • An image
  • A review

Adding More to our JSON Response

A general (improved) JSON format of the pointsofinterest.php?city=all request:

This information is much more nicely structured with JSON! But we don't have the image/review information from cities.txt...

{
    "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

Reading From Directories

Getting information from a directory

From Yesterday: 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")

Motivation: A (V3) Directory-based POI Web Service

directory structure for cities

Suppose we store information about each Point of Interest in a file in a city subdirectory.

The name of the file is the name of the Point of Interest (e.g. "Space_Needle.txt")

Each file contains 2 lines:

  • The absolute URL of an image of the Point of Interest
  • A review of the Point of Interest

glob vs. scandir Exercise: City Names

How would you get an array of all city directory names?

directory structure for cities
$all_folders = scandir("cities/");
$cities = array_diff($all_folders, array(".", ".."));

Or alternatively...

$full_paths = glob("cities/*");
$cities = array();
foreach ($full_paths as $path) {
  array_push($cities, basename($path));
}

PHP

Extracting POI File Contents

How could you get the name, image url, and review (as an associative array) for a path to a POI file?

Example: for "cities/Seattle/Space_Needle", return
{"name":"Space Needle", "image":"...", "review":"..." } (abbreviated)

function get_info_from_file($file_path) {
  # Remember you can use list to assign variables quickly when you know
  # how many parts there are in an array (each POI file has two lines)
  list($image, $review) = file($file_path, FILE_IGNORE_NEW_LINES);

  # e.g. get "Space Needle" from 
  # "cities/Seattle/Space_Needle" POI file path
  $name = str_replace("_", " ", basename($file_path));
  return array("name" => $name,
               "image" => $image,
               "review" => $review);
}

PHP

Putting it All Together: V3

function get_all_data() {
  /* Get all of the cities in the cities directory */
  $cities = glob("cities/*");
  $output = array();
  foreach ($cities as $city) {

    // Now get the POIs for each city (here, $city still has the full file path)
    $poi_files = glob($city . "/*");
    $pois = array();
    foreach ($poi_files as $poi_file) {
      // From the previous slide!
      $poi_data = get_info_from_file($poi_file);
      array_push($pois, $poi_data);
    }

    // Create the associative array for the city with the city name and its POI sites. 
    $city_info = array();
    $city_info["name"] = str_replace("_", " ", basename($city));
    $city_info["sites"] = $pois;

    // Add this city data to our output array
    array_push($output, $city_info);
  }

  /* Final step!!! Don't forget the last association for our JSON!!! */
  return array("cities" => $output);
}

PHP

This version gives us the JSON we wanted!

A Directory-Based POI Service (V3): poi-v3.php

A complete pointsofinterest.zip example, including our front-end using the web service previewed on Wednesday!