PDO

PDO 개요

GOLDILOCKS PDO (PDO_GOLDILOCKS) 드라이버는 PDO에서 GOLDILOCKS 데이터베이스에 접근할 수 있도록 PDO 인터페이스를 제공한다.

PDO_GOLDILOCKS는 GOLDILOCKS CLI 드라이버를 기반으로 작성되었다. GOLDILOCKS 배포 패키지는 PHP 버전에 맞는 PDO_GOLDILOCKS 소스와 PECL 패키지 메타데이터를 제공한다.

설치/구성

요구 사항

PHP와 동일한 시스템에 GOLDILOCKS의 클라이언트 패키지를 설치하고 $GOLDILOCKS_HOME 환경 변수를 설정해야 한다.

소스를 빌드하려면 C 컴파일러와 설치된 PHP 버전에 맞는 개발 패키지가 필요하다. 또한 phpize 및 php-config 명령을 사용할 수 있어야 한다. PECL 패키지를 생성하거나 설치하려면 PEAR/PECL도 설치되어 있어야 한다.

설치

PHP 버전에 맞는 소스 선택

PDO_GOLDILOCKS 소스는 $GOLDILOCKS_HOME/app_dev/pdo 디렉터리 아래에 PHP 버전별 디렉터리로 제공된다. 설치된 PHP 버전에 맞는 소스 디렉터리를 선택한다.

PHP 버전

소스 디렉터리

PHP 5.1 ~ 5.6

PDO_legacy

PHP 7.0 ~ 8.0

PDO_php7_to_80

PHP 8.1 이상

PDO_php81_and_above

php, phpize, php-config 명령은 모두 동일한 PHP 버전에 대응하는 것을 사용해야 한다. 다음 명령으로 설치된 PHP 버전을 확인할 수 있다.

% php -v

PECL은 PHP 버전에 맞는 소스 디렉터리를 자동으로 선택하지 않는다. 따라서 PECL을 사용하는 경우에도 위 표를 참고하여 먼저 해당 PHP 버전에 맞는 소스 디렉터리를 선택해야 한다.

PECL을 이용한 설치

선택한 소스 디렉터리에서 pecl pickle 명령을 실행하여 PECL 패키지를 생성한다. 다음은 PHP 7.0 ~ 8.0 용 소스를 사용하는 예이다.

% cd $GOLDILOCKS_HOME/app_dev/pdo/PDO_php7_to_80
% pecl pickle
Attempting to process the second package file
Package PDO_GOLDILOCKS-26.1.0.tgz done

PDO_GOLDILOCKS 패키지 파일이 생성되면 pecl 명령을 사용하여 패키지를 설치한다.

% sudo -E pecl install ./PDO_GOLDILOCKS-26.1.0.tgz

소스 빌드를 통한 설치

PECL 패키지를 생성하지 않고 선택한 소스 디렉터리에서 직접 빌드할 수도 있다. 다음은 PHP 7.0 ~ 8.0 용 소스를 사용하는 예이다.

  1. 소스 디렉터리로 이동한다.

% cd $GOLDILOCKS_HOME/app_dev/pdo/PDO_php7_to_80
  1. phpize 명령을 실행하여 PDO_GOLDILOCKS 확장 모듈의 빌드 환경을 생성한다.

% phpize
  1. php-config 명령과 $GOLDILOCKS_HOME 경로를 지정하여 빌드한다.

% ./configure --with-php-config="$(command -v php-config)" --with-pdo-goldilocks="$GOLDILOCKS_HOME"
% make
  1. 빌드한 확장 모듈을 설치한다.

% sudo -E make install

PHP 설정 파일 수정

다음 명령으로 PHP 에서 사용하는 설정 파일과 추가 설정 파일 디렉터리를 확인한다.

% php --ini

확인한 설정 파일 또는 추가 설정 파일 디렉터리의 pdo_goldilocks.ini 파일에 다음 내용을 추가한다.

extension=pdo_goldilocks.so

CLI 와 웹 서버 또는 PHP-FPM 은 서로 다른 PHP 설정 파일을 사용할 수 있으므로, 각각에서 사용하는 설정 파일을 확인해야 한다.

웹 서버 또는 PHP-FPM 재시작

