Thoughts on website ideas, PHP and other tech topics, plus going car-free
Online PHP beginners’ tutorial
Categories: Finished, Ideas

I’ve been working since Christmas last year on an online PHP tutorial for beginners, which is now pretty much ready to try out. The working title during development has been “I ♥ PHP” and I still rather like that, so I’ll stick with that at least for the alpha period.

The course is split over 12 pages, and it walks readers interactively through building a simple blogging application using PHP and SQLite. Code changes are made as a real application would be built, using graphical diffs to show what code change to make at each stage. I’ve not seen this approach in tutorials before, so it will be interesting to see if people find that helpful within their learning process. This tutorial will always be available without charge and at this stage sign-up is not expected to be mandatory.

Presently the tutorial is password-protected, and thus is invite-only, but I’ll open it out soon, all being well. If you have access to the site, please do offer feedback in the comments here. If you believe you’ve spotted a mistake, or if there’s something that could be done more intuitively, please do let me know.

Password-protected tutorial preview site


5th Oct 2014: the site is now live!

Live tutorial site

16th Aug 2018: I’ve added a support forum on Reddit. Please add questions/bugs/feedback there, since I’m more likely to see them than comments here.

Support forum

Immediate priorities

  • Get working satisfactorily on all modern desktop browsers (IE11/Win8.1, FF/Ubuntu and Chromium/Ubuntu are looking OK so far)
  • Code improvements
    • Remove any errors
    • Ensure everything is secure
    • Do the tutorial myself 🙂
  • Improve and add to the text so that things are explained sufficiently for a beginner audience
    • There’s no instructions on how to set up a web-server, is this necessary? It is covered very well elsewhere, but beginners may find a self-contained course helpful
    • Would it be easier to recommend Vagrant with the default image instead?
  • Write introduction page
  • Make the written material available for pull requests on Github
    • Example project
    • Tutorial text

Medium-term tasks

  • Satisfactory tablet rendering (iPad Retina is perfectly usable)
  • Comment anywhere (drag-and-drop comments plus reply system)

General support

I’m currently looking at support and interaction tools, as of October 2014, but they’re probably some way off. For the time being, reach out to me on Twitter (@ilovephp) or post a comment below. If you have thoughts about what sort of support would work well, do let me know, but ultimately using blog comments here isn’t going to scale very well. I think Stack Overflow PHP chat is one good option, or a forum such as Reddit’s /r/PHPhelp. It’s worth mentioning that any questions related to this course will probably be too broad or too localised for the main Stack Overflow site.

Changes from the community

The blog code and course text are held in separate VCS repositories. This is to facilitate the generation of the diffs directly from a real Git repo rather than storing them statically. This means that if a mistake is found, it can be fixed in a new commit, rebased into the correct position, any conflicts are resolved, and then the diffs are regenerated.

The blog developed in this tutorial is now available for pull requests – I have added a README on how code changes are best made. Here is the repository for the chapter texts, again with instructions.

The site will always be available free of charge. To offset the production time and hosting costs, I am considering adding a small and unobtrusive advert, similar to the Stack Overflow’s approach. That would preclude making the site itself F/OSS. Since I will be choosy about an ad provider (no Google Ads if I can help it!) I plan to ramp up some traffic before I make enquiries.

Open source contributions

The diff renderer was written especially for this project, and is now open source. I did look for an existing one, similar to the GitHub one, but didn’t find anything I liked.

