PHP, MySQL, JavaScript & HTML5 All-in-One For Dummies (2013)

Book VI: Web Applications

Chapter 2: Creating and Using a Web Service

In This Chapter

arrow.png Understanding web services

arrow.png Sharing data with web services

arrow.png Receiving web service data

If you’ve read Book V, you’re already familiar with how to get data from a MySQL database. To do so, you connect to the database, execute a query to get some data, and then do something with the results.

Databases work great for most everything that you’ll build with your own site. But there are times when you need to access information outside of your own database. In these instances, you might be able to use (or consume) a web service offered by another company. For example, Twitter offers web services that enable you to retrieve tweets and other information, Amazon offers various web services, and several other companies offer public web services into their data.

This chapter looks at how to create and consume web services. We start with a simple web service that returns the current date and then move into creating other web services that accept input.

Understanding Web Services

When you grow your web site, you might find that you want to create web services of your own, and then offer those to external sites or have for your own use. Doing so means that people who want to access your data don’t need to do so using MySQL. They can simply call your web service to get the data. This greatly enhances security because you control what data is returned and how it’s returned, rather than someone querying your database directly.

Web services return data in a couple different formats. PHP includes formatting functions that make returning data from a web service almost trivial.

Web services typically return data formatted as Extensible Markup Language (XML) or JavaScript Object Notation (JSON). JSON is a much less resource intensive format, requiring less overhead to send data and incorporate it into your programs.

One item of note with web services is that they don’t use sessions at all. You can, however, include variables from a session when calling a web service, but you can’t access any of them, as you see later in this chapter.

Returning Data from a Web Service

Anything that you can return from a PHP program can be returned as a web service. This section looks at returning data in web service format.

Returning the date

A simple way to get your feet wet with web services is to return a date in JSON format. Here’s how you can do that:

1. Open your text editor or programming IDE and create a new empty file.

2. Place the following code within the file:

<?php

$header = "Content-Type: application/json";

header($header);

$date = date("M d, Y");

print json_encode($date);

?>

3. Save the file as date.php in your document root.

4. View the page in your web browser at http://localhost/date.php.

You should see a page like that in Figure 2-1 (though the date will probably be different).

9781118213704-fg060201.eps

Figure 2-1: Viewing a JSON-encoded date web service.

The format for this web service just returns the date as a quoted string. It’s more common to return an array of data with each element labeled. The labels make it easier to find and use individual elements. For example, consider the code in Listing 2-1.

Listing 2-1: JSON-Encoded Data

<?php

$header = "Content-Type: application/json";

header($header);

$date = date("M d, Y");

$returnData = array("friendlyDate" => $date);

print json_encode($returnData);

?>

When viewed in a browser, the JSON-encoded data looks like that in Figure 2-2.

9781118213704-fg060202.eps

Figure 2-2: JSON-encoded data.

As you can see, there’s now more to the returned data. This means that you can return all sorts of data with the same web service and the consumers of the web service can choose which pieces they’ll use. For example, the upcoming Listing 2-2 shows an enhanced date web service that returns the friendly date, the Unix time, the month, the day of the week, and the year in various formats.

The examples shown so far (and others that create web services in this chapter) use the PHP header() function to send a Content-Type header to the browser. The Content-Type header tells the browser what type of information is to be expected as output. It's important for browsers so that they can parse the information properly.

Listing 2-2: Returning Various Date Formats in a Web Service

<?php

$header = "Content-Type: application/json";

header($header);

$friendlyDate = date("M d, Y");

$unixTime = time();

$month = date("M");

$dayOfWeek = date("l");

$year = date("Y");

$returnData = array(

        "friendlyDate" => $friendlyDate,

        "unixTime" => $unixTime,

        "monthNum" => $month,

        "dayOfWeek" => $dayOfWeek,

        "yearNum" => $year

);

print json_encode($returnData);

?>

When viewed in a browser, the code from Listing 2-2 returns data like that in Figure 2-3.

9781118213704-fg060203.eps

Figure 2-3: JSON-encoded dates in various formats.