PDO_GOLDILOCKS를 웹 애플리케이션에서 사용하는 경우 웹 서버 또는 PHP-FPM을 재시작한다.

PDO_GOLDILOCKS 설치 확인

다음 명령으로 PDO_GOLDILOCKS 확장 모듈과 PDO 드라이버가 활성화되었는지 확인한다.

% php -m | grep -i '^pdo_goldilocks$'
pdo_goldilocks
% php -i | grep 'PDO Driver for GOLDILOCKS'
PDO Driver for GOLDILOCKS => enabled

사용

데이터 원본 이름 (DSN)

PDO_GOLDILOCKS의 데이터 원본 이름 (DSN)은 다음과 같이 구성된다.

속성

설명

DSN prefix

goldilocks

DSN

ODBC 데이터 원본 이름 (DSN)

HOST

서버의 IP 주소

PORT

서버의 PORT 번호

UID

사용자 ID

PWD

사용자 password

사용 예

print "\n[Connecting to GOLDILOCKS]\n";
$db = new PDO('goldilocks:HOST=192.168.0.16;PORT=22581', 'test', 'test');
//$db = new PDO('goldilocks:DSN=GOLDILOCKS;UID=test;PWD=test');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$result = $db->exec('DROP TABLE IF EXISTS t1');
$result = $db->exec('CREATE TABLE t1 ( id INTEGER GENERATED BY DEFAULT AS IDENTITY, name VARCHAR(32) )');
$result = $db->exec("INSERT INTO t1(name) VALUES ('Carol')");
$result = $db->exec("INSERT INTO t1(name) VALUES ('Ted')");
$result = $db->exec("INSERT INTO t1(name) VALUES (null)");
$result = $db->exec("INSERT INTO t1(name) VALUES ('William')");
$result = $db->exec("INSERT INTO t1(name) VALUES ('Chelsea')");
$result = $db->exec("INSERT INTO t1(name) VALUES ('Colin')");

$result = $db->exec('DROP TABLE IF EXISTS t2');
$result = $db->exec('CREATE TABLE t2 ( id INTEGER, name VARCHAR(32) )');
$result = $db->exec("INSERT INTO t2 VALUES (1, 'John')");
$result = $db->exec("INSERT INTO t2 VALUES (1, 'John')");
$result = $db->exec("INSERT INTO t2 VALUES (1, 'John')");
$result = $db->exec("INSERT INTO t2 VALUES (2, 'Smith')");
$result = $db->exec("INSERT INTO t2 VALUES (2, 'Smith')");

$result = $db->exec('DROP TABLE IF EXISTS images');
$result = $db->exec('CREATE TABLE images ( id INTEGER, contenttype VARCHAR(256), imagedata LONG VARBINARY )');

$result = $db->exec('DROP PROCEDURE IF EXISTS sp_out_string');
$result = $db->exec("CREATE OR REPLACE PROCEDURE sp_out_string( v1 OUT VARCHAR(32) ) ".
                    "IS ".
                    "BEGIN ".
                    "  v1 := 'WORLD'; ".
                    "END; ");

$result = $db->exec('DROP PROCEDURE IF EXISTS sp_inout_string');
$result = $db->exec("CREATE OR REPLACE PROCEDURE sp_inout_string( v1 INOUT VARCHAR(32) ) ".
                    "IS ".
                    "BEGIN ".
                    "  v1 := V1 || ' WORLD'; ".
                    "END; ");
print "\n[Quoting a normal string]\n";
$string = 'Nice';

print "Unquoted string: $string\n";
print "Quoted string  : " . $db->quote($string) . "\n";
print "\n[Quoting a dangerous string]\n";
$string = 'Naughty \' string';

print "Unquoted string: $string\n";
print "Quoted string  :" . $db->quote($string) . "\n";
print "\n[Quoting a complex string]\n";
$string = "Co'mpl''ex \"st'\"ring";

print "Unquoted string: $string\n";
print "Quoted string  : " . $db->quote($string) . "\n";
print "\n[Error Handling]\n";
try
{
    //connect as appropriate as above
    $db->query('invalid query'); //invalid query!
}
catch(PDOException $ex)
{
    print "ERROR:".$ex->getMessage()."\n";
}
print "\n[Retrieving column metadata]\n";
$stmt = $db->query('SELECT * FROM t1');
foreach(range(0, $stmt->columnCount() - 1) as $column_index)
{
    $meta = $stmt->getColumnMeta($column_index);
    var_dump($meta);
}
print "\n[Running Simple Select Statements]\n";

