LAMP Programming in PHP

From NYC Resistor Wiki
Jump to navigation Jump to search

A quick review of the PHP language[edit]

Syntax[edit]

Mixing code and template[edit]

  • PHP pages mix code and display into a single page.
  • Markup starts as standard HTML.
  • Break into using <?php ...php code... ?>.
  • Use <?= ...php expression... ?> as shorthand to insert an expression into output.

Code Syntax[edit]

  • Syntax is similar to C or Perl.
  • Commands are separated by semi-colons.
  • Closing tag counts as a semi-colon.
  • Curly braces surround code blocks.
  • Comments use any common format:
    • // This is a comment.
    • /* This is a muliline comment. */
    • # This is a shell style comment.

Sample[edit]

<html>
<head>
  <title>Example PHP page</title>
</head>
<body>
  <?php
    // This function will be run on the server
    function foo() {
       return "World"; 
    }
    /* This
        is
         a
       comment */
  ?>
  <h1>Hello, <?= foo() ?></h1>
</body>
</html>

Data Types[edit]

  • Variables are prefixed with a dollar sign (eg: $foo)

Scalars[edit]

  • Represents a single value.
    • Boolean (eg: true, FALSE, tRuE)
    • Number (eg: 42, 3.141592653, 0xFF, -1)
    • String (eg: "foo", 'bar', "baz\n", "Hello, $foo")
      • Strings expand variables
      • Concatenate strings with . not +
    • NULL

Arrays[edit]

  • Represents a collection of values.
  • Unlike other languages, all PHP arrays are associative.
  • Created using array() function
    • array() - empty array.
    • array(1,2,3) - array containing 1, 2, 3 with indexes 0, 1, 2.
    • array('foo'=>1, 'bar'=>2, 'baz'=>3) - array containing 1, 2, 3 with indexes foo, bar, baz.
    • array('foo'=>1, 2) - array containing 1, 2 with indexes foo and 0.
  • Indexes can only be integers or strings. Other types will be converted.
  • Assigning arrays is a copy operation, use references.

Sample[edit]

<pre>
<?php
  $a = true;
  $b = 12;
  $c = "Hello, World!";
  $d = NULL;
  $e = array($a,$b,'foo'=>$c,'bar'=>$d);
  echo("$e[0]\n"); // Prints true
  echo("$e[1]\n"); // Prints 12
  echo($e['foo']."\n"); // Prints Hello, World!
  echo("$e[bar]\n"); // Prints nothing
  echo("$e\n"); // Prints Array
?>
</pre>

Variable Scope[edit]

Unlike other scripting languages like perl, all variables are local to their code block. To reference variables in an above scope use the global keyword.

<?php
  $a = 1;
  $b = 2;
  $c = 3;

  function foo() {
    global $b;
    $c = 4;
    echo($a);  // Prints nothing
    echo($b);  // Prints 2
    echo($c);  // Prints 4
  }

  foo();
  echo($c);  Still prints 3, foo didn't modify.
?>

Operators[edit]

  • Arithmetic
    • +, -, /, * (plus, minus, divide, multiply)
    • ++, -- (increment, deincrement)
    • % (modulus)
  • Assignment
    • = (set equal to)
    • +=, -=, *=, /= (plus-equal, minus-equal, ...)
    • .= (concatenate)
    • %= (modulus)
  • Compare
    • ==, <=, >=, <, > (equals, less than or equal to, ...)
    • === (is exactly - "1" === 1 is false.)
  • String
    • . (concatenate a string)
  • Logical
    • && (and)
    • || (or)
    • ! (not)

Statements[edit]

