What is PHP?

PHP (short for Hypertext Preprocessor) is an Open-Source scripting language which is mainly used to generate HTML-code when developing websites.

Example:

<?php
   $variable = "Testvariable";
   echo "This variable contains following text: " . $variable;
?>

Result:

This variable contains following text: Testvariable

In this example a basic variable is being defined and directly after that being outputted via “echo” after the text “This variable contains following text: “.

Due to the fact that PHP is a scripting language the content of the PHP file is being processed on every request. In contrast Java or C need to be “compiled” into machine code before it can be executed.

PHP offers quite a few functionalities out of the box – for example:

  • Database operations (MySQL, PostgreSQL, SQLite etc.)
  • Filesystem operations (Create/Edit/Delete folders or files etc.)
  • String manipulation (Create/Edit/Delete text etc.)
  • XML operations (Create/Edit/Delete XML data structures etc.)
  • and much more

These are just some basic which you can basically can find in any “Default PHP” installation. But PHP has so called “PHP modules” to extend the functionality PHP offers.

Popular PHP modules are:

  • XDebug (Extended Debug functionality)
  • OPCache (Saves precompiled bytecode from PHP in RAM instead of recompiling it on every request => performance boost)
  • MBString (Adds the ability to handle “MultiByte Strings” in PHP – e.g. Emoji Icons)
  • GD (Image editing in PHP like rotating or converting)
  • SOAP (A special, XML like data structure)
  • and much more

How PHP can be used via web servers or via the CLI can be seen in “CLI vs WebServer Integration“.

Due to the fact that PHP is always evolving I have gather the most important changes of PHP 7.1, 7.2 and 7.3 HERE.

Sources:
PHP: What is PHP – https://www.php.net/manual/de/intro-whatis.php

PHP versions

Everywhere in software development we have versions which evolve over time to improve performance, fix security issues and add new functionality. The following list contains a short overview of the changes from PHP 7.1 to 8.1.

PHP 5.6 and 7.0 will not be included here because they are no longer being supported (php.net) (April 2019)

PHP 8.1

Native ENUM support

It now is possible to (similar to other modern programming languages like Kotlin, Rust or Swift) to define ENUMs (short for enumerated types).

A simple case of an ENUM is the following

enum TransportMode {
  case Bicycle;
  case Car;
  case Ship;
  case Plane;
  case Feet;
}

and can be used like this

function travelCost(TransportMode $mode, int $distance): int
{ /* implementation */ } 

$mode = TransportMode::Boat;

$bikeCost = travelCost(TransportMode::Bicycle, 90);
$boatCost = travelCost($mode, 90);

// this one would fail: (Enums are singletons, not scalars)
$failCost = travelCost('Car', 90);

But you can also set scalar values for each case inside the ENUM:

enum Metal: int {
  case Gold = 1932;
  case Silver = 1049;
  case Lead = 1134;
  case Uranium = 1905;
  case Copper = 894;
}

BUT there are some rules you have to follow when setting scalar values:

  • If you set one scalar value to 1 case then ALL cases need to have one. So either none have one or all have one, nothing in between.
  • Scalar values (just like the case itself) need to be unique.
  • The scalar values are read only
  • To access the scalar value of an ENUM case you can do that via Metal::Gold->value

The never return type

Till now it was not possible to prevent functions from every returning anything. There was/is the void type but it doesn’t care if you either exit or just don’t return anything.

Forcing to exit can now be done via the new never return type:

function shutdown(): never {
    exit();
}

But also implicit exit calls are allowed like:

function redirectToHome(): never {
    redirect('/');
}

Fibers (Non-Blocking/asynchron PHP)

For all those developers who have doven into the World of e.g. NodeJS this topic may be a little strange. Basically PHP is written so all the lines of code are performed synchroniously. So like line 2 only gets executed after line 1 is done.

This however now can be changed via using fibers in PHP 8.1.

