Using JSON
Take a look at an alternate way to store our data, this time using JSON instead of XML.
JavaScript Object Notation, or JSON, is a way to specify information that lets you store data much in the same way you store it with XML, but in a much less verbose way. For example, a JSON representation of the data store looks something like Listing 32.
Listing 32. A sample JSON file
{
"statistics" : {
"total" : 2, "approved": 0
},
"fileInfo" : [
{
"status" : "pending",
"submittedBy" : "nickChase",
"approvedBy" : "",
"fileName" : "NoTooMiRoadmap.jpg",
"location" : "\/var\/www\/hidden\/",
"fileType" : "image\/jpeg",
"size" : 12813
},
{
"status" : "pending",
"submittedBy" : "stanley",
"approvedBy" : "",
"fileName" : "NoTooMiBeta.pem",
"location" : "\/var\/www\/hidden\/",
"fileType" : "application\/octet-stream",
"size" : 1692
}
]
} |
Look at a couple of different notations. Rather than having a workflow
root element, the data is part of a single object, and objects are delimited by curly braces ({}). So the
overall object is shown within the outer braces.
An object has one or more properties. In this case, the properties are statistics and
fileInfo.
The value of the statistics property [shown after the colon (:)]
is an object, which you can tell because it's wrapped in curly braces {}.
That object itself has two properties, total and approved,
both of which have integer values. The properties are delimited by commas.
Notice also that all strings—including the property names—are wrapped in quotes.
The value of the fileInfo property is a little different;
it's not an object, it's an array of objects, and you can tell that because it's wrapped
in straight brackets ([]). In this case, it's an array of objects,
each of which is wrapped in curly braces, which brings you right back to where you started.
To summarize:
- Objects are wrapped in curly braces.
- Objects are collections of properties, name-value pairs separated by a colon and delimited by commas.
- Arrays are collections of objects or properties, separated by commas and wrapped in brackets.
JSON objects are also capable of having properties that are functions, but that's beyond the scope of this tutorial. (See Resources for more information.)
Now you're ready to look at actually using this data.
The process of using JSON data in PHP generally has three steps:
- Create a PHP object that holds the data in a structure that matches the object you want to create. In some cases, you'll create that structure from scratch; in others you'll get it from an existing JSON data store.
- Manipulate the PHP object. This might consist of changing values, or it might consist of adding or removing data.
- Convert the PHP object to JSON and save it.
You will take these three steps using the workflow data.
What you see above is a text-based serialization of an object. To work with it in PHP, you need to turn it into an actual PHP object, which you can do in two ways.
- The first is to turn it into an actual object, which uses PHP's object notation,
such as:
$fileInfo->size
- The second is to use array notation, which looks at the data as an associative array,
which you saw earlier, as in:
$fileInfo["size"]
Which one you use depends on how you create the variable within PHP.
For two reasons you will use the array notation for this example. The first, and most obvious, is that you're familiar with arrays, but you haven't covered objects yet. The second is that certain operations, such as adding to a JSON array, are difficult or impossible using the object notation.
The first step is to create the basic PHP array to hold your data. To make things simple,
create the equivalent of one fileInfo element. In the scripts.txt
file, create the function in Listing 33.
Listing 33. Preparing the basic JSON object
function save_document_info_json($file){
$filename = $file["name"];
$filetype = $file["type"];
$filesize = $file["size"];
$fileInfo["status"] = "pending";
$fileInfo["submittedBy"] = $_SESSION["username"];
$fileInfo["approvedBy"] = "";
$fileInfo["fileName"] = $filename;
$fileInfo["location"] = UPLOADEDFILES;
$fileInfo["fileType"] = $filetype;
$fileInfo["size"] = $filesize;
} |
Although it's been re-arranged a bit, nothing here is really new. First you extract the values from the
$file array. Then you assign them to the newly (and dynamically) created
$fileInfo array.
This $fileInfo array represents a single object; if you saved it out as JSON
(which you'll do shortly) it looks something like Listing 34.
Listing 34. What the $fileInfo object looks like
{
"status" : "pending",
"submittedBy" : "stanley",
"approvedBy" : "",
"fileName" : "NoTooMiBeta.pem",
"location" : "\/var\/www\/hidden\/",
"fileType" : "application\/octet-stream",
"size" : 1692
} |
Next you'll see how to deal with nested properties.
So far you've dealt with a simple object, with simple values. Now you will see what happens when you delve a little deeper.
In the previous example, you built a single fileInfo object. Ultimately,
however, you'll build the overall workflow object, which includes
the statistics property. But the statistics property is
an object that itself has properties (see Listing 35).
Listing 35. Properties of properties
function save_document_info_json($file){
$workflow["statistics"]["total"] = 1;
$workflow["statistics"]["approved"] = 0;
$filename = $file['name'];
... |
As you can see, you create the $workflow variable, then the statistics
property. Once you have that, you can then specify the total and approved
properties.
You can proceed down this path to any level (within reason).
Now that you have the $workflow variable, you need to add the $fileinfo object
to its fileInfo property. Remember, fileInfo is an array, so you
can use the array_push() function to do that (see Listing 36).
Listing 36. Adding an item to an array
function save_document_info_json($file){
$workflow["fileInfo"] = array();
$workflow["statistics"]["total"] = 1;
......
$fileInfo["fileType"] = $filetype;
$fileInfo["size"] = $filesize;
array_push($workflow["fileInfo"], $fileInfo);
} |
The array_push() function adds one or more items to the target array. In this case,
that array is $workflow["fileInfo"], which you created as an empty array at the top of
the script. This is a typical (non-associative) array, so the first time you add an item, you can then
go back and refer to it as:
$workflow["fileInfo"][0] |
Now you're ready to look at saving the data.
At this point, you created a variable that represents the data structure you want to save as JSON, so it's time to look at serializing and saving that data.
PHP actually makes it incredibly easy to turn a variable such as $workflow into JSON text by providing the json_encode() (see Listing 37).
Listing 37. Adding an item to an array
function save_document_info_json($file){
$jsonFile = UPLOADEDFILES."docinfo.json";
$workflow["fileInfo"] = array();
$workflow["statistics"]["total"] = 1;
...
$fileInfo["fileType"] = $filetype;
$fileInfo["size"] = $filesize;
array_push($workflow["fileInfo"], $fileInfo);
$jsonText = json_encode($workflow);
file_put_contents($jsonFile, $jsonText);
} |
The json_encode() function converts the $workflow object to
JSON text, and then file_put_contents() saves that text to the docinfo.json
file.
You just tell the upload_action.php page to call this new function instead of the XML-based function (see Listing 38).
Listing 38. Calling the new function
...
if(!is_uploaded_file($tmpName) ||
!move_uploaded_file($tmpName, $newName)){
echo "FAILED TO UPLOAD " . $_FILES['ufile']['name'] .
"<br>Temporary Name: $tmpName <br>";
} else {
save_document_info_json($_FILES['ufile']);
}
... |
Now you can upload a new document and check the contents of the docinfo.json
file. It should look something like Listing 39 (spaces added for clarity).
Listing 39. The resulting JSON file
{
"statistics":{"total":1,"approved":0},
"fileInfo":[
{"status":"pending",
"submittedBy":"stanley",
"approvedBy":"",
"fileName":"NoTooMiBeta.pem",
"location":"c:\/sw\/temp\/",
"fileType":"application\/octet-stream",
"size":1692}
]
} |
So you didn't have to do any work to create the JSON notation; the json_encode()
function did it for you. Getting a variable back out is just as easy, and you'll see how to do that next.
Now that you can write JSON, how about reading it? Just as when dealing with XML,
you need to add new fileInfo objects to an existing docinfo.json
file. To do that, you can use relatives of functions that you've already seen (see Listing 40).
Listing 40. The resulting JSON file
function save_document_info_json($file){
$jsonFile = UPLOADEDFILES."docinfo.json";
if (is_file($jsonFile)){
$jsonText = file_get_contents($jsonfile);
$workflow = json_decode($jsonText, true);
} else{
$jsonText = '{"statistics": {"total": 0, "approved": 0}, "fileInfo":[]}';
$workflow = json_decode($jsonText, true);
}
$filename = $file['name'];
...
array_push($workflow["fileInfo"], $fileInfo);
$total = count($workflow["fileInfo"]);
$workflow["statistics"]["total"] = $total;
$jsonText = json_encode($workflow);
file_put_contents($jsonFile, $jsonText);
} |
Just as before, you first check to see if the file exists. If it does, rather than create the $workflow
variable from scratch, you first extract the existing JSON text using the file_get_contents()
function, then use the json_decode() function to turn that into a PHP variable.
Note the second parameters in the json_decode() function, which tells PHP whether to
return an array or not. The default value (in other words,
what the function will use if you don't provide a value) is false, which means that PHP will
not return an array, and will return an object instead. In this case, you wanted an array, so you provide
a value of true.
Just for the sake of an example, if the file doesn't exist, and you're creating things from scratch, this example
shows that you can just provide plain JSON text—after all, that's what you pull out
of the docinfo.json file anyway!
Finally, before you save the file, you can find out the number of fileInfo objects
by doing a count() on that array.
You can then set that value as the total.
Before moving on to actually using all this data, you need to know one very important thing.
The json_decode() function doesn't execute any code; it simply loads data. That
doesn't mean it's immune to security problems, because like other string-handling functions that are vulnerable
to buffer overflows and other techniques, it can be used for evil. That's an extremely rare situation.
The bigger problem is when you provide JSON data back to a JavaScript implementation. You might be tempted to use
JavaScript's eval() function to turn it into a JavaScript object. Unless you know
precisely what's in that data and that you can trust it, don't.
Use a JSON library intended for this purpose instead.
Now you can put together everything that you've learned so far to build a script that reads the JSON and displays the list of files on the upload_action.php page. Start by creating the function in scripts.txt (see Listing 41).
Listing 41. Reading the data
function display_files(){
echo "<table width='100%'>";
echo "<tr><th>File Name</th>";
echo "<th>Submitted By</th><th>Size</th>";
echo "<th>Status</th></tr>";
$workflow = json_decode(file_get_contents(UPLOADEDFILES."docinfo.json"), true);
$files = $workflow["fileInfo"];
for ($i = 0; $i < count($workflow["fileInfo"]); $i++){
echo "<tr>";
echo "<td>".$thisFile["fileName"]."</td>";
echo "<td>".$thisFile["submittedBy"]."</td>";
echo "<td>".$thisFile["size"]."</td>";
echo "<td>".$thisFile["status"]."<td>";
echo "</tr>";
}
} |
First get the $workflow object by using json_decode()
to analyze the contents of the docinfo.json file.
Once you have that, you can get the array that represents all the files by extracting the fileInfo
property, then loop through each one, displaying its information.
Now you need to add the function to the upload_action.php file (see Listing 42).
Listing 42. Calling the display function
...
if(!is_uploaded_file($tmpName) ||
!move_uploaded_file($tmpName, $newName)){
echo "FAILED TO UPLOAD " . $_FILES['ufile']['name'] .
"<br>Temporary Name: $tmpName <br>";
} else {
save_document_info_json($_FILES['ufile']);
echo "<h3>Available Files</h3>";
display_files();
}
} else {
echo "You need to select a file. Please try again.";
}
include("bottom.txt");
?> |
The end result is an HTML table that should look something like Figure 6.
Figure 6. Readable table of information
In Part 3, you'll look at enhancing this table with links and other information and functionality.




