CS3226

Web Programming and Applications

Lecture 07 - PHP

Dr Steven Halim
stevenhalim@gmail.com

For 'not newbies': Try this PHP quiz @ W3Schools first

Outline

Why Server-Side Programming?

So far, we have seen various client-side technologies:

  1. Week02: HTML5 & CSS3
  2. Week03: JS (jQuery)
  3. Week04: A Front-End framework
    (Bootstrap + Laravel Blade Templates)

However, client-side programming alone is not enough for a full-fledged web application because...

  1. Important reason: We may want to serve dynamic webpage that is created based on previous client inputs (not just during the current session) that have been stored in the server's database (or other technology)
  2. We do not, and ethically cannot, store our client data in some other client's computer
  3. We may not want some of our non-open-source code to be accessible in public like in client-side programming
  4. Server machines are usually (much) more powerful than the client machine and some computational tasks need (extremely) large processing power which client machine usually unable to handle

PHP: Hypertext Preprocessor

PHP is yet another programming language in this CS3226

It is one of the most popular server-side scripting language in the world (and open source a.k.a. free)

We will write programs on server-side and you will need a PHP-enabled web server (e.g. a LAMP=Linux Apache MySQL PHP stack) to test these programs, e.g. DO droplet that you/your team mate(s) have set up in Lab2

Or, you can install things like (free) XAMPP, WAMPServer, etc on your own computer to run PHP locally (@localhost)...

Hello World (from PHP)

Let's write our very first PHP code named as 'hello.php':

<?php // this is the signal of the start of PHP section
echo 'Hello World, this is my first PHP code'; // this is a comment
// the line below signal the end of PHP section
?>

For this demo, Steven uploads the PHP code to his VA server

Try accessing http://visualgo.net/cs3226/hello.php; Is it OK?
Then check the page source! Is it an HTML(5) document?

A wrongly set up server may cause php document to be displayed as plain text instead of executed... (bad :O)

PS: Laravel has 'shortcut' for those frequent <?php and ?>

CS1010 again... with PHP

Programming Methodology, but using PHP:

  1. How to declare variables, that $ symbol...
  2. Sequencing commands, the semicolon ; symbol
  3. Selection, if-else, (cond ? true : false), switch
  4. Repetition, for, foreach, while, do-while
  5. Declaring function: function and recursive function

Steven will only show a few of these live demo (using his VisuAlgo server, max approx 10 minutes) and will relegate the details for self exploration during Lab4

demo.php (1)

<!DOCTYPE html> <!--We can mix HTML, CSS, JS commands inside PHP file-->
<html lang="en">
<head><title>PHP demo</title></head>
<body><?php
$w = 1; $x = 2; $y = 3; $z = 4; // variable names starts with a $ sign
define("FOUR", 4); // this is PHP way to declare a constant
eChO "<p>variables demo<br>\n";
ecHo ($w/$x + $y*$z) . "<br>\n"; // echo 12.5; frequently used command
Echo ($x > $y) . "<br>\n"; // false is not echoed; "." is a concat symbol
ECHO ($x < $y && $z == FOUR) . "<br></p>\n"; // echo 1 (true)
// PHP commands are NOT case sensitive but variable names are...
echo "What is this: [$notexistent]?<br>\n"; // nothing is printed
$w = 'changed to string'; // PHP is not a strongly typed language, like JS
echo $w . "<br>\n";
print ($z%3); // print is similar to echo, % is modulo, prints out 1

PS: We go through vanilla PHP a bit in the event Laravel (PHP framework) becomes unusable (for whatever reason), you can switch to other PHP framework easier

demo.php (2)

echo "<p>if-else and (cond ? true : false) demo<br>\n";
if ($x > $y)
  echo "x is bigger than y<br>\n";
else
  echo "x is smaller than y<br>\n";
echo "x is " . ($x > $y ? "bigger" : "smaller") . " than y<br></p>\n";

echo "<p>switch demo<br>\n";
switch ($x) {
  case 1: echo "x is 1<br>\n"; break;
  case 2: echo "x is 2<br>\n"; break;
  default: echo "x is not 1 or 2<br>\n"; break;
}
echo "</p>\n";

demo.php (3)

echo "<p>for loop demo<br>\n";
for ($i = $x; $i <= $z; $i++) // $x = 2, $z = 4, loop from 2 to 4
  echo "i is " . $i . "<br>\n";
  // echo "i is $i<br>\n"; // is also possible, notice double quotes