Asynchron PHP already has been somewhat available via external packages like amphpReactPHP or Guzzle but there was no standardized way of doing it (till now).

A good example using fibers can be found HERE

Readonly properties

Now you are able to set a property to be readonly. This means, that it only can be initialized once and can never be changed after that.

class Release {
    public readonly string $version;
 
    public function __construct(string $version) {
        // Legal initialization.
        $this->version = $version;
    }
}

Whats the difference with defining a const property?

public readonly string $version; means, that the value of the property can differ from each object initialization of the type Release.

But public const string $version; means, that all objects created of the type Release must have the same value.

PHP 8.0

JIT (Just in Time Compiler)

Without going too much into detail PHP 8.0 added a JIT Compiler which improves the performance of your PHP app “under certain conditions”. Unfortunately these conditions are not related to typicall CMS (like WordPress) usage and more “numeric” or “calulating” tasks.

Bild: https://www.php.net/releases/8.0/en.php

Constructor property promotion

Till now properties needed to be initialized like so:

class Car {
    public int $wheels;
    public int $doors;

    public function __construct(
        int $wheels = 4,
        int $doors = 5
    ) {
        $this->wheels = $x;
        $this->doors = $y;
    }
}

But the exactly same logic can now be writte in a much smaller footprint:

class Carr {
    public function __construct(
        public int $wheels = 4,
        public int $doors = 5
    ) {}
}

Union Types

The only union type present before PHP 8.0 was the?Type to allow null values as well as the default Type.

Otherwise you would have to add PHPDoc Union Types so static analysis tools like PHPStan understand the code better.


/**
 * @property int|float $power
 */
class Car {
    private $power;

    /**
     * @param int|float $power
     */
    public function setPower($power) {
        $this->power = $power;
    }

    /**
     * @return int|float
     */
    public function getPower() {
        return $this->power;
    }
}

Now you are abe to set these kind of Union Types directly in the property type, parameter type and return type.

class Number {
    private int|float $number;

    public function setNumber(int|float $number): void {
        $this->number = $number;
    }

    public function getNumber(): int|float {
        return $this->number;
    }
}

PHP 8.0 in general focused on a more stricter typing system and typing features.

Named attributes

Defininig parameters inside your function required them to be in an order which seems logical so calling that functions is easier:

function calc($start, $end, $steps) {
    ...
}

so you could call it like that:

calc(10, 100, 5);

But now you can also do it like that:

calc(end: 100, start: 10, steps: 5);

Or even combine position oriented and named attributes like that:

calc(10, steps: 5, end: 100);

With this apporach you still have to position the not named attributes at the correct position!

Nullsafe-Operator

Sometimes you only wan’t call a specific method on an object if it is actually present/not null.

$result = null;
if($a !== null) {
    $result = $a->b();
}

This can now be written like so:

$result = $a?->b();

So if $a is null the methode b() will not be called and $result will be null.

New string compare functions

I regularely have to perform string operations to check if e.g. a given string is present inside another or if one string starts with a specific string.

Till now you had to use the strpos() function and depending on the return value build your logic which is not always very readable code.

With PHP 8.0 these pretty self explenatory functions have been introduced

  • str_contains( $haystack, $needle )
  • str_starts_with( $haystack, $needle )
  • str_ends_with( $haystack, $needle )

So you can write more readable and understandable code.

PHP 7.4

Spread-Operator for Arrays

Calling functions with a variable amount of arguments is nothing new for us.

function my_function(...$args) { var_dump($args); }
my_function('test', true, 123);

// Result
array(3) {
  [0]=>
  string(4) "test"
  [1]=>
  bool(true)
  [2]=>
  int(123)
}

But this functionality is now also available for array operations.

$bool_arr = [true, false];
$total_arr = ['text1', 123, ...$bool_arr , 456];
var_dump($total_arr );