print "\n#1\n";
foreach($db->query('SELECT * FROM t1') as $row)
{
    print $row['ID'].' '.$row['NAME']."\n";
}

print "\n#2\n";
$stmt = $db->query('SELECT * FROM t1');
 
while($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
    print $row['ID'].' '.$row['NAME']."\n";
}
print "\n[Fetching rows using different fetch styles]\n";
$stmt = $db->prepare("SELECT id, name FROM t1");
$stmt->execute();

print("PDO::FETCH_ASSOC: ");
print("Return next row as an array indexed by column name\n");
$result = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($result);
print("\n");

print("PDO::FETCH_BOTH: ");
print("Return next row as an array indexed by both column name and number\n");
$result = $stmt->fetch(PDO::FETCH_BOTH);
print_r($result);
print("\n");

print("PDO::FETCH_LAZY: ");
print("Return next row as an anonymous object with column names as properties\n");
$result = $stmt->fetch(PDO::FETCH_LAZY);
print_r($result);
print("\n");

print("PDO::FETCH_OBJ: ");
print("Return next row as an anonymous object with column names as properties\n");
$result = $stmt->fetch(PDO::FETCH_OBJ);
print $result->NAME;
print("\n");
print "\n[Fetching rows using different fetch styles]\n";
print "\n[Fetching rows with a scrollable cursor]\n";
$sql = 'SELECT id, name FROM t1 ORDER BY id';

print "Reading forwards:\n";
try
{
    $stmt = $db->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
    $stmt->execute();
    while( $row = $stmt->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_NEXT) )
    {
        $data = $row[0] . "\t" . $row[1] . "\n";
        print $data;
    }
    $stmt = null;
}
catch (PDOException $e)
{
    print $e->getMessage();
}

print "Reading backwards:\n";
try
{
    $stmt = $db->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
    $stmt->execute();
    $row = $stmt->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_LAST);
    do
    {
        $data = $row[0] . "\t" . $row[1] . "\n";
        print $data;
    } while( $row = $stmt->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_PRIOR) );

    $stmt = null;
}
catch (PDOException $e)
{
    print $e->getMessage();
}
print "\n[Construction order]\n";
class Person
{
    private $NAME;

    public function __construct()
    {
        $this->tell();
    }

    public function tell()
    {
        if (isset($this->NAME))
        {
            print "I am {$this->NAME}.\n";
        }
        else
        {
            print "I don't have a name yet.\n";
        }
    }
}
$stmt = $db->query("SELECT name FROM t1");
$stmt->setFetchMode(PDO::FETCH_CLASS, 'Person');
$person = $stmt->fetch();
$person->tell();
$stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'Person');
$person = $stmt->fetch();
$person->tell();
print "\n[Running Simple INSERT, UPDATE, or DELETE statements]\n";
$affected_rows = $db->exec("INSERT INTO t1(name) VALUES ('John'), ('Marry')");
print $affected_rows." were affected\n";
print "\n[Getting the Last Insert Id]\n";
$affected_rows = $db->exec("INSERT INTO t1(name) VALUES ('Smith')");
$insertId = $db->lastInsertId();
print "last insert id : ".$insertId."\n";
print "\n[Running Statements With Parameters]\n";

print "\n#1 array\n";
$id = 1;
$name = 'John';

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

var_dump($rows);

print "\n#2 bindValue\n";
$id = 2;
$name = 'Smith';

$stmt = $db->prepare("SELECT * FROM t2 WHERE id=? AND name=?");
$stmt->bindValue(1, $id, PDO::PARAM_INT);
$stmt->bindValue(2, $name, PDO::PARAM_STR);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

var_dump($rows);
print "\n[Named Placeholders]\n";

print "\n#1 array\n";
$id = 1;
$name = 'John';

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

var_dump($rows);

print "\n#2 bindValue\n";
$id = 2;
$name = 'Smith';

$stmt = $db->prepare("SELECT * FROM t2 WHERE id=:id AND name=:name");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

