Chapter six in “PHP The Good Parts” (MacIntyre, Peter B. PHP: The Good Parts. USA: O’Reilly, 2010) is about PHP objects. We visited object-oriented programming back in my posts on the ASP.NET/C# “Magic Menu” where I discussed some behind-the-scenes on object-oriented (or “oo”) concepts.
This post will be about what I programmed after reading about PHP objects – a doodle web app which started out as a simple little combination of PHP oo programming and some HTML5 canvas and turned into a wild blend of PHP, HTML5, Javascript, jQuery, CSS, AJAX and XML. The final result could have probably been accomplished with less technologies, but in the end it was fun to try out some “extras” in combination with the PHP and HTML5 and learn about those as well. You could say the code is a bit of “doodle” itself! Or I may be getting carried away with the doodle metaphor – on to the doodling app and it’s doodles (but not oodles) of code!
“artsyCoderDoodles” is a drawing, or more like a doodling, tool. It allows the user to choose a small or large canvas, select options for the shape, size of the shape and color of the shape, then click on the canvas to draw the shape. The tool includes some user instruction and a button to start over.
There are six files involved in this app:
php_objects_web_app.php
– PHP starting page
php_objects_web_app2.php
– PHP page called with AJAX
php_objects_web_app.js
– Javascript with AJAX and jQuery code for the PHP page
php_objects_web_app.css
– CSS for the starting PHP page
php_objects_web_app.xml
– XML file for the PHP page called with AJAX
jquery-1.6.4.min.js
– jQuery library file
The best way to describe how it was built is to travel through the action in a logical flow rather than file by file in a specific order, so bear with me and we’ll doodle our way through it! I know, I can’t stop with the “doodles”! Bear with that also please!
php_objects_web_app.php
First of all, what do we want to do here? We want to provide a drawing (doodling) tool. It would be easy enough to just build this tool client-side, using HTML5 canvas and Javascript. However, I wanted include some object-oriented PHP within it.
In the book, the examples show how you can use oo PHP to write HTML tags to your page. So my first idea was to write all my output for the canvas to the page, thinking a circle could be a PHP object, a square could be another object, etc. But I thought more and realized that since most of the drawing is actually done with Javascript, a client-side scripting language, it might not make the most sense to use PHP, a server-side scripting language, to write Javascript to the page.
Instead, I focused on what HTML tags I could write to the page that were part of the tool. I also had to think about how to “kick-off” my PHP object creation, as I didn’t want to have the PHP file write the whole page as previously decided, but I would need to get things started in some way. I decided to add a little fun element by allowing the user to choose the size of the canvas they would like to use – i.e. “doodle a bit?” or “doodle a lot?”. Then I could have a PHP class that would create the HTML 5 canvas
tag and write it to the screen, for a start.
That was all fine and good but how would I technically kick-off that PHP object creation code? Particularly since I wanted to do this when the learner makes a choice on the screen, and that choice was going to be made with Javascript (perhaps an onclick
event on an HTML element).
Was there a way to call a PHP function from Javascript? I did some searching and discovered, well no, which makes sense.
PHP is server-side code. Once it executes, and writes HTML to the page, it’s essentially “gone” on the client-side (i.e. your browser) – even though the file you are looking at still has the extension “.php”. Now it’s up to the client-side script, like Javascript, to take the lead. That Javascript can not access your PHP functions directly because they are not available on the client side.
There are essentially two ways to call more PHP back on the server at this point and have things happen in your browser:
- submit a form which uses a PHP file to process it – this will replace your current PHP file with this new file (which can be your current file again just to note) – this means generally you will lose what you are seeing on your browser, unless in vague terms you are storing the results of your actions somehow (if you have been taking some action) and writing everything out again with the newly loaded PHP file
- run a PHP file using AJAX, which stands for Asynchronous JavaScript and XML – a bit of technology which runs the PHP file and updates your page back on your browser, but without reloading the page. So in a way you can think of AJAX allowing PHP to act like Javascript.
I had already used the first method in my past PHP articles while learning this book, but the second sounded like the way to go with this web app. Mainly because although you will see I basically rewrite the contents of the HTML after the users first choice which would suggest the first method, I would want to update things later on in my HTML without losing the lovely doodle I was working on, thus using AJAX was the better alternative.
Still with me? Keeping this all in mind, let’s take a look at this first PHP file. It’s so simple – hard to imagine that everything works from this first page! We start off with some standard HTML5 code to set up our page, include our CSS and JS files and provide some introductory information, similar to my PHP web apps of the past.
Our highlighted line here shows how we give the user some choices of canvas size. This code has some Javascript calls on click of the choices (via javascript:
on anchor <a href...
tags), and those calls are going to evoke our AJAX which is defined in our Javascript file (more on that in a bit…). Things look pretty standard for now!
<!DOCTYPE html> <html lang="en"> <head> <title>PHP: Objects Web App</title> <link href="php_objects_web_app.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="jquery-1.6.4.min.js"></script> <script type="text/javascript" src="php_objects_web_app.js"></script> </head> <body> <div id="mainContent"> <div id="mainIntro"> <strong>Welcome to artsyCoderDoodles!</strong><br><br> Would you like to...<br><br> <a href="javascript:runAJAX('init','small');">doodle a bit?</a> or <a href="javascript:runAJAX('init','large');">doodle a lot?</a> </div> </div> </body> </html>
Alright, so what happens when the user clicks on “doodle a bit?” or “doodle a lot?”. On to our Javascript file…
php_objects_web_app.js
Before we get started on the AJAX code, let’s give credit where it’s due to a nice simple explanation and implementation of AJAX in this w3schools.com AJAX Tutorial which helped me out a great deal. (Note, this is a new 2017 link.)
At the top of our Javascript file, we have definition of a few arrays which we will return to later. Let’s zone in on the runAJAX
function evoked by the click of the links on the main PHP page.
First, runAJAX
is passed two parameters – func
which is a flag we will use in a bit to determine what we want to do in our AJAX code, and param
which is some data we will want to pass along to the PHP file when we call upon it with AJAX – in this first instance these two parameters are “init” and the size of the canvas the user has chosen.
Next, we create an AJAX object, and as you see, most modern browsers do this one way, and older IE does this with an ActiveXObject.
// function to peform AJAX calls to PHP file with parameters and return data function runAJAX(func, param) { // create new AJAX object var xmlHttp; if (window.XMLHttpRequest) { // IE7+, Firefox, Chrome, Opera, Safari xmlHttp=new XMLHttpRequest(); } else { // IE5, IE6 xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); } ... }
Now we’re going to take some action with that AJAX object based on our parameters passed into runAJAX
. We call xmlHttp.open
to send a request to the server, in particular to our second PHP file containing the class definition etc. We call this using a “GET” method, a simple method to use that works in this case, and we tell the AJAX to run “asynchronously” (the true
in the call), which allows the Javascript to keep running along while AJAX does it’s thing in the background.
Check out the call to the PHP file, it has a lot of things going on within it.
php_objects_web_app2.php?f=" + func + "&p=" + param + "&t=" + Math.random()
We are sending the func
and param
parameters along to the PHP file by appending them to the filename, as well as another random parameter which is supposed to help ensure we avoid caching the PHP file. Caching is basically the act of the browser holding onto bits of info on the client-side to save time and effort requesting them each time from the server – can be beneficial when you think of downloading interface images that do not change much and not so beneficial in the situation where you want to send fresh data and have fresh results each time. We are in the latter situation here.
We send the request with xmlHttp.send
. AJAX will let the Javascript know when it’s ready, and that leads into the next section we will talk about.
The xmlHttp.onreadystatechange
event looks for a change in the state of the AJAX server request and performs some action based on the status we isolate. In our case we are looking for indication that our request is done and we’ve got a response – xmlHttp.readyState
is 4
and the xmlHttp.status
is 200
(as opposed to that familiar 404
meaning “page not found”.)
Now here is where we are going to process whatever data the PHP file sent back to us. We don’t know yet how the PHP sent that data but we will…you guessed it…get into that in a bit!
For now just understand that we sent a request, we got word back at some point while we were still charging along on our merry way that our request is done and we have a response – so now it’s time to do something with what we received.
Let’s first examine what we received when we sent along the AJAX request with “init” and canvas size parameters. This response is going to create our canvas for us, along with some options for the drawing tool, instructions, and a start over button – and set up some actions to occur on click of those doodle options to boot. Well guess what, the second PHP file sent us a big old string as a response which contains all of this HTML code – canvas
tag, an unordered bulleted list of our option headings, some instruction to the learner and a button to start over. This can be popped right into the body
tag of our starting PHP page with a little jQuery $("body").html(xmlHttp.responseText);
.
The next few lines use more jQuery to define a click
method for the canvas, which will call a Javascript function draw
. It also defines a click
method for the option headings, which hides the instructions and button to start over, and then calls runAJAX
again, this time passing in the func
“opts” and param
that is the name of the option heading (“size”, “color”, “shape”). We will discover what that AJAX does in a bit.
// take certain action based on what the AJAX call returns xmlHttp.onreadystatechange=function() { if (xmlHttp.readyState==4 && xmlHttp.status==200) { if (func == "init") { // create canvas, option headings, instruction and start over button on screen $("body").html(xmlHttp.responseText); // allow canvas to be drawn on with a click on the canvas $("#artTool").click ( function() { draw(); }); // add options under option heading by making an AJAX call to obtain them on click of the option heading $(".artOption").click( function() { // hide the user instruction and start over link $(".instruction").hide(); $(".startOver").hide(); runAJAX("opts",$(this).html().split("<span")[0]); }); } ... } } // perform AJAX call to the PHP file with parameters xmlHttp.open("GET","php_objects_web_app2.php?f=" + func + "&p=" + param + "&t=" + Math.random(),true); xmlHttp.send(); } [/sourcecode] So where are we...we have seen how AJAX in our Javascript file works to write basically our whole drawing tool to the page based on a response from the second PHP file on the server. We must next find out what that PHP file is up to! <h2>php_objects_web_app2.php</h2> In the PHP file, we will find a class definition called <code>artTool</code>. In this class, there is a private property called <code>$tag</code>, as well as three methods, two of which are show below. The first method, <code>createCanvas</code> is what we will use to create the canvas tag for the drawing tool, by passing it the size the learner chose back on the first PHP page. We will store a canvas tag as a string in the <code>$tag</code> property based on that size. From within this method, we call the second method, <code>createOptionHeadings</code>. Notice how we refer to the private property and the second method using <code>$this</code>, which means we are evoking the property and method belonging to our <code>artTool</code> object that we have created with this class. <code>createOptionHeadings</code> is where I tried out some XML, Extensible Markup Language, which allows you to define your own tags and store data for later parsing and usage in other applications, in my own basic terms. A benefit of using XML could be that it abstracts or separates the data from the code and in combination with AJAX this could mean easy updates later on in the XML which could be reflected immediately in the application as the data is accessed. I stored the option headings and the options within XML. In the future, this could be improved upon by storing more information on the options, such as values for the colors, but for the purpose of this app, storing this amount of info was fine. I referenced this <a href="http://www.w3schools.com/php/php_ajax_xml.asp" target="_blank">w3schools.com PHP Example - AJAX and XML</a> tutorial to help with parsing through XML using PHP. We proceed to create a new <code>DOMDocument</code> object and load in our XML file, then travel through all the XML tags called <code>opts_title</code> and create a string of the option heading tags to write to the page by accessing the <code>nodeValue</code>, which is the text between the starting and closing XML tag (<code>opts_title</code> in this case). We also include some default values of "medium" for size, "red" for color and "circle" for shape (which again, in future could be defined as part of the XML). This is all appended to the <code>$this->tag</code> which means that it is added on to the canvas tag string. We then append HTML code for user instructions and a "start over" button which just reloads the starting PHP page. The most important line to finish things off is to echo out the string stored in <code>$this->tag</code>, which is if you will recall, what the AJAX code back in the Javascript file will receive for a response and then deal with as required. In our case here it will write that string of HTML to the <code>body</code> tag of the page, creating our drawing tool. /* artTool class */ class artTool { /* $tag private property */ private $tag; /* createCanvas method which creates a canvas tag specific to the size chosen by the user and sent by AJAX, assigns this HTML code to the $tag private property and then calls the createOptionHeadings method */ function createCanvas($size) { if ($size == "small") { $this->tag = "<canvas id='artTool' width='200' height='200'></canvas>"; } else if ($size == "large") { $this->tag = "<canvas id='artTool' width='500' height='500'></canvas>"; } $this->createOptionHeadings(); } /* createOptionHeadings method which pulls in the headings for the options from XML, then appends them, the user instruction and the start over button to the $tag private property containing the canvas tag and echos this HTML code to the page where the AJAX code uses it */ function createOptionHeadings() { $this->tag .= "<ul class='artOptions'>"; $xmlFile = new DOMDocument(); $xmlFile->load("php_objects_web_app.xml"); $xmlOpts=$xmlFile->getElementsByTagName('opts_title'); for ($i=0; $i<=$xmlOpts->length-1; $i++) { $this->tag .= "<li class='artOption'>" . $xmlOpts->item($i)->nodeValue . "<span id='" . $xmlOpts->item($i)->nodeValue . "_desc'>"; if ($xmlOpts->item($i)->nodeValue == "color") { $this->tag .= ": <strong>red</strong>"; } else if ($xmlOpts->item($i)->nodeValue == "size") { $this->tag .= ": <strong>medium</strong>"; } else if ($xmlOpts->item($i)->nodeValue == "shape") { $this->tag .= ": <strong>circle</strong>"; } $this->tag .= "</span><br><div id='" . $xmlOpts->item($i)->nodeValue . "Options' class='artOptionsList'></div></li>"; } $this->tag .= "</ul><br><br><br><br><div class='instruction'><strong>How to use artsyCoderDoodles:</strong><br><br>Select options above and click on the canvas to draw.<br><strong>Hints:</strong> Be sure to scroll to the top first and use a small white shape as an eraser.<br>This web app demonstrates usage of object-oriented PHP, AJAX, XML, HTML5 canvas, Javascript, jQuery and CSS.</div><br><strong><a class='startOver' href='php_objects_web_app.php'>start over</a></strong>"; echo $this->tag; } ...
php_objects_web_app.xml
Let’s hop over to the XML file for a second and see what that looks like as well.
<art_tool_options> <option> <opts_title>shape</opts_title> <opts> <opt> <opt_title>circle</opt_title> </opt> <opt> <opt_title>square</opt_title> </opt> </opts> </option> <option> <opts_title>color</opts_title> <opts> <opt> <opt_title>red</opt_title> </opt> ...
But wait, we have to take a look at how this class is brought to life. Well you recall how we used AJAX back on the first PHP page to call this second PHP page passing parameters with the filename? At the bottom of the second PHP page, outside of our class definition, the following code is present. This code pulls the parameters from the filename with $_GET
and then creates a new artTool
object from the class definition. Depending on the parameter passed for $func
, we then call a method of the class on the object, which at this point in time will be createCanvas
, passing it the second parameter $param
.
php_objects_web_app2.php – REVISITED
/* get the two parameters sent by AJAX */ $func = $_GET["f"]; $param = $_GET["p"]; /* based on the first parameter sent, create a new artTool object and call a specific method passing the second parameter */ $artTool = new artTool(); if ($func == "init") { /* call method to create the canvas of a specific size and option headings if this is the initialization of the web app */ $artTool->createCanvas($param); }
So at this point we pretty much have a brand new HTML page containing our drawing tool. At this point, there are two actions that can occur. The user can click on the canvas, which will draw something, or they can click on an option heading which will show a list of options for them to select.
Let’s take a look at the drawing aspect. This is accomplished simply by a call to a Javascript function draw
that has been assigned to the click event of our canvas as you may recall. All of the drawing is done with HTML5 canvas using Javascript. Back to the Javascript file!
php_objects_web_app.js – REVISITED
Recall that there were a number of arrays we skipped over when first visiting the Javascript file? These arrays are the additional data related to the options that are needed to draw with, and eventually would be better stored in the XML file as noted. We define “rgba” values for the colors – note that the “a” stands for “alpha” and here it is 0.5 which means the colors will be semi-transparent, which allows for some nicer layering effects I find with the drawing tool. We also define numeric values for sizes of each type of shape that will be used in drawing them.
In creating this drawing function and arrays I referenced the following Canvas tutorial – Mozilla Developer Network site.
// define mapping data arrays var colorMapArray = new Array(); colorMapArray["red"] = "rgba(255, 0, 0, 0.5)"; colorMapArray["blue"] = "rgba(0, 0, 200, 0.5)"; ... var sizeMapArray_circle = new Array(); sizeMapArray_circle["small"] = 10; sizeMapArray_circle["medium"] = 50; ... var sizeMapArray_square = new Array(); sizeMapArray_square["small"] = 20; ...
Later on in the Javascript file, we define the draw
function.
To begin, the drawing options are hidden and the instructions and start over button are shown. This was a simple way to deal with showing and hiding these. In the future, it would be ideal for the instructions and start over to always be present and the options to be hidden as soon as an option selection is made.
We determine where the user clicked on the canvas with event.clientX
and event.clientY
. What’s great about canvas is it doesn’t care if you click outside of the canvas area, because it just won’t be able to draw in that spot, so no to need to check for boundaries. However, I did find I had to subtract six from the x/y coordinates to center the mouse pointer as best I can in the middle of the shape so that the user is creating the shape where they clicked.
From this point on, we basically pull the options selected from the tool interface then find the canvas on the page and get its 2d
context so we’re ready to draw on it. Then based on the options chosen, we access data in the arrays and draw a circle or square at the location clicked with the size selected, filled in with the color selected.
// draw on the canvas based on the options currently selected function draw() { // hide the option lists $(".artOptionsList").hide(); // show the user instruction and start over link $(".instruction").show(); $(".startOver").show(); // determine where the canvas was clicked var posX = event.clientX - 6; var posY = event.clientY - 6; // determine shape, color and size chosen from the options var shapeChosen = $("#shape_desc").html().split("<strong>")[1].split("</strong>")[0]; var colorChosen = $("#color_desc").html().split("<strong>")[1].split("</strong>")[0]; colorChosen = colorMapArray[colorChosen]; var sizeChosen = $("#size_desc").html().split("<strong>")[1].split("</strong>")[0]; sizeChosen = eval("sizeMapArray_" + shapeChosen + "[\"" + sizeChosen + "\"]"); // find our canvas and get ready to draw on it var canvas = document.getElementById("artTool"); if (canvas.getContext){ var ctx = canvas.getContext("2d"); // set the fill color of the shape and draw it with the size chosen ctx.fillStyle = colorChosen; if (shapeChosen == "circle") { ctx.beginPath(); ctx.arc(posX,posY,sizeChosen,0,Math.PI*2,true); ctx.fill(); } else if (shapeChosen == "square") { posX = posX -(sizeChosen/2); posY = posY -(sizeChosen/2); ctx.fillRect(posX,posY,sizeChosen,sizeChosen); } } }
For our final act, we return to some AJAX and figure out how those options are appearing under the option headings. We travel a little further up back into our runAJAX
function in the Javascript file. You may recall that once we processed our first AJAX call and wrote our drawing tool to the page, we assigned this function to run on click of an option heading, passing the parameters of “opts” and the option heading chosen.
This in turn calls to the PHP file again, and returns to us an unordered bulleted list of options to display under the heading, which we then show. We also assign a click event to the options themselves which changes the current option displayed onscreen to now be this option.
else if (func = "opts") { // get and show the options under a heading $("#"+param+"Options").html(xmlHttp.responseText); $("#"+param+"Options").show(); // change the heading to include the option that has been selected on click of an option $("."+param+"s").click ( function() { $("#"+param+"_desc").html(": <strong>"+$(this).html()+"</strong>"); }); } } }
Let’s pop back into the second PHP file again to see how it sends this list of options.
php_objects_web_app2.php – REVISITED (2)
The method getOptions
in the artTool
class basically searches for the option heading in the XML that has been passed to the PHP file as part of the AJAX and when it finds it, locates it’s parent, which is the <option>
tag.
Through this tag, we access the third childNode
which is <opts>
, and then travel through the <opt>
nodes within that and extract the option values. These get assigned as part of a string of HTML code that goes in $this-tag
which then gets echoed to the page and processed by our AJAX in the Javascript.
Note, the XML parsing is effective here, but I think I still have some things to learn about how to best organize the XML and parse it, including a better understanding of the types of nodes that exist, so that I can avoid drilling into so many levels of the XML to obtain a simple amount of data.
/* getOptions method pulls in the options for the option heading chosen by the user and sent by AJAX, assigns HTML code to the $tag private property, and echos this HTML code to the page where the AJAX code uses it */ function getOptions($title) { $xmlFile = new DOMDocument(); $xmlFile->load("php_objects_web_app.xml"); $xmlOpts=$xmlFile->getElementsByTagName('opts_title'); $xmlOptChosen = ""; for ($i=0; $i<=$xmlOpts->length-1; $i++) { if ($xmlOpts->item($i)->nodeValue == $title) { $xmlOptChosen=($xmlOpts->item($i)->parentNode); } } $xmlOptChosen=$xmlOptChosen->childNodes->item(3)->childNodes; for ($j=0; $j<=$xmlOptChosen->length-1; $j++) { // process the element nodes which have child nodes if ($xmlOptChosen->item($j)->nodeType==1) { $this->tag .= "<span class='" . $title . "s' id='" . $title . "_" . $xmlOptChosen->item($j)->childNodes->item(1)->nodeValue . "'>" . $xmlOptChosen->item($j)->childNodes->item(1)->nodeValue . "</span><br>"; } } echo $this->tag; } }
Further down in the PHP file is where we call to action this method by creating an artTool
object again and evoking the getOptions
method on it.
else if ($func == "opts") { /* call method to get the options for the option heading sent if this is when the user clicks on an option heading */ $artTool->getOptions($param); }
Okay, so we’ve been through all our PHP, HTML5, Javascript and XML code now, all that remains is to take a peek at the CSS file.
php_objects_web_app.css
There is some more CSS occurring here than in past web apps. We have a lot of ids and classes on various elements to help the jQuery find them and then perform actions.
body { background-color: #ffffff; font-family: Arial, Sans-Serif; font-size: 16px; max-width: 600px; } canvas { border: 1px solid black; } #mainContent { position: absolute; left: 0px; top: 0px; margin: 0px; padding: 0px; } #mainIntro { text-align: center; padding: 50px; } .artOptions { position: absolute; left: -40px; list-style: none; } .artOptions li { cursor: pointer; float: right; margin-left: 10px; } .artOptionsList { cursor: pointer; border-left: 1px solid red; padding: 5px; display: none; } .instruction { font-size: 14px; font-style: italic; border-left: 1px solid red; padding: 5px; }
Final word, notice my “hints” to the user. The first is to scroll to the top of the page, because the way this currently works, if you are scrolled down, your position on the screen will still think it is up higher on the canvas even though you are clicking lower on the canvas, so the doodle may not appear where you are expecting.
Additionally, outside of some possible plug-ins or libraries that allow you to erase what you just drew on the canvas, there is no quick and easy way to do this, so I gave the user the option of “erasing” with the white shape and the additional “start over” button, which takes you back to the initial screen where you select the canvas size.
A long post but hopefully an informative one! Now take a break from all that code and doodle it up!