Vraag Waarom zou ik geen mysql_ * -functies in PHP gebruiken?


Wat zijn de technische redenen waarom men niet zou moeten gebruiken mysql_* functies? (B.v. mysql_query(), mysql_connect() of mysql_real_escape_string())?

Waarom zou ik iets anders gebruiken, zelfs als ze op mijn site werken?

Als ze niet werken op mijn site, waarom krijg ik dan fouten zoals

Waarschuwing: mysql_connect (): geen bestand of map


2194
2017-10-12 13:18


oorsprong


antwoorden:


De MySQL-extensie:

  • Wordt niet actief ontwikkeld
  • is officieel verouderd vanaf PHP 5.5 (uitgebracht in juni 2013).
  • Is geweest verwijderd geheel vanaf PHP 7.0 (uitgebracht december 2015)
    • Dit betekent dat vanaf 31 december 2018 het zal niet bestaan ​​in enige ondersteunde versie van PHP. Momenteel wordt het alleen maar veiligheid updates.
  • Gebrek aan een OO-interface
  • Ondersteunt niet:
    • Niet-blokkerende, asynchrone zoekopdrachten
    • Voorbereide verklaringen of geparameteriseerde vragen
    • Opgeslagen procedures
    • Meerdere verklaringen
    • transacties
    • De "nieuwe" authenticatiemethode voor wachtwoorden (standaard ingeschakeld in MySQL 5.6; vereist in 5.7)
    • Alle functionaliteit in MySQL 5.1

Omdat het is verouderd, maakt het gebruik van het uw code minder toekomstbestendig.

Gebrek aan ondersteuning voor voorbereide instructies is vooral belangrijk omdat ze een duidelijkere, minder foutgevoelige methode bieden om te ontsnappen en externe gegevens te citeren dan er handmatig uit te komen met een afzonderlijke functieaanroep.

Zien de vergelijking van SQL-extensies.


1808
2018-01-01 11:52



PHP biedt drie verschillende API's om verbinding te maken met MySQL. Dit zijn de mysql(verwijderd vanaf PHP 7), mysqli, en PDO extensies.

De mysql_* functies waren erg populair, maar het gebruik ervan wordt niet meer aangemoedigd. Het documentatieteam bespreekt de databasebeveiligingssituatie en het onderwijzen van gebruikers om weg te gaan van de veelgebruikte extensie ext / mysql maakt hier deel van uit (vink aan php.internals: ext / mysql beëindigen).

En het latere PHP-ontwikkelteam heeft de beslissing genomen om te genereren E_DEPRECATED fouten wanneer gebruikers verbinding maken met MySQL, of via mysql_connect(), mysql_pconnect() of de ingebouwde impliciete verbindingsfunctionaliteit ext/mysql.

ext/mysql was officieel verouderd vanaf PHP 5.5 en is geweest verwijderd vanaf PHP 7.

Zie de rode doos?

Als je het doet mysql_* functie handmatige pagina, ziet u een rood kader, waarin wordt uitgelegd dat het niet meer mag worden gebruikt.

Waarom


Weggaan van ext/mysql gaat niet alleen over beveiliging, maar ook over toegang tot alle functies van de MySQL-database.

ext/mysql werd gebouwd voor MySQL 3.23 en sindsdien zijn er maar weinig toevoegingen geweest terwijl de compatibiliteit met deze oude versie grotendeels behouden bleef, waardoor de code een beetje moeilijker te onderhouden is. Ontbrekende functies die niet worden ondersteund door ext/mysql omvatten: (van PHP handleiding).

Reden om niet te gebruiken mysql_* functie:

  • Niet onder actieve ontwikkeling
  • Verwijderd vanaf PHP 7
  • Gebrek aan een OO-interface
  • Ondersteunt geen niet-blokkerende, asynchrone zoekopdrachten
  • Ondersteunt geen voorbereide stellingen of geparametriseerde zoekopdrachten
  • Ondersteunt opgeslagen procedures niet
  • Ondersteunt niet meerdere verklaringen
  • Ondersteunt niet transacties
  • Ondersteunt niet alle functionaliteit in MySQL 5.1

Bovenstaand punt geciteerd uit Quentins antwoord

Gebrek aan ondersteuning voor voorbereide instructies is vooral belangrijk omdat ze een duidelijkere, minder foutgevoelige methode bieden om te ontsnappen en externe gegevens te citeren dan handmatig te ontkomen met een afzonderlijke functieaanroep.

Zie de vergelijking van SQL-extensies.


Ontkenning waarschuwingen onderdrukken

Terwijl de code wordt geconverteerd naar MySQLi/PDO, E_DEPRECATED fouten kunnen worden onderdrukt door in te stellen error_reporting in php.ini uitsluiten E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

Merk op dat dit ook zal verbergen andere afkeuringswaarschuwingen, die echter mogelijk voor andere zaken dan MySQL is. (van PHP handleiding)