// Result
array(5) {
  [0]=>
  string(5) "text1"
  [1]=>
  int(123)
  [2]=>
  bool(true)
  [3]=>
  bool(false)
  [4]=>
  int(456)
}

It is recommended to use this kind of array merging functionality instead of the typical array_merge() function because it is more performant.

Arrow functions

One typicall example to use a arrow functions is to transform this

function cube($n){
	return ($n * $n * $n);
}
$a = [1, 2, 3, 4, 5];
$b = array_map('cube', $a);

into this

$a = [1, 2, 3, 4, 5];
$b = array_map(fn($n) => $n * $n * $n, $a);

Null Coalescing Assignment Operator

PHP 7.0 already introduced a way to set a fallback value if a given variable is not set.

$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';

But this can now be more condensed into this

$this->request->data['comments']['user_id'] ??= 'value';

Typed Class-Arguments

Now you can transform this

class User {
    /** @var int $id */
    private $id;
    /** @var string $name */
    private $name;
 
    public function __construct(int $id, string $name) {
        $this->id = $id;
        $this->name = $name;
    }
}

into this

class User {
    public int $id;
    public string $name;
}

The following types are being allowed:

  • bool
  • int
  • float
  • string
  • array
  • object
  • iterable
  • self
  • parent
  • Any class or interface
  • nullable types (?type)

But neither void nor callable are allowed by definition!

Preloading

Without getting to complicated OPCache preloading improves PHP performance by always keeping the main libraries cached in the OPCache instead of reloading them every time a PHP process is being started.

This setting needs to be set in the php.ini

PHP 7.3

Trailing Commas are allowed in Calls

With PHP 7.3. it is now allowed to have a trailing comma even if it is the last parameter in a function call.

my_function(
    $param1,
    $param2,
);

JSON_THROW_ON_ERROR

Till now json_decode() returns null if an error occurs.
But null can also be a valid result, which can lead to confusion.

So now you can check for json errors with these functions:

  • json_last_error()
    • Returns (if present) the last error, which has occurred on the las encoding/decoding process for a JSON.
  • json_last_error_msg()
    • Return “No error” if the encode /decode was a success and FALSE if there were problems.

An anonymous user has written a very nice helper function on the json_last_error_msg() definition page on php.net:

<?php
    if (!function_exists('json_last_error_msg')) {
        function json_last_error_msg() {
            static $ERRORS = array(
                JSON_ERROR_NONE => 'No error',
                JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
                JSON_ERROR_STATE_MISMATCH => 'State mismatch (invalid or malformed JSON)',
                JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
                JSON_ERROR_SYNTAX => 'Syntax error',
                JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded'
            );

            $error = json_last_error();
            return isset($ERRORS[$error]) ? $ERRORS[$error] : 'Unknown error';
        }
    }
?>

Alternatively you an also solve that problem with a try-catch block:

try {
    json_decode("{", false, 512, JSON_THROW_ON_ERROR);
}
catch (\JsonException $exception) {
    echo $exception->getMessage(); // echoes "Syntax error"
}

is_countable function

Currently it is common to check a variable if it can be looped through a foreach with the following code:

$array = [1,2,3,4,5,6,7,8];

if(is_array($array) && sizeof($array) > 0){
  foreach($array as $value){
    
  }
}

But with PHP 7.3 you can write:

$array = [1,2,3,4,5,6,7,8];

if(is_countable($array)){
  foreach($array as $value){
    
  }
}

array_key_first(), array_key_last()

Till now it was not “easy” to get the first and last “key” of an array. But with PHP 7.3 you now have the functions array_key_first()and array_key_last().

PHP 7.2

Native support for BMP image format

Since PHP 7.2 the GD extension allows to handle .bmp images.

Typ “object” bei Parameter- und Rückgabewerten einstellbar

A new type, object, has been introduced that can be used for parameter typing and return typing of any objects.

function test (object $a): object {

}

Enhancements to the EXIF extension

