UTF ??? probleem
Als ik de singel qoute uit de tekst weglaat is er natuurlijk geen probleem. Hoe zou ik dat kunnen oplossen?
Dus de database is de laatste versie MariaDB gehost bij one.com. De csv file is gemaakt/weggeschreven als csvutf8.
Bedankt op voorhand.
Dit is de foutmelding:
d1002 Dhaenens, D'Haene, Dehaene,: Error updating record: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'Haene, Dehaene,' WHERE IDR LIKE 'd1002'' at line 2
Gewijzigd op 02/02/2024 18:02:43 door Ignace Verschaeve
In veel gevallen is je data niet goed als je ???? (ja, vraagtekens) ziet, of je benadert de data niet op de juiste manier. Waarschijnlijk heb je een verkeerde character encoding. (hier op PHPhulp ook, trouwens. Maar dat moet ik nog eens onderzoeken als ik een goede testopstelling gemaakt heb).
Dit is een interessant topic over UTF-8:
https://www.phphulp.nl/php/forum/topic/diakritische-tekens-als-weergegeven/103004/1/
Gewijzigd op 02/02/2024 18:25:41 door - Ariën -
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Haal data uit CSV file lijn per lijn
while(($line = fgetcsv($csvFile,0,";")) !== FALSE){
// Get rij data
$IDR = $line[0];
$variant = $line[1];
// Uitlezen van de gegevens in de csv
echo "<span style=\"color: black\">".$IDR." ".$variant.": ";
// Check of er reeds records zijn met dezelfde IDR en namen
$sqlprev = "SELECT IDR FROM anaamvar WHERE IDR LIKE '$IDR' ";
$result = $conn->query($sqlprev);
//$prevResult = $db->query($prevQuery);
if ($result->num_rows > 0)
{
// Update member data in the database
$sqlupd = "UPDATE anaamvar SET
variant = '".$variant."'
WHERE IDR LIKE '".$IDR."' ";
if (mysqli_query($conn, $sqlupd)) {
echo "<span style=\"color: black\">".$IDR." "."Record met succes geupdated. " . "<br/>";
} else {
echo "<span style=\"color: red\">"."Error updating record: " . mysqli_error($conn)."</span>"."<br/>";}
}
else{
$sqlins = "INSERT INTO anaamvar (IDR, variant)
VALUES ('".$IDR."',
'".$variant."')";
if (mysqli_query($conn, $sqlins)) {
echo "<span style=\"color: green\">"."-".$IDR." ".$variant." "."Record met succes bijgevoegd. " . "<br/>";
} else {
echo "<span style=\"color: red\">"."Error invoegen record: " . mysqli_error($conn)."</span>"."<br/>";}
$teller ++;
}
}
// Close opened CSV file
fclose($csvFile);
$qstring = '?status=succ';
}else{
$qstring = '?status=err';
}
}else{
$qstring = '?status=invalid_file';
}
while(($line = fgetcsv($csvFile,0,";")) !== FALSE){
// Get rij data
$IDR = $line[0];
$variant = $line[1];
// Uitlezen van de gegevens in de csv
echo "<span style=\"color: black\">".$IDR." ".$variant.": ";
// Check of er reeds records zijn met dezelfde IDR en namen
$sqlprev = "SELECT IDR FROM anaamvar WHERE IDR LIKE '$IDR' ";
$result = $conn->query($sqlprev);
//$prevResult = $db->query($prevQuery);
if ($result->num_rows > 0)
{
// Update member data in the database
$sqlupd = "UPDATE anaamvar SET
variant = '".$variant."'
WHERE IDR LIKE '".$IDR."' ";
if (mysqli_query($conn, $sqlupd)) {
echo "<span style=\"color: black\">".$IDR." "."Record met succes geupdated. " . "<br/>";
} else {
echo "<span style=\"color: red\">"."Error updating record: " . mysqli_error($conn)."</span>"."<br/>";}
}
else{
$sqlins = "INSERT INTO anaamvar (IDR, variant)
VALUES ('".$IDR."',
'".$variant."')";
if (mysqli_query($conn, $sqlins)) {
echo "<span style=\"color: green\">"."-".$IDR." ".$variant." "."Record met succes bijgevoegd. " . "<br/>";
} else {
echo "<span style=\"color: red\">"."Error invoegen record: " . mysqli_error($conn)."</span>"."<br/>";}
$teller ++;
}
}
// Close opened CSV file
fclose($csvFile);
$qstring = '?status=succ';
}else{
$qstring = '?status=err';
}
}else{
$qstring = '?status=invalid_file';
}
Het is eigenlijk een tamelijk simpel gegeven. Ik weet alleen niet hoe ik die single quote kan escapen. Daar reikt mijn magere kennis van PHP niet ver genoeg. IK leer elke dag bij door vallen en opstaan want ik heb nooit een opleiding gehad, alles is zelfstudie.
Gewijzigd op 02/02/2024 18:57:38 door Ignace Verschaeve
Eigenlijk valt dit al onder de basiskennis. Als je dit niet weet dan maak je jouw database behoorlijk lek.
Of gebruik prepared statements. Dan hoef je ook niet extra op deze zorgen te letten.
Gewijzigd op 02/02/2024 19:27:07 door - Ariën -
Ik weet wel wat over die real escape strings maar niet hoe ik dit kan toepassen op de tabellen/lijnen in het csv bestand. Daar ligt mijn knoop. En kom niet af met theorie. Dat kan ik niet. Ik moet een voorbeeld hebben om dit te begrijpen. Geen droge theoriën aub.
Je moet gewoon $IDR escapen. Hij struikelt nu over de ' van D'Haene die je query dus kapotmaakt.
Gewijzigd op 02/02/2024 20:42:20 door - Ariën -
Dit was de oorspronkelijke code die ik vervangen heb door een real_escape
Code (php)
1
2
3
2
3
$IDR = $line[0];
//$variant = $line[1];
$variant = mysqli_real_escape_string($csvFile, $line[1]);
//$variant = $line[1];
$variant = mysqli_real_escape_string($csvFile, $line[1]);
Waarbij $csvFile de .csv is waarvan ik inlees.
Maar ik krijg nu volgende foutmelding:
Fatal error: Uncaught TypeError: mysqli_real_escape_string(): Argument #1 ($mysql) must be of type mysqli, resource given in /customers/1/5/0/fv-vl-ardennen.be/httpd.www/beheerder/importdatanaamvar.php:49 Stack trace: #0 /customers/1/5/0/fv-vl-ardennen.be/httpd.www/beheerder/importdatanaamvar.php(49): mysqli_real_escape_string(Resource id #5, 'Aelvoet, Aalvoe...') #1 {main} thrown in /customers/1/5/0/fv-vl-ardennen.be/httpd.www/beheerder/importdatanaamvar.php on line 49
Waarbij die Aelvoet de eerste lijn is in die csv file:
a1000 Aelvoet, Aalvoet, Aelvoedt, Allevoet,
Ik zie niet direct waar ik het fout doe, jullie misschien wel?
Ik volg deze instructies: https://www.php.net/manual/en/function.mysql-real-escape-string.php
https://www.w3schools.com/php/func_mysqli_real_escape_string.asp
Denk dat dit voorbeeld in de buurt komt :
Zo dus :
Gewijzigd op 03/02/2024 11:00:24 door Ignace Verschaeve
is, dat $variant nu een bewerkte versie van de naam bevat.
Zou je ergens dit willen gebruiken voor iets anders dan in de query, dan heb je een probleem. (en ja: ik zie dat jij dat op regel 34 inderdaad doet)
Zet je op het scherm
Dan gaat dat 99 van de 100 keer goed, maar Jeanne d\'Arc is waarschijnlijk niet wat je op je scherm wilde zien.
Daarom ben ik er voorstander van om dergelijke escaping alleen toe te passen waar je het nodig hebt.
Dus in de opbouw van de query zelf.
(en als tegenhanger bij de opbouw van een html of xml bericht: voor de escaping met htmlspecialchars geldt hetzelfde)
Ik zou dus gekozen hebben voor de aanpassingen op regels 19 en 32:
Code (php)
1
2
3
4
5
2
3
4
5
<?php
$sqlupd = "UPDATE anaamvar SET
variant = '". mysqli_real_escape_string($conn, $variant)."'
WHERE IDR LIKE '".$IDR."' ";?>
$sqlupd = "UPDATE anaamvar SET
variant = '". mysqli_real_escape_string($conn, $variant)."'
WHERE IDR LIKE '".$IDR."' ";?>
En eveneens zou ik overal waar je dergelijke input naar je scherm stuurt weer htmlspecialchars() om variabelen zetten.
Is het niet voor bewuste acties van gebruikers, dan voorkom je toch ook problemen door onverwachte "rare" html-tekens in je invoer. Ook als je dat niet verwacht.
Dat escape teken komt dan wel niet zichtbaar in het uiteindelijke resultaat. En het doet wat ik wil doen. Qua beveiliging volstaat dit door het feit dat enkel bepaalde beheerders deze bewerkingen kunnen toepassen geen gebruikers. In wezen is het enkel alleen ik die het doe. Ik ben bezig met het opstellen van een lijst van 10.000den naamvarianten voor genealogische doeleinden. Gebaseerd op indexen van parochieregisters van omstreeks 1600 tot 1796 waarbij oude vormen gekoppeld worden aan de moderne versies. Mijn fout was dat ik een verkeerde redenering volgde over de werking van de real escape string. Ik slaag er nu eenmaal maar in om theorie te leren aan de hand van de praktijk en niet omgekeerd. Mijn leeftijd zeker?
Daar komt ten onrecht een \ in te staan.
En escaping van html en sql is niet alleen bedoeld als beveiliging tegen kwaadwillende derden.
Als jij lijsten inleest met geboorteregisters en dergelijke dan kan ik me voorstellen dat een deel daarvan met OCR is ingelezen en dat daarbij een C die wat hoeking geschreven is, zo maar verandert in een <
Of dat iemand bij het overtypen een typfout maakt en een < of iets dergelijks opneemt in de lijst.
Dáár wil je je ook tegen beschermen.
Dat kan leiden tot kapotte invoer in je database, of onverwacht blanco schermen
Voorbeeld?
Ik heb ooit eens een bug moeten zoeken omdat iemand in een fabriek nooit orders kon invoeren in het intranet.
Bij iedereen ging het goed, alleen bij deze gebruiker niet.
Uiteindelijk bleek dat degene die het gebouwd had van mening was dat de invoer uit de database veilig was en dat escaping onnodig zou zijn.
Alleen werd bij de order niet alleen user_id opgeslagen maar ook de naam.
En je raadt het al: deze persoon had een ' zijn naam zitten.
Dat gaf dus overlast voor deze gebruiker;
kostte tijd voor de opvolger van de developer (me)
Daarnaast wil je weer niet dat in je database Dhr Röntgen als "R&oul;tgen" wordt opgeslagen,
en op het scherm wil je de \\\\ niet zien staan.
Daarom ben ik voorstander van:
- altijd escapen op de plek waar het nodig is zodat je niet vertrouwen moet op "10 regels geleden zal dat wel geregeld zijn"
- en ook alleen daar waar het nodig is.
Je loopt dan ook niet aan tegen "ik moet een patch uitvoeren op de naam van dit record."
Want het gaat net zo goed fout bij een ' in de voornaam, geboorteplaats en zijn mailadres (al zullen daar weinig van zijn in dit geval)
Ignace Verschaeve op 05/02/2024 10:56:14:
Dat escape teken komt dan wel niet zichtbaar in het uiteindelijke resultaat. En het doet wat ik wil doen. Qua beveiliging volstaat dit door het feit dat enkel bepaalde beheerders deze bewerkingen kunnen toepassen geen gebruikers. In wezen is het enkel alleen ik die het doe. Ik ben bezig met het opstellen van een lijst van 10.000den naamvarianten voor genealogische doeleinden. Gebaseerd op indexen van parochieregisters van omstreeks 1600 tot 1796 waarbij oude vormen gekoppeld worden aan de moderne versies. Mijn fout was dat ik een verkeerde redenering volgde over de werking van de real escape string. Ik slaag er nu eenmaal maar in om theorie te leren aan de hand van de praktijk en niet omgekeerd. Mijn leeftijd zeker?
Nee, PHP is nodeloos ingewikkeld, zo is het nu eenmaal, dat heeft niets met leeftijd te maken.
En het gebruik van Mysqli::real_escape_string() is lastig, omdat je zelf heel goed moet snappen hoe je het per geval dient te gebruiken. Daarmee fouten maken kan catastrofaal zijn voor de veiligheid van je programma en de gegevens, SQL injectie staat nog steeds op de 3e plaats van meest voorkomende software fouten in de wereld in 2023: https://cwe.mitre.org/top25/archive/2023/2023_top25_list.html
Laten we eerlijk zijn, mysqli::real_escape_string() helpt daarbij nou niet echt om software veiliger te maken.
Gelukkig bestaat er ook nog zoiets als prepared statements, waarin escaping automatisch gaat. Je hoeft alleen een query te voorzien van plaatsvervangende symbolen, zoals $1, of met namen, zoals met PDO. Aparte PHP variabelen geef je mee aan de functies en escaping gaat dan altijd goed, er is geen onduidelijkheid over welk deel nu SQL code is, en welk deel SQL data. Zie: https://www.php.net/manual/en/mysqli.quickstart.prepared-statements.php
Het enige jammere van prepared statements is dat het niet voor alle situaties werkt. Stel je wilt de naam van een tabel in een JOIN variabel maken, dan is de enige optie om dat met de hand te doen (zoals met bijna alles waar het leuker wordt met SQL).
Er zijn ook betere databases als PostgreSQL. PostgreSQL heeft in PHP wel ondersteuning met pg_escape_identifier(), en nog veel meer leuke dingen.