Het artikel PDO versus MySQLi: welke moet u gebruiken? door Dejan Marjanovic zal je helpen om te kiezen.

En een betere manier is PDO, en ik ben nu een simpele aan het schrijven PDO tutorial.


Een eenvoudige en korte PDO-zelfstudie


V. De eerste vraag in mijn hoofd was: wat is `BOB`?

EEN. "PDO - PHP Data Objects - is een databasetoegangslaag die een uniforme methode biedt voor toegang tot meerdere databases. "

alt text


Verbinden met MySQL

Met mysql_* functie of we kunnen het op de oude manier zeggen (verouderd in PHP 5.5 en hoger)

$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

Met PDO: Alles wat u hoeft te doen is een nieuw maken PDO voorwerp. De constructor accepteert parameters voor het opgeven van de databasebron PDODe constructor neemt meestal vier parameters op DSN (naam databron) en optioneel username, password.

Hier denk ik dat je bekend bent met alles behalve DSN; dit is nieuw in PDO. EEN DSN is eigenlijk een reeks opties die vertellen PDO welk stuurprogramma te gebruiken en verbindingsgegevens. Raadpleeg voor meer informatie PDO MySQL DSN.

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

Notitie: je kan ook gebruiken charset=UTF-8, maar soms veroorzaakt het een fout, dus het is beter om te gebruiken utf8.

Als er een verbindingsfout is, gooit deze een PDOException object dat kan worden gevangen om te verwerken Exception verder.

Goed gelezen: Verbindingen en verbindingsbeheer ¶ 

U kunt ook verschillende stuurprogramma-opties doorgeven als een array naar de vierde parameter. Ik raad aan om de parameter die zet door te geven PDO in de uitzonderingsmodus. Omdat sommigen PDO stuurprogramma's ondersteunen geen native voorbereide instructies, dus PDO voert emulatie uit van het preparaat. Hiermee kunt u deze emulatie ook handmatig inschakelen. Als u de voorbereide instructies aan de kant van de server wilt gebruiken, moet u deze expliciet instellen false.

De andere is om voorbereidende emulatie uit te schakelen die ingeschakeld is in de MySQLstuurprogramma standaard, maar voorbereiding emulatie moet worden uitgeschakeld om te gebruiken PDO veilig.

Ik zal later uitleggen waarom de voorbereiding van emulatie moet worden uitgeschakeld. Om reden te vinden, kijk alstublieft deze post.

Het is alleen bruikbaar als u een oude versie van gebruikt MySQL die ik niet aanraad.

