Биндинг неопределенного числа параметров к запросу MySQL

Использование параметризованных запросов в MySQL позволяет практически исключить возможность взлома вашего сайта при помощи SQL-инъекции. Не нужно вспоминать про экранирование кавычек и прочие прелести подстановки переменных в запрос, знай себе подавай параметры в функцию bind_params.

Однако периодически возникает небольшая проблема: что делать, если запрос строится динамически, и число параметров заранее неизвестно.

Проблему можно решить с помощью функции call_user_func_array. В качестве притянутого за уши примера возьмём выборку людей по фамилии, имени или отчеству.

function load_people_list($mysql, $firstname, $lastname, $patrname) {
	$sql  = 'SELECT * FROM people';

	$where = array();  // Условия WHERE
	$params = array(); // Аргументы функции bind_params

	// Строка с типами параметров. Поскольку она идет первым аргументом в bind_params,
	// в массив аргументов добавляем её тоже первой
	$param_types = '';
	$params[] = &$param_types;

	if (strlen($firstname) > 0) {
		$param_types .= 's';
		// Поскольку функция bind_params требует передачи параметров по ссылке,
		// в массив аргументов добавляем их также по ссылке
		$params[] = &$firstname;
		$where[] = 'firstname=?';
	}

	if (strlen($lastname) > 0) {
		$param_types .= 's';
		$params[] = &$lastname;
		$where[] = 'lastname=?';
	}

	if (strlen($patrname) > 0) {
		$param_types .= 's';
		$params[] = &$patrname;
		$where[] = 'patrname=?';
	}

	// Собираем запрос
	if (count($where) > 0)
		$sql .= ' WHERE ' . implode(' AND ', $where);

	$stmt = $mysql->prepare($sql);

	// Первым параметром call_user_func_array идет имя вызываемой функции.
	// В данном случае вызовется $stmt->bind_param()
	// Вторым параметром идет массив аргументов для вызываемой функции.

	if (count($params) > 1)
		call_user_func_array(array($stmt, 'bind_param'), $params);

	$stmt->execute();

	return $stmt;
}