HervéRenault.fr

PHPJavaScript

Mon aide-mémoire PHP-JavaScript

Ce document m'aide à passer d'un langage à l'autre. C'est mon pense-bête, mon antisèche quand je programme. Je pourrais le cacher, mais je le mets en ligne pour pouvoir facilement le retrouver à tout moment, où que je sois. Le publier me motive aussi pour le maintenir en bon état et ne pas y écrire trop de bêtises.

J'avais fait une version PHP-Perl-Python-JavaScript-Java il y a quelques années, mais je l'ai abandonnée parce que je n'utilise plus que PHP et JavaScript.

Ce document a donc une longue histoire et conserve encore des traces de PHP 5 et des remarques sur Internet Explorer. Quand je peux, j'y ajoute des éléments de PHP 7 ou 8 (à l'heure où j'écris) et de JS ES6, etc.

Dernière remarque, sur la présentation : c'est très compact, pour me permettre de retrouver rapidement ce que je cherche. Ce n'est donc pas un document didactique.

  PHP Javascript
doc php.net
Liste de toutes les fonctions
MDN
essais
php -a
F12 ou Shift-Ctl-K dans Firefox 11+
ou node en ligne de commande
valider la syntaxe
php -l script.php

Analyse statique : PHPStan, Phpactor (prononcer "factor")
ESLint

ou node -c mon_script.js
commentaires /* foo bar (plusieurs lignes possibles) */

// foo bar (une seule ligne)

/**
 * foo bar
 * format PHPDoc
 */

# vieux style "shell", une seule ligne

Attention depuis PHP 8 :

#[Foo('bar', 'bidule')] est un attribut

(plusieurs lignes possibles)
/* foo bar (plusieurs lignes possibles) */

// foo bar (une seule ligne)
variable $a = 123; // ancien, ne plus utiliser :
a = 123;
var a = 123; // globale, peut être redéclarée = danger

// depuis ES6
let a = 123; // locale au bloc, peut être mise à jour
constante
define("FOO", 42);
echo FOO;
// ou
echo constant("FOO");

// dans une classe A
const FOO = 42;
echo self::FOO; // dans la classe
echo A::FOO; // ailleurs
// depuis ES6 :
const foo = 42;

// mais on peut redéfinir la constante dans un bloc :

const foo = 42;
if (true) {
  const foo = 53; // c'est permis !
  console.log(foo);
}
console.log(foo); // 42
incrémenter $a++; ++$a;
a++; ++a;
diviser $a /= 2;        # 62.5
a /= 2;        // 62.5

modulo
$a = 5 % 2;
a = 5 % 2;
typage
$a = 1;
$a += "b"; // ça passait jusqu'à PHP 5
// PHP 7 Warning A non-numeric value encountered
// PHP 8 Warning Unsupported operand types: int + string

// Pour interdire ça complètement, depuis PHP 7 :
declare(strict_types=1);
// cf la doc.
a = 1;
a += "b"; // "1b"
if ...
if ($a == 1) {
   echo "un";
} elseif ($a == 2) {
   echo "deux";
} else {
   echo "je ne sais pas";
}
if (a == 1) {
    alert("un");
} else if (a == 2) {
    alert("deux");
} else {
    alert("je ne sais pas");
}
switch
switch($a) {
    case 1:
        echo "un";
        break;
    case 2:
        echo "deux";
        break;
    default:
        echo "je ne sais pas";
}
switch(a) {
    case 1:
        alert("un");
        break;
    case 2:
        alert("deux");
        break;
    default:
        alert("je ne sais pas");
}
nouveauté PHP 8
plus pratique que switch
(un bon article)
$b = match ($a) {
    1, 2, 3 => 'pas beaucoup',
    4 => 'quatre',
    5, 6, 7, 8, 9 => 'plus que quatre',
    default => 'au moins une dizaine',
};
pas d'équivalent pour le moment
if ($a === "2") {
    echo "égaux et du même type";
} elseif ($a == "2") {
    echo "égaux (avec conversion)";
} else {
    echo "pas égaux";
}

# l'inverse de === est !==
if (a === "2") {
    alert("égaux et du même type");
} else if (a == "2") {
    alert("égaux (avec conversion)");
} else {
    alert("pas égaux");
}

// l'inverse de === est !==
variable non
initialisée
if (!isset($b)) {
    echo '$b non initialisée !';
    echo $b; // PHP Notice:  Undefined variable
}
if (typeof b == 'undefined') {
    alert("b non initialisée");
    alert(b); // ReferenceError: b is not defined
}
variable "vide"
ou comme on dit en anglais, "falsy" (vaguement fausse)
if (empty($a)) {
  // 0 ou '' ou false ou null ou non-initialisée
}
if (Boolean(a)) {
  // 0 ou '' ou false ou null mais pas non-initialisée !
  // ReferenceError si non-initialisée
}

// variante cosmétique ("not not")
if (!!a) {
  ...
}
commenter
// une ligne
# une ligne
/*
plusieurs lignes
 */
// une ligne
/*
plusieurs lignes
 */
afficher sans passer à
la ligne
echo abs(-4);
document.write(Math.abs(-4));
en passant à la ligne
echo abs(-4)."\n";
document.writeln(Math.abs(-4));
// ou write(4 + "\n");
contenu sur plusieurs lignes ("heredoc" ou "here-doc") $a = <<<EOT
ligne 1
ligne 2
EOT;

echo $a;
// template literals ES6, cf MDN

a = `ligne 1
ligne 2`;

console.log(a);

// pour détecter si c'est utilisable (= pas IE)
function supportsLiterals() {
  try {
    return eval("''===``")
  }
  catch(e) {
    return false;
  }
}
écrire sur la sortie d'erreur fwrite(STDERR, "oups\n"); console.error("oups");
booléens
$a = TRUE; # true, True, TrUe ...
a = true;
opérateurs logiques
if (1 && 1) {
    echo "vrai\n";
}
if (1 and 1) {
    echo "idem\n";
}

if (1 || 0) {
    echo "vrai aussi\n";
}
if (1 or 0) {
    echo "idem\n";
}
if (true && true) {
    alert("vrai");
}
if (true || false) {
    alert("vrai aussi");
}
chaîne de
caractères
$a = "test";
$a = 'test';
a = "test";
a = 'test';

echo $a[2];  // ou $a{2}
alert(a.charAt(2));
concaténation
$a .= "foo";
a += "foo";

echo strlen($a);
// pour Unicode :
echo mb_strlen($a);
// ou encore :
echo grapheme_strlen($a)
alert(a.length);
code caractères
for ($i = 0; $i < strlen($a); $i++) {
    echo ord($a[$i])."\n";
}
for (i in a) {
    alert(a.charCodeAt(i));
}
normaliser les accents du Mac de 3 octets en 2 octets echo normalizer_normalize('é', Normalizer::FORM_C );
"stf"
echo substr($a,2,3);

// pour UTF-8 :
mb_substr('hé !',1,1,'UTF-8');
alert(a.substring(2,5)); // start, stop
// ou
alert(a.substr(2,3)); // start, length
// voir aussi slice()
"tfoo"
echo substr($a,3); alert(a.substring(3));
// ou
alert(a.substr(3));
"testf"
echo substr($a,0,-2); alert(a.substring(0,a.length - 2));

echo strtoupper($a);

// pour de l'UTF-8 :
echo mb_strtolower('HÉ !', 'UTF-8');
alert(a.toUpperCase());
"Testfoo"
echo ucfirst($a);
alert(a.charAt(0).toUpperCase()+a.substr(1));

echo ucwords("jorge luis borges");

// pour UTF-8 :
echo mb_convert_case('évariste galois', MB_CASE_TITLE, 'UTF-8');

chaîne de caractères sur plusieurs lignes echo "voici une ".
     "très longue ".
     "phrase";
alert("voici une "
+ "très longue "
+ "phrase");

if (strpos("hervérenault.fr", "a") > -1) {
    echo "a y est\n";
}
if ("hervérenault.fr".indexOf("a") > -1) {
    alert("a y est");
}
// note : IE8 n'a pas indexOf
// utiliser jQuery.inArray()

if (strpos("hervérenault.fr", "foo") === false) {
    echo "pas foo\n";
}
if ("hervérenault.fr".indexOf("foo") == -1) {
    alert("pas foo");
}
supprimer le retour à la ligne en fin de chaîne
$a = "bonjour\n";
$a = rtrim($a, "\n");
print "$a !\n";
// à partir d'IE10 ?
if (String.prototype.trim) {
    alert("bonjour\n".trim() + " !");
}

// sinon, aveec une regexp :
a = "bonjour\n".replace(/\n/, "");
alert(a + " !");

// ou jQuery.trim()
supprimer les blancs avant et après
$a = "  bonjour \t ";
echo "[".trim($a)."]";
idem précédent
formatage
printf("%s a %d ans\n", "Jean", 60); // avec ES6 : utiliser les template literals
let a = ['Jean', 60];
alert(`${a[0]} a ${a[1]} ans\n`);

// note : les template literals permettent
// d'autres choses très intéressantes

$s = sprintf("%s a %d ans\n", "Jean", 60); // avec ES6 : utiliser les templates literals
let a = ['Jean', 60];
s = `${a[0]} a ${a[1]} ans\n`;
regular expressions
(regexps)
$botte_de_foin = "Jean a 60 ans";
$aiguille = "j[ue]an";
if (preg_match("/$aiguille/i", $botte_de_foin)) {
    echo "trouvé en ignorant la casse !\n";
}
botte_de_foin = 'Jean a 60 ans';
aiguille = /j[ue]an/i;

// on peut écrire aussi :
aiguille = new RegExp('j[ue]an', 'i');

// on peut même passer une chaîne qui est convertie
// implicitement en objet RegExp (mais sans i)

if (botte_de_foin.match(aiguille)) {
    alert("trouvé en ignorant la casse !");
}
extraire des groupes d'une regexp
$a = "Jean a 60 ans";
if (preg_match("/([A-Z].*?) /", $a, $m)) {
    echo $m[1];
}
a = "Jean a 60 ans";
m = a.match("([A-Z].*?) ");
if (m) {
    alert(m[1]);
}
extraire toutes les occurences d'une regexp
$a = "Jean est plus vieux que Jérome et Jacques";
preg_match_all("/J\w+/u", $a, $m);
print_r($m);

