PHP

PHP (PHP Hypertext preprocessor, a recursive acronym) is still a quite used language to dynamically generate HTML pages. You can use

  • 🍛 Expressions (e.g. dynamic values such as the current date)
  • 🔁 Loops (for, foreach, while...)
  • 🔀 Branching (if, else)
  • 🥬 Fetch data from a database/... to render a page
  • 🦄 Handle HTML forms that are submitted

Where to learn?

To install PHP, you will need a web server such as Apache. ➡️ Well-known versions of PHP are: 5.6, 7.*, and 8 (latest, since 2020).

Additionally, you may install extensions later:

$ sudo apt install php-curl
$ sudo apt install php-intl
$ sudo apt install php-ext-dom
$ sudo apt install php-dom
$ sudo apt install php-mbstring
$ sudo apt install php-mysql

You can enable extensions on the server level, or you can edit each php.ini manually. On Linux, you can also use sudo phpenmod xxx.

$ sudo phpenmod mod_rewrite       # enable module
$ sudo systemctl restart apache2  # restart web server

➡️ Installed mods are stored in: /etc/php/X.Y/mods-available.


Get started

Inside a .php file, most of the time, there are both HTML and PHP.

<p><?php echo "Hello, World!"; ?></p>

The PHP blocks (<?php ?>, <?= ?>) will be executed, and an HTML file will be generated and sent to the client.

<p>Hello, World!</p>

👉 As we may have a lot of PHP blocks to echo something, the block <?= ?> was introduced as an implicit echo.

<p><?= "Hello, World!" ?></p>

➡️ Sometimes, you have files with only PHP inside. In such a scenario, you can omit the closing ?>.

➡️ You can omit ; in PHP blocks with only one instruction.

Imports another PHP

