WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru Шаблоны сайтов на русском

wpdb{} WP 0.71

Позволяет производить любые операции с базой данных WordPress: вставлять, обновлять, получать или удалять данные.

WordPress предоставляет возможность удобно манипулировать своей базой данных за счет php класса wpdb.

php класс - относится к ООП (объектно-ориентированному программированию) и в традиционном своем понимании представляет собой полностью самодостаточный код, который должен выполнять определенную функцию. Например, в этом случае через класс wpdb мы может производить всевозможные операции с Базой Данных WordPress зная всего несколько "рычагов" управления классом, которые называются методами. Если говорить условно, то методы - это обычные php функции, только внутри класса.

Обращаться к методам класса wpdb нужно обязательно через глобальную переменную $wpdb (это экземпляр класса wpdb). Также нужно помнить, что внутри обычных функций нужно обязательно глобализировать $wpdb, иначе она будет простой переменной внутри функции, делается это так:

global $wpdb;

C помощью методов $wpdb можно управлять произвольными таблицами в базе данных, не обязательно только теми, которые были созданы WordPress. Допустим, среди прочих таблиц WP, есть таблица newtable и нам нужно выбрать все поля id из нее. Реализовать это можно таким SQL запросом используя $wpdb:

$newtable = $wpdb->get_results( "SELECT id FROM newtable" );

Создавать свои таблицы рекомендуется с помощью функции dbDelta(). Или можно использовать обычный SQL запрос и метод $wpdb->query('CREATE TABLE ...');.

Для плагинов создание новых таблиц обычно вешается на хук активации плагина, см. register_activation_hook().

Создание отдельного подключения к базе данных

Нужно понимать, что один объект класса wpdb{} работает с одной БД - с текущей базой данных WordPress. Если нужно работать параллельно с какой-либо другой БД, то нужно создать еще один объект класса wpdb с указанием новых параметров соединения, отличных от тех что указаны в wp-config.php. Делается это так:

global $wpdb2;

$wpdb2 = new wpdb( 'имя_юзера', 'пароль', 'название_БД', 'localhost' );

// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if( ! empty($wpdb2->error) )
	wp_die( $wpdb2->error );

// Готово, теперь используем функции класса wpdb
$results = $wpdb2->get_results( "SELECT * FROM table" );

Для сложных соединений с несколькими БД (репликами), есть хороший плаглин, который рекомендуют разработчики WP - hyperdb. Этот плагин расширяет возможности базового класса wpdb. Устанавливается он не как обычный плагин и требует определенных знаний работы с базами данных (в противном случае, толку от этого плагина не будет).

А теперь, перейдем к разбору класса wpdb.

Хуки из класса
Возвращает

Экземпляр класса.

query — произвольный запрос к Базе Данных WordPress

Позволяет выполнять любые запросы к базе данных WordPress. В общем случае, она подразумевает выполнение не SELECT запросов, хотя и они могут также выполняться (но для них существуют $wpdb->get_results и другие функции см. ниже).

Возвращает

Число/false. Вернет количество строк, которые были задействованы в результате запроса (удалены/изменены/выбраны). Если запрос вызвал ошибку, то будет возвращено значение false.

$wpdb->query('query');
query(строка)
Запрос который нужно выполнить.

Имейте ввиду, что как и для всех функций класса wpdb, передаваемые параметры нужно очистить от SQL инъекций, сделать это можно двумя способами:

1) esc_sql( $user_entered_data_string ).
2) $wpdb->prepare(  'query' , value_parameter[, value_parameter ... ] ).

Подробнее читайте в секции "Защита запросов от SQL инъекций". см. ниже.

Примеры

#1. Удалить произвольное поле 'gargle' и его значение у поста 13

$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE post_id = '13' AND meta_key = 'gargle'" );

#2. Установить родительскую страницу 7 для страницы 15

$wpdb->query( "UPDATE $wpdb->posts SET post_parent = 7 WHERE ID = 15 AND post_status = 'static'" );

#3. Удалить осиротевшие произвольные поля

$wpdb->query("DELETE pm FROM $wpdb->postmeta pm LEFT JOIN wp_posts wp ON wp.ID = pm.post_id WHERE wp.ID IS NULL");

#4. Изменим ключ у полей-повторителей ACF