var_dump($rows);
print "\n[Executing prepared statements in a loop]\n";
$values = array('bob', 'alice', 'lisa', 'john');
$name = '';
$stmt = $db->prepare("INSERT INTO t1(name) VALUES(:name)");
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
foreach($values as $name)
{
   $stmt->execute();
}

foreach($db->query('SELECT * FROM t1') as $row)
{
    print $row[0].' '.$row[1]."\n";
}
print "\n[Calling a stored procedure with an output parameter]\n";
$stmt = $db->prepare("CALL sp_out_string(?)");
$stmt->bindParam(1, $value, PDO::PARAM_STR, 32); 
$stmt->execute();

print "procedure returned $value\n";
print "\n[Calling a stored procedure with an input/output parameter]\n";
$stmt = $db->prepare("CALL sp_inout_string(?)");
$value = 'HELLO';
$stmt->bindParam(1, $value, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 32); 
$stmt->execute();

print "procedure returned $value\n";
print "\n[Transactions]\n";

print "\n#1 commit\n";
try
{
    $db->beginTransaction();
 
    $db->exec("INSERT INTO t1(name) VALUES('kim')");

    $name = 'lee';
    $stmt = $db->prepare("INSERT INTO t1(name) VALUES(?)");
    $stmt->execute(array($name));
 
    $id = 3;
    $name = 'park';
    $stmt = $db->prepare("INSERT INTO t2 VALUES(?, ?)");
    $stmt->execute(array($id, $name));
 
    $db->commit();
}
catch(PDOException $ex)
{
    //Something went wrong rollback!
    $db->rollBack();
    print $ex->getMessage()."\n";
}

print "\nT1\n";
foreach($db->query('SELECT * FROM t1') as $row)
{
    print $row[0].' '.$row[1]."\n";
}

print "\nT2\n";
foreach($db->query('SELECT * FROM t2') as $row)
{
    print $row[0].' '.$row[1]."\n";
}

print "\n#2 rollback\n";
try
{
    $db->beginTransaction();
 
    $db->exec("INSERT INTO t1(name) VALUES('KIM')");

    $name = 'LEE';
    $stmt = $db->prepare("INSERT INTO t1(name) VALUES(?)");
    $stmt->execute(array($name));
 
    $id = 3;
    $name = 'PARK';
    $stmt = $db->prepare("INSERT INTO invalid VALUES(?, ?)");
    $stmt->execute(array($id, $name));
 
    $db->commit();
}
catch(PDOException $ex)
{
    //Something went wrong rollback!
    $db->rollBack();
    print $ex->getMessage()."\n";
}

print "\nT1\n";
foreach($db->query('SELECT * FROM t1') as $row)
{
    print $row[0].' '.$row[1]."\n";
}

print "\nT2\n";
foreach($db->query('SELECT * FROM t2') as $row)
{
    print $row[0].' '.$row[1]."\n";
}
print "\n[Inserting an image into a database]\n";
try
{
    $stmt = $db->prepare("INSERT INTO images (id, contenttype, imagedata) VALUES (?, ?, ?)");

    $id = 1;
    $type = "image/png";
    $fp = fopen("logo.png", "rb");

    $stmt->bindParam(1, $id, PDO::PARAM_INT);
    $stmt->bindParam(2, $type, PDO::PARAM_STR, 256);
    $stmt->bindParam(3, $fp, PDO::PARAM_LOB);

    $db->beginTransaction();
    $stmt->execute();
    $db->commit();
}
catch(PDOException $ex)
{
    $db->rollBack();
    print $ex->getMessage()."\n";
}

foreach($db->query('SELECT id, contenttype, lengthb(imagedata) FROM images') as $row)
{
    print $row[0].' '.$row[1].' '.$row[2]."\n";
}
print "\n[Displaying an image from a database]\n";
try
{
    $stmt = $db->prepare("SELECT contenttype, imagedata FROM images WHERE id = ?");

    $id = 1;
    $stmt->execute(array($id));
    $stmt->bindColumn(1, $type, PDO::PARAM_STR, 256);
    $stmt->bindColumn(2, $lob, PDO::PARAM_LOB);
    $stmt->fetch(PDO::FETCH_BOUND);

    print 'Content-Type: '.$type."\n";
    print 'size: '.file_put_contents($id.".png", $lob)."\n";
}
catch(PDOException $ex)
{
    print $ex->getMessage()."\n";
}