<pre>
<?php
  // If-Else (anything non-false is true)
  if (true && 1 && "foo" && "false") {
     echo("This executes\n");
  }
  else {
     echo("This does not\n");
  }

  // If-Elseif (only these things are false)
  if (false || 0 || NULL || "") {
     echo("This does not execute\n");
  }
  elseif (true) {
     echo("This does\n");
  }

  // For loop
  for ($x=0; $x<10; $x++) {
     echo("This executes 10 times\n");
  }    
  
  // While loop
  $x=10
  while ($x>0) {
     echo("This also executes 10 times\n");
     $x--;
  }

  // Foreach loop iterates over an array
  things = array(1,2,3,4,5,6,7,8,9,10);
  foreach($things as $thing) {
     echo("Thing is $thing\n"); // Prints 1-10
  } 

  // Foreach loop also iterates associatively
  things = array(one=>1, two=>2, three=>3);
  foreach($things as $key => $value) {
     // Prints one is 1, two is 2, three is 3.
     echo("$key is $value\n"); 
  }

  // Break leaves a looping block
  while(true) {
     break;  // Won't loop forever
  }

  // Continue restarts at the top
  for($x=0; $x<10; $x++) {
     echo("$x\n");
     continue;   // Jump to the next iteration
     echo($x/0); // Divide by zero -- won't run!
   }
?>

Commonly used functions[edit]

PHP has so many functions. Much of it is very non-OOP, preferring to litter the global namespace with lots and lots of functions. Functions available vary on how PHP was compiled, use phpinfo() to see that information. Here's some common ones.

  • Array functions
    • count($array) - Number of items in an array
    • sort($array) - Sort an array
    • ksort($array) - Sort an associative array by key
    • array_push($array,$item) - Push an item onto the end of the array
    • array_pop($array) - Pop a item off the end of the array
    • array_unshift($array,$item) - Add an item onto the beginning of the array
    • array_shift($array) - Shift the first item off the array
  • Output functions
    • echo($a[,$b,...]) - Prints something to the web output
    • printf($format[,$a,...]) - Formatted printing
    • print_r($a) - Recursively output a data structure
  • String functions
    • chop($string) - Removes the last character from a string
    • sprintf($format[,$a...]) - Format a string
    • strtolower($string)
    • strtoupper($string)
    • ucfirst($string)
    • ucwords($string)
    • strip_tags($string)
    • join($delmiter,$array)
    • str_split($delimiter,$string)
  • Regular expression functions
    • preg_match($pattern,$string)
    • preg_replace($pattern,$replacement,$string)
    • preg_split($patter,$string)
  • Misc
    • die($message) - Abort with error
    • exit($message) - Exit with message
    • eval($code) - Execute arbitrary code (careful!)
    • phpinfo() - Spits out lots of information about your PHP install, don't leave this lying around.

User-defined functions[edit]

function foo($bar, $baz) {
  // Code
  return $result;
}

Objects[edit]

<?php
class A
{
    function foo()
    {
        if (isset($this)) {
            echo '$this is defined (';
            echo get_class($this);
            echo ")\n";
        } else {
            echo "\$this is not defined.\n";
        }
    }
}

class B
{
    function bar()
    {
        A::foo();
    }
}

$a = new A();
$a->foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>

Processing Web Forms[edit]

GET and POST requests[edit]

  • GET requests are for non-modifying requests, like searches
  • GET requests send variable information in the URL
  • GET requests are cachable
  • use $_GET['key'] to access GET variables
  • POST requests are for modifying requests, like a sign up form
  • POST requests send variable information in the HTTP body
  • POST requests should not be cached
  • use $_POST['key'] to access POST variables
  • $_REQUEST['key'] will get from either GET or POST or COOKIES. Maybe that's a bad idea.
  • $_SERVER['REQUEST_METHOD'] will tell you if it's a GET or POST.

Forms[edit]

  • The form tag defines a block of form elements to submit
    • method="GET" or method="POST"
    • action="url to submit to"
    • enctype is useful if you're sending files
  • Form elements
    • Form elements all have a name attribute, this is the key the element is submitted as
    • Form elements should also include an id attribute, so javascript can address them
    • The input tag is the most basic form element, the type attribute controls how it works
      • type="text" - A standard text box
      • type="password" - A standard text box but the characters are obscured
      • type="hidden" - A hidden form element, useful for behind the scenes data
      • type="checkbox" - A check box. If unchecked nothing is submitted for this key
      • type="radio" - A radio button. Include multiple of these with the same name
      • type="submit" - The submit button, every form should include one. This sends the form.
      • type="image" - A submit button with an image for the button.
      • The value attribute controls the default value
      • The size attribute controls the size of the element
    • The textarea tag is for a big block of text.
      • Use rows and cols to control the size of the textarea
      • Use the wrap to control word wrapping. You probably want wrap="soft"
    • The select tag is a section list.
      • Use an option tags within the select tag for each list item

