PDO 연결을 올바르게 설정하는 방법


92

때때로 데이터베이스 연결에 관한 질문을 봅니다.
대부분의 대답은 내가하는 방식이 아니거나 정확하게 대답을 얻지 못할 수도 있습니다. 어쨌든; 내가하는 방식이 나를 위해 일하기 때문에 나는 그것에 대해 생각해 본 적이 없습니다.

그러나 여기에 미친 생각이 있습니다. 아마도 내가이 모든 일을 잘못하고있을 수도 있고, 만약 그렇다면; PHP와 PDO를 사용하여 MySQL 데이터베이스에 올바르게 연결하고 쉽게 액세스 할 수 있도록하는 방법을 알고 싶습니다.

내가하는 방법은 다음과 같습니다.

첫째, 여기 내 파일 구조이다 (옷을 벗었) :

public_html/

* index.php  

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

index.php
맨 위에는 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');

수업을 포함하는 더 낫거나 더 정확한 방법이 있다는 것을 알고 있지만 그것이 무엇인지 기억할 수 없습니다. 아직 조사 할 시간이 없었지만 autoload. 그런 ...

configure.php
여기서는 기본적으로 일부 php.ini 속성을 재정의 하고 사이트에 대한 다른 전역 구성을 수행합니다.

connect.php
다른 클래스가 이것을 확장 할 수 있도록 클래스에 연결을 설정했습니다 .

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

최근에 OOP를 배우고 mysql 대신 PDO를 사용하기 시작한 이후로 엄청난 개선의 여지가 있다고 생각합니다.
그래서 저는 몇 가지 초보자 튜토리얼을 따라했고 다른 것들을 시도했습니다 ...

sessions.php
일반 세션을 처리하는 것 외에도 다음과 같이 일부 클래스를 세션으로 초기화합니다.

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

이런 식으로이 수업은 어디서나 사용할 수 있습니다. 이것은 좋은 습관이 아닐 수도 있습니다 (?) ...
어쨌든, 이것이이 접근 방식을 통해 어디에서나 할 수 있습니다.

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

내 내부 sqlQuery- 클래스 , extendsconnect_pdo- 클래스 , I라는 공용 함수가 getAreaName내 데이터베이스에 대한 요청을 처리합니다.
꽤 깔끔한 것 같아요.

매력처럼 작동
합니다. 그래서 기본적으로 제가하는 방식입니다.
또한 클래스 내에서 DB에서 무언가를 가져와야 할 때마다 다음과 비슷한 작업을 수행합니다.

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

connect_pdo.php 내부의 변수에 연결을 넣었으므로 참조 만했고 갈 수 있습니다. 효과가있다. 예상되는 결과를 얻습니다 ...

그러나 그것에 관계없이; 내가 여기서 멀리 떨어져 있는지 말해 주실 수 있다면 정말 감사하겠습니다. 대신해야 할 일, 개선을 위해 변경할 수 있거나 변경해야하는 영역 등 ...

배우고 싶어요 ...


9
애플리케이션의 모든 단일 파일 을 한 번 에 포함하는 대신 오토로더를 사용해야합니다 .
Lusitanian

4
이 질문은 아마도 Code Review
Madara 's Ghost

답변:


105

목표

내가보기에이 경우의 목표는 두 가지입니다.

  • 데이터베이스 당 단일 / 재사용 가능한 연결 생성 및 유지
  • 연결이 제대로 설정되었는지 확인

해결책

PDO 연결을 처리하기 위해 익명 기능과 공장 패턴을 모두 사용하는 것이 좋습니다. 사용 방법은 다음과 같습니다.

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

그런 다음 다른 파일 또는 동일한 파일의 하위 :

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

공장 자체는 다음과 같아야합니다.

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

}

이렇게하면 중앙 집중식 구조를 갖게되어 필요할 때만 연결이 만들어집니다. 또한 단위 테스트 및 유지 관리 프로세스가 훨씬 쉬워집니다.

이 경우 공급자는 부트 스트랩 단계 어딘가에서 찾을 수 있습니다. 이 접근 방식은 또한 DB에 연결하는 데 사용하는 구성을 정의 할 명확한 위치를 제공합니다.

이것은 매우 단순화 된 예 입니다. 다음 두 동영상을 시청하면 도움이 될 수도 있습니다.

