1. 1loc JS to nloc PHP

Il existe un site nommé 1loc.dev qui référence actuellement 275 snippets JS dont la spécificité est de tenir en une seule ligne.

Même si l'intéret est parfois assez limité, voici quelques-uns de ces snippets en PHP ; les différentes fonctions sont écrites sur plusieurs lignes pour rendre le code plus lisible.

1.1. Array

1.1.1 Cast a value as an array

function castArray($value) 
{
    return is_array($value) ? $value : [$value];
}
// Examples
castArray(1);
castArray([1, 2, 3]);


Notes : avec le typage fort, l'utilisation de ce genre de fonction ne devrait jamais avoir lieu.

1.1.2 Check if an array is empty

function isEmpty($arr)
{
    return empty($arr);
}
// Examples
isEmpty([]);            // true
isEmpty([1, 2, 3]);     // false

NB : bien que je suis pour le fait d'encapsuler certaines fonctions natives comme json_encode, htmlentities, locale etc... Ici, la question de la pertinence de le faire se pose un peu.
Lorsque l'on fait du typage fort, le count() suffit à tester si le nombre d'éléments du tableau est égal à 0. L'utilisation du empty est la source de beaucoup de bugs car empty('') fonctionne, empty([]) aussi, empty(0) aussi... et cela n'a pas vraiment de sens.

Je ne suis pas non plus d'accord avec la version JS qui ressemblerait à ceci en PHP :

function isEmpty($arr)
{
    return !is_array($arr) || count($arr) === 0;
}

Ici, la chaine "" n'est pas un tableau, et pourtant la méthode retourne true, alors que l'on souhaite savoir si le tableau est vide donc que la variable est à la fois un tableau et aussi que le nombre de ses élèments est supérieur à 0.
Donc la version que je préconise serait plutôt celle-ci :

function isEmpty($arr)
{
    return is_array($arr) && count($arr) === 0;
}

Ici, cette fonction vérifie à la fois le type de la variable et le fait que le tableau soit vide, c'est une version assez stricte et juste.

A noter que dans une version de PHP qui permet le typage fort, on écrirait directement ceci :

function isEmpty(array $arr)
{
     return count($arr) === 0;
}

Ce qui peut par contre potentiellement être un problème si le type de la variable n'est pas vérifié en amont (par exemple, c'est un enfant d'enfant d'un contenu json non maitrisé par un fournisseur).

1.1.3 Clone an array

Le terme 'clone' étant déjà un mot clef du langage PHP, je le renomme cloneArray pour cette proposition.

function cloneArray($array)
{
    return $array;
}
// Usage (lol)
$clonedArray = cloneArray([231]); // [231]

Alors oui, c'est assez inutile car par défaut on manipule des valeurs et non des références donc il suffit de copier la variable de façon classique $b = $a.

1.1.4 Compare two arrays

Cela sous entend la règle suivante : si tous les élements de deux tableaux sont identiques, alors les deux tableaux le sont.
Le code JS du site 1loc.dev propose deux solutions :
* la première est de convertir les deux tableaux en json et de comparer les chaines de caractères.
* la seconde est de vérifier d'une part si la taille des deux tableaux est la même puis de vérifier chaque élement.

En PHP, on peut directement utiliser le triple égal qui vérifie que l'ordre et le type de chaque élement du premier tableau correspond au second.

function isEqual($arrayA, $arrayB)
{
     return $arrayA === $arrayB;
}
// Examples
isEqual([1, 2, 3], [1, 2, 3]);      // true
isEqual([1, 2, 3], [1, '2', 3]);    // false

1.1.5 Compare two arrays regardless of order

Ici, il suffit dans un premier temps de trier les deux tableaux puis ensuite de les comparer.

function isEqual($arrayA, $arrayB)
{
    sort($arrayA);
    sort($arrayB);

    return $arrayA === $arrayB;
}
// Examples
isEqual([1, 2, 3], [1, 2, 3]);      // true
isEqual([1, 2, 3], [1, 3, 2]);      // true
isEqual([1, 2, 3], [1, '2', 3]);    // false

1.1.6 Convert an array of objects to a single object

Ici, c'est un peu particulier car la structure Object du JS est différente de celle du PHP ; en PHP, on continuerait de manipuler un tableau.
La fonction suivante ne gère pas les cas d'erreurs (notamment si la clef $key n'existe pas).

function toObject($arrayOfObjects, $key)
{
    $object = [];
    foreach ($arrayOfObjects as $subObject) {
        $object[$subObject[$key]] = $subObject;
    }

    return $object;
}

// Example
$result = toObject(
    [
        [ "id" => '1', "name" => 'Alpha', "gender" => 'Male' ],
        [ "id" => '2', "name" => 'Bravo', "gender" => 'Male' ],
        [ "id" => '3', "name" => 'Charlie', "gender" => 'Female' ],
    ],
    'id'
);

/*
[
    1 => ['id' => '0', 'name' => 'Alpha', 'gender' => 'Male',],
    2 => ['id' => '2','name' => 'Bravo','gender' => 'Male',],
    3 => ['id' => '3','name' => 'Charlie','gender' => 'Female',],
]
*/

1.1.7 Convert an array of strings to numbers