Example[edit]

<?php
  $error = NULL;

  function authenticate($username, $password) {
    // magic authentication code here
  }

  // If the request is POST and the Login button was clicked
  if ($_SERVER['REQUEST_METHOD']=='POST' && $_POST['submit'] == "Login") {
    if (authenticate($_POST['username'],$_POST['password'])) {
      // Send the user back to the page they came from.
      header("Location: $_POST['return_url']");
    }
    else {
      $error = "Incorrect username or password.";
    }
  }
?>

<h1>My Simple Login Form</h1>
<?php
  // If the form processed has an error, print it out here. 
  if ($error) {
    echo("<div class='error'>$error</div>");
  }
?>
<!-- $_SERVER['PHP_SELF'] is the url to this very form -->
<form method="POST" action="<?= $_SERVER['PHP_SELF'] ?>">
  <p>
    <label for="username">Username:</label><br /> 
    <input type="text" id="username" name="username" size="30" />
  </p>
  <p>
    <label for="password">Password:</label><br />
    <input type="password" id="password" name="password" size="30" />
  </p>
  <p>
    <!-- remember will be true if checked, NULL if not -->
    <input type="checkbox" id="remember" name="remember" value="true" />
    <label for="remember">Remember me</label>
  </p>
  <p>
    <!-- The submit button sends data too.  The value is the label of the button. -->
    <input type="submit" id="submit" name="submit" value="Login" />
  </p>
  <!-- The page to send the user back to.  Check out the short cicruit.  Notice REFERER is mispelled. -->
  <input type="hidden" id="return_url" name="return_url" 
     value="<?= $_GET['return_url'] ? $_GET['return_url'] : $_SERVER['HTTP_REFERER'] ?>" 
  />

</form>

Stateless programming[edit]

  • HTTP is a stateless protocol
    • That means every request is a fresh start for your program
    • We use cookies and post back information to keep track of who and what's going on
    • Thankfully PHP has a handy session management system that abstracts this all away

Example[edit]

<?php
// Starts/Restores session
session_start();

// Clear the session if user sends reset
if ($_GET['reset']) {
  // Unset all of the session variables.
  $_SESSION = array();

  // If it's desired to kill the session, also delete the session cookie.
  // Note: This will destroy the session, and not just the session data!
  if (isset($_COOKIE[session_name()])) {
      setcookie(session_name(), '', time()-42000, '/');
  }

  // Finally, destroy the session.
  session_destroy();
}

if (!isset($_SESSION['count'])) {
  $_SESSION['count'] = 0;
} 
else {
  $_SESSION['count']++;
}
?>

<p>You have visited this page <?= $_SESSION['count'] ?> times.</p>

<p><a href="<?= $_GLOBAL['PHP_SELF'] ?>?reset=1">Reset Counter</a></p>

Secure user-input[edit]

  • Never allow user input to be echoed back onto the page as-is.
    • Someone can mess up the design of your page by inserting HTML
    • Someone can steal your users cookies by inserting Javascript
  • Use strip_tags($string) to remove all HTML
  • Use strip_tags($string,$allowable_tags) to most HTML.
    • Careful, this doesn't remove attributes so I can use onmouseover to hack your site.
  $username = strip_tags($_POST['username']);
  $about = strip_tags($_POST['about'],"a,b,u,i,strong");

  ## Strip out attributes using regular-expressions.
  $about = preg_replace("/<(\w+)\s+.*?>/",'<$1>',$about);

Storing data[edit]