# Note: /u pour que \w matche é
a = "Jean est plus vieux que Jérome et Jacques";
re = RegExp('([A-Z].*?) ', 'g');
while ((matches = re.exec(a)) !== null) {
    alert(matches[0]);
}

// ou en ES2020, si on peut ignorer IE, matchAll
substitutions
$a = preg_replace('/^\w+/', "Antoine", $a);
echo $a;
a = a.replace(/^\w+/, "Antoine");
alert(a);
avec backreference (référence arrière) echo preg_replace("/^(\w+)/", "Mon voisin $1", $a); alert(
a.replace(/^(\w+)/, "Mon voisin $1")
);
nombre de "a" substitués par "?" (2)
preg_replace('/a/', "?", $a, -1, $count);
echo $count;
// en deux étapes
m = a.match(/a/g);
a = a.replace(/a/g, "?");
alert(m.length);

idem en ignorant la casse (3)
preg_replace('/a/i', "?", $a, -1, $count);
echo $count;
m = a.match(/a/ig);
a = a.replace(/a/ig, "?");
alert(m.length);
matcher tout y compris un passage à la ligne (\n)
$a="Voici une ligne
et puis deux et voilà";
$a = preg_replace('/une.*?deux/s', "", $a);
echo $a;
a="Voici une ligne\net puis deux et voilà";
a = a.replace(/une[\s\S]*?deux/, "");
alert(a);
particularité PHP
// pour matcher de l'UTF-8
$a = "HÉ !";
echo preg_replace('/é/iu', 'a', $a);

chaîne contenant un nombre ?
$a = "123.45";
if (is_numeric($a)) {
    echo "ça ressemble à un nombre";
}
var a = '123.45';
if (!isNaN(a)) {
    alert('ça ressemble à un nombre');
}
variable contenant un entier ?
$a = 123;
if (is_int($a)) {
    echo "c'est un entier";
}
TODO
un décimal ?
$a = 123.45;
if (is_float($a)) {
    echo "c'est un décimal";
}
TODO
boucle
for ($x = 2; $x < 6; $x++) {
    echo "$x\n";
}

// ou

foreach (range(2, 5) as $x) {
    echo "$x\n";
}
for (var x = 2; x < 6; x++) {
    alert(x);
}

echo str_repeat(".", 3);

saute 3
arrête à 10
$a = 0;
while (true) {
    $a++;
    if ($a == 3) continue;
    if ($a > 10) break;
    echo "$a\n";
}
let a = 0;
while (true) {
    a++;
    if (a == 3) continue;
    if (a > 10) break;
    console.log(a);
}
liste homogène
$a = array(1, 2, 3, 4);
a = [1, 2, 3, 4];
// ou
a = Array(1, 2, 3, 4);
// ou
a = new Array(1, 2, 3, 4);
liste hétérogène
$a = array(1, 2, 'foo', 'bar');

// à partir de PHP 5.4 :
// $a = [1, 2, 'foo', 'bar'];
a = [1, 2, 'foo', 'bar'];
// ou
a = Array(1, 2, 'foo', 'bar');
// ou
a = new Array(1, 2, 'foo', 'bar');

echo $a[2];
alert(a[2]);

$a[] = 5;
// ou
array_push($a, 5);
a.push(5);

if (in_array(3, $a)) {
    echo "3 y est\n";
}
if (a.indexOf(3) > -1) {
    alert("3 y est");
}
// note : IE8 n'a pas indexOf
// utiliser jQuery.inArray()

// Attention avec le DOM !
// indexOf n'existe pas sur une classList
document.body.classList.indexOf('foo');
// classList n'est pas un Array malgré la ressemblance
// classList a sa propre fonction "contains"
document.body.classList.contains('foo');

foreach ($a as $b) {
    echo $b;
}
for (b in a) {
  console.log(a[b]);
}
// attention à for...in !
// s'il y a des propriétés ajoutées au prototype de Array
// elles apparaîtront ici.
// pour éviter ça :
for (b in a) {
  if (Object.hasOwn(a, b)) {
    console.log(a[b]);
  }
}
// avec IE il fallait faire if (a.hasOwnProperty(b))

// ou plus simplement
for (b of a) {
  console.log(b);
}
// parce qu'un Array est itérable
liste d'affectations
list($b, $c, $d) = $a;    # le 4e est ignoré

# swap :
list($b, $c) = array($c, $b);
// depuis ES6 :
[b, c, d] = a;
// le 4e est ignoré

// affectation par décomposition
// (destructuring assignement)
[b, c, ...d] = a;
// ⇒ d contient ["foo", "bar"]
idem avec un objet // voir get_object_vars() // ES6
const a = {
  truc: 123,
  bidule: "coucou",
  machin: 456
}

let {truc, bidule, machin} = a;
type
if (is_array($a)) {
    echo "array";
}
// piège !
if (a instanceof Array) {
  alert('a a Array dans sa chaine de prototype');
  // mais si le prototype est modifié...
  // ça peut ne plus marcher !
}

// plus sûr :
if (Array.isArray(a)) {
  alert('a est vraiment un Array');
}
join
$b = join(", ", $a);

// ou

$b = implode(", ", $a);
b = a.join(", ");
split
$a = explode(", ", $b); // pas de regexp !

// ou
$a = preg_split('/, /', $b);
// mais pas split : obsolète depuis 5.3
a = b.split(", ");

// ou
a = b.split(/, /);
variante
list($c, $d, $reste) = split(", ", $b, 3);


echo $a;
# affiche Array
alert(a);
// affiche 1,2,foo,bar

print_r($a);
# ou plus précis :
var_dump($a);
console.log(a);

echo $a[1];
alert(a[1]);

echo count($a);
alert(a.length);

echo $a[count($a) -1];
# ou
echo end($a);
alert(a[a.length -1]);

$b = array(1,1,1,2,3,3,4);
$b = array_unique($b);
print_r($b);

# affiche :
Array
(
    [0] => 1
    [3] => 2
    [4] => 3
    [6] => 4
)
# attention, $b[1] n'existe plus !

copier une liste $b = $a; /* JS copie par référence par défaut. Dans le jargon JS, c'est une "copie superficielle" (shallow copy) et pour faire une copie des valeurs comme en PHP, il faut une "copie profonde" (deep copy) comme ceci : */

b = JSON.parse(JSON.stringify(a));
listes dynamiques
imbriquées
$a = array(1,2,3,array(4,5),6,7);
a=[1,2,3,[4,5],6,7];
hash (table de hachage, "dictionnaire" en Python, "associative array" en PHP)
$a = array("foo" => 3, "bar" => 4, "baz" => NULL);
a = { "foo" : 3, "bar" : 4, "baz" : null };

// a est un objet
// "foo" est une propriété
// a n'est pas ordonné (ordre pas garanti)



// il existe aussi l'objet Map qui est ordonné et itérable
const a = new Map([
  ["foo", 3],
  ["bar", 4],
  ["baz", null],
]);

a.set("et aussi", "coucou");

for (const [k, v] of a) {
  console.log(k, v);
}

echo $a["foo"]; alert(a["foo"]);
// ou
alert(a.foo);

// pour un objet Map :
alert(a.get('foo'));
taille
echo count($a);
# ou
echo sizeof($a);
Object.keys(a).length

// pour un objet Map : a.size

if (! array_key_exists("truc", $a)) {
    echo "clé truc n'existe pas\n";
}
if (a["truc"] === undefined) {
    alert("clé truc n'existe pas");
}

// ou
if (!Object.hasOwn(a, "truc"))

// ou plus ancien (à l'époque d'IE)
if (!a.hasOwnProperty("truc"))

if ($a["baz"] == null) {
    echo "pas de valeur associée à baz\n";
}
// si pas de clé, PHP Notice: Undefined index

// attention !
if (!isset($a["baz"])) {
    echo "pas de clé OU de valeur associée !\n";
}
// attention++ !
if (empty($a["baz"])) {
    echo "idem OU valeur 0 ou chaine vide\n";
}
if (a["baz"] === null) {
    alert("pas de valeur associée à baz");
}

// attention !
if (a["baz"] == undefined) {
    alert("pas de clé OU de valeur associée !");
}
// idem :
if (a["baz"] == null) {
    alert("pas de clé OU de valeur associée");
}

foreach ($a as $k => $v) {
    echo "$k => $v\n";
}

// ou

reset($a);
while (list($k, $v) = each($a)) {
    echo "$k => $v\n";
}
for (k in a) {
    alert(k + " => " + a[k]);
}

// attention à for...in !
// s'il y a des propriétés ajoutées au prototype de a
// elles apparaîtront ici.
// pour éviter ça :
for (k in a) {
  if (a.hasOwnProperty(k)) {
    console.log(k + " => " + a[k]);
  }
}

// ou employer une Map et faire :
for (const [k, v] of a) {
  console.log(k, v);
}

// ⚠️ on ne peut pas faire for (b of a) comme un Array
// parce que ce n'est pas un itérable

unset($a["foo"]);
delete a["foo"];

// pour un objet Map :
a.delete('foo');
intersection
(2,3)
array_intersect(array(1,2,3), array(2,3,4));

// fonctionne aussi avec des arrays assoc.

différence array_diff(['foo', 'bar'], ['foo', 123]) // [1 => 'bar']

// ⚠️ array_diff([], ['foo', 123]) retourne un array vide
// mais
// array_diff(['foo', 'bar'], []) retourne ['foo', 'bar']
TODO
différence symétrique
(1,4)
$a = array(1,2,3);
$b = array(2,3,4);
$i = array_intersect($a,$b);
$d = array_merge(array_diff($a,$i), array_diff($b,$i));
TODO
trier une liste
$a = array(4, 2, 3, 10);

sort($a); // tri numérique
a = [4, 2, 3, 10];

a = a.sort(); // tri alphabétique