With that arraylike output, it's easy to access individual elements. Say you have an application that needs to know the day of the week. You can call your web service and use the built-in json_decode() PHP function to get access to the dayOfWeek element. Listing 2-3 shows code to consume a web service.

Listing 2-3: Consuming a Web Service

<?php

$curlHandle = curl_init("http://localhost/date.php");

curl_setopt($curlHandle, CURLOPT_HEADER, 0);

curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1);

$output = curl_exec($curlHandle);

$decoded = json_decode($output,TRUE);

print $decoded['dayOfWeek'];

?>

When this page is viewed in a browser, the output is simply the day of the week. The code in Listing 2-3 uses the cURL library, which connects into PHP through a set of powerful functions to interact with web pages and sites, including submitting forms. In this case, the code initializes the cURL object (through curl_init()), sets some options, and then loads the URL.

The output is saved into a variable called $output, which is then decoded using the json_decode() function. The Boolean TRUE that you see within the json_decode() function sets the output as an array, which is what you want. Finally, the dayOfWeek is retrieved from the decoded output and displayed to the screen.

This pattern is pretty typical of web service consumption. In fact, it’s common to set up a shared function or a class for cURL so that you can call cURL web services without having to include this same code in all your files. Chapter 1 of this minibook discusses including helper functions.

So what's the advantage of calling a date web service instead of just simply calling the date() function? That depends. On one hand, you could argue that setting up a common date function that returns all sorts of formats is easier than trying to remember the exact formatting for thedate() function everywhere you need it. On the other hand, you could say that calling a web service might slow down the overall response time. Both are true and valid.

The date() function is used in this chapter primarily because it provides an easy way to demonstrate returning data from a web service, without your humble book authors having to explain too much about what the date() function is doing.

Returning web service data from a database

A frequent use of web services is to retrieve information from a database. This section looks at returning simple data from a database. Later sections in this chapter show how to accept input and query the database through a web service.

Creating the database

For this section, you use a database that marks whether or not a certain website is up and operational. The web service then simply returns “Up” or “Down” based on the contents of the database table.

You use a database called sites for this section. Therefore, the first step is to create the database itself, with the command:

mysqladmin -u <yourUser> -p create sites

The <yourUser> in that command would be the user that you have that can create databases. If you're using a shared hosting provider, you might not be able to create databases. If that's the case, then you can use whatever database the hosting provider has created for you. If you're using a MySQL server on your local computer, then the user is probably called root.

The database table will be called siteStatus and the CREATE statement for it is as follows:

CREATE TABLE siteStatus (

id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,

siteURL VARCHAR(255),

siteStatus VARCHAR(10)

);

You can enter that SQL into the MySQL Command Line Interface (CLI) to create the table. Be sure to connect to or use the sites database when creating the table, with the command:

CONNECT sites;

or

USE sites;

Once the database has been created, a single row can be added for this demonstration:

INSERT INTO siteStatus (siteURL,siteStatus) VALUES ('http://www.braingia.org','Up');

Creating the web service

The web service is created by setting up the MySQL connection, querying the database, and then returning the data. Of course, there’s also error handling, in case something goes wrong with the query.

Listing 2-4 shows the code for creating this web service.

Listing 2-4: A Web Service That Uses Data from a Database Query

<?php

$header = "Content-Type: application/json";

header($header);

$dbLink = mysqli_connect('localhost','USER','PASSWORD','sites');

if (!$dbLink) {

    $row = array("siteStatus" => "Database Error");

    print json_encode($row);

} else {

    $query = "SELECT siteStatus FROM siteStatus WHERE siteURL = 'http://www.braingia.org'";

    if ($result = mysqli_query($dbLink,$query)) {

        $row = $result->fetch_array(MYSQLI_ASSOC);

        if (is_null($row)) {

            $row = array("siteStatus" => "Error - Site Not Found");

        }

    } else {

        $row = array("siteStatus" => "General Error");

    }

    print json_encode($row);

    mysqli_close($dbLink);

} // End else condition (for database connection)

?>

The code from Listing 2-4 contains a good amount of error handling, including error handling if the database connection can't be established, if there's a problem with the query, or if the site wasn't found. In all these cases, the end result is that output is sent to the user thanks to thejson_encode($row).

remember.eps This is an important point with web services: Send output back to the web service consumer indicating that there was an error, rather than merely exiting.

You should always include feedback in the output of the web service for error conditions so that the person calling the web service can handle the error.

Figure 2-4 shows the output from this web service for non-error conditions.

9781118213704-fg060204.eps

Figure 2-4: Returning the status of a site from a web service.

Accepting Input to a Web Service

Up until this point, the web services you've created have simply returned data but haven't accepted any input of their own. You can add the capability to accept input and then react based on that input, much like you'd do for a web form. For example, you might accept input to the date web service to convert a date into other formats, or you might accept a URL into the site status web service to check its status. This section examines accepting input to a web service.

remember.eps Prior to accepting input, you should understand a bit about two HyperText Transfer Protocol (HTTP) methods. HTTP methods are ways of interacting with a web server. Here are two primary methods used on the web:

check GET: This request sends everything right along with the URL, and you see GET requests in the address bar of your web browser. GET requests are limited by web browsers to a certain length (the length varies depending on the browser).

check POST: These requests send data as part of the data that gets sent to the server behind the scenes. POST requests are not limited by the web browser and are therefore appropriate for long forms or for sending large files through the web.

Querying with input data

Web services can accept input from a GET or a POST. For the purposes here, you use a GET request to accept a URL for your site status web service.

Listing 2-5 shows the new site status web service, with code added to retrieve the URL from the query string.

Listing 2-5: Retrieving the URL

<?php

$header = "Content-Type: application/json";

header($header);

if (isset($_GET['siteURL'])) {

    $site = $_GET['siteURL'];

} else {

    print json_encode(array("siteStatus" => "No site specified"));

    exit;

}

$dbLink = mysqli_connect('localhost','USER','PASSWORD','sites');

if (!$dbLink) {

    $row = array("siteStatus" => "Database Error");

    print json_encode($row);

} else {

$escSite = mysqli_real_escape_string($dbLink,$site);

    $query = "SELECT siteStatus FROM siteStatus WHERE siteURL = '{$escSite}'";

    if ($result = mysqli_query($dbLink,$query)) {

        $row = $result->fetch_array(MYSQLI_ASSOC);

        if (is_null($row)) {

            $row = array("siteStatus" => "Error - Site Not Found");

        }

    } else {

        $row = array("siteStatus" => "General Error");

    }

    print json_encode($row);

    mysqli_close($dbLink);

} // End else condition (for database connection)

?>

The primary code addition for this new web services is at the top:

if (isset($_GET['siteURL'])) {

    $site = $_GET['siteURL'];

} else {

    print json_encode(array("siteStatus" => "No site specified"));

    exit;

}

This code checks to see if the siteURL variable is on the query string and if it is, sets it to the $site variable.

Later in the code, the $site variable is escaped so that it's safe to use in a query, and the query itself is changed to use that newly escaped variable:

$escSite = mysqli_real_escape_string($dbLink,$site);

$query = "SELECT siteStatus FROM siteStatus WHERE siteURL = '{$escSite}'";

With that code in place, the web service can be called again. This time, though, instead of just loading the web service like http://localhost/sitestatus.php, you need to include the URL to check as part of the address, like so:

http://localhost/sitestatus.php?siteURL=http%3A%2F%2Fwww.braingia.org

But wait! What's all that %3A%2F%2F in the http://www.braingia.org URL? Those are URL-encoded characters. Certain characters are reserved or restricted from use in a URL. It just so happens that :// are some of those restricted characters. Therefore, they need to be converted (or escaped) to be a safe URL to use.

In any event, when that URL is loaded, the site is looked up in the database and its status is returned.

Returning XML results

Up until this point, you’ve been returning results in JSON format. Sometimes you might want to return results in XML format. You might do this because the consuming program for your web service can handle XML easier than JSON or because the person requesting the web service just wants XML.

Listing 2-6 shows the date web service with XML output instead of JSON.

