diff --git a/README.md b/README.md index adc69c8..087f752 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Tutorial text for "I ♥ PHP" === -This repo contains the text for the "I ♥ PHP" tutorial. +This repo contains the text for the [I ♥ PHP](http://ilovephp.jondh.me.uk) tutorial. The tutorial has been designed for beginner and improving-beginner programmers wishing to learn some good practices in PHP web development. The code example uses PDO/SQLite, and demonstrates parameterisation, HTML escaping, logic/content separation, authentication, form handling, sessions @@ -10,12 +10,13 @@ improvements if they wish. Code changes in the tutorial are shown as diffs in the text, and each modified file can be downloaded in its entirety at that point of development. To facilitate this, files and diffs are -extracted from this repo by a script, rather than being copied in manually. This means that -code improvements are much easier to transpose to the tutorial, than the traditional method of +extracted from this repo by a script, rather than being copied in manually. This makes +code improvements much easier to transpose to the tutorial than the traditional method of making adjustments by hand. -Here is [a blog post about the tutorial](http://blog.jondh.me.uk/2014/08/online-php-beginners-tutorial/), -which is presently in alpha status. +Here is [a blog post about the tutorial](http://blog.jondh.me.uk/2014/08/online-php-beginners-tutorial/) +that was written prior to, and at the time of, going live. Since then, some readers have made +suggestions, tickets have been raised, and improvements have been committed and published. See also the [code repo here](https://github.com/halfer/php-tutorial-project). diff --git a/pages/original.html b/pages/original.html index 4877928..dcde376 100644 --- a/pages/original.html +++ b/pages/original.html @@ -13,7 +13,7 @@
@@ -53,7 +53,7 @@
- The first is that the mysql_ library is still in use (a library is
+ The first is that the mysql_ library is still in use (a library is
just a package of useful functions). This has been a staple of database access
in PHP applications for some ten years, but has now been superceded by newer systems.
In particular, libraries such as PDO and mysqli offer parameterisation, a feature
@@ -68,44 +68,106 @@
- I'll assume that your development environment is set up. Here's what you will need: + For your development environment, here's what you will need:
-
- For Windows and Mac users, you can get all of this with
- XAMPP. If you use Linux, you may have all of this
- already — although XAMPP works on Linux too, and adds a user-friendly control panel.
- If you get stuck, turn to your favourite search engine, and
- ask "How do I install [whatever] on [Windows]". In 99% of cases, your question has already
- been answered, so be persistent.
+ The first three things can be installed in one go, by installing
+ XAMPP — just download and run the appropriate
+ package for your operating system. Note that if you use Linux, PHP and Apache may be installed
+ already; there is often a ready-made web folder at /var/www from which you can run
+ your files. By all means use that, but if in doubt, XAMPP works on Linux too, and adds a
+ user-friendly control panel.
+
+ You'll also need a programmer's editor. There are many different ones available, and the one + that you choose will depend on your preference as well as what you find works best on your + system. I'll therefore mention several, in no particular order, so you can decide for yourself. + In most cases you can download the appropriate installer, double-click it from your Downloads + folder, and install as you would any software. +
+ ++ For convenience, I have supplied official download links for each product. If you like, you + can verify these links by exploring the appropriate website and looking for the 'Downloads' + section. +
+ + ++ First up is Brackets, which is available on all platforms. + Download here: +
+ + + ++ Next is Light Table: +
+ + + ++ If you are running Linux, have a look at Gedit, + which is pre-installed on the Ubuntu and Mint distributions (and probably others too). It does + syntax highlighting for PHP as standard.
-- You'll also need an editor to modify files. Most computers have a free text editor already - installed, but these are rarely good enough. Use whatever you like, but if you have no - preference, download and install NetBeans. +
+ If the above editors don't work on your system, or you don't get on with them, that's no + problem — there's a substantial + list of editors on Wikipedia.
- One of the other features of this tutorial is that it uses an absolute minimum of additional + One of the other features of this tutorial is that it uses no additional software to get it working. Software projects nearly always make use of libraries, each of which offer some particular pre-written functionality. These are well worth using in proper development, since they are better-tested by more people than an individual can usually achieve on their own. However, for a tutorial I think they can add integration complexity, and explaining the advanced-level code within is usually quite a distraction. Thus, I have eschewed - libraries more than I would normally, and suspect this will be corrected in a second (more - advanced) tutorial. -
-- I have made one exception for this rule, however, for reasons of password security. The login - system requires some cryptographic code, and this sort of thing is quite easy to get wrong. Whilst - this is just a tutorial, if it teaches readers to leave security to the experts (the - library-writers), it has served a valuable purpose. + libraries more than I would normally.
http://localhost/, you can assume I am referring to the root of your project, even
+ http://localhost/, you can assume I am referring to the root of your project, even
if your URL is somewhat different.
@@ -190,15 +245,30 @@
- Then, run this in your browser, by accessing the URL http://localhost/info.php.
+ Then, run this in your browser, by accessing the URL http://localhost/info.php.
You should see a PHP configuration page. If you do, then check you are running a version later
- than 5.4; if it is earlier than this then
+ than 5.5; if it is earlier than this then
you will need to upgrade. If you don't see this screen at all, then check that you have created
the file in the right folder.
+ It is also worth ensuring your PHP installation has the necessary database support. Double-check
+ this by searching the list of modules until you find the two modules highlighted in the
+ screenshot below: PDO and pdo_sqlite.
+ It may help to know that the modules are listed in alphabetical order:
+
+
+
- Once you have this working, and showing an acceptable version number, you can delete this file. + Once you have this showing an acceptable version number and the required modules, you + can delete this file.
Here's the first bit of code to add:
@@ -211,7 +281,7 @@http://localhost/index.php in your browser, to ensure that you can see the
+ http://localhost/index.php in your browser, to ensure that you can see the
initial mock-up of our blog's home page.
@@ -219,14 +289,15 @@ <body> and <h1>, each
- of which has an opening tag (without the slash) and a closing tag (with the slash).
+ is made up of tags such as <body> and
+ <h1>, each of which has an opening tag (without the slash)
+ and a closing tag (with the slash).
These
are nested to form a hierarchy in the same way as an ordinary letter is hierarchical: instead of
an envelope containing a document containing headings and paragraphs, we have an
- <html> containing a <body> containing
- <h1> and <p>. Easy peasy!
+ <html> containing a <body> containing
+ <h1> and <p>. Easy peasy!
@@ -234,17 +305,17 @@@@ -353,7 +424,7 @@What do the tags mean?
-
DOCTYPE: this tells the browser to expect a particular dialect of HTML +- -
DOCTYPE: this tells the browser to expect a particular dialect of HTML — in this case HTML5html: this is the "envelope" for the whole document. It acts as a container for +- -
html: this is the "envelope" for the whole document. It acts as a container for everything- -
head: this contains stuff about the document, such as the title- -
body: unsurprisingly, this contains the main part of the documenth1,h2,h3: title headings of decreasing +- +
head: this contains stuff about the document, such as the title- +
body: unsurprisingly, this contains the main part of the document- -
h1,h2,h3: title headings of decreasing importance- -
p: a paragraph of text- -
div: a "division", i.e. a section- +
a: a hyperlink- +
p: a paragraph of text- +
div: a "division", i.e. a sectiona: a hyperlinkWe'll use a few more as we go on, but those are the main ones.
@@ -275,18 +346,18 @@
Thus, if you wish, you can delete both article blocks and replace them with the new loop block - (containing the
for()through to theendfor). However, the way this was + (containing thefor()through to theendfor). However, the way this was actually written was as follows:@@ -302,11 +373,11 @@
- Delete the second article
-- Insert the start of the
-for()loop before the first article, on a new line- Insert the end of the loop (
endfor) at the end of the first article, on a new +- Insert the start of the
+for()loop before the first article, on a new line- Insert the end of the loop (
endfor) at the end of the first article, on a new line- Indent the contents of the loop (in most editors, you can do this by selecting the article markup and pressing the Tab key)
-- Finally, the two snippets of PHP to output the
$postIdare inserted into the +- Finally, the two snippets of PHP to output the
$postIdare inserted into the articleWhat are loops?
- The
for()andendforyou have seen form a loop block. This is + Thefor()andendforyou have seen form a loop block. This is a way of telling PHP that you want the enclosed code to be run a number of times. In this - case, it sets a value to 1, and carries on looping whilst that value is less than or greater - than 3. For each iteration of the loop, the value is incremented by 1, which is carried out by - the++operator. + case, it sets a value to 1, and carries on looping whilst that value is less than or equal + to 3. For each iteration of the loop, the value is incremented by 1, which is carried out by + the++operator.
Broadly, databases are stored in terms of tables, columns and rows. Each
different kind of data we wish to store needs its own table, but to start with, we only have one:
- a post. The CREATE TABLE statement specifies which properties we want a post to have,
+ a post. The CREATE TABLE statement specifies which properties we want a post to have,
namely:
- The column id has two particular features:
+ The column id has two particular features:
PRIMARY KEY in order to mark it as the predominant unique
+ PRIMARY KEY in order to mark it as the predominant unique
identifier for the row. This makes it suitable to refer to the row from another
table (which we will do later on)AUTOINCREMENT option, so unique values are generated for us
+ AUTOINCREMENT option, so unique values are generated for us
by the database server automatically
A blog post is therefore just a row added to the posts table, since each row has space to store
all of these values. Once the table is created, we also create some dummy article data, using
- INSERT. The format of this command, as used in the SQL script, is simply thus:
+ INSERT. The format of this command, as used in the SQL script, is simply thus:
-
+
INSERT INTO table_name (column_1, column_2, ...) VALUES (value_1, value_2, ...);
- The first half of the code is written in PHP. Here, we use a series of if()
- statements to ensure everything goes smoothly. The variable $error is set to an
+ The first half of the code is written in PHP. Here, we use a series of if()
+ statements to ensure everything goes smoothly. The variable $error is set to an
error message if something goes wrong. Here are the steps we take:
touch()
+ SQLite will work fine with just an empty file, so we create one with touch()
and report if there was any problems doing that (such as being disallowed by file system
permissions).
file_get_contents() and report an error
+ We then read the SQL script in using file_get_contents() and report an error
if the file cannot be found (this is unlikely, but it is good practice to check
everything that can go wrong!).
$pdo->exec() and report any issues
+ We then try running the SQL commands using $pdo->exec() and report any issues
with that.
- The second half of the file (from <!DOCTYPE html>) presents the results of
+ The second half of the file (from <!DOCTYPE html>) presents the results of
the script, in HTML.
So, if you have not already tried it, visit http://localhost/install.php in your
+
So, if you have not already tried it, visit http://localhost/install.php in your
browser, and make sure some posts are created. If you want to re-run it, delete the data.sqlite
file manually and refresh your browser page.
The first few lines determine the file path to the database, and then we create a new PDO object
- with new PDO(), which we can use to access the data. We then use the
- query() method to run a SQL statement that reads articles from the post
+ with new PDO(), which we can use to access the data. We then use the
+ query() method to run a SQL statement that reads articles from the post
table.
- The SELECT command is used to read from the database. For a single table, it takes a
+ The SELECT command is used to read from the database. For a single table, it takes a
format like this:
- The WHERE is optional, and is useful if we want to filter on some particular
+ The WHERE is optional, and is useful if we want to filter on some particular
feature of each row.
- So, refresh your browser's view of http://localhost/index.php, and ensure that your
+ So, refresh your browser's view of http://localhost/index.php, and ensure that your
new changes render the posts from the database without errors. I'll then explain what is happening.
After connecting to the database, the query is used to retrieve column values for each table row,
- returning ordering the rows in created_at DESC (i.e. in reverse creation order, or most
- recent first). It then enters a loop (in this case a while()) to render all the posts it
+ returning ordering the rows in created_at DESC (i.e. in reverse creation order, or most
+ recent first). It then enters a loop (in this case a while()) to render all the posts it
finds.
- We use $stmt->fetch() to read the next available row, until the point when there are no
+ We use $stmt->fetch() to read the next available row, until the point when there are no
rows left. When that happens, this method will return false and the loop will exit.
For every row, there are two observations worth making. Firstly, rows are returned in an array, so we
- access them using the array syntax such as $row['title']. Secondly, you'll see that
- where text strings are output, they are wrapped in the htmlspecialchars() function. The
+ access them using the array syntax such as $row['title']. Secondly, you'll see that
+ where text strings are output, they are wrapped in the htmlspecialchars() function. The
reason for this is that, if user input (a blog title or blog post in this case) contains angle brackets,
it could break the HTML used in the page layout, and worse, might let a user inject unauthorised
JavaScript that would be run on other people's computers.
+++ What are the htmlspecialchars() parameters? +
++ The first parameter is, of course, the item of text we wish to safely output. The other + two are: +
+ ++
+ +- +
ENT_HTML5: this tells PHP to understand tags as HTML5, rather than any + other dialect of HTML- +
'UTF-8': this specifies how characters are encoded. UTF-8 is + now the standard approach to encoding text because it supports a large number + of language-specific charactersIt's worth noting that while the default character set for this function is 'UTF-8' + (and so we don't really need to set it), the PHP manual advises that it should be + specified explicitly. +
+
You'll notice that most of the PHP code to deal with the database side of things is in the top half of the file, and the second half is predominantly HTML. This isn't an accident: this arrangement creates @@ -533,22 +627,22 @@
Now, it is our intention to link this page from individual posts on the home page. However, we've
not quite done that bit yet, so let's visit the new page manually, in order to test it. Open a
- new tab, paste the link http://localhost/view-post.php into the address
+ new tab, paste the link http://localhost/view-post.php into the address
bar, and check that a simple post appears.
- The connection to the database is the same as before, but we now have a WHERE clause
- in our SELECT statement, and we are we are now using prepare() and
- execute() to send the statement to the database driver.
+ The connection to the database is the same as before, but we now have a WHERE clause
+ in our SELECT statement, and we are now using prepare() and
+ execute() to send the statement to the database driver.
- Let's take the WHERE statement first. In the home page, we ran a query without
+ Let's take the WHERE statement first. In the home page, we ran a query without
this, because we wanted everything in the table. However it is more often the case that we
want to limit returned rows to those matching one or more conditions. This is where the
- WHERE comes in. For our new page, we only want rows that have a specific id (and
+ WHERE comes in. For our new page, we only want rows that have a specific id (and
since "id" is unique, we know we won't get more than one).
- The other change is swapping out the call to query() with two new methods.
- prepare() is used to set up the statement, and indicates with a colon where values
- should go (i.e. ":id"). The execute() statement then runs the query, swapping
+ The other change is swapping out the call to query() with two new methods.
+ prepare() is used to set up the statement, and indicates with a colon where values
+ should go (i.e. ":id"). The execute() statement then runs the query, swapping
place-holders with real values (in this case, the number 1). This technique is known as
parameterisation, and is a good way to inject user-supplied input in a secure manner
(the post ID is not yet user-supplied, but it soon will be).
@@ -579,7 +673,7 @@
require to pull it in.
+ require to pull it in.
@@ -607,7 +701,7 @@
We saw in the common.php library a set of code blocks that
start with the word
- function. These are named blocks of code that can be called from different
+ function. These are named blocks of code that can be called from different
places, and are an essential item in your toolkit. They help reduce duplication (since
you don't need to write code again) and they help improve modularity (since functions
can be stored in separate files).
@@ -617,11 +711,18 @@
getRootPath() gets the full directory
+ called. For example, the function getRootPath() gets the full directory
name of your web project on disk.
+
+ Since the htmlspecialchars() calls have a few parameters we don't want to have to
+ keep typing in, let's also move that to its own function:
+
Since this involves database changes, we'll need to delete our database file, and re-run
the installer. So, delete the file in data/data.sqlite and then
- reinstall by visiting http://localhost/install.php again.
+ reinstall by visiting http://localhost/install.php again.
@@ -671,7 +772,7 @@
Now we're ready to start adding some comment-related changes. To start with, let's add
- a count of comments on the front page. This uses the SQL command COUNT() to count
+ a count of comments on the front page. This uses the SQL command COUNT() to count
the number of rows that would be returned by a query:
addCommentToPost() for validation and saving.$errors array.addCommentToPost() for validation and saving.
+ $errors array.
When we visit a web site normally — such as the home page of our blog, say —
we are in GET mode. This is the state that is suitable for reading things from
- a web server. Some pages containing a <form> also operate in GET mode,
+ a web server. Some pages containing a <form> also operate in GET mode,
such as search forms, since these too are used to read information.
Forms that may result in data being saved, on the other hand, should use POST. You can
see this with the comment form we just installed; have a look for the
- method="post" declaration in the form tag.
+ method="post" declaration in the form tag.
This was one of the reasons behind the earlier change to the installer: before the @@ -866,7 +967,7 @@
- Adding a comment doesn't work yet, as the INSERT command is missing a value for
+ Adding a comment doesn't work yet, as the INSERT command is missing a value for
the date of comment creation. Let's fix that now:
htmlspecialchars(), which prevents any rendering problems if the
- user has used any HTML characters such as angle brackets.
+ the PHP function htmlspecialchars() via our custom function htmlEscape(),
+ which prevents any rendering problems if the user has used any HTML characters such as angle
+ brackets.
@@ -965,8 +1067,8 @@
There are two fields in the new user table that I added based on my experience rather than an
- immediate need. These are created_at, which holds the date and time when the user
- was first set up, and is_enabled, which allows us to turn users on and off. Most
+ immediate need. These are created_at, which holds the date and time when the user
+ was first set up, and is_enabled, which allows us to turn users on and off. Most
user systems will find a practical use for these simple features during their lifetime.
- So, let's make that improvement straight away. To do so, we'll need the library I talked about
- at the start of the tutorial, stored here in vendor/password_compat.
- The features it provides are so useful they have been built into PHP 5.5, so if you are running
- this version (or later) you can skip adding the new file and the associated
- require_once. However if you are running an earlier version, or if you are not
- sure which version you have, add all of these changes.
+ So, let's make that improvement straight away. Here are the changes:
- So, what does the new code do? It makes use of a new function called
- password_hash(), which takes a password as input and produces what is known as a
+ So, what does the new code do? It makes use of a PHP function called
+ password_hash(), which takes a password as input and produces what is known as a
hash. A hash is a mathematical calculation that is strictly one way, which means that
if sometimes steals our database of password hashes, they will find it very difficult indeed
to recreate the passwords they were generated from.
- Whether or not you are using it, by all means do have a read of the source code in - password.php. However, an in-depth exploration of it would be rather - advanced at this stage, so for our purposes we will assume it just works. -
-The next step is to add a login form, and a link from which to access it:
@@ -1033,8 +1116,8 @@
- If the password matches however, then we call login() to sign in the user, and
- then we redirect to the home page using redirectAndExit().
+ If the password matches however, then we call login() to sign in the user, and
+ then we redirect to the home page using redirectAndExit().
@@ -1057,8 +1140,8 @@@@ -1121,14 +1204,14 @@
PHP makes this nice and simple for developers: we just turn on sessions using -
session_start(), and then we can just read and write to the -$_SESSIONarray. Easy peasy! +session_start(), and then we can just read and write to the +$_SESSIONarray. Easy peasy!
-<div style="border: 1px solid #ff6666; padding: 6px;"> … </div>+<div style="border: 1px solid #ff6666; padding: 6px;"> … </div>
- The purpose of the style attribute to specify CSS rules (otherwise known as
- style rules) to the HTML within. Firstly, we have the border rule, which says
+ The purpose of the style attribute to specify CSS rules (otherwise known as
+ style rules) to the HTML within. Firstly, we have the border rule, which says
the content (an error message) should have a red border rendered in an unbroken line, and
- the padding means that there should be six pixels of gap between the border and
+ the padding means that there should be six pixels of gap between the border and
an invisible box around the content.
- That applies two rules to the block: one called error and another called
- box. That's much easier to remember, easier to read, and — since we
+ That applies two rules to the block: one called error and another called
+ box. That's much easier to remember, easier to read, and — since we
centralise the definitions in assets/main.css — easier
to change if we have to.
We now turn our attention to an improvement to the database. This consists of the tables
- post and user, for blog posts and authors respectively. When
- creating a post record, we insert our automatically generated user.id in
- post.user_id to store which user has authored it (of course, we only have one
+ post and user, for blog posts and authors respectively. When
+ creating a post record, we insert our automatically generated user.id in
+ post.user_id to store which user has authored it (of course, we only have one
user in our test data, but in practice we might have several).
- However, it is possible to insert any number in post.user_id, so if we have
+ However, it is possible to insert any number in post.user_id, so if we have
an undiscovered bug in our code, it might cause a non-existent user primary key to be stored
here. Since we rely on values here always pointing to a user row, if a bad value were to get
in, it might crash our application.
@@ -1193,7 +1276,7 @@
PRAGMA command.
+ be turned on explicitly, using the PRAGMA command.
@@ -1202,7 +1285,7 @@
user table does not result
+ Deleting the user table does not result
in a constraint violation, since we refuse to run the SQL unless the database file is
manually deleted.
INSERT that in the script with a dummy hash value, and
- UPDATE it afterwards with the real hash generated in PHPINSERT that in the script with a dummy hash value, and
+ UPDATE it afterwards with the real hash generated in PHP
+ The file you've just added is used to set options for the web server, and in this case
+ for a module known as mod_rewrite. This module allows the creation of special rules
+ for various web addresses, and in our case to refuse to serve ones that are private.
+ I'll provide a little information about these rules below, but if you don't fully follow them,
+ don't worry. They are rather out of scope for a beginners' course, and we won't need to
+ consider them again.
+
++ __NEWFILE__+ What do the mod_rewrite commands do? +
+ ++ The first command is simple:
+RewriteEngine onenables the module for the site. + After that, theRewriteCondsets up a condition (i.e. when the rule will + apply) and theRewriteRuledoes the action. ++ The condition is looking at a placeholder known as
+%{REQUEST_URI}. This is the + URL of the page, relative to the domain. So it will always start with + / and then it will contain the address, e.g. + /styles/assets/main.css (our stylesheet) or + /install.php (a PHP page). However there are some resources + we want users specifically not to access directly. ++ These ones are in the data, lib, + templates and vendor folders, so + we add those into the second part of this command. The bar character means "or" and the +
+^means "begins with". This format is known as a + regular expression, and is + a popular and flexible method of testing strings for certain conditions. ++ The
+RewriteRulebasically says if you get a match, then this is the + (L) last rule (i.e. don't process any more rules) and the server should + (R) redirect to a 404 page (a special server code for "not found"). +
Let's turn our attention to another major feature: writing a new article. We start off with
setting up a new page, laying out the necessary form input controls, and adding a new logged-in
- menu item. There's also a change here to add a generic class user-form, so our
+ menu item. There's also a change here to add a generic class user-form, so our
forms across the whole application can easily acquire a common look-and-feel; see how I've
gone back to existing form comment-form.php to update that too.
- While we are here, let's take a look at how redirectAndExit() works; this is in
+ While we are here, let's take a look at how redirectAndExit() works; this is in
common.php. It starts by reading the domain we are running
- on (such as localhost), since it is good not to hardwire this into our code.
- It then sends a Location HTTP header to the browser, which will cause it to
+ on (such as localhost), since it is good not to hardwire this into our code.
+ It then sends a Location HTTP header to the browser, which will cause it to
request the specified address. Since the PHP script would happily carry on running at this
point (until it has detected that the browser has disconnected) we also need to forcibly
exit and wait for the redirect.
@@ -1298,9 +1423,9 @@
User-Agent header)Host)
- Cookie)
+ (the User-Agent header)Host)
+ Cookie)
@@ -1308,9 +1433,9 @@
Server)Server)Content-Type)Content-Type)
In a similar way to saving comments, we first test if we are in a post operation, using
- if ($_POST). This contains an array of input values that will be only be present
+ if ($_POST). This contains an array of input values that will be only be present
if a user has submitted the form.
That was nice and easy: if we find we are not in a post operation and we have a
post primary key and that row exists, then show it in the edit form. You can see that
- now we need the database connection (in $pdo) for two things, I've moved
+ now we need the database connection (in $pdo) for two things, I've moved
that line so that it is always executed.
- As we have done before, this code uses htmlspecialchars() to prevent users
+ As we have done before, this code uses our htmlEscape() function to prevent users
from entering HTML, which could break our page layout or introduce security problems. It is
perhaps less of a worry here, since at least these users are authenticated, and hence
they might be considered more trustworthy than anonymous commenters.
@@ -1374,16 +1499,16 @@
$_GET['post_id'] will be available, and we can look up that row
+ In both cases, $_GET['post_id'] will be available, and we can look up that row
from the post table, and obtain title/body data if it is read successfully.
- Within a POST operation, we can then check $postId, and if it has a value we
+ Within a POST operation, we can then check $postId, and if it has a value we
know we are editing an existing article rather than creating a new one. Thus, if we are editing,
- we call the new function editPost(), which will run the necessary
- UPDATE command against the database, rather than addPost(), which
- would run an INSERT.
+ we call the new function editPost(), which will run the necessary
+ UPDATE command against the database, rather than addPost(), which
+ would run an INSERT.
@@ -1426,7 +1551,7 @@
- You may have noticed that the class name (read-more) of the wrapping div was no
+ You may have noticed that the class name (read-more) of the wrapping div was no
longer accurate, which is why I changed this to something more generic. As it happens, this
section doesn't have any CSS rules specifically attached to it, but it's good to have something
for future styles to hook onto).
@@ -1485,7 +1610,7 @@
This is pretty simple. If the button is pressed, the form is submitted, and the submitted
- key save gets the value of the button, "Save".
+ key save gets the value of the button, "Save".
@@ -1513,7 +1638,7 @@
- This helps up cheat by doing some of our parsing for us. Here's what happens in that form + This helps us cheat by doing some of our parsing for us. Here's what happens in that form if we press the second Delete button:
@@ -1529,7 +1654,7 @@
This makes things nice and easy: we just look up the name of the element
- (i.e. delete), and then grab the first key value.
+ (i.e. delete), and then grab the first key value.
- While working on the home page, I noticed I'd used the variable name $row. This
+ While working on the home page, I noticed I'd used the variable name $row. This
is rather generic — most things read from a database are rows — so I swapped to
a better name for it. This is more readable, and fits in with the common naming convention
of using a plural name for an array and the corresponding singular name for each item.
@@ -1603,9 +1728,9 @@
post_id) upon which a comment is being added or
+ post_id) upon which a comment is being added or
deletedadd-comment and delete-comment), which
+ add-comment and delete-comment), which
can be read by our code to determine what the user wishes to do
We now need to read the action key we set up, so we can decide what feature to call.
- If the user is deleting a comment, the new function deleteComment in the next set
- of changes is called; although we could just delete by comment.id, to be sure of
- deleting the right thing we filter additionally by comment.post_id.
+ If the user is deleting a comment, the new function deleteComment in the next set
+ of changes is called; although we could just delete by comment.id, to be sure of
+ deleting the right thing we filter additionally by comment.post_id.
- Note that we also add an if() clause to ensure that the delete operation is skipped if
+ Note that we also add an if() clause to ensure that the delete operation is skipped if
the user is not logged in. Although we do not render delete buttons for anonymous users, it is
still possible for security crackers to fake a form submission that contains a valid deletion
request — this clause prevents that.
@@ -1642,7 +1767,7 @@
Finally, I noticed that the logic to add a comment is contained within a function, but the
- logic to delete one is still in the switch() block in
+ logic to delete one is still in the switch() block in
view-post.php. The following change just tidies that up:
comment_count at the end of it.
+ comment_count at the end of it.
- This sub-query will count rows in the comment table, based on which post they
+ This sub-query will count rows in the comment table, based on which post they
belong to. Since the job of the outer query is to list posts, we can filter the comment count
- for each row by making a comparison to the outer table, post.
+ for each row by making a comparison to the outer table, post.
@@ -1736,13 +1861,13 @@
Whilst we are looking at the database side of things, I noticed that the
- user.is_enabled field hasn't yet been used. This was intended mainly for future
+ user.is_enabled field hasn't yet been used. This was intended mainly for future
enhancements (in particular for a user administration page), but the application can make an
initial use of it immediately, by disallowing authenticated features to non-enabled users.
- The installer sets the test user to is_enabled=1 already, so all we need
+ The installer sets the test user to is_enabled=1 already, so all we need
to do is to adjust the SQL statements that fetch data from the user table.