mirror of
https://github.com/m-lamonaca/dev-notes.git
synced 2025-06-08 10:47:13 +00:00
feat: restructure docs into "chapters" (#12)
* feat(docker, k8s): create containers folder and kubernetes notes
This commit is contained in:
parent
b1cb858508
commit
2725e3cb70
92 changed files with 777 additions and 367 deletions
109
docs/languages/php/composer.md
Normal file
109
docs/languages/php/composer.md
Normal 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",
|
||||
...
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
102
docs/languages/php/database.md
Normal file
102
docs/languages/php/database.md
Normal 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.
|
78
docs/languages/php/dependency-injection.md
Normal file
78
docs/languages/php/dependency-injection.md
Normal 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
925
docs/languages/php/php.md
Normal 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";
|
||||
}
|
||||
}
|
||||
```
|
146
docs/languages/php/plates-templating.md
Normal file
146
docs/languages/php/plates-templating.md
Normal 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() ?>
|
||||
```
|
34
docs/languages/php/psr-7.md
Normal file
34
docs/languages/php/psr-7.md
Normal 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*.
|
97
docs/languages/php/simple-mvc/rest-api.md
Normal file
97
docs/languages/php/simple-mvc/rest-api.md
Normal 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);
|
||||
}
|
||||
}
|
||||
```
|
146
docs/languages/php/simple-mvc/simple-mvc.md
Normal file
146
docs/languages/php/simple-mvc/simple-mvc.md
Normal 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);
|
||||
}
|
||||
```
|
230
docs/languages/php/unit-tests.md
Normal file
230
docs/languages/php/unit-tests.md
Normal 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
152
docs/languages/php/web.md
Normal 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.
|
Loading…
Add table
Add a link
Reference in a new issue