From a9db3ff3dcf3520502112e086578798da568acd5 Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Sat, 4 Oct 2014 12:18:46 +0100 Subject: [PATCH 01/19] First cut at refactoring htmlspecialchars() --- pages/original.html | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pages/original.html b/pages/original.html index 4877928..d731f35 100644 --- a/pages/original.html +++ b/pages/original.html @@ -506,6 +506,12 @@

rows left. When that happens, this method will return false and the loop will exit.

+ +
+ Add an explanation of the parameters to the HTML escaping function. +
+ +

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 @@ -622,6 +628,13 @@

+

+ 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: +

+ + + __NEWFILE__

Adding more features

@@ -878,6 +891,13 @@

a form does not by default contain values, so we have to add them manually.

+ +
+ Should I change the reference to htmlspecialchars() now this has been + placed in a function? +
+ +

Thus, we now set empty values for the GET case (no form submission) in view-post.php as well as @@ -1356,6 +1376,13 @@

+ +
+ Again, maybe change the reference to htmlspecialchars() now this has been + placed in a function? +
+ +

As we have done before, this code uses htmlspecialchars() to prevent users from entering HTML, which could break our page layout or introduce security problems. It is From d1aff2de4607f6fba91091dab63d233be4b5367b Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Sat, 4 Oct 2014 13:57:49 +0100 Subject: [PATCH 02/19] Add missing anchor points again --- pages/original.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/original.html b/pages/original.html index d731f35..2ed1faa 100644 --- a/pages/original.html +++ b/pages/original.html @@ -628,7 +628,7 @@

-

+

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:

From aa37a6006ce9f10df87d5d55a970cd6de7aa8e62 Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Sun, 5 Oct 2014 10:47:38 +0100 Subject: [PATCH 03/19] Add sidebar explaining the htmlspecialchars() params --- pages/original.html | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/pages/original.html b/pages/original.html index 2ed1faa..12d6608 100644 --- a/pages/original.html +++ b/pages/original.html @@ -506,12 +506,6 @@

rows left. When that happens, this method will return false and the loop will exit.

- -
- Add an explanation of the parameters to the HTML escaping function. -
- -

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 @@ -521,6 +515,29 @@

JavaScript that would be run on other people's computers.

+ +

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 From c3023e38a7225ce63f5dd118bb4decbb45990100 Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Sun, 5 Oct 2014 10:50:38 +0100 Subject: [PATCH 04/19] More tweaks resulting from HTML escaping changes --- pages/original.html | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/pages/original.html b/pages/original.html index 12d6608..4778408 100644 --- a/pages/original.html +++ b/pages/original.html @@ -908,19 +908,13 @@

a form does not by default contain values, so we have to add them manually.

- -
- Should I change the reference to htmlspecialchars() now this has been - placed in a function? -
- -

Thus, we now set empty values for the GET case (no form submission) in view-post.php as well as the already existing POST case. Where we output the user-supplied data, we pass it through - the PHP function 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.

@@ -1393,15 +1387,8 @@

- -
- Again, maybe change the reference to htmlspecialchars() now this has been - placed in a function? -
- -

- 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. From ac00901d4330104dbc09432396d1508d03ffa19e Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Tue, 28 Oct 2014 16:34:34 +0000 Subject: [PATCH 05/19] Fix an incorrect description of the <= comparison --- pages/original.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/original.html b/pages/original.html index 4778408..94ad379 100644 --- a/pages/original.html +++ b/pages/original.html @@ -304,8 +304,8 @@

The for() and endfor you 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 + 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.

From 9e2c4e8e21d7132ab16eec37610daeb04d53185d Mon Sep 17 00:00:00 2001 From: gregwittman Date: Mon, 10 Nov 2014 14:51:35 -0500 Subject: [PATCH 06/19] Typo --- pages/original.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/original.html b/pages/original.html index 94ad379..c8364fd 100644 --- a/pages/original.html +++ b/pages/original.html @@ -563,7 +563,7 @@

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 + in our SELECT statement, and we are now using prepare() and execute() to send the statement to the database driver.