EXIF (Exchangeable Image File Format) is a standard to save metadata in image files.

Till now the automatically parsed EXIFF data for images files was very limited. With PHP 7.2 many EXIF formats from well known camera suppliers have been added. See HERE

Encrypted ZIP-Archives

With PHP 7.2 you can now create ZIP archives with a password protection.

PHP 7.1

“Nullable types”

function test1(?array $a) {
 
}
 
function test2($a): ?array {
 
}

The first function (test1) defines, that the first parameter can have the type “Array”, but can also be “null” (? before the array)

The second function (test2) defines, that the return value can have the type “Array”, but also “null” too.

Without the prefixed ? the function test1 called with a parameter “null” or the second function test2 with a return value of “null” would lead into a “Fatal Error” when executing that code.

Array and list have same functionality

Old Code:

$array = array(0 => 'a', 1 => 'b');
 
list($a, $b) = $array;
 
// $a is = 'a'
// $b is = 'b'

New Code:

$array = array(0 => 'a', 1 => 'b', 2 => 'c');
 
[$a, $b] = $array;
 
// $a is = 'a'
// $b is = 'b'
 

list(1 => $b, 2 => $c) = $array;
// same as above
[1 => $b, 2 => $c] = $array;

Visibility of constants in classes

As already common in other object oriented programming languages you can now set “visibilities” for class constants.

  • public Access to variable allowed from everywhere.
  • protected Access to variable only allowed in its own class AND all extended classes.
  • private Access to variable only allowed in its own class.
class test {
    const PUBLIC1 = 'TEST';
 
    public const PUBLIC2 = 'TEST';
    protected const PROTECTED = 'TEST';
    private const PRIVATE = 'TEST';
}

Multi catch exception handling

You can now have multiple exceptions in one try-catch block.

try {
     throw new Exception('Fehler');
} catch (Exception | AndererExceptionTyp $catchedException) {
     var_dump($catchedException);
}

mcrypt extension deprecated => use OpenSSL

The functions of the “mcrypt” extension (function names all start with “mcrypt_”) will be marked as “deprecated” markiert and therefore produce a line in the error.log.

Use the OpenSSL extension as a replacement.

Sources

mod_php vs (Fast)CGI vs FPM

There are different ways how a web server can handle PHP files. Following are the most common implementations how this is done:

mod_php

“mod_php” is a module for the web-server “Apache”.

With this module PHP is therefore “integratedinto the web-server. So there is no extra PHP-Process which handles the PHP interpretation. Instead everything is handled by the Apache process.

The main advantage for using “mod_php” is performance. Compared to CGI you usually gain 300-500% when changing to mod_php.

Main reason for that is the ability to cache PHP modules, or specifically the configuration which usually (in CGI and FastCGI) has to be parsed on every request.

How do I know, if mod_php is being used?

Since mod_php is a web-server module it needs to be present in the web-server config which looks something like that

LoadModule php7_module modules/mod_php.so

The following entry was located on my Ubuntu server under/etc/apache2/mods-available/php7.4.load

LoadModule php7_module /usr/lib/apache2/modules/libphp7.4.so

Looking at the generated phpinfo() output you an also detect mod_php via the following entries:

Server APIApache 2.0 Handler
Loaded Modulescore mod_so mod_watchdog http_core mod_log_config mod_logio mod_version mod_unixd mod_access_compat mod_alias mod_auth_basic mod_authn_core mod_authn_file mod_authz_core mod_authz_host mod_authz_user mod_autoindex mod_deflate mod_dir mod_env mod_filter mod_mime prefork mod_negotiation mod_php7 mod_reqtimeout mod_setenvif mod_status

CGI

The “Common Gateway Interface” (short CGI) implementation means, that the web-server starts an extra PHP process for each request. Therefore all PHP modules, the php.ini and all other configuration needs to be loaded and parsed on every request, which is inefficient.