$wpdb->query( "
	UPDATE $wpdb->postmeta
	SET meta_key = REPLACE(meta_key, 'knowledge-base-type', 'knowledge-base-list')
	WHERE `meta_key` LIKE '%knowledge-base-type%'
" );
меню

get_var — получение определенной ячейки таблицы

Получает значение первой колонки и первой строки из результата запроса.

Чтобы получить значение второй ячейки из второй строки результата, используйте второй и третий параметры: $column_offset = 1, $row_offset = 1.

Возвращает

Значение колонки: число, строку, дату и т.д.

  • Если запрос получает значение одной колонки, то функция вернет это значение (без всяких массивов).
  • Если запрос получает одну строку и несколько колонок, то функция вернет значение первой колонки (ячейки).
  • Если запрос получает несколько строк и колонок, то вернется значение первой строки первой колонки.
  • Вернет NULL, если результата нет.
$wpdb->get_var( 'query', $column_offset, $row_offset );
query(строка)
Запрос который нужно выполнить. Можно установить этот параметр в значение null, тогда функция вернет результат последнего запроса, который был выполнен классом (сохранился в переменной).
column_offset(число)
Нужная колонка (отступ по колонкам). по умолчанию 0 - первая колонка.
row_offset(число)
Нужная строка (отступ по строкам). по умолчанию 0 - первая строка.

Примеры

#1. Выведем на экран количество пользователей

$user_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->users;" );
echo '<p>Количество пользователей равно: ' . $user_count . '</p>';

#2. Выведем на экран сумму значений определенных произвольных полей

// определяем произвольный ключ, который нужно посчитать
$meta_key = 'miles';
$allmiles = $wpdb->get_var($wpdb->prepare(
	"SELECT sum(meta_value) FROM $wpdb->postmeta WHERE meta_key = %s", $meta_key
));
echo '<p>Общее количество произвольных полей miles: '.$allmiles . '</p>';

#3. Набор выводов статистики блога

### Общее Количество авторов блога
function get_totalauthors() {
	global $wpdb;
	$totalauthors = intval($wpdb->get_var(
		"
		SELECT COUNT(ID) FROM $wpdb->users 
		LEFT JOIN $wpdb->usermeta ON $wpdb->usermeta.user_id = $wpdb->users.ID 
		WHERE $wpdb->users.user_activation_key = '' AND $wpdb->usermeta.meta_key = '{$wpdb->prefix}user_level' AND (meta_value+0.00) > 1
		"
	));

	return $totalauthors;
}

### Общее Количество постов
function get_totalposts() {
	global $wpdb;
	$totalposts = intval( $wpdb->get_var(
		"SELECT COUNT(ID) FROM $wpdb->posts WHERE post_type = 'post' AND post_status = 'publish'"
	));

	return $totalposts;
}

### Общее Количество страниц
function get_totalpages() {
	global $wpdb;
	$totalpages = intval( $wpdb->get_var(
		"SELECT COUNT(ID) FROM $wpdb->posts WHERE post_type = 'page' AND post_status = 'publish'"
	));

	return $totalpages;
}

### Общее Количество комментариев
function get_totalcomments() {
	global $wpdb;
	$totalcomments = intval( $wpdb->get_var(
		"SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = '1'"
	));

	return $totalcomments;
}

### Общее Количество комментаторов
function get_totalcommentposters() {
	global $wpdb;
	$totalcommentposters = intval($wpdb->get_var(
		"SELECT COUNT(DISTINCT comment_author) FROM $wpdb->comments WHERE comment_approved = '1' AND comment_type = ''"
	));

	return $totalcommentposters;
}

### Общее Количество ссылок
function get_totallinks() {
	global $wpdb;
	$totallinks = intval( $wpdb->get_var("SELECT COUNT(link_id) FROM $wpdb->links") );

	return $totallinks;
}
меню

get_row — выбор строки таблицы

Получает одну строку таблицы в виде объекта. Если результат запроса содержит больше одной строки, то функция получит только первую строку результата. Чтобы получить вторую строку из запроса, укажите параметр $row_offset = 1.

Возвращает

Объект/массив/null. Вернет null, если не удалось получить данные (запрашиваемых данных нет в бд).

$wpdb->get_row( 'query', $output_type, $row_offset );
query(строка)
Запрос который нужно выполнить.
output_type

Одна из трех констант. Может быть:

  • OBJECT - результат будет возвращен в виде объекта.
  • ARRAY_A - результат будет возвращен в виде ассоциативного массива.
  • ARRAY_N - результат будет возвращен в виде пронумерованного массива.
    По умолчанию OBJECT
row_offset(число)
Номер возвращаемой строки результата запроса. 0 - это первая строка (установлено по умолчанию)

Примеры

#1. Получим всю информацию о ссылке 10

$mylink = $wpdb->get_row( "SELECT * FROM $wpdb->links WHERE link_id = 10" );

// Теперь, свойства (переменные) $mylink - это названия
//колонок из таблицы $wpdb->links со значениями полей таблицы:
echo $mylink->link_id; // выведет на экран "10"

#2. С использованием константы:

$mylink = $wpdb->get_row( "SELECT * FROM $wpdb->links WHERE link_id = 10", ARRAY_A );

// результатом будет ассоциативный массив
echo $mylink['link_id']; // выведет на экран "10"

или

$mylink = $wpdb->get_row( "SELECT * FROM $wpdb->links WHERE link_id = 10", ARRAY_N );
// результатом будет пронумерованный массив
echo $mylink[1]; // выведет на экран"10"
меню

get_col — выбор столбца таблицы

Функция выбирает данные целой колонки таблицы и возвращает их в виде одномерного массива.

Если запрос вернул больше чем одну колонку (столбец), то функция вернет только данные первого столбца, остальное можно будет получить из переменной last_result. Можно указать отступ в $column_offset, чтобы получить данные не первого столбца, а, например, второго: $column_offset = 1.

Возвращает

Массив с данными запроса.  Пустой массив, если не удалось получить данные.

$wpdb->get_col( $query, $column_offset );
$query(строка)
Запрос который нужно выполнить. Можно установить этот параметр в значение null, тогда функция вернет результат последнего запроса, который был произведен.
$column_offset(число)
Флаг указывающий какую колонку возвращать. По умолчанию 0 (первая колонка).

Примеры

#1. Пример того как работает метод и что возвращает

Допустим у нас есть такой запрос на получение всех ревизий:

// получает одно поле
$revision_ids = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type = 'revision'" );

// или получаем все поля
$revision_ids = $wpdb->get_col( "SELECT * FROM $wpdb->posts WHERE post_type = 'revision'" );

/*
В обоих случаях вернется одинаковый массив. 
Когда указываются все поля, get_col получает только первое - это ID

Array(
	[0] => 106
	[1] => 101
	[2] => 102
	[3] => 103
	...
)
*/

#2. Выбор столбца из результатов запроса

Для этого примера представим, что у нас блог об автомобилях. Каждый пост описывает какой-либо автомобиль (например, Ford Mustang 1969 года). Для каждого поста предусмотрено по 3 произвольных поля: manufacturer (производитель), model(модель) и year(год выпуска). Этот пример выведет на экран заголовки постов, отфильтрованных по производителю (ford) и отсортированных по модели и году.

get_col здесь используется для того, чтобы получить массив ID всех записей, удовлетворяющих определенным критериям и отсортированных в нужном порядке. Затем через цикл foreach мы выводим заголовки по имеющимся у нас ID:

<?php
$meta_key1 = 'model';
$meta_key2 = 'year';
$meta_key3 = 'manufacturer';
$meta_key3_value = 'Ford';

$postids = $wpdb->get_col($wpdb->prepare("
SELECT      key3.post_id
FROM        $wpdb->postmeta key3
INNER JOIN  $wpdb->postmeta key1
			on key1.post_id = key3.post_id
			and key1.meta_key = %s
INNER JOIN  $wpdb->postmeta key2
			on key2.post_id = key3.post_id
			and key2.meta_key = %s
WHERE       key3.meta_key = %s
			and key3.meta_value = %s
ORDER BY    key1.meta_value, key2.meta_value",$meta_key1, $meta_key2, $meta_key3, $meta_key3_value)); 

if ($postids) {
  echo 'List of ' . $meta_key3_value . '(s), sorted by ' . $meta_key1 . ', ' . $meta_key2;
  foreach( $postids as $id ){
	$post = get_post(intval($id));
	setup_postdata($post);
	?>
	<p><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></p>
	<?php
  }
}

#3. Список постов которые имеют определенное произвольное поле (Color), но отсортированы они по значению другого произвольного поля (Display_Order).

$meta_key1 = 'Color';
$meta_key2 = 'Display_Order';

$postids = $wpdb->get_col($wpdb->prepare("
SELECT      key1.post_id
FROM        $wpdb->postmeta key1
INNER JOIN  $wpdb->postmeta key2
			on key2.post_id = key1.post_id
			and key2.meta_key = %s
WHERE       key1.meta_key = %s
ORDER BY    key2.meta_value+(0) ASC",
		 $meta_key2,$meta_key1)); 

if ($postids) {
  echo 'List of '. $meta_key1  . ' posts, sorted by ' . $meta_key2 ;
  foreach ($postids as $id) {
	$post=get_post(intval($id));
	setup_postdata($post);?>
	<p><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></p>
	<?php
  }
}
меню

get_results — выбор нескольких строк таблицы

Получает различные данные из нескольких строк таблицы. Функция возвращает результат запроса в массиве, каждый элемент которого представляет собой результат функции get_row, т.е. в каждом элементе массива находятся все запрашиваемые данные одной строки таблицы или строки из полей указанных в запросе.

Возвращает

Массив/null.

  • Результат запроса в виде массива объектов (OBJECT, OBJECT_K), массива массивов (ARRAY_A, ARRAY_N).
  • Вернет пустой массив: array() — когда строк по запросу не найдено или ошибка запроса.
  • Вернет NULL — когда запрос пустая строка или передан неправильный тип вывода ($output_type).
$wpdb->get_results( 'query', output_type );
query(строка)
Запрос который нужно выполнить. Можно установить этот параметр в значение null, тогда функция вернет результат последнего запроса, который был произведен.
output_type(строка)

Флаг указывающий как нужно вернуть данные. По умолчанию OBJECT. есть 4 варианта:

  • OBJECT - вернет массив объектов с числовыми ключами - элементы массива будут объекты полученных данных.
  • OBJECT_K - похож на предыдущий, только в индексах главного массива будут значения первой колонки результата запроса. Обратите внимание, если в индекс будут попадать одинаковые значения, то данные будут стирать друг друга...
  • ARRAY_A - вернет нумерованный двумерный массив, каждый вложенный элемент которого будет ассоциативным массивом, в котором ключом будет название колонки.
  • ARRAY_N - вернет нумерованный двумерный массив, каждый вложенный элемент которого будет так же нумерованным массивом.

Примеры

#1. Получим ID и заголовки черновиков, ID автора которых равен 5 и выведем на экран заголовки постов.

$fivesdrafts = $wpdb->get_results( "SELECT ID, post_title FROM $wpdb->posts WHERE post_status = 'draft' AND post_author = 5" );

foreach ( $fivesdrafts as $fivesdraft ) {
	echo $fivesdraft->post_title;
}

#2. Выведем на экран ссылки на черновики автора с ID = 5

<?php
$fivesdrafts = $wpdb->get_results( "SELECT * FROM $wpdb->posts WHERE post_status = 'draft' AND post_author = 5");
if( $fivesdrafts ) :
	foreach( $fivesdrafts as $post ){
		setup_postdata($post);
		?>
			<h2><a href="<?php the_permalink(); ?>" rel="bookmark"
				title="Permanent Link to <?php the_title(); ?>"><?php the_title(); ?></a></h2>
		<?php
	}
else :
?>
	<h2> Не найдено</h2>
<?php endif; ?>

#3. Пример сложного запроса с GROUP BY (отзывов в WooCommerce)

/**
* Возвращает результаты рейтинга (отзывов в WooCommerce) сгрупированный по оценкам
* 
* @param int $post_id идентификатор поста
* 
* @return array массив объектов, где каждый объект - сгрупированные данные по одной из оценок
*/
function get_cnt_rating_reviews_one_product( $post_id ){
	global $wpdb;

	return $wpdb->get_results( $wpdb->prepare(
		"
		SELECT COUNT(meta.meta_id) as num, meta.meta_value
		FROM $wpdb->comments as comments
		INNER JOIN $wpdb->commentmeta as meta ON comments.comment_ID = meta.comment_id
		WHERE comments.comment_post_ID = %d AND meta_key = 'rating'
		GROUP BY meta.meta_value;
		",
		$post_id
	) );

}

// использование
get_cnt_rating_reviews_one_product( 4350 );
меню

insert — вставка новой записи (строки) в таблицу

Вставляет указанные данные в указанную таблицу.

$wpdb->insert() включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']...

Возвращает

true/false.

  • true — при успешной записи данных;
  • false — если данные не были вставлены в таблицу.

После добавления данных созданное значение AUTO_INCREMENT можно получить в переменной: $wpdb->insert_id

$wpdb->insert( $table, $data, $format );
table(строка)
Название таблицы в которую будем вставлять данные.
data(строка)
Данные которые нужно вставить ('колонка куда вставлять' => 'что вставлять').
format(строка)

Формат данных который будет ассоциирован с указанными значениями в параметре $data. Если не указать, то для всех значений $data будет указан формат строки. При указании формата, WordPress переводит переданные данные в указанный формат перед тем, как сохранить данные в базу данных. Возможные форматы данных:

  • %s - строка
  • %d - целое число
  • %f - дробное число

Примеры

#1. Вставим в значение в строку таблицы table, где первое значение строка, а второе число:

$wpdb->insert(
	'table',
	array( 'column1' => 'value1', 'column2' => 123 ),
	array( '%s', '%d' )
)
меню

update — обновление записи (строки) в таблице

Возвращает

Число/false.

  • число - сколько строк было обработано
  • 0 - запрос был выполнен корректно, но ни одна строка не была обработана.
  • false - запрос провалился или ошибка запроса.

Так как возвращается 0, если никакие поля не были обновлены (изменены), но запрос был выполнен корректно, проверку результата запроса на ошибку нужно делать с учетом типа возвращаемых данных $res === false.

$wpdb->update() включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']...

$wpdb->update( $table, $data, $where, $format = null, $where_format = null );
table(строка)
Название таблицы, которую нужно обновить.
data(массив)
Данные, которые нужно обновить ('название колонки' => 'новое значение').
where(массив)
Ассоциированный массив с условием для замены (WHERE) ( 'название колонки' => 'чему равно').
format(массив/строка)
Формат данных который будет ассоциирован с указанными значениями в параметре $data.
По умолчанию: null
where_format(массив/строка)
Формат данных который будет ассоциирован с указанными значениями в параметре $where. Возможные значения формата: %s - строка; %d - любое число; %f - FLOAT число. Если не указать формат, то все значения в WHERE будут обработаны как строки.
По умолчанию: null

Примеры

#1. Обновим строку ID которой равен 1

Значение первой колонки строка, значение второй колонки число:

$wpdb->update( 'table',
	array( 'column1' => 'value1', 'column2' => $_GET['val'] ),
	array( 'ID' => 1 )
);

#2. Тоже самое с указанием типов передаваемых данных

$wpdb->update( 'table',
	array( 'column1' => 'value1', 'column2' => $_GET['val'] ),
	array( 'ID' => 1 ),
	array( '%s', '%d' ),
	array( '%d' )
);
меню

replace — замена строки

Обновляет или создает строку в таблице. Если строка с указанным ключом PRIMARY KEY уже есть все остальные указанные поля будут обновлены у строки. Если такой строки в таблице еще нет, то функция вставит (insert) новую строку.

После вставки, ID созданный для колонки AUTO_INCREMENT можно получить в свойстве $wpdb->insert_id.

PRIMARY KEY может состоять из нескольких ключей. В этом случае при поиске для замены или для создания новой строки всегда будут использоваться колонки указанные в PRIMARY KEY. Например PRIMARY KEY состоит из двух колонок ID и type, т.е. ключ при создании указывался так: PRIMARY KEY (ID,type). Тогда для замены существующей строки в $data нужно указать значение этих двух колонок. Если например указать ID и не указать type, то существующая строка не будет найдена и будет создана новая строка с указанным ID и пустым значением type. Все остальные поля кроме полей из PRIMARY KEY в поиске существующей строки не участвуют.

Если длина строки в параметре $data больше чем допускается в ячейке таблицы MySQL, вставка провалиться и функция вернет false. При этом в свойство $wpdb->last_error сообщение об ошибке НЕ будет записано!

Поэтому очень важно убедиться, что вставляемые данные подходят под размер ячеек (колонок). Иначе просто ничего не произойдет и вы не увидите никаких ошибок или предупреждений...

Всегда заменяются все поля таблицы. Например, если у таблицы есть поля primary, one, two, three, а мы указали в параметре $data только primary, one, то поля two и three получат дефолтные значения, даже если там уже были данные.

Возвращает

Число (кол-во обработанных строк), 0 или false.

  • число - сколько строк было обработано (удалено или вставлено). Если была замена (обновление), то для каждой обновленной строки вернется +2 (удаление и вставка). Если была просто вставка, то для каждой вставленной строки вернется +1.
  • 0 - запрос был выполнен корректно, но ни одна строка не была обработана.
  • false - запрос провалился - ошибка запроса. Или когда невозможно заменить или создать новую строку в таблице.

Вернет количество затронутых строк - это сумма вставленных и удаленных строк. Если вернулось 1 при замене одной строки - это значит что одна строка была вставлена и ни одна не была удалена. Если число больше 1 - это значит, что одна или больше строк были удалены перед вставкой новой строки.

Иногда может произойти замена сразу нескольких старых строк - это когда заменяемые данные совпадают с существующими, а уникальный индекс у старых строк при этом разный.

$wpdb->replace( $table, $data, $format = null );
$table(строка)
Название таблицы, в которой нужно заменить данные.
$data(массив)
Данные, которые нужно заменить/вставить: 'название колонки' => 'новое значение'.
$format(массив/строка)
Формат данных который будет ассоциирован с указанными значениями в параметре $data. Если не указать, то все поля будут считаться строками, если другого не указано в свойстве wpdb::$field_types. Возможные значения формата: %s - строка, %d - число (любые числа), %f - дробное число (с плавающей точкой).
По умолчанию: null

Примеры

Заменим строку с главным ключом ID = 1 (подразумевается что таблица состоит из трех колонок ID, column1, column2).

Строка будет добавлена если её нет или полностью обновлена если она есть. Тут важно указывать значения для всех полей, потому что поля без значений получат дефолтные, даже если в них до этого были данные.

$wpdb->replace( 'table_name', array( 
	'ID'      => 1,
	'column1' => 'value1', 
	'column2' => 123 
) );
меню

delete — удаление строки из таблицы

Удаляет строки из таблицы по переданному в параметр $where условию.

$wpdb->delete() включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']...

Возвращает

Число/0. Число удаленных строк или 0, если ничего не удалено.

$wpdb->delete( $table, $where, $where_format = null );
$table(строка) (обязательный)
Название таблицы.
$where(массив) (обязательный)
Массив условий, по которым будут выбираться строки для удаления. Например:
array('id' => 25, 'title' => 'книги') удалит строку с полями  id=25 и title=книги.
$where_format(строка/массив)
Формат данных который будет ассоциирован с указанными значениями в параметре $where. Возможные значения формата: %s - строка; %d - любое число; %f - FLOAT число. Если не указать формат, то все значения в WHERE будут обработаны как строки.
По умолчанию:  null

Примеры

// Удалим строку с полем ID=1 из таблицы table
$wpdb->delete( 'table', array( 'ID' => 1 ) );

// Укажем формат значения $where
$wpdb->delete( 'table', array( 'ID'=>'1' ), array( '%d' ) ); // 1 будет обработано как число (%d)...
меню

prepare — защита запроса от SQL инъекций

В SQL есть такое понятие как «инъекция» (внедрение в запрос SQL кода). Cделать его можно, когда в запрос передаются динамические данные. Например, допустим в запрос передаётся значение input поля, тогда в поле формы можно указать данные, которые в итоге станут частью SQL запроса. Так можно внедриться в запрос и что-нибудь испортить или просто нарушить код самого запроса. Выглядит это так:

$sql = "SELECT * FROM table WHERE id = '$var'";

Теперь, если $var = 2' AND id = (DROP TABLE table2) то в результате запрос получиться такой:

SELECT * FROM table WHERE id = '2' AND id = (DROP TABLE table2)

Таким образом можно внедриться в сам запрос и изменить его.

Чтобы этого не произошло запросы с передаваемыми в них переменными нужно обрабатывать методом wpdb::prepare():

$sql = $wpdb->prepare( "SELECT * FROM table WHERE id = %s", $var );
Синтаксис
$sql = $wpdb->prepare( 'query' param1, param2... );
query(строка)

Запрос. В нем можно использовать заменители:

  • %d - число
  • %s - строка
  • %f - дробное число (число с плавающей точкой, с версии 3.3).

Использование знака % в любых других случаях может вызывать ошибку, поэтому его нужно претворять еще одним %. Например, чтобы использовать команду LIKE %вхождение%, её нужно записать так: LIKE %%вхождение%%.

ВАЖНО! Плейсхолдеры вида %1$d запрещены с версии 4.8.2 в целях безопасности. Запросы с ними Работать не будут. Например:

$wpdb->prepare( 'SELECT * FROM table WHERE field = %1$s OR field2 = %1$s', 'foo' );
//> SELECT * FROM table WHERE field = %1$s OR field2 = %1$s

До версии 4.8.2 можно было делать так:

$wpdb->prepare('SELECT * FROM table WHERE field = %1$d OR field2 = %1$d', 2 );
//> SELECT * FROM table WHERE field = 2 OR field2 = 2

Но для строк в таком случае крайние скобки не ставятся, их нужно ставить в самом запросе:

$wpdb->prepare("SELECT * FROM table WHERE field = '%1$s' OR field2 = '%1$s'", '"str' );
//> SELECT * FROM table WHERE field = "\"str" OR field2 = "\"str"
param1(строка/число/массив)

Переменные, которые будут использованы при замене плейсхолдеров в запросе.

  • Переменные можно указать через запятую, как в php функции sprintf( 'query', param1, param2 ).
  • Переменные можно передать в массиве: $wpdb->prepare( 'query', [ param1, param2 ] ).
param1...
Еще переменные...

Заметка: с версии WP 3.5 wpdb::prepare() обязательно должны быть переданы минимум 2 параметра: запрос и значение переменной, иначе появится php ошибка.

esc_sql()

Кроме метода $wpdb->prepare() запрос можно очистить функцией esc_sql(). Но «prepare» предпочтительнее, потому что исправляет некоторые ошибки форматирования.

$name   = esc_sql( $name );
$status = esc_sql( $status );

$wpdb->get_var( "SELECT something FROM table WHERE foo = '$name' and status = '$status'" );

ВАЖНО! После esc_sql() очищенную строку можно использовать только внутри кавычек '' или "". Т.е. правильно писать field = '$value', а не field = $value, где $value = esc_sql( $value );

Примеры

#1. Добавим произвольное поле к посту 10:

$metakey   = "'крах' БД";
$metavalue = "WordPress может 'сломать' Базу Данных если не экранировать запрос.";

$wpdb->query(
	$wpdb->prepare(
		"INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value ) VALUES ( %d, %s, %s )",
		10,
		$metakey,
		$metavalue
  )
);

Как видно из примера, с prepare() нет необходимости заботиться об экранировании кавычек и прочего, что может навредить запросу.

#2. Такой же пример

Только тут все переменные передаются во втором параметре в виде массива:

$metakey = "'крах' БД";
$metavalue = "WordPress может 'сломать' Базу Данных если не экранировать запрос.";

$wpdb->query(
	$wpdb->prepare(
		"INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value ) VALUES ( %d, %s, %s )",
		array(
			10, 
			$metakey, 
			$metavalue
		) 
  )
);

Передавать параметры в виде массива, может быть полезно, когда мы заранее не знаем количество аргументов, которые нужно будет передать.

меню

esc_like — очистка LIKE строки

Чтобы подготовить строку для использования в LIKE части SQL запроса, используйте $wpdb->esc_like( $text ):

global $wpdb;
$link = $wpdb->esc_like( $link ); // подготовим строку для LIKE аргумента
$link = esc_sql( $link );         // очистим переменную
$link = '%' . $link . '%';        // создадим полную переменную поиска LIKE

// найдем комментарии в тексте или ссылке автора, есть указанная ссылка
$spammy = $wpdb->query("SELECT comment_approved FROM $wpdb->comments 
	WHERE (comment_content LIKE '$link' OR comment_author_url LIKE '$link')
		AND comment_approved = 'spam' 
	LIMIT 1;"
);

Короткая запись с prepare()

global $wpdb;

$link = '%' . $wpdb->esc_like( $link ) . '%';

$comment = $wpdb->get_row( $wpdb->prepare(
	"SELECT * FROM $wpdb->comments WHERE comment_author_url LIKE %s LIMIT 1", $link
 ) );

Функция like_escape( $string ) запрещена с версии 4.0. Используйте вместо нее $wpdb->esc_like( $string )

меню

show/hide/print_error — показать или спрятать ошибки SQL

Есть возможность управлять ошибками, включать или выключать показ ошибок для последнего запроса:

$wpdb->show_errors(); // включит показ ошибок
$wpdb->hide_errors(); // выключит показ ошибок

Так же можно вывести на экран саму ошибку, если таковая имеется:

$wpdb->print_error();

get_col_info — получить информацию о колонке

Получает информацию о колонках последнего запроса. Функция вернет массив с информацией об определенных в запросе колонках. Если в запросе колонки не определены, то функция вернет информацию о всех колонках таблицы. Это может пригодится, когда был возвращен объект, о данных которого мы ничего не знаем.

Работает на основе кэша, поэтому сначала нужно запустить запрос с помощью get_resiults(), get_col(), get_var() и т.д. а потом вызвать wpdb::get_col_info() которая вернет данные колонок последнего запроса.

$wpdb->get_col_info( $type, $offset );
$type(строка)

В этом параметре указывается какую информацию нам нужно получить. По умолчанию: name. Вот список возможных вариантов:

  • name - название колонки.
  • table - название таблицы к которой принадлежит колонка.
  • max_length - максимальная длинна данных колонки
  • not_null - 1 если ячейка колонки не может принимать значение NULL
  • primary_key - 1 если колонка является первичным ключом
  • unique_key - 1 если ячейки колонки должны быть всегда уникальны
  • multiple_key - 1 если ячейки колонки могут быть не уникальны
  • numeric - 1 если колонка содержит числовые данные
  • blob - 1 если колонка содержит данные типа BLOB (двоичные данные)
  • type - тип колонки
  • unsigned - 1 если колонка имеет тип данных UNSIGNED
  • zerofill - 1 если колонка имеет тип данных ZEROFILL

По умолчанию: name

$offset(число)

Указатель, информацию о какой колонке нужно получить:

  • Если указать -1, то будут получена информация о всех колонках в виде массива. По умолчанию.

  • Если указать 0 или больше то будет возвращена информация об указанной колонке, где 0 - первая колонка, 1 - вторая и т.д.
    По умолчанию: -1

Примеры

global $wpdb, $table_name;

$wpdb->show_errors(); // включим показ SQL ошибок

$results   = $wpdb->get_results( "SELECT * FROM $table_name" );
$cols_name = $wpdb->get_col_info('name');

echo 'Колонки таблицы `'. $table_name .'`:';
if( is_array($cols_name) ){
	foreach( $cols_name as $name )
		echo " $name";
}
else {
	echo 'Ошибка, нет данных: ';
	$wpdb->print_error();
}

$wpdb->hide_errors(); // выключим показ SQL ошибок
меню

flush — сброс кэша

Можно сбросить последние сохраненные данные в свойствах класса:

$wpdb->flush();

Эта команда очистит следующие свойства (переменные): $wpdb->last_result, $wpdb->last_query и $wpdb->col_info.

Свойства (переменные) класса

$show_errors
Показывать ошибки или нет, когда возвращается результат.
По умолчанию: true (да)
$suppress_errors
Подавлять ли ошибки в процессе построения запроса.
$last_error
Последняя ошибка из любого запроса.
$num_queries
Количество запросов которые выполняются.
$num_rows
Количество строк возвращенных последним запросом.
$rows_affected
Сохраняет число задействованных строк из последнего запроса
$insert_id
Идентификатор (ID) сгенерированный последним запросом, для SQL параметра AUTO_INCREMENT
$last_query
Последний запрос, который был выполнен классом.
$last_result
Результат последнего запроса.
$func_call
Текстовое описание последнего вызова query/get_row/get_var
$queries
Можно сохранить все запросы которые были сделаны к БД и их время выполнения, для этого нужно определить константу SAVEQUERIES как TRUE (например в config.php). По умолчанию она выключена (false). После того как эта константа включена в эту переменную будут собираться все запросы в виде массива данных.
$col_info
Информация о колонках последнего запроса.
$prefix
Префикс таблиц БД определенный для WordPress. Может пригодиться для мульти-сайтов.
$base_prefix
Префикс базовой таблицы WordPress. В мультисайте для сайтов сети префик отличается. Тут храниться префикс таблиц основного сайта сети.
$ready
Логический. Готов ли класс к выполнению запросов.
$blogid
Идентификатор текущего блога.
$siteid
ID сайта.
$tables
Список названий таблиц сайта (без префикса).
$global_tables
Глобальные таблицы: 'users', 'usermeta'
$ms_global_tables
Глобальные таблицы в режиме MU: 'blogs', 'signups', 'site', 'sitemeta', 'sitecategories', 'registration_log', 'blog_versions'
$collate
Режим сопоставления (сравнивания) данных в колонках базы данных.
меню

Таблицы Базы Данных WordPress

Схема базы данных WordPress - структура всех таблиц

Записи - подробнее

$wpdb->posts
Таблица куда записываются посты, постоянные страницы, произвольные типы записей, вложения и т.п.
$wpdb->postmeta
Таблица с записями о произвольных полях.
Мета поля для таблицы $wpdb->postmeta.

Пользователи

$wpdb->users
Таблица с данными о зарегистрированных пользователях.
$wpdb->usermeta
Дополнительная информация о пользователях, такая как Имя, Ник, права и прочее.
Мета поля для таблицы $wpdb->users.

Комментарии

$wpdb->comments
Таблица с записями комментариев.
$wpdb->commentmeta
Мета поля для таблицы $wpdb->comments.

Таксономии - подробнее

$wpdb->terms
Таблица содержащая в себе информацию о названии категорий, меток, категорий ссылок и других таксономий.
$wpdb->termmeta
Таблица содержащая в себе дополнительные поля для таблицы $wpdb->terms.
$wpdb->term_taxonomy
Таблица с информацией о различных таксономиях их описание.
$wpdb->term_relationships
Таблица связывающая таксономии с контентом (постами, записями и т.п.)

Остальное

$wpdb->links
Таблица с записями ссылок.
$wpdb->options
Таблица опций (настроек).

Таблицы в MU (Multisite) сборке

$wpdb->blogs
Все сайты подсети.
$wpdb->blog_versions
Содержит текущую версию базы данных каждого сайта. Данные обновляются при обновлении БД для каждого сайта сети.
$wpdb->registration_log
Содержит данные администраторов сайтов, которые создаются при создании сайтов.
$wpdb->signups
Содержит пользователей, которые были зарегистрированы через базовую регистрацию WordPress со страницы: Администрация > Супер Админ > Настройки.
$wpdb->site
Содержит, адреса основных сайтов.
$wpdb->sitemeta
Данные сайтов: различные опции, включая администратора сайта.
$wpdb->users
Список пользователей всех сайтов сети. Это общая таблица пользователей для всей сети. Это привычная таблица, только в мультисайт версии добавляются еще 2 поля: spam и delete.
$wpdb->usermeta
Содержит мета-данные пользователей. Настройки пользователя для разных сайтов сети отличаются префиксом в индексе ключа. Например роли хранятся в таких метаполях: wp_capabilities, wp_1_capabilities, wp_2_capabilities ...
Базовые таблицы каждого сайта сети
Таблицы сайта сети: wp_posts, wp_options и т.д.. Для каждого сайта сети создаются одинаковые таблицы, но с разным префиксом, например: wp_options, wp_1_options, wp_2_options...

Много быстрых и качественных подписчиков на свой канал в Телеграмм можно приобрести на сервисе https://doctorsmm.com/. Здесь, при покупке ресурса, Вы сможете подобрать для себя наиболее приемлемый формат аудитории и скоростной режим, в котором подписчики будут поступать к Вам в сообщество за более чем приятные глазу цены. Теперь развиваться в Телеграмме стало еще проще, быстрее и дешевле!

Код wpdb: wp-includes/wp-db.php VER 5.1.1

Код слишком большой, смотрите его на отдельной странице...

Cвязанные функции

Из раздела: Классы

117 комментов
Полезные 11 Вопросы 2 Все
  • $wpdb->insert вставляет только одну строку, или способен на большее? Нужно вставить 5000+ строк..

    Ответить2.8 года назад #
    • Kama7364

      Только одну, вызовете его 5000 раз в цикле, желательно не в один вызов PHP а то 5к многовато... Или используйте $wpdb->query и там оформите запрос вручную...

      Ответить2.8 года назад #
  • Opk

    А $wpdb->update() получается уже включает в себя защиту от SQL инъекций? т.е. нет необходимости как то прикручивать prepare() ? иначе что делают последние 2 передаваемых ей параметра - $format, $where_format ?
    Подскажите плз..

    Ответить2.7 года назад #
    • Kama7364

      Да включает, чистить не нужно...

      Последние параметры это не обязательные, когда нужно по каким-то причинам указать тип передаваемого параметра. Чаще всего их можно не указывать, тогда будут переданы все параметры как строки, а дальше БД уже разберется...

      Ответить2.7 года назад #
  • campusboy3279 cайт: www.youtube.com/c/wpplus

    А где почитать инфу про взаимодействие со 2 базой? Делаю конверт с ДЛЕ на ВП. Неужели без плагина никак?

    Ответить2.6 года назад #
    • campusboy3279 cайт: www.youtube.com/c/wpplus

      Пока использую medoo.in, но хотелось бы на родном WP всё делать без всяких "примесей".

      Ответить2.6 года назад #
      • Просто работать со 2ой базой? В статье вроде бы есть про это. Создать $wpdb с новыми параметрами..

        Вот так подключаюсь ко второй базе (самописной) для копирования данных:

        // подключаемся к базе данных
        $wpdb2 = new wpdb( DB_USER, DB_PASSWORD, 'имя БД', DB_HOST );
        // если подключение установлено без ошибок
        if (!isset($wpdb2->error)){
        	$dirct = $wpdb2->get_results( $wpdb2->prepare("query", $data) ), "ARRAY_A");
        }

        Проверка isset($wpdb2->error) - это собственная придумка, возможно, есть более правильный способ.

        DB_USER, DB_PASSWORD, DB_HOST - константы из конфигур. WordPress`а, у меня 2я база лежит на том же сервере

        2
        Ответить2.6 года назад #
        • campusboy3279 cайт: www.youtube.com/c/wpplus

          В статье рекомендуется плагин для использования нескольких баз. А как сделать подключение ко 2 базе методами WP я не увидел, хотя может это кому-то и очевидная вещь. Спасибо за подсказку! Сейчас попробую. А проверка на ошибку вполне нормальная, чего нет-то? 1 строка - что ещё надо smile

          Ответить2.6 года назад #
          • Сейчас $wpdb2->error появляется только когда есть ошибка. Нет гарантии, что в будущем не обновят $wpdb и error не будет висеть постоянно.
            Если данные для подключения к базе2 вводит пользователь, в случае неудачи можно выдать ему ошибку в html формате

                $wpdb2->error->errors[db_select_fail][0]
            1
            Ответить2.6 года назад #
        • Kama7364

          Я думаю не стоит делать эту проверку вообще

          isset($wpdb2->error)

          Потому что, если подключения не будет, wpdb сам выкенет ошибку и в ней покажет, в какой она строке, какой запрос и т.д. Так же, раз есть код для второй БД, то наверное она должна работать, если нет, то лучше увидеть ошибку, что что-то не так...

          Но если такая проверка все же нужна, то делать её нужно так:

          if( !empty( $wpdb2->error ) )
          	wp_die($wpdb2->error);

          П.С. спасибо за дискуссию! Добавлю сейчас вариант подключения к БД в статью.

          Ответить2.6 года назад #
  • campusboy3279 cайт: www.youtube.com/c/wpplus

    Не даёт покоя тема. Вчера весь день убил на поиск инфы всякой, но её мало и решения найдено не было (не уверен, что оно вообще существует). Итак, такой код:

    function convert_posts_dle_to_wp(){
    
      $go_time = microtime(true);
    
      global $dledb;
      $cnt = 0;
    
      printf( '<br>%s сек - %s Mb', round(microtime(true) - $go_time, 5) , round(memory_get_usage()/1024/1024,2) );
    
      $dle_posts_id = $dledb->get_col( "SELECT id FROM dle_post LIMIT 1000" );
    
      printf( '<br>%s сек - %s Mb<br>', round(microtime(true) - $go_time, 5) , round(memory_get_usage()/1024/1024,2) );
    
      $cnt=0;
    
      foreach ( $dle_posts_id as $dle_post_id ){
    
    	$dle_posts_data = $dledb->get_row( "SELECT * FROM dle_post WHERE id={$dle_post_id->id}" );
    
    	unset ( $dle_posts_data );
    
    	printf( '<br>%s. %s сек - %s Mb', ++$cnt, round(microtime(true) - $go_time, 5) , round(memory_get_usage()/1024/1024,2) );
    
      }
    
      printf( '<br>%s сек - %s Mb<br>', round(microtime(true) - $go_time, 5) , round(memory_get_usage()/1024/1024,2) );
    
    }

    Замеры:

    0 сек - 19.4 Mb
    0.017 сек - 19.72 Mb
    
    1. 0.018 сек - 19.49 Mb
    2. 0.018 сек - 19.49 Mb
    3. 0.018 сек - 19.5 Mb
    .
    .
    .
    998. 0.382 сек - 20.02 Mb
    999. 0.382 сек - 20.02 Mb
    1000. 0.383 сек - 20.02 Mb
    
    0.383 сек - 20.02 Mb

    Вопрос: куда расходуется память?

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

    К примеру, если этот код повторить при следующих условиях, то результаты такие:

    Создание рубрик сначала (складирование в стат. переменную), создание записей + тегов на лету
    Записей 8887
    Рубрик 5
    Меток 513
    Время выполнения 365,9 сек
    Потребляемая память 160,61 Мб

    Хотя по коду я сначала беру только ID записей с БД от DLE, это небольшой объём данных, а затем в цикле подставляю в запрос, получаю уже данные по 1 записи с DLE и посредством insert_post_to_wp вставляю в WP. По идее старый запрос (и выделенная под него память) должен удаляться, но они как будто складируются, но куда? Я уже и $wpdb->flush() использовал, и unset где только можно, но это никак не влияет на рост потребляемой памяти.

    А первоначально скрипт работал по другому (тогда-то я и обратил внимание на эту проблему): он сразу пачкой брал ВСЕ записи из DLE, помещал в переменную, а затем через foreach отправлялся в базу DLE с помощью insert_post_to_wp. Тогда логично, что переменная с результатами запроса всех записей может занимать такой объём. Потому я переписал скрипт (код его выше), чтобы бралась 1 запись с DLE и отправлялась в WP. Я наивно предполагал, что старый запрос будет перезатираться новым, но нарастающее потребление памяти что в 1 варианте, что во 2 идентичное!

    Буду очень признателен, если кто поделиться своими мыслями, что происходит в данных случаях и можно на это как-либо повлиять.

    Ответить2.6 года назад #
    • Kama7364

      Если ты все правильно делаешь и функцию convert_posts_dle_to_wp() вызывается один раз. То это все странно и похоже что-то с серваком...

      Вот тестанул как работает memory_get_usage() на этом серваке. Память чиститься, ничего лишнего не утекает...

      Попробуй у себя запусти эти 2 кода, что покажет, я щас на opanserver запущу тоже...

      Пример 1:

      // Пример memory_get_usage
      
      $a = '';
      echo memory_get_usage() / 1024 . "\n"; // 5404
      
      $a = str_repeat("Hello", 999);
      echo memory_get_usage() / 1024 . "\n"; // 5412
      
      $a = str_repeat("Hello", 9999);
      echo memory_get_usage() / 1024 . "\n"; // 5456
      
      $a = '';
      echo memory_get_usage() / 1024 . "\n"; // 5404

      Пример 2:

      function memory_usage_test(){
      	global $wpdb;
      
      	// точка отсчета
      	$start_memory = memory_get_usage();
      
      	$fn__memory_get_usage = function() use ($start_memory){
      		echo round( (memory_get_usage() - $start_memory) / 1024, 0) ." KB\n";
      	};
      
      	$fn__memory_get_usage(); // 1 KB
      
      	$pids = $wpdb->get_col("SELECT ID FROM $wpdb->posts LIMIT 500" );
      
      	$fn__memory_get_usage(); // 276 KB
      
      	foreach( $pids as $id ){
      
      		$temp = $wpdb->get_row( "SELECT * FROM $wpdb->posts WHERE ID = $id" );
      
      		$fn__memory_get_usage(); // 20-80 KB
      	}
      }
      
      memory_usage_test();
      
      /* Получаем:
      	1 KB
      	276 KB
      	30 KB
      	22 KB
      	.
      	.
      	30 KB
      	25 KB
      */

      И тут еще мне не понятно куда делась память от $pids... Это как-то так PHP работает...

      Обновление: запустил на локалке openserver результат второго кода такой:

      1 KB
      22 KB
      14 KB
      14 KB
      .
      .
      14 KB
      14 KB
      
      Ответить2.6 года назад #
      • campusboy3279 cайт: www.youtube.com/c/wpplus

        Так-с, 1 тест, результаты:

        19735.59375
        19740.46875
        19784.4140625
        19735.59375

        Второй тест:

        1 KB
        165 KB
        50 KB
        50 KB
        .
        .
        .
        50 KB
        50 KB
        

        Это всё на локалке.

        Ответить2.6 года назад #
        • Kama7364

          Ну все значит нормально с серваком. Попробуй свой код который тут выложил переписать без printf, может в нем проблема, может не чистит эта функция память, хотя это было бы странно! Как не крути, проблема где-то в коде, попробуй пошаманить туда сюда...

          Ответить2.6 года назад #
          • campusboy3279 cайт: www.youtube.com/c/wpplus

            Устал шаманить, уже 2 дня изучаю тему. Пора взять перерыв smile Я без printf пробовал. Чисто вначале замерял, затем после всех обработок, в целом всего 2 раза. Это никак не влияло на используемую память. Придётся с этим мирится.

            Ответить2.6 года назад #
          • Kama7364

            Щас проверил, не в ней дело... Остается одно $dledb сохраняет результат запроса у себя где-то. Ты там нигде не ставил константы, чтобы все запросы сохранялись? Что-то типа такого не делал:

            Можно сохранить все запросы которые были сделаны к БД и их время выполнения, для этого нужно определить константу SAVEQUERIES как TRUE (например в config.php). По умолчанию она выключена (false). После того как эта константа включена в эту переменную будут собираться все запросы в виде массива данных.

            Или может плагин типа query monitor стоит?

            Ответить2.6 года назад #
            • campusboy3279 cайт: www.youtube.com/c/wpplus

              Всё с этим в порядке, кеш отключен, плагины все отрубил. Всё чисто. Сравнивал твой и свой код - вроде же принцип 1 в 1, но! Сейчас нашёл у себя ошибку:

              $dle_posts_id = $dledb->get_col( "SELECT id FROM dle_post LIMIT 1000" );
              
              foreach ( $dle_posts_id as $dle_post_id ){
              	$dle_posts_data = $dledb->get_row( "SELECT * FROM dle_post WHERE id={$dle_post_id->id}" );
              }
              

              Почему я $dle_post_id->id поставил? А хрен знает! Первый запрос ведь просто массив возвращает, без объектов. И самый прикол - работало laugh ошибок не вылетало. Сейчас просто вписал $dle_post_id - память как замороженная! С синтетическим тестом разобрались. Буду дальше тестить, теперь попробую записывать статьи. Будет что интересное - отпишусь (я уж наверное задолбал тут всех).

              Ответить2.6 года назад #
              • Kama7364

                я уж наверное задолбал тут всех

                Я так не думаю, всем пофиг, а кому нет, те может чего полезного увидят. И вообще, так думать неправильно, но я тебя понимаю...

                Ответить2.6 года назад #
              • campusboy3279 cайт: www.youtube.com/c/wpplus

                Итак, вроде "синтетический тест" получился, утечек памяти не было, даже с 10к циклов. Я прям радовался, теперь дело осталось за малым, в цикл засунуть wp_insert_post и начать грузить в базу статейки. Всё тот же код, что у Тимура в примере 2, у меня тоже самое в принципе. Добавляем в цикл wp_insert_post:

                wp_insert_post( array( 'post_title'    => 'Заголовок записи '.$cnt, 'post_content'  => str_repeat("hellohello", 100000) ) );

                Где $cnt номер цикла, а str_repeat генерирует контент в 1Мб

                Цикл из 10 повторений даёт такую картину:

                0001 - 0.068 сек - 20.279 Mb
                0002 - 0.133 сек - 21.235 Mb
                0003 - 0.191 сек - 22.192 Mb
                0004 - 0.248 сек - 23.148 Mb
                0005 - 0.303 сек - 24.105 Mb
                0006 - 0.366 сек - 25.061 Mb
                0007 - 0.428 сек - 26.018 Mb
                0008 - 0.484 сек - 26.974 Mb
                0009 - 0.544 сек - 27.931 Mb
                0010 - 0.600 сек - 28.887 Mb
                1
                Ответить2.6 года назад #
              • campusboy3279 cайт: www.youtube.com/c/wpplus

                Вроде получилось! Уже ни в чём не уверен, но цифры говорят сами за себя. Не буду параметры писать у wp_insert_post, они остались теже. На старт, внимание, марш!

                wp_insert_post();
                wp_cache_flush();

                0001 - 0.063 сек - 19.254 Mb
                0002 - 0.123 сек - 19.254 Mb
                0003 - 0.184 сек - 19.254 Mb
                0004 - 0.245 сек - 19.254 Mb
                0005 - 0.304 сек - 19.254 Mb
                0006 - 0.363 сек - 19.254 Mb
                0007 - 0.425 сек - 19.254 Mb
                0008 - 0.492 сек - 19.254 Mb
                0009 - 0.556 сек - 19.254 Mb
                0010 - 0.616 сек - 19.254 Mb

                wp_cache_flush();
                wp_insert_post();

                0001 - 0.063 сек - 20.267 Mb
                0002 - 0.130 сек - 20.267 Mb
                0003 - 0.187 сек - 20.267 Mb
                0004 - 0.246 сек - 20.267 Mb
                0005 - 0.314 сек - 20.267 Mb
                0006 - 0.377 сек - 20.267 Mb
                0007 - 0.436 сек - 20.267 Mb
                0008 - 0.497 сек - 20.267 Mb
                0009 - 0.557 сек - 20.267 Mb
                0010 - 0.618 сек - 20.267 Mb

                wp_cache_flush();
                wp_insert_post();
                wp_cache_flush();

                0001 - 0.068 сек - 19.254 Mb
                0002 - 0.134 сек - 19.254 Mb
                0003 - 0.199 сек - 19.254 Mb
                0004 - 0.260 сек - 19.254 Mb
                0005 - 0.321 сек - 19.254 Mb
                0006 - 0.381 сек - 19.254 Mb
                0007 - 0.440 сек - 19.254 Mb
                0008 - 0.505 сек - 19.254 Mb
                0009 - 0.568 сек - 19.254 Mb
                0010 - 0.629 сек - 19.254 Mb

                Использование wp_suspend_cache_addition( true ) в самом начале плагина. В общем перед всем всем. wp_cache_flush() уже не используем нигде.

                0001 - 0.071 сек - 19.322 Mb
                0002 - 0.137 сек - 19.322 Mb
                0003 - 0.199 сек - 19.322 Mb
                0004 - 0.262 сек - 19.322 Mb
                0005 - 0.324 сек - 19.322 Mb
                0006 - 0.413 сек - 19.322 Mb
                0007 - 0.485 сек - 19.322 Mb
                0008 - 0.555 сек - 19.322 Mb
                0009 - 0.625 сек - 19.322 Mb
                0010 - 0.697 сек - 19.322 Mb

                Пробовал ещё $wpdb->flush() в разных местах скрипта, ничего не дало.

                Самое интересно, с утра игрался (по подсказке от Konstantin Kovshenin в группе по WP), но в старом скрипте я вместо переменной dle_post_id, где уже содержался ID записи, которую надо скопировать с 1 базы в другую, я писал dle_post_id->id (чёрт дёрнул, думал там объект). Интересно, что всё копировалось без ошибок, но как видим куда-то улетала память. Куда? Загадка. Может создавался новый объект? Сейчас всё нормально. Всё! Как как-то сказал Ельцин: "Я устал, я ухожу".

                1
                Ответить2.6 года назад #
                • Kama7364

                  Про функцию wp_suspend_cache_addition() не знал, как ни странно. А ведь все логично!

                  Ну и еще добавлю: ты не забудь после всего этого вернуть wp_suspend_cache_addition() в прежнее состояние.

                  Я эту функцию сегодня опишу тут...

                  Ну, и спасибо за твои дневники... smile

                  1
                  Ответить2.6 года назад #
                  • campusboy3279 cайт: www.youtube.com/c/wpplus

                    Ну и еще добавлю: ты не забудь после всего этого вернуть wp_suspend_cache_addition() в прежнее состояние.

                    Обязательно, если это реально нужно. На странице плагина для моих целей не вижу смысла в конце добавлять wp_suspend_cache_addition( false ), ибо это работает ведь только для 1 страницы. Да и есть прикол, о нём ниже.

                    К примеру, я в самом начале плагина включаю wp_suspend_cache_addition, замеряю, память не утекает (в примерах выше видно это). Потом, думаю, надо ж отключать. Потому в сааааамом конце плагина отключаю. И что думаешь? Память снова начинает утекать! Как будто я вначале и не прописывал wp_suspend_cache_addition(true) вовсе. Потому я пока использую просто wp_cache_flush() сразу после wp_insert_post() и этого для моих целей достаточно.

                    Рад, если кому пригодится, но это вряд ли, слишком специфично всё smile а описание функции жду, вдруг что ещё интересненького обнаружится.

                    Ответить2.6 года назад #
                    • Kama7364

                      К примеру, я в самом начале плагина включаю wp_suspend_cache_addition, замеряю, память не утекает (в примерах выше видно это). Потом, думаю, надо ж отключать. Потому в сааааамом конце плагина отключаю.

                      Так ты там порядок не усмотрел наверное. Например:

                      wp_suspend_cache_addition( true ); // выключаем кэш
                      
                      add_action('plugins_loaded', 'plugin_function');
                      
                      function plugin_function(){
                      	// инициализация плагина
                      }
                      
                      wp_suspend_cache_addition(); // включаем кэш

                      В этом случае порядок выполнения функций при обработке скрипта будет такой:

                      wp_suspend_cache_addition( true );
                      wp_suspend_cache_addition();
                      
                      plugin_function();

                      Описание тут: wp_suspend_cache_addition(). Там вроде все предельно просто ...

                      1
                    • campusboy3279 cайт: www.youtube.com/c/wpplus

                      У меня сделано как в 1 примере по ссылке.

                      wp_suspend_cache_addition( true ); // отключаем кэширование
                      
                      // Работа плагина без каких либо add_action, просто идут wbdb запросы туда сюда и wp_insert_post в цикле.
                      
                      wp_suspend_cache_addition(); // включаем кэширование
                    • Kama7364

                      Хм, тогда странно, что не работает...

            • campusboy3279 cайт: www.youtube.com/c/wpplus

              Тимур, может ты и знал, но я открыл это недавно xDebug, в "вопросах" тупил даже. Ну вот, после его активизации не только можно сохранять запросы в файл и анализировать работу скрипта, но и var_dump стал выводить инфу по-другому, вот так:

              Наглядность суперская.

              1
              Ответить2.6 года назад #
              • Kama7364

                Спасибо, я с ним не знаком, познакомлюсь обязательно...

                Ответить2.6 года назад #
  • Михайлов Михаил

    Использовал

    $wpdb->update
    (
    	$table_photos,  
    	array( 'cat_id' => $_POST['cat_id'], 'name' => $_POST['photoname'], 'desc' => $_POST['photodesc'], 'photo_url' => $_POST['photoUrl'], 'date' => current_time('mysql')),  
    	array( 'id' => $_GET['edit_photo_id'] )
    );

    Вначале всё ок было, потом понадобилось добавить в $_POST['photodesc'] шорткод с атрибутами и тут началась беда. Функция экранировала все символы " и ' которыми выделяются атрибуты шорткода.

    Ответить2.5 года назад #
    • campusboy3279 cайт: www.youtube.com/c/wpplus

      Попробуйте все POST обернуть в функцию wp_unslash.

      2
      Ответить2.5 года назад #
      • Михайлов Михаил

        Обернул переменную которая и будет иметь шорткоды в функцию, помогло, теперь шорткод не крашится при внесении в бд. Спасибо!

        Ответить2.5 года назад #
  • Александр

    Вам спасибо за ваш труд.
    Вопрос по статье - очистка от sql-инекций, разумеется, вызовет изменение передаваемых данных. Как потом их восстановить в первозданном виде? Поясню: с помощью insert помещаю в таблицу данные, среди которых html-код. Этот код содержит адреса ссылок, заключенные в двойные кавычки. При вставке все двойные кавычки экранируются. Когда получим эти данные обратно и вставим этот html-код на страницу, ни одна из ссылок (а также картинок и т.д.) корректно работать не будет. Как быть?

    Ответить2.1 года назад #
    • Kama7364

      Экранированная кавычка становится символом "кавычка" а не частью SQL запроса. Т.е. при экранировании в БД записывается просто кавычка без \. Либо у вас проблема не в том, либо её нет, а вы думаете наперед неправильно...

      Ответить2.1 года назад #
      • Александр

        В textarea набрал код
        <a href="www.google.ru"><img src="<?php echo SIMPLE_PATH; ?>images/cat.jpg"></a>
        Вставил в БД insert-ом. В БД выглядело так:
        <a href=\"www.google.ru\"><img src=\"<?php echo SIMPLE_PATH; ?>images/cat.jpg\"></a>

        Ответить2.1 года назад #
        • Kama7364

          При post запросе сервер экранирует строки... В результате в inset попадает то что получается в БД... Перед вставкой в insert обработай строку через wp_unslash()

          1
          Ответить2.1 года назад #
  • Александр

    $wpdb->delete( $table, $where, $where_format = null );
    Как указывать условие удаления строк, если в параметре $where нужно указать промежуток значений (например от 3 до 7 ).... ?

    Ответить2 года назад #
  • Здравствуйте! С помощью get_col_info можно проверить отдельную ячейку?
    Например есть запрос

    $check_image = $lwpdb->get_var("SELECT photo_blob FROM tovar WHERE group_tov_id = '16' AND tovar_id='$id'")or wp_die( $lwpdb->error );
    Ответить2 года назад #
    • Kama7364

      А что проверить нужно? get_col_info дает данные о самой колонке, а не о значении ячейки... вы не путайте... она оч редко нужна...

      Ответить2 года назад #
  • Сергей

    Всем добра! Нужна помощь. Написал для теста такую функцию, она должна по мета ключу вернуть метазначение, но возвращает пустой массив. Если делаю выборку по meta_id выбирает без проблем, по meta_key вообще не выбирает...

    function get_meta_value($meta_key){
    	global $wpdb;
    	$metas = $wpdb->get_results("
    		SELECT meta_value 
    		FROM". $wpdb->postmeta ." 
    		WHERE meta_key = ".$meta_key, 'ARRAY_A');
    }
    Ответить2 года назад #
    • Kama7364

      Неправильный запрос... Во первых он не безопасный, во-вторых там строка и её нужно в кавычки совать ''

      function get_meta_value( $meta_key ){
      	global $wpdb;
      	$sql = $wpdb->prepare("SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = %s", $meta_key );
      	$metas = $wpdb->get_results( $sql, ARRAY_A );
      	return $metas;
      }
      Ответить2 года назад #
Здравствуйте, !     Войти . Зарегистрироваться