Hieronder ziet u een voorbeeld van hoe u het kunt doen:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password',
              array(PDO::ATTR_EMULATE_PREPARES => false,
              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

Kunnen we kenmerken instellen na BOB-constructie?

Ja, we kunnen ook enkele attributen instellen na de BOB-constructie met de setAttribute methode:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Foutafhandeling


Foutafhandeling is veel gemakkelijker in PDO dan mysql_*.

Een veel voorkomende praktijk bij het gebruik mysql_* is:

//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die() is geen goede manier om met de fout om te gaan, omdat we het ding niet kunnen verwerken die. Het beëindigt het script gewoon abrupt en herhaalt vervolgens de foutmelding naar het scherm dat u normaal NIET wilt laten zien aan uw eindgebruikers, en laat bloedige hackers uw schema ontdekken. Als alternatief de retourwaarden van mysql_* functies kunnen vaak in combinatie met worden gebruikt mysql_error () om fouten af ​​te handelen.

PDO biedt een betere oplossing: uitzonderingen. Alles waar we mee doen PDO moet worden ingepakt in een try-catch blok. We kunnen dwingen PDO in een van de drie foutmodi door het kenmerk voor de foutmodus in te stellen. Drie foutafhandelingsmodi staan ​​hieronder.

  • PDO::ERRMODE_SILENT. Het stelt alleen foutcodes in en gedraagt ​​zich vrijwel hetzelfde als mysql_* waar je elk resultaat moet controleren en dan naar kijken $db->errorInfo(); om de foutdetails te krijgen.
  • PDO::ERRMODE_WARNING verhogen E_WARNING. (Runtime-waarschuwingen (niet-fatale fouten) De uitvoering van het script wordt niet onderbroken.)
  • PDO::ERRMODE_EXCEPTION: Uitzonderingen gooien. Dit is een fout opgetreden door BOB. Je moet niet gooien PDOException van je eigen code. Zien Uitzonderingen voor meer informatie over uitzonderingen in PHP. Het lijkt heel veel op or die(mysql_error());, wanneer het niet wordt gepakt. Maar niet zoals or die(), de PDOException kan netjes worden betrapt en behandeld als je daarvoor kiest.

Goed gelezen:

Graag willen:

$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

En je kunt het inpakken try-catch, zoals hieronder:

try {
    //Connect as appropriate as above
    $db->query('hi'); //Invalid query!
} 
catch (PDOException $ex) {
    echo "An Error occured!"; //User friendly message/message you want to show to user
    some_logging_function($ex->getMessage());
}

Je hoeft er niet mee om te gaan try-catch direct. Je kunt het op elk gewenst moment vangen, maar ik raad je ten zeerste aan om het te gebruiken try-catch. Het kan ook logischer zijn om het te vangen buiten de functie die de PDO stuff:

function data_fun($db) {
    $stmt = $db->query("SELECT * FROM table");
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

//Then later
try {
    data_fun($db);
}
catch(PDOException $ex) {
    //Here you can handle error and show message/perform action you want.
}

Je kunt ook omgaan met or die() of we kunnen zeggen zoals mysql_*, maar het zal heel gevarieerd zijn. U kunt de gevaarlijke foutmeldingen in de productie verbergen door te draaien display_errors off en gewoon het lezen van uw foutenlogboek.

Nu, na het lezen van alle bovenstaande dingen, denk je waarschijnlijk: wat is dat nou als ik gewoon gewoon wil beginnen met leunen SELECT, INSERT, UPDATEof DELETE uitspraken? Maak je geen zorgen, hier gaan we:


Gegevens selecteren

PDO select image

Dus waar ben je mee bezig? mysql_* is:

<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());

$num_rows = mysql_num_rows($result);

while($row = mysql_fetch_assoc($result)) {
    echo $row['field1'];
}

Nu in PDO, je kunt dit doen als:

<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

Of

<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

//Use $results

Notitie: Als u de methode zoals hieronder gebruikt (query()), retourneert deze methode a PDOStatement voorwerp. Dus als u het resultaat wilt ophalen, gebruik het dan zoals hierboven.

<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

In PDO Data wordt het verkregen via de ->fetch(), een methode voor uw afhandelingshandle. Voordat u Fetch oproept, zou de beste aanpak zijn om de BOB te laten weten hoe u wilt dat de gegevens worden opgehaald. In de onderstaande sectie leg ik dit uit.

Modi ophalen

Let op het gebruik van PDO::FETCH_ASSOC in de fetch() en fetchAll() code hierboven. Dit vertelt PDO om de rijen terug te brengen als een associatieve array met de veldnamen als sleutels. Er zijn ook veel andere ophaalmodi die ik een voor een zal uitleggen.

Allereerst leg ik uit hoe je de fetch-modus selecteert:

 $stmt->fetch(PDO::FETCH_ASSOC)

In het bovenstaande heb ik gebruikt fetch(). Je kan ook gebruiken:

Nu kom ik om de modus op te halen:

  • PDO::FETCH_ASSOC: retourneert een array geïndexeerd op kolomnaam zoals geretourneerd in uw resultatenset
  • PDO::FETCH_BOTH (standaardinstelling): retourneert een array die is geïndexeerd door zowel de kolomnaam als het 0-geïndexeerde kolomnummer dat is geretourneerd in uw resultatenset

Er zijn nog meer keuzes! Lees er alles over in PDOStatement Haal documentatie op..

De rij tellen:

In plaats van gebruiken mysql_num_rows om het aantal geretourneerde rijen te krijgen, kunt u een PDOStatement en doe rowCount(), graag willen:

<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

De laatst ingevoerde ID verkrijgen

<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();

Instructies invoegen en bijwerken of verwijderen

Insert and update PDO image

Wat we doen in mysql_* functie is:

<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

En in pdo kan hetzelfde gebeuren door:

<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;

In de bovenstaande zoekopdracht PDO::exec voer een SQL-instructie uit en retourneert het aantal betrokken rijen.

Invoegen en verwijderen wordt later behandeld.

De bovenstaande methode is alleen nuttig als u de variabele in de query niet gebruikt. Maar wanneer u een variabele in een query moet gebruiken, probeer het nooit zoals hierboven en daar voor voorbereide verklaring of geparametriseerde verklaring is.


Voorbereide verklaringen

Q. Wat is een voorbereide uitspraak en waarom heb ik ze nodig?
EEN. Een voorbereide instructie is een vooraf gecompileerde SQL-instructie die meerdere keren kan worden uitgevoerd door alleen de gegevens naar de server te verzenden.

De typische workflow van het gebruik van een voorbereide verklaring is als volgt (geciteerd uit Wikipedia drie drie punten):

  1. Bereiden: De verklaringenjabloon wordt gemaakt door de toepassing en verzonden naar het databasebeheersysteem (DBMS). Bepaalde waarden blijven niet gespecificeerd, parameters genoemd, tijdelijke aanduidingen of variabelen binden (gelabeld ? hieronder):

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. De DBMS parseert, compileert en voert query-optimalisatie uit op de instructiesjabloon en slaat het resultaat op zonder het uit te voeren.

  3. uitvoeren: Op een later tijdstip levert de toepassing waarden (of bindt) voor de parameters en voert de DBMS de instructie uit (mogelijk een resultaat retourneren). De toepassing kan de verklaring zo vaak uitvoeren als hij wil met verschillende waarden. In dit voorbeeld kan het 'Brood' leveren voor de eerste parameter en 1.00voor de tweede parameter.

U kunt een voorbereide instructie gebruiken door tijdelijke aanduidingen in uw SQL op te nemen. Er zijn in principe drie zonder plaatsaanduidingen (probeer dit niet met variabele de bovenstaande), één met naamloze plaatsaanduidingen en één met naamaanduiding.

Q. Dus nu, wat zijn tijdelijke aanduidingen en hoe gebruik ik ze?
EEN. Benoemde tijdelijke aanduidingen. Gebruik beschrijvende namen voorafgegaan door een dubbele punt, in plaats van vraagtekens. We geven niets om de positie / volgorde van waarde in naamhouder:

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

U kunt ook binden met behulp van een execute array:

<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

Een andere leuke functie voor OOP vrienden is dat benoemde tijdelijke aanduidingen de mogelijkheid hebben om objecten rechtstreeks in uw database in te voegen, ervan uitgaande dat de eigenschappen overeenkomen met de benoemde velden. Bijvoorbeeld:

class person {
    public $name;
    public $add;
    function __construct($a,$b) {
        $this->name = $a;
        $this->add = $b;
    }

}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);