Main advantage for using CGI is the complete separation between the executing web-server Code and the PHP code.

FastCGI

FastCGI is a PHP implementation, which contains the security advantages from CGI but also being efficient like mod_php.

Here we don’t start a new PHP process on every request, instead we use “ready made” PHP interpreter instances which only get the PHP files passed on to be handled.

How do I know, if FastCGI is being used?

There are multiple ways how you can implement FastCGI in the web-server config. One example would be to send all .php request to a lokal PHP-CGI binary:

ScriptAlias /myPHP /home/devguide/php-install-directory
AddHandler cgi-php .php
Action cgi-php /myPHP/php-cgi

So here all .php files requested by the webserver are going to call the PHP-CGI binary located in /home/devguide/php-install-directory/php-cgi. Usually this binary is being installed when you install the general PHP package but it could also be a separate package.

The phpinfo() shows you the following for FastCGI:

Server APICGI/FastCGI

FPM

The „PHP-FastCGI Process Manager“ (short PHP-FPM) is an alternative to the FastCGI implementation.

Here the main difference is, that there is always a “parallel” PHP-FPM Process which is connected to the web-server process.

For more details on FPM see HERE

Source: https://blog.layershift.com/which-php-mode-apache-vs-cgi-vs-fastcgi/

How do I know, if FPM is being used?

FPM uses its own service to manage the FPM-Pools. Therefore if you can see the running services on your server (e.g. via top or htop) you can pretty easily detect the PHP-FPM service.

The phpinfo() will show you the following for FPM:

Server APIFPM/FastCGI
php-fpmactive

PHP-FPM

The “PHP-FastCGI Process Manager” (short PHP-FPM) is an alternative FastCGI Implementation for PHP in a web-server.

Here we always have (at least) one PHP-FPM process parallel to the web-server process which handles PHP interpretation.

FPM processes group up into different “pools”. In these pools there will usually be several proccess created which handle PHP interpretation for a specific web page.

The amount of these processes and many more settings can be set in the FPM configuration (see bellow).

Installation (Debian based Distros)

sudo apt install php7.2 php7.2-common php7.2-cli php7.2-fpm

FPM Configuration

This is located in “/etc/php/7.2/fpm/pool.d/”.
Here we should create one file per socket/website.

[<pool-name>]
user = <username>
group = <groupname>
listen = /run/php/<socket-name>.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
chroot = <docroot-path>
php_admin_value[openssl.capath] = /etc/ssl/certs

The “listen” defines where the linux socket should be placed in the file system. This socket will be created after the PHP-FPM process has been restarted.

Via this socket the web-server can redirect PHP interpretation to the FPM process (see bellow)

Web-Server Configuration (NGINX)

The web-server has to know how and where it should send PHP-Files to the corresponding PHP process.

This is an example for: /etc/nginx/sites-available/<domain-name>.conf

server {
    listen 443 ssl http2; # managed by Certbot
    listen [::]:443 ssl http2;

    ssl_certificate /etc/letsencrypt/live/<domain-name>/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/<domain-name>/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    root <path-to-docroot>;
    server_name <domain-name>;

    index index.html index.htm index.php;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }	

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
	fastcgi_pass unix:/run/php/<fpm-socket-name>.sock;
    }
	
    error_log <path-to-logs>/error.log warn;
    access_log <path-to-logs>/access.log apm;
}

server {
    if ($host = <domain-name>) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
	
    listen 80;
    listen [::]:80;

    server_name <domain-name>;
    return 404; # managed by Certbot
}

The following line is responsible for the “redirection” to the FPM process:

fastcgi_pass unix:/run/php/<fpm-socket-name>.sock;

When you change something in the PHP-FPM config or the NGINX config you should always restart both services:

sudo systemctl restart php7.2-fpm.service nginx

CLI vs web-server integration

As described in PHP-FPM you can have different configurations for different web pages.

One advantage for that is the fact, that you can have different PHP versions for different web pages.

