Van tijd tot tijd zie ik vragen over het verbinden met de database.
De meeste antwoorden zijn niet zoals ik het doe, of ik krijg de antwoorden misschien niet goed. In ieder geval; Ik heb er nooit over nagedacht omdat de manier waarop ik het doe voor mij werkt.
Maar hier is een gekke gedachte; Misschien doe ik dit helemaal verkeerd, en als dat het geval is; Ik zou heel graag willen weten hoe ik op de juiste manier verbinding kan maken met een MySQL-database met behulp van PHP en PDO en deze gemakkelijk toegankelijk kan maken.
Dit is hoe ik het doe:
Ten eerste, hier is mijn bestandsstructuur (uitgekleed):
public_html/
* index.php
* initialize/
-- load.initialize.php
-- configure.php
-- sessions.php
index.php
Helemaal bovenaan heb ik require('initialize/load.initialize.php');
.
load.initialize.php
# site configurations
require('configure.php');
# connect to database
require('root/somewhere/connect.php'); // this file is placed outside of public_html for better security.
# include classes
foreach (glob('assets/classes/*.class.php') as $class_filename){
include($class_filename);
}
# include functions
foreach (glob('assets/functions/*.func.php') as $func_filename){
include($func_filename);
}
# handle sessions
require('sessions.php');
Ik weet dat er een betere of correctere manier is om klassen op te nemen, maar ik kan me niet herinneren wat het was. Ik heb nog geen tijd gehad om ernaar te kijken, maar ik denk dat het iets was met autoload
. zoiets…
configure.php
Hier overschrijf ik eigenlijk gewoon enkele php.ini-eigenschappen en doe ik een andere globale configuratie voor de site
connect.php
Ik heb de verbinding met een klas gemaakt zodat andere klassen deze kunnen verlengen…
class connect_pdo
{
protected $dbh;
public function __construct()
{
try {
$db_host = ' '; // hostname
$db_name = ' '; // databasename
$db_user = ' '; // username
$user_pw = ' '; // password
$con = new PDO('mysql:host='.$db_host.'; dbname='.$db_name, $db_user, $user_pw);
$con->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$con->exec("SET CHARACTER SET utf8"); // return all sql requests as UTF-8
}
catch (PDOException $err) {
echo "harmless error message if the connection fails";
$err->getMessage() . "<br/>";
file_put_contents('PDOErrors.txt',$err, FILE_APPEND); // write some details to an error-log outside public_html
die(); // terminate connection
}
}
public function dbh()
{
return $this->dbh;
}
}
# put database handler into a var for easier access
$con = new connect_pdo();
$con = $con->dbh();
//
Hier geloof ik dat er ruimte is voor enorme verbetering sinds ik onlangs ben begonnen met het leren van OOP en het gebruik van PDO in plaats van mysql.
Dus ik heb net een paar tutorials voor beginners gevolgd en verschillende dingen uitgeprobeerd…
sessions.php
Naast het afhandelen van reguliere sessies, initialiseer ik ook sommige lessen in een sessie zoals deze:
if (!isset($_SESSION['sqlQuery'])){
session_start();
$_SESSION['sqlQuery'] = new sqlQuery();
}
Op deze manier is deze les overal beschikbaar. Dit is misschien geen goede gewoonte(?)…
Hoe dan ook, dit is wat ik met deze aanpak overal kan doen:
echo $_SESSION['sqlQuery']->getAreaName('county',9); // outputs: Aust-Agder (the county name with that id in the database)
In mijn sqlQuery
–klasse, die extends
mijn connect_pdo
–klasse, ik heb een openbare functie genaamd getAreaName
die het verzoek naar mijn database afhandelt.
Best netjes denk ik.
Werkt als een tierelier
Dus dat is eigenlijk hoe ik het doe.
Ook, wanneer ik iets uit mijn DB moet ophalen van niet binnen een klas, doe ik gewoon iets soortgelijks als dit:
$id = 123;
$sql = 'SELECT whatever FROM MyTable WHERE id = :id';
$qry = $con->prepare($sql);
$qry -> bindParam(':id', $id, PDO::PARAM_INT);
$qry -> execute();
$get = $qry->fetch(PDO::FETCH_ASSOC);
Sinds ik de verbinding in een variabele in connect_pdo.phpheb gezet, hoef ik er alleen maar naar te verwijzen en ik ben klaar om te gaan. Het werkt. Ik krijg mijn verwachte resultaten…
Maar ongeacht dat; Ik zou het erg op prijs stellen als jullie me kunnen vertellen of ik er ver naast zit. Wat ik in plaats daarvan zou moeten doen, gebieden die ik zou kunnen of moeten veranderen voor verbetering enz…
Ik ben leergierig…
Antwoord 1, autoriteit 100%
Het doel
Zoals ik het zie, is je doel in dit geval tweeledig:
- een enkele/herbruikbare verbinding per database maken en onderhouden
- zorg ervoor dat de verbinding correct is ingesteld
Oplossing
Ik zou aanraden om zowel de anonieme functie als het fabriekspatroon te gebruiken voor het omgaan met PDO-verbindingen. Het gebruik ervan ziet er als volgt uit:
$provider = function()
{
$instance = new PDO('mysql:......;charset=utf8', 'username', 'password');
$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$instance->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
return $instance;
};
$factory = new StructureFactory( $provider );
Vervolgens in een ander bestand of lager in hetzelfde bestand:
$something = $factory->create('Something');
$foobar = $factory->create('Foobar');
De fabriek zelf zou er ongeveer zo uit moeten zien:
class StructureFactory
{
protected $provider = null;
protected $connection = null;
public function __construct( callable $provider )
{
$this->provider = $provider;
}
public function create( $name)
{
if ( $this->connection === null )
{
$this->connection = call_user_func( $this->provider );
}
return new $name( $this->connection );
}
}
Op deze manier zou je een gecentraliseerde structuur hebben, die ervoor zorgt dat er alleen verbinding wordt gemaakt als dat nodig is. Het zou ook het proces van unit-testing en onderhoud veel gemakkelijker maken.
De provider zou in dit geval ergens in de bootstrap-fase te vinden zijn. Deze benadering zou ook een duidelijke locatie geven waar de configuratie moet worden gedefinieerd, die u gebruikt om verbinding te maken met de database.
Houd er rekening mee dat dit een uiterst vereenvoudigd voorbeeldis. U kunt ook profiteren van het bekijken van twee volgende video’s:
Ik zou ook sterk aanbevelen om een goede tutorial te lezen over het gebruik van PDO (er is een logboek met slechte tutorials online).
Antwoord 2, autoriteit 23%
Ik raad aan om $_SESSION
niet te gebruiken om wereldwijd toegang te krijgen tot uw DB-verbinding.
U kunt een van de volgende dingen doen (in volgorde van slechtste naar bestepraktijken):
- Toegang tot
$dbh
metglobal $dbh
binnen uw functies en klassen -
Gebruik een singleton-register en open dat wereldwijd, zoals:
$registry = MyRegistry::getInstance(); $dbh = $registry->getDbh();
-
Injecteer de database-handler in de klassen die het nodig hebben, zoals:
class MyClass { public function __construct($dbh) { /* ... */ } }
Ik zou de laatste ten zeerste aanbevelen. Het staat bekend als afhankelijkheidsinjectie (DI), inversion of control (IoC), of gewoon het Hollywood-principe (bel ons niet, we bellen u).
Het is echter iets geavanceerder en vereist meer “bedrading” zonder kader. Dus als afhankelijkheidsinjectie te ingewikkeld voor je is, gebruik dan een singleton-register in plaats van een heleboel globale variabelen.
Antwoord 3, autoriteit 7%
Ik kwam onlangs zelf tot een soortgelijk antwoord/vraag. Dit is wat ik deed, voor het geval iemand geïnteresseerd is:
<?php
namespace Library;
// Wrapper for \PDO. It only creates the rather expensive instance when needed.
// Use it exactly as you'd use the normal PDO object, except for the creation.
// In that case simply do "new \Library\PDO($args);" with the normal args
class PDO
{
// The actual instance of PDO
private $db;
public function __construct() {
$this->args = func_get_args();
}
public function __call($method, $args)
{
if (empty($this->db))
{
$Ref = new \ReflectionClass('\PDO');
$this->db = $Ref->newInstanceArgs($this->args);
}
return call_user_func_array(array($this->db, $method), $args);
}
}
Om het aan te roepen hoeft u alleen deze regel aan te passen:
$DB = new \Library\PDO(/* normal arguments */);
En de type-hint als je het gebruikt om (\Bibliotheek\PDO $DB).
Het lijkt erg op zowel het geaccepteerde antwoord als het jouwe; het heeft echter een opmerkelijk voordeel. Overweeg deze code:
$DB = new \Library\PDO( /* args */ );
$STH = $DB->prepare("SELECT * FROM users WHERE user = ?");
$STH->execute(array(25));
$User = $STH->fetch();
Hoewel het er misschien uitziet als een normale PDO (het verandert alleen door die \Library\
), initialiseert het het object pas als u de eerste methode aanroept, welke dat ook is. Dat maakt het meer geoptimaliseerd, omdat het maken van een PDO-object enigszins duur is. Het is een transparante klasse, of wat het een Ghostwordt genoemd, een vorm van Lazy Loading. U kunt de $DB behandelen als een normale PDO-instantie, deze doorgeven, dezelfde bewerkingen uitvoeren, enz.
Antwoord 4
$dsn = 'mysql:host=your_host_name;dbname=your_db_name_here'; // define host name and database name
$username = 'you'; // define the username
$pwd='your_password'; // password
try {
$db = new PDO($dsn, $username, $pwd);
}
catch (PDOException $e) {
$error_message = $e->getMessage();
echo "this is displayed because an error was found";
exit();
}
Antwoord 5
Er zijn een paar basisfouten in uw configuratie:
- Het bestand
configure.php
mag niet in de documenthoofdmap van de webserver staan - als de server ooit verkeerd is geconfigureerd, kunt u inloggegevens openbaar maken. Je denkt misschien dat het jou niet zal overkomen, maar het is een risico dat je gewoon niet hoeft te nemen. Lessen zouden er ook niet moeten zijn… dit is niet zo belangrijk, maar toch zou alles wat niet openbaar hoeft te zijn niet openbaar moeten zijn. - Tenzij je met een bijzonder groot project werkt, zou je geen map “initialisatie” moeten hebben. Het laden van één groot bestand is ongeveer 10x sneller dan het laden van tien kleine bestanden met dezelfde inhoud. Dit kan behoorlijk oplopen naarmate een project groeit en kan PHP-sites echt vertragen.
- Probeer dingen niet te laden, tenzij het echt nodig is. Maak bijvoorbeeld geen verbinding met PDO, tenzij dat echt nodig is. Niet
session_start()
u daadwerkelijk leest/schrijft naar een sessie. Voeg geen klassedefinitiebestand toe, tenzij u een instantie van de klasse maakt. Er zijn grenzen aan het aantal verbindingen dat u kunt hebben. En API’s, zoals sessies, stellen “vergrendelingen” in die de uitvoering van code kunnen pauzeren voor andere mensen die dezelfde bron gebruiken. - Voor zover ik weet, gebruik je Composer niet. Je zou het moeten gebruiken – het zal het leven zoveel gemakkelijker maken, zowel voor je eigen code als voor afhankelijkheden van derden.
Hier is mijn voorgestelde directorystructuur, die vergelijkbaar is met wat ik gebruik voor middelgrote projecten:
init.php Replaces public_html/initialize. Your PDO connection details
are held here.
classes/ Replaces public_html/classes
vendor/autoload.php Your class autoload script generated using the
industry standard Composer command line tool
composer.json The file where you describe how autoload.php
operates among other things. For example if you
don't use namespaces (maybe you should) it might be:
{"autoload": {"psr-4": { "": "classes/" }}}
public_html/index.php Your landing page
public_html/other.php Some other page
public_html/css/foobar.css ...and so on for all static resources
Het bestand init.php
kan er ongeveer zo uitzien:
date_default_timezone_set('Etc/UTC');
require 'vendor/autoload.php';
$pdoConnect = function() {
static $pdo = false;
if (!$pdo) {
$pdo = new PDO('mysql:dbname=db;host=localhost', 'user', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
}
return $pdo;
};
// similar anonymous functions for session_start(), etc.
index.php
kan er als volgt uitzien:
require '../init.php';
$pdo = $pdoConnect();
// go from there
other.php
kan vergelijkbaar zijn, maar misschien maakt het geen verbinding met de database en voert het daarom $pdoConnect niet uit.
Je moet zoveel mogelijk het grootste deel van je code in de klassendirectory schrijven. Houd index.php
, other.php
, enz. zo kort en krachtig mogelijk.