Andrew Sellergren
jharvard@cs50.net
.$_GET
and $_POST
which store input from forms. In addition to these superglobal variables, there are also the following:
$_SERVER
$_COOKIE
$_SESSION
$_SERVER
stores some interesting information about the server and user (e.g. IP address, request method). $_COOKIE
keeps track of cookies, unique identifiers that are assigned to users. Most useful to us, however, is $_SESSION
. This variable can store anything we want to keep track of, e.g. strings, arrays, objects, for the duration of a user's visit to our website. Think of it like a shopping cart on a merchant's website. Whatever's in the cart stays there until the user closes his browser or until the user or web server decides to delete it.To see how we might use the $_SESSION
variable, take a look at counter.php
:
<?php
/*************************************************************
* counter.php
*
* David J. Malan
* malan@harvard.edu
*
* Implements a counter. Demonstrates sessions.
*****************************************************************/
// enable sessions
session_start();
// check counter
if (isset($_SESSION["counter"]))
{
$counter = $_SESSION["counter"];
}
else
{
$counter = 0;
}
// increment counter
$_SESSION["counter"] = $counter + 1;
?>
<!DOCTYPE html>
<html>
<head>
<title>Counter</title>
</head>
<body>
You have visited this site <?= $counter ?> time(s).
</body>
</html>
session_start
function. Then we check if our $_SESSION
variable already has an index in it named "counter." If it does, we initialize a temporary variable to its value. If it doesn't, we initialize the temporary variable to 0. In both cases, we increment the value stored at the "counter" key in $_SESSION
. Finally we spit out some HTML that prints out the value of the temporary variable. To dynamically insert the value of a PHP variable into some HTML, we simply enclose it with the <?=
and ?>
tags. This is just shorthand for a number of other functions we could have called, including print
, printf
, and echo
.$_SESSION
variable will be invaluable to you as you implement Problem Set 7, a fake stock-trading website. We'll provide you with the ability to query Yahoo! Finance for a stock price using a single function. It will be up to you to empower users to register for your website as well as to buy and sell stocks.index.php
which links to lectures.php
, which links to week1.php
and week2.php
, each of which has PDF files for download. This is all well organized, but if we want to change the aesthetics of the website, we have a lot of files to modify.header.php
and footer.php
. Now each of our pages need only call the require
function to include these common files.We can take this abstraction one step further in version 2 by writing some helper functions to render the header and footer:
<?php
/**
* Renders footer.
*/
function renderFooter($data = [])
{
extract($data);
require('footer.php');
}
/**
* Renders header.
*/
function renderHeader($data = [])
{
extract($data);
require('header.php');
}
?>
Both functions take a single argument named $data
. By writing $data = []
, we specify that the default value for $data
should be an empty array. extract
is a function which takes the keys of an associative array and turns them into local variables. So, if you have an array like [ "foo" => "bar" ]
, then extract
will create a local variable named $foo
whose value is "bar." Having done this, header.php
and footer.php
will then have access to these local variables. Indeed, in header.php
, we now don't have to hardcode "CS50" as the title, but rather can use a variable $title
:
<!DOCTYPE html>
<html>
<head>
<title><?php echo htmlspecialchars($title) ?></title>
</head>
<body>
<h1><?php echo htmlspecialchars($title) ?></h1>
htmlspecialchars
function ensures that any special HTML characters that are passed to it are first escaped so that they aren't treated as actual HTML. This helps prevent what's called a cross-site scripting (XSS) attack in which someone else can inject HTML code into your website.Let's go deeper in version 3. Our renderFooter
and renderHeader
function look pretty similar, so why not write one render
function that does the work of both:
<?php
/**
* Renders template.
*/
function render($template, $data = [])
{
$path = $template . '.php';
if (file_exists($path))
{
extract($data);
require($path);
}
}
?>
Here we take an extra argument (e.g. "footer" or "header"), concatenate the file extension ".php" to it, then include it via require
as before. Our index.php
now looks like this:
<?php require('helpers.php'); ?>
<?php render('header', ['title' => 'CS50']); ?>
<ul>
<li><a href="lectures.php">Lectures</a></li>
<li><a href="http://cdn.cs50.net/2012/fall/lectures/0/syllabus.pdf">Syllabus</a></li>
</ul>
<?php render('footer'); ?>
includes/
and templates/
. Now that we've moved our helpers.php
file into the includes/
directory, we need to be sure to pass includes/helpers.php
to require
. As you will also notice in index.php
, we're now using the require_once
function in place of require
. As you might guess, require_once
ensures that a file is only included once.html/
which has index.php
, lectures.php
, week1.php
, and week2.php
within it. These files are the only ones that need to be publicly accessible. The others, which may be more sensitive, are kept separate in the templates/
and includes/
directories.../includes/helpers.php
to require_once
. This tells the function to go up one directory before looking for the includes/helpers.php
path.$1$
is an artifact of MD5, the method by which they were encrypted. The 50$
is a salt that is combined with the password to enhance security. You don't need to worry about understanding MD5, though, because this users table will be provided for you in Problem Set 7.DELETE
INSERT
UPDATE
SELECT
lecture
. On the lefthand menu, we now see the word "lecture" which, when clicked, takes us to a prompt to create tables. Let's imagine that we want to store a id, name, and e-mail address for some number of students, so we can create a table named students
that has 3 columns. This will present us with a form that has 3 rows, one for each column we just asked for.id
column should probably be INT
and we'll take it one step further by specifying UNSIGNED
from the Attributes dropdown menu so that none of our IDs are negative. The name
and email
columns should probably be strings. In SQL, a string which has a variable length is designated a VARCHAR
. For this type, we have to provide the Length/Values option, which means the maximum length that this column's values can be. By convention, we'll use 255, a power of 2 (minus 1). Note that a VARCHAR
is smart enough to store only the number of bytes that a string actually requires, not the maximum for every string.id
is (just so long as it's unique), we can have MySQL generate it for us by checking the A_I checkbox. A_I stands for AUTO_INCREMENT
.PRIMARY
from the Index dropdown for our id
column, then we've told MySQL that the value of id
will always be unique. Knowing this, MySQL can build up a special data structure called a B-tree to make lookups against this column as fast as possible. In theory, email
could also have been the primary key for this table, since e-mail addresses might be unique across users. However, in general it's better to use an integer as an index rather than a string since an integer only requires 4 bytes of memory whereas a string might require many more bytes. Still, if we want email
to be unique, we can choose UNIQUE
from the Index dropdown menu. This tells MySQL that the same e-mail address should not be inserted more than once.id
column blank and we set name
to be "David Malan" and email
to be "malan@harvard.edu." For our second entry, we again leave id
blank and we set name
to be "Mike Smith" and email
to be "smith@example.com." We click Go and suddenly we have two rows in our table! We can see these rows by clicking on the Browse tab for this table.Soon you'll be writing your own SQL without the help of phpMyAdmin. Let's try getting some data out of this table we just created:
SELECT * FROM students WHERE email="malan@harvard.edu"