You can import another PHP script using require (raise an error if the import failed) or import (don't mind failed includes). Most of the time, you want a script to be imported once, which could be done with xxx_once.

include "test.php";
include_once "test.php";
require "test.php";
require_once "test.php";

Basics

Declare a variable

Types are implicit. The name starts with $.

$n = 5; // integer | int
$n = 5.0; // float
$n = "5"; // string
$n = '5'; // string
$n = null; // null
$n = true; // boolean | bool
$n = false; // boolean 

You can also declare constants either using const, or define. The latter should be used when the right-hand value is a non-const variable.

// same as define ('XXX', 5);
const XXX = 5;
echo XXX;

➡️ Before PHP7.3, constants were case-insensitive, meaning we could call the constant above with "Xxx" or "xxx" and it worked.

Finally, you can declare global variables. This can be used to allow functions to use external variables, or to nicely pass variables between scripts.

global $g_someVariable; // declare
$g_someVariable = 150; // assign

// later
global $g_someVariable; // declare (load existing value)
echo $g_someVariable; // output: 150

Types

Types are: boolean, integer, double, string, array, object, resource, and NULL. You can check/get the type using:

$someVariableType = gettype($someVariable); // get
if (is_integer($someVariable)) {} // check

➡️ PHP uses "Type juggling" meaning the variable is converted based on the context (ex: inside an if statement, it is converted to a boolean...)

Comments

// a comment
/* a comment */
# a comment

Print some text

To print some text, you can use echo. You can also use var_dump, and print_r, but they are mostly used for debugging and objects/arrays.

echo "XXX: $variable"; // ✅ $variable is replaced
echo 'XXX: $variable'; // ❌ not replaced
echo "XXX: a{$variable}z"; // ✅ $variable is replaced
echo "XXX: a".$variable."z"; // ✅ concatenation
// advanced 🧐: like in Bash, you can dynamically
// generate the name of the variable you want to call
echo "XXX: ${"variable"}";
var_dump($variable); // print debug information

Operators

Every basic operator is available.

  • arithmetic: +, -, *, /, +=, -=, *=, /=, ** (power), % (mod), ^ (xor)
  • comparison: >, >=, <=, <
  • booleans: &&, ||, ! (not)
  • booleans: and, or (use &&/||)

For equality operators, there are two: === and !== (strict, same value and same type) or == and !=/<> (same value after casting to the same type).

👉️ Always use STRICT equality: 0 == 'true' && 0 == 'false' is true in PHP, so 0 is both 'true' and 'false'.

Existence

PHP does not check that variables were declared before executing some code. isset check that the variable exists, while empty check both that the variable exists, and that it is not "empty".

if (!isset($someInputFromAUser)) { /* error */ }
if (!empty($someInputFromAUser)) { /* error */ }
$yyy = $xxx ?? 0; // or, use default values if not set

➡️ You can destroy a variable with unset.


Control-flow structures

If PHP, there are two kinds of structures. Those using braces, and those using starting (:) and ending tags (endXXX).

This is because you may use HTML inside structures, and you may close and re-open a PHP block, but it doesn't look clean using braces:

<?php if (...) { ?>
<p>This HTML element is only shown when the IF is true</p>
<?php } ?>

Branching

if ($condition) { /* code */ }
elseif ($condition) { /* code */ }
else { /* code */ }

if ($condition): /* ... */ endif;
switch ($number) {
    case 0: /* code */ break;
    case 1: case 2: /* code */ break;
    default: /* code */ break;
}

switch ($number): /* code */ endswitch;

There is also the ternary operator (inline if statement)

$result = $condition ? $value_if_true : $value_if_false;

Since PHP 8, a match statement was introduced.

$result = match ($variable) {
    'value1' => 'result1',
    'value2', 'value3' => 'result2',
    default => 'default result',
};

Loops

// for
for ($i = 0; $i < 10; $i++) { /* code */ }
for ($i = 0; $i < 10; $i++): /* code */ endfor;

// while
while ($condition){ /* code */ }
while ($condition): endwhile;

// do while
do { /* code */ } while ($condition);

➡️ You can use break to exit a loop, and continue to move to the next iteration. You can also add a number: break 2;.


Functions

Since PHP 7.*, you can type parameters and the return type.

function sum(int $a, int $b) : int {
    return $a + $b;
}

echo sum(5, 6);

➡️ You must use int/bool/... instead of integer/boolean/....

➡️ Since PHP 7.4, you can use void.

Passage by reference

In PHP, arguments are passed by value. If you want a function to modify a variable, you can pass it by reference.

function sum(int $a, int $b, &$res) : void {
    $res = $a + $b;
}

$res = -1; // declare it
sum(5, 6, $res); // fill it
echo $res; // output: 11

Default values

You can give default values to parameters

function sum(int $a, int $b = 10) : int {}

Nullable and multi-types arguments

You can declare a nullable parameter or return type

function sum(int $a, ?int $b) : ?int {}

Since PHP 8.0, we can explicitly declare multiple types with |:

function sum(int $a, int | null $b) : int | null {}

Anonymous functions/Closures

$x = function () { echo "XXX"; };
$x();

Anonymous functions can "use" variables from the outside.

$a = 10;
$x = function () use ($a) { echo $a; };
$x(); // output: 10

Arrays

Arrays in PHP can be multi-types, and can be either indexed using integers (starting from 0), or they can be associative, meaning a string is associated with a value.

Creation

$array = array(5, "10", 15.0);
$array = [5, "10", 15.0];
$associative = ["five"=>5, "ten" => "10"];
$multi_dimensional = [[5], [10]]; // [0][0] == 5

Add/Edit values

// add a value
$array[] = 20; // at the end
// modify a value
$array[0] = 2; // change the first value
$associative["five"] = "5"; // change a value

Get values

echo $array[1]; // print 10
echo $associative["ten"]; // print 10

Print array

print_r($array);
var_dump($array);
// pretty print array
echo "<pre>".print_r($array)."</pre>"

Iterate arrays

// index are "0", "1"...
// values are 2, "10", 15.0
foreach ($array as $value) {}
foreach ($array as $index => $value) {}
// values are "5", "10"
// keys are "five" "ten"
foreach ($associative as $value) {}
foreach ($associative as $key => $value) {}

Useful methods

  • count($array): number of elements
  • is_array($array): true if this is an array
  • You can deconstruct an array: [$v1, $v2, $v3] = $array;

Classes and objects

A class is a template to create objects. A class called table could define attributes such as cost, and methods such as "sell".

Objects are concrete instances of the class, in which we give value to attributes, such as TableXXX: cost = 10.

Basic syntax

class Person {
    public string $name = "John Doe";
    public int $age = 0;

    function doNothing() : void {}
}

Instantiation is done by calling the new keyword

$somePerson = new Person();
$somePerson->doNothing(); // call a method
$name = $somePerson->name; // access an attribute

Constructors

We usually fill attributes in a magic method called the constructor.

class Person {
    public function __construct(string $name, int $age)
    {
        // internally, use "$this->" to access attributes
        $this->name = $name;
        $this->age = $age;
    }
}
$janeDoe = new Person("Jane Doe", 18);

Getters/Setters

We usually use private attributes, and add getters/setters when needed (if both are present and trivial, you may use public).

class Person {
    public function getName(): string
    { return $this->name; }

    public function setAge(int $age): void
    { $this->age = $age; }
}

➡️ Attributes and functions can be public/private/protected. Aside from static members, there is no default visibility.

Static

Classes can have methods/attributes using static. As a reminder, static attributes are shared between every object and static methods can be called without having to create an object.

class SomeClass {
    public const XXX = 18; // public by default
    public static $YYY = 5; // public by default
    private const ZZZ = 2;
    
    public static function zzz() : int
    { return self::XXX; } // or SomeClass::
}
$xxx = SomeClass::XXX;
$yyy = SomeClass::$YYY;
$zzz = SomeClass::zzz();

Inheritance

A class can inherit one class (public/protected methods/attributes), and zero or more interfaces. Interfaces are basically custom types.

// some interface with one function
interface IPerson {
    function fun1();
}
// ➡️ Use "," to implement more interfaces
class Person implements IPerson {
    function fun1() {}
}
// 👉 Example with a normal class
class DoeFamilyMember extends Person {}

You can create an abstract class, meaning a class that has 0 or more methods that were not implemented.

abstract class IncompleteImplementation {
    abstract function fun2();
}
class ConcreteClass extends IncompleteImplementation {
    function fun2() {}
}

Traits

A class can privately inherit from multiple "trait" classes.

trait Parent1 { function xxx() {} } // public
trait Parent2 { function yyy() {} } // public
class Child { // inherit public/protected
    use Parent1; // from Parent1
    use Parent2; // and Parent2
}

Error handling

PHP error handling is mainly based on exceptions. A programmer can raise a signal called an exception when something unexpected, mostly an error, occurs. This signal will stop a function/script and go back to the caller until it's caught. If no one catches and handles it, then it may be displayed to the user (according to the server configuration).

For debugging purposes, you can add these two lines to display all errors to the "user" (a.k.a. you, the programmer).

// usually at the start of the script
error_reporting(E_ALL);
ini_set("display_errors", 1);

To raise an exception, use throw

throw new \Exception("a message");

To catch and handle a function raising an exception, use try-catch

try {
    // code that may raise an exception
} catch (\Exception $e) {
    // print the message
    echo "Error: ".$e->getMessage();
    // pretty print of the stacktrace + message
    echo "<pre>".var_dump($e)."</pre>"
}

➡️ An alternative to "finally" could be register_shutdown_function.

It's also possible to raise user-level warnings/errors/notices/...

trigger_error("a message", E_USER_DEPRECATED);

Website specific

Redirection

You can use header to redirect a user using PHP. Note that you must call it before rendering anything, otherwise use an HTML redirect. Also, note that you must call exit right after, otherwise, the remaining PHP code will still be executed before redirecting.

header("Location: index.php");
exit();

Sessions

Sessions can be used to keep information about a client on the server during their browsing session, such as their logging status.

All data is stored in an array called $_SESSION and available after starting the session.

session_start(); // 👉 session_destroy to delete it

Example: add the username if logged in, remove it on logout.

$_SESSION['username'] = 'root'; // on login
unset($_SESSION['username']); // on logout
// use isset before accessing a field
if (isset($_SESSION['username'])) {}

Handle a form

Forms are handled in PHP. HTML forms have an attribute action which takes a URL. This URL can lead to a PHP script that will handle the form. According to the method specified in the HTML form, values will be stored either in $_POST or $_GET.

➡️ Use a var_dump($_POST)/var_dump($_GET) to ensure that the array is filled as you expect it to be before processing.

✅ You should ensure that all values are here using isset and that they are not empty using empty. You should also do some server-side verification of the type (ex: ctype_digit(str) for integers) and the value:

if (filter_var($email, FILTER_VALIDATE_EMAIL) !== false){
    // this should be a valid email
} // otherwise, handle the error

👉 You may also trim(...) strings to remove leading/trailing spaces.

Convenient functions

  • nl2br: convert \n to <br/>

Additional notes

JSON

$decoded = json_decode("{}", true);
$json = json_encode($decoded)

Date and time

$date = date("Y-m-d");
$time = time();

Heredoc Strings

You can use Heredoc strings.

$some_variable = <<<EOF
You can use both ' and "
EOF;

Hash passwords

🙅‍♀️ Don't store/use cleartext passwords, hash your passwords!

// (usually saved in a database)
$hash = password_hash("apassword", PASSWORD_DEFAULT);

// from a hash and a password
// you can check if this password is matching the hash
$boolean = password_verify("apassword", $hash);

👻 To-do 👻

Stuff that I found, but never read/used yet.

  • spaceship (<=>) operator PHP 8 to compare two values (0, 1, -1)
  • type TypeName = integer; (or int before PHP8)
  • @file_get_contents(xxx)/file_get_contents/file_put_contents
  • __FILE__ (full path to file?) and __DIR__
  • enums
  • str functions (str_length, substr, str_split...)
  • ??=
  • htmlescapechars, htmlspecialchars
  • array_reduce
  • php -m
  • phpDoc
  • phpstan
  • php spark serve
$ php -ini | head
$ sudo nano /etc/php/v.v/mods-available/xxx.ini # debian
$ sudo phpenmod xxx
# https://pecl.php.net/package/xxx
  • see generators (yield/...)
  • magic methods
  • namespaces (PHP 5.3+), and use
  • unions return types
  • annotations (#[Pure])
  • basically, what's new since PHP 8.0
  • roundcube
  • php-fig
  • cburch
$files = glob('./*/*.txt');
$dirIterator = new RecursiveDirectoryIterator($path);
foreach (new RecursiveIteratorIterator($dirIterator) as $file) {
    // $file->isFile()
    // $file->getExtension()
    // $file->getPathname()
    // $file->getMTime()
}

arsort($array);
array_slice(array_keys($array), 0, $limit);
reset($xxx)

$this->$hidden

return new class implements XXXInterface {};

sprintf("%06d", 33)