또한 PDO 사용에 대한 적절한 자습서를 읽을 것을 강력히 권장합니다 (온라인에는 나쁜 자습서 로그가 있습니다).


3
PHP5.3조차도 EOL에 접근하고 있기 때문에. 오래된 PHP 버전을 사용하는 대부분의 사이트는 실제로 Wordpress를위한 저렴한 호스팅입니다. 5.3 이전 환경이 전문성 개발에 미치는 영향 (이런 스 니펫을 통해 혜택을받을 수 있음)은 내 추정치로 무시할 수 있습니다.
tereško 2013 년

5
@thelolcat 동의합니다. 그것은 이다 더 많거나 적은 같은 대답. 그것은 당신이 그것이 완전히 다르다는 사실을 보지 못하는 경우입니다.
PeeHaa 2013

1
@thelolcat이면 종속성 주입 이 무엇인지 알아야합니다 . 계속해서 자신을 부끄럽게하는 대신. 재미있게도 위 포스트의 두 번째 비디오 ( "Do n't Look For Things" )는 실제로 DI가 무엇인지, 어떻게 사용하는지 설명했습니다.하지만 당연히 당신은 그런 사소한 일들에 대해 너무 발전했습니다.
tereško 2013

2
이것은 오래된 답변이지만 좋은 답변입니다-그리고 마지막에 Mysql pdo에 대한 훌륭한 링크
Strawberry

1
@teecee 먼저 PDO 사용법을 배워야합니다. 이 튜토리얼을 추천합니다 : wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developersmysql_* . PDO 로 마이그레이션하려는 사람들을 위해 정확하게 만들어 졌기 때문 입니다. 그런 다음 다시 돌아와서이 솔루션을 볼 수 있습니다.이 솔루션은 이미 PDO를 사용하고 있지만 여러 클래스간에 DB 연결을 공유하는 방법이 필요한 솔루션입니다.
tereško

24

$_SESSION전 세계적으로 DB 연결에 액세스 하는 데 사용하지 않는 것이 좋습니다 .

몇 가지 작업 중 하나를 수행 할 수 있습니다 ( 최악에서 모범 사례 순으로 ).

  • 함수 및 클래스 내부를 $dbh사용하여 액세스global $dbh
  • 단일 레지스트리를 사용하고 다음과 같이 전역 적으로 액세스합니다.

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
  • 다음과 같이 데이터베이스 핸들러를 필요한 클래스에 삽입합니다.

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

나는 마지막 것을 적극 추천합니다. DI (의존성 주입), IoC (Inversion of Control) 또는 단순히 Hollywood 원칙 (전화하지 마십시오. 전화하겠습니다)으로 알려져 있습니다.

그러나 조금 더 발전된 것이며 프레임 워크없이 더 많은 "배선"이 필요합니다. 따라서 종속성 주입이 너무 복잡하다면 여러 전역 변수 대신 단일 레지스트리를 사용하십시오.


그래서 내 sqlQuery-class를 세션으로 설정할 때 내 db 연결에 전역 적으로 액세스 connect_pdo합니까?
ThomasK

7

최근 저도 비슷한 답변 / 질문을 받았습니다. 누군가가 관심이있는 경우를 대비하여 이것이 내가 한 일입니다.

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

이를 호출하려면 다음 라인 만 수정하면됩니다.

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

그리고 (\ Library \ PDO $ DB)에 사용하는 경우 유형 힌트.

받아 들여진 대답과 당신의 대답과 정말 비슷합니다. 그러나 그것은 주목할만한 이점이 있습니다. 이 코드를 고려하십시오.

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

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

일반 PDO처럼 보일 수 있지만 (그것에 \Library\의해서만 변경됨 ) 실제로 첫 번째 메서드를 호출 할 때까지 개체를 초기화하지 않습니다. PDO 개체 생성이 약간 비싸기 때문에 더 최적화됩니다. 투명 클래스 또는 Ghost , Lazy Loading의 한 형태입니다 . $ DB를 일반 PDO 인스턴스로 취급하고, 전달하고, 동일한 작업을 수행하는 등의 작업을 수행 할 수 있습니다.


이것은 "데코레이터 패턴"이라고합니다

0
$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();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.