I’ve written a PHP Git wrapper to export a repo at a particular commit, to get metadata for a commit, and to get the changed files for a commit. I’ve got a bit of code to do a partial repo clone too, which I think will be very useful (this creates a new repo based on the full history of a specific path in a source repo). The code’s not great so it’s not public yet, but hopefully it will be soon. Ping me if you want a peek at it.


  • 9th Sep 2014 – some downloadable files were inaccessible, now fixed
  • 12th Sep 2014 – I’ve a fair bit of Git refactoring work to do, still beavering away!
  • 14th Sep 2014 – finished a rough version of a PHP script to extract a partial clone of a Git repo
  • 17th Sep 2014 – the chapter text has been extracted into its own repo and published to GitHub
  • 1st Oct 2014 – improved the existing build tools a bit
  • 2nd Oct 2014 – added htmlspecialchars() improvements as suggested in the comments (not yet live)
  • 3rd Oct 2014 – page build process now takes text branch into account
  • 4th Oct 2014 – added version device in tutorial site

33 Comments to “Online PHP beginners’ tutorial”

  1. PeeHaa says:

    On the introduction page you say:

    > PHP; anything over 5.3.7 is fine, although the latest version is usually best

    But PHP 5.3 is officially EOL. Would be better to tell people (escpecially new ones) to use PHP 5.4+

  2. PeeHaa says:

    Second chapter:

    Instead of manually having to throw up when something goes wrong when using PDO set the PDO object up to throw exceptions itself.

    Also you are not setting the appropriate encoding (UTF-8) when handling the data using htmlspecialchars (note this is the default for php5.4+).

    It would also be nice to just setup the PDO object to always use PDO::FETCH_ASSOC as the fetch style instead if having to tell it that everytime you are getting recordsets.

    Generic improvements / suggestions:

    – more links to manual of functions and techniques you are using, so that the user can look at the official docs and examples
    – Show the rendered end result after each chapter / after each block of changes

    • Jon says:

      Great ideas, thanks for those. I’ll do the htmlspecialchars() as a priority.

      Screenshots are an interesting idea, though I do want readers to write all the code out (well, at least copy and paste) and I don’t want to give them any reason not to bother. There’s something good for the learning process by doing some hard typing work, I think!

      As an alternative to screenshots, I thought of hosting the app in all stages of its creation, so users can try it out. This would be easy to automate, since I have a folder of every checkout anyway (for the download links).

    • Jon says:

      Interesting. I was going to suggest that since PHP5.4 has set the default character set for htmlspecialchars() as UTF-8, it makes no sense to specify it.

      However, the PHP team think differently:

      Although this argument is technically optional, you are highly encouraged to specify the correct value for your code if you are using PHP 5.5 or earlier, or if your default_charset configuration option may be set incorrectly for the given input.

      Righto! So that means using htmlspecialchars($string, ENT_HTML5, 'UTF-8') – eugh! I’ll wrap it in a function.

    • Jon says:

      The htmlspecialchars() change is now done, thanks for the suggestion.

      I’ll look at the PDO and links suggestions in due course.

  3. Jon says:

    Phew, the tutorial has now gone live. Feedback welcome on an ongoing basis.

  4. Michael says:

    Hey Jon!

    First off, this tutorial is amazing! I’m learning PHP for a personal project and your tutorial is exactly what I needed. Its clear, concise, and has helped me by leaps and bounds.

    I have a question though. I’m currently on Chapter 4 and its been bugging me that the view-post.php doesn’t adapt to which post the user clicks on in index.php. Then I came across this snippet in one of the diffs:

    if (isset($_GET[‘post_id’]))

    I’m assuming that’s where the magic happens, but I didn’t see this change made yet. Have I just not gotten that far yet?

    If you would shoot me an email or message me on Reddit (secret_life_of_trees) that would be great.

    Thanks again!

  5. Michael says:

    Wow, I did the index.php portion of that diff, but skipped the view-post.php portion! Whoops. My confusion led me to researching GET and POST methods and now I have a better understanding, so at the end of the day I guess it was a good thing. I ended up implementing almost the exact code that is in that diff.

    • Jon says:

      Glad you fixed it!

      At some point I may add the ability to download a zip/tarball of your current point, so any minor mistakes don’t hamper progress.

      That said, as you say, the debugging practice is very useful 🙂

  6. Michael says:

    Hey Jon, I’ve looked over your PHP course. It looks pretty awesome! I wanted to share a couple of things that might help it be a little more usable.

    For the menu:
    – Perhaps have the link highlight instead of the containing “.menu-entry” div. Since the div highlights and it’s a huge box, I think it is an actual button I can press. When in reality it is the text (anchor) that is only clickable.

    – Could you perhaps move the menu to the left and have it scroll with the user? Or perhaps have another menu at the bottom that’ll easily let me go to the next section once I’m done with the current page? Would help let me stay focused on completing the course.

    Feedback loop:
    It’d be great to have some sort of ability to get feedback loop based around the topic/page at hand. There are simple solutions like Disqus, that lets you embed comments on your web pages. That way, if I have a question about something on the page while I’m going through the materials, I could ask it right away.

    Really enjoy the layout of the material and the code samples look solid!

    Hope that helps!


    • Jon says:

      Thanks Michael, most helpful.

      Good idea on the clickability of the menus. I have a new branch to make this a drop-down, and your suggestion would be a good one to add to the list. A next button/link at the end is already planned, I can probably do that on the live branch.

      For integrated discussion, yes: I have a HTML prototype of that already. Comments can be attached to any paragraph, and can be replied to. The database work is easy but the CSS/layout is tricky to get right (especially since I’m not a frontend dev!).

      I’ve been busy with other projects, and since the traffic numbers are presently low, I am currently waiting to see if the development effort is worthwhile. I’ll definitely come back to it, though.

      • Michael says:

        “For integrated discussion, yes: I have a HTML prototype of that already. Comments can be attached to any paragraph, and can be replied to. The database work is easy but the CSS/layout is tricky to get right (especially since I’m not a frontend dev!).”

        Perhaps you could do something like Medium did. Have a little comment bubble appear when the paragraph is hovered over?

        Yeah, completely understandable. Keep at it. I’m sure people find it helpful, even if the traffic numbers are low 🙂

  7. ToreS says:


    I want you to know that I’m currently working on your blog post tutorial and it really is exactly what I needed!

    I really like it so far! So, thank you for directing me to it!

    • Jon says:

      You’re most welcome, ToreS.

      • ToreS says:

        Hello again!

        I have been messing with your code for over a week now and it has been much fun.

        I have a question regarding SQLite though:

        I uploaded the files to my website. Running the install.php worked. It created a .sql file in /data/. Yesterday, I realized that my host, hostgator, provides a database setup. I created a db with a user and password and I tried to use this db instead of the . I changed the common.php so that the new POD is addressed to my new db.

        function getPDO()
        $pdo = new PDO(‘mysql:localhost;dbname=myuser_db’, ‘myuser_admin’, ‘userPW’);

        $result = $pdo->query(‘PRAGMA foreign_keys = ON;’);
        if ($result === false)
        throw new Exception(‘Could not turn on foreign key constraints’);

        return $pdo;

        It did not work, and this is my error:

        Fatal error: Uncaught exception ‘Exception’ with message ‘Could not turn on foreign key constraints’ in ~/lib/common.php:46 Stack trace: #0 ~/install.php(12): getPDO() #1 {main} thrown in ~/lib/common.php on line 46 ( I used ~ instead of the whole address as I am not sure that is unsafe to post or not).

        Next thing I tried was to run the text in init.sql in phpMyAdmin under the tab “SQL”. There is a text field there and I just paste it in it.

        That gave me this error:

        SQL query:

        CREATE TABLE post (
        title VARCHAR NOT NULL,
        body VARCHAR NOT NULL,
        user_id INTEGER NOT NULL,
        created_at VARCHAR NOT NULL,
        updated_at VARCHAR
        MySQL said: Documentation

        #1064 – You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘AUTOINCREMENT NOT NULL,
        title VARCHAR NOT NULL,
        body VARCHAR NOT NULL,
        use’ at line 2

        This is one of the first init.sql you provided.

        Extra info from info.php:
        PDO drivers mysql, sqlite


        PDO Driver for SQLite 3.x enabled
        SQLite Library

        I have trouble troubleshooting this. Do you know how I can fix this?

        Thank you so much for helping me! Btw, php is really fun 😀

  8. Jon says:

    Hi @ToreS, apologies for the belated reply – my blog doesn’t notify me when I get new comment!

    The problem is that you’re trying to run SQLite dialect SQL in MySQL – the PRAGMA is not valid syntax. Also, in MySQL, the VARCHAR types need to have a size associated with them, which will vary depending on what the column is for. For example you might have VARCHAR(100) for a post title.

    I’ve not tried converting the site to MySQL, and whilst I am sure it could be made to work, I don’t have the time to test it against that database as well. Still, great idea to give it a go!

  9. Allen says:

    Hi Halfer. I noticed that you had answered questions related to “blockchain”, which is new to China. We, a Chinese company, are looking for an engineer in this regard because of a shortage of talents in the country. Do you have any interest in working in China if you are the eligible candidate for us? Looking forward to hearing from you.

  10. Ash says:

    Nice tutorial, really thorough.

  11. Emmanuel says:

    It’s a nice tutorial just looking at it, I’ve not gone through it yet.
    I do notice you used PDO in stead of msqli, I’ll like to know what led you to do this.

    • Jon says:

      PDO is often thought to have a simpler API. In MySQLi, parameters need to be declared using a type string like “ssiis”, where “s” means string, “i” means integer, etc.

      However, if folks use MySQLi, that’s not a bad thing in itself: it is actively maintained by the PHP team, and offers parameter binding, which is critical for security.

  12. Hello, I’m a little late with building this, but I can’t log in for some reason. I generate the password, but the username: admin doesn’t work.

    • Jon says:

      Sorry for the belated response. I’d be happy to help, but I don’t think there is enough information there for me to determine the cause of failure.

      If you happen to see this, drop me a line using my email in the “About” section, and include as much relevant information as you can.

  13. JC says:

    Thank you for your tutorial !

    It’s possible to have all code of this project (+sql database for importation) ?

  14. Skye says:

    Hello, I am not sure if you still look at this, but I ran into a problem when trying out your PHP tutorial.
    Currently, I am at chapter 4 (improving the installer) and whenever I try to do the first step, the one where you have to change the install.php a lot, I somehow break the code somewhere. Downloading the file and copypasting has no effect. I get the error message that there are undefined indexes for count and error on line 81 82. A fair warning that I am completely new at this and am probably missing something simple.

    Thank you for your time

    • Jon says:

      Hi Skye. I am indeed still supporting this, but my contact form is persistently playing up, so I don’t see messages. I really should create a Reddit forum to help people!

      My advice generally is to present your problem on a PHP forum like PHPhelp ( so that you’re not stuck waiting for me to see your message!

      In general, broken code may be down to making a mistake in the code. Are you using an IDE like NetBeans, Eclipse or PHPStorm? That is a very good way to iron out several classes of mistakes, since it has a built-in syntax checker.

  15. Nicholas says:

    hey, Jon, I’m loving the blog tutorial, however I reached the point to add a delete button on the “All Posts” page and it won’t work. It says that the function “deletePost” is undefined, but I checked my lib/list-posts.php file and it’s defined. I don’t know what I’m doing wrong, can you help me? I’ve compared the source code with my own and it’s exactly the same.

    • Jon says:

      Ah, apologies, I did not see this message. I can say that the code definitely works. I wonder whether you have defined the function, but not called “require_once” on the script that defines it?

  16. xiao wei says:

    Dear devotees, and dear teachers. I have completed your tutorial, and the code runs successfully. Thank you very much. Can you introduce me to some other excellent PHP tutorials? Or any guidance?

Leave a Reply