Q. Dus nu, wat zijn naamloze plaatsaanduidingen en hoe gebruik ik ze?
EEN. Laten we een voorbeeld hebben:

<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();

en

$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));

In het bovenstaande kun je die zien ? in plaats van een naam zoals in een naamplaatshouder. Nu, in het eerste voorbeeld, wijzen we variabelen toe aan de verschillende tijdelijke aanduidingen ($stmt->bindValue(1, $name, PDO::PARAM_STR);). Vervolgens wijzen we waarden toe aan die tijdelijke aanduidingen en voeren deze uit. In het tweede voorbeeld gaat het eerste arrayelement naar het eerste ? en de tweede tot de tweede ?.

NOTITIE: In naamloze plaatsaanduidingen we moeten zorgen voor de juiste volgorde van de elementen in de matrix die we doorgeven aan de PDOStatement::execute() methode.


SELECT, INSERT, UPDATE, DELETE voorbereide vragen

  1. SELECT:

    $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
    $stmt->execute(array(':name' => $name, ':id' => $id));
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
  2. INSERT:

    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
    $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
    
  3. DELETE:

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
    
  4. UPDATE:

    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
    $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();
    

NOTITIE:

Echter PDO en / of MySQLi zijn niet helemaal veilig. Controleer het antwoord Zijn BOB verklaringen voldoende voorbereid om SQL-injectie te voorkomen? door ircmaxell. Ik citeer ook een deel van zijn antwoord:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));

1160
2017-10-12 13:28



Laten we eerst beginnen met de standaardcommentaar die we iedereen geven:

Gebruik alsjeblieft niet mysql_* functies in nieuwe code. Ze worden niet langer onderhouden en zijn officieel verouderd. Zie de rode doos? Leren over voorbereide verklaringen in plaats daarvan, en gebruik BOB of MySQLi - Dit artikel zal u helpen beslissen welke. Als u kiest voor BOB, hier is een goede tutorial.

Laten we dit doornemen, zin voor zin, en uitleggen:

  • Ze worden niet langer onderhouden en zijn officieel verouderd

    Dit betekent dat de PHP-gemeenschap langzamerhand de ondersteuning voor deze zeer oude functies verliest. Ze zullen waarschijnlijk niet bestaan ​​in een toekomstige (recente) versie van PHP! Voortgezet gebruik van deze functies kan uw code breken in de (niet zo) verre toekomst.

    NIEUWE! - ext / mysql is nu officieel verouderd vanaf PHP 5.5!

    Nieuwer! ext / mysql is verwijderd in PHP 7.

  • In plaats daarvan moet u leren van voorbereide verklaringen

    mysql_* extensie ondersteunt niet voorbereide verklaringen, wat (onder andere) een zeer effectieve tegenmaatregel is tegen SQL injectie. Het herstelde een zeer ernstige kwetsbaarheid in MySQL-afhankelijke applicaties waarmee aanvallers toegang kunnen krijgen tot je script en kunnen uitvoeren elke mogelijke vraag in uw database.

    Zie voor meer informatie Hoe kan ik SQL-injectie in PHP voorkomen?

  • Zie de rode doos?

    Wanneer je naar een gaat mysql functie handmatige pagina, ziet u een rood kader, waarin wordt uitgelegd dat het niet meer mag worden gebruikt.

  • Gebruik PDO of MySQLi

    Er zijn betere, robuustere en goed gebouwde alternatieven, PDO - PHP-databaseobject, dat een volledige OOP-benadering biedt voor database-interactie, en MySQLi, wat een MySQL-specifieke verbetering is.


279
2017-12-24 23:30



Makkelijk te gebruiken

De analytische en synthetische redenen werden al genoemd. Voor nieuwkomers is er een grotere stimulans om te stoppen met het gebruik van de gedateerde mysql_-functies.

