feat: restructure docs into "chapters" (#12)

* feat(docker, k8s): create containers folder and kubernetes notes
This commit is contained in:
Marcello 2023-03-13 11:33:51 +00:00
parent b1cb858508
commit 2725e3cb70
92 changed files with 777 additions and 367 deletions

View file

@ -0,0 +1,109 @@
# Composer & Autoloading
## Autoloading
The function [spl_autoload_register()](https://www.php.net/manual/en/function.spl-autoload-register.php) allows to register a function that will be invoked when PHP needs to load a class/interface defined by the user.
In `autoload.php`:
```php
# custom function
function autoloader($class) {
# __DIR__ -> path of calling file
# $class -> className (hopefully same as file)
# if class is in namespace $class -> Namespace\className (hopefully folders mirror Namespace)
$class = str_replace("\\", DIRECTORY_SEPARATOR, $class); # avoid linux path separator issues
$fileName = sprintf("%s\\path\\%s.php", __DIR__, $class);
# or
$filename = sprintf("%s\\%s.php", __DIR__, $class); # if class is in namespace
if (file_exists($fileName)) {
include $fileName;
}
}
spl_autoload_register('autoloader'); // register function
```
In `file.php`:
```php
require "autoload.php";
# other code
```
> **Note**: will fuck up if namespaces exists.
### Multiple Autoloading
It's possible to resister multiple autoloading functions by calling `spl_autoload_register()` multiple times.
```php
# prepend adds the function at the start of the queue
# throws selects if errors in loading throw exceptions
spl_autoload_register(callable $func, $throw=TRUE, $prepend=FALSE);
spl_autoload_functions() # return a list of registered functions.
```
## [Composer](https://getcomposer.org/)
Open Source project for dependency management and autoloading of PHP libraries and classes.
Composer uses `composer.json` to define dependencies with third-party libraries.
Libraries are downloaded through [Packagist](https://packagist.org/) and [GitHub](https://github.com/).
In `composer.json`:
```json
{
"require": {
"php": ">=7.0",
"monolog/monolog": "1.0.*"
}
}
```
### Installing Dependencies
In the same folder of `composer.json` execute `composer install`.
Composer will create:
- `vendor`: folder containing all requested libraries
- `vendor\autoload.php`: file for class autoloading
- `composer.lock`
In alternative `composer require <lib>` will add the library to the project and create a `composer.json` if missing.
> **Note**: to ignore the php version use `composer <command> --ignore-platform-reqs`
### Updating Dependencies
To update dependencies use `composer update`. To update only the autoloading section use `composer dump-autoload`.
### [Autoloading Project Classes](https://getcomposer.org/doc/04-schema.md#autoload)
[PSR-4 Spec](https://www.php-fig.org/psr/psr-4/)
Composer can also autoload classes belonging to the current project. It's sufficient to add the `autoload` keyword in the JSON and specify the path and autoload mode.
```json
{
"autoload": {
"psr-4": {
"RootNamespace": "src/",
"Namespace\\": "src/Namespace/",
},
"file": [
"path/to/file.php",
...
]
}
}
```

View file

@ -0,0 +1,102 @@
# Databases in PHP
## PHP Data Objects ([PDO][pdo])
[pdo]: https://www.php.net/manual/en/book.pdo.php
PDO is the PHP extension for database access through a single API. It supports various databases: MySQL, SQLite, PostgreSQL, Oracle, SQL Server, etc.
### Database Connection
```php
$dsn = "mysql:dbname=<dbname>;host=<ip>";
$user="<db_user>";
$password="<db_password>";
try {
$pdo = new PDO($dsn, $user, $password); # connect, can throw PDOException
} catch (PDOException $e) {
printf("Connection failed: %s\n", $e->getMessage()); # notify error
exit(1);
}
```
### Queries
To execute a query it's necessary to "prepare it" with *parameters*.
```php
# literal string with markers
$sql = 'SELECT fields
FROM tables
WHERE field <operator> :marker'
$stmt = $pdo->prepare($sql, $options_array); # returns PDOStatement, used to execute the query
$stmt->execute([ ':marker' => value ]); # substitute marker with actual value
# fetchAll returns all matches
$result = $stmt->fetchAll(); # result as associative array AND numeric array (PDO::FETCH_BOTH)
$result = $stmt->fetchAll(PDO::FETCH_ASSOC); # result as associative array
$result = $stmt->fetchAll(PDO::FETCH_NUM); # result as array
$result = $stmt->fetchAll(PDO::FETCH_OBJ); # result as object of stdClass
$result = $stmt->fetchAll(PDO::FETCH_CLASS, ClassName::class); # result as object of a specific class
```
### Parameter Binding
```php
# bindValue
$stmt = pdo->prepare(sql);
$stmt->bindValue(':marker', value, PDO::PARAM_<type>);
$stmt->execute();
# bindParam
$stmt = pdo->prepare(sql);
$variable = value;
$stmt->bindParam(':marker', $variable); # type optional
$stmt->execute();
```
### PDO & Data Types
By default PDO converts all results into strings since it is a generic driver for multiple databases.
Its possible to disable this behaviour setting `PDO::ATTR_STRINGIFY_FETCHES` and `PDO::ATTR_EMULATE_PREPARES` as `false`.
> **Note**: `FETCH_OBJ` abd `FETCH_CLASS` return classes and don't have this behaviour.
```php
pdo->setAttribute()
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $pdo->prepare($sql);
$stmt->execute([':marker' => value]);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
```
### PDO Debug
```php
$stmt = $pdo->prepare($sql);
$stmt->execute([':marker' => value]);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->debugDumpParams(); # print the SQL query that has been sent to the database
```
## [SQLite3](https://www.php.net/manual/en/book.sqlite3.php)
```php
$db = SQLite3("db_file.sqlite3"); // connection
$stmt = $db->prepare("SELECT fields FROM tables WHERE field <operator> :marker"); // prepare query
$stmt->bindParam(":marker", $variable); // param binding
$result = $stmt->execute(); // retrieve records
$result->finalize(); // close result set, recommended calling before another execute()
$records = $results->fetchArray(SQLITE3_ASSOC); // extract records as array (false if no results)
```
> **Note**: Result set objects retrieved by calling `SQLite3Stmt::execute()` on the same statement object are not independent, but rather share the same underlying structure. Therefore it is recommended to call `SQLite3Result::finalize()`, before calling `SQLite3Stmt::execute()` on the same statement object again.

View file

@ -0,0 +1,78 @@
# Dependency Injection
Explicit definition of a class dependencies with the injection through the constructor or *getters*/*setters*.
```php
class Foo
{
public function __construct(PDO $pdo) // depends on PDO
{
$this->pdo = $pdo;
}
}
```
## Dependency Injection Container
The **Dependency Injection Container** (DIC) allow to archive all the dependencies in a single `Container` class. Some offer automatic resolution of the dependencies.
## [PHP-DI](https://php-di.org/)
The dependency injection container for humans. Installation: `composer require php-di/php-di`
- **Autowire** functionality: the ability of the container to create and inject the dependency automatically.
- Use of [Reflection](https://www.php.net/manual/en/intro.reflection.php)
- Configuration of the container through annotations & PHP code.
```php
class Foo
{
private $bar;
public function __construct(Bar $bar) // depends on Bar
{
$this->bar = $bar;
}
}
class Bar{}
$container = new DI\Container(); // DI Container
$foo = $container->get('Foo'); // get instance of Foo (automatic DI of Bar)
```
### DIC Configuration
```php
// Foo.php
class Foo
{
public function __construct(PDO $pdo) // depends on PDO
{
$this->pdo = $pdo;
}
}
```
```php
// config.php
use Psr\Container\ContainerInterface;
// config "primitive" dependencies (dependency => construct & return)
return [
'dsn' => 'sqlite:db.sq3',
PDO::class => function(ContainerInterface $c) {
return new PDO($c->get('dsn'));
},
...
];
```
```php
$builder = new \DI\ContainerBuilder();
$builder->addDefinitions("config.php"); // load config
$container = $builder->build(); // construct container
$cart = $container->get(Foo::class); // Instantiate & Inject
```
> **Note**: `get("className")` requires the explicit definition of `className` in the config file. `get(ClassName::class)` does not.

925
docs/languages/php/php.md Normal file
View file

@ -0,0 +1,925 @@
# PHP
[PHP Docs](https://www.php.net/docs.php)
```php
declare(strict_types=1); # activates variable type checking on function arguments
# single line comment
//single line comment
/* multi line comment */
```
## Include, Require
```php
include "path\\file.php"; # import an external php file, E_WARNING if fails
include_once "path\\file.php"; # imports only if not already loaded
require "path\\file.php"; # import an external php file, E_COMPILE_ERROR if fails
require_once "path\\file.php"; # imports only if not already loaded
```
### Import configs from a file with `include`
In `config.php`:
```php
//config.php
//store configuration options in associative array
return [
setting => value,
setting = value,
]
```
```php
$config = include "config.php"; // retrieve config and store into variable
```
## Namespace
[PSR-4 Spec](https://www.php-fig.org/psr/psr-4/)
```php
namespace Foo\Bar\Baz; # set namespace for all file contents, \ for nested namespaces
use <PHP_Class> # using a namespace hides standard php classes (WHY?!?)
# namespace for only a block of code
namespace Foo\Bar\Baz {
function func() {
# coded here
}
};
Foo\Bar\Baz\func(); # use function from Foo\Bar\Baz without USE instruction
use Foo\Bar\Baz\func; # import namespace
func(); # use function from Foo\Bar\Baz
use Foo\Bar\Baz\func as f; # use function with an alias
f(); # use function from Foo\Bar\Baz
use Foo\Bar\Baz as fbb # use namespace with alias
fnn\func(); # use function from Foo\Bar\Baz
```
## Basics
```php
declare(strict_types=1); # activates type checking
# single line comment
//single line comment
/* multi line comment */
```
### Screen Output
```php
echo "string"; # string output
echo 'string\n'; # raw string output
printf("format", $variables); # formatted output of strings and variables
sprintf("format", $variables); # return formatted string
```
### User Input
```php
$var = readline("prompt");
# if readline is not installed
if (!function_exists('readline')) {
function readline($prompt)
{
$fh = fopen('php://stdin', 'r');
echo $prompt;
$userInput = trim(fgets($fh));
fclose($fh);
return $userInput;
}
}
```
## Variables
```php
$variableName = value; # weakly typed
echo gettype(&variable); # output type of variable
var_dump($var); # prints info of variable (bit dimension, type & value)
```
### Integers
```php
&max = PHP_INT_MAX; # max value for int type -> 9223372036854775807
&min = PHP_INT_MIN; # min value for int type -> -9223372036854775808
&bytes = PHP_INT_SIZE; # bytes for int type -> 8
&num = 255; # decimal
&num = 0b11111111; # binary
&num = 0377; # octal
&num = 0xff; # hexadecimal
```
### Double
```php
$a = 1.234; // 1.234
$b = 1.2e3; // 1200
$c = 7E-10; // 0.0000000007
```
### Mathematical Operators
| Operator | Operation |
| -------- | -------------- |
| `-` | Subtraction |
| `*` | Multiplication |
| `/` | Division |
| `%` | Modulo |
| `**` | Exponentiation |
| `var++` | Post Increment |
| `++var` | Pre Increment |
| `var--` | Post Decrement |
| `--var` | Pre Decrement |
### Mathematical Functions
- `sqrt($x)`
- `sin($x)`
- `cos($x)`
- `log($x)`
- `round($x)`
- `floor($x)`
- `ceil($x)`
## Strings
A string is a sequence of ASCII characters. In PHP a string is an array of characters.
### String Concatenation
```php
$string1 . $string2; # method 1
$string1 .= $string2; # method 2
```
### String Functions
```php
strlen($string); # returns the string length
strpos($string, 'substring'); # position of substring in string
substr($string, start, len); # extract substring of len from position start
strtoupper($string); # transform to uppercase
strtolower($string); # transform to lowercase
explode(delimiter, string); # return array of substrings
$var = sprintf("format", $variables) # construct and return a formatted string
```
## Constants
```php
define ('CONSTANT_NAME', 'value')
```
### Magic Constants `__NAME__`
- `__FILE__`: script path + script filename
- `__DIR__`: file directory
- `__LINE__`: current line number
- `__FUNCTION__`: the function name, or {closure} for anonymous functions.
- `__CLASS__`: name of the class
- `__NAMESPACE__`: the name of the current namespace.
## Array
Heterogeneous sequence of values.
```php
$array = (sequence_of_items); # array declaration and valorization
$array = [sequence_of_items]; # array declaration and valorization
# index < 0 selects items starting from the last
$array[index]; # access to the items
$array[index] = value; # array valorization (can add items)
$array[] = value; # value appending
```
### Multi Dimensional Array (Matrix)
Array of array. Can have arbitrary number of nested array
```php
$matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
```
### Array Printing
Single instruction to print whole array is ``
```php
$array = [1, 2, 3];
print_r($array); # print all the array values
```
### Array Functions
```php
count($array); # returns number of items in the array
array_sum($array) # sum of the array value
sort($array); # quick sort
in_array($item, $array); // check if item is in the array
array_push($array, $item); // append item to the array
unset($array[index]); # item (or variable) deletion
# array navigation
current();
key();
next();
prev();
reset();
end();
# sorting
sort($array, $sort_flags="SORT_REGULAR");
array_values($array); # regenerates the array fixing the indexes
list($array1 [, $array2, ...]) = $data; # Python-like tuple unpacking
```
### Associative Arrays
Associative arrays have a value as an index. Alternative names are _hash tables_ or _dictionaries_.
```php
$italianDay = [
'Mon' => 'Lunedì',
'Tue' => 'Martedì',
'Wed' => 'Mercoledì',
'Thu' => 'Giovedì',
'Fri' => 'Venerdì',
'Sat' => 'Sabato',
'Sun' => 'Domenica'
];
$italianDay["Mon"]; # evaluates to Lunedì
```
## Conditional Instructions
### Conditional Operators
| Operator | Operation |
| ----------- | ------------------------ |
| $a `==` $b | value equality |
| $a `===` $b | value & type equality |
| $a `!=` $b | value inequality |
| $a `<>` $b | value inequality |
| $a `!==` $b | value or type inequality |
| $a `<` $b | less than |
| $a `>` $b | greater than |
| $a `<=` $b | less or equal to |
| $a `>=` $b | greater or equal to |
| $a `<=>` $b | spaceship operator |
With `==` a string evaluates to `0`.
### Logical Operators
| Operator | Example | Result |
| -------- | ----------- | ---------------------------------------------------- |
| `and` | `$a and $b` | TRUE if both `$a` and `$b` are TRUE. |
| `or` | `$a or $b` | TRUE if either `$a` or `$b` is TRUE. |
| `xor` | `$a xor $b` | TRUE if either `$a` or `$b` is TRUE, but _not both_. |
| `not` | `!$a` | TRUE if `$a` is _not_ TRUE. |
| `and` | `$a && $b` | TRUE if both `$a` and `$b` are TRUE. |
| `or` | `$a || $b` | TRUE if either `$a` or `$b` is TRUE. |
### Ternary Operator
```php
condition ? result_if_true : result_if_false;
condition ?: result_if_false;
```
### NULL Coalesce
```php
$var1 = $var2 ?? value; # if variable == NULL assign value, otherwise return value of $var2
# equivalent to
$var1 = isset($var2) ? $var2 : value
```
### Spaceship Operator
```php
$a <=> $b;
# equivalent to
if $a > $b
return 1;
if $a == $b
return 0;
if $a < $b
return -1;
```
### `If` - `Elseif` - `Else`
```php
if (condition) {
# code here
} elseif (condition) {
# code here
} else {
# code here
}
if (condition) :
# code here
elseif (condition):
# code here
else:
# code here
endif;
```
### Switch Case
```php
# weak comparison
switch ($var) {
case value:
# code here
break;
default:
# code here
}
# strong comparison
switch (true) {
case $var === value:
# code here
break;
default:
# code here
}
```
### Match Expression (PHP 8)
`match` can return values, doesn't require break statements, can combine conditions, uses strict type comparisons and doesn't do any type coercion.
```php
$result = match($input) {
0 => "hello",
'1', '2', '3' => "world",
};
```
## Loops
### For, Foreach
```php
for (init, condition, increment){
# code here
}
for (init, condition, increment):
# code here
endfor;
foreach($sequence as $item) {
# code here
}
foreach($sequence as $item):
# code here
endforeach;
# foreach on dicts
foreach($sequence as $key => $value) {
# code here
}
```
### While, Do-While
```php
while (condition) {
# code here
}
while (condition):
# code here
endwhile;
do {
# code here
} while (condition);
```
### Break, Continue, exit()
`break` stops the iteration.
`continue` skips one cycle of the iteration.
`exit()` terminates the execution of any PHP code.
## Functions
[Function Docstring](https://make.wordpress.org/core/handbook/best-practices/inline-documentation-standards/php/)
Parameters with default values are optional in the function call and must be the last ones in the function declaration. Return type is optional if type checking is disabled.
```php
declare(strict_types=1); # activates type checking
/**
* Summary.
*
* Description.
*
* @since x.x.x
*
* @see Function/method/class relied on
* @link URL
* @global type $varname Description.
* @global type $varname Description.
*
* @param type $var Description.
* @param type $var Optional. Description. Default.
* @return type Description.
*/
function functionName (type $parameter, $parameter = default_value): Type
{
# code here
return <expression>;
}
```
### Void function
```php
function functionName (type $parameter, $parameter = default_value): Void
{
# code here
}
```
### Passing a parameter by reference (`&$`)
```php
function functionName (type &$parameter): Type
{
# code here
return <expression>;
}
```
### Variable number of parameters, variadic operator (`...`)
```php
function functionName (type $parameter, ...$args): Type
function functionName (type $parameter, type ...$args): Type
{
# code here
return <expression>;
}
```
### Nullable parameters
```php
function functionName (?type $parameter): ?Type
{
# code here
return <expression>;
}
```
## Anonymous Functions (Closure)
```php
# declaration and assignment to variable
$var = function (type $parameter) {
# code here
};
$var($arg);
```
### Use Operator
```php
# use imports a variable into the closure
$foo = function (type $parameter) use ($average) {
# code here
}
```
### Union Types (PHP 8)
**Union types** are a collection of two or more types which indicate that _either_ one of those _can be used_.
```php
public function foo(Foo|Bar $input): int|float;
```
### Named Arguments (PHP 8)
Named arguments allow to pass in values to a function, by specifying the value name, to avoid taking their order into consideration.
It's also possible to skip optional parameters.
```php
function foo(string $a, string $b, ?string $c = null, ?string $d = null) { /* … */ }
foo(
b: 'value b',
a: 'value a',
d: 'value d',
);
```
## Object Oriented Programming
### Scope & Visibility
`public` methods and attributes are visible to anyone (_default_).
`private` methods and attributes are visible only inside the class in which are declared.
`protected` methods and attributes are visible only to child classes.
`final` classes cannot be extended.
### Class Declaration & Instantiation
```php
# case insensitive
class ClassName
{
const CONSTANT = value; # public by default
public $attribute; # null by default if not assigned
public Type $attribute; # specifying the type is optional, it will be enforced if present
# class constructor
public function __construct(value)
{
$this->attribute = value
}
public getAttribute(): Type
{
return $this->attribute;
}
public function func(): Type
{
# code here
}
}
$object = new ClassName; # case insensitive (CLASSNAME, ClassName, classname)
$object->attribute = value;
$object->func();
$object::CONSTANT;
$var = $object; # copy by reference
$var = clone $object # copy by value
$object instanceof ClassName // check type of the object
```
### Static classes, attributes & methods
Inside static methods it's impossible to use `$this`.
A static variable is unique for the class and all instances.
```php
class ClassName {
public static $var;
public static function func(){
//code here
}
public static function other_func(){
//code here
self::func();
}
}
ClassName::func(); // use static function
$obj = new ClassName();
$obj::$var; // access to the static variable
```
### [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection)
Parameters of the dependency can be modified before passing the required class to the constructor.
```php
class ClassName
{
private $dependency;
public function __construct(ClassName requiredClass)
{
$this->dependency = requiredClass; # necessary class is passed to the constructor
}
}
```
### Inheritance
If a class is defined `final` it can't be extended.
If a function is declared `final` it can't be overridden.
```php
class Child extends Parent
{
public __construct() {
parent::__construct(); # call parent's method
}
}
```
### Abstract Class
Abstract classes cannot be instantiated;
```php
abstract class ClassName
{
# code here
}
```
### Interface
An interface is a "contract" that defines what methods the implementing classes **must** have and implement.
A class can implement multiple interfaces but there must be no methods in common between interface to avoid ambiguity.
```php
interface InterfaceName {
// it is possible to define __construct
// function has no body; must be implements in the class that uses the interface
public function functionName (parameters) : Type; // MUST be public
}
class ClassName implements InterfaceName {
public function functionName(parameters) : Type {
//implementation here
}
}
```
### Traits
`Traits` allows the reutilization of code inside different classes without links of inheritance.
It can be used to mitigate the problem of _multiple inheritance_, which is absent in PHP.
In case of functions name conflict it's possible to use `insteadof` to specify which function to use. It's also possible to use an _alias_ to resolve the conflicts.
```php
trait TraitName {
// code here
}
class ClassName {
use TraitName, {TraitName::func() insteadof OtherTrait}, { func() as alias }; # can use multiple traits
# code here
}
```
### Anonymous Classes
```php
$obj = new ClassName;
$obj->method(new class implements Interface {
public function InterfaceFunc() {
// code here
}
});
```
## Serialization & JSON
```php
$serialized = serialize($obj); # serialization
$obj = unserialize($serialized); # de-serialization
$var = json_decode(string $json, bool $associative); # Takes a JSON encoded string and converts it into a PHP variable.ù
$json = json_encode($value); # Returns a string containing the JSON representation of the supplied value.
```
## Files
### Read/Write on Files
```php
file(filename); // return file lines in an array
// problematic with large files (allocates memory to read all file, can fill RAM)
file_put_contents(filename, data); // write whole file
file_get_contents(filename); // read whole file
```
## Regular Expressions
```php
preg_match('/PATTERN/', string $subject, array $matches); # returns 1 if the pattern matches given subject, 0 if it does not, or FALSE if an error occurred
# $matches[0] = whole matched string
# $matches[i] = i-th group of the regex
```
## Hashing
Supported hashing algrithms:
- `md2`, `md4`, `md5`
- `sha1`, `sha224`, `sha256`, `sha384`, `sha512/224`, `sha512/256`, `sha512`
- `sha3-224`, `sha3-256`, `sha3-384`, `sha3-512`
- `ripemd128`, `ripemd160`, `ripemd256`, `ripemd320`
- `whirlpool`
- `tiger128,3`, `tiger160,3`, `tiger192,3`, `tiger128,4`, `tiger160,4`, `tiger192,4`
- `snefru`, `snefru256`
- `gost`, `gost-crypto`
- `adler32`
- `crc32`, `crc32b`, `crc32c`
- `fnv132`, `fnv1a32`, `fnv164`, `fnv1a64`
- `joaat`
- `haval128,3`, `haval160,3`, `haval192,3`, `haval224,3`, `haval256,3`, `haval128,4`, `haval160,4`, `haval192,4`, `haval224,4`, `haval256,4`, `haval128,5`, `haval160,5`, `haval192,5`, `haval224,5`, `haval256,5`
```php
hash($algorithm, $data);
```
### Password Hashes
`password_hash()` is compatible with `crypt()`. Therefore, password hashes created by `crypt()` can be used with `password_hash()`.
Algorithms currently supported:
- **PASSWORD_DEFAULT** - Use the _bcrypt_ algorithm (default as of PHP 5.5.0). Note that this constant is designed to change over time as new and stronger algorithms are added to PHP.
- **PASSWORD_BCRYPT** - Use the **CRYPT_BLOWFISH** algorithm to create the hash. This will produce a standard `crypt()` compatible hash using the "$2y$" identifier. The result will always be a 60 character string, or FALSE on failure.
- **PASSWORD_ARGON2I** - Use the **Argon2i** hashing algorithm to create the hash. This algorithm is only available if PHP has been compiled with Argon2 support.
- **PASSWORD_ARGON2ID** - Use the **Argon2id** hashing algorithm to create the hash. This algorithm is only available if PHP has been compiled with Argon2 support.
**Supported options for PASSWORD_BCRYPT**:
- **salt** (string) - to manually provide a salt to use when hashing the password. Note that this will override and prevent a salt from being automatically generated.
If omitted, a random salt will be generated by password_hash() for each password hashed. This is the intended mode of operation.
**Warning**: The salt option has been deprecated as of PHP 7.0.0. It is now preferred to simply use the salt that is generated by default.
- **cost** (integer) - which denotes the algorithmic cost that should be used. Examples of these values can be found on the crypt() page.
If omitted, a default value of 10 will be used. This is a good baseline cost, but you may want to consider increasing it depending on your hardware.
**Supported options for PASSWORD_ARGON2I and PASSWORD_ARGON2ID**:
- **memory_cost** (integer) - Maximum memory (in kibibytes) that may be used to compute the Argon2 hash. Defaults to PASSWORD_ARGON2_DEFAULT_MEMORY_COST.
- **time_cost** (integer) - Maximum amount of time it may take to compute the Argon2 hash. Defaults to PASSWORD_ARGON2_DEFAULT_TIME_COST.
- **threads** (integer) - Number of threads to use for computing the Argon2 hash. Defaults to PASSWORD_ARGON2_DEFAULT_THREADS.
```php
password_hash($password, $algorithm); # create a new password hash using a strong one-way hashing algorithm.
password_verify($password, $hash); # Verifies that a password matches a hash
```
## Errors
Types of PHP errors:
- **Fatal Error**: stop the execution of the program.
- **Warning**: generated at runtime, does not stop the execution (non-blocking).
- **Notice**: informative errors or messages, non-blocking.
```php
$a = new StdClass()
$a->foo(); // PHP Fatal Error: foo() does not exist
```
```php
$a = 0;
echo 1/$a; // PHP Warning: Division by zero
```
```php
echo $a; // PHP Notice: $a undefined
```
### Error Reporting
[PHP Error Constants](https://www.php.net/manual/en/errorfunc.constants.php)
Its possible to configure PHP to signal only some type of errors. Errors below a certain levels are ignored.
```php
error_reporting(E_<type>); // set error report threshold (for log file)
// does not disable PARSER ERROR
ini_set("display_errors", 0); // don't display any errors on stderr
ini_set("error_log", "path\\error.log"); // set log file
```
### Triggering Errors
```php
// generate E_USER_ errors
trigger_error("message"); // default type: E_USER_NOTICE
trigger_error("message", E_USER_<Type>);
trigger_error("Deprecated Function", E_USER_DEPRECATED);
```
### [Writing in the Log File](https://www.php.net/manual/en/function.error-log.php)
It's possible to use log files unrelated to the php log file.
```php
error_log("message", 3, "path\\log.log"); // write log message to a specified file
//example
error_log(sprintf("[%s] Error: _", date("Y-m-d h:i:s")), 3, "path\\log.log")
```
## Exception Handling
PHP offers the possibility to handle errors with the _exception model_.
```php
try {
// dangerous code
} catch(ExceptionType1 | ExceptionType2 $e) {
printf("Errore: %s", $e->getMessage());
} catch(Exception $e) {
// handle or report exception
}
throw new ExceptionType("message"); // throw an exception
```
All exceptions in PHP implement the interface `Throwable`.
```php
Interface Throwable {
abstract public string getMessage ( void )
abstract public int getCode ( void )
abstract public string getFile ( void )
abstract public int getLine ( void )
abstract public array getTrace ( void )
abstract public string getTraceAsString ( void )
abstract public Throwable getPrevious ( void )
abstract public string __toString ( void )
}
```
### Custom Exceptions
```php
/**
* Define a custom exception class
*/
class CustomException extends Exception
{
// Redefine the exception so message isn't optional
public function __construct($message, $code = 0, Exception $previous = null) {
// some code
// make sure everything is assigned properly
parent::__construct($message, $code, $previous);
}
// custom string representation of object
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
public function customFunction() {
echo "A custom function for this type of exception\n";
}
}
```

View file

@ -0,0 +1,146 @@
# Templates with Plates
## Template
To separate HTML code and PHP code it's possible to use **templates** with markers for variable substitution.
Variables are created in the PHP code and are passed to the template in the **rendering** phase.
The server transmits only the `index.php` file to the user. The php file renders the templates as needed.
```html
<html>
<head>
<title><?= $this->e($title)?></title>
</head>
<body>
<?= $this->section('content')?>
</body>
</html>
```
## [Plates](https://platesphp.com/)
Plates is a template engine for PHP. A template engine permits to separate the PHP code (business logic) from the HTML pages.
Installation through composer: `composer require league/plates`.
```php
# index.php
require "vendor/autoload.php";
use League\Plates\Engine;
$templates = new Engine("path\\to\\templates");
echo $templates->render("template_name", [
"key_1" => "value_1",
"key_2" => "value_2"
]);
```
```php
# template.php
<html>
<head>
<title><?= $key_1?></title>
</head>
<body>
<h1>Hello <?= $key_2 ?></h1>
</body>
</html>
```
Variables in the template are created through an associative array `key => value`. The key (`"key"`) becomes a variable (`$key`) in the template.
### Layout
It's possible to create a layout (a model) for a group of pages to make that identical save for the contents.
In a layout it's possible to create a section called **content** that identifies content that can be specified at runtime.
> **Note**: Since only the template has the data passed eventual loops have to be done there.
```php
# index.php
require 'vendor/autoload.php';
use League\Plates\Engine;
$template = new Engine('/path/to/templates');
echo $template->render('template_name', [ "marker" => value, ... ]);
```
```php
# template.php
# set the layout used for this template
<?php $this->layout("layout_name", ["marker" => value, ...]) ?> # pass values to the layout
# section contents
<p> <?= $this->e($marker) ?> </p>
```
```php
# layout.php
<html>
<head>
<title><?= $marker ?></title>
</head>
<body>
<?= $this->section('content')?> # insert the section
</body>
</html>
```
### Escape
It's necessary to verify that the output is in the necessary format.
Plates offers `$this->escape()` or `$this->e()` to validate the output.
In general the output validation allows to prevent [Cross-Site Scripting][owasp-xss] (XSS).
[owasp-xss]: https://owasp.org/www-community/attacks/xss/
### Folders
```php
# index.php
$templates->addFolder("alias", "path/to/template/folder"); # add a template folder
echo $templates->render("folder::template"); # use a template in a specific folder
```
### Insert
It's possible to inject templates in a layout or template. It is done by using the `insert()` function.
```php
# layout.php
<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?php $this->insert('template::header') ?> # insert template
<?= $this->section('content')?> # page contents
<?php $this->insert('template::footer') ?> # insert template
</body>
</html>
```
### Sections
It's possible to insert page contest from another template with the `section()` function.
The content to be inserted must be surrounded with by the `start()` and `stop()` functions.
```php
# template.php
<?php $this->start("section_name") ?> # start section
# section contents (HTML)
<?php $this->stop() ?> # stop section
# append to section is existing, create if not
<?php $this->push("section_name") ?>
# section contents (HTML)
<?php $this->end() ?>
```

View file

@ -0,0 +1,34 @@
# PSR-7
## [PSR-7](https://www.php-fig.org/psr/psr-7/)
Standard of the PHP Framework Interop Group that defines common interfaces for handling HTTP messages.
- `Psr\Http\Message\MessageInterface`
- `Psr\Http\Message\RequestInterface`
- `Psr\Http\Message\ResponseInterface`
- `Psr\Http\Message\ServerRequestInterface`
- `Psr\Http\Message\StreamInterface`
- `Psr\Http\Message\UploadedFileInterface`
- `Psr\Http\Message\UriInterface`
Example:
```php
// empty array if not found
$header = $request->getHeader('Accept');
// empty string if not found
$header = $request->getHeaderLine('Accept');
// check the presence of a header
if (! $request->hasHeader('Accept')) {}
// returns the parameters in a query string
$query = $request->getQueryParams();
```
### Immutability
PSR-7 requests are *immutable* objects; a change in the data will return a new instance of the object.
The stream objects of PSR-7 are *not immutable*.

View file

@ -0,0 +1,97 @@
# REST API with Simple-MVC
## Routing (Example)
```php
// config/route.php
return [
[ 'GET', '/api/user[/{id}]', Controller\User::class ],
[ 'POST', '/api/user', Controller\User::class ],
[ 'PATCH', '/api/user/{id}', Controller\User::class ],
[ 'DELETE', '/api/user/{id}', Controller\User::class ]
];
```
## Controller (Example)
```php
public class UserController implements ControllerInterface
{
public function __construct(UserModel $user)
{
$this->userModel = $user;
// Set the Content-type for all the HTTP methods
header('Content-type: application/json');
}
// method dispatcher
public function execute(ServerRequestInterface $request)
{
$method = strtolower($request->getMethod());
if (!method_exists($this, $method)) {
http_response_code(405); // method not exists
return;
}
$this->$method($request);
}
public function get(ServerRequestInterface $request)
{
$id = $request->getAttribute('id');
try {
$result = empty($id)
? $this->userModel->getAllUsers()
: $this->userModel->getUser($id);
} catch (UserNotFoundException $e) {
http_response_code(404); // user not found
$result = ['error' => $e->getMessage()];
}
echo json_encode($result);
}
public function post(ServerRequestInterface $request)
{
$data = json_decode($request->getBody()->getContents(), true);
try {
$result = $this->userModel->addUser($data);
} catch (InvalidAttributeException $e) {
http_response_code(400); // bad request
$result = ['error' => $e->getMessage()];
} catch (UserAlreadyExistsException $e) {
http_response_code(409); // conflict, the user is not present
$result = ['error' => $e->getMessage()];
}
echo json_encode($result);
}
public function patch(ServerRequestInterface $request)
{
$id = $request->getAttribute('id');
$data = json_decode($request->getBody()->getContents(), true);
try {
$result = $this->userModel->updateUser($data, $id);
} catch (InvalidAttributeException $e) {
http_response_code(400); // bad request
$result = ['error' => $e->getMessage()];
} catch (UserNotFoundException $e) {
http_response_code(404); // user not found
$result = ['error' => $e->getMessage()];
}
echo json_encode($result);
}
public function delete(ServerRequestInterface $request)
{
$id = $request->getAttribute('id');
try {
$this->userModel->deleteUser($id);
$result = ['result' => 'success'];
} catch (UserNotFoundException $e) {
http_response_code(404); // user not found
$result = ['error' => $e->getMessage()];
}
echo json_encode($result);
}
}
```

View file

@ -0,0 +1,146 @@
# [SimpleMVC](https://github.com/ezimuel/simplemvc) Mini-Framework
SimpleMVC is a micro MVC framework for PHP using [FastRoute][fastroute], [PHP-DI][php-di], [Plates][plates] and [PHP-DI][php-di] standard for HTTP messages.
This framework is mainly used as tutorial for introducing the Model-View-Controller architecture in modern PHP applications.
[php-di]: https://php-di.org/
[fastroute]: https://github.com/nikic/FastRoute
[psr7]:https://github.com/Nyholm/psr7
[plates]: https://platesphp.com/
## Installation
```ps1
composer create-project ezimuel/simple-mvc
```
## Structure
```txt
|- config
| |- container.php --> DI Container Config (PHP-DI)
| |- route.php --> routing
|- public
| |- img
| |- index.php --> app entry-point
|- src
| |- Model
| |- View --> Plates views
| |- Controller --> ControllerInterface.php
|- test
| |- Model
| |- Controller
```
### `index.php`
```php
<?php
declare(strict_types=1);
chdir(dirname(__DIR__));
require 'vendor/autoload.php';
use DI\ContainerBuilder;
use FastRoute\Dispatcher;
use FastRoute\RouteCollector;
use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7Server\ServerRequestCreator;
use SimpleMVC\Controller\Error404;
use SimpleMVC\Controller\Error405;
$builder = new ContainerBuilder();
$builder->addDefinitions('config/container.php');
$container = $builder->build();
// Routing
$dispatcher = FastRoute\simpleDispatcher(function(RouteCollector $r) {
$routes = require 'config/route.php';
foreach ($routes as $route) {
$r->addRoute($route[0], $route[1], $route[2]);
}
});
// Build the PSR-7 server request
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator(
$psr17Factory, // ServerRequestFactory
$psr17Factory, // UriFactory
$psr17Factory, // UploadedFileFactory
$psr17Factory // StreamFactory
);
$request = $creator->fromGlobals();
// Dispatch
$routeInfo = $dispatcher->dispatch(
$request->getMethod(),
$request->getUri()->getPath()
);
switch ($routeInfo[0]) {
case Dispatcher::NOT_FOUND:
$controllerName = Error404::class;
break;
case Dispatcher::METHOD_NOT_ALLOWED:
$controllerName = Error405::class;
break;
case Dispatcher::FOUND:
$controllerName = $routeInfo[1];
if (isset($routeInfo[2])) {
foreach ($routeInfo[2] as $name => $value) {
$request = $request->withAttribute($name, $value);
}
}
break;
}
$controller = $container->get($controllerName);
$controller->execute($request);
```
### `route.php`
```php
<?php
use SimpleMVC\Controller;
return [
[ 'GET', '/', Controller\Home::class ],
[ 'GET', '/hello[/{name}]', Controller\Hello::class ],
[ "HTTP Verb", "/route[/optional]", Controller\EndpointController::class ]
];
```
### `container.php`
```php
<?php
use League\Plates\Engine;
use Psr\Container\ContainerInterface;
return [
'view_path' => 'src/View',
Engine::class => function(ContainerInterface $c) {
return new Engine($c->get('view_path'));
}
// PHP-DI configs
];
```
### `ControllerInterface.php`
Each controller *must* implement this interface.
```php
<?php
declare(strict_types=1);
namespace SimpleMVC\Controller;
use Psr\Http\Message\ServerRequestInterface;
interface ControllerInterface
{
public function execute(ServerRequestInterface $request);
}
```

View file

@ -0,0 +1,230 @@
# PHP Unit Test
## Installation & Configuration
### Dev-Only Installation
```ps1
composer require --dev phpunit/phpunit
```
```json
"require-dev": {
"phpunit/phpunit": "<version>"
}
```
### Config
PHPUnit can be configured in a XML file called `phpunit.xml`:
```xml
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuit name="App\\Tests">
<directory>./test<directory>
</testsuit>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src</directory>
</whitelist>
</filter>
</phpunit>
```
## Testing
### Test Structure
**PHPUnit** tests are grouped in classes suffixed with `Test`. Each class *extends* `PHPUnit\Framework\TestCase`.
A test is a method of a *test class* prefixed with `test`.
PHPUnit is executed from the command line with `vendor/bin/phpunit --colors`.
```php
namespace App;
class Filter
{
public function isEmail(string $email): bool
{
// @todo implement
}
}
```
```php
namespace App\Test;
use PHPUnit\Framework\TestCase;
use App\Filter;
class FilterTest extends TestCase
{
public function testValidMail()
{
$filter = new Filter();
$this->assertTrue($filter->isEmail("foo@bar.com"));
}
public function testInvalidEmail()
{
$filter = new Filter();
$this->assertFalse($filter->idEmail("foo"));
}
}
```
### [PHPUnit Assertions](https://phpunit.readthedocs.io/en/9.3/assertions.html)
- `asseretTrue()`: verifies that the element is true
- `assertFalse()`: verifies that the element is false
- `assertEmpty()`: verifies that the element is empty
- `assertEquals()`: verifies that the two elements are equal
- `assertGreaterThan()`: verifies that the element is greater than ...
- `assertContains()`: verifies that the element is contained in an array
- `assertInstanceOf()`: verifies that the element is an instance of a specific class
- `assertArrayHasKey(mixed $key, array $array)`: verify that a specific key is in the array
### [PHPUnit Testing Exceptions](https://phpunit.readthedocs.io/en/9.3/writing-tests-for-phpunit.html#testing-exceptions)
```php
public function testAggiungiEsameException(string $esame)
{
$this->expectException(Exception::class);
$this->expectExceptionMessage("exception_message");
// execute code that should throw an exception
}
// https://github.com/sebastianbergmann/phpunit/issues/2484#issuecomment-648822531
public function testExceptionNotThrown()
{
$exceptionWasThrown = false;
try
{
// code that should succeed
}
catch (EsameException $e)
{
$exceptionWasThrown = true;
}
$this->assertFalse($exceptionWasThrown);
}
// same as
/**
* @doesNotPerformAssertions
*/
public function testNoExceptions(string $esame)
{
// code that should succeed (exceptions will make the test fail)
}
```
### Test Setup & Teardown (Example)
```php
class ClassTest extends TestCase
{
// initialize the test
public function setUp(): void
{
file_put_contents("/tmp/foo", "Test")
}
// reset the test
public function tearDown(): void
{
unlink("/tmp/foo")
}
public function testFoo()
{
// use temp file
}
}
```
> **Note**: `setUp()` and `tearDown()` are called *before* and *after* each test method.
### Data Provider
```php
class DataTest extends TestCase
{
/**
* @dataProvider provider
*/
public function testAdd($a, $b, $expected)
{
$this->assertEquals($expected, $a + $b);
}
// test receives array contents as input
public function provider()
{
// must return array of arrays
return [
[0, 0, 0],
[0, 1, 1]
];
}
// test receives array of arrays as input
public function provideArrayOfArrays()
{
return [
[
[
[0, 0, 0],
[0, 1, 1]
]
]
];
}
}
```
### Mock Objects
```php
class UnitTest extends TestCase
{
public function setUp()
{
// names of mock are independent from tested class variables
$this->mock = $this->createMock(ClassName::class); // create a mock object of a class
$this->returned = $this->createMock(ClassName::class); // mock of returned object
$this->mock->method("methodName") // simulate method on mock
->with($this->equalTo(param), ...) // specify input params (one param per equalTo)
->willReturn($this->returned); // specify return value
}
public function testMethod()
{
$this->mock
->method("methodName")
->with($this->equalTo($arg)) // arg passed to the method
->willReturn(value); // actual return value for THIS case
// or
->will($this->throwException(new Exception())); // method will throw exception
// assertions
}
}
```
### Code Coverage (needs [XDebug](https://xdebug.org/))
```ps1
vendor/bin/phpunit --coverage-text # code coverage analysis in the terminal
```

152
docs/languages/php/web.md Normal file
View file

@ -0,0 +1,152 @@
# PHP for the Web
## PHP Internal Web Server
Command Line Web Server in PHP, useful in testing phase. Limited since handles only one request at a time. **Do not use in production**.
```ps1
PHP -S <ip:post> # start web server
PHP -S <ip:post> -t /path/to/folder # execute in specified folder at specified address
PHP -S <ip:post> file.php # redirect requests to single file
```
## HTTP Methods
Handling of HTTP requests happens using the following global variables:
- `$_SERVER`: info on request headers, version, URL path and method (dict)
- `$_GET`: parameters of get request (dict)
- `$_POST`: parameters of post request (dict)
- `$_COOKIE`
- `$_FILES`: file to send to web app.
### `$_FILES`
```html
<!-- method MUST BE post -->
<!-- must have enctype="multipart/form-data" attribute -->
<form name="<name>" action="file.php" method="POST" enctype="multipart/form-data">
<input type="file" name="photo" />
<input type="submit" name="Send" />
</form>
```
Files in `$_FILES` are memorized in a system temp folder. They can be moved with `move_uploaded_file()`
```php
if (! isset($_FILES['photo']['error'])) {
http_response_code(400); # send a response code
echo'<h1>No file has been sent</h1>';
exit();
}
if ($_FILES['photo']['error'] != UPLOAD_ERR_OK) {
http_response_code(400);
echo'<h1>The sent file is invalid</h1>';
exit();
}
$path = '/path/to/' . $_FILES['photo']['name'];
if (! move_uploaded_file($_FILES['photo']['tmp_name'], $path)) {
http_response_code(400);
echo'<h1>Error while writing the file</h1>';
exit();
}
echo'<h1>File successfully sent</h1>';
```
### `$_SERVER`
Request Header Access:
```php
$_SERVER["REQUEST_METHOD"];
$_SERVER["REQUEST_URI"];
$_SERVER["SERVER_PROTOCOL"]; // HTTP Versions
$_SERVER["HTTP_ACCEPT"];
$_SERVER["HTTP_ACCEPT_ENCODING"];
$_SERVER["HTTP_CONNECTION"];
$_SERVER["HTTP_HOST"];
$_SERVER["HTTP_USER_AGENT"];
// others
```
### `$_COOKIE`
[Cookie Laws](https://www.iubenda.com/it/cookie-solution)
[Garante Privacy 8/5/2014](http://www.privacy.it/archivio/garanteprovv201405081.html)
All sites **must** have a page for the consensus about using cookies.
**Cookies** are HTTP headers used to memorize key-value info *on the client*. They are sent from the server to the client to keep track of info on the user that is visiting the website.
When a client receives a HTTP response that contains `Set-Cookie` headers it has to memorize that info and reuse them in future requests.
```http
Set-Cookie: <cookie-name>=<cookie-value>
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<seconds>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
```
Anyone can modify the contents of a cookie; for this reason cookies **must not contain** *personal or sensible info*.
When a client has memorized a cookie, it is sent in successive HTTP requests through the `Cookie` header.
```http
Cookie: <cookie-name>=<cookie-value>
```
[PHP setcookie docs](https://www.php.net/manual/en/function.setcookie.php)
```php
setcookie (
string $name,
[ string $value = "" ],
[ int $expire = 0 ], // in seconds (time() + seconds)
[ string $path = "" ],
[ string $domain = "" ],
[ bool $secure = false ], // use https
[ bool $httponly = false ] // accessible only through http (no js, ...)
)
// example: memorize user-id 112 with 24h expiry for site example.com
setcookie ("User-id", "112", time() + 3600*24, "/", "example.com");
// check if a cookie exists
if(isset($_COOKIE["cookie_name"])) {}
```
### [$_SESSION](https://www.php.net/manual/en/ref.session.php)
**Sessions** are info memorized *on the server* associated to the client that makes an HTTP request.
PHP generates a cookie named `PHPSESSID` containing a *session identifier* and an *hash* generated from `IP + timestamp + pseudo-random number`.
To use the session it's necessary to recall the function `session_start()` at the beginning of a PHP script that deals with sessions.
After starting the session information in be saved in the `$_SESSION` array.
```php
$_SESSION["key"] = value; // save data in session file (serialized data)
unset($_SESSION["key"]); // delete data from the session
session_unset(); # remove all session data
session_destroy(); # destroy all of the data associated with the current session.
# It does not unset any of the global variables associated with the session, or unset the session cookie.
```
Session data is be memorized in a file by *serializing* `$_SESSION`. Files are named as `sess_PHPSESSID` in a folder (`var/lib/php/sessions` in Linux).
It's possible to modify the memorization system of PHP serialization variables by:
- modifying `session.save_handler` in `php.ini`
- writing as personalized handler with the function `session_set_save_handler()` and/or the class `SessionHandler`
## PHP Web Instructions
`http_response_code()` is used to return an HTTP response code. If no code is specified `200 OK` is returned.
`header("Location: /route")` is used to make a redirect to another UTL.