But these configurations don’t change the system-wide installed PHP version.

This means, that the PHP version from a PHP-Info file doesn’t have the be the same coming from the web-server and coming from the CLI.

php -v
PHP 7.2.19-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: May 31 2019 11:17:15) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.19-1+ubuntu18.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies
    with Xdebug v2.7.1, Copyright (c) 2002-2019, by Derick Rethans

How to change the system-wide default PHP version?

Note: This is only possible if you have root/sudo privileges on the server!

Via the following command you can find out where the php command is located:

which php

On my server this is:

/usr/bin/php

If you now check this “file” you see:

ls -al /usr/bin/php        
lrwxrwxrwx 1 root root 21 Jan  3  2018 /usr/bin/php -> /etc/alternatives/php

This means, that /usr/bin/php only points to /etc/alternatives/php. But as you can see on the path this is handled by the “alternatives”.

If you check this “file” you see:

ls -al /etc/alternatives/php        
lrwxrwxrwx 1 root root 15 May 31  2018 /etc/alternatives/php -> /usr/bin/php7.2

This now points to the “real” binary, which executes PHP.

ls -al /usr/bin/php7.2      
-rwxr-xr-x 1 root root 4899864 May 31 13:17 /usr/bin/php7.2

Therefore the command “php” points to “/usr/bin/php”, which points to “/etc/alternatives/php” and finally to “/usr/bin/php7.2”.

To change this “link” (and therefore change the CLI PHP version) you can execute the following command:

sudo update-alternatives --set php /usr/bin/php7.3

After that you successfully changed the PHP CLI from 7.2 to 7.3.

php -v
PHP 7.3.6-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: May 31 2019 11:06:48) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.6, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.6-1+ubuntu18.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies
    with Xdebug v2.7.1, Copyright (c) 2002-2019, by Derick Rethans

What is a webserver?

A webserver delivers data over the HTTP protocoll to a web browser. Typically these are HTML, CSS, JavaScript and image files.

Dependent on the chosen webserver you can use scripting languages or deliver dynamic data with for example:

  • PHP
  • JS
  • ASP
  • JSP

Typical webserver implementations:

  • Apache
  • NGINX
  • Cloudflare
  • Microsoft ISS
  • NodeJS
  • Tomcat (Java)
  • lighthttpd

Currently the distribution of webservers (22nd february 2020) is:

Source: https://en.wikipedia.org/wiki/Web_server

Apache

The Apache HTTP server ist a free to use, open source webserver which has been published in 1995 and is currently the most used webserver (February 2020 – see “What is a webserver?“).

The installations process for the Apache webserver is mostly different on every linux distribution. In the following example I will show you how to install it on a Debian/Ubuntu based system.

sudo apt-get install apache2

After that you should be able to check the Apache2 version with:

apache2 -v

As usual in Debian based distributions the config files are located in /etc/apache2

In this folder we have the following files and folders:

  • apache2.conf
    • General Apache2 config file
  • conf-available
    • Contains additional config files which can be added to the default config
  • conf-enabled
    • Contains symbolic links to the “conf-available” which to show which configs should be enabled and which not.
  • envvars
    • Environment config for the Apache2
  • mods-available
    • same as conf-available just for Apache2 modules
  • mods-enabled
    • same as conf-enabled just for Apache2 modules
  • ports.conf
    • contains settings on which ports the server should listen
  • sites-available
    • same as conf-available just for vHost configs
  • sites-enabled
    • same as conf-available just for vHost configs
  • magic
    • Rules on which MIME-Types should be detected

Standard Apache vHost config

Folder: /etc/apache2/sites-available/<filename>

<VirtualHost *:80>
    ServerName www.domain.com
    ServerAlias domain.com
    DocumentRoot /var/www/html/docroot
</VirtualHost>