// tri numérique :
a.sort(function(x,y) { return x - y; });
avec des accents UTF-8
$b = array('a', 'z', 'é', 'b', 'e');

setlocale(LC_COLLATE, 'fr_FR.UTF-8');
sort($b, SORT_LOCALE_STRING);

// ou

$collator = new Collator('fr_FR');
$collator->sort($b);
// voir aussi Collator::asort (mais pas de ksort)
b = ['a', 'z', 'é', 'b', 'e'];
b.sort((x, y) => x.localeCompare(y, 'fr'));

// voir autres options de localeCompare
comparer deux chaines UTF-8
setlocale(LC_COLLATE, 'fr_FR.UTF-8');
echo strcoll('élément', 'zéro');
// pas strcmp !
alert('élément'.localeCompare('zéro'));
// locale par défaut
max d'une liste
echo max($a);
alert(Math.max.apply(null, a));
retirer un élément en tête
echo array_shift($a); # 1
alert(a.shift());
en fin de liste
echo array_pop($a); # 4 alert(a.pop());
ajouter en tête
array_unshift($a, 5);
a.unshift(5);
ou ailleurs
array_splice($a,2,0,9);
a.splice(2,0,9);
concaténer deux listes ou hashes
$c = array_merge($a, $b);
// $b écrase $a si clés communes

// ou
$c = $a + $b;
// $a écrase $b si clés communes

// NE PAS utiliser array_merge si clé numériques
// exemple [123 => 'foo', 456 => 'bar']
// parce que array_merge renumérote
// comme indiqué dans la doc
c = a.concat(b);

# hashes avec jQuery
c = $.extend({}, a, b);
# ou étend a avec b
$.extend(a, b)
modifier une liste
$a = array_map(function($v) { return $v * 2; }, $a);

// ou
array_walk($a, function(&$v) { $v *= 2; }); // 5.3+

// ou avant PHP 5.3 :
function modif(&$v) {
    $v *= 2;
}
array_walk($a, "modif");
// à partir de IE9
a = a.map(function(x) { return x * 2; })

// avec ES6 et les fonctions fléchées
a = a.map(x => x * 2);

// (tester Array.prototype.map)
avec un paramètre supplémentaire
array_walk($a, function(&$v, $k, $p) {
    $v *= $p;
}, 3);

filtrer une liste (garde les éléments pairs) $a = array(1,2,3,4);
$b = array_filter($a, function($e) {
    return $e % 2 == 0;
});
a = [1,2,3,4];
b = a.filter(function(e) {
    return e % 2 == 0;
});
trouver si la liste contient au moins un élément qui satisfait une condition array_reduce($a, function ($retour, $element) {
    return $retour || $element > 2;
})

// syntaxe condensée depuis PHP 7.4 :
array_reduce(
    $a, fn($retour, $element) => $retour || $element > 2
)

// assez concis mais moins efficace que some() en JS
// parce qu'on va parcourir toute la liste
a.some(element => element > 2)

// plus efficace que l'exemple en PHP
// stoppe à la 1re occurrence qui satisfait la condition
remplir un tableau ou une portion d'un tableau avec une valeur donnée $a = [];
$a = array_fill(2, 3, 'foo');
print_r($a);
Array
(
    [2] => foo
    [3] => foo
    [4] => foo
)
// ES6
const a = ['', '', '', '', '']; // il doit être pré-rempli
a.fill('foo', 2, 5);
Array(5) [ "", "", "foo", "foo", "foo" ]
trouver le premier élément supérieur à 2 dans liste foreach ($a as $v) {
    if ($v > 2) {
        echo $v;
        break;
    }
}
a.find(v => v > 2);

// c'est super compact grâce à la fonction flèche
// mais ça peut s'écrire plus verbeusement :
a.find(function(v) {
  return v > 2;
});

MDN Array.prototype.find()
trouver les éléments d'une liste par une regex $a = preg_grep('/u+/', ['truc', 'bidule', 'chose']);
print_r($a);
Array
(
    [0] => truc
    [1] => bidule
)


// ne pas confondre avec array_search (cf ci-dessous)
?
trouver un élément de liste par sa valeur
retourne uniquement 'f*' (chaîne de caractères)
$a = ['foo' => 'bar', 'f*' => 'you', 'hello' => 'you'];
$b = array_search('you', $a);

