97 votes

Comment configurer correctement une connexion PDO

De temps en temps, je vois des questions concernant la connexion à la base de données.
La plupart des réponses ne sont pas la façon dont je le fais, ou je pourrais simplement ne pas obtenir les réponses correctement. Quoi qu'il en soit, je n'y ai jamais pensé parce que la façon dont je le fais fonctionne pour moi.

Mais voici une idée folle : peut-être que je m'y prends mal, et si c'est le cas, j'aimerais vraiment savoir comment se connecter correctement à une base de données MySQL en utilisant PHP et PDO et la rendre facilement accessible.

Voici comment je m'y prends :

Tout d'abord, voici la structure de mon fichier (dépouillé) :

public_html/

* index.php  

* initialize/  
  -- load.initialize.php  
  -- configure.php  
  -- sessions.php   

index.php
Tout en haut, j'ai 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');

Je sais qu'il y a une meilleure façon, ou une façon plus correcte, d'inclure les classes, mais je ne me souviens pas de ce que c'était. Je n'ai pas encore eu le temps d'y jeter un coup d'œil, mais je pense que c'était quelque chose comme autoload . quelque chose comme ça...

configure.php
Ici, je ne fais que remplacer des php.ini -et effectuer d'autres configurations globales pour le site.

connecter.php
J'ai mis la connexion sur une classe pour que les autres classes puissent étend celui-là...

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();
//

Je pense qu'il est possible de s'améliorer considérablement dans ce domaine, car j'ai récemment commencé à apprendre la programmation orientée objet et à utiliser PDO au lieu de mysql.
J'ai donc suivi quelques tutoriels pour débutants et essayé différents trucs...

sessions.php
En plus de gérer les sessions régulières, j'initialise également certaines classes dans une session comme ceci :

if (!isset($_SESSION['sqlQuery'])){
    session_start();
    $_SESSION['sqlQuery'] = new sqlQuery();
}

De cette façon, cette classe est disponible partout. Ce n'est peut-être pas une bonne pratique ( ?)...
Bref, voilà ce que cette approche me permet de faire de partout :

echo $_SESSION['sqlQuery']->getAreaName('county',9);  // outputs: Aust-Agder (the county name with that id in the database)

Dans mon sqlQuery - classe qui extends mon connect_pdo - classe J'ai une fonction publique appelée getAreaName qui gère la requête vers ma base de données.
Plutôt chouette, je trouve.

Fonctionne comme un charme
Donc c'est en gros la façon dont je le fais.
De plus, lorsque j'ai besoin d'extraire quelque chose de ma base de données à partir d'une classe, je fais quelque chose de similaire à ceci :

$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);

Puisque j'ai mis la connexion dans une variable à l'intérieur connect_pdo.php j'ai juste à m'y référer et je suis prêt à partir. Cela fonctionne. J'obtiens les résultats attendus...

Mais indépendamment de cela, j'apprécierais vraiment si vous pouviez me dire si je suis à côté de la plaque. Ce que je devrais faire à la place, les domaines que je pourrais ou devrais changer pour m'améliorer, etc...

Je suis impatient d'apprendre...

9 votes

Vous devriez utiliser un autoloader au lieu d'inclure chaque fichier unique dans votre demande immédiatement.

4 votes

Cette question est probablement mieux sur Examen du code

107voto

tereško Points 32847

L'objectif

Comme je le vois, votre objectif dans ce cas est double :

  • créer et maintenir une connexion unique/réutilisable par base de données
  • s'assurer que la connexion a été correctement établie

Solution

Je recommande d'utiliser à la fois la fonction anonyme et le modèle d'usine pour traiter la connexion PDO. L'utilisation de ce modèle ressemble à ceci :

$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 );

Puis dans un fichier différent ou plus bas dans le même fichier :

$something = $factory->create('Something');
$foobar = $factory->create('Foobar');

L'usine elle-même devrait ressembler à quelque chose comme ceci :

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 );
    }

}

Cette méthode vous permet de disposer d'une structure centralisée, qui garantit que la connexion n'est créée que lorsque cela est nécessaire. Elle facilite également le processus de test unitaire et de maintenance.

Dans ce cas, le fournisseur se trouverait quelque part au stade de l'amorçage. Cette approche donne également un emplacement clair où définir la configuration, que vous utilisez pour vous connecter à la base de données.

Gardez à l'esprit qu'il s'agit d'une exemple extrêmement simplif simplifié . Vous pourriez également tirer profit des deux vidéos suivantes :

En outre, je vous recommande vivement de lire un véritable tutoriel sur l'utilisation de PDO (il y a beaucoup de mauvais tutoriels en ligne).

0 votes

Je vais me pencher sur la question. Je suis assez novice en matière de POO et de PDO, donc il y a beaucoup de choses à étudier... Des liens comme celui-ci sont très utiles pour moi...

3 votes

