[{"data":1,"prerenderedAt":1009},["ShallowReactive",2],{"page-\u002Fphp\u002Fpdo-et-composer":3,"pieces-php":967,"autour-\u002Fphp\u002Fpdo-et-composer":992,"prerequis-\u002Fphp\u002Fpdo-et-composer":996,"plan-php":998},{"id":4,"title":5,"body":6,"description":936,"extension":937,"ligne":60,"meta":938,"motsCles":939,"navigation":105,"niveau":943,"path":944,"prerequis":945,"ressources":947,"seo":958,"sources":959,"stem":965,"__hash__":966},"docs\u002Fphp\u002F06.pdo-et-composer.md","Base de données avec PDO et Composer",{"type":7,"value":8,"toc":923},"minimark",[9,14,32,36,47,50,54,192,202,205,209,270,275,279,282,353,364,370,374,381,477,493,497,500,592,604,610,614,620,646,657,661,668,724,735,747,750,818,841,845,848,867,871,919],[10,11,13],"h2",{"id":12},"ce-que-tu-vas-apprendre","Ce que tu vas apprendre",[15,16,17,26,29],"ul",{},[18,19,20,21,25],"li",{},"Te connecter à une base de données avec ",[22,23,24],"strong",{},"PDO"," et lire des lignes.",[18,27,28],{},"Écrire des requêtes préparées pour empêcher l'injection SQL.",[18,30,31],{},"Installer des bibliothèques et charger tes classes avec Composer.",[10,33,35],{"id":34},"pdo-un-accès-unique-aux-bases","PDO : un accès unique aux bases",[37,38,39,41,42,46],"p",{},[22,40,24],{}," (",[43,44,45],"em",{},"PHP Data Objects",") est l'interface standard de PHP pour parler à une base de données. Le même code fonctionne avec plusieurs systèmes ; seule l'adresse de connexion change.",[37,48,49],{},"Pour rester simple, ce chapitre utilise SQLite, une base stockée dans un seul fichier, sans serveur à installer. L'extension SQLite de PDO doit être activée dans ton installation de PHP.",[10,51,53],{"id":52},"se-connecter","Se connecter",[55,56,62],"pre",{"className":57,"code":58,"filename":59,"language":60,"meta":61,"style":61},"language-php shiki shiki-themes github-light-default github-dark-default github-dark-default","\u003C?php\ndeclare(strict_types=1);\n\n$pdo = new PDO(\"sqlite:recettes.db\", null, null, [\n    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,\n    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,\n]);\n","db.php","php","",[63,64,65,78,100,107,140,165,186],"code",{"__ignoreMap":61},[66,67,70,74],"span",{"class":68,"line":69},"line",1,[66,71,73],{"class":72},"sOnC1","\u003C?",[66,75,77],{"class":76},"s5iOY","php\n",[66,79,81,84,88,91,94,97],{"class":68,"line":80},2,[66,82,83],{"class":72},"declare",[66,85,87],{"class":86},"scfrF","(",[66,89,90],{"class":76},"strict_types",[66,92,93],{"class":72},"=",[66,95,96],{"class":76},"1",[66,98,99],{"class":86},");\n",[66,101,103],{"class":68,"line":102},3,[66,104,106],{"emptyLinePlaceholder":105},true,"\n",[66,108,110,113,115,118,121,123,127,130,133,135,137],{"class":68,"line":109},4,[66,111,112],{"class":86},"$pdo ",[66,114,93],{"class":72},[66,116,117],{"class":72}," new",[66,119,120],{"class":76}," PDO",[66,122,87],{"class":86},[66,124,126],{"class":125},"shWOn","\"sqlite:recettes.db\"",[66,128,129],{"class":86},", ",[66,131,132],{"class":76},"null",[66,134,129],{"class":86},[66,136,132],{"class":76},[66,138,139],{"class":86},", [\n",[66,141,143,146,149,152,155,157,159,162],{"class":68,"line":142},5,[66,144,145],{"class":76},"    PDO",[66,147,148],{"class":72},"::",[66,150,151],{"class":76},"ATTR_ERRMODE",[66,153,154],{"class":72}," =>",[66,156,120],{"class":76},[66,158,148],{"class":72},[66,160,161],{"class":76},"ERRMODE_EXCEPTION",[66,163,164],{"class":86},",\n",[66,166,168,170,172,175,177,179,181,184],{"class":68,"line":167},6,[66,169,145],{"class":76},[66,171,148],{"class":72},[66,173,174],{"class":76},"ATTR_DEFAULT_FETCH_MODE",[66,176,154],{"class":72},[66,178,120],{"class":76},[66,180,148],{"class":72},[66,182,183],{"class":76},"FETCH_ASSOC",[66,185,164],{"class":86},[66,187,189],{"class":68,"line":188},7,[66,190,191],{"class":86},"]);\n",[37,193,194,195,198,199,201],{},"Le premier argument est le ",[22,196,197],{},"DSN",", qui indique le type de base et sa position. Les deux ",[63,200,132],{}," sont l'utilisateur et le mot de passe, inutiles avec SQLite.",[37,203,204],{},"Les deux options règlent le comportement : en cas d'erreur SQL, PHP lève une exception visible au lieu de se taire, et chaque ligne est renvoyée sous forme de tableau associatif.",[10,206,208],{"id":207},"créer-une-table","Créer une table",[55,210,212],{"className":57,"code":211,"language":60,"meta":61,"style":61},"\u003C?php\n$pdo->exec(\"\n    CREATE TABLE IF NOT EXISTS recettes (\n        id INTEGER PRIMARY KEY AUTOINCREMENT,\n        titre TEXT NOT NULL,\n        duree INTEGER NOT NULL\n    )\n\");\n",[63,213,214,220,237,242,247,252,257,262],{"__ignoreMap":61},[66,215,216,218],{"class":68,"line":69},[66,217,73],{"class":72},[66,219,77],{"class":76},[66,221,222,225,228,232,234],{"class":68,"line":80},[66,223,224],{"class":86},"$pdo",[66,226,227],{"class":72},"->",[66,229,231],{"class":230},"syCW7","exec",[66,233,87],{"class":86},[66,235,236],{"class":125},"\"\n",[66,238,239],{"class":68,"line":102},[66,240,241],{"class":125},"    CREATE TABLE IF NOT EXISTS recettes (\n",[66,243,244],{"class":68,"line":109},[66,245,246],{"class":125},"        id INTEGER PRIMARY KEY AUTOINCREMENT,\n",[66,248,249],{"class":68,"line":142},[66,250,251],{"class":125},"        titre TEXT NOT NULL,\n",[66,253,254],{"class":68,"line":167},[66,255,256],{"class":125},"        duree INTEGER NOT NULL\n",[66,258,259],{"class":68,"line":188},[66,260,261],{"class":125},"    )\n",[66,263,265,268],{"class":68,"line":264},8,[66,266,267],{"class":125},"\"",[66,269,99],{"class":86},[37,271,272,274],{},[63,273,231],{}," exécute une requête fixe, sans donnée venue de l'extérieur.",[10,276,278],{"id":277},"linjection-sql","L'injection SQL",[37,280,281],{},"Voici ce qu'il ne faut jamais écrire :",[55,283,285],{"className":57,"code":284,"language":60,"meta":61,"style":61},"\u003C?php\n\u002F\u002F DANGEREUX\n$sql = \"SELECT * FROM recettes WHERE titre = '\" . $_GET[\"titre\"] . \"'\";\n",[63,286,287,293,299],{"__ignoreMap":61},[66,288,289,291],{"class":68,"line":69},[66,290,73],{"class":72},[66,292,77],{"class":76},[66,294,295],{"class":68,"line":80},[66,296,298],{"class":297},"sd6ml","\u002F\u002F DANGEREUX\n",[66,300,301,304,306,309,312,315,318,321,324,327,329,332,335,338,341,344,347,350],{"class":68,"line":102},[66,302,303],{"class":86},"$sql ",[66,305,93],{"class":72},[66,307,308],{"class":125}," \"",[66,310,311],{"class":72},"SELECT",[66,313,314],{"class":72}," *",[66,316,317],{"class":72}," FROM",[66,319,320],{"class":125}," recettes ",[66,322,323],{"class":72},"WHERE",[66,325,326],{"class":125}," titre ",[66,328,93],{"class":72},[66,330,331],{"class":125}," '\"",[66,333,334],{"class":72}," .",[66,336,337],{"class":86}," $_GET[",[66,339,340],{"class":125},"\"titre\"",[66,342,343],{"class":86},"] ",[66,345,346],{"class":72},".",[66,348,349],{"class":125}," \"'\"",[66,351,352],{"class":86},";\n",[37,354,355,356,359,360,363],{},"Si le visiteur envoie ",[63,357,358],{},"' OR '1'='1",", la requête devient vraie pour toutes les lignes. Avec d'autres textes, il peut lire d'autres tables ou tout effacer. C'est une ",[22,361,362],{},"injection SQL"," : la donnée saisie est interprétée comme du code SQL.",[365,366,367],"caution",{},[37,368,369],{},"Ne colle jamais une valeur venue de l'utilisateur dans une chaîne SQL. Aucun nettoyage manuel n'est aussi fiable qu'une requête préparée.",[10,371,373],{"id":372},"la-requête-préparée","La requête préparée",[37,375,376,377,380],{},"Une ",[22,378,379],{},"requête préparée"," sépare la requête, envoyée d'abord avec des emplacements vides, des valeurs, transmises ensuite. La base ne confond plus jamais une valeur avec du SQL.",[55,382,384],{"className":57,"code":383,"language":60,"meta":61,"style":61},"\u003C?php\n$stmt = $pdo->prepare(\"SELECT * FROM recettes WHERE duree \u003C= :max\");\n$stmt->execute([\"max\" => 45]);\n\n$rapides = $stmt->fetchAll();\n",[63,385,386,392,432,455,459],{"__ignoreMap":61},[66,387,388,390],{"class":68,"line":69},[66,389,73],{"class":72},[66,391,77],{"class":76},[66,393,394,397,399,402,404,407,409,411,413,415,417,419,421,424,427,430],{"class":68,"line":80},[66,395,396],{"class":86},"$stmt ",[66,398,93],{"class":72},[66,400,401],{"class":86}," $pdo",[66,403,227],{"class":72},[66,405,406],{"class":230},"prepare",[66,408,87],{"class":86},[66,410,267],{"class":125},[66,412,311],{"class":72},[66,414,314],{"class":72},[66,416,317],{"class":72},[66,418,320],{"class":125},[66,420,323],{"class":72},[66,422,423],{"class":125}," duree ",[66,425,426],{"class":72},"\u003C=",[66,428,429],{"class":125}," :max\"",[66,431,99],{"class":86},[66,433,434,437,439,442,445,448,450,453],{"class":68,"line":102},[66,435,436],{"class":86},"$stmt",[66,438,227],{"class":72},[66,440,441],{"class":230},"execute",[66,443,444],{"class":86},"([",[66,446,447],{"class":125},"\"max\"",[66,449,154],{"class":72},[66,451,452],{"class":76}," 45",[66,454,191],{"class":86},[66,456,457],{"class":68,"line":109},[66,458,106],{"emptyLinePlaceholder":105},[66,460,461,464,466,469,471,474],{"class":68,"line":142},[66,462,463],{"class":86},"$rapides ",[66,465,93],{"class":72},[66,467,468],{"class":86}," $stmt",[66,470,227],{"class":72},[66,472,473],{"class":230},"fetchAll",[66,475,476],{"class":86},"();\n",[37,478,479,482,483,485,486,488,489,492],{},[63,480,481],{},":max"," est un emplacement nommé. ",[63,484,441],{}," le remplit avec la valeur du tableau. ",[63,487,473],{}," renvoie toutes les lignes ; ",[63,490,491],{},"fetch"," n'en renvoie qu'une.",[10,494,496],{"id":495},"ajouter-une-ligne","Ajouter une ligne",[37,498,499],{},"Voici l'enregistrement qui complète le formulaire du chapitre précédent :",[55,501,503],{"className":57,"code":502,"language":60,"meta":61,"style":61},"\u003C?php\n$stmt = $pdo->prepare(\n    \"INSERT INTO recettes (titre, duree) VALUES (:titre, :duree)\"\n);\n$stmt->execute([\"titre\" => $titre, \"duree\" => $dureeNombre]);\n\n$id = $pdo->lastInsertId();\n",[63,504,505,511,526,543,547,572,576],{"__ignoreMap":61},[66,506,507,509],{"class":68,"line":69},[66,508,73],{"class":72},[66,510,77],{"class":76},[66,512,513,515,517,519,521,523],{"class":68,"line":80},[66,514,396],{"class":86},[66,516,93],{"class":72},[66,518,401],{"class":86},[66,520,227],{"class":72},[66,522,406],{"class":230},[66,524,525],{"class":86},"(\n",[66,527,528,531,534,537,540],{"class":68,"line":102},[66,529,530],{"class":125},"    \"",[66,532,533],{"class":72},"INSERT INTO",[66,535,536],{"class":125}," recettes (titre, duree) ",[66,538,539],{"class":72},"VALUES",[66,541,542],{"class":125}," (:titre, :duree)\"\n",[66,544,545],{"class":68,"line":109},[66,546,99],{"class":86},[66,548,549,551,553,555,557,559,561,564,567,569],{"class":68,"line":142},[66,550,436],{"class":86},[66,552,227],{"class":72},[66,554,441],{"class":230},[66,556,444],{"class":86},[66,558,340],{"class":125},[66,560,154],{"class":72},[66,562,563],{"class":86}," $titre, ",[66,565,566],{"class":125},"\"duree\"",[66,568,154],{"class":72},[66,570,571],{"class":86}," $dureeNombre]);\n",[66,573,574],{"class":68,"line":167},[66,575,106],{"emptyLinePlaceholder":105},[66,577,578,581,583,585,587,590],{"class":68,"line":188},[66,579,580],{"class":86},"$id ",[66,582,93],{"class":72},[66,584,401],{"class":86},[66,586,227],{"class":72},[66,588,589],{"class":230},"lastInsertId",[66,591,476],{"class":86},[37,593,594,596,597,600,601,346],{},[63,595,589],{}," donne l'identifiant de la ligne qui vient d'être créée. Le résultat s'affiche ensuite avec ",[63,598,599],{},"foreach",", en pensant à ",[63,602,603],{},"htmlspecialchars",[605,606,607],"note",{},[37,608,609],{},"Une requête préparée protège les valeurs, pas les noms de tables ou de colonnes. Si le nom d'une colonne vient de l'utilisateur, compare-le à une liste de noms autorisés.",[10,611,613],{"id":612},"composer-le-gestionnaire-de-dépendances","Composer : le gestionnaire de dépendances",[37,615,616,619],{},[22,617,618],{},"Composer"," installe les bibliothèques PHP dont ton projet a besoin et charge tes propres classes. Il se télécharge sur getcomposer.org. Dans ton dossier de projet :",[55,621,625],{"className":622,"code":623,"language":624,"meta":61,"style":61},"language-bash shiki shiki-themes github-light-default github-dark-default github-dark-default","composer init\ncomposer require monolog\u002Fmonolog\n","bash",[63,626,627,636],{"__ignoreMap":61},[66,628,629,633],{"class":68,"line":69},[66,630,632],{"class":631},"sIXEE","composer",[66,634,635],{"class":125}," init\n",[66,637,638,640,643],{"class":68,"line":80},[66,639,632],{"class":631},[66,641,642],{"class":125}," require",[66,644,645],{"class":125}," monolog\u002Fmonolog\n",[37,647,648,649,652,653,656],{},"La première commande crée ",[63,650,651],{},"composer.json",", qui décrit le projet. La seconde télécharge une bibliothèque dans le dossier ",[63,654,655],{},"vendor\u002F"," et l'inscrit dans le fichier.",[10,658,660],{"id":659},"lautoload-psr-4","L'autoload PSR-4",[37,662,663,664,667],{},"Plutôt que d'écrire un ",[63,665,666],{},"require"," pour chaque fichier, Composer sait charger une classe à partir de son nom. On associe un espace de noms à un dossier :",[55,669,673],{"className":670,"code":671,"filename":651,"language":672,"meta":61,"style":61},"language-json shiki shiki-themes github-light-default github-dark-default github-dark-default","{\n    \"autoload\": {\n        \"psr-4\": { \"App\\\\\": \"src\u002F\" }\n    }\n}\n","json",[63,674,675,680,689,714,719],{"__ignoreMap":61},[66,676,677],{"class":68,"line":69},[66,678,679],{"class":86},"{\n",[66,681,682,686],{"class":68,"line":80},[66,683,685],{"class":684},"s5JFL","    \"autoload\"",[66,687,688],{"class":86},": {\n",[66,690,691,694,697,700,703,705,708,711],{"class":68,"line":102},[66,692,693],{"class":684},"        \"psr-4\"",[66,695,696],{"class":86},": { ",[66,698,699],{"class":684},"\"App",[66,701,702],{"class":72},"\\\\",[66,704,267],{"class":684},[66,706,707],{"class":86},": ",[66,709,710],{"class":125},"\"src\u002F\"",[66,712,713],{"class":86}," }\n",[66,715,716],{"class":68,"line":109},[66,717,718],{"class":86},"    }\n",[66,720,721],{"class":68,"line":142},[66,722,723],{"class":86},"}\n",[37,725,726,727,730,731,734],{},"Avec cette règle, la classe ",[63,728,729],{},"App\\Recette"," se trouve dans ",[63,732,733],{},"src\u002FRecette.php",". Après avoir modifié cette section, régénère l'autoloader :",[55,736,738],{"className":622,"code":737,"language":624,"meta":61,"style":61},"composer dump-autoload\n",[63,739,740],{"__ignoreMap":61},[66,741,742,744],{"class":68,"line":69},[66,743,632],{"class":631},[66,745,746],{"class":125}," dump-autoload\n",[37,748,749],{},"Dans ton point d'entrée, une seule ligne suffit ensuite :",[55,751,754],{"className":57,"code":752,"filename":753,"language":60,"meta":61,"style":61},"\u003C?php\nrequire __DIR__ . \"\u002Fvendor\u002Fautoload.php\";\n\nuse App\\Recette;\n\n$r = new Recette(\"Crêpes\", 30);\n","index.php",[63,755,756,762,776,780,790,794],{"__ignoreMap":61},[66,757,758,760],{"class":68,"line":69},[66,759,73],{"class":72},[66,761,77],{"class":76},[66,763,764,766,769,771,774],{"class":68,"line":80},[66,765,666],{"class":72},[66,767,768],{"class":76}," __DIR__",[66,770,334],{"class":72},[66,772,773],{"class":125}," \"\u002Fvendor\u002Fautoload.php\"",[66,775,352],{"class":86},[66,777,778],{"class":68,"line":102},[66,779,106],{"emptyLinePlaceholder":105},[66,781,782,785,788],{"class":68,"line":109},[66,783,784],{"class":72},"use",[66,786,787],{"class":76}," App\\Recette",[66,789,352],{"class":86},[66,791,792],{"class":68,"line":142},[66,793,106],{"emptyLinePlaceholder":105},[66,795,796,799,801,803,806,808,811,813,816],{"class":68,"line":167},[66,797,798],{"class":86},"$r ",[66,800,93],{"class":72},[66,802,117],{"class":72},[66,804,805],{"class":76}," Recette",[66,807,87],{"class":86},[66,809,810],{"class":125},"\"Crêpes\"",[66,812,129],{"class":86},[66,814,815],{"class":76},"30",[66,817,99],{"class":86},[819,820,821],"tip",{},[37,822,823,824,826,827,830,831,834,835,837,838,346],{},"Mets ",[63,825,655],{}," dans ton ",[63,828,829],{},".gitignore",". Il se reconstruit avec ",[63,832,833],{},"composer install"," à partir de ",[63,836,651],{}," et ",[63,839,840],{},"composer.lock",[10,842,844],{"id":843},"étapes-suivantes","Étapes suivantes",[37,846,847],{},"Tu sais maintenant afficher, recevoir et enregistrer des données de façon sûre. Pour aller plus loin, tu peux :",[849,850,851,858,864],"ol",{},[18,852,853,854,857],{},"ranger l'accès à la base dans une classe, par exemple ",[63,855,856],{},"RecetteRepository"," ;",[18,859,860,861,857],{},"ajouter des mots de passe hachés avec ",[63,862,863],{},"password_hash",[18,865,866],{},"découvrir un framework comme Laravel ou Symfony, qui reposent sur Composer.",[10,868,870],{"id":869},"à-retenir","À retenir",[15,872,873,880,887,895,903,912],{},[18,874,875,876,879],{},"PDO est l'interface standard de PHP vers les bases de données ; le DSN ",[63,877,878],{},"sqlite:fichier.db"," désigne une base SQLite.",[18,881,882,883,886],{},"Active ",[63,884,885],{},"PDO::ERRMODE_EXCEPTION"," pour que les erreurs SQL soient signalées.",[18,888,889,890,892,893,346],{},"Ne construis jamais du SQL en collant une valeur utilisateur : utilise ",[63,891,406],{}," puis ",[63,894,441],{},[18,896,897,899,900,902],{},[63,898,473],{}," renvoie toutes les lignes, ",[63,901,491],{}," une seule.",[18,904,905,906,909,910,346],{},"Composer installe les bibliothèques (",[63,907,908],{},"composer require",") dans ",[63,911,655],{},[18,913,914,915,918],{},"L'autoload PSR-4 relie un espace de noms à un dossier ; ",[63,916,917],{},"require \"vendor\u002Fautoload.php\""," suffit à tout charger.",[920,921,922],"style",{},"html pre.shiki code .sOnC1, html code.shiki .sOnC1{--shiki-light:#CF222E;--shiki-default:#FF7B72;--shiki-dark:#FF7B72}html pre.shiki code .s5iOY, html code.shiki .s5iOY{--shiki-light:#0550AE;--shiki-default:#79C0FF;--shiki-dark:#79C0FF}html pre.shiki code .scfrF, html code.shiki .scfrF{--shiki-light:#1F2328;--shiki-default:#E6EDF3;--shiki-dark:#E6EDF3}html pre.shiki code .shWOn, html code.shiki .shWOn{--shiki-light:#0A3069;--shiki-default:#A5D6FF;--shiki-dark:#A5D6FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .syCW7, html code.shiki .syCW7{--shiki-light:#8250DF;--shiki-default:#D2A8FF;--shiki-dark:#D2A8FF}html pre.shiki code .sd6ml, html code.shiki .sd6ml{--shiki-light:#6E7781;--shiki-default:#8B949E;--shiki-dark:#8B949E}html pre.shiki code .sIXEE, html code.shiki .sIXEE{--shiki-light:#953800;--shiki-default:#FFA657;--shiki-dark:#FFA657}html pre.shiki code .s5JFL, html code.shiki .s5JFL{--shiki-light:#116329;--shiki-default:#7EE787;--shiki-dark:#7EE787}",{"title":61,"searchDepth":80,"depth":102,"links":924},[925,926,927,928,929,930,931,932,933,934,935],{"id":12,"depth":80,"text":13},{"id":34,"depth":80,"text":35},{"id":52,"depth":80,"text":53},{"id":207,"depth":80,"text":208},{"id":277,"depth":80,"text":278},{"id":372,"depth":80,"text":373},{"id":495,"depth":80,"text":496},{"id":612,"depth":80,"text":613},{"id":659,"depth":80,"text":660},{"id":843,"depth":80,"text":844},{"id":869,"depth":80,"text":870},"Se connecter à une base avec PDO, écrire des requêtes préparées contre l'injection SQL, puis organiser le projet avec Composer et son autoload.","md",{},[24,379,362,940,473,618,941,942],"SQLite","autoload","PSR-4","avance","\u002Fphp\u002Fpdo-et-composer",[946],"\u002Fphp\u002Fformulaires",[948,952,955],{"nom":949,"url":950,"type":951},"Manuel PHP en français, PDO","https:\u002F\u002Fwww.php.net\u002Fmanual\u002Ffr\u002Fbook.pdo.php","doc",{"nom":953,"url":954,"type":951},"Manuel PHP en français, injection SQL","https:\u002F\u002Fwww.php.net\u002Fmanual\u002Ffr\u002Fsecurity.database.sql-injection.php",{"nom":956,"url":957,"type":951,"anglais":105},"Documentation de Composer (en anglais)","https:\u002F\u002Fgetcomposer.org\u002Fdoc\u002F",{"title":5,"description":936},[960,962,964],{"nom":961,"url":950},"Manuel PHP, PDO",{"nom":963,"url":954},"Manuel PHP, injection SQL",{"nom":956,"url":957},"php\u002F06.pdo-et-composer","ih7E-OCh4_i_nEt_WCNN2-RHeQBHdVTFX7VPK-lxzQ0",[968,972,976,980,984,987,988],{"path":969,"title":970,"stem":971},"\u002Fphp\u002Fpremiers-pas","Premiers pas en PHP","php\u002F01.premiers-pas",{"path":973,"title":974,"stem":975},"\u002Fphp\u002Ftypes-et-chaines","Types, chaînes et conditions","php\u002F02.types-et-chaines",{"path":977,"title":978,"stem":979},"\u002Fphp\u002Fboucles-et-tableaux","Boucles et tableaux","php\u002F03.boucles-et-tableaux",{"path":981,"title":982,"stem":983},"\u002Fphp\u002Ffonctions-et-objets","Fonctions et objets","php\u002F04.fonctions-et-objets",{"path":946,"title":985,"stem":986},"Formulaires et sécurité","php\u002F05.formulaires",{"path":944,"title":5,"stem":965},{"path":989,"title":990,"stem":991},"\u002Fphp","PHP","php\u002Findex",[993,995],{"title":985,"path":946,"stem":986,"description":994,"children":-1},"Recevoir les données d'un formulaire avec $_GET et $_POST, les valider et protéger l'affichage contre les failles XSS avec htmlspecialchars.",null,[997],{"path":946,"title":985,"ligne":60},[999,1001,1002,1003,1005,1006,1007],{"path":969,"title":970,"niveau":1000,"stem":971},"debutant",{"path":973,"title":974,"niveau":1000,"stem":975},{"path":977,"title":978,"niveau":1000,"stem":979},{"path":981,"title":982,"niveau":1004,"stem":983},"intermediaire",{"path":946,"title":985,"niveau":1004,"stem":986},{"path":944,"title":5,"niveau":943,"stem":965},{"path":989,"title":990,"niveau":1008,"stem":991},"memo",1794583449047]