Using files[edit]

Writing[edit]

// Append some data to a file.
$handle = fopen("/secure/location/file.txt","a");
fputs($handle, $data);
fclose($handle);

// Slurp up an entire file
$data = file_get_contents("/secure/location/file.txt");

Using JSON[edit]

  • JSON is a handy way to encode data to pass to a browser for AJAX calls, and to store on disk if we're not using a real database.
function write_new_user($username, $password, $first_name, $last_name, $email) {
  $data = array(
    "username" => $username,
    "password" => md5($password),
    "first_name" => $first_name,
    "last_name" => $last_name,
    "email" => $email
  );
  $handle = fopen("/secure/location/users.txt","a");
  puts($handle,json_encode($data));
  flclose($handle);
}

function read_users() {
  
}

Security[edit]

  • Make sure you write your files somewhere secure -- Don't put them in a directory accessible on the web!
  • Be real careful with your filenames. Never allow user input to influence the filename without validation.
  • I like to pass all filenames and paths through lookup tables or configuration files to make extra sure.

Using MySQL[edit]

CREATE TABLE[edit]

  CREATE TABLE users (
     id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
     username VARCHAR(50) NOT NULL,
     password VARCHAR(50) NOT NULL,
     first_name VARCHAR(50),
     last_name VARCHAR(50),
     email VARCHAR(50) NOT NULL,
     last_seen DATETIME
     UNIQUE(username),
     INDEX(username,password),
     INDEX(last_seen)
  );

  ## Create a posts table

INSERT[edit]

  INSERT INTO some_table (column1, column2, column2) VALUES ("foo","bar","baz");
  
  # OR YOU CAN DO THIS

  INSERT INTO some_table SET foo=1, bar="two", baz=1+1+1;

  INSERT INTO users (username, password, email) VALUES ("justin","doifjsdofijsd","[email protected]");

UPDATE[edit]

  UPDATE users SET last_seen=NOW() WHERE id=1234;

  ## CAREFUL!

  UPDATE users SET password=MD5(?);

DELETE[edit]

   DELETE FROM users WHERE id=1234;
  • I HATE DELETE -- WHY NOT THIS INSTEAD??
  UPDATE users SET deleted=1 WHERE id=1234;

SELECT[edit]

  ## Easy authentication!
  SELECT username, email, first_name, last_name FROM users WHERE username=? AND password=MD5(?);

  ## Give me all users in order of last sign on
  SELECT id,username FROM users ORDER BY last_seen DESC;

  ## That's too many users, how about this instead
  SELECT id,username FROM users ORDER BY last_seen DESC LIMIT 0,30;

  ## Page 3
  SELECT id,username FROM users ORDER BY last_seen DESC LIMIT 60,30;

  ## JOINS combine table from two or more tables
  ## LIKE does a substring search, the % is a wildcard.
  SELECT posts.id AS posts_id, posts.title, users.id AS users_id, users.username
  FROM posts
  JOIN users ON posts.users_id=users.id
  WHERE posts.title LIKE CONCAT('%',?,'%')

Do it in PHP[edit]

  • If you can, use mysqli!
 function get_mysql_connection() {
     $host="localhost";
     $port=3306;
     $socket="/var/run/mysqld/mysqld.sock";
     $user="justin";
     $password="password";
     $dbname="justin";

     $conn = new mysqli($host, $user, $password, $dbname, $port, $socket)
     or die ('Could not connect to the database server' . mysqli_connect_error());

     return $conn;
  }

  function write_user($username, $password, $first_name, $last_name, $email) {
      $conn = get_mysql_connection();
      $stmt = $conn->prepare(
         "INSERT INTO users (username,password,first_name,last_name,email) " .
         "VALUES (?,MD5(?),?,?,?);"
      );
      $stmt->bind_param('sssss',$username,$password,$first_name,$last_name,$email);
      $rows_affected = $stmt->execute();

      return $rows_affected; // Should be 1 if all goes well.
   }

   function write_post($users_id, $title, $body) {
      // Write this function
   }

