Choose a version here. If you've not already started the tutorial, just go with the latest one.

30 Aug 2014 Preview version Choose
5 Oct 2014 Explicitly adds a specific charset for htmlspecialchars(), and wraps it with a custom function Choose
16 Oct 2014 Updated redirect function to work with vhost subfolders Choose
4 Nov 2014 Minor improvements: added missing docblock, fixed security issue, CSS tweak. Switched hashing method to DEFAULT instead of BCRYPT, this is best practice. Choose
25 Nov 2014 Improve the notes on getting started, in particular choosing a programmer's editor. Added introduction to mod_rewrite rules. Choose
16 Aug 2018 Some bug fixes, remove compatibility library for earlier version of PHP Choose
OK
NB: There are several versions of this tutorial, each successive one containing additional improvements. If you're in the middle of working through it, please check the versions panel above, to ensure you're not mixing code from different versions.

Make your own blog

What is this?

It's a small, interactive course to teach beginners the PHP programming language.

Who's it for?

Anyone who wants to learn how to create simple database-powered web applications.

How much does it cost?

It's free of charge.

Do I need to sign up?

No, not at all.

How do I get started?

Click on the first chapter in the index.

How is each change introduced?

I use a visual device known as a diff. Related file changes are grouped into sets, like this example — explore by clicking on the tabs.

Expand/contract code area Select previous tab
Select next tab
1
2
3
4
5
6
7
8
9
10
11
12
<?php
// Work out the path to the database, so SQLite/PDO can connect
$root = __DIR__;
$database = $root . '/data/data.sqlite';
$dsn = 'sqlite:' . $database;
// Connect to the database, run a query, handle errors
$pdo = new PDO($dsn);
$stmt = $pdo->query(
'SELECT
id, title, created_at, body
1
2
 
 
 
 
3
4
5
6
7
8
<?php
require_once 'lib/common.php';
// Connect to the database, run a query, handle errors
$pdo = getPDO();
$stmt = $pdo->query(
'SELECT
id, title, created_at, body
1
 
 
2
3
4
5
6
7
8
40
41
42
43
44
45
46
<?php
// Get the PDO DSN string
$root = realpath(__DIR__);
$database = $root . '/data/data.sqlite';
$dsn = 'sqlite:' . $database;
$error = '';
// Connect to the new database and try to run the SQL commands
if (!$error)
{
$pdo = new PDO($dsn);
$result = $pdo->exec($sql);
if ($result === false)
{
1
2
3
4
5
6
 
7
8
9
40
41
42
43
44
45
46
<?php
require_once 'lib/common.php';
// Get the PDO DSN string
$root = getRootPath();
$database = getDatabasePath();
$error = '';
// Connect to the new database and try to run the SQL commands
if (!$error)
{
$pdo = getPDO();
$result = $pdo->exec($sql);
if ($result === false)
{
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
/**
* Gets the root path of the project
*
* @return string
*/
function getRootPath()
{
return realpath(__DIR__ . '/..');
}
/**
* Gets the full path for the database file
*
* @return string
*/
function getDatabasePath()
{
return getRootPath() . '/data/data.sqlite';
}
/**
* Gets the DSN for the SQLite connection
*
* @return string
*/
function getDsn()
{
return 'sqlite:' . getDatabasePath();
}
/**
* Gets the PDO object for database access
*
* @return \PDO
*/
function getPDO()
{
return new PDO(getDsn());
}
1
2
3
4
5
6
7
8
13
14
15
16
17
18
19
<?php
// Work out the path to the database, so SQLite/PDO can connect
$root = __DIR__;
$database = $root . '/data/data.sqlite';
$dsn = 'sqlite:' . $database;
// Get the post ID
if (isset($_GET['post_id']))
}
// Connect to the database, run a query, handle errors
$pdo = new PDO($dsn);
$stmt = $pdo->prepare(
'SELECT
title, created_at, body
1
2
 
 
 
3
4
5
13
14
15
16
17
18
19
<?php
require_once 'lib/common.php';
// Get the post ID
if (isset($_GET['post_id']))
}
// Connect to the database, run a query, handle errors
$pdo = getPDO();
$stmt = $pdo->prepare(
'SELECT
title, created_at, body

What, no framework?

Deliberately so. I think it's a good idea to learn what can be done with a language before using frameworks. Frameworks are good because they hide the nuts and bolts of applications from developers, but they can be bad, because they hide the nuts and bolts of applications from learners.

Is the tutorial finished?

Most tutorials are published and forgotten about, which is why there are so many on the web promoting old or insecure practices. This one is finished in the sense it is ready to use, but I intend to maintain it.

Will this work with the latest version of PHP?

Indeed it will. It will work on a version of PHP as low as 5.5, but since it was written with best practices to start off with, it works just fine with any PHP in the 7.x series as well.

How do I obtain assistance if I get stuck?

I have set up a support channel on Reddit, where you are welcome to post about any problems you may have with this material. As the author, I'll try to be around to help.

If I'm not around, PHPhelp is also worth a try. There are also plenty of other help forums and chat rooms on the web, which can be found with an internet search, and many of these will cater to beginners.

Can I offer some general feedback?

Of course. If you have some general remarks, then please post a new topic on the support channel. If you see a specific issue, or you can see something that could be done better, please raise a bug report on GitHub.

Any tips on how to break through beginner frustration?

Building a group of fellow programmers of a similar level of learning can be very helpful. You can do this in your local area, or reach out to people over the web via social media. You can use tools like instant messaging, perhaps meeting at an agreed weekly time, to chat whilst you learn.