Hedendaagse database-API's zijn gewoon gemakkelijker gebruiken.

Het is meestal het gebonden parameters die code kan vereenvoudigen. En met uitstekende tutorials (zoals hierboven te zien) de overgang naar BOB is niet overdreven zwaar.

Het herschrijven van een grotere codebasis kost echter tijd. Raison d'être voor dit tussenproduct:

Equivalent pdo_ * functioneert in plaats van mysql_ *

Gebruik makend van <pdo_mysql.php> je kunt met de oude mysql_-functies overschakelen minimale inspanning. Het voegt eraan toe pdo_ functie wrappers die hun vervangen mysql_ tegenhangers.

  1. eenvoudigweg include_once("pdo_mysql.php"); in elk aanroepscript dat moet communiceren met de database.

  2. Verwijder de mysql_ functieprefix overal en vervang het door pdo_.

    • mysql_connect() wordt pdo_connect()
    • mysql_query() wordt pdo_query()
    • mysql_num_rows() wordt pdo_num_rows()
    • mysql_insert_id() wordt pdo_insert_id()
    • mysql_fetch_array() wordt pdo_fetch_array()
    • mysql_fetch_assoc() wordt pdo_fetch_assoc()
    • mysql_real_escape_string() wordt pdo_real_escape_string()
    • enzovoort... 

       
  3. Je code werkt hetzelfde en ziet er nog steeds grotendeels hetzelfde uit:

    include_once("pdo_mysql.php"); 
    
    pdo_connect("localhost", "usrABC", "pw1234567");
    pdo_select_db("test");
    
    $result = pdo_query("SELECT title, html FROM pages");  
    
    while ($row = pdo_fetch_assoc($result)) {
        print "$row[title] - $row[html]";
    }
    

En voila.
Uw code is gebruik makend van BOB.
Nu is het tijd om het echt te doen gebruik maken van het.

Gebonden parameters kunnen eenvoudig te gebruiken zijn

U hebt gewoon een minder logge API nodig.

pdo_query() voegt zeer gemakkelijke ondersteuning toe voor gebonden parameters. Het omzetten van oude code is eenvoudig:

Verplaats uw variabelen uit de SQL-reeks.

  • Voeg ze toe als door komma's gescheiden functieparameters aan pdo_query().
  • Plaats vraagtekens ? als tijdelijke aanduidingen waar de variabelen eerder waren.
  • Van afkomen ' enkele aanhalingstekens die eerder stringwaarden / variabelen omsloten.

Het voordeel wordt duidelijker voor langere code.

Vaak worden stringvariabelen niet alleen geïnterpoleerd naar SQL, maar worden ze aaneengeschakeld met escapende gesprekken daartussenin.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
   pdo_real_escape_string($title) . "' AND user <> '" .
   pdo_real_escape_string($root) . "' ORDER BY date")

Met ? placeholders toegepast, daar hoef je je niet mee bezig te houden:

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

Onthoud dat pdo_ * nog steeds toestaat of of.
Ontsnap gewoon niet aan een variabele en bind het in dezelfde query.

  • De tijdelijke aanduiding wordt geleverd door de echte PDO erachter.
  • Dus ook toegestaan :named plaatsaanduidinglijsten later.

Wat nog belangrijker is, je kunt $ _REQUEST [] -variabelen veilig achter elke vraag doorgeven. Indien ingediend <form> velden komen overeen met de databasestructuur, het is zelfs nog korter:

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

Zoveel eenvoud. Maar laten we teruggaan naar enkele meer herschrijfadviezen en technische redenen waarom u misschien wilt verdwijnen mysql_en ontsnappen.

Fix of verwijder elke oldschool sanitize() functie

Als je alles hebt geconverteerd mysql_ oproepen naar pdo_query met gebonden params, verwijder alle overbodig pdo_real_escape_string noemt.

In het bijzonder zou u om het even welke moeten herstellen sanitize of clean of filterThis of clean_data functies zoals geadverteerd door gedateerde zelfstudies in de ene of de andere vorm:

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

