204 lines
6.6 KiB
PHP
204 lines
6.6 KiB
PHP
<?php
|
||
/**
|
||
* Класс для работы с базой mysql по средствам PDO
|
||
*/
|
||
|
||
Class MysqlPdo{
|
||
|
||
private static $instance;
|
||
|
||
private $pdo;
|
||
private $opt = [
|
||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||
PDO::ATTR_EMULATE_PREPARES => false,
|
||
];
|
||
private $host;
|
||
private $port = '3306';
|
||
private $db;
|
||
private $user;
|
||
private $pass;
|
||
private $charset = 'utf8';
|
||
private $sphinx = false;
|
||
private $PostgreSQL = false;
|
||
|
||
|
||
public function __construct($host, $db, $user, $pass, $sphinx = false, $port = null, $PostgreSQL=false, $opt = array(), $charset = null){
|
||
|
||
$this->host = $host;
|
||
$this->db = $db;
|
||
$this->user = $user;
|
||
$this->pass = $pass;
|
||
if(!empty($opt)) {
|
||
$this->opt = $opt;
|
||
}
|
||
if($charset){
|
||
$this->charset = $charset;
|
||
}
|
||
|
||
if($sphinx){
|
||
$this->sphinx = $sphinx;
|
||
}
|
||
|
||
if($PostgreSQL){
|
||
$this->PostgreSQL = $PostgreSQL;
|
||
|
||
}
|
||
|
||
if($port){
|
||
$this->port = $port;
|
||
}
|
||
|
||
$this->pdo = $this->connect();
|
||
|
||
self::$instance = $this;
|
||
}
|
||
|
||
// Кэш недоступности PG-хостов в рамках одного PHP-запроса.
|
||
// Чтобы не делать fsockopen-чек повторно для одного и того же хоста (экономит 2 сек на каждом повторе).
|
||
private static $pg_unreachable_cache = array();
|
||
|
||
//Подключение
|
||
private function connect(){
|
||
$dsn = "mysql:host=".$this->host.";port=".$this->port.";dbname=".$this->db;
|
||
if($this->sphinx){
|
||
$dsn = "mysql:host=".$this->host.";port=".$this->port;
|
||
} else if ($this->PostgreSQL){
|
||
$dsn = "pgsql:host=".$this->host.";port=".$this->port;
|
||
|
||
// Быстрая проверка доступности PG-хоста (2 сек таймаут).
|
||
// Без этой проверки PDO-connect может висеть 30-60 секунд при недоступном хосте,
|
||
// что валит весь кабинет на окружениях без PG (например, dev).
|
||
// На проде PG доступен — проверка мгновенна. На dev — 2 сек на первый раз, потом 0 сек.
|
||
$cacheKey = $this->host . ':' . $this->port;
|
||
if (isset(self::$pg_unreachable_cache[$cacheKey])) {
|
||
// Не логируем — про этот хост в текущем запросе уже отписались.
|
||
throw new RuntimeException(self::$pg_unreachable_cache[$cacheKey]);
|
||
}
|
||
$sock = @fsockopen($this->host, (int)$this->port, $errno, $errstr, 2);
|
||
if (!$sock) {
|
||
$msg = "PostgreSQL unreachable at {$this->host}:{$this->port} ({$errno}: {$errstr})";
|
||
self::$pg_unreachable_cache[$cacheKey] = $msg;
|
||
// Пишем ОДИН раз в php error_log при первом обнаружении недоступности в текущем запросе.
|
||
// На проде это видно в стандартном error_log сервера и поможет быстро поймать момент отказа PG.
|
||
$reqUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : 'cli';
|
||
@error_log("[MysqlPdo] {$msg} | request: {$reqUri}");
|
||
throw new RuntimeException($msg);
|
||
}
|
||
fclose($sock);
|
||
}
|
||
|
||
try {
|
||
$pdo = null;
|
||
if($this->sphinx){
|
||
$pdo = new PDO($dsn);
|
||
} else {
|
||
$pdo = new PDO($dsn, $this->user, $this->pass, $this->opt);
|
||
}
|
||
|
||
return $pdo;
|
||
} catch (PDOException $e) {
|
||
// Для MySQL/Sphinx без БД приложение работать не может — die как и было.
|
||
// Для PG нас сюда не должны пускать (fsockopen-check выше уже отсёк),
|
||
// но на всякий случай — бросаем исключение, не die.
|
||
if ($this->PostgreSQL) {
|
||
throw new RuntimeException('PostgreSQL connection failed: ' . $e->getMessage());
|
||
}
|
||
die('Ooooops что-то пошло не так. Мы уже работаем над проблемой.');
|
||
}
|
||
}
|
||
|
||
//mysql_query
|
||
public function query($query, $error = false){
|
||
$stmt = null;
|
||
try{
|
||
$stmt = $this->pdo->query($query);
|
||
} catch (Exception $e){
|
||
print_r($query);
|
||
print_r($e->getMessage());
|
||
}
|
||
|
||
if(!$stmt && $error){
|
||
print_r($query);
|
||
print_r($this->pdo->errorInfo());
|
||
}
|
||
return $stmt;
|
||
}
|
||
|
||
//mysql_query with named params
|
||
public function queryNamed($query, $params){
|
||
$statement = $this->pdo->prepare($query);
|
||
$statement->execute($params);
|
||
return $statement;
|
||
}
|
||
|
||
//mysql_fetch_assoc
|
||
public function fetch_assoc($stmt){
|
||
if (!$stmt || !($stmt instanceof PDOStatement)) {
|
||
return null;
|
||
}
|
||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
return $row;
|
||
}
|
||
|
||
//mysql_num_rows
|
||
public function num_rows($stmt){
|
||
if (!$stmt || !($stmt instanceof PDOStatement)) {
|
||
return 0;
|
||
}
|
||
$rows = $stmt->rowCount();
|
||
return $rows;
|
||
}
|
||
|
||
//mysql_result
|
||
public function result_pdo($stmt){
|
||
if (!$stmt || !($stmt instanceof PDOStatement)) {
|
||
return [];
|
||
}
|
||
$res = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
return $res;
|
||
}
|
||
|
||
public function result($stmt, $column = 0){
|
||
if (!$stmt || !($stmt instanceof PDOStatement)) {
|
||
return null;
|
||
}
|
||
$res = $stmt->fetchColumn($column);
|
||
return $res;
|
||
}
|
||
|
||
//mysql_fetch_row
|
||
function fetch_row($stmt){
|
||
if (!$stmt || !($stmt instanceof PDOStatement)) {
|
||
return null;
|
||
}
|
||
$row = $stmt->fetch(PDO::FETCH_NUM);
|
||
return $row;
|
||
}
|
||
|
||
//mysql_insert_id
|
||
function insert_id(){
|
||
$id = $this->pdo->lastInsertId();
|
||
return $id;
|
||
}
|
||
|
||
//mysql_error
|
||
function error(){
|
||
return $this->pdo->errorInfo();
|
||
}
|
||
|
||
//mysql_error
|
||
function quote($str){
|
||
return $this->pdo->quote($str);
|
||
}
|
||
|
||
static function get() {
|
||
return self::$instance;
|
||
}
|
||
|
||
public function errorInfo() {
|
||
return $this->pdo->errorInfo();
|
||
}
|
||
|
||
}
|