За чистый и ясный код!

Статьи на тему программирования под веб, используя PHP, MySQL, Jquery и многое другое

Валидация формы на фреймворке Kohana, класс Validation. Часть 2

Июль3
Если у тебя командировка в Краснодар, то лучшей гостиницы просто не найти!

Валидация формыВ первой части я рассказал как построить форму используя фреймворк Kohana, во второй части я хочу рассказать как происходит валидация формы и вывод ошибок.

Как обычно, для просмотра примера используем форму представленную тут.

Статья написано для работы с версией Kohana 3.3.0 (текущая версия на момент написания статьи)

Класс Validation (валидация формы)

Для чего вообще требуется валидировать данные? Есть много причин — защита от дурака, получения более правдивых данных, предотвращение XSS атак и тд. Лично для меня валидация занимает важное место в создании сайта, т.к. именно с правильными данными приходится потом работать и если не делать проверку, например, на номер телефона (цифры и некоторые символы), эл.почты, ссылки и тд, потом можно очень сильно поплатиться.

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

Рассмотрим основные методы для валидцаии класса.

Создание экземпляра класса Validation

Есть два способа, классический

$post = Security::xss_clean($this->request->post()); // данные массива $_POST обработанные от XSS
$valid = new Validation(array_merge($post, $_FILES)); //тут я передаю в валидатору сразу два массива данных $_POST и $_FILES

и с помощью паттерна фабрики

$post = Security::xss_clean($this->request->post()); // данные массива $_POST обработанные от XSS
$valid = Validation::factory(array_merge($post, $_FILES)); //тут я передаю в валидатору сразу два массива данных $_POST и $_FILES

Правила (rule, rules)

С помощью этих методов передается название поля, которое требуется провалидировать, и правило которое требуется для этого.

Например, необходимо, чтобы поле «Ваше имя» было заполнено, для этого создаем правило

$valid->rule('name', 'not_empty');

Первый параметр метода rule это название поля валидации в теге input формы, второй параметр это метод класса Valid который будет применен к этому полю. В ядре фреймворка в классе Valid.  Это самый простой метод валидации, т.к. он не требует дополнительных параметров, просто проверить данные на пустоту и все. Есть более сложные методы проверки, например когда вам нужно проверить данные и для этого требуется передать методу класса Valid дополнительные параметры, например проверить данные на соответствие регулярному выражению, тогда запись правила валидации выглядит немного иначе:

$this->template->form->type_error = array(
	'0'=>' ',
	'1'=>'изменение информации о сайте',
	'2'=>'стилистические, грамматические, орфогр. ошибки'
);
...
$valid->rule('type_error', 'regex', array(':value','/[1-2]+/'));

Разберем подробнее. Есть поле формы с именем type_error, это тег типа select в нём имеются 3 опции, так вот важно чтобы были выбраны опции со значениями 1 или 2, поэтому я решил сделать проверку выбора с помощью регулярного выражения. Поэтому вторым параметром я указываю название метода, который есть в классе Valid. Метод требует для работы 2 параметра

public static function regex($value, $expression)
{
	return (bool) preg_match($expression, (string) $value);
}

А данные для метода Valid::regex идут в 3-й параметром в методе проверки array(‘:value’,’/[1-2]+/’),
где :value — это текущее значение поля формы type_error,
‘/[1-2]+/’ — это регулярное выражение.

Теперь надеюсь понятно, как строятся правила валидации для полей формы. Это еще не все пояснения, дальше будет.

Бывает случаи, когда для одного поля требуется написать сразу несколько правил валидации. Можно воспользоваться методом $valid->rule(…) и написать их столько к полю, сколько нужно, а можно просто использовать другой метод $valid->rules(…) для добавления сразу нескольких правил валидации за один раз, рассмотрим пример.

В форме есть поле email, это поле обязательно для заполнения, следовательно оно не должно быть пустым, еще нужно проверять,чтобы длина эл. адреса не превышала 254 символов, а еще чтобы туда был введен правильные электронный адрес, следовательно мне нужно использовать 3 правила валидации.

$valid->rules(
	'email', array(
		array('not_empty'),
		array('max_length', array(':value', 254)),
		array('email')
	)
);

Способом выше можно записать сразу все правила валидации для одного поле, обратите внимание, что метод Valid::max_length требует 2 параметра, значение и максимальную длину, которую я и передал.

Это способы которые идут «из коробки», но бывают ситуации, когда нужно проверить данные таким способом, который не описан в классе Valid, например проверить Captcha, на пустоту поля проверить можно, а вот правильность ввода как? Также есть решения:

$valid->rules(
	'captcha', array(
		array('not_empty'),
		array('Captcha::valid')
	)
);

Передаем сразу все правила за один раз в валидатор, проверяем на пустоту, стандартным методов Valid::not_empty, а также на корректность ввода, но обработку данных будет уже вести метод Captcha::valid, класс расположен в модуле Captcha. Еще пример такой же записи, теперь для картинок.

$valid->rule('img', 'Upload::type', array(':value', array('jpg', 'png', 'gif')))

Требуется загружать файлы, только с расширениями ‘jpg’, ‘png’, ‘gif’, за эту валидация отвечает метод Upload::type, который требует 2 параметра, данные и типы файла.

Как создать свой собственный валидатор я расскажу в следующей статье, а теперь переходим к другим метода класса валидации — label и labels.

Метки (label, labels)

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

Переменная $_labels внутри класса Validation заполняется самостоятельно при создании правил валидации, в дальнейшем по данным массива этих меток находятся переводы для заполнения массива ошибок, об этом тоже чуток позже. Но есть нюансы, в самом месте где происходит заполнение стоит регулярное выражение, которые удаляет «лишние» символы из название полей:

$this->_labels[$field] = preg_replace('/[^\pL]+/u', ' ', $field);

поэтому если у вас имеется в названии поля -, _ и прочие знаки, вам нужно указать это в методе $valid->label(…). В моём примере также имеется поле с «неправильным названием» type_error и для того, чтобы метка не изменилась, я указываю её:

$valid->label('type_error', 'type_error');

Конечно можно и поступить иначе, ошибки формы и переводы также буду показываться, даже если не указывать метку для «проблемного» поля, но я предпочитаю единообразие в назывании, чтобы через какое-то время не вспоминать, а чего тут поле называется type_error, а в переводах оно «type error«. Метод $valid->labels(…) принимает просто массив меток с параметром ключ=>значение, просто за один метод передать все данные формы.

Вывод ошибок (errors)

При валидации формы необходимо, в случае ошибок валидации, уведомлять об этом пользователя, что в таком-то поле вы что-то не то сделали. Для таких целей существует метод $valid->errors(…), рассмотрим его работу:

public function errors($file = NULL, $translate = TRUE){...}

Методу для работы требуется 2 параметра — название файла, в котором содержатся объяснения ошибок или переводы этих ошибок, второй параметр отвечает за перевод, по-умолчанию перевод включен.
Я назвал свой файл contact, сам файл можно размещать либо в модуле, либо в главной папке (application/messages/), запомните, что если будет находится 2 одинаковых файла в главной папке и в модуле (пример /modules/modulename/messages/), то данные будут прочитаны из главной папки, т.к. у Kohana такая иерархия обхода директорий. Т.к. модуля у меня нет, то я все примеры и переводы храню в главной папке (/application/messages/contact.php), вот его содержание:

<?php defined('SYSPATH') or die('No direct script access.'); 
$messages = array(
 	'name'	=>
		array(
			'not_empty'    	=> ':field must not be empty',
		),
	'email'	=>
		array(
			'not_empty'    	=> ':field must not be empty',
			'max_lenght'  	=> ':field must not exceed :param2 characters long',
			'email'			=> ':field not email address'
		),
	'sex'	=>
		array(
			'not_empty'    	=> ':field must not be empty',
		),
	'type_error'	=>
		array(
			'regex'    	=> ':field does not match the required format',
		),
	'descr'	=>
		array(
			'not_empty'    	=> ':field must not be empty',
		),
	'img'	=>
		array(
			'Upload::not_empty' => ':field must not be empty',
			'Upload::type' => ':filed is not allowed file type',
		),

	'captcha'	=>
		array(
			'Captcha::valid'=> ':field captcha not valid'
		),
);

return $messages;

На примере кусочка файла разберем его структуру, для примера возьмем эту часть:

'email'	=>
	array(
		'not_empty'    	=> ':field must not be empty',
		'max_lenght'  	=> ':field must not exceed :param2 characters long',
		'email'		=> ':field not email address'
	),

где, email — название поля в форме,
далее массив, в котором имеются ключи — это название метода в классе проверки, в данном случае Valid::not_empty и тд.
Ключ not_empty имеет значение :field must not be empty, что это? В ядре фреймворка имеется изначальный файл переводов (только английский язык), при проверке именно английское название будет выводится, часть начинающаяся с двоеточий :field будет заменена на название проверяемого поля, вот что получится — email must not be empty, вроде бы ура, всё ясно, но это не по-людски, нужна так выводить ошибки, чтобы человек мог понять, что от него просят! Поэтому нужно оформить переводы, т.к. у меня только русская локаль, то перевод я сделаю для неё, по аналогичной схеме можно делать переводы и для других языков. Конечно можно сделать халтуру и просто написать свой перевод вместо значения :field must not be empty, что-то типа, «Заполните электронный адрес!«, но в таком случае на всех языках сайта будут только один перевод на одном языке.

Немного о том, как создавать файл переводов. Файлы переводов хранятся или в главной папке application/i18n или в файле модуля, подчиняется тем же правилам считывания, что и все файлы фреймворка. В папке переводов i18n создается папка с двухзначным буквенным кодом локали в нижнем регистре, по ISO 639-1 (смотрите Вики), далее в этой папке будет файл с переводами, файл также создается по ISO 639-1 в нижнем регистре, например для русского языка будет ru, т.е. получится такая цепочка application/i18n/ru/ru.php или, для Украины, application/i18n/uk/ua.php, для Британии application/i18n/en/gb.php и тд.

Пример файла перевода:

<?php defined('SYSPATH') or die('No direct script access.'); 
return array(
  	// Переводы для валидации
  	':field must not be empty'  => 'Поле "<span>:field</span>" не должно быть пустым.',
	':field not email address'  => 'Поле "<span>:field</span>" содержит не корректный e-mail адрес.',
	':field captcha not valid'	=> 'В поле "<span>:field</span>" введён не верный код.',
	':filed is not allowed file type' => 'Файл в поле "<span>:field</span>" должен иметь расширения <span>:param2</span>.',
	':field does not match the required format'         => 'Поле "<span>:field</span>" не выбрано',
	':field must not exceed :param2 characters long' => 'Поле "<span>:field</span>" имеет длину больше чем <span>:param2</span> символа.',

	// Переводы для полей форм
	'name' => 'Ваше имя',
	'email' => 'Ваш эл. адрес',
	'sex' => 'Укажите Ваш пол',
	'type_error' => 'Тип проблемы',
	'descr' => 'Описание проблемы',
	'captcha' => 'Введите код',
	'img' => 'Картинка с ошибкой'
);

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

В переводах можно заметить некие сущности, такие как :field, :param2 и тд, эти сущности будут подменяться на название поля для :field и значение которое мы передаем в правиле для валидации.
Например, если посмотреть на одно из правил валидации поля эл. адреса, то можно увидеть, что мы передаем в метод валидации некие параметры array(‘max_length’, array(‘:value’, 254)), вот эти параметры можно получить в выводе ошибок, например второй параметр имеет сущность :param2 и будет равен 254, и полный текст ошибки будет такой «Поле «Ваш эл. адрес» имеет длину больше чем 254 символа.»

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

Многие могут спросить, а зачем заполнять массив в файле contact.php? Да, можно просто оставить пустой массив, а использовать только файлы-переводов, но файл contact.php должен быть, иначе получите не информативный массив ошибок, с котором ничего не сделаешь. Если массив файла contact.php будет пуст, то весь перевод будет браться из файлов-переводов и эти ошибки будет ОДИНАКОВЫ для всех форм на сайте, если же вы хотите вывести другое описание ошибки с одинаковым типом ошибки, то вам нужно заполнить массив и значение перевода выдумать своё, а далее это значение написать в файл переводов и для одной из форм у вас будет иной перевод.

Пример:

файл application/messages/contact.php
<?php defined('SYSPATH') or die('No direct script access.');
$messages = array(
 	'sex'	=>
		array(
			'not_empty'    	=> 'pol_ukagi',
		),
);

return $messages;

файл application/i18n/ru/ru.php
<?php defined('SYSPATH') or die('No direct script access.');  
return array(
 	// Переводы для валидации
  	'pol_ukagi'  => 'А вы кто, девочка или мальчик?',
);

После это вывод поля sex измениться с Поле «Укажите Ваш пол» не должно быть пустым. на А вы кто, девочка или мальчик?.

Теперь у вас все сделано для оформления ошибок, хочу еще сказать как получить массив всех этих ошибок. Как раз метод $valid->errors(‘contact’); и выдаст массив ошибок, где ключ — название поля, а значение отформатированное название ошибки. Далее делайте с ним, что вы хотите, например я во вьюхе пробежался по нему циклом и вывел все ошибки.

Контроллер

Хочу показать оформление контроллера.

public function action_index()
{
	$this->template->title = 'Пример работы валидатора';
	$this->template->form = new View('form_contact');
	$this->template->form->type_error = array(
		'0'=>' ',
		'1'=>'изменение информации о сайте',
		'2'=>'стилистические, грамматические, орфогр. ошибки'
	);

	$post = $this->request->post();

	if (count($post) > 0)
	{
		$post = Security::xss_clean($post);

		$valid = new Validation(array_merge($post, $_FILES));

		$valid->rule('name', 'not_empty', array(':value'))
			 ->rules(
				 'email', array(
					 array('not_empty'),
					 array('max_length', array(':value', 254)),
					 array('email'),
				 )
			 )
			 ->rule('sex', 'not_empty')
			 ->rule('type_error', 'regex', array(':value','/[1-2]+/'))->label('type_error', 'type_error')
			 ->rule('descr', 'not_empty')
			 ->rule('img', 'Upload::type', array(':value', array('jpg', 'png', 'gif')))
			 ->rules(
				'captcha', array(
						array('not_empty'),
						array('Captcha::valid')
					)
				)
		;

		if ($valid->check())
		{
			print_r($valid->data()); // валидированные данные
			print_r($post); // пост данные
			print_r($_FILES); // данные файлов
			exit;
		}
		else
		{
			$this->template->form->errors = $valid->errors('contact');
		}
	}
}

Как видно из контроллера, мы получаем переменную post() c данными формы, прогоняем данные через очистку от XSS, далее создаем экземпляр класса валидации, куда передаем смешанный массив данных для валидации, далее оформляем правила валидации, и осуществляем проверку, прошли проверку валидации — вывели данные массивов, а там делайте с ними, что вам угодно, не прошли валидацию — получите список ошибок валидации.

Валидация через ORM

В одной из следующих статей я рассмотрю работы формы и взаимодействие с БД. Этот способ мне нравится больше, чем просто валидация, там тоже есть на что обратить внимание.

Статья просмотренна 293493 раз, зашло посетителей 25125

Как изменить цвет консоли в ZF2

Март23
Собрался в поход в горы, в лес, на байдарках? Для надежной связи советую использовать пыле / влагозащищенный телефон Runbo X5

Вывод данных, поиск решения

В последнее время я работаю на большом проекте и переписываю часть его ядра. Т.е. никакого UI я не вижу, все что я пишу запускается из командной строки. Соответственно вся техническая информация которая выводится в командную строку отображается в таком сероватом цвете, в цвете по-умолчанию. Информации много, я уже разделил её на блоки, обрамив каждый скобками, квадратными, фигурными, обычными…, но всё это не то. Нужно чтобы инфа выводилось в цвете, чтобы глаз сразу видел, что если красный цвет я вывожу, то где-то, скажем поломка, если желтый, то внимание ну и тд.
Занялся я этим вопросом, начал изучать как устроен терминал, какие последовательности команд надо ему подать, чтобы выводимая инфа была в определенном месте, в определенном цвете. Пишу я свою часть на ZF2 и я случайно там обнаружил, что есть классы которые реализованы для отображения данных в цвете, как для ОС семейства Линукс, так и Виндовс. Все решается очень просто!

Небольшой пример кода, который выводи инфу в цвете:

// Подключаем необходимые библиотеки
use Zend\Console\Console as Console;
use Zend\Console\ColorInterface as Color;

//где-то, где вам надо просто используем классы, вывод строки с данными
Console::getInstance()->write("  " . str_pad($i,6) . '- (') .Console::getInstance()->write(str_pad($percent,5), Color::LIGHT_CYAN) . Console::getInstance()->write('% (') . Console::getInstance()->write($countVendorHotelsOst, Color::LIGHT_MAGENTA) . Console::getInstance()->write(') w-') . Console::getInstance()->write(str_pad($weight,3), $weight < 100 ? Color::LIGHT_YELLOW : Color::YELLOW) . Console::getInstance()->write(') - ' . str_pad($vendorHotel->hotelCode . ' ', 40,'-') . '-> ' . $idHotel);

// новая строка
Console::getInstance()->writeLine();

В итоге получаю такую картинку

Вывод цветных данных скрипта в командную строку

А вот как было без использования специальных классов:

Вывод данных скрипта в командную строку

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

Что еще позволяют сделать эти классы

Позволяет работать с адаптерами терминалов
— Posix
— Virtual
— Windows
— Windows Ansicon

Работает с кодировками
— UTF-8
— UTF-8 Heavy
— ASCII
ASCII Extended (расширенная)
DECSG

Позволяет выводить данные
— в строку, друг за другом
— в строку с переводом на новую
— устанавливать цвет текста
— устанавливать цвет фона
— выводить данные в определенное место в консоли
— прятать/показывать курсор при работе скрипта
— очищать экран
— получать различные данные о консоли

Еще фишка этих классов по работе с консолью, они позволяют ждать ввода инфы при работающем скрипте, т.е. в консоле возможен диалог скрипта с разработчиком.

С такими возможностями есть шансы, что скоро кто-то напишет велосипед, аля FAR или MC на PHP.

ЗЫ Я нашел еще кучу классов, для работы с цветом, ссылки ниже.

ANSI Escape sequences
Как подключить вывод цвета в терминале Windows
PHP CLI Colors

Статья просмотренна 314841 раз, зашло посетителей 20835

Создать динамический запрос с использованием PREPARE и EXECUTE в MySQL

Январь8
Нужно место в сети? Тогда ваш нужно разработать интернет сайт, милости просим.

Возможно в этой статье я буду выглядеть как Капитан Очевидность, но пофиг, хочу поделиться информацией.

Иногда в процессе кодинга бывает необходимость менять SQL запрос в зависимости от обстоятельств, в php обычно такое происходитс помощью простого изменения содержимого самого запроса и отправляется на выполнение, ждем результат. Что делать, если нам надо сделать хранимую процедуру (stored runtime, procedure) или функцию, в которой запрос меняется в зависимости от условий?! Вот и предлагаю с этим ознакомиться.

Пример я придумал такой.

У нас есть две таблицы country_ru и country_en, где хранятся данные о странах на русском и английском языках соответственно.

CREATE TABLE `country_ru` (
	`code` CHAR(2) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	`name` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	UNIQUE INDEX `code` (`code`)
)
COMMENT='страны на русском'
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB;

INSERT INTO `country_ru` (`code`, `name`) VALUES ('ru', 'Российская Федерация');
INSERT INTO `country_ru` (`code`, `name`) VALUES ('ua', 'Украина');

##################################

CREATE TABLE `country_en` (
	`code` CHAR(2) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	`name` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	UNIQUE INDEX `code` (`code`)
)
COMMENT='страны на английском'
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB;

INSERT INTO `country_en` (`code`, `name`) VALUES ('ru', 'Russian Federation');
INSERT INTO `country_en` (`code`, `name`) VALUES ('ua', 'Ukraine');

и стоит задача выдавать страны в зависимости от локали. Т.е. подается локаль ru или en и данные должны быть на соответственном языке.

Можно конечно было сделать условие и в зависимости от него выполнять один или второй запрос, что-то типа

DELIMITER //
CREATE DEFINER=`root`@`%` PROCEDURE `getCountry`(IN `locale` char(2))
	LANGUAGE SQL
	NOT DETERMINISTIC
	CONTAINS SQL
	SQL SECURITY DEFINER
	COMMENT ''
BEGIN
	IF locale = 'ru' THEN
		SELECT * FROM country_ru;
	END IF;
	
	IF locale = 'en' THEN
		SELECT * FROM country_en;
	END IF;
END//
DELIMITER ;

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

Вот тут лучше сделать динамический запрос, который меняет своё тело в зависимости от локали. Если сделать вот так

	CONCAT('SELECT * FROM country_', locale);

, будет ошибка, вы даже не сможете сохранить процедуру.

Для решения этой задачи в MySQL есть 3 команды подробнее тут.

PREPARE — подготавливает запрос для выполнения.
EXECUTE — выполняет запрос
DEALLOCATE PREPARE — очищает запрос

Вот так выглядит процедура с этими командами

DELIMITER //
CREATE DEFINER=`root`@`%` PROCEDURE `getCountry`(IN `locale` char(2))
	LANGUAGE SQL
	NOT DETERMINISTIC
	CONTAINS SQL
	SQL SECURITY DEFINER
	COMMENT ''
BEGIN
	SET @sql = CONCAT('SELECT * FROM country_', locale);
	PREPARE getCountrySql FROM @sql;
	EXECUTE getCountrySql;
	DEALLOCATE PREPARE getCountrySql;
END
DELIMITER ;

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

Для вызова используем

CALL getCountry('en');

результат
+------+--------------------+
| code | name               |
+------+--------------------+
| ru   | Russian Federation |
| ua   | Ukraine            |
+------+--------------------+
2 rows in set (0.01 sec)

и на русском 

CALL getCountry('ru');

результат
+------+-----------------------------------------+
| code | name                                    |
+------+-----------------------------------------+
| ru   | Российская Федерация                    |
| ua   | Украина                                 |
+------+-----------------------------------------+
2 rows in set (0.00 sec)

Вот и все, конечно команды могут быть расширены, об этом подробно написано в мануале.

В помощь %username%.

Статья просмотренна 334454 раз, зашло посетителей 27034

Логирование ошибок сайта для Kohana.

Январь27

Файл регистрации, журналирование, логирование — все это одно и тоже и сводится к одному — записи различных действия на носитель. Для чего всё это нужно, ой тут много ответов — для протоколирования какой-нибудь процедуры, для контроля поведения кого-то и тд. В общем дело это нужное, и я хотел бы рассказать как я веду логирование ошибок у себя на сайтах написанных с помощью фреймверка Kohana.

В самой Кохане уже есть логирование на уровне ядра фрейма и оно хорошо работает, записывает в папку application/logs данные о логировании (по-дефолту, управляется всё из файла bootstrap.php, строка Kohana::$log->attach(new Kohana_Log_File(APPPATH.’logs’));). Система формирует структуру папок YYYY/MM/DD.php,
где YYYY — год, когда создался файл, MM — месяц (две цифры) и DD.php сам файл с номеров дня месяца.
Файл заполняется по команде:

try
{
	return $this->_write();
}
catch (Exception $e)
{
	// Log & ignore all errors when a write fails
	Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e))->write();

	return FALSE;
}

само содержание файла лога выглядит так:

2010-10-01 17:54:15 --- ERROR: ReflectionException [ -1 ]: Class controller_contact does not exist ~ SYSPATH\classes\kohana\request.php [ 1094 ]
2010-10-01 17:54:37 --- ERROR: ErrorException [ 4 ]: syntax error, unexpected ';', expecting T_STRING or T_VARIABLE or '$' ~ APPPATH\views\main.php [ 5 ]
2010-10-01 17:56:23 --- ERROR: ReflectionException [ -1 ]: Class controller_contact does not exist ~ SYSPATH\classes\kohana\request.php [ 1094 ]
2010-10-01 17:56:33 --- ERROR: ReflectionException [ -1 ]: Class controller_contact does not exist ~ SYSPATH\classes\kohana\request.php [ 1094 ]
2010-10-01 17:59:58 --- ERROR: ReflectionException [ -1 ]: Class controller_contact does not exist ~ SYSPATH\classes\kohana\request.php [ 1094 ]
2010-10-01 18:28:37 --- ERROR: ErrorException [ 1 ]: Class 'Helper_Html' not found ~ APPPATH\views\form_contact.php [ 37 ]
2010-10-01 18:29:14 --- ERROR: ReflectionException [ -1 ]: Class controller_captcha does not exist ~ SYSPATH\classes\kohana\request.php [ 1094 ]
2010-10-01 18:29:22 --- ERROR: ReflectionException [ -1 ]: Class controller_captcha does not exist ~ SYSPATH\classes\kohana\request.php [ 1094 ]
2010-10-01 18:30:45 --- ERROR: ReflectionException [ -1 ]: Class controller_captcha does not exist ~ SYSPATH\classes\kohana\request.php [ 1094 ]
2010-10-01 18:30:49 --- ERROR: ReflectionException [ -1 ]: Class controller_captcha does not exist ~ SYSPATH\classes\kohana\request.php [ 1094 ]

В принципе вся инфа о падении, добавления своих собственных комментариев и т.д. всё будет фиксироваться. Но мне не очень понравилось то кол-во информации которое я мог почерпнуть из этого файла. Например состояние cookie, что за браузер и тд. Пришлось поискать готовые решения. Такое решение было найдено — kohana-log, что мне тут понравилось, что данные доступны по урлу, что в логе сохраняются следующие данные:

  1. Сама ошибка
  2. IP клиента
  3. uri где произошла ошибка
  4. referer
  5. agent — браузер
  6. cookie

По ссылке http://localhost/path/to/log/day, где day — дата за которую хотим просмотреть лог, вида DD.MM.YYYY. Можно посмотреть лог за текущий или любой день не входя по ФТП или SSH, но были конечно и бока — порой не верный парсинг, отсутствие возможности сохранять свои данные в БД (может и глупо, но было требование заказчика). Для более удобного набора ссылки в браузере, да и человека, я оставил её в виде сео чпу, вообще мне нравятся роутинги фреймворков, которые позволяют сделать чпу-ссылки, а не ссылки в виде набора параметров. Также есть еще ссылка, для загрузки лога, в виде чпу http://localhost/path/to/log/download/day (эту ссылку я уже добавил с своей версии).

Более подробно на странице плагина.

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

Для логирования в файл нужно в bootstrap.php написать следующий код

Kohana::$log->attach(new Log_Writer_File(APPPATH.'logs'));

Для логирования в БД нужно в bootstrap.php написать следующий код

Kohana::$log->attach(new Log_Writer_Database);

Также для БД нужно сделать табличку

CREATE TABLE IF NOT EXISTS `logs` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `level` enum('EMERGENCY','CRITICAL','ERROR','WARNING','NOTICE','INFO','DEBUG') COLLATE utf8_unicode_ci DEFAULT 'INFO',
  `message` varchar(5000) COLLATE utf8_unicode_ci DEFAULT NULL,
  `client` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL,
  `uri` varchar(500) COLLATE utf8_unicode_ci DEFAULT NULL,
  `referer` varchar(500) COLLATE utf8_unicode_ci DEFAULT NULL,
  `agent` varchar(500) COLLATE utf8_unicode_ci DEFAULT NULL,
  `cookie` varchar(2000) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Логирование ошибок сайта' AUTO_INCREMENT=1 ;

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

Удачного дебага и профилирования.

Статья просмотренна 391295 раз, зашло посетителей 33231

Расширения браузера Google Chrome для веб-программиста

Январь10

VS

FireFox-vs-Google-Chrome Каждый день в своей работе (создание сайтов любой сложности) я использую для разработки два основных браузера FireFox (больше) и Goolge Chrome (меньше), но в последнее время, наверное благодаря телефону с ОС Андроид, я начал больше присматриваться к Хрому. И дабы чувствовать себя «как рыба в воде» хотелось чтобы те расширения которые я использую в FireFox-е хотелось бы использовать и тут.
На удивление почти все мои расширения есть в магазине Хрома. И так что же я использую:

FireBug Lite

FireBug LiteНаверное один и самых ценных инструментов для разработки. Хром конечно содержит по-дефолту свою панель разработчика, но от привычек надо отказываться постепенно. Поживем увидим. Скачать расширение можно по тут

Edit This Cookie

Edit This CookieРасширение для работы с куками (cookie), позволяет как добравлять, так и изменять существующие куки на сайте. Закачать расширение можно тут

ColorZilla

ColorZillaОчень часто необходимо узнать цвет пипеткой или перевести RGB в HEX и наоборот, для этих целей это прекрасное расширение.
Закачать расширение можно тут

HTTP Headers

HTTP HeadersПросмотре заголовков страницы. Единственное я не нашел расширение аля Liveheaders, оно мне нравилось тем, что показывало абсолютно все заголовки которые происходили во время загрузки страницы. Если кто-то может поделиться так расширением для Хрома, буду рад увидеть это в комментариях.
Закачать расширение можно тут

JSONView

JSONViewОчень наглядно показывает содержания JSON ответа. Рекомендую для отладки приложений.
Закачать расширение можно тут

MeasureIt

MeasureItКуда без линейки при верстке!
Закачать расширение можно тут

PerfectPixel by WellDoneCode

PerfectPixel by WellDoneCodeВот это злое расширение, когда оно попадает в руки заказчику и он начинает все проверять…, но и очень полезное разработчику, сразу видно где «халтурка».
Закачать расширение можно тут

Web Developer

Web DeveloperНабор различных примочек для включения/отключения и показ скрытых элементов. Жаль что здесь на работает валидация локальной страницы — очень помогало в ФФ, а также не работает линейка, изменение размера экрана и тд, в общем поставил чтобы было, вдруг починят.
Закачать расширение можно тут

Редактирование и отправка скриншотов

Редактирование и отправка скриншотовПолезный инструмент, позволяет сделать скрин или ВСЕГО сайта, а не только видимого окна или сделать часть, также подредактировать скрин и получить короткую ссылку для отправки кому-то. Полезно при пояснении где именно что-то не так или куда именно надо нажать.
Закачать расширение можно тут

Удаленный рабочий стол Chrome

Удаленный рабочий стол ChromeК разработки сайтов отношения не имеет, но очень помогает что-то показать «криворуким пользователям», включаем у себя хром с расширением и клиент. Клиент запускает расширение у себя, дает вас код, вы запускаете у себя и вводите код, дальше на вашем экране появляется экран «криворукого юзера» и вы можете показать что да как надо делать.
Закачать расширение можно тут

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

Скачивайте гугл хром!

Статья просмотренна 383233 раз, зашло посетителей 35637

Поиск узких мест при создании сайтов на фреймверке Kohana

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

Kohana Debug Toolbar (KDT)

KDT портировал на  Kohana3 Иван Броткин почитать можно тут, также была статья на Хабре по работе с этим инструментом, правда в Kоhana версии 2.3.x .

С версии 2 было много изменений о которых я и опишу.

 

  1. Версия Kohana — (блин букву на картинке пропустил), при клике на номеру версии переход на страницу фреймверка;
  2. Время выполнения — показывает затраченное время на выполнения различных частей сайта, также показывает метки времени поставленные пользователем;
  3. Затраченная память — как и в предыдущем параметре показывает сколько памяти «съел» каждый кусок сайта;
  4. Кол-во запросов к БД — показывает все запросы выполненные на странице, времени выполнения, используемой памяти. Также может разделять запросы по подключению к разным БД, используя разные секции конфига БД (по-умолчанию default);
  5. Глобальные переменные — $_GET, $_POST etc, показывает все их значения, на момент построения страницы;
  6. Аякс запросы — показывает ко-во аякс запросов на странице;
  7. Подключённые файлы — показывает файлы с полным путём, размером и кол-вом строк в файле;
  8. Подключённые модули — показывает какие модули были подключены при выполнении страницы и их путь на серваке;
  9. Роуты — показывает все роуты в системе и жирным выделяет используемый роутер;
  10. Пользовательские переменные — никогда не юзал, сказать ничего не могу;
  11. Выравнивание и закрытие KDT — думаю название говорит само за себя.

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

Пощупать можно по ссылке внизу статьи.

Profiler

Тот же Иван Броткин в своей статье описывал работы с внутренним инструментов Kohana — profiler. Именно этот инструмент мне помогу найти узкие места по времени выполнения разных классов используемых при создании сайта, о котором я писал в своём предыдущем обзоре.

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

FirePHP

Еще один способ посмотреть данные, для этого надо установить в FireFox расширения FirePHP и FireBug. Скачать отсюда  модуль FirePHP  немного обработать напильником (там старая версия)  и у вас получится передача данных в заголовках (headers) и в панели фаербага будет видно следующее

Если посмотреть внимательно, то в этот инструмент модуль KDT может также высылать свою инфу. Честно сказать, мне этот инструмент ничего нового не принес, просто еще один способ получения инфы. Кому нужно могу выслать готовый модуль на почту.

SQLmon

Ещё один инструмент, изюминкой которого, по моему мнению, является разъяснения запросов, explain был написан программистов Владимиром, вот ссылка на статью посвящённую этому инструменту.

Единственное для работы в версии 3.2 я в нём внёс некую коррективу в класс Kohana_Database_SqlMon изменив набор аргументов метода query:

class Kohana_Database_SqlMon extends Kohana_Database_MySQL
{
это
public function query($type, $sql, $as_object)
заменил на это
public function query($type, $sql, $as_object = FALSE, array $params = NULL)

И все работает.

Все инструменты с примерами можно посмотреть тут.

Статья просмотренна 399882 раз, зашло посетителей 34011

Высокая нагрузка сайта написанного на Kohana

Октябрь29

Вступление

«A very fast framework. Benchmarking a framework is hard and rarely reflects the real world, but Kohana is very efficient and carefully optimized for real world usage.»

Так написано на главной странице этого фреймворка, так ли это? Да так, но с напильничком. Немного о проблеме.
У меня был заказ от студии creative-cafe.ru на один высоконагруженный проект http://www.jv.ru/longlivers, вроде на девелоперской машине работает хорошо, быстро. Показатели загрузки главной страницы с отколюченными системами кеширования под Ubuntu 11.10 показывают 1-2 секунды (после оптимизации 30-50ms), под виндой вообще караул 4-5 секунд, причём одно и тоже железо, просто ОСи разные, но в этой статье не об этом (советую работать под ОСью приближенной к продакшену). Так вот показатели вроде неплохо 1-2 секунды, а на продакшене сервер то мощнее будет, там вообще летает, но до некоторых действия. Когда заказчик нажал F5 и подержал так секунд 10, сервер смог загрузить контент только через 30 сек… Всего было отправлено около 200-300 запросов… Это уже не порядок, а что если 1000 зайдет?

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

Инструменты для оптимизации

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

Оптимизация БД

БД в проекте вообще маленькая 15 таблиц, она полностью сделана по нормализации, поставлены ключи в тех местах которые нужны, оптимизированы все запросы, запросы которые «системные» (об этом позже) закешированы. Всего на главной выполняется 22 запроса после кеширования и занимает это вообще мелочь 2.5 ms.
Для оптимизации таблицы могу порекомендовать юзать функию explain, почитать что такое нормализация таблиц БД и ещё рекомендую настроить и смотреть лог медленных запросов, он будет актуальным когда база наберет инфу и тогда можно смотреть какой запрос превышает порог медленных запросов. Но все это не относится к фреймверкам, это просто понимание работы БД.

Чем же нам может помочь фреймворк? Для работы с БД я использовал ORM, как я мог заметить при работе с разными фреймами, все ОРМы считывают структуры таблицы, что Zend Framework, что Kohana. И так зачем нам каждый раз считывать структуру таблиц, если они крайне редко меняются особенно на продакшене? Для этого надо закешировать эту информацию, ниже приведён класс который позволяет это сделать:

<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
 * Перекрытие метода получения данных о структуре таблиц 
 *
 * @package	Core
 * @author Maxim Nagaychenko <maxnag [at] meta.ua>
 * @copyright  Maxim Nagaychenko
 * @filesource
 */
class ORM extends Kohana_ORM
{
	/**
	 * Кеширование структуры таблицы
	 * 
	 * @see Kohana_ORM::list_columns()
	 * @return array 
	 */
	public function list_columns()
	{
	    $cache_lifetime=360000; // 100 часов
	    $cache_key = $this->_table_name ."_structure";
	    $result = Cache::instance()->get($cache_key);
	    
	    if ($result) {
	        $columns_data = $result;
	    }
	 
	    if( !isset($columns_data)) {
	        $columns_data = $this->_db->list_columns($this->_table_name);
	        Cache::instance()->set($cache_key, $columns_data, $cache_lifetime);
	    }

	    return $columns_data;
	}
}

Данный метод я положил в папку application/classes/orm.php в моделях которые используют наследование от ORM я ничего не менял, т.е.

class Model_Contest extends ORM {
....
}

фреймворк ищет сначала «у меня» этот файл и если не найдет, то найдет «у себя» в модуле ORM. И так этим методом я решил проблему лишних запросов. Также, уже в зависимости от проекта, можно данные запросов которых несут статических характер (у меня это разные настройки, мыло аминов, кол-во данных на страницу и тд) также можно закешировать.

Конечно сам ОРМ нагружает систему, т.к. выдает все данные запроса, т.е. select * from, а как сделать если надо только определённые поля? Я не знаю, кто знает подскажите. Особенно это заметно на большой таблице с большим кол-вом полей и забором большого кол-во данных. Тут конечно больше проблем в БД, но всё же ОРМу тяжко. Использовать простые запросы куда менее ресурсоёмко, но увы проигрываем в простоте построения запросов.

Стили и скрипты

Лучше если все стили и скрипты будут сложены в одном файле соот., это сэкономит на кол-ве запросов к серверу, что тоже время. Например я на главной использую 8 файлов-скриптов, 2 файла стилей, отсюда дополнительно 10 запросов на сервер. Как можно с этим бороться? Для этого я использую модуль Minify. У него хорошая документация, легко подключается к системе и выполняет свои функции, кроме «слепки» файлов в один, он еще может удалять комментарии и различные спецсимволы.

Кеширование

Для системы кеширования я выбрал memcache, он позволяет сохранять данные по типу key=>value, хранит их в ОЗУ сервера, соответственно работа с этим типом кеша очень быстрая.

Что я храню в мемкеше? Данные по структуре таблиц, которые требуются для работы с ORM, различные настройки сайта, которые меняются крайне редко, это у меня кол-во записей на странице, для каждого в раздела, контактные данные для отправки писем. Также по всему сайту есть сквозной блок новостей, которые показывает последние две новости проекта, вот такие блоки я и закешировал, при изменении данных новостей, кеш переписывается на новый. На сайте имеются много модальных окон, они построены на основе jQueryUI, а контент (формы, тексты) строятся каждый раз при создании страницы, а как часто люди при просмотре сайта нажимают зайти, зарегистрироваться и тд? Думаю намного реже, чем просто смотрят контекстные страницы, поэтому html этих окон закеширован, что дало тоже большой прирост. Прирост дало то, что контент в эти окна грузятся с БД, а формы строятся с помощью класса Form, что сильно нагружает систему.

Отказаться где можно от использования ООП

Да, это так. Программы разработанные на ООП занимают в памяти куда больше места, чем обычные процедурные, и на больших проектах заметны тормоза. Ниже я привел примеры построения формы на чистом XHTML и с использование класса Form. По своей сути этот класс всего лишь обёртка над тегами формы, можно обойтись и без него, но честно труднее, т.к. валидация, всякие изменения хорошо работают с этим классом. Также есть класс URL которые выдает полный урл, конечно можно без него обойтись, кто мешает в теге ссылки написать относительный урл? Но этот фреймверк, он должен быть универсальным, поэтому исключить этот класс нельзя. В чем же заключается его универсальность? Например сегодня ваш сайт живет по адресу http://example.com, потом вы решили его перенести по пути http://example.com/blog, что же будет без использования класса URL, понадобится все ссылки перебить с / на /blog, а представляете сколько их может быть по всем файлам, также менять роутеры и прочее… беда, а вот с использованием этого класса позволяет всего навсего в файлах bootstrap.php и .haccess изменить урл сайта на нужный вам и все ссылки тут же преобразуются. Тут надо и делать выбор или писать без этого класса, выигрыш в скорости или с ним, но тогда потеря производительности, но универсальность. Думайте сами.

Конкатенация содержимого файлов

При работе сайта по управлением Kohana подключается множество файлов через внутренний автолоадер, только системных файлов порядка 70, остальные сильно зависят от подключённых модулей и достигает до 260 на главной странице сайта. Время которое мой локальный комп подключет эти файлы вообще смешное 5 ms, я бы и не обращал на это внимание, но заказчик попросил объединить это все в один файл и уменьшить кол-во инклюдов… Ну что тут скажешь, он платит.
Нашелся этот модуль у одного блогера, описание и проблема решения тут SuperCache. После его применения кол-во фалов сократилось до 75 c 260.

Инструменты профайлинга

Для отслеживания «узких» мест я использовал различные инструменты. Например мне понравился DebugToolbar, это чисто информационный модуль который показывает все необходимые глобальные переменные со значениями, время выполнения различных частей фреймворка, запросы на странице, какие модули подключены, какие роутеры есть и какой задействован и много другое, это все я указал в примере, внизу страницы, также показывает замеры времени по меткам поставленным с помощью класса Profiler.

Второй инструмент это класс Profiler и он встроен в ядро фреймворка. Как раз с помощью этого класса я смог поставить точки в разных частях сайта и посмотреть какая часть сколько времени отрабатывается. Например интересует сколько времени занимает прорисовка формы, берем ставим точку запуска в начале формы и в конце точку остановки. Профайлер нам выдаст сколько времени это заняло, как раз это и мне помогла найти «узкие» места. Как работать с профайлером опишу в другой статье.

Примеры

Я тут приведу всего один пример, который покажет 2 одинаковых формы (просто прорисовка без логики работы). В самом низу формы как раз расположен профайлер, в нём есть раздел «Построение формы» и в нем два пункта, как можно убедится результаты ощутимы — построение на чистом html в тысячи раз быстрее чем строить тоже самое на ООП, особенно если представить это в рамках большого проекта. Обратите внимание еще на кол-во используемой памяти.
Также в верхнем правом углу расположен DebugToolbar, там тоже есть нужная инфа для работы.

Пример

PS Нужны скидки на товары или услуги? Приобретайте купоны на www.newsavings.ca, которые помогут Вам сэкономить ваши средства.

Статья просмотренна 407386 раз, зашло посетителей 36295

Работа с realtime индексами (RT) в поисковом движке Sphinx

Сентябрь18
Добро пожаловать на форум про php, интерсные статьи, задачи и обзоры.

Кто не знает что такое Sphinx

Сфинк — это бесплатный полнотекстовый поисковый движок. С быстрой индексацией документов, стоп-словами, умеет интегрироваться с самыми известными БД Mysql, MsSql, Oracle и тд, также имеет АПИ с популярными языками программирования PHP, Perl, Java и тд. Также имеет поддержку SQL синтаксиса. Более подробнее тут.

Версии поисковика

На сегодня последняя версия 2.0.1, не смотрите что она бета, на этом проекте беты очень стабильны.

Также есть версия 1.10, из неё родилась версия выше, а эту отправили в утиль архив.

К стабильной версии относится 0.9.9

Я к сожалению не знаток всех этих версий в полной мере, и не могу что-то особо сильно утверждать, поэтому дам сугубо поверхностную оценку различий версий.

Начиная с версии 1.10 появились relatime индексы. Это индексы которые изменяются «на лету», т.е. не нужно делать промежуточные индексы и через какое-то время сливать их с главным индексом.

Во всех версия появилась возможность создавать таблицу в MySQL 5.x с движком SPHINX (Sphinx storage engine), т.е. появилась возможность обращаться к данным поискового индекса через SQL запросы к БД MySQl, более подробно тут

Также во всех версиях доступен API.

Более подробно обращайтесь к разным блогам, документации поисково движка.

Какие бывают индексы

Есть обычный индекс с ним неотъемлемо связан дельта индекс, который затем сливается с индексом (merge).
Чем мне этот тип индекса не понравился. Например, у вас есть БД с огромным кол-во записей (новостная система) и каждый день добавляется по 500-1000 новостей, вот тут необходимо производить множество операций, создавать описание главного индекса, также создавать описание вспомогательного индекса, который будет потом добавлять в главный индекс, т.е. небольшое кол-во данных которые пришли за указанное вами время. Затем необходимо запускать процедуру объединения дельта индекса и главного индекса. Получается ситуация, что добавили новость в БД, а она появится в поисковом индексе только когда произойдет объединение индексов, время конечно задаёт программер, но все таки это какой-то промежуток + плюс куча операций.

С версии 1.10 появились «realtime» (RT) индексы, они работают только с SphinxQL, т.е. заполнение данных в индекс, удаление, изменение происходит по средству SQL запросов (очень хорошо описано тут). Самая ВАЖНАЯ особенность этого типа индекса, то что данные попадают в тот же момент, когда они попадают в БД (вам нужно просто создать два запроса, один в БД, а другой в индекс Сфинкса). Этот индекс мне очень понравился и я его с успехом применил в одном проекте. Вот как с ним работать я бы и хотел поведать в этой статье.

Работа с RealTime Index

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

Создаем БД MySQL и в ней таблицу новостей

CREATE TABLE news (
	id INT(11) NOT NULL AUTO_INCREMENT,
	date TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
	title VARCHAR(500) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	descr VARCHAR(2000) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	keywords VARCHAR(200) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	text VARCHAR(16000) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	`show` TINYINT(1) NOT NULL DEFAULT '0',
	PRIMARY KEY (id)
)
COMMENT='Новости'
COLLATE='utf8_unicode_ci'
ENGINE=MyISAM
AUTO_INCREMENT=1;

Далее создадим конфиг для поисковика для индекса новостей.

index rt_news
{
	type 					= rt
	path 					= f:/sphinx/data/rt_news
	rt_field				= title
	rt_attr_timestamp		= date
	rt_field				= descr
	rt_field				= keywords
	rt_field				= text
	rt_attr_uint			= view

	rt_attr_string			= title
	rt_attr_string			= descr
	rt_attr_string			= keywords
	rt_attr_string			= text

	docinfo					= extern
	morphology				= stem_enru
	min_word_len			= 1
	html_strip				= 1
	charset_type			= utf-8
	enable_star				= 1
	rt_mem_limit			= 256M
}

searchd
{
	listen			= 127.0.0.1:9306:mysql41
	log				= f:/sphinx/searchd.log
	query_log		= f:/sphinx/query.log
	pid_file		= f:/sphinx/searchd.pid
	read_timeout	= 5
	max_children	= 30
	max_matches		= 1000
	workers			= threads # for RT to work
}

В конфиге есть две обязательные секциии index нужна для описания индекса и searchd тут описываются настройки самого демона (службы) поисковика.

Разберем.

После названия секции index идет название индекса (произвольное [a-zA-Z0-9_] возможно есть еще знаки, но думаю этого достаточно для описания), далее идет описание самого индекса, указывается тип индекса, путь где будет хранится индекс, лучше пусть путь будет на латинице, т.к. используется кодировка UTF-8 и под виндой русское название будет в кракозябрах. Далее идет описание полей и их тип подробнее о различных типах тут, далее идут управляющие команды индексом, какую морфологию использовать, сколько памяти отводить, какая кодировка и тд, есть еще куча настроек, но она специфична и зависит уже от поставленных задач, обращайтесь к документации.

В секции searchd описываются команды для поисковика, какой порт слушать для обращения к MySQL, где хранятся разные лог-файлы и прочее.

Имея конфиг запустим поисковик, буду показывать все в командной строки для наглядности

F:\sphinx\bin>searchd.exe --config f:\Sphinx\sphinx.conf
Sphinx 2.0.1-beta (r2792)
Copyright (c) 2001-2011, Andrew Aksyonoff
Copyright (c) 2008-2011, Sphinx Technologies Inc (http://sphinxsearch.com)

using config file 'f:\Sphinx\sphinx.conf'...
listening on 127.0.0.1:9306
precaching index 'rt_news'
precached 1 indexes in 0.011 sec

Служба запущена и показывает что слушает Mysql на порту 9306 и создан один индекс с названием rt_news.

Добавление данных в БД и индекс

Пришло время заполнить БД и индекс данными. Подключаемя к поисковику через mysql на порту 9306

F:\Zend\MySQL 5.1.52\bin>mysql -P9306
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 2.0.1-beta (r2792)

Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Запишем просто одну запись в БД (в примере только запрос, как его исполнять решать вам) и одну запись в поисковый индекс.

# В БД
INSERT INTO `news` (`id`, `date`, `title`, `descr`, `keywords`, `text`, `show`) VALUES (1, '2011-09-17 12:02:49', 'Название новости', 'Описание новости', 'Ключевые слова', 'Текст новости ', 1);
# В поисковый индекс
F:\Zend\MySQL 5.1.52\bin>mysql -P9306
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 2.0.1-beta (r2792)

Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> INSERT INTO `rt_news` (`id`, `date`, `title`, `descr`, `keywords`, `text`, `view`) VALUES (1, '1316250169', 'Название новости', 'Описание новости', 'Ключевые слова', 'Текст новости ', 1);
Query OK, 1 row affected (0.03 sec)

mysql>

Обратите внимание, что вставлять в индекс надо с указанием primary key, по умолчанию это поле id, также необходимо указывать название индекса куда вставляются данные.

Теперь в БД и индексе есть данные, можно с ними работать.

Извлечение данных из индекса

mysql> SELECT * FROM rt_news WHERE view=1\G;
*************************** 1. row ***************************
      id: 1
  weight: 1
    view: 1
    date: 1316250169
   title: Название новости
   descr: Описание новости
keywords: Ключевые слова
    text: Текст новости
1 row in set (0.00 sec)
# Параметр \G позволяет вывести данные вертикально

Изменение данных в БД и индексе

# Для БД
UPDATE `news` SET `title`='Name of news' WHERE  `id`=1 LIMIT 1;

 

# Для индекса
mysql> REPLACE INTO `rt_news` (`id`, `date`, `title`, `descr`, `keywords`, `text`, `view`) VALUES (1, '1316250169', 'Name of news', 'Описание новости'
, 'Ключевые слова', 'Текст новости ', 1);
Query OK, 1 row affected (0.03 sec)

# просмотр изменений для индекса
mysql> SELECT * FROM rt_news WHERE view=1\G;
*************************** 1. row ***************************
      id: 1
  weight: 1
    view: 1
    date: 1316250169
   title: Name of news
   descr: Описание новости
keywords: Ключевые слова
    text: Текст новости
1 row in set (0.00 sec)

Удаление данных из БД и индекса

# Для БД
DELETE FROM `news` WHERE  `id`=1 LIMIT 1;

 

# Для индекса
mysql> DELETE FROM `rt_news` WHERE  id=1;
Query OK, 0 rows affected (0.00 sec)

# просмотр данных из индекса
mysql> SELECT * FROM rt_news WHERE view=1\G;
Empty set (0.01 sec)

Заключение

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

Слышал мнение, что RT индексы сильно медленно работаю по сравнению с обычными индексами, но данная статья это опровергает.

Будут вопросы задавайте.

ЗЫ Еще один момент, все запросы которые относятся к БД я делал через обращение к mysql через порт 3306 (дефолтный), а запросы которые относятся к индексу через порт 9306, который указан в конфиге.

Статья просмотренна 405301 раз, зашло посетителей 34511

Спуск в Харьковские катакомбы

Август12

В компании в которой я работаю создается проект посвященный разным организациям нашего города и не только. И вот в рамках знакомства с организацией «Дети Подземелья» мы попросили провести небольшую экскурсию по «Харьковским катакомбам». Hазвать Харьковские катакомбы, катакомбами конечно трудно… это просто старые подвалы, какие-то лазы, но было интересно.

И так нас собралось 6 желающих для совершения «погружения» под землю. Мы встретились с Андреем Ковалевым, президентом этой организации на улице Рымарская 23.

Как нам рассказали, это старое здание постройки 19 века, и в то время под землей построили большой подвал для хранения угля и прочего провианта, сейчас часть этого подвала завалена обрушением стен и потолка, другие вполне сохранившиеся.
В 2006 году под землю провалился автобус с музыкантами, подробнее можно почитать тут.

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

В общем первый спуск мне понравился.

Освещения у меня было только с фонарика мобильного, поэтому фотки получили не очень:

Вид на Харьковску Филармонию

Вид на Харьковскую Филармонию

Вход катакомбы

Вход катакомбы

Вид с улицы в сами катакомбы, дырка использовалась для засыпания угля

Вид с улицы в сами катакомбы, дырка использовалась для засыпания угля

Главный зал. Тут есть колонная поддерживающая свод и соверменные канализации.

Главный зал. Тут есть колонная поддерживающая свод и соверменные канализации.

Это мы на перекрестке коридоов, осещаем рассказчика всеми фанарями.

Это мы на перекрестке коридоров, освещаем рассказчика всеми фанарями.

Высота земли над нами 3 метра, и даже тут есть корни и улитки! А там ведь тьма!

Высота земли над нами 3 метра, и даже тут есть корни и улитки! А там ведь тьма!

Улитка, местный житель

Улитка, местный житель

Вид на дырку для угля с катакомб

Вид на дырку для угля с катакомб

PS Вот еще коллега помогла найти статью Привидений в харьковских подземельях нет
PSS вот еще нашел

Статья просмотренна 393755 раз, зашло посетителей 29252

Мой первый jQuery плагин. Работа с размерами текста.

Август8

Фреймверк jQuery мне понравился с первых строк кода которые я на нём написал. Для одного проекта мне нужно было написать свой jQuery плагин для увеличения/уменьшения шрифта в статье, как и что делать я был не в курсе, помогли статьи на эту тему с разных источников.

Требования к jQuery плагину:
1 От начального значения шрифта текста уметь изменять высоту шрифта в заданом диапазоне и с заданым шагом.
2 запоминать измененное значение шрифта, дабы при следующем заходе на странице шрифт был выбранной ранее высоты.

Первый пункт подразумевает кол-во итераций в увеличение/уменьшение от основного значения, шаг — на какое число будет изменен начальный размер шрифта.
Пример, допустим изначальный размер шрифта равен 16px, если указать кол-во итераций равное 3-м, а шага 1, то это означает, что шрифт при увеличении будет изменяться 3 раза на 1 пиксель (17 18 19), при уменьшении будет иметь значения (15 14 13). Менять указанные переменные можно при настройке шрифта.

Для реализации второго пункта я привлек к работе возможность работы с куками, а именно плагин jquery.cookie.js, если данного плагина не будет, то основной палгин будет работать, но сохранять выбранный размер шрифта не сможет.

Ниже приведен тест самого плагина:

(function($) {
	/*
	 * значени по умолчанию
	 */ 
	var defaults = {
		iteration : 5,
		step : 1
	};
	
	/*
	 * глобальная переменная опций 
	 */
	var options;
	
	/*
	 * настройка кукисов 
	 */
	var cookieSettings = {
		expires: 7,
		path : '/',
		name : 'zoomtext'
	};
	
	/*
	 * Создаем объект с методами
	 */ 
	var methods = {
		
		/*
		 * Инициализация
		 */
		init:function(params) {

			var type, size;
			options = $.extend({}, defaults, params);

			if ($.cookie != undefined &amp;&amp; $.cookie(cookieSettings.name) != null )
			{
				if ($.cookie(cookieSettings.name) == $(this).css('font-size'))
				{
					type = options.type =  'px';
					size = options.defaultSize = $.cookie(cookieSettings.name).split(options.type).join('');
				}
				else
				{
					type = 'px';
					size = $.cookie(cookieSettings.name).split(type).join('');
					options.type =  'px';
					options.defaultSize = $(this).css('font-size').split(options.type).join('');
				}
			}
			else
			{
				type = options.type =  'px';
				size = options.defaultSize = $(this).css('font-size').split(options.type).join('');
			}

			$(this).css('font-size', size+type);			

			return $(this).data(cookieSettings.name) ? this : $(this).data(cookieSettings.name, true);
		},
		
		/*
		 * Увеличение шрифта
		 */
		inc:function() {
			
			var maxSize = parseInt(options.defaultSize)+options.iteration;
			var currentSize = parseInt($(this).css('font-size').split(options.type).join(''));

			if (currentSize &lt; maxSize)
			{
				var changedSize = (currentSize+options.step).toString()+options.type;
				$.cookie != undefined ? $.cookie(cookieSettings.name, changedSize, cookieSettings) : '';
				$(this).css('font-size', changedSize);
			}
		},

		/*
		 * Уменьшение шрифта
		 */
		dec:function() {
			
			var minSize = parseInt(options.defaultSize)-options.iteration;
			var currentSize = parseInt($(this).css('font-size').split(options.type).join(''));

			if (currentSize &gt; minSize)
			{
				var changedSize = (currentSize-options.step).toString()+options.type;
				$.cookie != undefined ? $.cookie(cookieSettings.name, changedSize, cookieSettings) : '';
				$(this).css('font-size', changedSize);
			}			
		},
		
		/*
		 * Сброс шрифта
		 */
		reset:function() {
			$.cookie != undefined ? $.cookie(cookieSettings.name, null, cookieSettings) : '';
			$(this).removeAttr('style');
		}
	};

	/*
	 *  магические методы для работы плагина
	 */
	$.fn.zoomtext = function(method){
		if (methods[method]) {
			// переброс на запрашиваемый метод + передача параметров метода
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		} else if (typeof method === 'object' || ! method) {
			// передача методу инициализации (init) параметров
			return methods.init.apply(this, arguments);
		} else {
			// просто инициализация, если что-то пошло не так
			return methods.init.apply(this);
		}
	};
})(jQuery);

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

	/*
	 * значени по умолчанию
	 */ 
	var defaults = {
		iteration : 5,
		step : 1
	};
	
	/*
	 * глобальная переменная опций 
	 */
	var options;
	
	/*
	 * настройка кукисов 
	 */
	var cookieSettings = {
		expires: 7,
		path : '/',
		name : 'zoomtext'
	};

в переменной defaults есть два ключа:
iteration, задает кол-во итераций в ту или другу сторону (описывал чуть выше в статье),
step, задает шаг шрифта, т.е. приращение к существующему.

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

Переменная cookieSettings необходима для работы с куками,
ключ expires задает кол-во дней жизни куки от начала её становки,
ключ path указывает на какую область сайта дейстует кука, в данном случае на весь сайт,
ключ name это название куки.

Для более полного описания параметров переменной cookieSettings лучше обратится к официальной документации

Далее я реализовал несколько методов для работы:
init — метод который определяет тукущий размер шрифта, применяет его к тексту с помощью инлайл-стилей (style=’font-size:размер_шрифта’) и сохраняет данные с помощью функции jQuery.data();

inc — метод который позволяет увеличить шрифт, если не превышен порог по увеличению (defaults.iteration), применяет к тексту и записывает данные в куку (если плагин кук подключен и работает);

dec — метод который позволяет уменьшить шрифт, если не превышен порог по уменьшению (defaults.iteration), применяет к тексту и записывает данные в куку (если плагин кук подключен и работает);

reset — позволяет сбросить шрифт к изначальному состоянию и убить куку.

Как видно из выше описанного ничего сложного тут нет, но где-же магия? Магия заключается в этих строках, повторное использование кода, цепочка вызовов:

	/*
	 *  магические методы для работы плагина
	 */
	$.fn.zoomtext = function(method){
		if (methods[method]) {
			// переброс на запрашиваемый метод + передача параметров метода
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		} else if (typeof method === 'object' || ! method) {
			// передача методу инициализации (init) параметров
			return methods.init.apply(this, arguments);
		} else {
			// просто инициализация, если что-то пошло не так
			return methods.init.apply(this);
		}
	};

Более подробно описано в статье jQuery для начинающих. Часть 7. Пишем плагины, на блоге Антона Шевчука, за что ему большое спасибо!

Как использовать этот плагин:

Подключаем jQuery, plugins

&lt;script src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;http://blog.nagaychenko.com/jquery/plugins/jquery.cookie.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;http://blog.nagaychenko.com/jquery/plugins/jquery.zoomtext.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;

Необходимо создать управления, т.е. теги которые будут отвечать за увеличение/сброс/уменьшение шрифта, в моём случае я исользовал конструкцию ul-li

&lt;ul id=&quot;zoom&quot;&gt;
	&lt;li&gt;&lt;a href=&quot;#zoom&quot; id=&quot;increase&quot; title=&quot;увеличить шрифт&quot;&gt;increase&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;#zoom&quot; id=&quot;reset&quot; title=&quot;сбросить шрифт&quot;&gt;reset&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;#zoom&quot; id=&quot;decrease&quot; title=&quot;evtymibnm шрифт&quot;&gt;decrease&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

каждому элементу li я задал ИД, для отслеживания событий кликов по ним.
Далее сам текст я указал в параграфе с уникальным ИД

&lt;p id=&quot;content&quot;&gt;некий текст&lt;/p&gt;

Вызываем плагин с нужными параметрами и создаем обработчики кликов на элементы управления

&lt;script type=&quot;text/javascript&quot;&gt;
$(function(){
	// вызов плагина с передачей необходимых значений
	$('p#content').zoomtext({step:1, iteration:2}); 

	// отслеживаем кликл для увеличения шрифта
	$('a#increase').unbind().bind('click',  function(){ 
		$('p#content').zoomtext('inc');
	});

	// отслеживаем кликл для сброса шрифта
	$('#reset').unbind().bind('click', function(){ 
		$('p#content').zoomtext('reset');
	});

	// отслеживаем кликл для уменьшения шрифта
	$('a#decrease').unbind().bind('click', function(){
		$('p#content').zoomtext('dec');
	});
});
&lt;/script&gt;

Теперь все это вместе на одной странице демо тут.

Текст который я использовал в статье был взят с сайта сонников, гороскопов, знаков зодиака. Здесь я всегда смотрю свой бесплатный гороскоп.

Буду рад услышать любые замечания и предложения через комментарии.

Статья просмотренна 395331 раз, зашло посетителей 29114

 « Older Entries 

Облако тегов

cli csv dump events form Kohana locale models MySQL mysqldump orm PHP tools trigger validate газ газовый счетчик итоги кеширование переменные

Облако тегов плагина WP Cumulus для WordPress требует для просмотра Flash Player 9 или выше.

Я на твиттере!

  • у твиттера тоже бывают перерывы...

Календарь

Сентябрь 2016
Пн Вт Ср Чт Пт Сб Вс
« Июл    
 1234
567891011
12131415161718
19202122232425
2627282930  

Сейчас на сайте