Meest opvallende bug hier is het gebrek aan documentatie. Belangrijker was dat de volgorde van filteren precies in de verkeerde volgorde was.

  • De juiste volgorde zou zijn geweest: verouderde stripslashes als de binnenste oproep, dan trim, daarna strip_tags, htmlentities voor outputcontext, en slechts ten slotte de _escape_string omdat de toepassing ervan de SQL-intervertising direct zou moeten voorafgaan.

  • Maar als eerste stap gewoon doe afstand van de _real_escape_string noemen.

  • Misschien moet je de rest van je bewaren sanitize() functie voor nu als uw database en applicatiestroom HTML-contextveilige strings verwachten. Voeg een opmerking toe dat alleen HTML van toepassing is die voortaan wordt vrijgegeven.

  • String / value-afhandeling wordt gedelegeerd aan PDO en de geparametriseerde instructies.

  • Als er sprake was van stripslashes() in uw sanitatiefunctie kan dit duiden op een hoger overzicht.

    • Dat was er gewoonlijk om schade (dubbel te ontsnappen) aan het verouderde ongedaan te maken magic_quotes. Wat echter wel is best centraal opgelost, niet tekenreeks per string.

    • Gebruik een van de gebruikersland omkering benaderingen. Verwijder vervolgens de stripslashes() in de sanitize functie.

    Historische opmerking over magic_quotes. Die functie is terecht verouderd. Het wordt vaak ten onrechte afgeschilderd als mislukt veiligheid functie echter. Maar magic_quotes zijn net zo goed een mislukte beveiligingsfunctie als tennisballen hebben gefaald als voedingsbron. Dat was gewoon niet hun doel.

    De oorspronkelijke implementatie in PHP2 / FI introduceerde het expliciet met slechts "offertes worden automatisch geëscaped, waardoor het gemakkelijker wordt om formuliergegevens direct door te geven aan msql-query's"Het was met name per ongeluk veilig om mee te gebruiken mSQL, zoals alleen ASCII wordt ondersteund.
      Vervolgens heeft PHP3 / Zend magic_quotes voor MySQL opnieuw geïntroduceerd en verkeerd gedocumenteerd. Maar oorspronkelijk was het gewoon een gemaksfunctie, niet van plan voor beveiliging.

Hoe voorbereide verklaringen verschillen

Wanneer u tekenreeksvariabelen in de SQL-query's versleutelt, wordt het niet alleen ingewikkelder voor u om te volgen. Het is ook een extra inspanning van MySQL om code en gegevens opnieuw te scheiden.

SQL-injecties zijn gewoon wanneer gegevens lopen over in code context. Een databaseserver kan later niet zien waar PHP oorspronkelijk variabelen gelijmd heeft tussen query-clausules.

Met gebonden parameters scheidt u SQL-code en SQL-contextwaarden in uw PHP-code. Maar het wordt niet achter de schermen opnieuw geschud (behalve met PDO :: EMULATE_PREPARES). Uw database ontvangt de niet-geconfigureerde SQL-opdrachten en 1: 1-variabelewaarden.

Hoewel dit antwoord benadrukt dat u zich moet bekommeren om de leesbaarheid van vallen mysql_. Er is af en toe ook een prestatievoordeel (herhaalde INSERTs met slechts verschillende waarden) vanwege deze zichtbare en technische gegevens / codescheiding.

Pas op dat parameterbinding nog steeds geen magische one-stop oplossing is tegen alle SQL-injecties. Het behandelt het meest voorkomende gebruik voor gegevens / waarden. U kunt kolomnaam / tabel-ID's echter niet op de witte lijst plaatsen, niet helpen bij de constructie van dynamische clausules of gewoon in matrixlijsten.

Hybride PDO-gebruik

Deze pdo_* wrapper-functies maken een codeer-vriendelijke stop-gap-API. (Het is zo ongeveer wat MYSQLI zou kunnen zijn als het niet voor de kenmerkende shift van de handtekening was). Ze leggen ook de echte PDO op de meeste momenten bloot.
Herschrijven hoeft niet te stoppen bij het gebruik van de nieuwe pdo_ functienamen. U kunt één voor één elke pdo_query () omzetten in een eenvoudige $ pdo-> prepare () -> execute () aanroep.

Het is echter het beste om opnieuw te beginnen met vereenvoudigen. Bijvoorbeeld het ophalen van gemeenschappelijke resultaten:

