Photo by Steve Johnson from Pexels

Get started in web development with me — Part 4: HTML forms, PHP and dealing with problems

Gabriel Cruz
14 min readJan 21, 2019

--

Hey you! Hope you enjoyed my last post. If you haven’t checked part 1, part 2 and part 3 go ahead and read them, they’ll give you basic understanding of how this series work and get you started with some basic concepts.

So today we’re going to work a little bit more on —

Sh*t. I went to Facebook for a minute and turns out I spent my entire afternoon in it… woooops!

But to be honest I think procrastinating for 4 hours gave me a great idea of what important web concept of we’re missing.

Lost in the series? Here are Part 1, Part 2, Part 3, Part 4 and Part 5.

An important web concept we’re missing

Photo by Fancycrave.com from Pexels

Go ahead and log in your Facebook account real quick. If you don’t have a Facebook account any other social network will do.

Do you realize that all those websites allow you to modify them in a certain way? On Facebook, Twitter and LinkedIn you have your timeline where you can post stuff. Do you realize what’s going on here? You’re posting things.

Not clear yet? What you’re basically doing is modifying the content that’s available in the website, but with some restrictions (you can’t just go around changing everything on the server).

So how does a server receives input from its clients?

HTML Forms: How a server receives input from its clients

This is something I’ve known for some time, but I’ve never actually used one. So let’s take a look at a quick forms tutorial.

A form is a pretty obvious thing, you’ve probably seen them all over the web: login forms, posting forms, comment forms, etc.

Let’s build some sh*t.

Herschel Evans’ comment section

Remember our Herschel Evans’ website? Let’s add a commenting section to it so that people can submit their comments.

Actually, I had a better (simpler) idea. Instead of a commenting section, that adds/removes/edits things from our page, let’s add a voting form asking whether people liked or not the page.

Voting form

First of all we need an HTML form to receive the data, with radio buttons and a submit button:

Simple right? Let’s take a look at some things that are not so obvious:

  1. What’s the reason for the field value="yes" if the text displayed is already “Yes”? The value is the text that will be sent to the form handler, the thing that gets the form and does something with it, and the “Yes” is just the text that will be displayed after the button in the page.
  2. What about the field name="liked"? That is because those buttons belong to the same option, take a look at this health research form I made, I added various input types as well:

All right, we now know how to create basic HTML forms, we can receive input from our clients through HTML.

But how does a server processes input from its clients?

HTML handlers: how a server processes input from its clients

Now that we have a way for the client to tell us what he wants we need to collect this information the forms are sending us and do something with it. In the our case we are going to update a number (or percentage) that shows how many people liked and disliked our page.

Let’s take a look at the The Action Attribute section on the w3schools tutorial:

The action attribute defines the action to be performed when the form is submitted.

So, according to w3schools, we would do something like this:

<form action=”/action_page.php”>

Okay, seems simple. There’s just a tiny problem: I don’t know any PHP at all.

PHP — Hypertext Preprocessor

Photo by Fancycrave.com from Pexels

Let’s read the Wikipedia description for PHP:

PHP: Hypertext Preprocessor (or simply PHP) is a server-side scripting language designed for Web development.

Soooo what’s server-side scripting again?

Server-side scripting is a technique used in web development which involves employing scripts on a web server which produce a response customized for each user’s (client’s) request to the website. The alternative is for the web server itself to deliver a static web page.

So server-side scripting is basically a technique in which the client’s actions make the server execute some type of script, a piece of code that does something.

So PHP is a language for creating scripts so that the server can do some processing before it serves us the pages. In our voting case, what the server has to do is maintain two counters: one of likes and another one of dislikes. Then, when it receives a request for the web page, it puts the counters in the HTML and sends the page. Also, the server has to update those counters every time it receives a voting form.

This is interesting, we need to use PHP to actually build a part of our HTML pages on-the-fly. These pages are called dynamic Web pages.

First of all let’s install PHP, I’ll be using this installation tutorial for Debian.

