Православный коннект а базе данных

Фанат

oncle terrible
Команда форума
Надо сказать, что классов я толком раньше не писал.
А таких классов, как DAL - и подавно.
Поэтому вопрос к более опытным товарищам - как сделать максимально удобный yet функциональный конструктор?

Чтобы, с одной стороны, был синглтон, а с другой - возможность создать новый инстанс.

Делается это всё на mysqli.

Сейчас у меня так.

PHP:
	function __construct($opt = array())
	{
		$default = array(
			'host'     => 'localhost',
			'user'     => 'root',
			'pass'     => '',
			'db'       => 'test',
			'pconnect' => FALSE,
			'charset'  => 'utf8',
			'errmode'  => 'error', //or exception
		);
		$opt = array_merge($default,$opt);
		$this->errmode = $opt['errmode'];
		if ($opt['pconnect'])
		{
			$opt['host'] = "p:".$opt['host'];
		}
		@$this->conn = mysqli_connect($opt['host'], $opt['user'], $opt['pass'], $opt['db']);
		if ( !$this->conn )
		{
			$this->error(mysqli_connect_errno()." ".mysqli_connect_error());
		}
		mysqli_set_charset($this->conn, $opt['charset']) or $this->error(mysqli_error($this->conn));
		unset($opt); // I am paranoid
	}
но хочу сделать все-таки на файлах, поскольку писать параметру коннекта руками в скрипте - совсем не комильфо. А в файле иметь несколько настроек, выбираемых по индексу.

Соответственно, надо в конструкторе держать пул коннектов? массив вида
PHP:
$connect['default'] = $conn;
$connect['project1'] = $conn;
?
Единственное ограничение, важное для меня - никаких посторонних по отношению к классу механизмов, типа коллекций с настройками. в лучшем случае получаем из коллекции настройки массивом и передаем на вход конструктору.
 

fixxxer

К.О.
Партнер клуба
Ну например так.
Пул отдельно, DBAL отдельно. В пуле регистрировать по имени. С этим же именем - dsn в конфиге. Для краткости записи можно использовать Pool::__callStatic.
Если почти все время нужно только одно соединение - можно сделать функцию, возвращающую оное.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
я бы все же в конфиге все же сделал
PHP:
return array (
    'default' => array( 'host' => 'localhost', 'port' => 3306 ....),
    'other' => array(...),
);
а уже в объекте ->selectDatabase('other')

Как-то так.
 

c0dex

web.dev 2002-...
Команда форума
Партнер клуба
Чтобы, с одной стороны, был синглтон, а с другой - возможность создать новый инстанс.
Странного ты хочешь. Как это синглтон и новый инстанс, это противоречит друг другу...

Если же хочется "новый инстанс" - как новое соединение, то можешь посмотреть и покритиковать это: https://github.com/psilocyberunner/XiioFW/blob/master/Xiio/Core/Database/PdoMysql.php ;)

ЗЫ: но там куча посторонних механизмов.
 

Фанат

oncle terrible
Команда форума
c0dex
синглтон - неправильное слово.
правильнее будет говорить о коннекте.
вот что я хочу - ниже:
а уже в объекте ->selectDatabase('other')
примерно это я и хочу.
но с возможностью работы с несколькими коннектами одновременно
PHP:
$db = new db(); //дефолтный конфиг
$otherdb = new db('project1');
....
$db1 = new db(); // в другом месте работаем с тем де соединением, которое было открыто раньше
а в конструкторе, соответственно
PHP:
if (isset($connects[$key])) {
  $this->conn = $connects[$key];
} else {
  $this->conn = $this->connect($key);
}
 

Фанат

oncle terrible
Команда форума
fixxxer
Костя, я хочу сделать просто! Как можно проще! :)
Пишу-то я не только для себя. я хочу переучивать всех этих
mysql_connect() oir die("Could not connect to database");
mysql_query() or die("Error")
mysql_fetch_array()
Да, я знаю, что это утопия. но может, и получится. если код будет достаточно простым в использовании
 

serglt

Анус, ой, Ахтунг
У меня как то так

конфиг
PHP:
Sql::$config = array (
	'default' => array (...),
	'album'   => array (...),
	'user'    => array (...),
);
Сам класс
PHP:
class Sql extends mysqli {
	static $config;
	static $instances = array ();
	protected function __construct ($mode) {
		$c = self::$counfig [$mode];
		parent::__construct ($c ['user'], ...);
	}
	static function getInstance ($mode = 'default') {
		if (!isset (self::$instances [$mode]))
			self::$instances [$mode] = new self ($mode);
		return self::$instances [$mode];
	}
}
Соотв вызов

PHP:
$sql = Sql::getInstance ();
$sqlAlbum = Sql::getInstance ('album');
 

Vladson

Сильнобухер
Для таких давно есть код проще, их держит не отсутствие класса, а то что в литературе по которой они учились и в каждом втором OpenSource движке сделано именно так (и это для них авторитет)
 

Фанат

oncle terrible
Команда форума
serglt
а можешь объяснить, для чего пишется именно так,
PHP:
$sqlAlbum = Sql::getInstance ('album');
а не
PHP:
$sqlAlbum = new Sql('album');
?
 

Vladson

