Класс для безопасной работы с MySQL

Итак, какая же цель? Облегчение говнокодинга? Или его устранение?
цель - принять Иисуса реальность, а не жить в облачном хостинге, почесывая ЧСВ о новичков в стиле "сначала добейся, заработай на правильный хостинг, потом поговорим"
цель Фанат сформулировал в первом посте: сделать мир лучше для студентов, создать удобную простую библиотеку для новичков

студенты не пойдут платить 300 р за хостинг для своего сайтика потому что там правильно работает mb_string
нет, они выкинут все наши правильные библиотеки, позавидуют всем нам умным и богатым со своими серверами, и напишут по-своему, криво, но работающее у них

если мы хотим сделать мир лучше для людей - надо решать их проблемы, а не свои
 
Пока родил вот это.
Полноценно тестировать нет уже мочи, но, вроде, работает.
PHP:
	private function prepareQuery($args)
	{
		$query = '';
		$raw   = array_shift($args);
		$array = preg_split('~(\?[nsiuap?])~',$raw,null,PREG_SPLIT_DELIM_CAPTURE);
		$anum  = count($args);
		$pnum  = floor(count($array) / 2);

		if ( $pnum != $anum )
		{
			$this->error("Number of args ($anum) doesn't match number of placeholders ($pnum) in [$raw]");
		}

		foreach ($array as $i => $part)
		{
			if ( $i % 2 )
			{
			    $value = array_shift($args);
				switch ($part)
				{
					case '?n':
						$part = $this->escapeIdent($value);
						break;
					case '?s':
						$part = $this->escapeString($value);
						break;
					case '?i':
						$part = $this->escapeInt($value);
						break;
					case '?a':
						$part = $this->createIN($value);
						break;
					case '?u':
						$part = $this->createSET($value);
						break;
					case '?p':
						$part = $value;
						break;
					case '??':
						$part = '?';
						break;
					default:
						$this->error("Unknown error");
				}
			}
			$query .= $part;
		}
		return $query;
	}
 
Так.
Оно косячит на удвоенных вопросах.
Удвоенные вопросы мы делаем для экранирования.
Ну так может и нахрен это экранирование?
В запросе сочетание ?s само по себе встретиться не может.
В передаваемых данных оно нам теперь по барабану
Вуаля! Выкидываем вопрос из паттерна.

В итоге код получился даже проще.

PHP:
	private function prepareQuery($args)
	{
		$query = '';
		$raw   = array_shift($args);
		$array = preg_split('~(\?[nsiuap])~u',$raw,null,PREG_SPLIT_DELIM_CAPTURE);
		$anum  = count($args);
		$pnum  = floor(count($array) / 2);
		if ( $pnum != $anum )
		{
			$this->error("Number of args ($anum) doesn't match number of placeholders ($pnum) in [$raw]");
		}

		foreach ($array as $i => $part)
		{
			if ( ($i % 2) == 0 )
			{
				$query .= $part;
				continue;
			}

		    $value = array_shift($args);
			switch ($part)
			{
				case '?n':
					$part = $this->escapeIdent($value);
					break;
				case '?s':
					$part = $this->escapeString($value);
					break;
				case '?i':
					$part = $this->escapeInt($value);
					break;
				case '?a':
					$part = $this->createIN($value);
					break;
				case '?u':
					$part = $this->createSET($value);
					break;
				case '?p':
					$part = $value;
					break;
			}
			$query .= $part;
		}
		return $query;
	}

Единственное ограничение - нельзя динамически собирать сами шаблоны запроса.
Его надо будет оговорить специально.
 
?p допускает иньекции, а мой сниппет из лички - нет ;)