Now we can start reading the w3schools’ PHP tutorial. We don’t need to know everything it talks about, so here are the sections I believe are most important to us right now: Intro, Syntax, Variables, Echo/Print, Form Handling.

We don’t need to be experts in PHP just now. Let’s, as always, start small and simple. And what better way to do this than with a Hello World?

(P)H(P)ello World!

Let’s access this page using our browser… Sh*t, it doesn’t work: 404, Not Found. What the — ?

Maybe this is because of the path in which the file is stored: /home/gabriel/html/php/hello_world_php.html. No, same error when it’s on /home/gabriel/html.

Maybe this is because I’m trying to embed PHP code into HTML in a funny way, let’s try raw PHP:

404 Not Found. Alright, check out the Problems section 1 for the solution of this problem.

Okay now we can access PHP files… But the code above shows this on the browser:

<?php
echo "<h1>Hello World!</h1>";
?>

This means that our server is not processing PHP at all! See Problems section 2 on how to solve this.

Phew! I had a lot of trouble installing and configuring PHP.

Now that we know how to say ‘Hello World’ on PHP we can start doing some more interesting stuff. How about a calculator?

Calculator

This should be easy: simple form that takes two numbers and an operation and redirects to a page with the answer:

Now we add a handler PHP file to calculate that:

IT WORKS!!!

If you don’t have much programming experience it’s possible that you understood close to nothing about this code. In that case I recommend you read the operators and the IF/ELSE tutorials on PHP. If you don’t have much experience with programming I suggest you dig into switch statements as well in order to practice a little bit more.

Thanks for reading

Thank you so much for reading. To be honest this was the first time on this series I didn’t really know what was going on at all, so please let me know if you have thoughts on how to improve my explanation.

Thank you for reading, see you next time.

PROBLEMS

IMPORTANT: Don’t go around copying everything I’m doing before reading all the section because, more often than not, I tend to break things before I understand how to fix them. Read the entire section, figure out what’s going on, and than fix the problem you’re having.

1. PHP 404 Not Found: FastCGI and Linux packages

The problem here is that the server isn’t opening (404: Not Found) PHP files (files with the.phpextention). But it serves fine HTML files with PHP code in it. Well… It serves HTML files with PHP code, but it doesn’t seem to process the PHP code.

Let’s try this Stack Overflow question, it says something about getting an specific error on the FastCGI log file, let’s check that file and look for some errors.

You: But wait! What the hell is FastCGI?

Me: Great question, I have absolutely no idea, lol.

I did some digging on Wikipedia and found a question on Quora:

What is FastCGI in PHP?

It’s a way for PHP to be executed by a process that’s separate from the webserver. This allows the FastCGI process to execute more efficiently.

CGI stands for Common Gateway Interface. In other words, it’s a way for other programs (processes) to do things for the web server, so that these things don’t have to be done all in one process (the server’s process).

I found something interesting while looking for the error log, check this out:

I’m trying to set up a global location in nginx. It all works perfectly right now, other than PHP scripts requiring fastcgi. They're running a 404.

The thing to realize here is not the question itself, but the information contained in it: There’s this location thing, I remember this from when I was installing PHP in Nginx, I had to add a location thing in the configuration file:

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}

And after that the tutorial told me to restart the Nginx service:

$ sudo systemctl restart nginx

And I did. But I know that nginx reloads the configuration file with:

$ sudo nginx -s reload

or with

$ sudo nginx -s restart

So I let’s try the reload. No luck.

Here’s something interesting I found on the tutorial I “followed” (I didn’t read it…. oooops):

Unlike Apache, Nginx doesn’t have a built in support for processing PHP files so we need to install a separate application such as PHP FPM (“fastCGI process manager”) which will handle PHP files.

And then it tells me to download the php-fpm package:

$ sudo apt install php7.2-fpm

But here’s the catch: the tutorial is a bit old! Take a look at my PHP version:

$ php -v
PHP 7.3.1 (cli) (built: Jan 15 2019 14:20:03) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.1, Copyright (c) 1998-2018 Zend Technologies