Security[edit]

  function get_mysql_connection() {
     $host="p:localhost";
     $port=3306;
     $socket="/tmp/mysql.sock";
     $user="root";
     $password="password";
     $dbname="test";

     $conn = new mysqli($host, $user, $password, $dbname, $port, $socket)
     or die ('Could not connect to the database server' . mysqli_connect_error());
     
     return $conn;
  }

  function write_user($username, $password, $first_name, $last_name, $email) {
      $conn = get_mysql_connection();
      $rows_affected = $conn->query(
          "INSERT INTO users (username,password,first_name,last_name,email) ".
          "VALUES ('$username',MD5('$password'),'$first_name','$last_name','$email'"
      );

      return $rows_affected; // Should be 1 if all goes well.
   }
  • Look how much easier that is! What happens if my username is "; DELETE FROM users;" though?!

How NOT to use MySQL[edit]

  • Don't over rely the database.
    • It's first job is to be correct, it's second job is to be fast.
    • You know your data better!
    • Sometimes it's better to do things in code.
  • INDEX things that you'll use in the WHERE part of a query.
  • Learn to love EXPLAIN!
  • NEVER EVER EVER ALLOW UNVALIDATED USER INPUT IN QUERIES

The Uber Example[edit]

<?php
 $VALIDATION_ERRORS = array();

 function get_mysql_connection() {
     $host="localhost";
     $port=3306;
     $socket="/var/run/mysqld/mysqld.sock";
     $user="justin";
     $password="day";
     $dbname="justin";
 
     $conn = new mysqli($host, $user, $password, $dbname, $port, $socket)
     or die ('Could not connect to the database server' . mysqli_connect_error());
 
     return $conn;
  }
 
  function write_user($username, $password, $first_name, $last_name, $email) {
      $conn = get_mysql_connection();
      $stmt = $conn->prepare(
         "INSERT INTO users (username,password,first_name,last_name,email) " .
         "VALUES (?,MD5(?),?,?,?);"
      );
      $stmt->bind_param('sssss',$username,$password,$first_name,$last_name,$email);
      $rows_affected = $stmt->execute();

	  // TODO FIXME Return LAST_INSERT_ID() instead!
      return $rows_affected; // Should be 1 if all goes well.
   }

   function get_user_by_username($username) {
   		$conn = get_mysql_connection();
		$stmt = $conn->prepare("SELECT id,username,email,first_name,last_name FROM users WHERE username=? LIMIT 1");
		$stmt->bind_param('s',$username);
		
		$stmt->execute();
		$stmt->bind_result($users_id,$username,$email,$first_name,$last_name);

		if ($stmt->fetch()) {
			return array("users_id"=>$users_id,"username"=>$username,"email"=>$email,"first_name"=>$first_name,"last_name"=>$last_name);
		}
		else {
			return NULL;
		}
	}

   function get_user_by_username_password($username,$password) {
   		$conn = get_mysql_connection();
		$stmt = $conn->prepare("SELECT id,username,email,first_name,last_name FROM users WHERE username=? AND password=MD5(?) LIMIT 1");
		$stmt->bind_param('ss',$username,$password);
		
		$stmt->execute();

		if ($stmt->fetch()) {
			return array("users_id"=>$users_id,"username"=>$username,"email"=>$email,"first_name"=>$first_name,"last_name"=>$last_name);
		}
		else {
			return NULL;
		}
	}
 
   function write_post($users_id, $title, $body) {
   	   $conn = get_mysql_connection();
	   $stmt = $conn->prepare(
	      "INSERT INTO posts (posted_on, users_id, title, body) ".
		  "VALUES (NOW(), ?, ?, ?);"
	   );
	   $stmt->bind_param("dss",$users_id,$title,$body);
	   $stmt->execute();
	   // TODO FIXME Add SELECT LAST_INSERT_ID() call to get new posts.id
   }

	function echo_sign_in_area() {
		echo('<div id="SignInArea">');
		if ($_SESSION['username']) { 
			echo('Welcome ' . $_SESSION['username'] . '. <a href="'.$_SERVER['PHP_SELF'].'?command=Sign+Out">Sign Out</a>');
		}
		else {
			echo('<a href="javascript:promptToSignIn();">Sign In</a>');
			echo(' or ');
			echo('<a href="javascript:promptToSignUp();">Sign Up</a>');
		}
		echo('</div>');
	}

	function echo_blog_entry($posts_id, $users_id, $username, $posted_on, $title, $body) {
		echo("<div id='BlogEntry$posts_id' class='BlogEntry'>");
		echo("<div class='Title'>$title</div>");
		echo("<div class='Meta'>Written by <a href='view_user.php?users_id=$users_id'>$username</a> on $posted_on.</div>");
		echo("<div class='Body'>$body</div>");
		echo("</div>");
	}

	function echo_blog_entries() {
		$conn = get_mysql_connection();
		$rows = $conn->query(
			"SELECT id AS posts_id, posted_on, title, body, users.id AS users_id, username ".
			"FROM posts JOIN users ON posts.users_id=users.id " .
			"ORDER BY posted_on DESC ".
			"LIMIT 10"
		);

		if (!$rows)
			return;

		while($row = $rows->fetch_assoc()) {
			echo_blog_entry(
				$row['posts_id'],
				$row['users_id'],
				$row['username'],
				$row['posted_on'],
				$row['title'],
				$row['body']
			);
		}
	}

	function set_validation_error($id,$error) {
		global $VALIDATION_ERRORS;

		$VALIDATION_ERRORS[$id] = $error;
	}

	function echo_validation_error($id) {
		global $VALIDATION_ERRORS;

		if ($VALIDATION_ERRORS[$id]) {
			echo("<div id='".$id."Error' class='Error'>".$VALIDATION_ERRORS[$id]."</div>");
		}
		else {
			// Put a hidden error there for client-side validation
			echo("<div id='".$id."Error' class='Error' style='display:none'></div>");
		}
	}

	function process_command() {
		switch($_REQUEST['command']) {
			case "Sign Up":
				process_sign_up();
				break;
			case "Sign In":
				process_sign_in();
				break;
			case "Sign Out":
				process_sign_out();
				break;
			default:
				break;
		}
	}

	function validate_sign_up() {
		$result = TRUE;

		if (!$_POST['username']) {
			set_validation_error('username','Username is required');
			$result = FALSE;
		}

		$user = get_user_by_username($_POST['username']);
		if ($user) {
			set_validation_error('username','That username has already been taken.');
			$result = FALSE;
		}

		if (!$_POST['password']) {
			set_validation_error('password','Password is required');
			$result = FALSE;
		}

		if ($_POST['repeat'] != $_POST['password']) {
			set_validation_error('repeat','Passwords do not match');
			$result = FALSE;
		}

		if (!$_POST['email']) {
			set_validation_error('email','Email is required');
			$result = FALSE;
		}

		return $result;
	}

	function process_sign_up() {
		if (validate_sign_up()) {
			write_user(
				$_POST['username'],
				$_POST['password'],
				$_POST['first_name'],
				$_POST['last_name'],
				$_POST['email']
			);
			
			$_SESSION['username'] = $_POST['username'];
		}
	}

	function process_sign_in() {
		$user = get_user_by_username_password($_POST['username'],$_POST['password']);

		if (!$user) {
			set_validation_error('username','Incorrect username or password');
		}

		$_SESSION['username'] = $user['username'];
	}

	function process_sign_out() {
		$_SESSION['username'] = NULL;
	}

	session_start();
	process_command();