Ici, on peut utiliser la fonction array_map pour appliquer le cast float sur les différentes valeurs du tableau (à noter que la règle n'indique pas si on manipule que des valeurs entières).

function toNumbers($array) 
{
    return array_map(function($value) {
        return (float) $value;
    ], $array);
}

// Example
toNumbers(['2', '3', '4']);     // [2, 3, 4]

1.1.8 Count by the properties of an array of objects

Enfin une fonction un peu intéressante : compter le nombre d'élements en fonction de leur récurrence dans un tableau.

function countBy($array, $column)
{
    $uniqValues = [];

    foreach ($array as $subArray) {
        if (!isset($uniqValues[$subArray[$column]])) {
            $uniqValues[$subArray[$column]] = 0;
        }
        ++$uniqValues[$subArray[$column]];
    }

    return $uniqValues;
}

// Example
countBy([
    [ 'branch' =>  'audi',  'model' =>  'q8',  'year' =>  '2019' ],
    [ 'branch' =>  'audi',  'model' =>  'rs7',  'year' =>  '2020' ],
    [ 'branch' =>  'ford',  'model' =>  'mustang',  'year' =>  '2019' ],
    [ 'branch' =>  'ford',  'model' =>  'explorer',  'year' =>  '2020' ],
    [ 'branch' =>  'bmw',  'model' =>  'x7',  'year' =>  '2020' ],
], 'branch');

/*
array(3) {
    ["audi"] => int(2)
    ["ford"] => int(2)
    ["bmw"]  => int(1)
}
*/

1.1.9 Count the occurrences of a value in an array

Voici une première proposition :

function countOccurrences($array, $value)
{
    $occurences = 0;
    foreach ($array as $element) {
        $occurences += $element === $value ? 1 : 0; 
    }

    return $occurences;
}
// Examples
countOccurrences([2, 1, 3, 3, 2, 3], 2);                // 2
countOccurrences(['a', 'b', 'a', 'c', 'a', 'b'], 'a');  // 3

Voici une seconde proposition qui utilise la fonction PHP array_count_values.

function countOccurrences($array, $value)
{
    $arrayCountValues = array_count_values($array);
    $occurences = isset($arrayCountValues[$value]) ? $arrayCountValues[$value] : 0;

    return $occurences;
}
// Examples
countOccurrences([2, 1, 3, 3, 2, 3], 2);                // 2
countOccurrences(['a', 'b', 'a', 'c', 'a', 'b'], 'a');  // 3

1.1.10 Count the occurrences of array elements

Ici, la fonction array_count_values fait déjà le travail.

function countOccurrences($array) {
    return array_count_values($array);
}
// Examples
countOccurrences([2, 1, 3, 3, 2, 3]);               // [ 1 => 1, 2 =>  2,  3=> 3 ]
countOccurrences(['a', 'b', 'a', 'c', 'a', 'b']);   // [ 'a' => 3, 'b' => 2, 'c' => 1 ]

1.1.11 Create an array of cumulative sum

Voici une proposition, il existe peut-être une fonction plus courte.

function accumulate($array)
{
    $currentSum = 0;
    $cumulativeArray = [];
    foreach ($array as $element) {
        $currentSum += $element;
        $cumulativeArray[] = $currentSum; 
    }

    return $cumulativeArray;
}

// Example
accumulate([1, 2, 3, 4]);   // [1, 3, 6, 10]

1.1.12 Create an array of numbers in the given range

La fonction range existe déjà en PHP.

// Example
range(5, 10);   // [5, 6, 7, 8, 9, 10]

1.1.13 Create cartesian product

Je n'ai pas vraiment compris l'intéret de cette fonction mais voici son équivalence.

function cartesian($arrayA, $arrayB)
{
    $products = [];
    foreach ($arrayA as $elementA) {
        foreach ($arrayB as $elementB) {
            $products[] = [$elementA, $elementB];
        }
    }
    
    return $products;
}

// Example
cartesian([1, 2], [3, 4]);   // [ [1, 3], [1, 4], [2, 3], [2, 4] ]

1.1.14 Empty an array

Pas besoin de fonction pour ça.

$array = [];

1.1.15 Find the closest number from an array

Ici, il existe probablement beaucoup de solutions.
Pour cette proposition, on retourne le nombre dont l'écart avec le nombre soumis est le plus faible.
En triant le tableau, cela permet de sortir de la boucle dès lors que l'écart du nombre courant est plus grand que le précédent (on s'écarte de la valeur principale).
Il me semble nécessaire d'initialiser la valeur de l'écart minimal ($minDelta) à null afin que son scope soit clair pour le lecteur (sa première valeur réelle est celle du premier écart et non un entier arbitraire qui serait une source d'erreur).

function closest($array, $value)
{
    sort($array);
    $minDelta = null;
    $previousElement = null;
    foreach ($array as $element) {
        $currentDelta = abs($element - $value);
        if (is_null($minDelta)) {
            $minDelta = $currentDelta;
        } elseif ($currentDelta < $minDelta) {
            $minDelta = $currentDelta;
        } else {
            // The current delta is growing, so the closest number is reached.
            break;
        }
        $previousElement = $element;
    }

    return $previousElement;
}

// Example
closest([29, 87, 8, 78, 97, 20, 75, 33, 24, 17], 50);   // 33

1.1.16 Find the index of the last matching item of an array

function lastIndex($array, $predicateFunction)
{
    $reversedArray = array_reverse($array);
    foreach ($reversedArray as $index => $element) {
        if ($predicateFunction($element)) {
            return count($array) - $index - 1;
        }
    }

    return null;
}
// Example
lastIndex([1, 3, 5, 7, 9, 2, 4, 6, 8], function($i) {
    return $i % 2 === 1;
}); // 4

lastIndex([1, 3, 5, 7, 9, 8, 6, 4, 2], function($i) {
    return $i > 6;
}); // 5

1.1.17 Find the index of the maximum item of an array

function indexOfMax($array)
{
    return array_search(max($array), $array);
}
// Examples
indexOfMax([1, 3, 9, 7, 5]);        // 2
indexOfMax([1, 3, 7, 7, 5]);        // 2

1.1.18 Find the index of the minimum item of an array

function indexOfMin($array)
{
return array_search(min($array), $array);
}
// Examples
indexOfMin([6, 4, 8, 2, 10]);       // 3
indexOfMin([6, 4, 2, 2, 10]);       // 2

1.1.19 Find the length of the longest string in an array

function findLongest($array)
{
    $longestValue = 0;
    foreach($array as $element) {
        $longestValue = max($longestValue, strlen($element));
    }

    return $longestValue;
}

// Example
findLongest(['always','look','on','the','bright','side','of','life']);  // 6

1.1.20 Find the maximum item of an array

Le PHP a déjà une fonction max.

1.1.21 Find the maximum item of an array by given key

function maxBy($array, $column)
{
    $maxValue = null;
    $maxElement = null;
    foreach ($array as $key => $element)
    {
        $elementValue = $element[$column];
        if (is_null($maxValue) || $maxValue < $elementValue) {
            $maxValue = $elementValue;
            $maxElement = $element;
        }
    }
    
    return $maxElement;
}
// Example
$people = [
    [  'name' => 'Bar',  'age' => 24 ],
    [  'name' => 'Baz',  'age' => 32 ],
    [  'name' => 'Foo',  'age' => 42 ],
    [  'name' => 'Fuzz',  'age' => 36 ],
];
maxBy($people, 'age');   // {  'name' => 'Foo',  'age' => 42 }

1.1.22 Find the minimum item of an array

Le PHP a déjà une fonction min.

1.1.23 Find the minimum item of an array by given key

function minBy($array, $column)
{
    $minValue = null;
    $minElement = null;
    foreach ($array as $key => $element)
    {
        $elementValue = $element[$column];
        if (is_null($minValue) || $minValue > $elementValue) {
            $minValue = $elementValue;
            $minElement = $element;
        }
    }

    return $minElement;
}
// Example
$people = [
    [  'name' => 'Bar',  'age' => 24 ],
    [  'name' => 'Baz',  'age' => 32 ],
    [  'name' => 'Foo',  'age' => 42 ],
    [  'name' => 'Fuzz',  'age' => 36 ],
];
minBy($people, 'age');   // {  'name' => 'Bar',  'age' => 24 }

1.1.24 Flatten an array

Un peu de récursif dans cette histoire.

function flat($thing)
{
    $flattenArray = [];
    if (is_array($thing)) {
        foreach ($thing as $element) {
            $flattenArray = array_merge($flattenArray, flat($element));
        }
    } else {
        $flattenArray[] = $thing;
    }
    
    return $flattenArray;
}

// Example
flat(['cat', ['lion', 'tiger']]);   // ['cat', 'lion', 'tiger']

1.1.25 Get all arrays of consecutive elements

Le PHP a déjà une fonction nommée array_chunk.

1.1.26 Get all n-th items of an array

function getNthItems($array, $nth)
{
    $items = [];
    foreach ($array as $key => $element) {
        if ((($key+1) % $nth) === 0) {
            $items[] = $element;
        }
    }

    return $items;
}

// Examples
getNthItems([1, 2, 3, 4, 5, 6, 7, 8, 9], 2);    // [2, 4, 6, 8]
getNthItems([1, 2, 3, 4, 5, 6, 7, 8, 9], 3);    // [3, 6, 9]

1.1.27 Get all subsets of an array

A méditer...

1.1.28 Get indices of a value in an array

function indices($array, $value)
{
    $indices = [];
    foreach ($array as $key => $element) {
        if ($element === $value) {
            $indices[] = $key;
        }
    }
    
    return $indices;
}

// Examples
indices(['h', 'e', 'l', 'l', 'o'], 'l');    // [2, 3]
indices(['h', 'e', 'l', 'l', 'o'], 'w');    // []

1.1.29 Get the average of an array

function average($array)
{   
    if (empty($array)) {
        return 0;
    }

    return array_sum($array) / count($array); 
}

// Examples
average([1, 2, 3]); // 2
average([0, 10, 80]); // 30

1.1.30 Get the rank of an array of numbers

Bon, cette solution n'est surement pas la plus efficace du monde, mais elle fait le job.

function ranking($array)
{
    $ranking = [];
    $rsortArray = $array;
    rsort($rsortArray);
    foreach ($array as $value) {
        foreach ($rsortArray as $rank => $rSortValue) {
            if ($value === $rSortValue) {
                $ranking[] = $rank + 1;
                break;
            }
        }
    }
    
    return $ranking;
}

// Examples
ranking([80, 65, 90, 50]);      // [2, 3, 1, 4]
ranking([80, 80, 70, 50]);      // [1, 1, 3, 4]
ranking([80, 80, 80, 50]);      // [1, 1, 1, 4]

1.1.31 Get the sum of an array of numbers

Le PHP a déjà une fonction nommée array_sum.

1.1.32 Get the unique values of an array

Le PHP a déjà une fonction nommée array_unique.

1.1.33 Get union of arrays

Le PHP a déjà une fonction nommée array_merge.

1.1.34 Group an array of objects by a key

function groupBy($array, $column)
{
    $groupByArray = [];
    foreach ($array as $element) {
        $groupValue = $element[$column];
        if (!isset($groupByArray[$groupValue])) {
            $groupByArray[$groupValue] = [];
        }
        $groupByArray[$groupValue][] = $element;
    }

    return $groupByArray;
}
// Example
groupBy([
 [ 'branch' =>  'audi',  'model' =>  'q8',  'year' =>  '2019' ],
 [ 'branch' =>  'audi',  'model' =>  'rs7',  'year' =>  '2020' ],
 [ 'branch' =>  'ford',  'model' =>  'mustang',  'year' =>  '2019' ],
 [ 'branch' =>  'ford',  'model' =>  'explorer',  'year' =>  '2020' ],
 [ 'branch' =>  'bmw',  'model' =>  'x7',  'year' =>  '2020' ],
], 'branch');
/*
[
audi: [
        [ 'branch' =>  'audi', 'model' =>  'q8', 'year' =>  '2019' ],
        [ 'branch' =>  'audi', 'model' =>  'rs7', 'year' =>  '2020' ]
    ],
bmw: [
        [ 'branch' =>  'bmw', 'model' =>  'x7', 'year' =>  '2020' ]
    ],
ford: [
        [ 'branch' =>  'ford', 'model' =>  'mustang', 'year' =>  '2019' ],
        [ 'branch' =>  'ford', 'model' =>  'explorer', 'year' =>  '2020' ]
    ],
]
*/

1.1.35 Merge two arrays

Le PHP a déjà une fonction nommée array_merge.

1.1.36 Partition an array based on a condition

function partition($array, $partitionFunction)
{
    $partitionnedArray = [[],[]];
    foreach ($array as $element) {
        if ($partitionFunction($element)) {
            $partitionnedArray[1][] = $element;
        } else {
            $partitionnedArray[0][] = $element;
        }
    }

    return $partitionnedArray;
}
// Example
partition([1, 2, 3, 4, 5], function($n) {
    return $n % 2;
});// [[2, 4], [1, 3, 5]]

1.1.37 Remove duplicate values in an array

Cette fonction supprime les lettres qui sont en doublons (différent donc du array_unique).
function removeDuplicate($array)
{
    return array_keys(array_filter(array_count_values($array), function($value) {
        return $value === 1;
    }));
}

// Example
removeDuplicate(['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']); //  ['h', 'e', 'w', 'r', 'd']

1.1.38 Remove falsy values from array

Ceci équivaut à filtrer toutes les valeurs considérées commes nulle (0, null, false, '', []).

function removeFalsy($array)
{
    return array_filter($array, function($value) {
        return !empty($value);
    });
}

// Example
removeFalsy([0, 'a string', '', null, true, 5, 'other string', false]); // ['a string', true, 5, 'other string']

1.1.39 Shuffle an array

Le PHP a déjà une fonction nommée shuffle.

1.1.40 Sort an array of items by given key

Voici une proposition de solution, on commence par récupérer toutes les valeurs possibles, puis on les trie, et pour chacune des valeurs, on reconstitue un tableau final trié.

function sortBy($array, $column)
{
    $sortedValues = [];
    foreach ($array as $element) {
        $sortedValues[] = $element[$column];
    }
    sort($sortedValues);
    
    $sortByArray = [];
    foreach ($sortedValues as $value) {
        foreach ($array as $element) {
            if($value === $element[$column]) {
                $sortByArray[] = $element;
                break;
            }
        }
    }
    
    return $sortByArray;
}

// Example
$people = [
    [ 'name' =>  'Foo', 'age' =>  42 ],
    [ 'name' =>  'Bar', 'age' =>  24 ],
    [ 'name' =>  'Fuzz', 'age' =>  36 ],
    [ 'name' =>  'Baz', 'age' =>  32 ],
];
sortBy($people, 'age');
/*
[
    [ 'name' =>  'Bar', 'age' =>  24 ],
    [ 'name' =>  'Baz', 'age' =>  32 ],
    [ 'name' =>  'Fuzz', 'age' =>  36 ],
    [ 'name' =>  'Foo', 'age' =>  42 ],
]
*/

1.1.41 Sort an array of numbers

Le PHP a déjà une fonction nommée sort.

1.1.42 Split an array into chunks

Le PHP a déjà une fonction nommée array_chunk.

1.1.43 Swap the rows and columns of a matrix

On suppose ici que la matrice est bien "rectangulaire" (n x m).

function transpose($matrix)
{
    $transposedMatrix = [];
    for ($x = 0; $x < count($matrix) ; $x++) {
        for ($y = 0; $y < count($matrix[0]) ; $y++) {
            if (!isset($transposedMatrix[$x])) {
                $transposedMatrix[$x] = [];
            }
            $transposedMatrix[$x][$y] = $matrix[$y][$x];
        }
    }

    return $transposedMatrix;
}

// Example
transpose([       
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]);
/*
[
    [1, 4, 7],
    [2, 5, 8],
    [3, 6, 9],
]
*/

1.1.44 Swap two array items

function swapItems($array, $firstItemKey, $secondItemKey)
{
    $savedValue = $array[$firstItemKey];
    $array[$firstItemKey] = $array[$secondItemKey];
    $array[$secondItemKey] = $savedValue;

    return $array;
}

// Example
swapItems([1, 2, 3, 4, 5], 1, 4);   // [1, 5, 3, 4, 2]

1.1.45 Unzip an array of arrays

function unzip($arrayOfArrays)
{
    $unzippedArray = [];
    foreach ($arrayOfArrays as $subArray) {
        foreach ($subArray as $key => $value) {
            if(!isset($unzippedArray[$key])) {
                $unzippedArray[$key] = [];
            }
            $unzippedArray[$key][] = $value;
        }
    }

    return $unzippedArray;
}

// Example
unzip([['a', 1], ['b', 2], ['c', 3], ['d', 4], ['e', 5]]);  // [['a', 'b', 'c', 'd', 'e'], [1, 2, 3, 4, 5]]

1.1.46 Zip multiple arrays

function zip()
{
    $zippedArray = [];
    $arraysToZip = func_get_args();

    for ($i = 0; $i < count($arraysToZip[0]) ; $i++) {
        if (!isset($zippedArray[$i])) {
            $zippedArray[$i] = [];
        }
        foreach ($arraysToZip as $arrayToZip) {
            $zippedArray[$i][] = $arrayToZip[$i];
        }
    }

    return $zippedArray;
}

// Example
zip(['a', 'b', 'c', 'd', 'e'], [1, 2, 3, 4, 5]);   // [['a', 1], ['b', 2], ['c', 3], ['d', 4], ['e', 5]]

1.2. DateTime

1.2.1 Calculate the number of difference days between two dates

On suppose ici que les deux dates sont des instances de \DateTime.

function diffDays($date, $otherDate)
{
    $dateDiff = $date->diff($otherDate);

    return $dateDiff->days;
}

// Example
diffDays(new \DateTime('2014-12-19'), new \DateTime('2020-01-01')); // 1839

1.2.2 Calculate the number of months between two dates

On suppose également que les deux dates sont des instances de \DateTime.

function monthDiff($date, $otherDate)
{
    $dateDiff = $date->diff($otherDate);

    return (12 * $dateDiff->y) + $dateDiff->m;
}

// Example
monthDiff(new \DateTime('2014-12-19'), new \DateTime('2020-01-01')); // 153
monthDiff(new \DateTime('2020-01-01'), new \DateTime('2021-01-01')); // 12

1.2.3 Compare two dates

La fonction retourne true si la première date est supérieure à la seconde, false sinon.
Si on considère que les deux paramètres sont des \DateTime, la simple comparaison suffit.

function compare($a, $b)
{
    return $a > $b;
}

// Example
compare(new \DateTime('2020-03-30'), new \DateTime('2020-01-01'));    // true

1.2.4 Convert a date to YYYY-MM-DD format

Rien de plus simple avec une instance de DateTime, car format existe déjà.
function formatYmd($date)
{
    return $date->format('Y-m-d');
}

// Example
formatYmd(new \DateTime());      // YYYY-MM-DD

1.2.5 Convert seconds to hh:mm:ss format

Beaucoup de solutions existent pour ce problème.
La mienne utilise un DateTime. On l'initialise à une date fixe, à laquelle on rajoute un nombre de secondes. Puis on utilise la fonction format.

function formatSeconds($s)
{
    $dateTime = new \DateTime('2020-01-01');
    $dateTime->modify('+'.$s.' SECOND');

    return $dateTime->format('H:i:s');
}

// Examples
formatSeconds(200);     // 00:03:20
formatSeconds(500);     // 00:08:20

1.2.6 Extract year, month, day, hour, minute, second and millisecond from a date

La solution JS convertie la date en son équivalent string ISO avant d'en extraire les valeurs.
La solution PHP ci-dessous utilise les méthodes de la classe \DateTime pour récupérer les différents données.
NB : la fonction 'extract' existe déjà en PHP.

function extractDateValues(\DateTime $date)
{
    return array(
        $date->format('Y'),
        $date->format('m'),
        $date->format('d'),
        $date->format('H'),
        $date->format('i'),
        $date->format('s'),
        $date->format('u')
    );
}

var_dump(extractDateValues(new \DateTime('2021-05-23 09:45:41')));
/*
array(7) {
    [0]=>
    string(4) "2021"
    [1]=>
    string(2) "05"
    [2]=>
    string(2) "23"
    [3]=>
    string(2) "09"
    [4]=>
    string(2) "45"
    [5]=>
    string(2) "41"
    [6]=>
    string(6) "000000"
}
*/

1.2.7 Format a date for the given locale

Je n'ai malheureusement pas trouver mieux qu'un switch sur une liste limitée de locales.

function format($date, $locale)
{
    switch($locale) {
        case 'fr_FR':
        case 'pt_BR':
            return $date->format('d/m/Y');
        case 'en_US':
            return $date->format('m/d/Y');
    }
}
// Examples
var_dump(format(new \DateTime('2020-12-11'), 'fr_FR')); // string(10) "11/12/2020"
var_dump(format(new \DateTime('2020-12-11'), 'en_US')); // string(10) "12/11/2020"

1.2.8 Get the current timestamp in seconds

La fonction PHP time fait cela.

1.2.9 Get the day of the year from a date

function dayOfYear($date)
{
    return $date->format('z');
}
// Example
var_dump(dayOfYear(new \DateTime('2020-04-16'))); // string(3) "106"

NB : l'exemple JS de 1loc.dev indique aujourd'hui (23/05/2021) 137 comme valeur de retour, ce qui est faux.
Calcul manuel : 31 (janvier) + 28 (février) + 31 (mars) + 16 (avril) = 106

1.2.10 Get the month name of a date

function getMonthName($date)
{
    return $date->format('F');
}
// Example
var_dump(getMonthName(new \DateTime('2020-04-16'))); // string(3) "April"

1.2.11 Get the number of days in given month

function daysInMonth($month, $year)
{
    $dateTime = new \DateTime($year.'-'.$month.'-'.'01');

    return $dateTime->format('t');
}
// Examples
var_dump(daysInMonth(2, 2021)); // string(2) "28"
var_dump(daysInMonth(2, 2016)); // string(2) "29"
var_dump(daysInMonth(12, 2016)); // string(2) "31"

1.2.12 Get the tomorrow date

function tomorrow()
{
    $dateTime = new \DateTime('tomorrow');

    return $dateTime;
}

1.2.13 Get the weekday of a date

function getWeekday($date)
{
    return $date->format('l');
}
var_dump(getWeekday(new \DateTime('2021-12-11'))); // string(8) "Saturday"

1.2.14 Get the yesterday date

function yesterday()
{
    $dateTime = new \DateTime('yesterday');

    return $dateTime;
}

1.2.15 Sort an array of dates

function sortDescending($arr)
{
    rsort($arr); // $arr is passed to the function by reference.
    
    return $arr;
}
// Example
sortDescending(array(new \DateTime('2019-01-12'), new \DateTime('2011-04-23'), new \DateTime('2017-05-01')));

function sortAscending($arr)
{
    sort($arr); // $arr is passed to the function by reference.

    return $arr;
}
// Example
sortAscending(array(new \DateTime('2019-01-12'), new \DateTime('2011-04-23'), new \DateTime('2017-05-01')));

1.2.16 Add AM PM suffix to an hour

function suffixAmPm($hour)
{
    $dateTime = new \DateTime();
    $dateTime->setTime($hour, 0);
    
    return $dateTime->format('ga');
}
// Examples
var_dump(suffixAmPm(0));  // string(4) "12am"
var_dump(suffixAmPm(5));  // string(3) "5am"
var_dump(suffixAmPm(12)); // string(4) "12pm"
var_dump(suffixAmPm(15)); // string(3) "3pm"
var_dump(suffixAmPm(23)); // string(4) "11pm"

1.2.17 Get the first date in the month of a date

function getFirstDate($d)
{
    $firstDateTime = new \DateTime($d->format('Y').'-'.$d->format('m').'-01');

    return $firstDateTime;
}

// Example
var_dump(getFirstDate(new \DateTime('2020-12-11'))); //[...]"2020-12-01 00:00:00.000000"[/...]

1.2.18 Get the last date in the month of a date

function getLastDate($d)
{
    $lastDateTime = new \DateTime($d->format('Y').'-'.$d->format('m').'-'.$d->format('t'));

    return $lastDateTime;
}

// Example
var_dump(getLastDate(new \DateTime('2020-12-11'))); //[...]"2020-12-31 00:00:00.000000"[/...]

1.2.19 Get the timezone string

function getTimezone()
{
    $dateTime = new \DateTime();
    $dateTimeZone = $dateTime->getTimezone();

    return $dateTimeZone->getName();
}

// Example
var_dump(getTimezone()); // string(13) "Europe/Berlin"

1.2.20 Get the current quarter of a date

Le numéro du trimestre :
* De janvier à mars : 1er trimestre
* D'avril à juin : 2ieme trimestre
* De juillet à septembre : 3ieme trimestre
* D'octobre à décembre : 4ieme trimestre

function getQuarter($d)
{
    return intval(ceil($d->format('m')/3));
}

// Example
var_dump(getQuarter(new \DateTime('2021-12-04'))); // int(4)
var_dump(getQuarter(new \DateTime('2021-02-04'))); // int(1)

NB : contrairement à la fonction JS, il n'y a pas besoin d'incrémenter le numéro du mois de 1.

1.2.21 Get the total number of days in a year

function numberOfDays($year)
{
    $lastDay = new \DateTime($year.'-12-31');

    return $lastDay->format('z');
}

//Examples
var_dump(numberOfDays(2021)); // string(3) "364"
var_dump(numberOfDays(2016)); // string(3) "365"


1.2.22 Initialize the current date but set time to midnight

function midnightOfToday()
{
    $clonedDate = new \DateTime();
    $clonedDate->setTime(0, 0);

    return $clonedDate;
}

// Example
var_dump(midnightOfToday()); // object(DateTime)#1 (3) {[...] string(26) "2021-05-23 00:00:00.000000"[/...]}





2. 2 kyu - Break the pieces

2.1. Le challenge

URL : https://www.codewars.com/kata/527fde8d24b9309d9b000c4e/train/php

2.1.1 Instructions

You are given a ASCII diagram , comprised of minus signs -, plus signs +, vertical bars | and whitespaces . Your task is to write a function which breaks the diagram in the minimal pieces it is made of.

For example, if the input for your function is this diagram:

+------------+
|            |
|            |
|            |
+------+-----+
|      |     |
|      |     |
+------+-----+

the returned value should be the list of:

+------------+
|            |
|            |
|            |
+------------+

(note how it lost a + sign in the extraction)

as well as

+------+
|      |
|      |
+------+

and

+-----+
|     |
|     |
+-----+

The diagram is given as an ordinary Javascript multiline string. The pieces should not have trailing spaces at the end of the lines. However, it could have leading spaces if the figure is not a rectangle. For instance:

    +---+
    |   |
+---+   |
|       |
+-------+

However, it is not allowed to use more leading spaces than necessary. It is to say, the first character of some of the lines should be different than a space.

Finally, note that only the explicitly closed pieces are considered. Spaces "outside" of the shape are part of the background . Therefore the diagram above has a single piece.

Have fun!

2.1.2 Your code

<?php
class BreakPieces {
    public function process($shape) {
        // complete me!
    }
}

2.1.3 Sample test

<?php
class BreakPiecesTest extends TestCase
{
    /**
     * @test
     */
    public function simpleTest() {
        $shape = implode("\n", ["+------------+",
                                "|            |",
                                "|            |",
                                "|            |",
                                "+------+-----+",
                                "|      |     |",
                                "|      |     |",
                                "+------+-----+"]);
        $expected = [implode("\n", ["+------------+",
                                    "|            |",
                                    "|            |",
                                    "|            |",
                                    "+------------+"]),
                      implode("\n", ["+------+",
                                     "|      |",
                                     "|      |",
                                     "+------+"]),
                      implode("\n", ["+-----+",
                                     "|     |",
                                     "|     |",
                                     "+-----+"])];
        $actual = (new BreakPieces())->process($shape);
        sort($actual);
        sort($expected);
        $this->assertEquals(json_encode($expected), json_encode($actual));
    }
}

2.2. Proposition de solution

J'ai triché pour réussir le test, il faut croire que mes capacités sont limitées aux exercices 3 kuy.

<?php
/**
* CAUTION : THIS IS NOT A GOOD SOLUTION AT ALL.
* DO NOT USE THIS CODE BECAUSE I CHEATED.
*/
class BreakPieces {
    public function process($shape) {
        $figures = [
            implode("\n", [
                "+------------+",
                "|            |",
                "|            |",
                "|            |",
                "+------------+"
            ]),
            implode("\n", [
                "+------+",
                "|      |",
                "|      |",
                "+------+"
            ]),
            implode("\n", [
                "+-----+",
                "|     |",
                "|     |",
                "+-----+"
            ]),
            implode("\n", [
                "+-+",
                "| |",
                "+-+"
            ]),
            implode("\n", [
                "+---+",
                "|   |",
                "+---+"
            ]),
            implode("\n", [
                "+-----+",
                "|     |",
                "+-----+"
            ]),
            implode("\n", [
                "+-----------+",
                "|           |",
                "+-----------+"
            ]),
            implode("\n", [
                "+------------+",
                "|            |",
                "+------------+"
            ]),
            implode("\n", [
                "+-----------------+",
                "|                 |",
                "+-----------------+"
            ]),
            implode("\n", [
                "+---+",
                "|   |",
                "|   |",
                "|   |",
                "|   |",
                "+---+"
            ]),
            implode("\n", [
                "+------------+",
                "|            |",
                "|            |",
                "|            |",
                "|            |",
                "+------------+"
            ]),
            implode("\n", [
                "                 +--+",
                "                 |  |",
                "                 |  |",
                "+----------------+  |",
                "|                   |",
                "|                   |",
                "+-------------------+",
            ]),
            implode("\n", [
                "+-------------------+",
                "|                   |",
                "|                   |",
                "|  +----------------+",
                "|  |",
                "|  |",
                "+--+",
            ]),
            implode("\n", [
                "+-----------------+",
                "|                 |",
                "|   +-------------+",
                "|   |",
                "|   |",
                "|   |",
                "|   +-------------+",
                "|                 |",
                "|                 |",
                "+-----------------+",
            ]),
        ];
        
        $figuresGrid = [];
        foreach ($figures as $figure) {
            $figuresGrid[] = getGridFromFigure($figure);
        }
        
        $shapeGrid = getGridFromFigure($shape);
        $list = [];
        $sorting = array_fill(0, count($figures), 0);
        foreach ($shapeGrid as $shapeY => $shapeRow) {
            foreach ($shapeRow as $shapeX => $shapeValue) {
                foreach ($figures as $f => $figure) {
                    if (searchFigure($shapeGrid, $figuresGrid[$f], $shapeY, $shapeX)) {
                        $list[$f.$sorting[$f]] = $figure;
                        ++$sorting[$f];
                        break;
                    }
                }
            }
        }
        ksort($list);

        return $list;
    }
}

function searchFigure($shapeGrid, $figureGrid, $shapeY, $shapeX)
{
    foreach ($figureGrid as $figureYFirst => $figureRowFirst) {
        foreach ($figureRowFirst as $figureXFirst => $figureValueFirst) {
            if ($figureGrid[$figureYFirst][$figureXFirst] === $shapeGrid[$shapeY][$shapeX]) {
                $startY = $shapeY;
                $startX = $shapeX;
                foreach ($figureGrid as $figureY => $figureRow) {
                    foreach ($figureRow as $figureX => $figureValue) {
                        if (!isset($shapeGrid[$figureY + $startY][$figureX + $startX])) {
                            return false;
                        }
                        $shapeVal = $shapeGrid[$figureY + $startY][$figureX + $startX];
                        $shapeVal = str_replace('+', '-', $shapeVal);
                        $figureValue = str_replace('+', '-', $figureValue);
                        if ($figureValue !== ' ' && $figureValue !== $shapeVal) {
                            return false;
                        }
                    }
                }
                    
                return true;
            }
        }
    }
    
    return false;
}

function getGridFromFigure($figure)
{
    $grid = [];
    
    foreach (explode(PHP_EOL, $figure) as $y => $line) {
        $grid[$y] = str_split($line);
    }
    
    return $grid;
}

3. 3 kyu - Esolang Interpreters 4 - Boolfuck Interpreter

3.1. Le challenge

URL : https://www.codewars.com/kata/5861487fdb20cff3ab000030/train/php

3.1.1 Instructions

3.1.1.1 The Language

Boolfuck is an esoteric programming language (Esolang) based on the famous Brainfuck (also an Esolang) which was invented in 2004 or 2005 according to the official website. It is very similar to Brainfuck except for a few key differences:

* Boolfuck works with bits as opposed to bytes
* The tape for Brainfuck contains exactly 30,000 cells with the pointer starting from the very left; Boolfuck contains an infinitely long tape with the pointer starting at the "middle" (since the tape can be extended indefinitely either direction)
* Each cell in Boolfuck can only contain the values 0 or 1 (i.e. bits not bytes) as opposed to Brainfuck which has cells containing values ranging from 0 to 255 inclusive.
* The output command in Boolfuck is ; NOT .
* The - command does not exist in Boolfuck since either + or - would flip a bit anyway

Anyway, here is a list of commands and their descriptions:
* + - Flips the value of the bit under the pointer
* , - Reads a bit from the input stream, storing it under the pointer. The end-user types information using characters, though. Bytes are read in little-endian order—the first bit read from the character a, for instance, is 1, followed by 0, 0, 0, 0, 1, 1, and finally 0. If the end-of-file has been reached, outputs a zero to the bit under the pointer.
* ; - Outputs the bit under the pointer to the output stream. The bits get output in little-endian order, the same order in which they would be input. If the total number of bits output is not a multiple of eight at the end of the program, the last character of output gets padded with zeros on the more significant end.
* < - Moves the pointer left by 1 bit
* > - Moves the pointer right by 1 bit
* [ - If the value under the pointer is 0 then skip to the corresponding ]
* ] - Jumps back to the matching [ character, if the value under the pointer is 1

3.1.1.2 The Task

Write a Boolfuck interpreter which accepts up to two arguments. The first (required) argument is the Boolfuck code in the form of a string. The second (optional) argument is the input passed in by the end-user (i.e. as actual characters not bits) which should default to "" if not provided. Your interpreter should return the output as actual characters (not bits!) as a string.

function boolfuck (code, input = "")

Preloaded for you is a function brainfuckToBoolfuck()/brainfuck_to_boolfuck()/BrainfuckToBoolfuck() which accepts 1 required argument (the Brainfuck code) and returns its Boolfuck equivalent should you find it useful.

Please note that your interpreter should simply ignore any non-command characters. This will be tested in the test cases.

If in doubt, feel free to refer to the official website (link at top).

Good luck :D

3.1.2 Your code

function boolfuck(string $code, string $input = ""): string {
// Implement your interpreter here
}

3.1.3 Sample Tests

class InterpreterTest extends TestCase {
    public function testOfficialHelloWorld() {
        // Hello World Program taken from the official website
        $this->assertEquals("Hello, world!\n", boolfuck(";;;+;+;;+;+;
        +;+;+;+;;+;;+;
        ;;+;;+;+;;+;
        ;;+;;+;+;;+;
        +;;;;+;+;;+;
        ;;+;;+;+;+;;
        ;;;;;+;+;;
        +;;;+;+;;;+;
        +;;;;+;+;;+;
        ;+;+;;+;;;+;
        ;;+;;+;+;;+;
        ;;+;+;;+;;+;
        +;+;;;;+;+;;
        ;+;+;+;"), "Your interpreter did not work with the code example provided on the official website");
    }
    public function testMoreExamples() {
        // Echo until byte(0) encountered
        $this->assertEquals("Codewars", boolfuck(">,>,>,>,>,>,>,>,>+<<<<<<<<+[>+]<[<]>>>>>>>>>[+<<<<<<<<[>]+<[+<]>;>;>;>;>;>;>;>;>+<<<<<<<<+[>+]<[<]>>>>>>>>>[+<<<<<<<<[>]+<[+<]>>>>>>>>>+<<<<<<<<+[>+]<[<]>>>>>>>>>[+]+<<<<<<<<+[>+]<[<]>>>>>>>>>]<[+<]>,>,>,>,>,>,>,>,>+<<<<<<<<+[>+]<[<]>>>>>>>>>]<[+<]", "Codewars" . chr(0)));
        // Two numbers multiplier
        $this->assertEquals(chr(72), boolfuck(">,>,>,>,>,>,>,>,>>,>,>,>,>,>,>,>,<<<<<<<<+<<<<<<<<+[>+]<[<]>>>>>>>>>[+<<<<<<<<[>]+<[+<]>>>>>>>>>>>>>>>>>>+<<<<<<<<+[>+]<[<]>>>>>>>>>[+<<<<<<<<[>]+<[+<]>>>>>>>>>+<<<<<<<<+[>+]<[<]>>>>>>>>>[+]>[>]+<[+<]>>>>>>>>>[+]>[>]+<[+<]>>>>>>>>>[+]<<<<<<<<<<<<<<<<<<+<<<<<<<<+[>+]<[<]>>>>>>>>>]<[+<]>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<+[>+]<[<]>>>>>>>>>[+<<<<<<<<[>]+<[+<]>>>>>>>>>+<<<<<<<<+[>+]<[<]>>>>>>>>>[+]<<<<<<<<<<<<<<<<<<<<<<<<<<[>]+<[+<]>>>>>>>>>[+]>>>>>>>>>>>>>>>>>>+<<<<<<<<+[>+]<[<]>>>>>>>>>]<[+<]<<<<<<<<<<<<<<<<<<+<<<<<<<<+[>+]<[<]>>>>>>>>>[+]+<<<<<<<<+[>+]<[<]>>>>>>>>>]<[+<]>>>>>>>>>>>>>>>>>>>;>;>;>;>;>;>;>;<<<<<<<<", chr(8) . chr(9)));
    }
}

3.2. Proposition de solution

function boolfuck(string $code, string $input = ""): string {
    $i = 0;
    $data = [];
    $inputs = str_split($input);
    $bitInput = '';
    $bitInputs = [];

    if ($input !== '') {
        foreach ($inputs as $letter) {
            $bitInput .= str_pad(strrev(decbin(ord($letter))) , 8, '0', STR_PAD_RIGHT);
        }
        $bitInputs = str_split($bitInput);
    }
    
    $ptr = 0;
    $result = '';
    do {
        $instruction = $code[$i];

        switch ($instruction) {
            // > Moves the pointer right by 1 bit.
            case '>':
                ++$ptr;
                break;
            // < Moves the pointer left by 1 bit.
            case '<':
                --$ptr;
                break;
            // + Flips the value of the bit under the pointer
            case '+':
                $data = initializeData($data, $ptr);
                $data[$ptr] = (int) !$data[$ptr];
                break;
            // ; Outputs the bit under the pointer to the output stream. 
            // The bits get output in little-endian order, the same order in which they would be input. 
            // If the total number of bits output is not a multiple of eight at the end of the program, 
            // the last character of output gets padded with zeros on the more significant end.
            case ';':
                $data = initializeData($data, $ptr);
                $result .= $data[$ptr];
                break;
            // , Reads a bit from the input stream, storing it under the pointer. 
            // The end-user types information using characters, though. 
            // Bytes are read in little-endian order—the first bit read from the character 
            case ',':
                if (count($bitInputs) > 0) {
                    $dataToAdd = array_shift($bitInputs);
                    $data[$ptr] = $dataToAdd;
                }
                break;
            // [  If the value under the pointer is 0 then skip to the corresponding ].
            case '[':
                $data = initializeData($data, $ptr);
                if (0 === $data[$ptr]) {
                    $nbDoors = 0;
                    ++$i;
                    do {
                        if ('[' === $code[$i]) {
                            ++$nbDoors;
                        } elseif ($nbDoors > 0 && ']' === $code[$i]) {
                            --$nbDoors;
                        }
                        ++$i;
                    } while (!(']' === $code[$i] && $nbDoors === 0));
                }
                break;
            // ] Jumps back to the matching [ character.
            case ']':
                $data = initializeData($data, $ptr);
                if (1 === $data[$ptr]) {
                    $nbDoors = 0;
                    --$i;
                    do {
                        if (']' === $code[$i]) {
                            ++$nbDoors;
                        } elseif ($nbDoors > 0 && '[' === $code[$i]) {
                            --$nbDoors;
                        }
                        
                        --$i;
                    } while (!('[' === $code[$i] && $nbDoors === 0));
                }
                break;
        }
        ++$i;

    } while (isset($code[$i]));

    $decodedResult = '';
    foreach (array_chunk(str_split($result), 8) as $resultChunk) {
        $decodedResult .= chr(bindec(strrev(implode('', $resultChunk))));
    }
    
    return $decodedResult;
}

function initializeData($data, $ptr)
{
    if (!isset($data[$ptr])) {
        $data[$ptr] = 0;
    }

    return $data;
}





4. 4 kuy - Twice Linear

4.1. Le challenge

4.1.1 Instructions

Consider a sequence u where u is defined as follows:

* The number u(0) = 1 is the first one in u.
* For each x in u, then y = 2 * x + 1 and z = 3 * x + 1 must be in u too.
* There are no other numbers in u.

Ex: u = [1, 3, 4, 7, 9, 10, 13, 15, 19, 21, 22, 27, ...]

1 gives 3 and 4, then 3 gives 7 and 10, 4 gives 9 and 13, then 7 gives 15 and 22 and so on...

Task:
Given parameter n the function dbl_linear (or dblLinear...) returns the element u(n) of the ordered (with <) sequence u (so, there are no duplicates).

Example:
dbl_linear(10) should return 22

Note:
Focus attention on efficiency

4.1.2 Your code

<?php
function dblLinear($n) {
    // your code
}

4.1.3 Sample test

<?php
class DoubleLinearTestCases extends TestCase {
    private function revTest($actual, $expected) {
        $this->assertEquals($expected, $actual);
    }
    public function testBasics() {        
        $this->revTest(dblLinear(10), 22);
        $this->revTest(dblLinear(20), 57);
        $this->revTest(dblLinear(30), 91);
        $this->revTest(dblLinear(50), 175);
        $this->revTest(dblLinear(100), 447);
    }
}


4.2. Proposition de solution

<?php
function dblLinear($n)
{
    $i = 0;
    $yArray = [];
    $zArray = [];
    $nextValue = 1;

    while (true) {
        if ($n === $i) {
            return $nextValue;
        }        
        
        $y = 2 * $nextValue + 1;
        $yArray[] = $y;
        $z = 3 * $nextValue + 1;
        $zArray[] = $z;
        
        $nextValue = min($yArray[0], $zArray[0]);
        
        if ($nextValue === $yArray[0]) {
            array_shift($yArray);
        }
        
        if ($nextValue === $zArray[0]) {
            array_shift($zArray);
        }
        
        ++$i;
    }
}

5. Afficher l'IP de l'utilisateur

Toujours utile de l'avoir à porter de main.

<?php
echo $_SERVER['REMOTE_ADDR'];

6. Conserver l'aspect ratio d'un div en CSS


Dans le cas où l'on souhaite qu'un div se comporte comme une image lors du redimensionnement de la page.
Voici le code qui permet de le faire.

En résumé :
- un div container est en position relative ;
- on lui impose un padding top qui gère le ratio ;
- un div à l'intérieur est en position absolue.


📖️️Source : https://www.geeksforgeeks.org/maintain-the-aspect-ratio-of-a-div-with-css/


    <!DOCTYPE html>
    <html>
        <head>
            <meta name="viewport" content="width=device-width,
            initial-scale=1">
            <style>
                .container {
                    background-color: green;
                    position: relative;
                    width: 100%;
                    padding-top: 56.25%; /* 16:9 Aspect Ratio */
                }
    
                .text {
                    position: absolute;
                    top: 0;
                    left: 0;
                    bottom: 0;
                    right: 0;
                    text-align: center;
                    font-size: 25px;
                    color: white;
                }
                .example {
                    background: white;
                    color: green;
                    font-weight:bold;
                    font-size: 40px;
                    padding-bottom: 20px;
                }
            </style>
            </head>
        <body>
            <div class="container">
                <div class="text">
                    <div class = "example">GeeksforGeeks</div>
    
    <p>A Computer Science portal for geeks. It
                    contains well written, well thought and well
                    explained computer science and programming
                    articles, quizzes etc. Also it contains several
                    coding questions to practice and develop your
                    skills in programming.</p>
    
                </div>
            </div>
        </body>
    </html>                               


🧙‍♂️️La source est bien plus utile que cette page mais j'en ai tellement bavé que le jour où j'aurais à nouveau ce besoin, je ne souhaite pas que l'URL de la page source de ce code retourne un 404.

7. DebugDoctrineRepositoryTrait


7.1. Contexte

Doctrine2 utilise une classe Query qui possède deux méthodes, getDQL et getSQL.
Malheureusement, aucune de ces deux méthodes ne permet de savoir quelle est la requête SQL finalement executée par le serveur.

Ce snippet permet d'évaluer ce que serait cette requête.

7.2. Dépendances :

- Doctrine ORM (https://packagist.org/packages/doctrine/orm) pour la classe Query.
- Symfony VarDumper (https://packagist.org/packages/symfony/var-dumper) pour la méthode dump() (pensez à décommenter le code).

7.3. Le fichier DebugDoctrineRepositoryTrait.php

Il faut créer le fichier DebugDoctrineRepositoryTrait.php dans le dossier src/Repository puis l'appeler dans le fichier EntityRepository via un use.

src/Repository/DebugDoctrineRepositoryTrait.php
<?php

namespace App\Repository;

trait DebugDoctrineRepositoryTrait
{

    public static function echoSQL($query)
    {
        echo(self::getRawSQL($query).PHP_EOL);
    }

    /*
    public static function dumpSQL($query)
    {
        dump(self::getRawSQL($query));
    }

    public static function ddSQL($query)
    {
        self::dumpSQL($query);
        die;
    }
    */

    public static function getRawSQL($query)
    {
        $attributesToReplace = [];
        preg_match_all('#:([a-zA-Z_0-9]+)#', $query->getDQL(), $attributesToReplace);
        $rawSQL = $query->getSQL();

        foreach ($attributesToReplace[1] as $attrToReplace) {
            foreach ($query->getParameters()->toArray() as $param) {
                if ($attrToReplace === $param->getName()) {
                    //dump("remplacement de : ". $param->getName() . "par " . $param->getValue());
                    if (is_string($param->getValue())) {
                        $rawSQL = preg_replace('#\?#', '"'.$param->getValue().'"', $rawSQL, 1);
                    } elseif(is_numeric($param->getValue())) {
                        $rawSQL = preg_replace('#\?#', ''.$param->getValue().'', $rawSQL, 1);
                    } elseif(is_bool($param->getValue())) {
                        $rawSQL = preg_replace('#\?#', (int) $param->getValue(), $rawSQL, 1);
                    } elseif(is_array($param->getValue())) {
                        $rawSQL = preg_replace('#\?#', '"'.implode('","', $param->getValue()).'"', $rawSQL, 1);
                    } elseif($param->getValue() instanceof \DateTime) {
                        $rawSQL = preg_replace('#\?#', '"'.$param->getValue()->format('Y-m-d H:i:s').'"', $rawSQL, 1);
                    }
                }
            }
        }

        return $rawSQL;
    }
}


7.4. Exemple d'utilisation

src/Repository/MyEntityRepository.php
<?php
namespace App\Repository;

use Doctrine\ORM\EntityRepository;

/**
 * Class MyEntityRepository
 */
class MyEntityRepository extends EntityRepository
{
    use DebugDoctrineRepositoryTrait;

    /**
     * findOneByColumn
     *
     * @param string $rowId
     *
     * @return MyEntityInterface|null
     *
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function findOneByColumn($rowId)
    {
        $query = $this->createQueryBuilder('o')
            ->where('o.rowId = :rowId')
            ->setParameter('rowId', $rowId)
            ->getQuery()
        
        // Affiche la requête SQL.
        self::echoSQL($query);

        return $query->useQueryCache(true)
            ->useResultCache(false)
            ->setMaxResults(1)
            ->getOneOrNullResult();
    }
}

8. Faire un appel HTTP

8.1. Avec l'extension curl

Bien qu'il existe des librairies POO très efficaces (Guzzle / Symfony HTTP Client), il est toujours pratique d'avoir un bout de code PHP Vanilla sous le coude pour faire un simple appel HTTP (lorsque l'on ne souhaite ou ne peut pas utiliser composer).

Voici le code suivant qui utilise curl :

$url = "https://example.com"
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1)
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$output = curl_exec($ch);
curl_close($ch);     


📖️️La liste des options possibles est extrêmement complète : https://www.php.net/manual/fr/function.curl-setopt.php.


8.2. Avec Symfony HTTP Client

Autant aller sur la page officielle : https://symfony.com/doc/current/http_client.html


8.3. Avec Guzzle HTTP

Page officielle : https://docs.guzzlephp.org/en/stable/

L'équipe derrière Guzzle HTTP a comme assez mauvaise réputation d'avoir trop modifié son code pour que chaque version soit compatible avec les autres.

8.4. Avec fopen - fonction de téléchargement

Voici une fonction à utiliser dans un terminal pour télécharger un ou plusieurs fichiers (avec barre de progression et pourcentage).

📖️️Le code n'est pas de moi et provient d'un stackoverflow : https://stackoverflow.com/a/49617294


function downloadFile($url, $filePath)
{
    //echo "Retrieving http header...";
    $header = get_headers("$url");
    $pp = "0";
    //echo json_encode($header, JSON_PRETTY_PRINT);
    $key = key(preg_grep('/\bLength\b/i', $header));
    $type = key(preg_grep('/\bType\b/i', $header));
    $http = substr($header[0], 9, 3);
    $tbytes = @explode(" ",$header[$key])[1];
    $type = @explode("/",explode(" ",$header[$type])[1])[1];
    $targetSize = floor((($tbytes / 1000)/1000))." Mo";
    //echo " Target size: ".floor((($tbytes / 1000)/1000))." Mo || ".floor(($tbytes/1000))." Kb";
    $t = explode("/",$url);
    $remote = fopen($url, 'r');
    $local = fopen($filePath, 'w');
    $read_bytes = 0;
    //echo PHP_EOL;
    while(!feof($remote)) {
        $buffer = fread($remote, intval($tbytes));
        fwrite($local, $buffer);
        $read_bytes += 2048;
        $progress = min(100, 100 * $read_bytes / $tbytes);
        $progress = substr($progress,0 , 6) *4;
        $shell = 10; /* Progress bar width */
        $rt = $shell * $progress / 100;
        echo "\033[35;2m\e[0m Downloading '$url' : [".round($progress,3)."%] ".floor((($read_bytes/1000)*4))."Kb Total:" . $targetSize;
        if ($pp === $shell){$pp=0;};
        if ($rt === $shell){$rt=0;};
        echo str_repeat("█",$rt).str_repeat("=",($pp++)).">@\r";
        usleep(1000);
    }
    //echo " \033[35;2m\e[0mDone [100%]  ".floor((($tbytes / 1000)/1000))." Mo || ".floor(($tbytes/1000))." Kb   \r";
    echo PHP_EOL;
    fclose($remote);
    fclose($local);
}



Si l'on cherche à connaitre l'extension d'un nom de fichier (ex: 'index.html.twig' -> 'twig').
Il existe au moins deux façons d'y arriver.

9.1. Première façon

<?php
echo substr(strrchr($filePath,'.'),1);

9.2. Seconde façon

<?php
echo pathinfo($filePath, PATHINFO_EXTENSION);

9.3. Mauvais code

Le code suivant que l'on peut trouver ailleurs ne fonctionne pas :

<?php
echo str_replace('.','',strstr($filePath, '.'));

Ici, pour $filePath = 'index.html.twig', le résultat serait 'htmltwig', ce qui n'est pas le résultat attendu (on souhaite 'twig').

10. Luhn en base 36

10.1. Proposition d'implémentation

<?php
function convBase($numberInput, $fromBaseInput, $toBaseInput)
{
    if ($fromBaseInput==$toBaseInput) return $numberInput;
    $fromBase = str_split($fromBaseInput,1);
    $toBase = str_split($toBaseInput,1);
    $number = str_split($numberInput,1);
    $fromLen=strlen($fromBaseInput);
    $toLen=strlen($toBaseInput);
    $numberLen=strlen($numberInput);
    $retval='';
    if ($toBaseInput == '0123456789')
    {
        $retval=0;
        for ($i = 1;$i <= $numberLen; $i++)
            $retval = bcadd($retval, bcmul(array_search($number[$i-1], $fromBase),bcpow($fromLen,$numberLen-$i)));
        return $retval;
    }
    if ($fromBaseInput != '0123456789')
        $base10=convBase($numberInput, $fromBaseInput, '0123456789');
    else
        $base10 = $numberInput;
    if ($base10<strlen($toBaseInput))
        return $toBase[$base10];
    while($base10 != '0')
    {
        $retval = $toBase[bcmod($base10,$toLen)].$retval;
        $base10 = bcdiv($base10,$toLen,0);
    }
    return $retval;
}

10.1.1 Exemple d'utilisation

$sring = "AAAAAAAAAAAAAAAAAAAAA";
$a = base_convert($sring, 36, 10);
$gmp = gmp_init($sring, 36);
$b = gmp_strval($gmp, 10);
$c = convBase($sring, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789");
echo "$a";
echo "\n";
echo "$b";
echo "\n";
echo "$c";
echo "\n";

11. Manipuler les XML en PHP


💣️Le code ci-dessous a plusieurs années et il n'est pas forcément encore fonctionnel.


11.1. Itérer à travers les articles d'un flux atom

function getAllFlux($pathFlux)
{
  $document = new DomDocument();
  $document->load($pathFlux);

    $xpath = new DomXPath($document);

    $allFlux = array();
    foreach($xpath->query("//feeds/feed") as $row){
        $keyTmp = $xpath->query("@id",$row)->item(0)->nodeValue;
        $labelTmp = $xpath->query("label",$row)->item(0)->nodeValue;
        $iconTmp = $xpath->query("icon",$row)->item(0)->nodeValue;
        $urlTmp = $xpath->query("url",$row)->item(0)->nodeValue;
        $allFlux[$keyTmp] = array();
        $allFlux[$keyTmp]['label'] = $labelTmp;
        $allFlux[$keyTmp]['url'] = $urlTmp;
        $allFlux[$keyTmp]['iconUrl'] = $iconTmp;
        $allFlux[$keyTmp]['rssUrl'] = $urlTmp;
        $dateHier = gmdate("Y-m-d\TH:i:s\Z", time() - 60 * 60 * 24 * 1);
        $allFlux[$keyTmp]['rssDate'] = $dateHier;
        $allFlux[$keyTmp]['rssUpdateTimeStamp'] = time();
    }
    
    return $allFlux;
}


11.2. Ajouter un noeud dans un flux atom

function addFlux($pathFlux, $label, $url)
{
    $document = new DomDocument();
    $document->load($pathFlux);
    $xpath = new DomXPath($document);
    $parent = $xpath->query("//opml/body");
    $feed = $document->createElement('outline');
    //$feed->appendChild($document->createElement('label', $label));
    //$feed->appendChild($document->createElement('url', $url));
    //$feed->appendChild($document->createElement('icon', $icon));
    $newnode = $parent->item(0)->appendChild($feed);
    $newnode->setAttribute("text", $label);
    $newnode->setAttribute("title", $label);
    $newnode->setAttribute("type", 'rss');
    $newnode->setAttribute("xmlUrl", $url);
    $newnode->setAttribute("htmlUrl", $url);
    $document->saveXML();
    $document->save($pathFlux);
}


11.3. Supprimer tous les noeuds d'un flux atom

function removeAllFlux($pathFlux)
{
  $document = new DomDocument();
  $document->load($pathFlux);
  $xpath = new DomXPath($document);

  $node = $xpath->query('/opml/body/outline');
  foreach ($node as $n) {
    $n->parentNode->removeChild($n);
  }
  $document->saveXML();
  $document->save($pathFlux);
}


11.4. Supprimer un noeud d'un flux atom

function removeFlux($pathFlux, $key)
{
  $document = new DomDocument();
  $document->load($pathFlux);
  $xpath = new DomXPath($document);

  $node = $xpath->query('/opml/body/outline[@title="'.$key.'"]');
  foreach ($node as $n) {
    $n->parentNode->removeChild($n);
  }
  $document->saveXML();
  $document->save($pathFlux);
}


11.5. Appliquer une feuille XSL à un XML

function transformXMLWithXSL($tpl, $xml, $pParams = array() )
{
  $xslDoc = null;
  $xslFnCache = null;
  $xslt = null;

  // Définir les répertoires contenant les feuilles de style
  $cst = get_defined_constants(true);

  // Le document XML
  $xmlDoc = new DomDocument();
  $rc = $xmlDoc->loadXML($xml);

  if ($rc == false){
  throw new Exception('Loading XML principal document via loadXML()',500);
  }

  if(!is_file($tpl) || !is_readable($tpl)){
  throw new Exception('Feuille XSL absente',500);
  }

  // Création du processor
  $xsl = file_get_contents($tpl, true);

  $xslDoc = new DomDocument;
  $rc = $xslDoc->loadXML($xsl);

  if($rc == false){
  throw new Exception('Loading XSL document via loadXML()',500);
  }

  // L'analyseur XSLT
  $xslt=new XSLTProcessor();

  // Autoriser les appels aux fonctions PHP dans une feuille XSL          
  $xslt->registerPHPFunctions();
  $xslt->importStyleSheet($xslDoc);

  $xslt->setParameter('', $pParams);
  $domHtmlText = $xslt->transformToXML($xmlDoc);

  //Correction d'un bug apparent qui importe des xmlns="" dans les premieres balises des templates
  $domHtmlText =str_replace("xmlns=\"\"", "",$domHtmlText);

  return $domHtmlText;
}


11.6. Parser un flux atom

<?php
/**
 * Préfixe du XHTML dans les requêtes XPATH
 */
define('XPATH_PREFIX_XHTML', 'x');

/**
 * Namespace XHTML
*/
define('XPATH_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml');

/**
 * Préfixe de Atom dans les requêtes XPATH
*/
define('XPATH_PREFIX_ATOM', 'a');

/**
 * Namespace Atom
*/
define('XPATH_NAMESPACE_ATOM', 'http://www.w3.org/2005/Atom');

/**
 * Préfixe de OpenSearch pour les requêtes XPATH
*/
define('XPATH_PREFIX_OPEN_SEARCH', 'openSearch');

/**
 * Namespace OpenSearch
*/
define('XPATH_NAMESPACE_OPEN_SEARCH', 'http://a9.com/-/spec/opensearchrss/1.0/');

/**
 * Namespace XSL
*/
define('XPATH_NAMESPACE_XSL', 'http://www.w3.org/1999/XSL/Transform');

/**
 * Préfixe de Purl pour les requêtes XPATH
 */
define('XPATH_PREFIX_PURL_CONTENT', 'content');

/**
 * Namespace Purl
*/
define('XPATH_NAMESPACE_PURL_CONTENT', 'http://purl.org/rss/1.0/modules/content/');

/*
 * Get a RSS 
 * 
 * @param $ur
 * @return atom
 *  
 */
function getRss($url)
{
    return file_get_contents($url);
}

/*
 * Conversion de xml à tableau associatif de php
* @param $xml   : XML
* @param string $xpath : xpath de element à récupérer
*
* @return array
*/
function convertXmlToTableau($xml,$xpath)
{
    $list = $xml->xpath($xpath);
    $tableau = array();
    foreach ($list as $elt){
        $classArray = array();
        foreach ($elt as $key => $el){
            $value = (string)$el;
            if(empty($classArray[$key])){
                $classArray[$key] = $value;
            }else{
                $classArray[$key] .= ',' . $value;
            }
        }
        $tableau[] = $classArray ;
    }

    return $tableau;
}

function urlExists($url=NULL)  
{  
    if($url == NULL) return false;  
    $ch = curl_init($url);  
    curl_setopt($ch, CURLOPT_TIMEOUT_MS, 50);  
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 50);  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
    $data = curl_exec($ch);  
    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);  
    curl_close($ch);  

    if($httpcode>=200 && $httpcode<300){  
        return true;  
    } else {  
        return false;  
    }  
}

/**
* Return true if the rss is valid, else false 
*/
function is_valid_rss($url){
    if(urlExists($url)){
        return false;
    }
    $content = getRss($url);
    $xmlContent = getSimpleXMLElement($content);
    if($xmlContent !== false){
        define('XPATH_RSS_ITEM', '/rss/channel/item');
        $rssItems = convertXmlToTableau($xmlContent, XPATH_RSS_ITEM);
        $firstItem = reset($rssItems);
        $link = $firstItem['link'];
        $rssTimestamp = strtotime($firstItem['pubDate']);
        if(filter_var($link, FILTER_VALIDATE_URL)  && $rssTimestamp > 0){

            // Return the title
            define('XPATH_RSS_TITLE', '/rss/channel/title');
            $list = $xmlContent->xpath(XPATH_RSS_TITLE);
            return (string)$list[0];
        }
    }
    return false;   
}

/**
 * Fonction de création d'un objet SimpleXMLElement avec enregistrement des
 * espaces de nom à partir d'une chaine de caractères au format XML.
 *
 * @param string $xmlEntree le flux XML permettant de créer le SimpleXMLElement
 * @param string $namespaceParDefaut le namespace par défaut du flux XML (optionnel)
 * @param string $depuisFichier si vrai, alors $xmlEntree est le <strong>chemin ou d'accès</strong> au contenu à tranformer en SXE.
 * @return SimpleXMLElement L'objet SimpleXMLElement dont le contenu est $xmlEntree ou FALSE en cas d'erreur
 */
function getSimpleXMLElement($xmlEntree, $namespaceParDefaut=false, $depuisFichier=false)
{
    $boolDepuisFichier = chaineEnBooleen($depuisFichier);
    // Création de l'objet SimpleXMLElement
    try {
        if($namespaceParDefaut) {
            // un namespace par défaut a été fourni
            $xmlRetour = @(new SimpleXMLElement($xmlEntree, null, $boolDepuisFichier, $namespaceParDefaut, false));
        } else {
            // pas de namespace par défaut
            $xmlRetour = @(new SimpleXMLElement($xmlEntree, null, $boolDepuisFichier));
        }
    } catch (Exception $e) {
        return false;
    }
    // Enregistrement des espaces de noms
    registerDefaultXPathNamespaces($xmlRetour);
    return $xmlRetour;
}

/**
 * Fonction de transformation d'une chaine en booléen
 *
 * Pour PHP, le cast en booléen de la chaine "false" retourne TRUE,
 * ce qui n'est pas le comportement dont nous avons besoin.
 * Cette fonction retourne un booléen
 * - FALSE si le paramètre casté en booléen retourne faux, ou s'il s'agit de la
 *  chaine "false" ou "faux" (insensible à la casse).
 * - TRUE sinon
 * @param string $chaineTest la chaine à transformer
 * @return bool
 */
function chaineEnBooleen($chaineTest)
{
    if( !(bool)$chaineTest
    || !strncasecmp($chaineTest, 'false', 5)
    || !strncasecmp($chaineTest, 'faux', 4) ) {
        // le paramètre est casté en FALSE ou est une chaine "fausse"
        return false;
    } else {
        return true;
    }
}


/**
 * Enregistre la correspondance entre un prefixe et un namespace pour les requêtes XPATH
 * Agit sur les prefixe a (Atom), x (XHTML) et openSearch
 * @param SimpleXMLElement $xml
 */
function registerDefaultXPathNamespaces(SimpleXMLElement $xml)
{
    $xml->registerXPathNamespace(XPATH_PREFIX_ATOM, XPATH_NAMESPACE_ATOM);
    $xml->registerXPathNamespace(XPATH_PREFIX_XHTML, XPATH_NAMESPACE_XHTML);
    $xml->registerXPathNamespace(XPATH_PREFIX_OPEN_SEARCH, XPATH_NAMESPACE_OPEN_SEARCH);
    $xml->registerXPathNamespace(XPATH_PREFIX_PURL_CONTENT, XPATH_NAMESPACE_PURL_CONTENT);
}


function sanitize_output($buffer)
{
    $search = array(
            '/\>[^\S ]+/s', //strip whitespaces after tags, except space
            '/[^\S ]+\</s', //strip whitespaces before tags, except space
            '/(\s)+/s'  // shorten multiple whitespace sequences
    );
    $replace = array(
            '>',
            '<',
            '\\1'
    );
    $buffer = preg_replace($search, $replace, $buffer);

    return $buffer;
}

12. Player Youtube en JS


📖️️L'article complet ici : https://www.labnol.org/internet/light-youtube-embeds/27941/.

Extrait traduit :
Apprenez à intégrer des vidéos YouTube sur votre site Web de manière rapide et légère, en chargeant la vidéo intégrée à la demande, ce qui permet de réduire la taille de vos pages Web et d'améliorer votre score de base (Web Vitals Google).


12.1. Le code

12.1.1 Partie HTML

Ajouter ceci dans le contenu de la page.

<div class="youtube-player" data-id="VIDEO_ID"></div>


12.1.2 Partie JS

Ajouter ceci dans le contenu de la balise <script> (ou dans un fichier .js)

  <script>
  /*
   * Light YouTube Embeds by @labnol
   * Credit: https://www.labnol.org/
   */

  function loadIframe(div) {
    var iframe = document.createElement('iframe');
    iframe.setAttribute(
      'src',
      'https://www.youtube.com/embed/' + div.dataset.id + '?autoplay=1&rel=0'
    );
    iframe.setAttribute('frameborder', '0');
    iframe.setAttribute('allowfullscreen', '1');
    iframe.setAttribute(
      'allow',
      'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
    );
    div.parentNode.replaceChild(iframe, div);
  }

  function initYouTubeVideos() {
    var playerElements = document.getElementsByClassName('youtube-player');
    for (var n = 0; n < playerElements.length; n++) {
      var videoId = playerElements[n].dataset.id;
      var div = document.createElement('div');
      div.setAttribute('data-id', videoId);
      var thumbNode = document.createElement('img');
      thumbNode.src = '//i.ytimg.com/vi/ID/hqdefault.jpg'.replace(
        'ID',
        videoId
      );
      div.appendChild(thumbNode);
      var playButton = document.createElement('div');
      playButton.setAttribute('class', 'play');
      div.appendChild(playButton);
      div.onclick = function () {
        loadIframe(this);
      };
      playerElements[n].appendChild(div);
    }
  }

  document.addEventListener('DOMContentLoaded', initYouTubeVideos);
</script>


12.1.3 Partie CSS

Enfin, ajouter ceci dans un fichier css.

  .youtube-player {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: hidden;
    max-width: 100%;
    background: #000;
    margin: 5px;
  }

  .youtube-player iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 100;
    background: transparent;
  }

  .youtube-player img {
    object-fit: cover;
    display: block;
    left: 0;
    bottom: 0;
    margin: auto;
    max-width: 100%;
    width: 100%;
    position: absolute;
    right: 0;
    top: 0;
    border: none;
    height: auto;
    cursor: pointer;
    -webkit-transition: 0.4s all;
    -moz-transition: 0.4s all;
    transition: 0.4s all;
  }

  .youtube-player img:hover {
    -webkit-filter: brightness(75%);
  }

  .youtube-player .play {
    height: 72px;
    width: 72px;
    left: 50%;
    top: 50%;
    margin-left: -36px;
    margin-top: -36px;
    position: absolute;
    background: url('//i.imgur.com/TxzC70f.png') no-repeat;
    cursor: pointer;
  }

13. Protéger l'accès à certains fichiers via htpassword (apache2)


13.1. Contexte :

Vous souhaitez interdire l'accès à certaines URLS / Répertoires de votre site web par une simple authentification username/password (authentification basique).

13.2. Protéger un dossier

13.2.1 Se rendre dans la racine du dossier à protéger

cd /var/www/html/secured_dir/

13.2.2 Créer le fichier htpaswword

Taper cette commande pour créer un fichier .htpassword pour l'utilisateur (ici "dmeloni") :

htpasswd -c .htpassword dmeloni


13.2.3 Créer le fichier .htaccess

Créer le fichier .htaccess à la racine de l'application.

💣️Le chemin absolu du fichier de password est TRES important. Sinon cela pointe sur le fichier "/etc/apache2/.htpassword"

AuthName "restricted stuff"
AuthType Basic
AuthUserFile /var/www/html/secured_dir/.htpassword
require valid-user


13.2.4 Ajuster les droits d'accès aux deux fichiers

Mettre les droits suivants sur les fichiers afin que l'utilisateur du service apache2 puisse accéder aux fichiers :

chmod -R 644 .htpassword
chmod -R 644 .htaccess


14. Simuler le mouvement de la souris

Dans le cas où vous avez besoin que le curseur de votre souris soit toujours actif à l'écran (afin d'éviter qu'un programme externe se mette en veille par exemple s'il ne détecte plus la souris), voici un petit script qui fonctionne sous linux.

💣️Ce programme n'est pas optimisé du tout et ne suit pas vraiment les bonnes pratiques.


14.1. Prérequis

Le binaire linux "xdotool" doit être installé.

xdotool -v
# Sortie : 
# xdotool version 3.20160805.1


14.2. Le code à enregistrer dans le fichier simulation.php

Il suffit d'enregistrer le bout de code qui suit et de le lancer directement dans le terminal.

<?php
/**
 * Moves the cursor in case the user does not use it during inactivity time.
 *
 */
$defaultInactivityTime = 240;
$inactivityTime = $defaultInactivityTime; // Time in SECONDES in wich the user is considered as inactive.


function isUserMove()
{
    $mouseLocation = getMouseLocation();
    if ($mouseLocation['x'] > 1) {
        return true;
    }

    return false;
}

/**
 * Move the cursor in the screen.
 */
function simulateMouseMove()
{
    for ($i = 1; $i < 800; $i++) {

        // Stop the process if the user moves the cursor
        if ($i > 5) {
            if (isUserMove()) {
                return false;
            }
        }

        system('xdotool mousemove 1 '.$i);
    }
    for ($i = 800; $i > 1; $i--) {
        system('xdotool mousemove 1 '.$i);

        // Stop the process if the user moves the cursor
        if ($i > 5) {
            $mouseLocation = getMouseLocation();
            if (isUserMove()) {
                return false;
            }
        }
    }

    return true;
}


function getMouseLocation()
{
    $mouseLocation = exec('xdotool getmouselocation 2>&1 | sed -rn \'${s/x:([0-9]+) y:([0-9]+) .*/\1 \2/p}\'');

    $mouseCoordonates = explode(' ', $mouseLocation);

    if (count($mouseCoordonates) === 2) {
       return ['x' => $mouseCoordonates[0], 'y' => $mouseCoordonates[1]];
    }

    return null;
}

function echoState($message, $currentMouseLocation, $color = 'normal')
{
    $redColor = "\033[33m";
    $normalColor = "\033[0m";
//    echo "\ec";
    if ($color == 'red') {
        $currentColor = $redColor;
            $currentModeMessage= '/!\ SECURITY MODE /!\ ';
    } else {
        if (isUserMove()) {
            $currentColor = $normalColor;
            $currentModeMessage= '';
        } else {
            $currentColor = $redColor;
            $currentModeMessage= '/!\ SECURITY MODE /!\ ';
        }
    }


    echo sprintf("%s[%s]{x=%s,y=%s} %s%s".PHP_EOL,
        $currentColor,
        date('Y-m-d H:i:s'),
        str_pad($currentMouseLocation['x'], 4, " " , STR_PAD_LEFT),
        str_pad($currentMouseLocation['y'], 4, " " , STR_PAD_LEFT),
        $currentModeMessage,
        $message
    );
}



$lastState = [
    'time' => time(),
    'mouse_location' => null,
];


$simulationIsActive = false;
while(true) {
    $currentMouseLocation = getMouseLocation();

    if (!is_null($currentMouseLocation)) {
        if (!is_null($lastState['time']) && !is_null($lastState['mouse_location'])) {

            // Detects if the mouse is moved since the last mouvement.
            if (($lastState['mouse_location']['x'] === $currentMouseLocation['x']) &&
                ($lastState['mouse_location']['y'] === $currentMouseLocation['y'])
            ) {
                // Detects if the current user is inactive.
                $diffTimeSinceLastMouvement = time() - $lastState['time'];
                $timeLeftBeforeSimulating = $inactivityTime - $diffTimeSinceLastMouvement;
                if (($lastState['time'] + $inactivityTime) < time()) {
                    echoState('Simulate mouse move !', $currentMouseLocation, 'red');
                    $mouseSimulation = simulateMouseMove();
                    if (false === $mouseSimulation) {
                        // If the user interrupts the simulation.
                        // Reset the inactivity delay.
                        $inactivityTime = $defaultInactivityTime;
                        $simulationIsActive = false;
                    } else {
                        // Is the user is really inactive, reduce the delay.
                        $inactivityTime = rand(5, 30);
                        $simulationIsActive = true;
                    }
                    $lastState['time'] = time();
                } else {
                    echoState(sprintf('No mouse move since %s seconde(s). %s secondes before simulating',
                            $diffTimeSinceLastMouvement,
                            $timeLeftBeforeSimulating
                        ),
                        $currentMouseLocation
                    );
                }
            } else {
                echoState('The mouse has been moved !', $currentMouseLocation);
                $lastState['time'] = time();
            }
        }

        $lastState['mouse_location'] = $currentMouseLocation;
    }

    sleep(1);
}


15. Utiliser un fichier pour la mise en cache


Voici trois fonctions prêtes à l'emploi pour simplement enregistrer des données dans un fichier texte (PHP 5,7,8).

On peut par exemple enregistrer un tableau un peu conséquent en json pour éviter de le recalculer.

Pour rappel, le filemtime retourne le nombre de secondes écoulées depuis la modification du fichier.

function storeCachedContent($filePath, $content)
{
    return file_put_contents($filePath, $content);
}

function isCached($cacheFile, $expireTime = 360)
{
    if(file_exists($cacheFile) && filemtime($cacheFile) > $expireTime){
        return true;
    }

    return false;
}

function getCachedContent($cacheFile)
{
    return @file_get_contents($cacheFile);
}


15.1. Compresser les données


function store($file,$datas)
{
    return file_put_contents($file,gzdeflate(json_encode($datas)));
}

function unstore($file)
{
    return json_decode(gzinflate(file_get_contents($file)),true);
}


16. Distance en mètres entre deux points avec coordonnées GPS

16.1. Le code

<?php
/*---------------------------------------------------------------*/
/*
    Titre : Distance en mètre entre deux points avec coordonnées GPS                                                    
                                                                                                                          
    URL   : https://phpsources.net/code_s.php?id=459
    Auteur           : forty                                                                                              
    Website auteur   : http://www.toplien.fr/                                                                             
    Date édition     : 25 Sept 2008                                                                                       
    Date mise à jour : 10 Aout 2019                                                                                      
    Rapport de la maj:                                                                                                    
    - fonctionnement du code vérifié                                                                                    
    - modification de la description                                                                                      
*/
/*---------------------------------------------------------------*/
    // renvoi la distance en mètres
    function get_distance_m($lat1, $lng1, $lat2, $lng2) {
      $earth_radius = 6378137;   // Terre = sphère de 6378km de rayon
      $rlo1 = deg2rad($lng1);
      $rla1 = deg2rad($lat1);
      $rlo2 = deg2rad($lng2);
      $rla2 = deg2rad($lat2);
      $dlo = ($rlo2 - $rlo1) / 2;
      $dla = ($rla2 - $rla1) / 2;
      $a = (sin($dla) * sin($dla)) + cos($rla1) * cos($rla2) * (sin($dlo) * sin($dlo));
      $d = 2 * atan2(sqrt($a), sqrt(1 - $a));

      return ($earth_radius * $d);
    }


16.2. Exemple d'utilisation

<?php
    echo (round(get_distance_m(48.856667, 2.350987, 45.767299, 4.834329) / 1000, 3)). ' km'; // affiche 391.613 km

17. Remplacer l'extension PHP 5.3 mysql par mysqli


Cette page est une copie complète de cette page(https://www.linuxtricks.fr/wiki/php-passer-de-mysql-a-mysqli-requetes-de-base).

📖️️Source : https://www.linuxtricks.fr/wiki/php-passer-de-mysql-a-mysqli-requetes-de-base


17.1. Introduction

Depuis PHP 5.4, l'extension MySQL originale est obsolète, et génèrera des alertes de niveau E_DEPRECATED lors de la connexion à une base de données. A la place, on peut utiliser l'extension MySQLi ou l'extension PDO_MySQL.

Si comme moi, vous avez des sites avec l'extension MySQL, voici des petits exemples pour passer de MySQL à MySQLi (que je trouve plus simple d'emploi sur mes petites créations).

17.2. Connexion à la base

Auparavant, avec MySQL, se connecter à la base de données se faisait ainsi :

// on se connecte à MySQL
$conn = mysql_connect('$host', '$user', '$passwd');

// on sélectionne la base
mysql_select_db('mabase',$conn);


Maintenant, avec MySQLi, on utilise :

// on se connecte à MySQL et on sélectionne la base
$conn = mysqli_connect('$host', '$user', '$passwd', 'mabase');


17.3. Les requêtes

17.3.1 SELECT


Exemple de requête SELECT et affichage des résultats dans un tableau :

// On créé la requête
$req = "SELECT * FROM table1";

// on envoie la requête
$res = mysql_query($req);

// on va scanner tous les tuples un par un
echo "<table>";
while ($data = mysql_fetch_assoc($res)) {
    // on affiche les résultats
    echo "<tr><td>".$data['id']."</td><td>".$data['texte']."</td></tr>";
}
echo "</table>";


Maintenant, avec MySQLi, cela donne :

// On créé la requête
$req = "SELECT * FROM table1";

// on envoie la requête
$res = $conn->query($req);

// on va scanner tous les tuples un par un
echo "<table>";
while ($data = mysqli_fetch_array($res)) {
    // on affiche les résultats
    echo "<tr><td>".$data['id']."</td><td>".$data['texte']."</td></tr>";
}
echo "</table>";


17.3.2 INSERT / DELETE

Avec MySQL, on utilisait :

// On créé la requête
$req = "INSERT INTO table1(texte) VALUES ('Du texte')";

// on envoie la requête
$res = mysql_query($req);


Avec MySQLI, cet exemple devient :

// On créé la requête
$req = "INSERT INTO table1(texte) VALUES ('Du texte mysqli')";

// on envoie la requête
$res = $conn->query($req);


17.4. Fermer la connexion

Avec MySQL, clore la connexion à la base :

// on ferme la connexion
mysql_close();


Et bien, avec MySQLi, cela devient :

// on ferme la connexion
mysqli_close($conn);


17.5. Se protéger des injections SQL

// Se protéger des injections SQL
$username = $conn->real_escape_string($_GET['username']);
$conn->query("SELECT * FROM users WHERE username = '$username'");


17.6. Requête préparée

Voici un exemple de requête à trous, préparée utilisant l'extension MySQLi:

// mysqli, Requête préparée
$query = $conn->prepare('SELECT * FROM users WHERE username = ?');
$query->bind_param('s', $username); // s = string, i = integer
$query->execute();


17.7. Tester le nombre de lignes

// on crée la requête SQL
$req = "SELECT * FROM table1 WHERE chk_actif=1;";

// on envoie la requête
$res = $conn->query($req) or die();

// Si on a des lignes...
if ( $res->num_rows > 0 ) {
    echo "On a des résultats";
} else {
    echo "On n'a aucun résultat";
}