From 14f7c1c059e9905ff4d2c4110590045103a587dd Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Mon, 24 Nov 2014 20:43:41 +0000 Subject: [PATCH 07/19] Improve/expand the notes on environment installation --- pages/original.html | 121 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 22 deletions(-) diff --git a/pages/original.html b/pages/original.html index c8364fd..74d7eb4 100644 --- a/pages/original.html +++ b/pages/original.html @@ -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:

-
    -
  • Web server, usually Apache
  • +
      +
    1. The Apache web server
    2. PHP, any version after 5.4
    3. -
    4. SQLite PHP module
    5. -
+
  • SQLite PHP module, for database access
  • +
  • Any programmer's editor, ideally with syntax highlighting
  • +
    I've swapped out references to MAMP and WAMP, as MAMP is quite restricted in the free - version (only one vhost) and it's nice to have one product for all systems. + version (only one vhost) and it's nice to have one product for all systems. Also, I'd + have liked to use PHP's built-in server, but htaccess is required!

    - 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. +

    + + +
    + Have had problems with both Light Table and Brackets on Ubuntu (both unusable out of the + box) but that's probably just Unity bugginess. The default Brackets installer bundles + proprietary Adobe software, but the direct download links avoid this. +
    + + +

    + 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.

    -
    - It might be nice to recommend something lightweight too, in addition to NB. - Brackets and - Light Table are both free, in frequent development - and x-platform - add one of these? +
    + Also considered Atom from GitHub, but it tracks users via Google Analytics.
    -

    - 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.

    ~
    @@ -197,8 +259,23 @@

    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: +

    + +

    + Screenshot of PHP info output +

    +

    - 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:

    From 42951f8aed258c5cf6032f1b5e8a326df7a805d9 Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Tue, 25 Nov 2014 01:28:07 +0000 Subject: [PATCH 08/19] Remove file no longer required --- project-branch | 1 - 1 file changed, 1 deletion(-) delete mode 100644 project-branch diff --git a/project-branch b/project-branch deleted file mode 100644 index 5eb0010..0000000 --- a/project-branch +++ /dev/null @@ -1 +0,0 @@ -rebase1 From 73850b472bce305f4450e83f5d8a4a9e1fa763b6 Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Thu, 4 Dec 2014 10:31:56 +0000 Subject: [PATCH 09/19] Update README now we're out of alpha --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index adc69c8..7f58ff5 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). From 2a9e95229d800e1f80766a775537712a23a0ec3f Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Thu, 4 Dec 2014 10:34:44 +0000 Subject: [PATCH 10/19] Fix mistake in URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f58ff5..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](http://ilovephp.jondh.me,uk) 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 From 779ae18bea2bf3f3aac8ba9a5bf3d706634aad39 Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Sat, 6 Dec 2014 21:15:16 +0000 Subject: [PATCH 11/19] Add skeleton code for explanation of mod_rewrite rules --- pages/original.html | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/pages/original.html b/pages/original.html index 74d7eb4..cb34b77 100644 --- a/pages/original.html +++ b/pages/original.html @@ -1350,6 +1350,48 @@

    Tidy up

    +

    + 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__

    New post creation

    From eef99f991cf45cb36d12a69ed313052f90ea3a39 Mon Sep 17 00:00:00 2001 From: michaellenahan Date: Sun, 11 Dec 2016 15:07:23 +0100 Subject: [PATCH 12/19] Add link to php.net in introduction See: https://github.com/halfer/php-tutorial-text/issues/20 --- pages/original.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/original.html b/pages/original.html index cb34b77..8527148 100644 --- a/pages/original.html +++ b/pages/original.html @@ -13,7 +13,7 @@

    Introduction

    if these terms don't mean anything yet, that's okay. I'm assuming readers are familiar with how to use their computer, but perhaps have not programmed before. That said, even a beginner's tutorial can get non-trivial quickly, so if you are brand new to programming, you - may wish to research unfamiliar keywords (usually on the PHP website). + may wish to research unfamiliar keywords (usually on the PHP website).

    From 2107c26bf8f7fdba23f48aa8f46f94bb05d56ff1 Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Fri, 12 May 2017 10:55:20 +0100 Subject: [PATCH 13/19] Add classes to inline code statements --- pages/original.html | 285 ++++++++++++++++++++++---------------------- 1 file changed, 143 insertions(+), 142 deletions(-) diff --git a/pages/original.html b/pages/original.html index 8527148..5e47af5 100644 --- a/pages/original.html +++ b/pages/original.html @@ -53,7 +53,7 @@

    Internet that make two fundamental errors.

    - 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 @@ -90,7 +90,7 @@

    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 + 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.

    @@ -240,7 +240,7 @@

    editor, and that you can create folders as required. This might be in the root of your "htdocs" or "www" folder, depending on what you used to install Apache. You can use a subfolder of "blog" if you wish, or for more advanced users, feel free to set up a new vhost. So, when I use - 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.

    @@ -252,7 +252,7 @@

    - 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 you will need to upgrade. If you don't see this screen at all, then check that you have created @@ -262,8 +262,8 @@

    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: + screenshot below: PDO and pdo_sqlite. + It may help to know that the modules are listed in alphabetical order:

    @@ -288,7 +288,7 @@

    This is the initial file, so all we have to do is copy and paste it in. To do so, create index.php in the root of your project folder, open it in your editor, and ensure it is empty before pasting in the code. Then visit - 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.

    @@ -296,14 +296,15 @@

    If you can, then your first piece of coding is done! The language is HTML, which is understood by your web browser as a way to describe documents on the web. HTML is not actually a programming language: it is properly referred to as a markup language. The markup itself - 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). + 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!

    @@ -430,7 +431,7 @@

    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:

    @@ -444,25 +445,25 @@

    - The column id has two particular features: + The column id has two particular features:

      -
    • We declare it as a PRIMARY KEY in order to mark it as the predominant unique +
    • We declare it as a 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)
    • -
    • We specify the AUTOINCREMENT option, so unique values are generated for us +
    • We specify the 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, ...);

    @@ -482,8 +483,8 @@

    - 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:

    @@ -493,17 +494,17 @@

    accidentally overwrite data.
  • - SQLite will work fine with just an empty file, so we create one with 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).
  • - We then read the SQL script in using 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!).
  • - We then try running the SQL commands using $pdo->exec() and report any issues + We then try running the SQL commands using $pdo->exec() and report any issues with that.
  • @@ -512,10 +513,10 @@

    - 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.

    @@ -535,8 +536,8 @@

    Using real data

    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.

    @@ -545,11 +546,11 @@

    What is a SELECT statement?

    - 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:

    -
    SELECT
    +	
    SELECT
     	(column names in a list)
     FROM
     	(table)
    @@ -559,7 +560,7 @@ 

    (column names to sort on)

    - 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.

    @@ -567,26 +568,26 @@

    - 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. @@ -602,9 +603,9 @@

      -
    • ENT_HTML5: this tells PHP to understand tags as HTML5, rather than any +
    • 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 +
    • '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 characters
    @@ -633,22 +634,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 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).

    @@ -658,9 +659,9 @@

    - 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). @@ -679,7 +680,7 @@

    files shows that our blog title and synopsis is repeated in both files. That's not good, since if these things need to be amended, we need to change them more than once. Happily, the solution is easy: we create a PHP file for the duplicated snippet, and then use - require to pull it in. + require to pull it in.

    @@ -707,7 +708,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). @@ -717,13 +718,13 @@

    The functions we've seen here all provide a return value, which is the result of the function, and it can be of any type: a number, a string, whatever. This can either be printed out to the screen, or more frequently, stored in a variable at the place it is - called. For example, the function 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 + 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:

    @@ -765,7 +766,7 @@

    Adding more features

    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.

    @@ -778,7 +779,7 @@

    Adding more features

    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:

    @@ -917,8 +918,8 @@

    Commenting form

  • Detect if we are in a POST operation. If we are not (i.e. the page is just being rendered normally, rather than submitting a form) then don't do any more of this new stuff.
  • Get the author name, website URL and comment message, and pass them to new - function addCommentToPost() for validation and saving.
  • -
  • This will return any resulting errors into the $errors array.
  • + function addCommentToPost() for validation and saving. +
  • This will return any resulting errors into the $errors array.
  • If the comment-adding function successfully saves a comment to the database, redirect to self and exit. This will request the page again in GET mode.
  • If the comment save failed (perhaps because a mandatory field was not supplied) then @@ -949,13 +950,13 @@

    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 @@ -973,7 +974,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:

    @@ -989,7 +990,7 @@

    Thus, we now set empty values for the GET case (no form submission) in view-post.php as well as the already existing POST case. Where we output the user-supplied data, we pass it through - the PHP function htmlspecialchars() via our custom function htmlEscape(), + 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.

    @@ -1073,8 +1074,8 @@

    Adding a login system

    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.

    @@ -1100,7 +1101,7 @@

    Adding a login system

    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 + 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.

    @@ -1108,7 +1109,7 @@

    Adding a login system

    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 + 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. @@ -1141,8 +1142,8 @@

    Adding a login system

    - 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().

    @@ -1229,14 +1230,14 @@

    Tidy up

    - <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.

    @@ -1247,12 +1248,12 @@

    Tidy up

    - <div class="error box"> … </div> + <div class="error box"> … </div>

    - 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.

    @@ -1283,14 +1284,14 @@

    Tidy up

    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. @@ -1301,7 +1302,7 @@

    Tidy up

    constraints. These allow us to specify that values inserted into a particular column must exist in another column belonging to another table, and that an error must occur if this condition is not met. SQLite offers this feature, although it is unusual in that it needs to - be turned on explicitly, using the PRAGMA command. + be turned on explicitly, using the PRAGMA command.

    @@ -1310,7 +1311,7 @@

    Tidy up

    - Deleting the 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.
    @@ -1328,8 +1329,8 @@

    Tidy up

    order around, so the test user is created before the test post
  • We now need the test user to be created immediately in order to satisfy the foreign key - constraint, so we INSERT that in the script with a dummy hash value, and - UPDATE it afterwards with the real hash generated in PHP
  • + constraint, so we INSERT that in the script with a dummy hash value, and + UPDATE it afterwards with the real hash generated in PHP
  • Now when we make database connections in common.php, the first thing we do is to switch on foreign key constraints
  • @@ -1352,7 +1353,7 @@

    Tidy up

    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 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 @@ -1365,12 +1366,12 @@

    - The first command is simple: RewriteEngine on enables the module for the site. - After that, the RewriteCond sets up a condition (i.e. when the rule will - apply) and the RewriteRule does the action. + The first command is simple: RewriteEngine on enables the module for the site. + After that, the RewriteCond sets up a condition (i.e. when the rule will + apply) and the RewriteRule does the action.

    - The condition is looking at a placeholder known as %{REQUEST_URI}. This is the + 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 @@ -1381,14 +1382,14 @@

    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 + ^ 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 RewriteRule basically 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"). + The RewriteRule basically 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").

    @@ -1399,7 +1400,7 @@

    New post creation

    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.

    @@ -1415,10 +1416,10 @@

    New post creation

    - 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. @@ -1448,9 +1449,9 @@

    • Some information about the browser and operating system in use - (the User-Agent header)
    • -
    • Which website we wish to view (Host) -
    • The cookies previously served by the domain (Cookie) + (the User-Agent header)
    • +
    • Which website we wish to view (Host) +
    • The cookies previously served by the domain (Cookie)

    @@ -1458,9 +1459,9 @@

      -
    • Some information about the server environment in use (Server)
    • +
    • Some information about the server environment in use (Server)
    • The type of the content; web pages for example are served as "text/html" - (Content-Type)
    • + (Content-Type)

    @@ -1473,7 +1474,7 @@

    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.

    @@ -1495,7 +1496,7 @@

    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.

    @@ -1507,7 +1508,7 @@

    - As we have done before, this code uses our htmlEscape() function 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. @@ -1524,16 +1525,16 @@

    I've moved the code to read the current post towards the start of the page, since this is now useful in two situations. The first is when displaying an article for editing, and the second is when submitting the edit form to save any changes. - In both cases, $_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.

    @@ -1576,7 +1577,7 @@

    Post editing

    - 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). @@ -1630,12 +1631,12 @@

    -
    <input type="submit" name="save" value="Save" />
    +
    <input type="submit" name="save" value="Save" />

    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".

    @@ -1645,7 +1646,7 @@

    -
    <input type="submit" name="delete_1" value="Delete" />
    +
    <input type="submit" name="delete_1" value="Delete" />
     <input type="submit" name="delete_2" value="Delete" />
     <input type="submit" name="delete_3" value="Delete" />
    @@ -1657,7 +1658,7 @@

    -
    <input type="submit" name="delete[1]" value="Delete" />
    +
    <input type="submit" name="delete[1]" value="Delete" />
     <input type="submit" name="delete[2]" value="Delete" />
     <input type="submit" name="delete[3]" value="Delete" />
    @@ -1668,7 +1669,7 @@

    -
    Array
    +
    Array
     (
         [delete] => Array
             (
    @@ -1679,7 +1680,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.

    @@ -1692,7 +1693,7 @@

    - 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. @@ -1753,9 +1754,9 @@

    Comment admin

      -
    • The primary key of the post (post_id) upon which a comment is being added or +
    • The primary key of the post (post_id) upon which a comment is being added or deleted
    • -
    • A name for the action (add-comment and delete-comment), which +
    • A name for the action (add-comment and delete-comment), which can be read by our code to determine what the user wishes to do
    @@ -1776,13 +1777,13 @@

    Comment admin

    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. @@ -1792,7 +1793,7 @@

    Comment admin

    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:

    @@ -1826,7 +1827,7 @@

    Adding some polish

    -
    SELECT
    +
    SELECT
     	id, title, created_at, body,
     	(SELECT COUNT(*) FROM comment WHERE comment.post_id = post.id) comment_count
     FROM
    @@ -1839,13 +1840,13 @@ 

    Adding some polish

    The new part of this is the bracketed expression on the third line. This sort of SQL is known as a sub-query since it is a query contained inside another one. The result of it can be read like a real column, which we explicitly name by adding - 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.

    @@ -1886,13 +1887,13 @@

    Adding some polish

    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.

    From 1797f5688576ce7a08020e96e67b8ed72b83f544 Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Fri, 12 May 2017 13:13:00 +0100 Subject: [PATCH 14/19] Oops, fix non-inline code blocks --- pages/original.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pages/original.html b/pages/original.html index 5e47af5..2232e6e 100644 --- a/pages/original.html +++ b/pages/original.html @@ -546,7 +546,7 @@

    What is a SELECT statement?

    - 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:

    @@ -1248,7 +1248,7 @@

    Tidy up

    - <div class="error box"> … </div> + <div class="error box"> … </div>

    @@ -1631,7 +1631,7 @@

    -
    <input type="submit" name="save" value="Save" />
    +
    <input type="submit" name="save" value="Save" />

    @@ -1646,7 +1646,7 @@

    -
    <input type="submit" name="delete_1" value="Delete" />
    +
    <input type="submit" name="delete_1" value="Delete" />
     <input type="submit" name="delete_2" value="Delete" />
     <input type="submit" name="delete_3" value="Delete" />
    @@ -1658,7 +1658,7 @@

    -
    <input type="submit" name="delete[1]" value="Delete" />
    +
    <input type="submit" name="delete[1]" value="Delete" />
     <input type="submit" name="delete[2]" value="Delete" />
     <input type="submit" name="delete[3]" value="Delete" />
    @@ -1669,7 +1669,7 @@

    -
    Array
    +
    Array
     (
         [delete] => Array
             (
    
    From 21f879e7e7e4b5f13afd9d982ae4fd87f9eda4c2 Mon Sep 17 00:00:00 2001
    From: Jon Hinks 
    Date: Fri, 12 May 2017 14:49:04 +0100
    Subject: [PATCH 15/19] Fix new inline class error
    
    ---
     pages/original.html | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/pages/original.html b/pages/original.html
    index 2232e6e..1076182 100644
    --- a/pages/original.html
    +++ b/pages/original.html
    @@ -546,11 +546,11 @@ 

    What is a SELECT statement?

    - 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:

    -
    SELECT
    +	
    SELECT
     	(column names in a list)
     FROM
     	(table)
    
    From 800060a24c66d5cf67258f4cd8bb2453d767acd1 Mon Sep 17 00:00:00 2001
    From: Jon Hinks 
    Date: Fri, 12 May 2017 19:57:00 +0100
    Subject: [PATCH 16/19] Another non-inline tweak
    
    ---
     pages/original.html | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/pages/original.html b/pages/original.html
    index 1076182..2864fcb 100644
    --- a/pages/original.html
    +++ b/pages/original.html
    @@ -1827,7 +1827,7 @@ 

    Adding some polish

    -
    SELECT
    +
    SELECT
     	id, title, created_at, body,
     	(SELECT COUNT(*) FROM comment WHERE comment.post_id = post.id) comment_count
     FROM
    
    From dc19bc8d35320502cb96b31f3a1169c88769e367 Mon Sep 17 00:00:00 2001
    From: Jon Hinks 
    Date: Thu, 16 Aug 2018 19:06:46 +0100
    Subject: [PATCH 17/19] Remove references to libraries (password_compat now
     gone)
    
    ---
     pages/original.html | 36 +++++-------------------------------
     1 file changed, 5 insertions(+), 31 deletions(-)
    
    diff --git a/pages/original.html b/pages/original.html
    index 2864fcb..04ad51e 100644
    --- a/pages/original.html
    +++ b/pages/original.html
    @@ -73,7 +73,7 @@ 

    1. The Apache web server
    2. -
    3. PHP, any version after 5.4
    4. +
    5. PHP, any version after 5.5
    6. SQLite PHP module, for database access
    7. Any programmer's editor, ideally with syntax highlighting
    @@ -217,20 +217,13 @@

    - 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.

    ~
    @@ -254,7 +247,7 @@

    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.

    @@ -1088,21 +1081,8 @@

    Adding a login system

    form of protection should a cracker get through our security and steal our database.

    - -
    - I was originally of the view that the library should be loaded only if the PHP version - is <5.5, but this feels a bit fussy. I think the note here to say that users - on 5.5+ can omit these changes if they wish is a cleaner. -
    - -

    - 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:

    @@ -1115,12 +1095,6 @@

    Adding a login system

    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:

    From 33d5b428a6a2519b2da8cc26fc164882aeb1c7d8 Mon Sep 17 00:00:00 2001 From: Jon Hinks Date: Sat, 18 Aug 2018 17:13:49 +0100 Subject: [PATCH 18/19] This func is not so new any more... --- pages/original.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/original.html b/pages/original.html index 04ad51e..88f1896 100644 --- a/pages/original.html +++ b/pages/original.html @@ -1088,7 +1088,7 @@

    Adding a login system

    - So, what does the new code do? It makes use of a new function called + 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 From fd23a1e129513617d525452b213c80fc19b94da6 Mon Sep 17 00:00:00 2001 From: euclip <45699492+euclip@users.noreply.github.com> Date: Sat, 2 Mar 2019 19:58:42 +0200 Subject: [PATCH 19/19] Fix minor typo in note about brackets in forms --- pages/original.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/original.html b/pages/original.html index 88f1896..dcde376 100644 --- a/pages/original.html +++ b/pages/original.html @@ -1638,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: