Pull to refresh

Тонкости при работе с Sanitize в связке с save

Reading time3 min
Views6.7K
Доброго времени суток уважаемый %username%. Хотелось бы вам рассказать об одной интересной особенности работы с классом Sanitization. Данный класс является частью ядра замечательной фреймворка CakePHP и предназначен для «очистки» входящих данных. К примеру, его можно, нужно использовать для «очистки» данных передаваемых пользователем перед сохранением в базу данных. Официальная документация и примеры использования лежат тут book.cakephp.org/view/1183/Data-Sanitization. Настоятельно рекомендую каждому разработчику использующему CakePHP ознакомиться с этим классом.
Но у Sanitize есть одна особенность, которая не задокументирована и на выяснение которой у автора ушло достаточно много времени. Вот о ней и хотелось бы рассказать подробнее. Хотя если сесть и хорошенечко подумать, то никакая это не особенность и не загвоздка, просто нужно быть внимательнее и всё оказывается понятным, простым и очевидным. У Sanitize есть 4 статических метода:
  • Sanitize::paranoid — удаляет «лишние» символы из строки
  • Sanitize::html – режет html
  • Sanitize::escape – экранирует строку перед добавлением в базу данных
  • Sanitize::clean — «чистит» переданный массив.

В данном случае нас больше всего интересует метод clean. Sanitize::clean(mixed $data, mixed $options) принимает два параметра $data — массив и набор опций для очистки (или если угодно правила фильтрации)
  • odd_spaces
  • encode
  • dollar
  • carriage
  • unicode
  • escape
  • backslash

Каждое из этих правил применяется рекурсивно к каждому элементу из $data. Рассмотрим это на живом примере:
у нас есть таблица комментариев (id,first_name,last_name,email,comment)
модель Comment
контроллер Comments с методами add и view
Начнём с котроллера функция добавления комментария
public function add() {
		 //проверяем на наличие данных в $_POST
		if (isset($this->data['Comment']) and !empty($this->data['Comment']) ) {
			App::import('Sanitize');
			// "чистим" полученные данные
			$this->data = Sanitize::clean($this->data);
			$this->Comment->set($this->data);
			// проверяем валидность входных данных
			if ($this->Comment->validates()) {
				// сохранение в бд
				if($this->Comment->save()){
					$this->redirect('/comments/');
				}	
			}
		}
}

вроде ничего необычного и непонятного.
Sanitize::clean($this->data)
как видим мы использовали все фильтры доступные у этого метода.
Что же добавим первый комментарий
add comment
смотрим что получилось,
view comment
так стоп стоп стоп, откуда взялся второй слэш в поле last_name? Может это ошибка отображения?
Посмотрим как выглядят данные в базе.
in db
Итак кавычки были закодированные, это понятно. Но вот всё же откуда этот второй слеш в поле last_nme?? Мы с вами %username% прекрасно знаем что при экранировании в строку добавляются (простите за тавтологию) экранирующие слеши, но они присутствуют в строке только до момента попадания в базу. Иными словами экранирующие символы никогда не попадают в базу. Но что же произошло в данном случае? Давайте разберёмся! Для этого уберём в методе add redirect на defaul action (закомментируем строчку $this->redirect('/comments/');), введём те же данные и посмотрим что произойдёт. Смотрим на запрос добавления:
INSERT INTO `comments` (`first_name`, `last_name`, `email`, `comment`) VALUES ('Lorem ' ipsum "', 'Aliquam\\\\ut/metus', 'consectetur@amet.sit', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus posuere, lectus vel mollis laoreet, mauris est vehicula tortor, non tincidunt purus turpis id ligula. Ut gravida scelerisque nisi in auctor. Sed fringilla eleifend massa in mattis. Suspendisse potenti.')

Как мы с вами помним Sanitize очищает данные перед сохранением в бд. Значит часть символов была экранирована. Экранирующие слэши не записываются в базу, это и понятно. В нашем случае у нас 4 символа «\» два из них попали в базу, остальные были экранирующими. Стоит предположить что строка была экранирована дважды. На самом деле так оно и есть. Если покопаться в исходниках метода save (да и если просто хорошенечко подумать) то мы можем обнаружить что Model->save() так же экранирует строку перед сохранением. Таким образом если вы используете в связке save + Sanitize отключайте опцию escape в нашем случае
$this->data = Sanitize::clean($this->data,array('escape' => false));
Но если для записи данные вы пользуетесь методм Model->query() и Sanitize опция escape должна быть включена.
Спасибо всем кто дочитал до этого места. Не знаю, но мне самому кажется что это статья их раздела «О привет ребята, я вчера Америку открыл». Что же пусть будет так, но все же я думаю что это тонкость, о которую новичок (плохо читающую документацию) может серьёзно споткнутся. C большим вниманием выслушаю любые замечания и предложения
Tags:
Hubs:
Total votes 5: ↑4 and ↓1+3
Comments7

Articles