Сильнобухер
Ну не знаю, я в своё время как PDO попробовал, удивился насколько просто и безопасно. Если ты придумаешь что-то ещё лучше, я тебе спасибо скажу..,
 

serglt

Анус, ой, Ахтунг
И зачем использовать процедурные методы когда есть класс mysqli? Его перегрузил и дополнил своими методами
У нас на проджекте есть уже вот такой класс похожу, он со временем превращается в ужос, с постоянной допилкой.
Резалт тоже можно перегрузить

PHP:
class SqlResult extends mysqli_result {
	function __construct ($sql) {
		parent::__construct ($sql);
	}
}

class Sql extends mysqli {
...
...
...
	function query ($q) {
		$this -> real_query ($q);
		return new SqlResult ($this);
	}
}
 

serglt

Анус, ой, Ахтунг
Фанат
Это сингелтон, объект создается в статик функции, специально чтоб не плодить соединения к одной и той же базе
Ты не сможешь создать объект напрямую, я специально для этого конструктор сделал защищенным
 
  • Like
Реакции: craz

Фанат

oncle terrible
Команда форума
Vladson
Я писал, в известной статье, что PDO не безопасно - для безопасного добавления имени поля в запрос у PDO ничего нету
и не просто - элементарная задача получить все строки из простого запроса растягивается на три строчки:
PHP:
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$stmt->execute(array("%$_GET[name]%"));
$data = $stmt->fetchAll();
А если, не дай бог, надо в запрос добавить IN() то это выливается в такие эротические танцы, что про удобство уже и не вспоминаешь.

при том что если отбросить все ненужные, не значащие, бессмысленно повторяющиеся элементы, то останется одна строчка
PHP:
$sql  ="SELECT * FROM table WHERE name LIKE ?s AND id IN(?a) ORDER BY ?n";
$data = $db->getAll($sql,"%$_GET[name]%",$ids,$_GET['order']);
 

Фанат

oncle terrible
Команда форума
Хотя в последнем я наврал, для красоты
совсем православно будет побольше кода
PHP:
$order = $db->whiteList($_GET['order'],array('name','price'),TRUE);
if ($order === FALSE) {
    throw new http404; // или ваш собственный способ выдать 404 ошибку
}
$data = $db->getAll("SELECT name FROM table WHERE id IN(?a) ORDER BY ?n",$ids,$order);
Но если есть валидатор входящих где-то раньше, то здесь уже проверка не нужна будет и остаётся ровно одна строчка
 

craz

Нестандартное звание
Хотя в последнем я наврал, для красоты
совсем православно будет побольше кода
PHP:
$order = $db->whiteList($_GET['order'],array('name','price'),TRUE);
if ($order === FALSE) {
    throw new http404; // или ваш собственный способ выдать 404 ошибку
}
$data = $db->getAll("SELECT name FROM table WHERE id IN(?a) ORDER BY ?n",$ids,$order);
Но если есть валидатор входящих где-то раньше, то здесь уже проверка не нужна будет и остаётся ровно одна строчка
честно говоря мне такая абстракция совсем не понятна, пришлось бы лезть смотреть, что такое whiteList, причем набор входящих переменных... ну совсем мою логику, без знания внутренностей, сбивает.
 

Фанат

oncle terrible
Команда форума
craz
Это совершенно естественная реакция! потому что понятие проверки по белому списку - НОВОЕ для пхп разработчиков. При том, что оно ключевое для безопасности, когда мы работаем с именами полей и таблиц.
Это такая вещь, которую надо запомнить.
Впрочем, как я говорил уже выше, если есть контроль входящих параметров, то и проверка такая не понадобится.
В самом худшем случае, если даже мы пропустим поле без проверки, то получим ошибку запроса (Нет такого поля), но инъекции все равно не будет.
 
  • Like
Реакции: craz

serglt

Анус, ой, Ахтунг
Кстати для варианта
$db = new Sql ('mode');

Можно запилить как то так

PHP:
class Sql {
	protected $mode;
	static $instances = array ();
	function __construct ($mode) {
		$this -> mode = $mode;
		if (!isset (self::$instances [$mode]))
			self::$instances [$mode] = new mysqli_ext (...);
	}

	function __call ($m, $a) {
		return call_user_func_array (array (self::$instances [$this -> mode], $m), $a);
	}

	function __get ($k) {
		return self::$instances [$this -> mode] -> $k;
	}
}
Теперь в конфиге можно указывать классы для работы с БД, надо тока заменить (mysqli_ext) на соотв код.
 

Beavis

Banned
Я бы сделал простой независимый класс DBConnection, который обрабатывает только одно подключение, и класс DBConnectionManager, который управляет ими.
Нужно одно соединение - используешь первый класс, нужно много - второй.
А пихать всё в один класс, это не лучшая практика, особенно для новичков, которые будут считать твой код за образец.
 
  • Like
Реакции: craz

Фанат

oncle terrible
Команда форума
А пихать всё в один класс, это не лучшая практика, особенно для новичков, которые будут считать твой код за образец.
Совершенствоваться можно бесконечно. Нужны ещё еррор хендлер, ексепшен хендлер, и далее везде - прямой дорогой к фреймворку %)

За образец сейчас считают PDO, в котором никаких таких красот не наблюдается :)
 
Сверху