echo "</p>\n<p>while loop demo<br>\n";
$i = 0; // $y = 3
while ($i < $y) // 0, 1, 2 and then stop  
  echo "i is " . ($i++) . "<br>\n";

echo "</p>\n<p>do-while loop demo<br>\n";
$i = 0; // $y = 3 (different behavior with above if $y = 0)
do
  echo "i is " . ($i++) . "<br>\n";
while ($i < $y); // 0, 1, 2 and then stop (notice the ;)
echo "</p>\n";

demo.php (4)

echo "<p>more complex control flow demo<br>\n";
$a = array(2, 1, 7, 8, 3); // we will discuss array in more details soon
for ($i = $sum = 0; $i < count($a); $i++) // count: to count array length
  if ($a[$i]%2 == 0) continue; // an even number, skip this iteration
  else if ($sum > 7) break; // stop the associated loop
  else               $sum += $a[$i];
echo "the sum is $sum<br>\n";

$sum = 0;
foreach ($a as $key => $value) { // foreach loop
  if ($sum > 7) break;
  $sum += $value%2 == 0 ? 0 : $value;
}
echo "the sum is $sum<br></p>\n";

demo.php (5)

echo "<p>function demo<br>\n";
require_once("sum.php"); // this is like C/C++ #include (next slide)

$a = array(2, 1, 7, 8, 3);
echo "sum: " . sum($a) . "<br>\n"; // 21

function fib($n = 7) { // recursive function, default argument $n = 7
  if ($n <= 1) return $n;
  else return fib($n-1) + fib($n-2);
}

echo "fib(6) = " . "<br>\n"; // 8
echo "fib() = " . fib() . "<br></p>\n"; // default argument, fib(7) = 13

sum.php

<?php // an external file that is included in the previous slide
function sum($a) {
  $ans = 0;
  foreach ($a as $val)
    $ans += $val;
  return $ans;
}
?>

demo.php (6)

// PHP 1D array demonstration
$C = array(13, 1, 12, 5, 7);
array_splice($C, 2, 2, array(8, 4, 6)); // replace [12, 5] with [8, 4, 6]
$l = count($C); // length of array is now 6: [13, 1, 8, 4, 6, 7]
echo $C[5] . "<br>\n"; // 7
sort($C); // sort ascending, already compare by value
echo "sorted: ";
foreach ($C as $val) echo $val . " "; // manual
echo "<br>\n";
echo "compare with: " . implode(" ", $C) . "<br>\n"; // use implode
$D = array("N"=>2, "M"=>3); // PHP associative array ~ an ordered map
echo "Using var_dump:<br>\n";
var_dump($D);
echo "<br>\n";

demo.php (7)

// PHP 2D array demonstration
$G = array(
  array(1, 2),
  array(0),
  array(0)  
);
for ($i = 0; $i < count($G); $i++) {      
  echo "row $i: ";
  for ($j = 0; $j < count($G[$i]); $j++) 
    echo $G[$i][$j] . " ";
  echo "<br>\n";
}
echo "Using var_dump:<br>\n";
var_dump($G);
echo "<br>\n";

demo.php (8)

// PHP string manipulation demonstration
$str1 = 'steven';
echo strlen($str1) . "<br>\n"; // 6
echo $str1[1] . $str1[3] . "<br>\n"; // 'tv', 0-based indexing
$str2 = 'seven';
echo "strcmp: " . strcmp($str1, $str2) . "<br>\n"; // positive value
echo "eve: " . strpos($str1, "eve") . "<br>\n"; // index 2
echo "ev3: " . strpos($str1, "ev3") . "<br>\n"; // false/not found
$seven = 7;
echo "$seven<br>\n"; // will output: 7
echo '$seven<br>\n'; // will output: $seven<br>\n, notice the difference?
?>
</body>
</html>

Debugging PHP Code (1)

Now this can be painful...

When the client requests for a PHP file, the server will execute the PH(Pre-processor) code (pre-process it) before returning an HTML file (although we still see .php in the address bar of our client browser -- unless we set up our server to hide it)

If there is a warning in the PHP script, PHP will continue but logs the warning message in the web server error.log file (it is in folder /var/log/apache2 if you use LAMP stack on Ubuntu 16.04 @ DO droplet)

Debugging PHP Code (2)

For example, the script below will add a warning message at the tail of error.log