?>

<html>
<head>
  <title>Welcome to my SimpleBlog!</title>
  <!--<script type="text/javascript" src="jquery-latest.js"></script>-->
  <script type="text/javascript">
  	function getElement(id) {
		return document.getElementById(id);
	}

	function getValue(id) {
		return getElement(id).value;
	}

  	function promptToSignIn() {
		// Hide the sign up elements	
		getElement("SignUp").style.display="none";

		// Set the action to be signing in
		getElement("SignInSubmit").value="Sign In";

		// Show the sign in popup
		getElement("SignInPopup").style.display="block";

		// Focus on username
		getElement("username").focus();
	}

	function promptToSignUp() {
		// Show the sign up elements
		getElement("SignUp").style.display="block";

		// Set the action to be signing in
		getElement("SignInSubmit").value="Sign Up";

		// Show the sign in popup
		getElement("SignInPopup").style.display="block";

		// Focus on username
		getElement("username").focus();
	}

	function setValidationError(id,error) {
		var element = getElement(id + 'Error');
		element.innerHTML = error;
		element.style.display = "block";
	}

	function validateSignInForm() {
		var result = true;

		if (!getValue('username')) {
			setValidationError('username','Username is required');
			result = false;
		}

		if (!getValue('password')) {
			setValidationError('password','Password is required');
			result = false;
		}

		if (getValue('SignInSubmit') == 'Sign Up') {
			if (getValue('repeat') != getValue('password')) {
				setValidationError('repeat','Passwords do not match');
				result = false;
			}

			if (!getValue('email')) {
				setValidationError('email','Email is required');
				result = false;
			}
		}

		return result;
	}
  </script>
  <style type="text/css">
	.PopUp {
		display: none;
		position: absolute:
		top: 30px;
		width: 350px;
		margin: auto;
		padding: 10px;
		min-height: 50px;
		background-color: #ccc;
		border: 3px solid #999;
	}

	.PopUp th {
		width: 150px;
		text-align: left;
	}

	#BlogPostPopup {
		width: 600px;
	}

	.Error {
		color: red;
		font-weight: bold;
	}

	.Required {
		font-weight: bold;
		color: red;
	}

  </style>