Explenation

  • VirtualHost *:80
    • Listen on the port 80
  • ServerName www.domain.com
    • Use this configuration if the hostname is “www.domain.com”
  • ServerAlias domain.com
    • Also use this configuration if the hostname is “domain.com”
  • DocumentRoot /var/www/html/docroot
    • Show the content of the folder “/var/www/html/docroot”

Activate a new vHost config

ln -s /etc/apache2/sites-available/<Dateiname> /etc/apache2/sites-enabled/<Dateiname>

Test the new Apache vHost config

apache2ctl configtest

Restart the Apache webserver

apache2ctl restart

Add a PHP-Handler

In Ubuntu/Debian based systems its pretty easy since its just a package which you can install via apt-get.

sudo apt-get install -y php7.2-curl php7.2-gd php7.2-json php7.2-mbstring php7.2-mcrypt libapache2-mod-php7.2

The most important package here is “libapache2-mod-php7.2” which connects the globally installed PHP and the Apache webserver. See mod_php for more details.

Instead of php7.2 you can of course use php7.3 or any other future PHP version.

If everything went well you can restart the webserver with apachectl restart and create a new file /var/www/html/info.php with the following content:

<?php phpinfo();

After that you can open your webbrowser and open the following URL

http://<Server-IP>/info.php

Now you should see a PHP-Info page.

NGINX

NGINX is a free to use, open source webserver which has been invented in 1999 by Igor Sysoev and ist currently the number 2 of the most used webservers (February 2020 – see „What is a webserver?„).

Why do you need any other webserver then Apache?

NGINX was created primarily because of the “C10k Problem”.

The “C10k Problem” is about optimizing and handling a large amount of network sockets.

C => Connection | 10k => 10.000

NGINX with its event based, async architecture was the base for future “high performance” server software and was therefore declared the fastest available webserver.

Installation and configuration

As mentioned in the apache installation process this can be different on your used linux distribution but the following example is based on a Debian/Ubuntu distribution.

sudo apt-get install nginx

After that you can check the installed version with the following command:

nginx -v

Similar to other linux packages the configuration files for NGINX are located in /etc/nginx.

Here we can find the following files and folders:

  • conf.d/
    • Directory to add additional configuration files
  • fastcgi.conf & fastcgi_params
    • Sets default parameters for FastCGI requests
  • mime.types
    • Mapping for file endings and their associated MIME-Type
  • modules-available/
    • Contains modules which are available to include into NGINX
  • modules-enabled/
    • Contains symlinks to the modules (in modules-available) which should actually be “activated” in NGINX
  • nginx.conf
    • Base NGINX-Config File
    • Here all activated modules, configurations and vHosts are being loaded
  • proxy_params
    • Default Proxy parameters can be found here
  • scgi_params
    • Sets default SCGI parameters
  • sites-available/
    • Contains configuration files for each vHost
  • sites-enabled/
    • Same as “modules-enabled” just for vHost files
  • snippets/
    • Contains snippets for how to use PHP files over FastCGI and how to use a self signed HTTPS certificate

Not so important files

  • uwsgi_params
  • koi-utf
    • Mapping for “KOI8-R” (Cyrillic) to “UTF-8” characters
  • koi-win
    • Mapping for “KOI8-R” (Cyrillic) to “Windows-1251” characters
  • win-utf
    • Mapping for “Windows-1251” to “UTF-8” characters

Create a new vHost

In /etc/nginx/sites-available there is a “default” file

