PHP: Hypertext Preprocessor formerly: Personal Home Page A walk on the server side As anticipated in previous courses, PHP is a server-side scripting language. As such, its capabilities are much stronger than those of client-side languages: server-side scripts have direct access to the to the resources of Web servers (at least, those who grant it), and, maybe most importantly, they have data processing capabilities, which allow them to affect data through Web applications. But there’s a lot more, due to the fact that all these activities take place through the Internet, with all the related problems of data synchronization: for PHP programmers, the real difference is in the whole internal engine. PHP is a pre-processor, which prepares and sends complete HTML code to the browser (and hence to servers). For this reason, it has a somewhat wicked logic, as data must be accessed before building the page. Dynamicity is still ensured by HTML and JavaScript elements; yet, the changes brought by those actions are limited to the browser window, until some PHP mechanism updates the actual data on the server. PHP has some quite advanced functionalities (such as OOP features). This part of the course, however, will focus on the most basic, which is to access the server file system. By adding yet another management language (SQL1), it will be possible to view data through a database logic, and process them accordingly. But before then, the inner workings of PHP must be firmly grasped. Syntax PHP scripts can be embedded in HTML documents by enclosing them in a pair of delimiters (<?php and ?>), which, unlike JavaScript (<script>... </script>) identify a single tag. This is one of the details that show how the logical infrastructure of the Web was built piecemeal, with separate, distinct contributes. PHP syntax being very similar2 to C/C++, many of its statements, including comments, will look familiar. There is a twist, however, as most PHP code would not compile in a C program, because, in a most peculiar way, PHP variable names require a mandatory dollar sign ($) prefix3. Also, there are no variable declaration directives4. So PHP code has a slightly different feel5, as shown in following example: <?php if ( $x > 0 ) { $x = 2 * $x; // inline comment $a = 0; } else /* free comment (also multiline) */ $a = $b * $c; for ( $i=1; $i<=10; $i++) $c/=10; ?> As in JavaScript, both stray statements and functions are allowed. PHP is also loosely typed, though it has somewhat stronger type checking mechanisms. Here is an example of a function: function prime($n) { $i=2; while( $i<=$n/2 && $n%$i!=0 ) $i++; return ($i>$n/2 && $n>1); } 1 BEWARE !!! PHP variables are not declared. The one exception refers to the scope (visibility) of the variables. Structured Query Language. The version chosen for this course is MySQL. With the same proviso applying to JavaScript, the syntax may look almost the same, but it’s the engine underneath that is totally different. 3 This may ring a bell to programmers of old versions of BASIC, where a dollar sign postfix was the one and only way of declaring a string variable (e.g.: x$). 4 Actually, there is one, but it specifies the variable scope, not its type, nor its very existence. 5 This way, though, PHP variables will not easily be mistaken for database field names or function calls, so there may be a logic to it. 2 The environment PHP developers need much more than just a browser, as their applications work over the Internet. So, just to get started, we need to access a Web server: arguably, the most popular among open-source HTTP servers is Apache (from the Apache Software Foundation). Then, after learning the basics of the language, we will need data processing capabilities: again, we will resort to the popular choice among open-source Web developers, which is MySQL, a relational database management system6. This software, however, is impractical to use for data maintenance: the definition and the organization of MySQL databases can be done much more easily with an administrative tool, such as the very popular PHPMyAdmin. The three components can be found in the respective Web sites: http://httpd.apache.org http://www.mysql.com http://www.phpmyadmin.net Installing separately all the programs, though, can be a little annoying, especially for the uninitiated. But the real problem is not in the software itself. The fact is that it would be almost impossible to write and test our programs in direct contact with a server: development time could increase dramatically, due to the latency of the Internet infrastructure, as every page would require to be uploaded to the server before being examined in detail. Fortunately, there is a global solution to both of these problems, setting up a personal (i.e.: local) Web server. The whole Web environment can be simulated locally, on any computer, with a software bundle, called EasyPHP, which smoothly combines and coordinates all the aforementioned programs. Under GNU/Linux, there is plenty of such software bundles, one of which is called LAMP (standing for Linux Apache MySQL PHP), covered in many Web sites. On other systems, another program described as similar to EasyPhp is called MAMP. It must be noted, however, that any solution developed locally should be, sooner or later, uploaded and tested on an actual Web server for the final test: what’s more, the test should be done with different browser, to check that the respective idiosyncrasies do not affect the outcome of the application. For reference and download, some choices are the following: EasyPHP LAMP MAMP EasyPHP 6 http://www.easyphp.org http://www.howtoforge.com/installing-apache2-withphp5-and-mysql-support-on-ubuntu-13.04-lamp http://www.mamp.info/en/index.html LAMP MAMP DBMS (DataBase Management System) is the historical acronym for specialized data processing software. The relational model, pioneered by the renowned masters Edgar Codd and Chris Date, is the most widely used, having superseded other data organization schemes. The concepts behind relational databases are quite advanced, so they will be studied only in the basic details. Installation and setup The setup program is a classic wizard, which leads through the usual installation steps (installation language, license agreement, destination folder). Everything can be left to the default values. At the end of the procedure, EasyPHP will be added to the program list, with its easily recognizable icon (a lower-case block “e” with a red dot) When we start it, we can see from the splash screen7 that EasyPHP is not the actual software: what it does is to act as a launchpad for the activation of the two actual engines (Apache and MySQL). Once we see the green traffic lights, our computer is ready to simulate a complete Web environment. If, for any reason, one of the servers should be stopped (showing a red traffic light), it could be resumed by clicking on the corresponding button, then choosing Start. What we need right now is the Web engine (Apache), which is capable of translating PHP code into HTML. The database engine (MySQL) will be used later. All the administrative tools (including PHPMyAdmin) are not visible in the spash screen, but they can be activated with a right-click menu on the active EasyPHP icon (choosing Administration). As it happens, the very first thing to do is activating the administration page for some housekeeping, that is, telling Apache where to find the local pages. 7 A splash screen is an image that appears while a program is loading; the term is also used to describe an introductory page or window. Setting up the local folders The administration page is mainly used to associate local folders to each simulated Web site: this is done in the section labeled LOCAL FILES. The other relevant feature, though not to be used right now, is the “open” button for PhpMyAdmin (both features are outlined in red, and marked by an arrow). Each local folder is identified by an alias: of course, right after the installation the list will be empty. Aliases can be added (clicking the grey link add an alias , immediately to the right of the label LOCAL FILES) or deleted (clicking on the cross to the far right of the corresponding grey line), but, surprisingly enough, they cannot be edited: changing an alias requires deleting it, then setting up a new one. The dialog box for a new alias is quite simple. Step 1 (Create a directory) may already have been done; here, the alias name (2) is linked to the specified folder (3) after the user clicks on the Save link: Once in the list, the alias functions as a link to the corresponding folder. If the folder contains a page named index.html or index.php, it will be activated automatically, as in actual Web sites (if both are present, preference is given to the html page). If not, the new page will show the content of the folder, with links on every page or subfolder present. Accessing the local host Being server-side means carrying a heavier burden than those light client-side scripts. PHP processes a whole document, stored on a Web server (Apache or whatever), before sending any code to the browser. As we will see, this implies going through a parsing of the entire PHP page, which is then translated to pure HTML/JavaScript. The other implication is that, when working locally (i.e.: on our own computer), the pages must be stored in the “local Web” folder, or one of its subfolders8. They also must be opened when the local server is active, as the browser works, by default, only on the client side. In fact, when we open a PHP page locally, the IP address 127.0.0.1 on the URL line shows that we are linked to the local host: http://127.0.0.1/test site/thisPage.PHP 127.0.0.1 is a special address, reserved for debugging purposes (it cannot be assigned, either as public or private). It can be used, for example, to test if the ISO-OSI protocol stack is working. But these considerations hardly affect the process of writing Web pages, as the local server is an adequate substitute for an actual server, at least in the development process. The huge difference, from the programmer’s point of view, is in how the Web page is put together: whereas in DHTML we write the static HTML part, then embellish it with scripts that work on elements already known to the browser, here a whole page must be built before being sent to the browser for rendering. We still can add some client-side scripts, but it is the structure of the page that must be built in advance. The first experiment that we can make is to write a minimal page, displaying one of the classic welcome messages, then save it as a PHP file (for example, test.php: note that the php extension is mandatory). Any of the following could give the idea: <?php echo 'hi there'; ?> or <?php echo "hi there"; ?> The echo statement is the PHP way of displaying a string on the Web page. PHP code being embedded in HTML, anything outside the script, if present, should adhere to the rules of HTML. For this example, though, we do not need any HTML, as the browser will just display the message between the quotation marks, which can be either single or double, like in JavaScript9. NOTE When more than one statement are present, the semicolon (;) is necessary, or the pre-processor would give an error message (and rather unclear, at that). The syntax of PHP is loose, but not to that point. There is another, almost equivalent, command: <?php print 'hi there'; ?> print is hardly used for the following reasons: being a function, it spends some time in returning a value, so it is slower than echo; while echo accepts any number of comma-separated parameters, print accepts only one. The examples show the difference between the two, as well as how these statements work with or without parentheses (except when echo takes a parameter list): // echo accepts multiple arguments echo "one 2 three 4<br>"; echo("one 2 three 4<br>"); echo "one ", 2, ' three ', 4, '<br>'; // print() can only take one parameter print(" singleton <br>"); print " singleton <br>"; $x = print "We sail the ocean blue"; 8 // single parameter // single parameter // comma-separated (NO PARENTHESES) // returning a value In this course, we will not use actual servers. Anybody with access to a Web server, however, can upload there the exercises, giving them legit, public URLs. 9 Yet, unlike JavaScript or other programming languages, which quotation mark is used can make a huge difference. More on that later. Under the hood The action of sending strings to the browser through a PHP script can, of course, be accomplished by building a regular HTML document, with all the tags in place (provided the filename extension is still PHP). But here we discover the trick of the pre-processor. When we visualize the source code from the browser window, we see that what PHP actually does is to prepare the HTML code line by line, adding the result of its echo statements to the normal flux of the page. PHP CODE <html> <head> </head> <body> <?php echo 'hi there'; ?> </body> </html> WHAT’S IN THE PAGE <html> <head> </head> <body> hi there </body> </html> We could go to the extreme of echoing the whole page, even using PHP variables to store the content of the message, and nothing would change. By the way, remember that variable names have a dollar sign prefix: PHP CODE <?php echo '<html>'; echo '<head>'; echo '</head>'; echo '<body>'; $a = 'hi there'; echo $a; echo '</body>'; echo '</html>'; ?> WHAT’S IN THE PAGE <html> <head> </head> <body> hi there </body> </html> What happens, basically, is that PHP scripts, be they stray statements or function calls, must leave their trace as HTML code written on the page. Simply put, this language is a producer of HTML code. Yes, but where? If this question were asked in the JavaScript course, there would be no definitive answer, only some suggestions. In PHP, there are no doubts, because script output (echo) builds HTML code (after all, PHP is a pre-processor), so that whatever is sent as output through the echo command becomes part of the Web page. This means that, while PHP functions can (and should) be stored in libraries, at least some scripts will end up in the heat of the battle, where the corresponding HTML code is to be placed. The implications of this behavior are quite straightforward: echo output should adhere to the HTML syntax; there will invariably be stray PHP statements somewhere, or PHP functions would never have a chance to be put into action10; the place for PHP code is wherever needed in the normal flow of the page. PHP is neither event-driven nor interactive in itself. It is the resulting DHTML code that allows for that. NOTE As we will see, the very way PHP builds Web pages is the reason for its wicked data processing mechanism. But more on this later. FINAL ADVICE The golden rule is still: keep as much PHP as possible away from HTML. 10 Functions quietly sit, waiting to be called. But while JavaScript functions can answer to events happening on the page, PHP functions can only be activated through other PHP statements. PHP is not dynamic. When PHP met JavaScript, and the secret of the quotes PHP was not designed for interactivity: after all, its purpose is to pre-process and build HTML documents. In those cases where results just need to be shown (mainly for debugging purposes), there is still old hand JavaScript to help. For literal messages, or those involving only HTML elements, there is not even need for PHP, as JavaScript could suffice. So, in the following snippet, the two PHP statements below are just another version of the first DHTML line, and bear little significance, except for showing the difference between quotation marks: <script>alert("hi there");</script>; <?php echo "<script>alert('hi there');</script>"; echo '<script>alert("hi there");</script>'; ?> <!-- in HTML code --> // faster The literal delimited with single quotes translates faster than the other. The reason is that double quotes are a little more demanding, as they allow a mechanism called macro expansion11, which, to put it simply, is to store part of the echo string into a variable. This is shown in the following snippet, as opposed to a more traditional sequence of strings. We will also introduce the most peculiar symbol for string concatenation, which, quite surprisingly, is not a plus sign, but a dot (.) operator: $x = 87; echo "the value is: $x<br>"; echo 'the value is: ' . $i . '<br>'; String concatenation with the . operator So, fixed strings are best processed with single quotes, and macro expansion can be used whenever they make a variable expression simpler. For example, echoing a HTML list could be done in several ways, some of which really horrible. Of course, even the “good” method is not really so, as it should involve an array of choices, to be visited with a basic loop. But here we only need to get to the idea; more on arrays later. /* Not too bad */ echo echo echo echo echo '<ul>'; "<li>$var1</li>"; "<li>$var2</li>"; "<li>$var3</li>"; '</ul>'; Should require an array /* From bad to worse choices (violating the KISS principle). */ echo "<ul><li>$var1</li><li>$var2</li><li>$var3</li></ul>"; $x = '<ul>'."<li>$var1</li>"."<li>$var2</li>"."<li>$var3</li>".'</ul>'; echo $x; PHP has some flexibility, when it comes to automatic casting. We can mix strings with boolean or numeric values, but the dot operator must be separated with spaces, lest it be confused with a decimal point: $a = '2' . true; $a = '2' . 4; $a = 2 . 4; // // // string (also: '2' .true; '2'. true;) string (also: '2'. 4; wrong: '2' .4) string (wrong: 2. 4; 2 .4) The assignments written in red will not be accepted, singling out the number as the offending item. The resulting page will contain the following message: Parse error: syntax error, unexpected '4' (T_DNUMBER) ... (or: '.4') The rest of the message indicates the name of the page where the error was detected. A more complete list of error messages will be found in the next section. 11 Sometimes called macro substitution. Errors, errors everywhere Locating fatal errors in PHP is not as problematic as in JavaScript: after all, the page is processed in advance, so the parser has all the time to check the PHP code. Of course, the same does not apply to the rest of the HTML/JavaScript code, which is left to the browser, with all the inconveniences of the case: such errors as wrong tags or incorrect JavaScript statements must be located and trapped, either using a “wolf-fence algorithm” or by careful examination of the related code. Let us consider the following snippet, where a loop verifies which numbers between 1 and 10 are prime, by calling a function already analyzed, with the obvious name of prime. The results are on the right. for ($i=1; $i<=10; $i++) { echo $i . ' (' . prime($i) . ')<br>'; } function prime($n) { $i=2; while( $i<=$n/2 && $n%$i!=0 ) $i++; return ($i>$n/2 && $n>1); } 1 () 2 (1) 3 (1) 4 () 5 (1) 6 () 7 (1) 8 () 9 () 10 () This example works just fine, but we should not ask too much to the macro substitution, which only expands the variables present in the doubly-quoted string. Function calls, if present, would be impossible to decipher. The following line, though not causing a fatal error, would not produce the desired outcome: echo "$i (prime($i))<br>"; // results in a sequence of lines like: 2 (prime(2)) So, in these cases, we should stick to the concatenation operator, as shown in the first snippet. The following examples are a collection of errors and warnings12. As short as the snippet is, it is possible to mistype in so many of ways that it will be hard to remember all the possible error messages: most of them, anyway, are clear enough that it is not difficult to locate the cause of the error. For each example, there is a brief description of the error, the corresponding line (or lines), and the error message, where xxx stands for the name of the PHP document and yyy for the line where the error is detected. As is C/C++, sometimes the detection of the error happens in a following line, especially with missing punctuation: in those cases, error trapping needs a little backtracking. This is not meant to be a complete list; still, it should cover the most common errors. Missing $ in variable name (in a condition) for ($i=1; i<=10; $i++) Notice: Use of undefined constant i - assumed ' i ' in xxx on line yyy Missing $ variable name (in a function call) echo ... prime(i); Notice: Use of undefined constant i - assumed 'i' in xxx on line yyy Missing $ in variable name: "i" instead of "$i" for (i=1; $i<=10; $i++) Parse error: syntax error, unexpected '=', expecting ';' in xxx on line yyy loop parenthesis not closed for ($i=1; $i<=10; $i++ ??? Parse error: syntax error, unexpected '{', expecting ')' in xxx on line yyy (the first statement after the error) Nothing between the loop and the function for ($i=1; i<=10; $i++) function prime($n) Parse error: syntax error, unexpected T_STRING, expecting '(' in xxx on line yyy (the first statement after the error) 12 Warnings do not cause the parser to abort the page, but they can result in misprints. Loop brace not opened (only closed) for ($i=1; i<=10; $i++) echo... } Parse error: syntax error, unexpected '}' in xxx on line yyy As in other languages, it is not mandatory to use a compound statement : so the parser signals the line of the unmatched closing brace as the source of the error. wrong keyword (no parentheses) cho ... // instead of echo Parse error: syntax error, unexpected T_VARIABLE in xxx on line yyy wrong operator $i+) $i+++; // instead of $i++ // stand-alone Parse error: syntax error, unexpected ')' in xxx on line yyy In the second example will be ';', Could also be '}', or a variable, depending on the following line. Call to wrong or UNKNOWN FUNCTION retun(...) // instead of return (in the function) Fatal error: Call to undefined function retun() in xxx on line yyy MISSING ARGUMENT IN FUNCTION CALL ... prime() ... Warning: Missing argument 1 for prime(), called in xxx on line yyy and defined in xxx on line yyy (R) Note: there no problem with extra arguments. unclosed ) in function call Parse error: syntax error, unexpected ';' in xxx on line yyy echo ... prime($i ??? ... MISSING SEMICOLON AT THE END OF A STATEMENT echo ... [no ; here] } Parse error: syntax error, unexpected '}', expecting ',' or ';' in xxx on line yyy Parse error: syntax error, unexpected T_ECHO, expecting ',' or ';' in xxx on line yyy Usually, line yyy is the first statement after the error (excluding comments). So the message may vary: it depends on what the statement is. In the first case, a closing brace was encountered; in the second, an echo statement. Missing concatenation sign echo $i ')<br>'; Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING, expecting ',' or ';' in xxx on line yyy Missing opening quote echo $i . )<br>'; Parse error: syntax error, unexpected ')' in xxx on line yyy This is not difficult, as the first offending sign is promptly identified. Missing closing quote echo $i . ')<br>; ??? Parse error: syntax error, unexpected T_STRING, expecting ',' or ';' in xxx on line yyy This error is tricky because the string constant is considered terminated at the first quotation sign encountered, which could easily be in a correct statement, as in echo 'hello'; In that case, it is the closing quotation sign of the CORRECT statement to be considered incorrect. But then again, a good text editor would show the errors with syntax coloring. Last, but by no means least, is the worst parse error to backtrack: Loop brace not closed for ($i=1; i<=10; $i++) { echo... function prime($n) Parse error: syntax error, unexpected $end in xxx on line yyy This error is particularly tricky because it is usually refers to the last line of the document, and there are no clues as to where the closed brace was forgotten. Further considerations The repeated error As a curiosity, we might note how non-fatal errors and warnings become part of the HTML code of the actual page, and so they get mixed with those parts which PHP processes successfully; furthermore, their HTML syntax take into account those part of the messages to be rendered in boldface. In the case of a variable name without the dollar sign prefix, the error is reported for each step of the loop: <b>Notice</b>:Use of undefined constant i-assumed 'i' in <b>…</b> on line <b>…</b><br> 1 () <b>Notice</b>:Use of undefined constant i-assumed 'i' in <b>…</b> on line <b>…</b><br> 2 () <b>Notice</b>:Use of undefined constant i-assumed 'i' in <b>…</b> on line <b>…</b><br> 3 () ... Common errors in JavaScript activation Another simple snippet activates JavaScript, but there are a couple more errors to watch: echo "<script>alert('hi there');</script>"; No parentheses in echoing a script echo <script>alert("hi");</script> Parse error: syntax error, unexpected '<' in xxx on line yyy Same quotation marks inside a string echo "<script>alert("hi");</script>"; Parse error: syntax error, unexpected T_STRING, expecting ',' or ';' in xxx on line yyy Negotiating through missing items The only really cryptic error messages at least give a hint on what is missing: Parse error: syntax error, unexpected T_..., expecting ',' or ';' in xxx on line yyy We have seen several examples of what could be in place of the “unexpected”: T_VARIABLE T_ECHO T_STRING T_CONSTANT_ENCAPSED_STRING T_DNUMBER But it is probably useless to learn them all: with a bit of experience, and considering what was expected13, the missing item is normally easy to spot. Missing keywords The behavior of PHP is not uniform in the case of a missing keyword, due to the structure of the statement involved. In some cases the parser detects an error, but, after syntax has been cleared, the resulting HTML code depends on assumptions. For example: missing function keyword in the function header: intercepted as a syntax error by the parser missing for or while: intercepted as a syntax error by the parser missing return keyword in the function: forces a return null missing echo keyword in a normal display: no HTML action Uno alla volta, per carità PHP only reports the first error it encounters; however , this is hardly an inconvenience, as it does not leave too much space for distractions. After all, we can correct only one error at a time. 13 Actually, it would be more precise to say “what could be expected”, as the parser, which after all is only a syntax checker, is making assumptions on what could follow. Give us the tools, and we will finish the job14 PHP has some debugging tools (not many, to tell the truth, unless one resorts to some specialized IDE). In this section there is a brief summary of some useful features of the language. Echoing variables By far, the most used debugging technique is to display the content of variables. There are at least three such functions, with slightly different behaviors: $a = 185; $b = 'xyz'; var_dump($a); var_dump($b); var_dump($a,$b); var_export($a); var_export($b); print_r($a); print_r($b); // // // // // // // int(185) string(3) "xyz" int(185) string(3) "xyz" 185 'xyz' 185 xyz The most useful of the three is probably var_dump(), which shows the variable type in front of the value, and has the added advantage of supporting multiple arguments. The only advantage of the other functions is that the output string can be returned for subsequent processing instead of being echoed, as in: $z = var_export($a); Actually, the same thing could be obtained with var_dump, but in a horrible way (not suggested): ob_start(); // var_dump($array); $out = ob_get_clean(); // Activate "object buffering" Deactivate and assign Magic constants When errors occur, it may be useful to know where, to help implement a “wolf-fence” algorithm. This can be done by checking some of the magic constants of PHP, thus called because they change depending on circumstances15. Their names are self-explanatory, and they can be used to display information about logistics: echo echo echo echo __LINE__ . '<br>'; __FILE__ . '<br>'; __DIR__ . '<br>'; __FUNCTION__ . '<br>'; // // // // Current line number File name Folder name Function name Other magic constants (__CLASS__, __TRAIT__, __METHOD__, __NAMESPACE__) belong to the realm of OOP, and are not used in this course. Variable variables If PHP constants can change, another curious feature of the language is the possibility of storing a variable name into another variable, then access the value of the former with the latter. It can be useful when working with data structures (lists or arrays), or to enhance data flexibility. Actually, this is a very specialized tool, used in very complex situations. The following example shows how to deal with these elusive entities: $a = $v = echo $v = echo 14 15 'v'; 'Content of v'; $$a; 18; $$a*2; // displays Content of v // displays 36 Winston Churchill (February 9, 1941). Which does make them a little more like variables. Strings As in JavaScript, PHP strings are a staple of the language, possibly even more so. The processing of input elements and database values, in fact, calls for a great deal of string operations. A string constant (also called a “literal”) has the usual aspect as in most programming languages: "Here's your crowbar and your centrebit " In the first examples, we will see string variables getting their value from literals (more on input elements and data processing later), so the basic assignment is like this: $a = "We sail the ocean blue"; Most of the key concepts are the same as in JavaScript, so we may refer to the corresponding section. For example, as in all C-derived languages, PHP strings are zero-based: i.e., the first character of a string is in position ZERO. Strings are also analogous to arrays of characters. There are differences, however: PHP strings are not immutable, i.e. they can be directly modified (as in $a[4]="e"); of course, they can also be replaced by a new expression, possibly involving the previous value (as in $a=$a."x";). The length of a string is not evaluated with a method, but with a function, mutuated from C++: for example, strlen("abcde") returns 5. As seen in previous examples, most strikingly different is the concatenation operator, which is a dot (.) symbol: so, we will write "blue " . "dress", not +. $a=$a."x"; is equivalent to $a.="x";. PHP has the classic string delimiters, single and double quotes (' and "): for example, we may see echo("Take the 'A' train"); or echo('Take the "A" train');. The key difference, as we have seen, is the macro expansion mechanism in echo statements. When possible, use single quotes. Deletion, insertion, replacement, and search are still the other common operations. Arithmetic on the G string? PHP numeric strings can be used in the same quirky calculations possible in JavaScript, with the added bonus that addition works arithmetically ("+" being different from the dot concatenation operator): echo("4"+"2"); echo("4"+3); echo("4"*"2"); // // // result: 6 result: 7 result: 8 (the strings are cast to a numeric type) As already commented, this is just a curiosity, not to be followed in the real world. How to apply the JavaScript “show” method What could pass for interaction is the application of the JavaScript show method for string rendering. But it’s just an illusion, since, as we know, any element of the page must be known to PHP in advance. The following example is somehow limited because it refers to a direct assignation, but even in the case of an external source for variable values, the data must be supplied before the HTML code is built. So it is not that different from what we will meet in the data processing section. Note that quotation signs effectively turn the variable into a literal; in the second script, without quotations, assumptions are made, and PHP could print anything. The pre-processed HTML code is also included, to show the difference. <?php $x = "sinn fein"; echo "<script>'$x'.show(out1);</script>"; ?> Actual HTML: <script>'sinn fein'.show(out1)</script> <?php echo "<script>$x.show(out1);</script>"; sinn fein.show(out1);</script> ?> Actual HTML: <script>sinn fein.show(out1)</script> In most cases, nothing gets printed, but this is unpredictable. intVal, floatVal These functions are the counterpart to parseInt and parseFloat of JavaScript, and are used to transform string data into numbers. The context of their use is different, however: while in JavaScript they usually translate input data, in PHP data are read from text or database files, where it may happen, very often indeed, to find numeric data stored as strings. PHP numbers come in two varieties, integer and double (doubleprecision), but that depends on the values rather than on a type declaration. In fact, the type can change, and can be detected with the aptly named gettype function: $n = 6542.348; $n = 45; // // gettype($n) returns "double" gettype($n) returns "integer" The two functions examine the parameter string in similar ways, accepting all the numeric characters until the string ends or a non-numeric character is met (whichever comes first). Numeric characters include the "+" and "-" signs (provided there is only one of them, and at the beginning of the string), and the decimal point (only for the floatVal function, and only the first one: a second point stops the parsing, just like any non-numeric character). Clearly, the processing of the decimal point is what sets apart the two functions. Here are some examples of the two functions in action. An asterisk indicates where the parsing operation has been interrupted by a non-numeric character. Unlike JavaScript, null and empty strings force the result to 0. intVal("12") intVal("87y") intVal("730y3") intVal("-12.5") intVal("6 4") intVal("6-4") intVal("") intVal(" ") * * * 12 87 730 -12 6 6 0 0 floatVal("12") floatVal("87y") floatVal("730y3") floatVal("-12.5") floatVal("6 4") floatVal("7.8.9") floatVal("") floatVal(" ") 12 87 730 -12.5 6 7.8 0 0 * * * Numeric output Numeric formatting, with respect to JavaScript, has a somewhat backward feeling, derived from the C-style functions used. Among the several options, the simplest to use is probably sprintf (the name means, more or less, string-print-formatted) There are differences from C, however, due to the uniform nature of PHP numbers: the function, in fact, is rather unfazed by the presence of decimal digits, or lack thereof; so, instead of wasting time with the %d or %f formats, it is perhaps wiser to stick to the old hand, the %g format. In the following snippet, all formatting options yield the same result: $n $z $z $z = = = = 6542.348; sprintf($n,"%g"); sprintf($n,"%6.2f"); sprintf($n,"%d"); // // // 6542.348 6542.348 (waste of time) 6542.348 For documents or forms, there is another function, which uses the thousands separator and a fixed number of decimal places (which can be used with most currencies, such as dollars, pounds, and euros): $x = number_format($n,2); // 6,542.35 The default locale (which is, rather understandably, English) can changed by specifying the new decimal and thousands separators as additional parameters, sometimes with outrageous results:: $x = number_format($n,2,',','.'); $x = number_format(1520034,2,'comma','dot'); // // 6.542,35 6dot542comma35 Base conversion PHP offers a flexible base conversion function, aptly named base_convert, which can work with numbers or numeric strings, and returns a string. Both bases (source and target) are required; errors yield '0': $c = base_convert('35',8,10); $d = base_convert(35,10,36); $e = base_convert(8,8,36); // // // '29' 'z' '0' (error) Operations on strings PHP has literally scores of string functions, some quite mysterious. The most common are the following (shown on a fictitious string $s or $h): strToUpper($s) strToLower($s) substr($s,$i,$j) strPos($h,$n,[$i]) strrPos(...) return the content of "$s", with all letters in UPPERCASE just the same, only in lowercase extracts a substring of "$s" starting at position $i, with length $j returns the position of substring "$n" in "$h", ($i=start position) searches from the end of the string towards the beginning There is no counterpart to the JavaScript charAt method: characters (which are strings in their own merit) are extracted with the usual $s[$i] array format. At the bottom of the page, a brief summary is included. Before examining that, since the behavior of these functions is sometimes different from their counterparts in other languages, we should consider the following peculiarities of PHP string handling: strToUpper, strToLower These functions do not modify the string: they return a string with the same general content of the source string, processed accordingly. If we need to modify the original string, it is necessary to store the modified string back to the variable, with the usual assignation operator: $s = strToUpper($s); substr $x = substr($s,$i,$l); Incongruent parameters may cause the function to return false, null, or an empty string If the substring length is omitted, all remaining character will be extracted If $l is negative, that many characters will be omitted from the end of the string strPos, strrPos "$n" and "$h" are mnemonics for needle and haystack: respectively, what to search and in which string. If the second argument is missing (the default), the search starts from the beginning of the string; When present, the second argument specifies from which position to start the search; Unsuccessful searches return false. substr_count substr_count($h,$n[,$i]) counts all occurrences of "$n" in "$h" ($i=start position) This is a rather unique function, as no other commonly used language offers its equivalent16. Sample values $s "We sail the ocean blue" 0123456789 123456789 1 $i $j $m $t $u $v 16 8 20 3 "ocean" "sea" "e" Function calls Results strToUpper($s) strToLower($s) substr($s,$i,$m) strPos($s,$t) strPos($s,$t,$j) strPos($s,$v) strPos($s,$v,$j) strrPos($s,$v) strrPos($s,$v,$j) substr_count($s,'e') "WE SAIL THE OCEAN BLUE" "we sail the ocean blue" "the" 12 false 1 21 21 14 3 With the exception of the now defunct Visual FoxPro, which has never been considered a mainstream language. Solved problems Alternating uppercase and lowercase letters This example shows how to build a new string, one character at a time: function alternating(s) // input: donkey; output: DoNkEy { $t = ''; for( i=0; i<strLen(s); i++) if ( $i%2 == 0) $t.= strToLower($s[i]); else $t.= strToUpper($s[i]); return $t; } Reversing a string As we have seen in JavaScript, this problem can be solved with a reverse loop, quite similar to a sum of numbers. function reverse($s) // input: watch; output: hctaw { $t = ''; for( $i=strlen($s)-1; $i>=0; $i--) $t.= $s[i]; return $t; } function sum($n) { $t = 0; for($i=$n;$i>=$1;$i--) $t+=$i; return $t; } But there’s more: PHP offers a built-in function for string reversal: $x = strRev('rabbit'); // returns tibbar Building a proper name Proper names are obtained with two functions: ucFirst capitalizes the first character of the entire string (lcFirst curiously transforms it into lowercase), ucWords on all the words in a sentence. $x = ucFirst('this is a sentence'); $x = ucWords('this is a sentence'); // // This is a sentence (lcFirst) This Is A Sentence HTML spaces The mnemonic for a HTML (non-branching) space is "&nbsp;", not too simple to insert into an echo call. With the useful str_repeat function, it is possible to “multiply” any string a given number of times; the example shows how the task of echoing “true” spaces on the page is greatly simplified: function blank($n=1) { return str_repeat('&nbsp;',$n); } // Waste of time: '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'; // good: echo blank(5); String padding A padding is a protective layer put around objects. In data processing, the analogy describes the addition of a number of characters to a string, to make it reach a specified length (usually to fill a predetermined space on a form). Normally, the padding character is a blank space, as in: while( strlen($s) < $target ) $s.= ' '; The function str_pad does just that, with the possibility of specifying the padding string and where to add the characters (to the left, to the right, centering the original string).this format: str_pad( $input, $length, $padString, $mode); $mode can be set to STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH., so the preceding snipped could be replaced with the following: str_pad( $input, $target, ' ', STR_PAD_RIGHT); Following string changes PHP strings are processed by reassigning a value in the calling program, possibly combining multiple calls in the same statement. We can track the sequence of value changes in the following snippet: $s $s $s $s = = = = "abcdef"; ucFirst($s); strRev($s); strRev(ucFirst($s)); // // // // original value now "Abcdef" now "fedcbA" proper before reverse (capital F); now "abcdeF" Note that the calling order matters, as ucFirst(strRev(s)) would yield "Abcdef", with a capital "A". Character or substring substitution/deletion in PHP Since PHP strings are not immutable, the substitution of a single character can be made in array fashion: $s[$i] = strToLower($s[$i]); Substitutions could also be done by dividing the string into 3 parts, just like in JavaScript: $s = substr($s,0,$i) . strToLower($s[$i]) . substr($s,$i+1); PHP, however, among its many string functions, offers one for string replacement: $s = substr_replace($s, strToLower($s[$i]), $i, 1); Similar considerations apply when deleting parts of a string (only without a new substring in the middle). For example, one single character can be deleted with this call: $s = substr_replace($s, '', $i, 1); String normalization This operation is sometimes called purging, as it erases redundant blank spaces, three kinds of which exist: leading (" xyz"), trailing ("xyz "), and multiple ("x y z"). Reworking the JavaScript solution, we will first delete leading and trailing spaces, with the PHP function aptly named trim(), then looping through a search for double spaces. The following function returns the purged copy of its parameter s. The call to function space makes the script more readable: the expression space(2) identifies the object of the search, whereas the corresponding string literal " " could be easily misread. The function is also reusable in any other application. function purge($s) { $s = trim($s); $c = space(2); while( ($i=strPos($s,$c)) ) { $s = substr($s,0,$i) . substr($s,$i+1); } return $s; } function space($n=1) { return str_repeat(' ',$n); } The same result could be obtained with the substitution function. We can also introduce another mnemonic, called $b, standing for a single blank spaces: $c = space(2); $b = space(); // while( $p = strPos($s,$c)) $s = substr_replace($s,$b,$p,2); The default value of parameter is ONE Finally, a totally devilish method which uses repeating functions (functions that examine all the possible substrings). substr_count counts the total number of occurrences of a substring, str_replace replaces all such occurrences. This is rather working with an axe where a scalpel is needed: while( substr_count($s,$c)) $s = str_replace($c,$b,$s); // search – replace – subject Note that these methods are rather crude, and do not ensure working with any substitution. In the general case, a loop like the one which follows should be used. Searching for words Words are those parts of a string which are enclosed between consecutive two blank characters. That’s where a normalized string is needed: unwanted spaces may generate empty words, possibly fouling the processing (and the reconstruction) of the whole phrase. Let’s consider the string "We sail the ocean blue". Suppose that, with the strPos() function, we have managed to set "$i" to 2 and "$j" to 7: substr($s,$i+1,$j-$i-1) extracts the word "sail", which can be processed as desired17. The same applies for any such pair of indexes, as shown here: A single loop will suffice for the indexes to proceed along the string, provided that the search for each new blank started from the last position reached. Having "i" chase "j" is not a great deal; the situation, however, is not quite as clear at the boundaries of the phrase (that is, the first word and the last). The first word can easily be identified by setting the initial value of "i" to -1. The real problem is at the end, when "i" is 17 and "j" becomes false, which is the result of the unsuccessful search for another blank. One simple solution is to attach a blank space at the end of the string, thus ending up with the correct value of 22 for the forward index "j". It may sound silly to go through the trouble of normalizing the string, then adding a redundant space of the same sort as the ones we have struggled to delete. The point is that the trailing space is not of the same sort after all, being a totally different beast: besides being placed there for the purpose of coping with a search result which disrupts the algorithm, the extra space is temporary18, being added only for the duration of the function, quietly disappearing at the end of the process. The skeleton for this procedure is the following: function words($s) { $c = space(); BEWARE !!! $s.= $c; The function does not specify what to do $i = -1; with each word. This is left as an exercise. $j = strpos($s,$c); while ( $j ) { $word = substr($s,$i+1,$j-$i-1); ... // Processing the single word th $i = $j; // "i" reaches the "j " position $j = strpos($s,$c,$j+1); // Search for the next blank } } As in JavaScript, these is the much simpler solution which uses a slack array. The PHP functions for stringto-array and array-to-string transfers, named explode() and implode(), respectively, differ from their JavaScript counterparts in that explode() requires a mandatory separator character, as in: $a = explode(' ',$s); ... $s = implode(' ',$a); // // // Store the words into array "a" Process the array Reconstruct the sentence from array "a" But first we need to know a bit about arrays. This will be the topic of the next chapter. 17 18 The formula $j-$i-1 is used because the second parameter of the substr function is the length of the substring. Normally, the space is called a slack data, in that it is used, then discarded without further effects. Arrays Arrays should be known from previous courses. PHP arrays are no exception to the general rule, being collections of data, identified by their position (also called index). An example of array could be a list of the temperatures of one week: 25,30,28,34,32,35,36. As we saw with strings, PHP arrays are somewhat more primitive objects than in JavaScript, and their management reminds much the C++ language. Yet, array handling is quite advanced, thanks to many useful functions. Arrays can be declared in a couple of ways: $a $a $b $c = = = = array(); array(87); array(1,2,3,4); [1,5,3,2]; // // // // Empty NO JavaScript: one element, number 87 Direct value assignation Not safe (only PHP 5) Some considerations apply: The first element of a PHP array is in position zero (like C/C++ arrays); Unlike C/C++ arrays, PHP arrays are elastic (i.e.: their length can vary, adding or deleting elements); Tthe length of an array is a calculated with a function, called count($a); The last element of array $a is in the position count($a)-1 (length minus one, due to the presence of the element in position zero). There can be no unassigned array elements; they can be deleted, though, along with the index (this means that an array could be left, for example, with positions 0 and 2, but no position 1). PHP arrays are linear (or mono-dimensional); with some tricks, it is possible to mimic matrices (multidimensional arrays, with any number of dimensions), including the familiar syntax a[i][j]; matrices, however, are outside the scope of this course, as their use in Web applications is limited. Some of the most common functions for assigning and extracting values are listed below: array_push($a, ...); array_unshift($a,...); array_pop($a) array_shift($a) array_splice($a,$p,$n); array_splice($a,$p,$n,$b); array_reverse($a) sort($a)/rsort($a) array_slice($a,$p,$n); // // // // // // // // // Append at the end of the array (sequence) Insert at the beginning (sequence) Remove last element Remove first element Remove $n elements starting at position $p Remove, then insert (element or array) Flips the array Sort elements in ascending/reverse order Copy $n elements starting at position $p The pop and shift methods are limited to just one element. The same holds for the simplest way of adding an element to an array, which uses an empty couple of brackets, as in: $a[]=18;. The following sequence shows the methods in action. At each step, the new elements are written in green; the elements written in red are those which the following method would remove from the array: array_push($a,21); array_push($a,31,32,33); array_unshift($a,41); array_unshift($a,51,52,53); array_pop($a); array_shift($a); array_splice($a,3,4); array_splice($a,1,1,array(61,62,63)); array_splice($a,0,1,37); array_reverse($a); sort($a); $new = array_slice($a,1,3) 21 21,31,32,33 41,21,31,32,33 51,52,53,41,21,31,32,33 51,52,53,41,21,31,32 52,53,41,21,31,32 52,53,41 52,61,62,63,41 37,61,62,63,41 41,63,62,61,37 37,41,61,62,63 41,61,62 (new array) No show Just for curiosity, javascript:show sort of works with arrays, but not quite: the result is just a string containing the word Array. The flexibility of JavaScript does not hold in PHP, which, once again, is not dynamic, but must process the whole page before submitting it. The image below shows the effect: <?php $x = array(); echo "<script>'$x'.show(out1);</script>"; ?> The actual HTML also contains a warning that there is something fishy: Notice: Array to string conversion in xxx on line yyy <script>'Array'.show(out1)</script> Stretching arrays As seen before, arrays can be stretched with a generic assignment to the array, containing a pair of empty square brackets; actually, they could even be generated in the same way. It should be noticed, however, that in such cases the first element should be assigned directly, as with array $a in the example; and while array $b and $c have the burden of an extra element, array $d is built in a more proper way: $a[] $a[] $a[] $a[] $a[] $b[] $b[] $c[] $d $d[] = = = = = = = = = = 1; 'a'; false; array(); 5.6; null; 15; array(); array(); 21; // // // // // Not suggested (generates one null element) this element would be the SECOND one BAD: one element: an empty array Empty array extension Deleting variables The unset function can be used to erase any variable from the symbol table, and is sometimes used on array elements. Care must be taken, however, because this can disrupt the numeric indexing, as in the following example, which generates an error when trying to access $a[2] after its deletion:19 unset($a[2]); for($i=0;$i<count($a);$i++) var_dump($a[$i]); Notice: Undefined offset: 2 in xxx on line yyy Look ma, no indexes The previous error can be avoided with an useful array visiting tool, the foreach statement20. Disposing with the use of an index, foreach is simpler and safer than the usual traversing loop. The two loops of the following example have the same effect: for($i=0;$i<count($a);$i++) var_dump($a[$i]); foreach($a as $v) var_dump($v); It is also possible to call the elements by reference, in order to modify them: foreach($a as &$v) $v*=2; 19 Erasing variables (with the unset function) could be useful in complex applications. More widely used is the test on the existence of a variable, as in: if (isset($v))... 20 foreach is present in other languages, including Java and Visual Basic. In JavaScript, it is limited to objects. Associative arrays Arrays can be used without numeric positions, using only string indexes (this is the meaning of associative). This example was given by Jeffrey Way in one of his precious lessons, where he shows a list of links to some of his courses: $a = array ( "nettuts" => "http://net.tutsplus.com", "psdtuts" => "http://psd.tutsplus.com", "wptuts" => "http://wp.tutsplus.com", // ); // echo '<ul>'; foreach( $a as $name=>$url) // { echo "<li><a href='$url'>$name</a></li>"; } echo '</ul>'; this last comma is optional don’t forget the semicolon LIST OF COURSES AND URLs The $name=>$site association in the foreach statement corresponds to the => couples in the array declaration, so that: "nettuts" replaces $name "http://net.tutsplus.com" replaces $url The <li> elements can also be assigned with a forced double quote (which requires a backslash prefix): echo "<li><a href=\"$site\">$name</a></li>"; // echo "<li><a href="$site">$name</a></li>"; // This is also correct WRONG: same quotation sign An alternate syntax is given by the endforeach keyword, which can replace the closing brace while disposing of the opening one. This is not suggested, as it deviates from the usual syntax: foreach( $a as $name=>$site) echo ..."; endforeach Some consider this construct useful when breaking out of PHP, but the fact remains that, even if breaking were necessary, more PHP would be included in the loop, and confusion might ensue. Anything that mixes PHP and HTML is deprecated, and, fortunately, seldom used21: <?php foreach( $a as $name=>$site)?> <!-- HTML stuff --> <?php endforeach ?> // // // PHP HTML PHP The following examples (one bad, one worse) show the mess that can be generated by mixing too much: <ul> // <?php foreach($tuts_sizes as $name => $url) echo "<li><a href='$url'>$name</a></li>"; ?> </ul> BAD <ul> // WORSE <?php foreach($tuts_sizes as $name => $url)?> <li> <a href="<?php echo $url; ?> "> <?php echo $name; ?>" </a> </li> <?php endforeach ?> </ul> 21 PHP developers seem more willing to adhere to good programming practices than their JavaScript counterparts. Solved problems PHP built-in functions cover most of the routine array operations, such as sorting, flipping, and extracting. This frees the programmer from the burden of writing and testing the corresponding scripts. The following examples, though of little intrinsic value, will be used to introduce some useful features of the language. Flip part of an array Given an array, find two random positions $p and $q, ensuring that $p<$q, then flip the part of the array which lies between the two positions. In the examples, the values of $p and $q are 3 and 6, respectively: $a = [0,1,2,3,4,5,6,7,8,9]; $p=3; $q=6; // result: 0,1,2,6,5,4,3,7,8,9 We will tackle the numeric part first, showing how to deal with random numbers. The PHP tool is the rand function, one of a series of mathematical functions22. Unlike its JavaScript counterpart, the PHP random23 generator returns, by default, an integer value between 0 (included) and the result of the JavaScript function getrandmax()24 (excluded). It is quite simple, though, to specify an interval for the result: $x = rand(min,max); This way, we do not need any special calculations to find positions which belong to the array. The swap solution shown, a nice PHP alternative, is not the fastest around, so it would not be suggested for a situation with many exchanges: in any instance when time is a factor, the usual swap function is still preferred. $p = rand(0,count($a)-1); $q = rand(0,count($a)-1); if ($p>$q) list($p,$q) = array($q,$p); // // // The PHP list statement is a construct which assigns the values of an array to a list of variables There rest of the solution, with a “flip” loop, is elementary: while ($p<$q) { swap($a[$p],$a[$q]); $p++; $q--; } function { $c = $a = $b = } swap(&$a,&$b) $a; $b; $c; With the list construct, the swapping would be: list($a[$p],$a[$q]) = array($a[$q],$a[$p]); A much shorter method uses the array_splice function, which, contrary to JavaScript, allows for the insertion of an entire array in a given position. This way, it is possible to extract and reverse a section of the array, then graft it back in the same place where it was pruned. For the sake of simplicity, a new variable can be used to specify the length of the section ($p and $q are still 3 and 6): $n = $q-$p+1; array_splice($a,$p,$n,array_reverse(array_slice($a,$p,$n))); Find the smallest (or largest) element, using a comparison function A couple of other functions, min() and max(), can sometimes be useful in comparisons. They return, respectively, the minimum and maximum value among a list of values, as in: max(41,63,4,5,116,87); // result: 116 The lists can be of any length: there does not appear to exist an explicit limit, but some experts estimate a resource limit at around 2 billion (2*109). Unlike other similar functions in other languages, using min and max on an array seems almost unfair, as, unlike in JavaScript, they only need the array name: $a = [53,41,55,63,47,48]; $min = min($a); $max = max($a); 22 // // result: 41 result: 63 Many of these (exponential, logarithmic, trigonometric) are hardly used. Actually, a pseudo-random generator, since it follows an algorithm: for practical purposes this makes no difference. 15 24 Normally, this value is 32767 (2 -1). 23 Strings and arrays Just as in JavaScript, strings and arrays will meet in the processing of a whole sentence, without resorting to the search for blank spaces. The problem can be summarized as: 1 transfer the words of a sentence to an array 2 process the words separately 3 repack the array into a single string. The solution has been studied before, so it should surprise no one. The notable difference with JavaScript is that here the substring separator (the first argument) is mandatory in the function calls: $s = 'We sail the ocean blue'; $a = explode(' ',$s); foreach($a as &$x) $x = strRev($x); $s = implode(' ',$a); $t = "go with the flow"; $b = explode(' ',$t); foreach ($b as &$x) $x = ucFirst($x); $t = implode(' ',$b); // Reverse EACH WORD // Capitalize EACH WORD Of course, the second example could be solved with one call to ucWords($t). Omitting the separator in the call to explode is not a choice, as it would generate an error; an empty separator is also not allowed. The respective error messages are the following: Warning: explode() expects at least 2 parameters, 1 given in xxx on line yyy Warning: explode(): Empty delimiter in xxx on line yyy The blank separator in the subsequent call to implode, though not mandatory, is strongly suggested, or the spaces would not be restored (the result would be GoWithTheFlow). It is still possible to transfer one character per element in the target array, with the str_split function. This could turn out to be useful when some specific array functions are needed (array_reverse, to name one), or when a character might be replaced with a string of a different length (or even deleted). The following examples show nothing that could not be obtained with normal string operations, but they should give the idea. Actual problems would analyze the characters, and take the appropriate actions, with far more complicated processes. WARNING !!! the reconstructing implode function should be called without a separator, or at least with an empty one (which, contrary to explode, is allowed), to avoid inserting unwanted spaces between characters (as in "c u r s e", instead of "curse"). Normal array visit with character check $z = 'hammock'; $a = str_split($z); foreach($a as &$x) if ( $x=='m' ) $x='d'; $z = implode($a); hammock Before haddock After Deleting some characters $u = 'princess'; $a = str_split($u); unset($a[6]); $u = implode($a); Substituting with longer expression $v = 'curb'; $a = str_split($v); $a[3] = 'se'; $v = implode('',$a); Before After Before After princess princes curb curse PHP in action: processing parameters Strings and arrays are the staples of PHP, but, until now, they have shown nothing that could not be done with JavaScript25. Some of the real power of these tools can be appreciated with the processing of page parameters, which has been left pending both in the HTML and in the JavaScript courses. This is a possible complete URL of the called pages, along with the arguments: file:///D:/web/application/index.html?h1=oneword&h2=two+words There is a little tweaking to be done: it is necessary to substitute PHP pages for HTML ones, so the calling page should be rewritten in this way: <form action="php8.php" method=get> <input id=h1 name=h1 value="oneword"> <input id=h2 name=h2 value="two words"> <input type=submit value="send"> </form> In this example, the fields are in plain sight: this makes no difference in their handling by the called page. Of course, we will end up with this URL line: file:///D:/web/application/php8.php?h1=oneword&h2=two+words The page is called with the get method, just to show the parameters: normally, though, we should use the post method, for security reasons. Internally, there is actually no difference, as the called PHP page is always capable of recognizing the parameters, via a special array called superglobal26, which stores all the data that the page has been sent. For now, we will only display the array like so: <?php var_dump($_REQUEST); ?> Sure enough, the array contains the parameters. They are stored as strings27, keyed by their names (assigned in the form element of the calling page), as the following var_dump result shows: array 'h1' => string 'oneword' (length=7) 'h2' => string 'two words' (length=9) This is an associative array A PHP page has three such superglobal arrays. Just to avoid any doubts, the $_REQUEST arrays is sure to contain the desired values: $h1 $h2 $h1 $h2 $h1 $h2 = = = = = = $_GET['h1']; $_GET['h2']; $_POST['h1']; $_POST['h2']; $_REQUEST['h1']; $_REQUEST['h2']; // Parameters passed with the get method // Parameters passed with the post method // Any method After that, it is still advisable to check for the parameters received, with call to isset: if( isset($REQUEST['h1']) ... ; if( isset($REQUEST['h2']) ... ; isset checks for existence Also, default values can be assigned: if( ! isset($REQUEST['h1']) $_REQUEST['h1']='default value'; This is especially suggested when receiving input values from forms containing check boxes or radio buttons: when such elements are left empty, their names do not even appear in the $_REQUEST array. 25 This is going to change in the chapter dedicated to data processing There are several superglobal arrays, for various purposes. More on this later. 27 This is true also for numeric values, hence the importance of the type transformation functions. 26 Case study: accessing the server As already mentioned, PHP has access to the file system on the server. In this study, we will see some operations on text files. Reading a text file is the simplest operation, and one that does not require access permits; creating, modifying, and deleting files, on the other hand, are subject to the security measures on the server28. In a local environment, though, anything goes, so it will be possible to operate at will. We can use a simple text file, for example, one named text.txt, which contains a list of names. Cochrane Foxx Bishop Boley Dykes Simmons Miller Haas Grove File access is allowed once the file is opened (fopen), until it is closed (fclose). When the file is opened, a reference to it can be assigned to a handle (which is a particular object, called a resource), through which all further operations will be executed. Function Example Meaning fopen fclose fread fgets feof rewind filesize fwrite $h = fopen("text.txt","r") fclose($h); $x = fread($h,$n); $x = fgets($h); while (!feof($h)) ... rewind($h); ... filesize($h) ... $w = fwrite($h, ...); Open for reading Close Read a given number of bytes Read to eof or line break End-of-file Reposition at file beginning Total number of bytes Write to file (any value) A file can be opened in a number of ways (called modes), the most common being the classic three: mode Meaning r w a Input Output Append Some examples: $handle = fopen("text.txt","r"); $x = fread($handle,11); fclose($handle); $handle = fopen("text.txt","r"); $x = fread($handle,filesize("text.txt")); fclose($file); $handle = fopen("text.txt","r"); $x = fgets($handle); while(!feof($handle)) { echo $x . '<br>'; $x = fgets($handle); } fclose($handle); $handle = fopen("text.txt","r"); while($x = fgets($handle)) { echo $x . '<br>'; } fclose($handle); readfile('text.txt'); 28 Reading a given number of bytes Reading ALL the bytes of the file Reading each line of text. The test is done through the feof function. Shorthand: the loop assigns the line to a variable and tests for end-of-file in the same token Quick-and-dirty way for displaying text content Some servers do not allow direct handling of data, unless a user has logged in through a portal. In such cases, PHP pages only have reading privileges. Error trapping Whenever a program tries to access a file, the problem arises of whether the file actually exists: after all, you cannot extract data from a resource that cannot be located. Trying to open a file not present in the search folder, to use technical jargon, throws an error; and while the script does not abort, a flurry of error messages appears: $file = fopen("xxx.txt","r"); $x = fread($file,11); fclose($file); // This file does not exist This is what gets written on the screen: Warning: fopen(ext.txt): failed to open stream: No such file or directory in C:\web\textfile.php on line x Warning: fread() expects parameter 1 to be resource, boolean given in C:\web\textfile.php on line y Warning: fclose() expects parameter 1 to be resource, boolean given in C:\web\textfile.php on line z The fact is that the handle has a value of false, hence the clause “boolean given” in the message. Aborting the script can be a very simple choice with the aptly named die() clause, but a warning message appears anyway: $file = fopen("xxx.txt","r") or die(); Warning: fopen(ext.txt): failed to open stream: No such file or directory in C:\web\textfile.php on line x The situation can be better controlled by disabling the standard error handler (that is, the built-in function that processes errors and displays the corresponding messages). An empty error handler does not stop the script, but appropriate action should be taken, typically by checking the result of the fopen statement: set_error_handler("errorTrap"); // set a custom error handler // open successful? // proceed processing restore_error_handler(); // restore default error handler function errorTrap() {} // no action $file = fopen("ext.txt","r"); if($file!=null) { ... fclose($file); } In file processing, there are not many error situations, so an empty handler is perfectly legit. In other situations, it may be useful to give more detailed explanations; that’s why an error handler has several parameters that can help the programmer locate the error. $errno $errstr $errfile $errline numeric code error description name of the page where the error has occurred line number where the error has occurred The parameters are optional, and the program may contain a die() clause, although this is doubtful, or the error handler would not have been written in the first place: function errorTrap( $errno, $errstr, $errfile, $errline) { echo "<b>Error:</b> [$errstr]<br>"; echo "<b>Program:</b> [$errfile]<br>"; echo "<b>Line:</b> [$errline]<br>"; die(); // Cui prodest? } Writing text The quick-and-dirty way of storing text into the server folder is: file_put_contents('text1.txt', 'we sail the ocean blue'); The function, if successful, returns a value corresponding to the number of bytes written (in this case, 22).The resulting text file can be opened with Notepad, or any other editor: In a more conventional way, the file can be opened either in output or in append mode, then data can be written with the fwrite function (the combination chr(13).chr(10) forces an end-of-line): $x = array("we","sail","the","ocean","blue"); // data to be written if ($h=fopen('text2.txt','w')) // output mode { // (new file) foreach( $x as $v ) fwrite( $h, $v . chr(13) . chr(10) ); fclose($h); } if ($h=fopen('text2.txt','a')) // append mode { // (data are added) foreach( $x as $v ) fwrite( $h, strToUpper($v) . chr(13) . chr(10) ); fwrite( $h, 45); // numeric data fclose($h); } By using the “append” mode in the second loop, the array of words has been written a second time (in UPPERCASE), so that the final content of the text file is as follows (one word per line): The number 45 has been written without quotes: we can see that it has been transformed into a string. As in the previous examples, it should be noted that the result of the “open” operation is assigned to a variable, called handle. Every further reference to the file is done through that handle: the file name is not used any more, having already been linked to the file system. When write errors happen, the return value of fwrite is false (or 0), as in the following example: if ($h=fopen('text2.txt','r')) { $f = fwrite( $h, 'test'); echo $f; fclose($h); } // file opened in input // // attempt to write $f (return value) is FALSE Case study: building a photo gallery There are innumerable photo galleries on the Internet, from corporate sites to social networks. In this example, we will use HTML, CSS, JavaScript, and PHP to produce a synergic Web page accessing a folder with our favorite pictures. This page will also receive the name of the folder as a parameter, therefore completing the circle with most of the elements studied so far. The roles for each actor are: HTML CSS JavaScript PHP Infrastructure on which all the rest is built Appearance of the page Dynamic access to different pictures Engine to access the file system on the server, containing the pictures First, let’s examine how to access the images in a given folder (more specifically, a subfolder of where the page is located). The PHP functions that will be used here are four: Check if the string (presumably, a parameter of the function) contains a valid folder name. opendir(<name>) Open the folder and assign it to a handle (a variable for future reference) Access the folder content with the specified handle (each open folder requires readdir(<handle>) its handle: in our case, only one folder is used) closedir(<handle>) Deny further access to the folder (which, of course, can be reopened at will) is_dir(<string>) Step 1 – accessing the file system The following is a function which receives the folder name in input, checks for validity, then returns an array containing the names of the images (or an error message, if the parameter is not a valid folder name). function getImages($dir) { if (is_dir($dir)) { $images = array(); // empty array $handle = opendir($dir); // open folder while ($x = readdir($handle)) // reading loop { $x = strToLower($x); if // check common formats ( strPos($x,'.bmp') !=false || strPos($x,'.gif') !=false || strPos($x,'.jpg') !=false || strPos($x,'.jpeg')!=false || strPos($x,'.png') !=false ) $images[] = $dir . '/' . $x; // add to list } closedir($handle); // close folder } else // ERROR: return a string { $images = '"' . $dir . '" is not a valid folder name'; } return $images; } The features of this function: Variable $handle, which is the reference to the folder, once opened; The direct assignment of each folder entry to variable $x, in the visit to the folder. Step 2 – designing the page The page will consist of two containers: one to the left (<aside>), containing a list of links made with the preview images; one to the right (<div>, class content), containing the image selected from the list (initially, the first one, number zero). In the preview list, the selected image will have a red outline. A bit of CSS will suffice to set up the page: aside { background-color float width } aside, .content { height overflow text-align } .content { background-color width float margin } .preview { width margin-top } /* container for preview */ : paleGreen; : left; : 10%; /* shared settings /* : 100%; : auto; : center; /* centers the images */ /* magnified image */ : : : : lightCyan; 90%; right; auto; /* preview list */ : 80%; : 10px; Step 3 – building a JavaScript array Now, PHP meets JavaScript and builds the dynamic array for the pictures, which will make it possible to dynamically change the central image. The five red echo statements actually write into the page the declaration and initialization of a JavaScript array: function js_array($images) { $s=''; foreach( $images as $x ) $s.= '"' . $x . '",'; $s = substr( $s, 0, strlen($s)-1); echo '<script>'; echo "images = [$s];"; echo '</script>'; } // // comma-separated list cut last comma // could be: "a.gif,b.jpg,c.jpeg,d.png" Step 4 – building the dynamic elements Now, PHP will build the preview list and the central picture. Each entry in the preview list comes with an event which enables JavaScript to change the selected image, both in the list and in the central picture. Another JavaScript variable, named p, initialized through a stray statement, keeps track of the selection. The anchors are not really necessary: their only use is to change the cursor from ”arrow” to “link”. <script> p = 0; </script> // Selected image (initially 0) <?php function gallery($images) { $t = count($images); echo('<aside id=aside>'); // Preview (aside) for($i=0;$i<$t;$i++) { $x = $images[$i]; $z = "style='border:" . ($i==0 ? 'solid red 3px' : '0px') . "'"; echo("<a href='#'>"); echo("<img id='pic$i' class=preview $z src='$x' onclick='change($i)'>"); echo("</a>"); } echo '&nbsp;&nbsp;&nbsp;'; // Just for a bottom padding echo('</aside>'); // Central picture echo('<div class=content>'); echo("<img id=central height=90% src='$images[0]'>"); echo('</div>'); } ?> <script> /* changing the selected image */ function change(i) { z = document.getElementById('pic'+p); z.style.border = '0px'; z = document.getElementById('pic'+i); z.style.border = 'solid red 3px'; z = document.getElementById('central'); z.src = images[i]; } </script> // i = index in array "images" // Old "selected" loses border // New "selected" gets border // // Change central image (using name of image #i) Step 5 – calling the page Now it is time to put everything together. As anticipated, the “gallery” page will get the folder name from another page, where it will be written in a textbox. An example of a very minimalist page, which does not even need any PHP: <html> <head> </head> <body> <form action="gallery.php" method=post> <label for=folder>folder name</label> <input style="width:200px" id=folder name=folder> <input type=submit value=view title="view gallery"> </form> </body> </html> The content of textbox folder will be received by the called page. Step 6 – receiving the parameter value The necessary steps to get the parameter value with the folder name will be added to the page. The first PHP statement (***) are not necessary if the page is activated by the preceding one via the submit button; it is added to cover the case when the page is directly addressed in the URL line. After the call to getImages, it is possible to check for errors: the only one possible is a name not corresponding to a folder. If this is the case, only the error message appears: If the test is successful, the functions examined before will be activated, and, pronto, our gallery will appear. <html> <head> <link href='gallery.css' rel='stylesheet' type='text/css'> </head> <body> <?php if (!isset($_REQUEST['folder'])) $_REQUEST['folder']=false; // *** $folder = $_REQUEST['folder']; $images = getImages($folder); if (is_string($images)) // Remember: string means "error" { echo $images; // Display error message } else { js_array($images); // Build the JavaScript array gallery($images); // Build the rest of the page } ... ?> ... </body> </html> Of course, the ellipses stand for the rest of the page (PHP and JavaScript code). Step 7 – cleaning up the code The page may work, but we should oblige to the KISS principle and slim it up by using references to modules. Each component (PHP, JavaScript, CSS) has its most peculiar include directive: PHP JavaScript CSS <?php include ... ?> <script src ... > <link href ... > The resulting page shows the benefits of modular programming, as most activities are “hidden” in other files, and only the most relevant parts are in plain view. There is a bit of irony, though, since most of the PHP will not show up in the final HTML document <html> <head> <?php include('gallery lib.php'); ?> <!-- PHP --> <script src=gallery.js></script> <!-- js <link href='gallery.css' rel='stylesheet' type='text/css'> <!-- CSS --> --> </head> <body> <?php if (!isset($_REQUEST['folder'])) $_REQUEST['folder']=false; $folder = $_REQUEST['folder']; $images = getImages($folder); if (is_string($images)) { <html> echo $images; <head> } <script src=gallery.js></script> else <link href='gallery.css' ...> { </head> js_array($images); <body> gallery($images); <script>images = ... </script> } <aside id=aside> ?> <a href='#'><img ... </a> </body> ... </html> </aside> <script> <div class=content> p=0; <img ...> </script> </div> </body> </html> To the right, a glimpse of how the finished page <script> appears on the server: there are no surviving PHP p=0; statements (not even the include directive), as they </script> have all been pre-processed. The only trace left by PHP are the arguments of the echo calls. The finished project comprises the main page and three supplemental documents; there should also be at least one folder containing images, or :