My PHP is version 7.3 and I have the FPM for version 7.2! Let’s remove FPM for PHP 7.2 and install the one for version 7.3 then:

$ sudo apt remove php7.2-fpm
...
$ sudo apt install php7.3-fpm
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:
The following packages have unmet dependencies:
php7.3-fpm : Depends: php7.3-cli but it is not going to be installed
Depends: libpcre2-8-0 (>= 10.31) but 10.22-3 is to be installed

Ah crap, I installed a bunch of sh*t for PHP version 7.2. I could just reinstall PHP on version 7.2, but I want the newest version. Let’s remove this old crap and try to install the new crap:

$ sudo apt remove php7.2-*
...
$ sudo apt install
php7.3-common php7.3-cli
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:
The following packages have unmet dependencies:
php7.3-cli : Depends: libpcre2-8-0 (>= 10.31) but 10.22-3 is to be installed
E: Unable to correct problems, you have held broken packages.

Okay, I’m guessing libpcre is a package required by php7.*-cli, and probably php7.2-cliand php7.3-cli require different versions of libpcre. What happened is when apt removed php7.2-cli it didn’t remove the libpcre version of it required upon installation. Take a look at what apt tells us when I try to manually install libpcre:

The following packages were automatically installed and are no longer required:
libargon2-1 libsodium23 php-common
Use 'sudo apt autoremove' to remove them.

Great! It tells us how to remove old unnecessary packages:

$ sudo apt autoremove
...
$ sudo apt install php7.3-common php7.3-cli
...
The following packages have unmet dependencies:
php7.3-cli : Depends: libpcre2-8-0 (>= 10.31) but 10.22-3 is to be installed
E: Unable to correct problems, you have held broken packages.

Dammit, same problem.

You: Why are you going through all that trouble just to fix that? Just delete libpcre and move on dude!

Me: It’s possible that other stuff on our machine depends on libpcre, so we can’t just blindly uninstall it.

Oh, looks like we could’ve used aptitude:

-f, — fix-broken Fix; attempt to correct a system with broken dependencies in place. This option, when used with install/remove, can omit any packages to permit APT to deduce a likely solution. If packages are specified, these have to completely correct the problem.

Let’s give this a try, I’ll be using the packages from this tutorial:

$ sudo aptitude install -f php7.3-fpm php7.3-cli php7.3-mysql php7.3-gd php7.3-imagick php7.3-recode php7.3-tidy php7.3-xmlrpc
...
The following packages have unmet dependencies:
php7.3-cli : Depends: libpcre2-8-0 (>= 10.31) but 10.22-3 is installed
php7.3-fpm : Depends: libpcre2-8-0 (>= 10.31) but 10.22-3 is installed
The following actions will resolve these dependencies:
Keep the following packages at their current version:
1) php7.3-cli [Not Installed]
2) php7.3-fpm [Not Installed]
Accept this solution? [Y/n/q/?]

No! You’re telling me you’re just not going to install those packages? I DO NOT ACCEPT THAT “SOLUTION” YOU MORON!

The following actions will resolve these dependencies:Upgrade the following packages:                                            
1) libpcre2-8-0 [10.22-3 (now, stable) -> 10.31-3+0~20180714171306.1+stretch
Accept this solution? [Y/n/q/?] y

That’s more like it, and IT WORKS!

2. Nginx not processing PHP

Let’s google this (by the way, I actually use DuckDuckGo):

nginx not processing php

Found this question, here’s the answer:

Ok, so based on our comments back and forth you need to add this section to your nginx config for the server config listening on :8000.

That regex tells nginx that whenever it gets a request for a URL with a file ending in .php to send that to the fastcgi process. Otherwise it’s going to default to returning the raw file that matches in /var/www/html.

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
try_files $uri =404;
include snippets/fastcgi-php.conf;
# With php5-cgi alone:
#fastcgi_pass 127.0.0.1:9000;
# With php5-fpm:
#fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
#fastcgi_index index.php;
include fastcgi_params;
}