<?php $x = 1/0; // division by zero
?>
$ tail -1 /var/log/apache2/error.log # show the last line in error.log
[Sun Jan 29 12:57:14.878238 2017] [:error] [pid ????] [client ???.???.???.???:59850] PHP Warning:  Division by zero in /var/www/html/cs3226/error.php on line 8

Occassionally check the log file of your web server for such (silent) warning like this because if your web server is not configured properly, you may quickly run out of disk space due to accummulated warning messages

Debugging PHP Code (3)

If there is an unrecoverable error, the server cannot execute PHP code further, sends HTTP status 500 (Internal Server Error), (usually) shows a blank page (or partially rendered page), and also logs the error message in the error.log file

Such error usually cannot be handled even with this set_error_handler function (whereas the previous PHP warning can be handled this way)

Debugging PHP Code (4)

For example, the script below will add an error message at the tail of error.log

<?php echoes "Hello World"; // unknown PHP command 'echoes'
?>
$ tail -1 /var/log/apache2/error.log # change -1 to -n to get last n lines
[Sun Jan 29 12:59:51.149839 2017] [:error] [pid ????] [client ???.???.???.???:59888] PHP Parse error:  syntax error, unexpected '"Hello world"' (T_CONSTANT_ENCAPSED_STRING) in /var/www/html/cs3226/error.php on line 8

This time, the web programmer usually reacts faster as his/her PHP code does not work

Debugging server-side PHP code is indeed (much) harder than debugging client-side JavaScript code; but fortunately, Laravel has "debug" mode that we can use during development (turn it off in production for security)

Linking
Client-and-Server

PHP superglobals variables (1)

So far, what we have learned in this PHP lecture is similar with the client-side technology: JavaScript other than the fact it is now harder to see the PHP source code

We can use PHP to make the server reacts to client's inputs rather than just serving the same static webpage

PHP can process the information sent by client to server via HTTP GET* or HTTP POST* requests