$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {

Kan worden vervangen door slechts een voor elke iteratie:

foreach ($result as $row) {

Of beter nog, een directe en complete array retrieval:

$result->fetchAll();

In de meeste gevallen ontvang je meer nuttige waarschuwingen dan PDO of mysql_ meestal bieden na mislukte query's.

Andere opties

Dus dit heeft hopelijk wat gevisualiseerd praktisch redenen en een waardevol pad om te laten vallen mysql_.

Gewoon overstappen naar  snijdt het niet helemaal. pdo_query() is ook slechts een frontend daarop.

Tenzij u ook parameterbinding introduceert of iets anders uit de mooiere API kunt gebruiken, is het een zinloze switch. Ik hoop dat het eenvoudig genoeg is afgeschilderd om de ontmoediging van nieuwkomers niet te bevorderen. (Onderwijs werkt meestal beter dan verbod.)

Hoewel het in aanmerking komt voor de eenvoudigste-wat-kan-misschien-werkcategorie, is het ook nog steeds erg experimentele code. Ik schreef het gewoon in het weekend. Er is echter een overvloed aan alternatieven. Gewoon googlen voor PHP-database abstractie en blader een beetje. Er zijn en zijn altijd veel uitstekende bibliotheken voor dergelijke taken.

Als u uw database-interactie verder wilt vereenvoudigen, zoals mappers Paris / Idiorm zijn het proberen waard. Net zoals niemand de saaie DOM in JavaScript meer gebruikt, hoef je tegenwoordig niet langer een ruwe database-interface te babysitten.


200
2017-10-12 13:22



De mysql_ functies:

  1. zijn verouderd - ze worden niet meer onderhouden
  2. sta niet toe dat u gemakkelijk naar een andere database-backend kunt gaan
  3. ondersteun daarom geen voorbereide uitspraken
  4. moedig programmeurs aan om samenvoeging te gebruiken om query's te maken, wat leidt tot kwetsbaarheden in SQL-injecties

134
2018-01-01 17:42



Over dat gesproken technisch redenen zijn er slechts enkele, zeer specifieke en zelden gebruikt. Hoogstwaarschijnlijk zul je ze nooit in je leven gebruiken.
Misschien ben ik te onwetend, maar ik heb nooit de gelegenheid gehad om dingen als deze te gebruiken

  • niet-blokkerende, asynchrone zoekopdrachten
  • opgeslagen procedures die meerdere resultatens retourneren
  • Encryptie (SSL)
  • samendrukking

Als je ze nodig hebt - dit zijn ongetwijfeld technische redenen om je af te wenden van MySQL-uitbreiding naar iets dat meer stijlvol en modern oogt.

Niettemin zijn er ook enkele niet-technische problemen, die uw ervaring een beetje moeilijker kunnen maken

  • verder gebruik van deze functies met moderne PHP-versies zal meldingen van verouderde niveaus oproepen. Ze kunnen gewoon worden uitgeschakeld.
  • in een verre toekomst kunnen ze mogelijk worden verwijderd uit de standaard PHP-build. Ook niet erg, want mydsql ext wordt verplaatst naar PECL en elke host zal er met plezier PHP mee compileren, omdat ze geen klanten willen wiens sites al decennia werken.
  • sterke weerstand van de Stackoverflow-community. Еverytime noem je deze eerlijke functies, je wordt verteld dat ze onder strikt taboe staan.
  • omdat het een gemiddelde PHP-gebruiker is, is uw idee om deze functies te gebruiken hoogstwaarschijnlijk foutgevoelig en fout. Alleen al vanwege al deze talloze tutorials en handleidingen die je op de verkeerde manier leren. Niet de functies zelf - ik moet het benadrukken - maar de manier waarop ze worden gebruikt.

Dit laatste probleem is een probleem.
Maar naar mijn mening is de voorgestelde oplossing ook niet beter.
Lijkt mij te idealistisch een droom dat al die PHP-gebruikers leren om SQL-query's in één keer goed af te handelen. Hoogst waarschijnlijk veranderen ze mysql_ * in mysqli_ * mechanisch, de benadering hetzelfde laten. Vooral omdat mysqli voorbereide statements maakt gebruik ongelooflijk pijnlijk en lastig.
Om nog maar te zwijgen inheems voorbereide verklaringen zijn niet genoeg om te beschermen van SQL-injecties, en noch mysqli noch PDO biedt een oplossing.

Dus in plaats van deze eerlijke extensie te bestrijden, zou ik liever verkeerde praktijken bevechten en mensen op de juiste manier opvoeden.

Er zijn ook enkele valse of niet-belangrijke redenen, zoals

  • Ondersteunt geen opgeslagen procedures (we gebruikten mysql_query("CALL my_proc"); voor leeftijden)
  • Ondersteunt geen transacties (dezelfde als hierboven)
  • Ondersteunt geen meerdere verklaringen (wie heeft ze nodig?)
  • Niet onder actieve ontwikkeling (dus wat? Heeft het effect u op een praktische manier?)
  • Gebrek aan een OO-interface (één aanmaken is een kwestie van meerdere uren)
  • Ondersteunt Prepared Statements of Parametrized Queries niet

De laatste is een interessant punt. Hoewel mysql ext geen ondersteuning biedt inheems voorbereide verklaringen, ze zijn niet vereist voor de veiligheid. We kunnen voorbereide instructies gemakkelijk nabootsen met behulp van plaatsaanduidingen die handmatig zijn verwerkt (net als bij PDO):

function paraQuery()
{
    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = mysql_real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = mysql_query($query);
    if (!$result)
    {
        throw new Exception(mysql_error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

voila, alles is ingesteld en veilig.

Maar goed, als het rode vak in de handleiding je niet bevalt, ontstaat er een keuzeprobleem: mysqli of PDO?

Welnu, het antwoord zou als volgt zijn:

  • Als u de noodzaak begrijpt van het gebruik van een database abstractielaag en op zoek naar een API om er een te maken, mysqli is een zeer goede keuze, omdat het inderdaad veel mysql-specifieke functies ondersteunt.
  • Als, net als de meerderheid van de PHP-mensen, je ruwe API-aanroepen gebruikt in de toepassingscode (wat in essentie een verkeerde praktijk is) - BOB is de enige keuze, omdat deze extensie pretendeert niet alleen API te zijn, maar eerder een semi-DAL, nog steeds onvolledig maar biedt veel belangrijke functies, met twee maakt PDO zich kritisch onderscheiden van mysqli:

    • in tegenstelling tot mysqli kan PDO plaatshouders binden op waarde, waardoor dynamisch opgebouwde query's mogelijk zijn zonder verschillende schermen met nogal rommelige code.
    • in tegenstelling tot mysqli, kan PDO het queryresultaat altijd retourneren in een eenvoudige gebruikelijke array, terwijl mysqli dit alleen op mysqlnd-installaties kan doen.

Dus, als u een gemiddelde PHP-gebruiker bent en uzelf een hoop hoofdpijn wilt besparen bij het gebruik van native voorbereide instructies, is PDO - nogmaals - de enige keuze.
PDO is echter ook geen zilveren kogel en heeft zijn ontberingen.
Dus ik schreef oplossingen voor alle gebruikelijke valkuilen en complexe gevallen in de PDO-tag wiki

Niettemin heeft iedereen het over extensies die altijd de 2 belangrijke feiten over Mysqli en PDO:

  1. Voorbereide verklaring is geen zilveren kogel. Er zijn dynamische identificatiegegevens die niet kunnen worden gebonden met behulp van voorbereide instructies. Er zijn dynamische query's met een onbekend aantal parameters, waardoor query-opbouw een moeilijke taak is.

  2. Noch mysqli_ * noch PDO-functies hadden in de applicatiecode moeten voorkomen.
    Er zou een moeten zijn abstractielaag tussen hen en de applicatiecode, die alle vuile taken van binding, looping, foutafhandeling enz. binnenin zal uitvoeren, waardoor applicatiecode DRY en clean wordt. Vooral voor de complexe zaken zoals dynamische query-opbouw.

Dus, gewoon overschakelen naar PDO of mysqli is niet genoeg. Men moet een ORM of een querybuilder gebruiken, of welke database-abstractieklasse dan in plaats daarvan Raw-API-functies in hun code aanroepen.
En andersom - als je een abstractielaag hebt tussen je applicatiecode en mysql API - het maakt eigenlijk niet uit welke motor wordt gebruikt. U kunt mysql ext gebruiken totdat het verouderd is en vervolgens uw abstractieklasse gemakkelijk herschrijven naar een andere engine, alle applicatiecode intact hebben.

Hier zijn enkele voorbeelden op basis van mijn safemysql-klasse om te laten zien hoe een dergelijke abstractieklasse zou moeten zijn:

$city_ids = array(1,2,3);
$cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

Vergelijk deze ene enkele regel met hoeveelheid code die je nodig hebt met PDO.
Vergelijk dan met gekke hoeveelheid code je hebt dit nodig met onbewerkte voorbereidingen van Mysqli. Houd er rekening mee dat foutafhandeling, profilering en logboekregistratie al zijn ingebouwd en actief zijn.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

Vergelijk het met de gebruikelijke PDO-invoegingen, waarbij elke veldnaam zes tot tien keer wordt herhaald - in al deze talrijke benoemde plaatsaanduidingen, bindingen en querydefinities.

Een ander voorbeeld:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

Je kunt nauwelijks een voorbeeld vinden voor een BOB om zo'n praktisch geval te behandelen.
En het zal te omslachtig en waarschijnlijk onveilig zijn.

Dus, nogmaals, het is niet alleen een ruwe driver, maar een abstractieklasse, niet alleen handig voor domme voorbeelden uit de beginnershandleiding, maar ook voor het oplossen van alle echte problemen.


98
2017-10-12 13:23



Er zijn veel redenen, maar misschien wel het belangrijkste is dat deze functies onzekere programmeermethoden aanmoedigen omdat ze geen voorbereide uitspraken ondersteunen. Voorbereide instructies helpen bij het voorkomen van SQL-injectieaanvallen.

Tijdens gebruik mysql_* functies, moet u onthouden om door de gebruiker geleverde parameters door te voeren mysql_real_escape_string(). Als je het op één plek vergeet of als je toevallig slechts een deel van de invoer mist, kan je database worden aangevallen.

Voorbereidende verklaringen gebruiken in PDO of mysqli zal ervoor zorgen dat dit soort programmeerfouten moeilijker te maken zijn.


87
2017-10-12 13:24



Omdat (onder andere) het veel moeilijker is om ervoor te zorgen dat de invoergegevens worden opgeschoond. Als u geparameteriseerde zoekopdrachten gebruikt, zoals met PDO of mysqli, kunt u het risico volledig vermijden.

Als voorbeeld kan iemand gebruiken "enhzflep); drop table users" als een gebruikersnaam. Met de oude functies kunnen meerdere instructies per query worden uitgevoerd, dus zoiets als die vervelende bugger kan een hele tabel verwijderen.

Als iemand PDO van mysqli zou gebruiken, zou de gebruikersnaam uiteindelijk zijn "enhzflep); drop table users".

Zien bobby-tables.com.


70
2017-09-18 12:28