Let’s try adding this location bit to our server configuration file, changing the path /var/run/php5-fpm.sock to match our PHP version:

# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
server {
location / {
root /home/gabriel/html;
}
location ~ \.php$ {
try_files $uri =404;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php7.3-fpm.sock;
#fastcgi_index index.php;
include fastcgi_params;
}
}
}

And restart the nginx server:

$ sudo nginx -s reloadnginx: [emerg] "try_files" directive is duplicate in /etc/nginx/snippets/fastcgi-php.conf:5

No luck. My guess is that what we tried to specify is already specified in other configuration files, let’s take a look at /etc/nginx/snippets/fastcgi-php.conf:

# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;

I have no idea of what’s going on here, this tutorial seems to give a good overall explanation of FastCGI’s configurations:

Ubuntu places nginx’s configuration files in /etc/nginx and its sub-directories. Shared configuration fragments are kept in that root, and specific server setups reside in sites-available with symlinks in sites-enabled to make them active.

So it seems we have been using configurations’ files the wrong way, let’s move our server configuration to a file /etc/nginx/sites-available/hersheland create a symlink /etc/nginx/sites-enabled/hershel to that file.

Make a copy of /etc/nginx/sites-available/default and change the location to match our server’s location:

$ cd /etc/nginx/sites-available/
$ sudo cp default herschel
$ sudo vim hershel # use any editor you like (emacs, nano)

Now we change the file to include our root directory:

# remove this line
root /var/www/html;
# add this line to location / block
root /home/gabriel/html/; # our server's root (where our html is)

And we enable PHP by uncommenting this section, remember to change the version number to match your PHP version:

location ~ \.php$ {
include snippets/fastcgi-php.conf;

# With php-fpm (or other unix sockets):
fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
# With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
}

Then we create a symlink to replace the default symlink:

$ cd ..
$ sudo rm sites-enabled/default
$ sudo ln -s /etc/nginx/sites-available/herschel sites-enabled/default

Finally, we reload the configuration files:

$ sudo nginx -s reload
nginx: [emerg] "fastcgi_pass" directive is duplicate in /etc/nginx/sites-enabled/default:62

Crap. I guess I shouldn’t have uncommented both the fastcgi_pass’ lines, so let’s comment one of them (the one with php-cgi, because we’re using php-fpm):

location ~ \.php$ {
include snippets/fastcgi-php.conf;

# With php-fpm (or other unix sockets):
fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
# With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000; # comment this line
}

And reload the configuration files again:

$ sudo nginx -s reload

Okayy now we have our website back. Let’s try to access hello_world.php again:

404 NOT FOUND

Crap. Maybe restarting the php7.3-fpm service?

$ sudo systemctl restart php7.3-fpm

Nope. I think it’s time to stop trying to guess what’s going on. Let’s read another tutorial, and then try again:

No input file specified.

Sweet, it doesn’t throw a 404 any more. But it’s still not showing what we want, let’s google this:

php no input file specified nginx

I found this question which has this answer:

Now, there are many things wrong with this configuration. An obvious and glaring issue is the root directive in the location / block. When the root is defined inside the location block, it is available/defined for that block only.

This seems interesting. The way our configuration is the root directive is inside the location / block. Let’s get it out and try again.

YEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEES!

Beautiful, but why did that fix the problem? What was the problem? In the same answer there’s an explanation for that:

Here, the location /images block will not match for any request because it does not have any $document _root defined and we will have to redundantly define root again for it. Obviously, the root directive should be moved out of the location / block and defined in the server block. This way, the location blocks will inherit the value defined in the parental server block. Of course, if you want to define a different $document_root for a location, you can put a root directive in a location block.

Aaah, so because we defined the root directory inside a location block it was available only for that location block, and not others such as the PHP one.

--

--

Gabriel Cruz

Computer Science student at University of São Paulo. OSS/Linux enthusiast, trailing spaces serial killer, casual pentester