To do this in PHP is simple, we access its superglobal arrays $_GET (useful in some cases, e.g. in http://nusmods.com) or $_POST (recommended method in most cases)

PHP superglobals variables (2)

There are a few other PHP superglobal arrays, e.g. $_REQUEST (combination of $_GET, $_POST, and $_COOKIE), $_SESSION (will be discussed soon), and some optional ones: $_SERVER, $_FILES, $_ENV, $_COOKIE, $GLOBALS (PS: Laravel uses different way to access these)

During the HTML5 lecture, we briefly discussed about the HTML5 forms, mainly about the HTML5 tags to display the form elements, but we have not discuss on how to actually process the data collected from user and we will do so now

Closing the Loop

A simple form.html and the corresponding form.php

<--client side, a simple form ("form.html")-->
<form method="post" action="form.php">
<p>What is 7 + 3?<br></p>
<input type="radio" name="mcq" value="9">A. 9
<input type="radio" name="mcq" value="10">B. 10
<input type="radio" name="mcq" value="11">C. 11
<input type="submit" name="submit" value="Answer">
</form>
<--server side, form.php-->
<p>You selected: <?php $ans = $_POST["mcq"]; echo $ans;?><br>
Correct: <?php echo ($ans == 10) ? "Y" : "N"; ?><br></p>

Not shown in the simple example above: Always validate/sanitize form input on server-side too even if you have done so in client-side using either HTML5 form elements' settings or JavaScript (but see Laravel version)

PHP session

In JavaScript lecture, we have seen how to add "state" to the stateless HTTP protocol in client-side: using HTML5 localStorage (we abandoned HTTP cookies)

We can also add "state" to the stateless HTTP protocol in server-side, using PHP session control

count.php and reset.php

<?php session_start(); must be the first line of your PHP script
$elapsed = 0;
if (empty($_SESSION['count'])) { /* or use !isset */
  $_SESSION['count'] = 1;
  $_SESSION['visittime'] = time();
}
else {
  $_SESSION['count']++;
  $elapsed = time()-$_SESSION['visittime'];
  $_SESSION['visittime'] = time();
}
echo "You have visited this page " . $_SESSION['count'] . " times<br>\n";
echo session_id() . ", your last visit was " . $elapsed . "s ago<br>\n"; 
/* this is count.php */ ?>
<?php session_start(); // must still use this first
session_destroy();
echo "Try visiting count.php again<br>\n";
/* this is reset.php */ ?>

More about using session in Laravel framework soon

AJAX, revisited

Several ways to execute PHP script @ web-server:

  1. Specify the direct URL of the PHP script
  2. Use HTML5 form submit feature (the recent example)
  3. Use AJAX (with jQuery): Call a PHP script with some input/data, asynchronously receive the output/processed data, and update the client-side with this new data

We will likely pass (complex) data between client and server as JSON, so please study json_encode (convert PHP associative array into JSON to be read by client-side JS) and json_decode (take in JSON from client-side JS and decode it into PHP object that can be cast as associative array)

Laravel Form (User Input)

Now, let's use the same example as with vanilla PHP from this slide as the first illustration on how to use Laravel to easily create forms, use server-side validator :O, and to process the form (for now, without database access)

But first, we need to set up LaravelCollective package as for some unclear reason, these useful features (in my opinion) are removed from the core framework...

Read this (for Laravel version 5.3, it may change a bit? for the new version 5.4 :O)

Laravel Version of this

{{-- test.blade.php --}}
{!! Form::open() !!} {{-- Blade shortcut for creating HTML5 form --}}
<div class="form-group"> {{-- Group related form components together --}}
  {!! Form::label('name', 'What is your name:', ['class' => 'control-label']) !!}
  {!! Form::text('name', null, ['class' => 'form-control']) !!}
</div>
<div class="form-group">
  {!! Form::label('mcq', 'What is your answer:', ['class' => 'control-label']) !!}
  {!! Form::radio('mcq', '9', false, ['class' => 'form-control']) !!}A.9
  {!! Form::radio('mcq', '10', false, ['class' => 'form-control']) !!}B.10
  {!! Form::radio('mcq', '11', false, ['class' => 'form-control']) !!}C.11
</div>
<div class="form-group"> {{-- Don't forget to create a submit button --}}
  {!! Form::submit('Submit', ['class' => 'form-control']) !!}
</div>
{!! Form::close() !!}

Add two methods in a controller

// all else standard
use Validator; // we will use this later

class FormController extends Controller {
  public function test() { return view('test'); }

  public function check(Request $request) {
    // ps, yeah, I 'cheat' by combining Controller and View together...
    // separate them in your actual project
    $name = $request->input('name');
    $ans = $request->input('mcq');
    return "<p>" . $name . ", you selected: " . $ans . "<br>" . 
           "Correct: " . ($ans == 10 ? "Y" : "N") . "<br></p>";
  }
}

Add two more routes in routes/web.php

Route::get('test', 'FormController@test');
Route::post('test', 'FormController@check'); // notice the POST method

Now you can test the test route
The temporary URL is this one

Validator

Later in Security lecture, we will talk more about this
Here, we just want to show that it is very easy to add
server-side validation using Laravel

Just add this in function FormController@check

  public function check(Request $request) {
    $validator = Validator::make($request->all(), [ // as simple as this
      'name' => 'required|min:3|max:10',
      'mcq' => 'required',
    ]);
    if ($validator->fails()) {
      return redirect('test') // redisplay the form
             ->withErrors($validator) // to see the error messages
             ->withInput(); // the previously entered input remains
    }
    // all else the same as before

Display the Error
(Simple Version)

// Everything else is the same as 'test.blade.php'
// ...
  {!! Form::submit('Submit', ['class' => 'form-control']) !!}
</div>
@if (count($errors) > 0) {{-- just list down all errors found --}}
  <div class="alert alert-danger">
    <ul>
      @foreach ($errors->all() as $error)
        <li>{{ $error }}</li>
      @endforeach
    </ul>
  </div>
@endif
{!! Form::close() !!}

Now you can test this version again

Database Access

Usually we want to store user's (form) input in a persistent storage and a (relational) database is a good solution

Good News: PHP has built-in support to run (My)SQL queries

Even Better News: Laravel, the chosen PHP framework for this semester, has even better Eloquent ORM that can make database access even easier...

In the next Database lecture, we will learn on how to use database in our web application

Things will get much more exciting after this

Extra Challenges

Challenge 1: multtable.php

Same as with the challenge in JS lecture but now in PHP

Can I see the php code?

Challenge 2: minivisualgo.php*

Same as with the challenge in JS lecture but now in PHP

Can I see the php code?

The End

Now, try this PHP quiz @ W3Schools

What have not been discussed?

  • More details about input validation functions (self study this for now)
  • PHP header function (deferred to security lecture)
  • PHP exit function (defered to SQL lecture)
  • PHP mail function (defered to Web Server lecture)
  • PHP $_FILES superglobals
  • More details about Laravel PHP framework (soon)