// une clé peut être 0 ou '' ou false, donc :
if ($b === false) { // pas trouvé…
// et non if ($b == false) qui entraîne une conversion
a = {'foo': 'bar', 'f*': 'you', 'hello': 'you'};
b = Object.keys(a).find(clé => a[clé] === 'you');

if (b == undefined) [ // pas trouvé…
trier un hash par valeur (et non par clé)
$a = array("tom" => 5, "jerry" => 2, "alfonso" => 3);

asort($a);

// ou

array_multisort(array_values($a), $a);
a = { "tom" : 5, "jerry" : 2, "alfonso" : 3 };
// a est un objet, il n'y a pas d'ordre

// mais on peut créer un array d'arrays
// qui est ordonné, lui...
b = [];
for (k in a) {
    b.push([k, a[k]]); // [clé, valeur]
}
b.sort(function(x,y) { return x[1] - y[1]; });

console.log(b);
   

trier un hash par clé
ksort($a);

// voir aussi uksort
// mieux vaut utiliser une Map pour ce genre de chose
référence $aref = &$a;
aref = a;
// en JS, tout est par référence par défaut

echo $aref['tom']; # affiche 5
$a['tom'] = 123;
echo $aref['tom']; # affiche 123
console.log(aref['tom']); // affiche 5
a['tom'] = 123;
console.log(aref['tom']); // affiche 123

$a = array('foo', 'bar', 123);
foreach ($a as $i => $v) {
    echo "index $i : valeur $v\n";
}
a = ['foo', 'bar', 123];
// depuis ES6, avec un itérateur :
for (const [i, v] of a.entries()) {
  console.log('index %d : valeur %s', i, v);
}
fonction avec un nombre variable d'arguments (ou paramètres)

(variadic function, fonction variadique)
function f() {
    echo "nbe d'args : ".func_num_args()."\n";
    print_r(func_get_args());
}

// à partir de PHP 5.6 :
function f(...$params) {
    echo "nbe d'args : ".count($params)."\n";
    print_r($params);
}

f(4, 3, 2, 1);

// possible aussi de déclarer function f($a, $b, ...$c)
function f() {
    console.log("Nbe d'args : " + arguments.length);
    for (var i=0; i < arguments.length; i++) {
        console.log(arguments[i]);
    }
}

f(4, 3, 2, 1);
passer une liste d'arguments à une fonction
("unpacking", déballer les arguments)
function f($a, $b, $c) {
    echo $a + $b + $c;
}

$a = array(5, 6, 7);

// depuis PHP 5.6 :
f(...$a);

// avant 5.6, obligé de faire :
f($a[0], $a[1], $a[2]);
function f(a, b, c) {
    console.log(a + b + c);
}

a = [5, 6, 7];

f.apply(null, a);
ne pas respecter le nombre d'arguments passés à une fonction f(1, 2, 3, 4); // ça passe

f(1, 2); // PHP Warning: Uncaught ArgumentCountError

// dans une surcharge de fonction :
// PHP Warning: Declaration of B::f($a, $b)
//   should be compatible with A::f($a)
// le nombre d'arguments doit être le même
f(1, 2, 3, 4); // ça passe

f(1, 2); // ça passe… mais ça affiche NaN !
paramètres par défaut / paramètres optionnels function f($a = 'truc', $b = 123) {
    echo $a.' '.$b;
}

f('coucou', 1); // coucou 1
f('coucou'); // coucou 123
f(); // truc 123
// si absent,
function f(a = 'truc', b = 123) {
  console.log(a + ' ' + b);
}

f('coucou', 1); // coucou 1
f('coucou'); // coucou 123
f(); // truc 123
paramètres nommés // depuis PHP 8.0
function f($a, $b) {
    echo 'et voilà '.$a.$b;
}

f(b: 'bar', a: 'foo');
// et voilà foobar
// depuis ES6 (tous sauf IE)
// avec le destructuring assignement
function f({a, b}) {
    console.log('et voilà ' + a + b);
}

f({b: 'bar', a: 'foo'});
typage des paramètres (type hinting ou type declaration) introduit progressivement depuis PHP 5 function f(int $a): boolean {
// exemple :
// paramètre $a de type entier
// retour de type booléen
// impossible en JS, utiliser TypeScript
type "nullable" depuis PHP 7.1 function f(?int $a) {
// signifie que $a peut être de type int ou null
// impossible en JS, utiliser TypeScript
type composite depuis PHP 8.0 function f(string|int $a) {
// signifie que $a peut être de type string ou int
// cf doc pour les nombreux détails
// impossible en JS, utiliser TypeScript
fonction lambda (anonyme)
$f = function($a, $b) { return $a + $b; };

// depuis PHP 7.4, syntaxe "fonction flèche" plus courte :

$f = fn($a, $b) => return $a + $b;

// pour mémoire, avant PHP 5.3 c'était :
// $f = create_function('$a,$b', 'return $a + $b;');

echo $f(1, 2);
f = function(a, b) { return a + b; }

alert(f(1,2));

// depuis ES6, les "arrow functions" :
// (attention, pas de this, arguments, call, apply, etc.)
f = (a, b) => { return a + b };

// autre forme encore plus compacte :
f = (a, b) => a + b;

// encore plus compact s'il n'y qu'un paramètre :
f = a => a * 2;

// et s'il n'y a pas de paramètre :
f = () => Math.round(Math.random() * 100);

// pour retourner un objet, mettre des parenthèses :
f = a => ({bidule: a});
particularité PHP 5.3+
$a = array(0,1,2,3,4,5,6,7,8,9);

foreach (array(2,3,4) as $d) {
    $b = array_filter($a, function($v) use($d) {
        return !($v % $d);
    });
    print_r($b);
}

particularité Javascript
// exécution immédiate d'une fonction anonyme
(function() {
    var a = 'coucou';
    alert(a);
})();

// ou (moins explicite)
!function() {
    var a = 'bonjour';
    alert(a);
}();
// le ! provoque l'évaluation de ce qui suit
modifier une liste dans une fonction (dans cet exemple, remplacer tout le contenu de la liste, pour que ce soit plus parlant)
function change(&$a) {
    $a = array(5, 6);
}

change($a);
print_r($a);


même chose en ajout
function change(&$a) {
    $a[] = 5;
    $a[] = 6; // ou :
    array_push($a, 7);
}

change($a);
print_r($a);


eval('print join("+", $a);');
# ne pas oublier le point-virgule

variable globale
$a = 1;
function b() {
    global $a;
    echo $a;    # OK
    $a = 2;
}
function c() {
    echo $a;    # "Undefined variable"
}

b();    # affiche 1
echo $a;    # affiche 2
a = 1;
// ou var a = 1;
function b() {
    alert(a);
    a = 2;
}

b();    // affiche 1
alert(a);    // affiche 2

// ici, a est en fait window.a

// et si on ne l'avait pas créée par a = 1
// on l'aurait créée globale dans la fonction !
// (dangereux, interdit en mode strict)
variable locale
$a = 1;
function b() {
    $a = 2;
    echo $a;
}
function c($a) {
    echo $a;
}

c(3); # affiche 3
b(); # affiche 2
echo $a; # affiche 1
a = 1;
// ou var a = 1;
function b() {
    var a = 2;
    alert(a);
}
function c(a) {
    alert(a);
}

c(3);    // affiche 3
b();    // affiche 2
alert(a);    // affiche 1
déréférencer une liste en retour d'une fonction (exemple)
$a = "foo-bar";
// à partir de PHP 5.4 :
echo explode('-', $a)[0];

// PHP 5.3
echo current(explode('-', $a)); // foo
a = "foo-bar";
alert(a.split('-')[0]);
vérifier si une fonction existe if (function_exists('a')) {
    a();
}
if (this.window['a'] && typeof(this.window['a'] === 'function')) {
    a();
}

// ou, par exemple, pour savoir si la
// fonction indexOf existe pour les tableaux :
if ('indexOf' in Array.prototype) {
    alert("foobar".indexOf("b"));
}
// ou encore :
if (Array.prototype.indexOf) {
    alert("foobar".indexOf("b"));
}
appeler une fonction par son nom
function a($a) {
    echo "ici ".__FUNCTION__."($a)\n";
}

$b = 'a';
call_user_func($b, 'foo');
call_user_func_array($b, array('foo'));
$b('foo');
function a() {
    alert('ici ' + arguments.callee.name);
}

b = 'a';
window[b]();
// car appeler a() revient à appeler window['a']()

function a() {
    echo "ici ".__FUNCTION__."\n";
    echo "ligne ".__LINE__."\n";
    echo "fichier ".__FILE__."\n";
    echo "répertoire ".__DIR__."\n";
}

# avant PHP 5.3 __DIR__ n'existait pas
# => dirname(__FILE__)
function a() {
    alert('ici ' + arguments.callee.name);
    // ou afficher la déclaration de a :
    alert('ici ' + arguments.callee);
}
nom de la fonction appelante function a() {
    list (,$a) = debug_backtrace();
    echo "appel par ".$a['function'];
    if (isset($a['class']))
        echo " dans ".$a['class'];
}

function b() {
    a();
}

b(); // affiche "appel par b"
function a() {
    alert('appel par ' + arguments.callee.caller.name);
    // ou la déclaration de l'appelant :
    alert('appel par ' + arguments.callee.caller);
}

function b() {
    a();
}

b(); // affiche "appel par b"
modules / inclusion de code include 'fichier.php';
include_once 'fichier.php'

// provoque une erreur si le fichier n'existe pas
require 'fichier.php';
require_once 'fichier.php';

// si chemin relatif
// prend en compte la directive include_path
// il y a plusieurs formats de modules

// CommonJS est le format d'origine pour Node :
const bidule = require('bidule');

// ESM (ES modules) a été standardisé pour le navigateur :
import './bidule.js';
// ou
import { bidule } from './bidule.js';

// ESM est supporté par Node depuis la version 12 (2020 ?)

// Et il y a les imports dynamiques (asynchrones)
// qui permettent ça :
if (machin) {
  import('./bidule.js');
}
// ou
function truc() {
  console.log('coucou');
  import('./bidule.js');
}
// contrairement aux imports statiques
debug (débuguer) pas à pas // j'utilise Xdebug + Vdebug mais il en existe d'autres // dans le navigateur : F12 puis onglet Débogueur
// (Maj Ctrl Z ne fonctionne pas chez moi, pas grave)

// dans Node.js : node inspect myscript.js

// dans Pug - console.log(a) (pas de pas à pas)
particularité JavaScript, les fonctions asynchrones // exemples dans Node, possible aussi dans le navigateur


// fonction de rappel (callback) en argument
const fs = require('fs');
fs.readFile('fichier-long-à-lire', (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log('fini de lire !');
  // faire quelque chose avec les data
});
console.log('début');


// avec les promesses (Promise) de ES6
const fs = require('fs/promises');
fs.readFile('fichier-long-à-lire')
  .then((data) => {
    console.log('fini de lire !');
    // faire quelque chose avec les data
  })
  .catch(err, () => {
    console.error(err);
  });
console.log('début');


// avec la notation async await de ES7
const fs = require('fs/promises');
(async function() {
  try {
    const data = await fs.readFile('fichier-long-à-lire');
    console.log('fini de lire !');
    // faire quelque chose avec les data
  } catch(err) {
    console.error(err);
  }
})();
console.log('début');
mysql try {
    $dbh = new PDO(
        'mysql:host=localhost;dbname=test',
        'u',
        'p'
    );

    $sth = $dbh->query("create table if not ...");

    # ...

    # non ! $a = $dbh->quote($a);
    $sth = $dbh->prepare("select...where a=?");
    $sth->execute(array($a));

    echo $sth->rowCount()."\n";

    while($row = $sth->fetch()) {
        echo $row[0];
    }
    # ou :
    # while($row = $sth->fetch(PDO::FETCH_ASSOC)) {
    #        echo $row["a"];
    # }
    # ou $rows = $sth->fetchAll();
} catch (PDOException $e) {
    echo "Erreur : ".$e->getMessage();
}
// pour une séquence de requêtes, async/await est pratique
// pour éviter l'enfer des fonctions de rappel

var mysql = require('mysql2/promise');

var con = mysql.createConnection({
  host: 'localhost',
  database: 'test',
  user: 'u',
  password: 'p',
});

con.connect(async function(err) {
  if (err) throw err;
 
  try {
    let res = await con.query('create table etc...');
   
    // ...
   
    // non ! const a = con.escape(b);
    res = await con.query('select...where a = ?', [a]);
   
    console.log(res[0].length);

    for (const row of res[0]) {
      console.log(row.nom_du_champ);
    }
  } catch (e) {
    console.log('erreur', e);
  }
 
  con.end(); // sinon ça reste en attente
});
objet
(version minimale à la PHP 4 ou 5)
class A {
    function __construct($a1, $a2, $a3, $a4) {
        $this->a1 = $a1;
        $this->a2 = $a2;
        $this->a3 = $a3;
        $this->a4 = $a4;
    }

    function somme() {
        return $this->a1
            + $this->a2
            + $this->a3
            + $this->a4;
    }
}

$a = new A(1, 2, "foo", "bar");
// num + chaîne était toléré en PHP < 7, warning en PHP 7

echo $a->somme();

// 3
function A(a1,a2,a3,a4) {
    this.a1 = a1;
    this.a2 = a2;
    this.a3 = a3;
    this.a4 = a4;
    // cette méthode serait dupliquée
    // dans chaque instance :
    /* this.somme = function() {
        return this.a1
            + this.a2
            + this.a3
            + this.a4;
    }; */

}

// mieux :
A.prototype.somme = function() {
    return this.a1
        + this.a2
        + this.a3
        + this.a4;
};

a = new A(1,2,"foo","bar");
console.log(a.somme());

// 3foobar
PHP 8 : promotion des propriétés dans le constructeur (syntaxe plus compacte) class A {
    function __construct(
        public int $a1,
        public int $a2,
        public int $a3,
        public int $a4,

    ) {}

    function somme() {
        return $this->a1
            + $this->a2
            + $this->a3
            + $this->a4;
    }
}

$a = new A(1, 2, 3, 4);
// PHP 8 ne permet plus int + string

echo $a->somme();

// 10
PHP 8 : version plus propre avec visibilité et typage retour class A {
    public function __construct(
        public int $a1,
        public int $a2,
        public int $a3,
        public int $a4,
    ) {}

    public function somme(): int
    {
        return $this->a1
            + $this->a2
            + $this->a3
            + $this->a4;
    }
}

$a = new A(1, 2, 3, 4);

echo $a->somme();

echo $a->a4;
alert(a.a4);
alert(a["a4"]);
chaînage optionnel (optional chaining) // depuis PHP 8.0 il y l'opérateur nullsafe ?->
// qui est utile dans ce genre de cas :

class A {
    public function foo(): string {
        return 'bar';
    }
}

class B {
    /**
     * cette fonction peut retourner null
     * comme l'indique son return type hint
     * avec un point d'interrogation devant A
     */
    public function getA(): ?A {
        if (true) {
            return null; // pas de bol ;)
        }
        return new A(); // on aurait préféré ça !
    }
}

$b = new B();

echo $b->getA()?->foo(); // no problemo

echo $b->getA()->foo();
// Error: Call to a member function foo() on null

// note : ce n'est pas la même chose que
// l'opérateur null coalescing ?? (voir plus bas)
console.log(a.foo?.bar);

// comme a n'a pas de propriété foo,
// affiche undefined au lieu de planter
// comme le ferait console.log(a.foo.bar)
// cf. MDN

echo get_class($a);
alert(a.constructor.name);

if (is_a($a, 'A')) {
    echo '$a est du type A ou a un parent de type A';
}
// ou (synonyme)
if ($a instanceof A) {
    echo 'idem';
}

// pas pareil !
if (get_class($a) == 'A') {
    echo '$a est exactement du type A';
}
if (a instanceof A) {
  alert("a est un A ou...");
  alert("a a un A dans sa chaîne de prototype");
}

print_r(get_object_vars($a));
// ou
$r = new ReflectionObject($a);
print_r($r->getProperties());

accès à une variable privée
// class A { private $a1; ...

$p = $r->getProperties();
$p[0]->setAccessible(true);
print $p[0]->getValue($a);

accès par le nom de l'attribut
if (property_exists($a, "a1")) {
    $attr = "a1";
    $a->$attr = "blah";
    echo $a->$attr;
}
if (a.hasOwnProperty('a1')) {
    a['a1'] = 'blah';
    alert(a['a1']);
}
// normalement supporté par tout navigateur.
// vérifier que le navigateur a cette méthode :
// if (Object.prototype.hasOwnProperty) { ...
surcharge
class A {
    function __construct($a1, $a2, $a3, $a4) {
        $this->a1 = $a1;
        $this->a2 = $a2;
        $this->a3 = $a3;
        $this->a4 = $a4;
    }

    function __toString() {
        return(join(" + ", array($this->a1, $this->a2, $this->a3, $this->a4)));
    }
}

$a = new A(1, 2, "foo", "bar");

echo $a;
function A(a1,a2,a3,a4) {
    this.a1 = a1;
    this.a2 = a2;
    this.a3 = a3;
    this.a4 = a4;
}

A.prototype.toString = function() {
    return [this.a1, this.a2, this.a3, this.a4].join(" + ");
}

a = new A(1,2,"foo","bar");

alert(a);


héritage
class B extends A {
    function __construct($a1, $a2, $a3, $a4) {
        parent::__construct($a1, $a2, $a3, $a4);
    }

    function bonjour() {
        return("bonjour".$this->a1);
    }
}

$b = new B(1,2,3,4);
echo $b->bonjour();
echo $b; // appelle __toString() de A
function B() {
    A.apply(this, arguments);
}
B.prototype = new A();
B.prototype.constructor = B;

B.prototype.bonjour = function() {
    return "bonjour" + this.a1;
}

b = new B(1,2,3,4);
alert(b.bonjour());
alert(b);
classe abstraite // Je veux faire deux objets, Rectangle et Cercle,
// qui partagent des fonctions communes mais qui ont
// aussi des particularités. On peut le faire par simple
// héritage mais la classe abstraite permet de déclarer
// ça dès le départ, dès la classe parente. C'est une
// manière de bien organiser son code.
abstract class Forme
{
    public string $coins;
    // note : un membre abstrait, ça n'existe pas

    // cette méthode sera commune aux descendants
    public function getCoins(): string
    {
        return $this->coins;
    }

    // celle-ci dépendra de chaque descendant
    abstract public function getAire(): float;
}

class Rectangle extends Forme
{
    public string $coins = '4 coins';
    public int $l;
    public int $L;

    public function __construct(int $l, int $L)
    {
        $this->l = $l;
        $this->L = $L;
    }

    public function getAire(): float
    {
        return $this->l * $this->L;
    }
}

class Cercle extends Forme
{
    public string $coins = '0 coin';
    public int $r;

    public function __construct(int $r)
    {
        $this->r = $r;
    }

    public function getAire(): float
    {
        return pi() * pow($this->r, 2);
    }
}

$a = new Rectangle(2, 3);
echo 'Un rectangle de 2 x 3 a '.$a->getCoins()."\n";
echo 'Et une aire de '.$a->getAire()."\n";

$b = new Cercle(4);
echo 'Un cercle de rayon 4 a '.$b->getCoins()."\n";
echo 'Et une aire de '.$b->getAire()."\n";

// et grâce à abstract, on interdit ceci :
$x = new Forme();
// Uncaught Error: Cannot instantiate abstract class
// ce concept n'existe pas en JavaScript
// mais il a été ajouté dans TypeScript
interface // Je veux faire une classe A qui va afficher le nom
// de différents objets (utilisateurs, documents…)
// Au début, je peux bien traiter un seul type d'objet
// par exemple Utilisateur, mais je veux permettre
// d'étendre le principe plus tard à d'autres,
// pas forcément écrits par moi : A va donc être
// typiquement une bibliothèque de fonctions ou
// un "framework"…
class A
{
    public function afficheNom(Bidule $b): void
    {
        printf("Nom : %s\n", $b->getNom());
    }
}

// tout ce que je sais, c'est que j'ai besoin que
// ces objets me donnent leur nom, évidemment…
interface Bidule
{
    public function getNom(): string;
}

// un premier objet
class B implements Bidule
{
    public function getNom(): string
    {
        return 'Bernard';
    }
}

// un deuxième objet
class C implements Bidule
{
    public function getNom(): string
    {
        return 'Cahier des charges';
    }
}

// enfin, je peux utiliser la classe A en lui
// passant les objets divers que j'ai créés
$afficheur = new A();

$personne = new B();
echo "Voici une personne 👉️ ";
$afficheur->afficheNom($personne);

$document = new C();
echo "Voici un document 👉️ ";
$afficheur->afficheNom($document);

// plus tard, je peux créer de nouveaux objets
// (entreprises, sports, gâteaux…) et continuer
// à utiliser ma classe A inchangée pour afficher
// leurs noms ✌️

// dans la réalité, A va être une bibliothèque
// ou un framework, et B et C vont être des
// objets propres à chaque projet utilisant A

// note : dans les interfaces, on peut mettre des
// constantes

// note : on peut implémenter plusieurs interfaces
// (class D implements Bidule, Truc, Chose)
// ce concept n'existe pas en JavaScript
// mais il a été ajouté dans TypeScript
trier une liste d'objets $l = array(
     new A(3,4,'c','d'),
     new A(1,2,'a','b'),
);

// sur le membre a1 numérique :
usort($l, function ($x, $y) {
    return $x->a1 <=> $y->a1;
});

// sur le membre a4 alphanumérique :
usort($l, function ($x, $y) {
    return strcmp($x->a4, $y->a4);
});

// en utilisant une fonction de la classe de cet objet
// par exemple si la fonction s'appelle compare() :
usort($l, array('A', 'compare'));
TODO
idem sur chaîne UTF-8
$l = array(
     new A(3,4,'z','z'),
     new A(1,2,'é','é'),
);

setlocale(LC_COLLATE, 'fr_FR.UTF-8');
usort($l, function($a, $b) {
    return strcoll($a->a3, $b->a3);
});
TODO
annoter une liste d'objets // PHP 8

$a = new WeakMap();
$a[$objet1] = "notes sur l'objet 1";
$a[$objet2] = "l'objet 2 est encore mieux !";
$a[$objet3] = "mais je préfère l'objet 3";

unset($objet2);
echo count($a); // affiche 2

/* il existait déjà splObjectStorage mais les références étaient fortes c'est-à-dire que PHP ne pouvait pas nettoyer $objet2 à cause de la référence dans le splObjectStorage */
// pas dans IE11

const a = new WeakMap();
a.set(objet1, "notes sur l'objet 1");
a.set(objet2, "l'objet 2 est encore mieux !");
a.set(objet3, "mais je préfère l'objet 3");

delete objet2;
// a n'est pas énumérable
accès par le nom de la méthode
if (method_exists($b, "bonjour")) {
    $meth = "bonjour";
    echo $b->$meth();
}
TODO
introspection
$r = new ReflectionClass('B');
foreach ($r->getMethods() as $m) {
    echo $m->getName()."\n";
}
TODO
redéfinition de constante dans une classe fille class A {
    const C = 'chose';
   
    public function f()
    {
        echo '$this::C '.$this::C."\n";
        echo 'self::C '.self::C."\n"; // ⚠️
        echo 'static::C '.static::C."\n";
    }
}

class B extends A {
    const C = 123;
}

$a = new A();
echo "A\n";
$a->f();

$b = new B();
echo "B\n";
$b->f();

// même problème pour les variables "static"
// bon article sur le sujet
TODO
trait

réutilise du code horizontal, ici la méthode coucou

ou contourne l'asbence d'héritage multiple

déconseillé parce que ça contourne le principe de responsabilité unique (ça rend le code difficile à tester et maintenir)
// à partir de PHP 5.4
trait A {
    function coucou() {
        echo "coucou ".$this->z;
    }
}

class B {
    use A;
    private $z = 1;
}

class C {
    use A;
    private $z = 2;
}

$b = new B();
$b->coucou();

$c = new C();
$c->coucou();

idem sans trait, par héritage
// PHP < 5.4
class A {
    protected $a;
    function coucou() {
        echo "coucou ".$this->a;
    }
}

class B extends A {
    function __construct() {
        $this->a = 1;
    }
}

class C extends A {
    function __construct() {
        $this->a = 2;
    }
}

$b = new B();
$b->coucou();

$c = new C();
$c->coucou();

idem sans trait, par une classe utilitaire
class A {
    static function coucou($a) {
        echo "coucou $a";
    }
}

class B {
    function coucou() {
        A::coucou(1);
    }
}

class C {
    function coucou() {
        A::coucou(2);
    }
}

$b = new B();
$b->coucou();

$c = new C();
$c->coucou();

objet anonyme $a = new stdClass;
$a->truc = 'valeur';

// ou par conversion de tableau en objet :
$a = (object) array('truc' => 'valeur');
a = { "truc": "valeur" };
espaces de noms
# à partir de PHP 5.3

# dans ma_lib.php :
namespace ma\librairie\salutations;
function coucou() {
    echo "coucou !\n";
}

# utilisation :
require 'ma_lib.php';
ma\librairie\salutations\coucou();



/* Note : dans l'exemple ci-dessous, la variable $blah n'a rien à voir avec l'espace de nom, c'est une simple globale parce que "seuls les types de code suivants peuvent être affectés par les espaces de noms : les classes (incluant les abstraites et les traits), les interfaces, les fonctions et les constantes." (source) */

namespace foo\bar;
$blah = 123;
namespace autre\espace\de\nom;
echo "blah vaut $blah\n";
// ceci n'existe pas : echo foo\bar\$blah;

alias
require 'ma_lib.php';
use ma\librairie\salutations as a;
a\coucou();

conversion objet en array
$a = new A(1,2,3,4);
$aa = (array) $a;
// ou json_decode(json_encode($a), true);
// inutile : on peut écrire a["a1"] ou a.a1
for (x in a) {
    if (typeof(a[x] != "function") {
        alert(a[x]);
    }
}
et l'inverse
$o = (object) array('foo' => 123, 'bar' => 456);
echo $o->foo;

l'heure courante echo date("H:i:s");
cf format de date/heure en PHP
a = new Date();
alert(a.toLocaleTimeString());
secondes depuis le 01/01/1970
(l'heure Unix)
(Unix time)
echo time(); a = new Date();

alert(parseInt(a / 1000));

alert(Math.round(a / 1000));
date à partir de l'heure Unix echo date('d/m/Y H:i:s', 1677774680); new Date(1677774680 * 1000);
// JS veut des millisecondes

// attention, si heure Unix en chaîne de caractères
let a = '1677774680';
// il faut convertir en nombre :
new Date(Number(a));
// ou
new Date(a * 1);
// sinon 'Invalid Date'
timestamp
horodatage
AAAA-MM-JJ HH:MM:SS
// date locale :
echo date("Y-m-d H:i:s");

// GMT (UTC) sans timezone :
echo gmdate("Y-m-d H:i:s");
a = new Date();

// date locale :
alert(
a.getFullYear() + '-' +
('00' + (a.getMonth()+1)).slice(-2) + '-' +
('00' + a.getDate()).slice(-2) + ' ' +
('00' + a.getHours()).slice(-2) + ':' +
('00' + a.getMinutes()).slice(-2) + ':' +
('00' + a.getSeconds()).slice(-2)
);

// GMT (UTC) sans timezone :
alert(
a.getUTCFullYear() + '-' +
('00' + (a.getUTCMonth()+1)).slice(-2) + '-' +
('00' + a.getUTCDate()).slice(-2) + ' ' +
('00' + a.getUTCHours()).slice(-2) + ':' +
('00' + a.getUTCMinutes()).slice(-2) + ':' +
('00' + a.getUTCSeconds()).slice(-2)
);

// plus simple avec Moment.js
a = moment();
alert(a.format('YYYY-MM-DD HH:mm:ss'));

// ou Day.js (plus léger)
convertir une date dans une autre zone $utc = new DateTimeZone("UTC");
$paris = new DateTimeZone("Europe/Paris");
$a = new DateTime("2013-08-07 12:34:56", $utc);
$a->setTimezone($paris);
echo $a->format('Y-m-d H:i:s');
// utiliser Moment.js
a = moment.tz("2013-08-07 12:34:56", "UTC");
a.tz("Europe/Paris");
alert(a.format('YYYY-MM-DD HH:mm:ss'));

// ou Day.js
date locale "lundi 21 juillet 1969"
setlocale(LC_TIME, "fr_FR.UTF-8");
$a = mktime(0,0,0,7,21,1969);
echo strftime("%A %e %B %Y", $a);

// ⚠️ strftime est obsolète depuis PHP 8.1
// ⇒ à remplacer par IntlDateFormatter::format
// le plus approchant
a = new Date(1969,6,21,0,0,0);
alert(a.toLocaleString());
// ou utiliser une librairie qui émule strftime
retrouver le jour de la semaine echo date('N', $a);
// 1 = lundi (ISO-8601)
alert(a.getDay());
// 1 = lundi
ajouter 14 jours à cette date = lundi 4 août 1969
echo strftime("%A %e %B %Y", strtotime('+14 days', $a));

// notes sur DateTime :
$b = new DateTime(strftime("%F",$a));
$b->add(new DateInterval('P14D'));
echo $b->format('l j F Y'); // KO pas de locale !
// donc obligé de faire :
echo strftime("%A %e %B %Y", $b->getTimestamp());
a.setDate(a.getDate() + 14);
nombre de jours entre deux dates : 24 et 31 octobre 2013 (changement d'heure)
$a = mktime(0,0,0,10,24,2013);
$b = mktime(0,0,0,10,31,2013);
echo round(($a - $b)/86400);
a = new Date(2013,9,24,0,0,0);
b = new Date(2013,9,31,0,0,0);
alert(Math.round((a - b)/86400000));
// attention, milli-secondes !
comparer deux dates
if ($a < $b)
if (a < b)
convertir une chaine de date en timestamp
echo strtotime('2012-12-20'); // ok

echo strtotime('20/12/2012'); // ko, rien !

// avec DateTime :
$a = DateTime::createFromFormat(
    'd/m/Y H:i:s',
    '20/12/2012 00:00:00'
);
echo $a->format('U');

// avec le fuseau horaire de l'Alaska :
$a = DateTime::createFromFormat(
    'd/m/Y H:i:s',
    '20/12/2012 00:00:00',
    new DateTimeZone('America/Anchorage')
);
echo $a->format('U');
alert(Date.parse("Dec 20, 2012"));
// attention, milli-secondes !

// avec le fuseau horaire de l'Alaska :
alert(Date.parse("2012-12-20T00:00:00.000-09:00"));

// avec le datepicker de jQuery :
alert($.datepicker
    .parseDate("dd/mm/yy", "20/12/2012")
    .getTime());

// ou avec Moment.js ou Day.js :
a = moment("20/12/2012", "DD/MM/YYYY");
alert(a.format('X'));
// millisecondes :
alert(a.format('x'));
"découper" une date
print_r(strptime("31/10/2013", "%d/%m/%Y"));

cloner un objet Date

b = new Date(a);
lancer un sous-processus
print shell_exec("whois linux.com");
const { spawnSync } = require('child_process');
const res = spawnSync('whois', ['linux.com'], {
  encoding: 'utf8' // indispensable sinon illisible
});
console.log(res.stdout);
limiter le temps d'excécution d'une opération, par exemple la connexion à un port caché par un firewall (22 = SSH) ou la lecture d'un gros fichier ou l'accès à une URL
// ne fonctionne qu'en ligne de commande
declare(ticks=1);

function signal_handler($signal) {
    print "timeout atteint\n";
    pcntl_alarm(1);
}

pcntl_signal(SIGALRM, "signal_handler", true);
pcntl_alarm(1);

$f = fsockopen("herverenault.fr", 22, $errno, $errstr, 30);
if ($f) {
    echo "réponse du serveur : "; echo fgets($f, 4096);
} else {
    echo "connexion impossible en moins de 3 s\n";
}
const fs = require('fs/promises');
const ac = new AbortController();
const id = setTimeout(() => ac.abort(), 100); // ms

// avec ça, n'importe quel timeout marchera :
const fichier = '/dev/random';

(async function() {
  try {
    const c = await fs.readFile(fichier, {
      signal: ac.signal,
      encoding: 'utf8'
    });

    console.log('contenu', c);
  } catch(e) {
    console.log(e);
  }

  clearTimeout(id);
})();

résolution DNS (DNS lookup)
echo gethostbyname("herverenault.fr");

résolution DNS inverse (DNS reverse lookup)
echo gethostbyaddr("92.243.27.61");

récupérer un document par http(s)
$html = file_get_contents('https://herverenault.fr/');
// ne supporte pas les IDN

// ou avec cURL
// dans Node :
const axios = require('axios');
axios.get('https://hervérenault.fr/') // IDN 👍️
    .then(function (response) {
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    })
    .finally(function () {
      console.log('toujours exécuté');
    });

// ou avec l'API Fetch intégrée dans Node 18
// et dans les navigateurs depuis environ 2015
fetch('https://hervérenault.fr/')
    .then((response) => {
      if (!response.ok) {
        throw new Error(response.status +
          ' ' + response.statusText);
      }
      console.log('La réponse :', response);
      return response.text(); // ou .json ou .blob
    })
    .then(data => {
      console.log('Le contenu :', data);
    })
    .catch(err => {
      console.log(err);
    });

// ou avec la bonne vieille XMLHttpRequest

print urlencode("hé"); alert(encodeURIComponent("hé"));
// pas escape() obsolète !
// encodeURI() pour une URL complète
encoder du texte en entités html
echo htmlentities("hé -> &'", null, "UTF-8");
// encode aussi les accents

// ou
echo mb_convert_encoding("hé -> &'", "HTML-ENTITIES", "UTF-8");
// mais ça n'encode pas &
// n'existe pas en JS pur
// mais avec jQuery :
alert($('<p/>').text("hé -> &'").html());
vérifier qu'une URL est valide filter_var($a, FILTER_VALIDATE_URL);

// attention, il y a plein de subtilités
try {
  b = new URL(a);
} catch (e) {
  alert('URL invalide');
}

// pour lire le protocole :
alert('protocole ' + b.protocol);
vérifier qu'une adresse e-mail est valide if (filter_var('toto@exemple.fr',FILTER_VALIDATE_EMAIL)) {
    echo "valide";
}
// utiliser une regex
envoyer un mail "old school"
mail('you@you.com', 'test', 'ok...', 'From: me@me.com');

# ou carrément :

$h = popen("/usr/lib/sendmail -t", 'w');
fwrite($h, "From: me@me.com
To: you@you.com
Subject: test
     
ok...");
pclose($h);

envoyer un mail "moderne" avec

une partie HTML avec un charset UTF-8, encodée en Quoted-Printable

et une partie image encodée en Base64
# avec PEAR, sur Ubuntu :
# sudo apt-get install php-mail php-mail-mime

include "Mail.php";
include "Mail/mime.php";

$mime = new Mail_mime();
$mime->setHTMLBody("<b>hé !</b>");
$mime->addAttachment("image.jpg", "image/jpeg");
$body = $mime->get(array("html_charset" => "UTF-8"));
$body = $mime->get();

$headers = $mime->headers(array(
"From" => "Moi-même <me@me.com>",
"To" => "you@you.com",
"Subject" => "Hé, test php !"));

$mail = Mail::factory("smtp", array("host" => "localhost"));
$mail->send("you@you.com", $headers, $body);


# ou mieux, avec SwiftMailer :
# ============================
# composer require swiftmailer/swiftmailer

require_once 'lib/swift_required.php';

$t = Swift_SmtpTransport::newInstance('localhost', 25);

$mailer = Swift_Mailer::newInstance($t);

$message = Swift_Message::newInstance('Hé, test php !')
  ->setFrom(array('me@me.com' => 'Moi-même'))
  ->setTo('you@you.com')
  ->setBody('<b>hé !</b> en HTML', 'text/html')
  ->addPart('hé ! en texte', 'text/plain')
  ->attach(Swift_Attachment::fromPath('image.jpg'));

$result = $mailer->send($message);

attention à l'accent ! echo base64_encode("Hé ho !");
echo base64_decode("SMOpIGhvICE=");
alert(btoa("Hé ho !"));
alert(atob("SOkgaG8gIQ=="));
dernière erreur print_r(error_get_last()); // utiliser window.onerror
gestion des erreurs (principe général) try {
    foobar(3);
    foobar(0);
    foobar(4);
} catch (Exception $e) {
    echo 'Erreur ',  $e->getMessage(), "\n";
}

// si foobar n'existe pas :
// PHP Fatal error:  Call to undefined function foobar()
// en PHP, une erreur fatale n'est pas une exception

// exemple de foobar :
function foobar($a) {
    if ($a == 0) {
        throw new Exception('division par zéro');
    }
    echo (12 / $a)."\n";
}
// affiche :
4
Erreur division par zéro
try {
    foobar(3);
    foobar(0);
    foobar(4);
} catch (e) {
    alert('Erreur ' + e.message);
}

// si foobar n'existe pas :
// Erreur foobar is not defined

// exemple de foobar :
function foobar(a) {
    if (a == 0) {
        throw Error('division par zéro');
    }
    alert(12 / a);
}
// affiche :
4
Erreur division par zéro
loguer
error_log("ceci, celà");

# ou pour loguer dans un fichier particulier
ini_set('log_errors', 1);
ini_set('error_log', '/error.log');

# ou avec log4php
# http://logging.apache.org/log4php/

include('log4php/Logger.php');

Logger::configure('log4php.xml');
$log = Logger::getLogger("main");
$log->info("ceci, celà");

# + fichier log4php.xml
<configuration xmlns="http://logging.apache.org/log4php/">
    <appender name="fichier" class="LoggerAppenderFile">
        <param name="file" value="test.log" />
        <layout class="LoggerLayoutPattern">
            <param name="conversionPattern" value="%d{Y-m-d H:i:s.u} %-5p [%c] %F:%L - %m%n" />
        </layout>
    </appender>
    <root>
        <level value="INFO" />
        <appender_ref ref="fichier" />
    </root>
</configuration>

particularité PHP # afficher les erreurs d'un script
# directement dans le navigateur
# (en environnement de développement)
ini_set('display_errors', 'on');
# ou
ini_set('display_errors', 1);
# ou modifier la valeur display_errors dans php.ini

// appeler une fonction sur chaque message d'erreur
$old_error_handler = set_error_handler(
    function ($errno, $errstr, $errfile, $errline) {
        echo "Erreur $errno ($errstr) dans…";
        error_log("Erreur $errno ($errstr) dans…");
        // on peut aussi envoyer un mail(); etc

        return true; // c'est traité
        // si false, PHP continue à traiter comme d'hab
    }
);

// au besoin, on peut ensuite remettre l'ancien handler
// qu'on a conservé dans la variable $old_error_handler

// ⚠️ sujet complexe, lire la doc

// ⚠️ pour intercepter les erreurs fatales,
// il faut utiliser ce contournement :
register_shutdown_function(function () {
    if ($e = error_get_last()) {
        error_log('DEBUG erreur '.print_r($e, true));
    }
});

// voir aussi error_reporting()
// qui peut masquer ou ajouter des niveaux d'erreurs

naviguer dans le DOM pour remonter au parent $doc = new DOMDocument();
// ⚠️ indispensable pour du HTML5 :
$gestionnaire = libxml_use_internal_errors(true); // ⚠️
$doc->load('fichier.html');
libxml_clear_errors(); // ⚠️
libxml_use_internal_errors($gestionnaire); // ⚠️
$a = $doc->getElementsByTagName("div")[0]; // par exemple
$b = $a->parentNode;

// Il existe loadHTML et loadHTMLFile mais ça ne gère
// que le HTML4 et pas les nouveautés de HTML5
// dans le navigateur web :
a = document.getElementsByTagName('div')[0];
b = a.parentNode;
// plus pratique si on peut ignorer IE11 :
b = a.closest('header'); // remonte autant que nécessaire

// dans Node.js :
const { DOMParser } = require('xmldom');
doc = new DOMParser().parseFromString(truc, 'text/html');
a = doc.getElementsByTagName('div')[0];
b = a.parentNode;
manipulation de XML avec le DOM
$doc = new DOMDocument();
$doc->load('fichier.xhtml');
foreach ($doc->getElementsByTagName("a") as $e) {
    echo $e->textContent."\n";
    // voir aussi $e->nodeValue
    foreach ($e->attributes as $a) {
        echo $a->name." = ".$a->value."\n";
    }
    if ($e->hasAttribute('onclick')) {
        echo "avant ".$e->C14N()."\n";
        $e->removeAttribute('onclick');
        echo "apres ".$e->C14N()."\n";
    }
}
echo $doc->saveXML();
var anchors = document.getElementsByTagName('a');
for (i = 0; i < anchors.length ; i++) {
    console.log(anchors[i].textContent);
    var attr = anchors[i].attributes;
    for (j = 0; j < attr.length; j++) {
        console.log(attr[j].nodeName + ' = '
          + attr[j].nodeValue);
    }
    if (anchors[i].hasAttribute('onclick')) {
        console.log('avant ' + anchors[i].outerHTML);
        anchors[i].removeAttribute('onclick');
        console.log('après ' + anchors[i].outerHTML);
    }
}

console.log(document.documentElement.outerHTML);
avec XPath
$doc = new DOMDocument();
$doc->load('fichier.xhtml');
$xp = new DOMXPath($doc);
$xp->registerNamespace('x', 'http://www.w3.org/1999/xhtml');
echo $xp->query('//x:title')->item(0)->nodeValue;
foreach ($xp->query('//x:td[@style]') as $e) {
    echo $e->nodeName." ".$e->nodeValue."\n";
}

var iterateur = document.evaluate('//span[@style]',
  document, null,
  XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
try {
  var noeud = iterateur.iterateNext();
  while (noeud) {
    console.log(noeud.nodeName + ' '
      + noeud.textContent);
    noeud = iterateur.iterateNext();
  }
}
catch (e) {
  alert("Document modifié pendant l'itération" + e);
}

// exemple adapté compacté de MDN Introduction to using XPath

// pendant que j'y suis :

var octobre = document.evaluate(
  "count(//a[starts-with(., '2021-10')])",
  document, null, XPathResult.ANY_TYPE, null);
console.log(octobre.numberValue);

// voir MDN pour plein d'autres détails

// dans Node.js, uniquement du XML :
const xpath = require('xpath');
const dom = require('xmldom').DOMParser;
const doc = new dom().parseFromString(bidule);
const nodes = xpath.select("//truc", doc)
console.log(nodes[0].localName, nodes[0].firstChild.data)
autres versions
$xml = simplexml_load_file('fichier.xhtml');
foreach ($xml->body->table->tbody->tr as $tr) {
    echo "1ere cellule : ".$tr->td[0]."\n";
    foreach ($tr->children() as $e) {
        foreach ($e->attributes() as $a => $b) {
            echo "$a => $b\n";
        }
        if (isset($e['style'])) {
            echo $e->getName()." ".$e['style']."\n";
            unset($e['style']);
        }
    }
}
echo $xml->asXML();
// querySelector prend un sélecteur CSS en paramètre

var titre = document.querySelector('title');
console.log(titre.textContent);

var nœuds = document.querySelectorAll('span[style]');
for (i = 0; i < nœuds.length; i++) {
  console.log(nœuds[i].textContent);
  nœuds[i].removeAttribute('style');
}
// ou
nœuds.forEach((elt, index) => {
  console.log(elt.textContent);
  elt.removeAttribute('style');
});
// (ne pas confondre avec .each de jQuery)

// pas possible de chercher dans le contenu comme XPath
// mais querySelectorAll retourne une NodeList donc, en ES6,
// c'est possible de convertir en n2 = Array.from(nœuds)
// pour faire n2.filter(el => el.textContent.substr(0,3) == 'esc')
interfaces DOM et SimpleXML sur le même XML
$xml = simplexml_load_file('fichier.xhtml');
echo $xml->head->title; // affiche le titre
$doc = dom_import_simplexml($xml)->ownerDocument;
$doc->getElementsByTagName('title')->item(0)
         ->nodeValue = 'foo';
echo $xml->head->title; // affiche foo

idem en mode "stream" (gros fichier)


"couper-coller" un nœud, par exemple mettre #id2 avant #id1 $nœud1 = $doc->getElementById('id1');
$nœud1->parentNode->insertBefore(
    $doc->getElementById('id2'),
    $nœud1
);
// méthode ancienne (depuis Firefox 1)
document.getElementById('id1').parentNode
  .insertBefore(
    document.getElementById('id2'),
    document.getElementById('id1')
  );

// méthode nouvelle (depuis Firefox 49 en 2016)
document.getElementById('id1')
  .before(document.getElementById('id2'));
"copier-coller" (cloner, dupliquer) le nœud #id2 avant un éventuel #id3 $nœud = $doc->getElementById('id2')->cloneNode(true);
// true pour cloner le nœud et ses enfants
$nœud->setAttribute('id', 'id2bis');

// Il n'y a pas de "after" comme en JS !
// donc il faut utiliser insertBefore avec le next sibling
// S'il n'existe pas, il sera null mais ce n'est pas grave
$doc->getElementById('id1')->parentNode->insertBefore(
    $nœud,
    $doc->getElementById('id2')->nextSibling
);
let nœud = document.getElementById('id2').cloneNode(true);
// true pour cloner le nœud et ses enfants
nœud.id = 'id2bis';

document.getElementById('id1').after(nœud);
manipulation XSLT
$xml = new DOMDocument;
$xml->load('donnees.xml');
$xslt = new DOMDocument;
$xslt->load('transfo.xsl');
$proc = new XSLTProcessor;
$proc->importStyleSheet($xslt);
echo $proc->transformToXML($xml);
Dans le navigateur, voir MDN
Pour Node.js, voir saxon-js
manipulation de YAML
$a = yaml_emit(array(
"nom"      => "Renault",
"prénom"   => "Hervé",
"langages" => array(
"PHP", "Perl", "Python", "Java"
)));

$b = yaml_parse($a);

print_r($b);
print $b["nom"];

manipulation de JSON
$a = json_encode(array(
"nom"      => "Renault",
"prénom"   => "Hervé",
"langages" => array(
"PHP", "Perl", "Python", "JavaScript"
)));

$b = (array) json_decode($a);

print_r($b);
print $b["nom"];

manipulation de fichier
$f = fopen('fichier.html', 'w');
fwrite($f, $contenu);
fclose($f);

// raccourci :
file_put_contents('fichier.html', $contenu);

// plus moderne :
try {
    $f = new \SplFileObject('fichier.html', 'w');
    $f->fwrite($f, $contenu);
    unset($f);
} catch (\Exception $e) {
    echo $e->getMessage();
}
// dans Node.js

// synchrone
const fs = require('fs');
try {
  fs.writeFileSync('fichier.html', contenu);
} catch(e) {
  console.log(e);
}

// asynchrone
const fs = require('fs');
fs.writeFile('fichier.html', contenu, erreur => {
  if (erreur) {
    console.error(erreur);
    return;
  }
  console.log('réussi');
});
en ajout (append)
$f = fopen('fichier.html', 'a');
fichier temporaire
tempnam('/chemin', 'préfixe');
// renvoie le nom du fichier créé

lire tout le contenu d'un fichier dans une variable
$txt = file_get_contents("fichier.txt");
renommer un fichier
rename('fichier.html', 'fichier.txt');

$a = 'fichier.txt';
if (file_exists($a)) {
    unlink($a);
}


$f = fopen($a, 'r') or die("impossible de lire le fichier '$a'\n");
# et le motif de l'erreur est dans /var/log/... ou sur stderr

lire le contenu d'un fichier ligne par ligne
$f = fopen($a, 'r');
while (($ligne = fgets($f)) !== false) {
    echo "j'ai lu $ligne";
}
fclose($f);
TODO pour Node.js
idem mais fichier gzipé $f = gzopen("fichier.gz", "r");
while (($ligne = fgets($f)) !== false) {
    echo "j'ai lu $ligne";
}
gzclose($f);
TODO pour Node.js
lire l'entrée standard while (($ligne = fgets(STDIN)) !== false) {
    echo "j'ai lu $ligne";
}
TODO pour Node.js
lire ou écrire un fichier CSV fgetcsv et fputcsv TODO pour Node.js
extraire les fichiers d'un zip (unzip) $zip = new ZipArchive;
if ($zip->open('fichier.zip') === true) {
    $zip->extractTo('/un/répertoire/cible/');
    $zip->close();
} else {
    echo 'Erreur';
}
TODO

$a = stat("fichier.txt");
echo "Fichier modifié le : ".
    date("r", $a["mtime"]);

// ou filemtime() dans ce cas
import fs from 'fs';

const dateMod = fs.statSync('fichier.txt').mtime;
console.log('Fichier modifié le', dateMod);

chmod("fichier.txt", 0755);
// renvoie un booléen

on est dans quel répertoire ? echo getcwd(); // dans Node.js
console.log(process.cwd());
lecture de répertoire
foreach (scandir('repertoire') as $a) {
    if (! in_array($a, array('.','..'))) {
        echo "$a\n";
    }
}

// avant PHP 5 :
if ($dh = opendir('repertoire')) {
    while (($a = readdir($dh)) !== false) {
        if (! in_array($a, array('.','..'))) {
            echo "$a\n";
        }
    }
}
restreindre à certains fichiers
foreach (glob("*.txt") as $a) {
    echo "$a\n";
}


echo basename('/foo/bar/fichier.txt');
1er paramètre d'éxecution en ligne de commande echo $_SERVER["argv"][1];
// ou
echo $argv[1];
// dans Node.js
console.log(process.argv[1]);
nombre de paramètres echo $argc; // dans Node.js
console.log(process.argv.length);
environnement système
echo getenv("TERM");
variables pour http://... /test?foo=
bar%20baz
echo $_GET["foo"]; // bar baz
echo $_POST["foo"];
echo $_REQUEST["foo"];
echo $_COOKIE["foo"];
echo $_SERVER['QUERY_STRING']; // foo=bar%20baz
echo $_SERVER['REQUEST_URI']; // /test
echo $_SERVER["HTTP_REFERER"];
// etc...
- (utiliser un plugin jQuery)
-
-
alert(document.cookie); // tous les cookies
alert(document.location.search); // ?foo=bar%20baz
alert(document.URL); // http...baz
alert(document.referrer);
// etc...

// note : document.location ou window.location ? cf MDN
récupérer l'ancre de http…foo.php#bidule // Impossible par défaut
// L'ancre n'existe que dans le navigateur (DOM et JS)
// Mais en admettant qu'on le transmette en JS,
// PHP peut analyser l'URL :
$url = parse_url('http…foo.php#bidule');
echo $url["fragment"];
window.location.hash;
"foo bar"
echo urldecode("foo%20bar");
decodeURIComponent("foo%20bar");
opérateur ternaire
echo ($a == "A" ? "oui" : "non");
alert(a == "A" ? "oui" : "non");
// mais attention, priorité de +
alert("donc " + (a == "A" ? "oui" : "non"));
variante PHP : si a est non-vide, affiche-la, sinon affiche "non" echo $a ?: "non";

// attention, piège : si $a = 0
// ça affiche 'non' car 0 est considéré comme "vide"

// piège 2 : si $a n'est pas définie
// ça provoque un Warning: Undefined variable
// (voir la variante ?? ci-dessous)

// autre gaffe à ne pas faire :
// empty($a) ?: "non"; ← affiche 1
// car si $a est vide ou vaut 0
// PHP convertit empty($a) qui est vrai en 1
// (comme d'habitude)

variante "null coalescing operator" (opérateur de coalescence nulle) echo $a ?? "non";

// variante PHP 7 pour isset($a) ? $a : "non"

// également, le Null Coalescing Assignment Operator :

$a ??= 'non';

// si $a est null, alors affecte-lui la valeur 'non'
// raccourci pour $a = $a ?? 'non';
console.log(a ?? 'non');

// variante de
// if (typeof a === 'undefined' || a === null) { … }

// également, le Nullish coalescing assignment
// légèrement différent de PHP :

var a; // sinon "ReferenceError: a is not defined"
// ou let a;
a ??= 'non';
temporisation en secondes
sleep(3);
// pas exactement la même chose
t = setTimeout('alert("fini !")',3000);

// au passage, les paramètres se mettent après le temps
function foo(a1, a2, a3) {
  alert(a1 + a2 + a3);
}
setTimeout(foo, 3000, 'bar', 'ba', 'truc');

// pour que la tempo se repète
i = setInterval(foo, 3600000);

// pour l'annuler
clearInterval(i);
temporisation inférieure à 1 seconde
usleep(500000);
setTimeout('alert("fini !")',500);
boucle avec temporisation for ($i = 0; $i < 10; $i++) {
  echo "coucou $i\n";
  sleep(1);
}
// en JS classique
// (problème de pile si beaucoup d'appels récursifs)
function coucou(i) {
  if (i > 9) {
    return;
  }
  console.log('coucou', i);
  setTimeout(coucou, 1000, i + 1);
}
coucou(0);

// en JS moderne, simplifié à l'extrême
(async function () {
  for (let i = 0; i < 10; i++) {
    await new Promise(rslv => setTimeout(rslv, 1000))
      .then(console.log('coucou', i));
  }
})();

// ou encore
(async () => {
  for (let i = 0; i < 10; i++) {
    await new Promise(r => setTimeout(() => r(i), 1000))
      .then((valeur) => console.log('coucou', valeur));
  }
})();
hachage MD5
echo md5("ceci est un test");
http://pajhome.org.uk/crypt/md5/
106 (un million)
echo pow(10, 6);

random (0 à 1)
// Fonctions pas sûres pour le chiffrement :
echo rand(0, 1000) / 1000;
// mt_rand() est plus performante :
echo mt_rand(0, 1000) / 1000;

// Fonction sûre pour le chiffrement :
echo random_int(0, 1000) / 1000;
// (depuis PHP 7)
alert(Math.random());
remplir de zéros à gauche str_pad($a, 8, "0", STR_PAD_LEFT); TODO
arrondir
2.5 à 3
echo round(2.5);
alert(Math.round(2.5));
arrondir
2.55 à 2.6
echo round(2.55, 1);
alert(Math.floor((2.55*10)+0.5)/10);
// (retourne un nombre)

ou

const a = 2.55;
alert(a.toFixed(1));
// (retourne une chaine de caractères)

// (voir aussi .toPrecision pour le chiffre complet)
conversion chaîne vers nombre (avec ou sans décimale)
$a = "123.45";

echo $a + 1;
echo floatval($a) + 1; // plus sûr

echo intval($a) + 1;
echo (int)$a + 1; // idem
a = "23.45678";
alert(parseFloat(a) +1 );
alert(parseInt(a) +1 );
séparateur de décimales , au lieu de .
echo floatval(str_replace(",", ".", "123,45")) + 1;
TODO
conversion héxa <-> décimal
echo hexdec('cc');
echo intval('cc', 16);
echo 0xcc;

echo dechex('204');
alert(parseInt('cc', 16));

a = 204;
alert(a.toString(16));

cosinus de 60°
echo cos(60 / 180 * pi());
echo cos(pi() / 3);

désaccentuer
$a = "Éluard";
setlocale(LC_ALL, 'fr_FR.UTF-8');
echo iconv('UTF-8', 'ASCII//TRANSLIT', $a);