This basically contains the following (comments (#) have been removed)

server {
	listen 80 default_server; # IPv4 listen on Port 80
	listen [::]:80 default_server; # IPv6 listen on Port 80

	root /var/www/html; # Absolute path to Document-Root

	# Set default files to show when accessing the website
	# Add index.php to the list if you are using PHP
	index index.html index.htm index.nginx-debian.html;

	server_name mydomain.at; # the domain name

	location / {
		# First attempt to serve request as file, then
		# as directory, then fall back to displaying a 404.
		try_files $uri $uri/ =404;
	}

	# pass PHP scripts to FastCGI server
	#
	#location ~ \.php$ {
	#	include snippets/fastcgi-php.conf;
	#
	#	# With php-fpm (or other unix sockets):
	#	fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
	#	# With php-cgi (or other tcp sockets):
	#	fastcgi_pass 127.0.0.1:9000;
	#}

	# deny access to .htaccess files, if Apache's document root
	# concurs with nginx's one
	#
	#location ~ /\.ht {
	#	deny all;
	#}
}

If you want to activate your configuration you have to create a “Symlink” in the folder /etc/nginx/sites-enabled.

sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default

Or if you want to deactivate an already activated config:

unlink /etc/nginx/sites-enabled/default

Before you restart the NGINX server you should check your config syntax if you got any typos or wrong syntax:

nginx -t

After each change in the NGINX config you have to realod the NGINX to see the effect of the config chage

systemctl reload nginx

Source: https://www.nginx.com/resources/glossary/nginx/

Free HTTPS certificate with Lets Encrypt

Valid HTTPS certificates are necessary to show the lock beside of your URL.

Types of Certificates

  • Self-Signed Certificates
    • Everyone can create certificates and implement them in their websites.
    • But these will NOT be marked as “secure” and therefore don’t get a lock symbol.
    • To use these certificates every client has to mark these certificate as “secure” once in their system..
  • Wildcard Certificate
    • Are often used for Subdomain Certificates so you don’t have to create a new certificate when you add a new subdomain to your server.
    • Example: *.pfiff.me
  • Domain Validation (DV)
    • Here the applicant is being checked if he is actually the owner of the given domain. No other information like the identity of the company is being checked and therefore no other information is being displayed in the certificate.
    • Wildcard-Certificates are possible!
    • This is the default approach for LetsEncrypt certificates.
  • Organization Validation (OV)
    • Basically the same as a DV but additionally the companies name and locations is being checked. These information are then being displayed in the certificate.
    • Wildcard-Certificates are possible!
  • Extended Validation (EV)
    • Same as OV, but it’s a more detailed check of the company.
    • In the past with an EV certificate the name of the company was being displayed next to the lock but this feature has been removed from every currently common browser (Chrome, Firefox, Safari etc.). Therefore its debatable if this option is a good choice.
      See https://www.troyhunt.com/extended-validation-certificates-are-dead/
    • Wildcard-Certificates are NOT possible!

What is a “Certificate Authority” (CA)?

A “Certificate Authority” (short CA) is necessary to check certificates with specific, predefined methods and if that validation has been successful to sign these certificates. Additionally on each validation the certificate a date will be set when the certificate is being expired. After that period the certificate has to be checked and signed again.

In the past it was only possible to buy HTTPS certificates. But since 2015 “LetsEncrypt” or better the python script “Certbot” which makes this process way easier.

LetsEncrypt uses the “Domain-Vaildation” (DV) process. Therefore everyone, who is the owner a domain and shows the domain to a server, a signed LetsEncrypt certificate can be generated for this server to use this domain.

What is Certbot?

Certbot ist a software, which automatically generated and manages HTTPS certificates.

Currently there are even plugins available for the 2 mostly used webservers (Apache and NGINX), which even handle the configuration for the vhost files to include the certificate.

The easiest way to use certbot is to go to https://certbot.eff.org, enter your OS and your uses webserver and follow the instructions.

Usually a certificate generated by Certbot/LetsEncrypt is valid for 90 days. But Certbot checks the status of each installed certificate regularly and automatically renews certificates which will get expired in 30 days.

Therefore you only need to create and install your certificates once and everything else is handled by certbot.

Informations of a certificate

Source: https://www.digitalocean.com/community/tutorials/a-comparison-of-let-s-encrypt-commercial-and-private-certificate-authorities-and-self-signed-ssl-certificates