Во-первых, по поводу "допускает инъекции". Их допускает банальная переменная в запросе. Так что если дурак захочет - он всё равно поломает.
Во-вторых, снипет обладает очень ограниченной функциональностью.
Всё суть parse() в том, что ты можешь ваять не толкьо те конструкции, которые предусмотрел автор либы, а вообще любые. соблюдая при этом главное правило безопасности - "любые динамические элементы попадают в запрос только через плейсхолдер".
Хотя, может, я не до конца понимаю принцип работы снипета. Если покажешь мне примеры, как с твоим снипетом делаются
http://www.phpfaq.ru/examples#multiinsert
http://www.phpfaq.ru/examples#onduplicate (ладно, пусть без ?u - допустим, SET собирается руками)
http://www.phpfaq.ru/examples#where
http://www.phpfaq.ru/examples#complex
- тогда я всерьёз задумаюсь над тем, чтобы сделать что-то подобное.
 
Кстати, ты лукавишь.
Если говорить про ?p, то аналогом его является не сам снипет, а функция привязки результата к объекту - то есть, часть квери-билдера.
 
PHP:
$w = array();
$where = '';
if ($one) $w[] = $db->parse("one = ?s",$one); 
if ($two) $w[] = $db->parse("two IN (?a)",$two);
if ($tre) $w[] = $db->parse("tre <= ?i",$tre);
if (count($w)) $where = "WHERE ".implode(' AND ',$w);
$data = $db->getArr("SELECT * FROM table ?p LIMIT ?i,?i",$where, $start,$per_page);
я категорически против такого подхода, не знаю почему, но это мне не нравится.
ИМХО запрос должен формироваться как есть, аргументы - складироваться в массив и все это вызываться через
PHP:
call_user_func_array(array($this->db, 'query'), $args)
 
Василий М., поскольку плейсхолдеры непоименованные, замучаешься соответствие аргументов соблюдать.
 
Запросы не всегда бывают фиксированного вида. Часто (если не очень часто) запрос формируется динамически, проходя сначала через кучу if'ов и foreach'ей. Замучаешься соответствие аргументов соблюдать.
 
Во-первых, по поводу "допускает инъекции". Их допускает банальная переменная в запросе. Так что если дурак захочет - он всё равно поломает.
Во-вторых, снипет обладает очень ограниченной функциональностью.
Сниппет допускает в plain() только строки без кавычек или объекты класса сниппета, то есть мышь не проскакивает ;)

Хотя, может, я не до конца понимаю принцип работы снипета. Если покажешь мне примеры, как с твоим снипетом делаются
http://www.phpfaq.ru/examples#multiinsert

PHP:
$query = new DB_Snippet("INSERT INTO table VALUES " . mb_substr(str_repeat('%s,', sizeof($data), 0, -1));
foreach ($data as $row) {
    $query->plain(
        new DB_Snippet("(NULL,%s,%s, NOW())")
            ->q($row['name'])
            ->q($row['lastname'])
    )
}
$db->query($query);
Остальное по аналогии.
 
Ну, может, оно так и безопаснее. Поздравляю, у тебя получилось лучше.
 
я только не понял.
если снипет пропускает только без кавычек - это получается, что
PHP:
"INSERT INTO `table` VALUES "
в него передать нельзя?
 
я понял иначе, plain пропускает только строки без кавычек. а в сам Snippet возможно передать любой string...
PHP:
class DB_Snippet{
public function plain($q){
  if(is_string($q))
    $q=addslashes($q);
  elseif( $q instanceof DB_Snippet ) 
    $q=$q->toString()
  else throw ....
  ..
}
}
 
Василий М.
Подобные классы нафиг не нужны никому, кроме новичков, и носят исключительно образовательную роль.
А тебя слушать никто не будет, потому что ты все время ведешь себя, как придурок, даже если пишешь все хорошо и правильно :D
 
я только не понял.
если снипет пропускает только без кавычек - это получается, что
PHP:
"INSERT INTO `table` VALUES "
в него передать нельзя?
обратные кавычки можно ;)
одинарные и двойные можно только через DB_Query::q(), оно их безусловно квотирует
 
Сверху