Puisque même PHP5.3 est proche de l'EOL. La majorité des sites avec des versions PHP dépassées ne sont en fait que des hébergements bon marché pour Wordpress. L'impact des environnements pré-5.3 sur le développement professionnel (ceux qui bénéficieraient d'extraits comme celui-ci) est négligeable, à mon avis.

0 votes

-1 parce que cette réponse est plus ou moins la même que celle de Ian (il l'a proposée en premier).

23voto

Ian Unruh Points 123

Je suggère de ne pas utiliser $_SESSION pour accéder à votre connexion à la base de données de manière globale.

Vous pouvez faire l'une des choses suivantes (par ordre d'importance) du pire au meilleur pratiques) :

  • Accès $dbh en utilisant global $dbh à l'intérieur de vos fonctions et classes

  • Utilisez un registre singleton, et accédez-y globalement, comme ceci :

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
  • Injectez le gestionnaire de base de données dans les classes qui en ont besoin, comme ceci :

    class MyClass {
        public function __construct($dbh) { /* ... */ }
    }

Je recommande vivement la dernière. Elle est connue sous le nom d'injection de dépendances (DI), d'inversion de contrôle (IoC), ou tout simplement de principe hollywoodien (ne nous appelez pas, nous vous appellerons).

Cependant, elle est un peu plus avancée et nécessite plus de "câblage" sans framework. Donc, si l'injection de dépendances est trop compliquée pour vous, utilisez un registre singleton au lieu d'un tas de variables globales.

0 votes

Ainsi, j'accède à ma connexion à la base de données de manière globale lorsque je configure mon fichier sqlQuery -dans la session puisqu'elle étend la classe connect_pdo ?

7voto

Je suis récemment arrivé à une réponse/question similaire par moi-même. Voici ce que j'ai fait, au cas où cela intéresserait quelqu'un :

<?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);
    }
  }

Pour l'appeler, il suffit de modifier cette ligne :

$DB = new \Library\PDO(/* normal arguments */);

Et l'indication du type si vous l'utilisez pour ( \Library\PDO $DB).

Elle est vraiment similaire à la réponse acceptée et à la vôtre, mais elle présente un avantage notable. Considérez ce code :

$DB = new \Library\PDO( /* args */ );

$STH = $DB->prepare("SELECT * FROM users WHERE user = ?");
$STH->execute(array(25));
$User = $STH->fetch();

Bien qu'il puisse ressembler à un PDO normal (il change par que \Library\ seulement), il n'initialise en fait pas l'objet avant que vous n'appeliez la première méthode, quelle qu'elle soit. Cela la rend plus optimisée, puisque la création de l'objet PDO est légèrement coûteuse. C'est une classe transparente, ou ce que l'on appelle une Fantôme une forme de Chargement paresseux . Vous pouvez traiter la $DB comme une instance PDO normale, la faire circuler, effectuer les mêmes opérations, etc.

0 votes

C'est ce qu'on appelle le "motif décorateur".

2voto

Abhi Beckert Points 10768

Il y a quelques défauts de base dans votre installation :

  1. El configure.php ne devrait pas se trouver dans le répertoire des documents du serveur web - si le serveur est mal configuré, vous risquez d'exposer les informations d'identification au public. Vous pouvez penser que cela ne vous arrivera pas, mais c'est un risque que vous n'avez pas besoin de prendre. Les classes ne devraient pas non plus être présentes... ce n'est pas aussi important, mais tout ce qui n'a pas besoin d'être public ne devrait pas l'être.
  2. À moins que vous ne travailliez sur un projet particulièrement important, vous ne devriez pas avoir de répertoire "d'initialisation". Le chargement d'un gros fichier est environ 10 fois plus rapide que le chargement de dix petits fichiers ayant le même contenu. Cela a tendance à s'accumuler au fur et à mesure qu'un projet se développe et peut vraiment ralentir les sites PHP.
  3. Essayez de ne pas charger des choses à moins que vous n'en ayez réellement besoin. Par exemple, ne vous connectez pas avec PDO à moins que vous n'en ayez réellement besoin. N'utilisez pas session_start() vous lisez/écrivez réellement dans la session. N'incluez pas un fichier de définition de classe à moins que vous ne créiez une instance de la classe. Il y a des limites au nombre de connexions que vous pouvez avoir. Et les API comme session établissent des "verrous" qui peuvent interrompre l'exécution du code pour d'autres personnes utilisant la même ressource.
  4. D'après ce que je sais, vous n'utilisez pas Composer. Vous devriez l'utiliser - il vous facilitera grandement la vie, tant pour votre propre code que pour les dépendances de tiers.

Voici la structure de répertoire que je propose, qui est similaire à celle que j'utilise pour les projets de taille moyenne :

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

El init.php pourrait ressembler à quelque chose comme :

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 pourrait ressembler :

require '../init.php';

$pdo = $pdoConnect();

// go from there

other.php pourrait être similaire mais peut-être qu'il ne se connecte pas à la base de données, donc n'exécute pas $pdoConnect.

Dans la mesure du possible, vous devriez écrire la majeure partie de votre code dans le répertoire classes. Gardez index.php , other.php etc aussi bref que possible.

0voto

hi-code Points 1
$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();
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X