Listing 2-6: XML Output for the date Web Service

<?php

$friendlyDate = date("M d, Y","1369739047");

$unixTime = 1369739047;

$month = date("M","1369739047");

$dayOfWeek = date("l","1369739047");

$year = date("Y","1369739047");

$returnData = array(

        "friendlyDate" => $friendlyDate,

        "unixTime" => $unixTime,

        "monthNum" => $month,

        "dayOfWeek" => $dayOfWeek,

        "yearNum" => $year

);

$xml = new DOMDocument();

$dateInfoElement = $xml->createElement("dateInformation");

foreach ($returnData as $key => $value) {

    $xmlNode = $xml->createElement($key,$value);

    $dateInfoElement->appendChild($xmlNode);

}

$xml->appendChild($dateInfoElement);        

$header = "Content-Type:text/xml";

header($header);

print $xml->saveXML();

?>

The primary changes for the web service are to create an XML document. This is done through the DOMDocument object, which is part of PHP. With a new DOMDocument object instantiated, the next step is to create XML elements for each of the parts that you want to return. Wrap elements inside of a parent element called dateInformation. Doing so keeps the XML formatted properly.

The actual data for output is easy to make into XML. Because you have an array of date elements already, you can loop through that with a foreach() loop and run the createElement and appendChild methods.

The end result of your efforts is XML that looks like this:

<dateInformation>

<friendlyDate>May 28, 2013</friendlyDate>

<unixTime>1369739047</unixTime>

<monthNum>May</monthNum>

<dayOfWeek>Tuesday</dayOfWeek>

<yearNum>2013</yearNum>

</dateInformation>

Returning JSON and XML

You now know how to return JSON data and how to return XML data. However, doing so means that you need to choose which one you want at programming time, and that can never change unless you reprogram the output. The world would be a better place if you could return both XML and JSON, depending on what the calling program wants.

Accomplishing this feat is a matter of accepting input for the web service and then providing appropriate output. Listing 2-7 provides the code for this web service.

Listing 2-7: XML and JSON date Web Service

<?php

if (isset($_GET['format'])) {

    $format = $_GET['format'];

    if (!preg_match('/json|xml/',$format)) {

        print "Please choose a format: json or xml";

        exit;

    }

} else {

    print "Please choose a format: json or xml";

    exit;

}

$friendlyDate = date("M d, Y");

$unixTime = time();

$month = date("M");

$dayOfWeek = date("l");

$year = date("Y");

$returnData = array(

        "friendlyDate" => $friendlyDate,

        "unixTime" => $unixTime,

        "monthNum" => $month,

        "dayOfWeek" => $dayOfWeek,

        "yearNum" => $year

);

if ($format == "xml") {

    $xml = new DOMDocument();

    $dateInfoElement = $xml->createElement("dateInformation");

    foreach ($returnData as $key => $value) {

        $xmlNode = $xml->createElement($key,$value);

        $dateInfoElement->appendChild($xmlNode);

    }

    $xml->appendChild($dateInfoElement);

    $output = $xml->saveXML();

    $header = "Content-Type:text/xml";

} else if ($format == "json") {

    $output = json_encode($returnData);

    $header = "Content-Type:application/json";

}

header($header);

print $output;

?>

It may be helpful to break this code down. The first part of the code looks for the format to be sent back:

if (isset($_GET['format'])) {

    $format = $_GET['format'];

    if (!preg_match('/^(json|xml)$/',$format)) {

        print "Please choose a format: json or xml";

        exit;

    }

} else {

    print "Please choose a format: json or xml";

    exit;

}

If a GET parameter of format is available, it's set to the $format variable. This variable is tested using the preg_match() function. This function uses a regular expression to check that the format parameter is set to json or xml (lowercase). If it isn't, an error is displayed, as is the case if theformat parameter is not set at all.

From there, the code performs the same functions that you’ve seen already, obtaining the date in various formats and placing them into an array. Finally, the code sets up a conditional based on the requested format. If it’s XML, then the XML-related code is executed; if the requested format is JSON, then the JSON-related code is executed. Finally, the output is sent to the browser.