</head>
<body>
  <div id="Heading">
    <h1>Welcome to my SimpleBlog!</h1>
    <?php echo_sign_in_area(); ?>
  </div>
  <div id="Main">
     <?php echo_blog_entries(); ?>
  </div>
  <div id="SignInPopup" class="PopUp">
  	<form id="SignInForm" method="POST" action="<?= $_SERVER['PHP_SELF'] ?>" onsubmit="return validateSignInForm()">
    <table>
	<tr>
	  <th><label for="username" class="Required">Username:</label></th>
	  <td>
	  	<input type="text" id="username" name="username" size="30" />
		<?php echo_validation_error("username") ?>
	  </td>
	</tr>
	<tr>
	  <th><label for="password" class="Required">Password:</label></th>
	  <td>
	  	<input type="password" id="password" name="password" size="30" />
		<?php echo_validation_error("password") ?>
	  </td>
	</tr>
	</table>
	<table id="SignUp">
	<tr>
	  <th><label for="repeat" class="Required">Repeat:</label></th>
	  <td>
	  	<input type="password" id="repeat" name="repeat" size="30" />
		<?php echo_validation_error("repeat") ?>
	  </td>
	</tr>
	<tr>
	  <th><label for="email" class="Required">Email:</label></th>
	  <td>
	  	<input type="text" id="email" name="email" size="30" />
		<?php echo_validation_error("email") ?>
	  </td>
	</tr>
	<tr>
	  <th><label for="first_name">First Name:</label></th>
	  <td>
	  	<input type="text" id="first_name" name="first_name" size="30" />
		<?php echo_validation_error("first_name") ?>
	   </td>
	</tr>
	<tr>
	  <th><label for="last_name">Last Name:</label></th>
	  <td>
	  	<input type="text" id="last_name" name="last_name" size="30" />
		<?php echo_validation_error("last_name"); ?>
	  </td>
	</tr>
	</table>
	<table>
	<tr>
		<th>&nbsp;</th>
		<td><input type="submit" id="SignInSubmit" name="command" value="Sign In" />
	</tr>
	</table>
	</form>
  </div>
  <div id="BlogPostPopup" class="PopUp">
  </div>
</body>
</html>