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

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

Валидация формы на фреймворке 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

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

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

Логирование ошибок сайта для 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 ;

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

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

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

Поиск узких мест при создании сайтов на фреймверке 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)

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

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

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

Высокая нагрузка сайта написанного на 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, которые помогут Вам сэкономить ваши средства.

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

Создание и валидация формы на фреймворке Kohana. Часть 1

Январь13

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

На правах рекламы — интересный СЕО блог.

Для пример возьмем форму по этой ссылке.

Описание класса Form в Kohana

Для построения формы используется класс Form. Методы класса все статические, доступ к методам осуществляется через ::, например Form::input(…); Если так написать, то будет создан тег input и т.д.

Основная часть методов, которые формируют теги первым параметром принимает значение атрибута name,т.е. название переменной которое будет со своим значением передано на сервер. Вторым параметром принимается значение для создаваемого тега, для тега input это значение атрибута value, для тега select это массив, ключи которого есть значение тега option, а значение массива это то, что стоит между тегом option. Третьим параметров, в основном, принимается массив дополнительных атрибутов для создаваемого тега. Ключ массива это название атрибута, а значение массива — это значение атрибута, например array(‘id’=>’id_tag’).

Опишем более подробно.

Создание тега form

Для этого используется метод Form::open. В качестве первого аргумента он принимает значение атрибута action, но можно передавать NULL, тогда значение урла будут соответствовать текущей странице. Если передать пустоезнавение ‘ ‘, то тогда урл будет браться из переменной Kohana::base_url, которую мы задаем в bootstrap.php.

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

<!--?php echo Form::open(NULL, array('id'=-->'contact', 'enctype'=>'multipart/form-data'));?>

будет выведено</pre>
<form id="contact" action="/contact" method="post" enctype="multipart/form-data" accept-charset="utf-8">

Для закрытия тега формы применяется метод Form::close, он не принимает никаких параметров.

<!--?php echo Form::close();?-->

будет выведено

Создание тега input

Для создание этого тега предназначен метод Form::input.
Первый параметр это название тега, т.е. значение атрибута name=’name’
Второй параметр это значение атрибута value=’value’
Третий параметр это массив с данными по атрибутам.

<!--?php echo Form::input('name', Security::xss_clean(Arr::get($_POST, 'name', '')), array('id'=-->'name'));?>

будет выведено

<input id="name" type="text" name="name" value="" />

В атрибутах можно передать тип для тега input и видоизменить форму, можно сделать тип пароль, чекбокс, рейдиобатон и тд, но есть и отдельные методы которые просто реализуют некоторые типы, наверное это сделано для удобства понимая кода. К таким метода относятся Form::hidden, Form::password, Form::file.

Для создания чекбосков используется метод Form::checkbox. Все параметры похожи, имя, значение, выбран или нет элемент и массив атрибутов. Особо можно выделить третий параметр $checked он принимает истину или ложь и показывает выбран данный элемент формы или нет.

<!--?php echo Form::radio('sex', 1, ((isset($_POST['sex']) AND $_POST['sex']==1) ? TRUE : FALSE), array('id'=-->'your_sex_mail'));?>
<!--?php echo Form::checkbox('track', 0, (isset($_POST['track']) ? TRUE : FALSE), array('id'=-->'track'));?>

будет выведено, соответственно

<input id="your_sex_mail" type="radio" name="sex" value="1" />
<input id="track" type="checkbox" name="track" value="0" />

Создание тега textarea

Для создание этого тега предназначен метод Form::textarea.
Первый параметр это название тега, т.е. значение атрибута name=’name’
Второй параметр это сам текст который оказывается внутри тега.
Третий параметр это массив с данными по атрибутам.
Четвертый параметр предназначен для двойной кодировки спецсимволов, смотрите ф-цию htmlspecialchars

<!--?php echo Form::textarea('descr', Security::xss_clean(Arr::get($_POST, 'descr', '')), array('id'=-->'descr'));?>

будет выведено, соответственно

<textarea id="descr" name="descr" rows="10" cols="50"></textarea>

Создание тега select

Для создание этого тега предназначен метод Form::select.
Первый параметр это название тега, т.е. значение атрибута name=’name’
Второй параметр это массив с данными.
Третий параметр это или массив значений или значение выбранного элемента
Четвертый параметр это массив с данными по атрибутам.

Также этот тег позволяет построить вспомогательный элемент optgroup. Для его создания необходимо передавать массив, у которого в качестве ключа будет название группы а значение это будет вложенный массив ключ=>значение для элемента option.

<!--?php echo Form::select('type_error', array('0'=-->' ', '1'=>'изменение информации о сайте', '2'=>'стилистические, грамматические, орфогр. ошибки'), (isset($_POST['type_error']) ? $_POST['type_error'] : 0), array('id'=>'type_error'));?>

будет выведено, соответственно
<select name="type_error"> <option selected="selected" value="0"> </option></select>
<select name="type_error"> <option value="1">изменение информации о сайте</option></select>
<select name="type_error"> <option value="2">стилистические, грамматические, орфогр. ошибки</option></select>

Создание тега label

Первый параметр принимает значение ИД основного элемента формы, к которому привязан.
Второй элемент показывает описание к тегу.
Третий параметр — массив атрибутов.

<!--?php echo Form::label('email', __('Ваш эл. адрес').':'.'<span--> *');?>
<!--?php echo Form::input('email', Security::xss_clean(Arr::get($_POST, 'email', ' ')), array('id'=-->'email'));?>

будет выведено, соответственно

<label for="email">Ваш эл. адрес:<span> *</span></label>
<input id="email" type="text" name="email" value="" />

Остальные теги

В данном классе есть еще методы — submit, image, button. Все они являются частным случаем тегом input.

Пример построения формы

Тут привожу код вьюхи, формы которую рассматриваем.

</pre>
<h1></h1>
<pre>
<!--?php echo Form::open(Request::instance()--->uri, array('id'=>'contact', 'enctype'=>'multipart/form-data'));?>

<!--?php if(isset($errors) AND count($errors)-->0):?>
	<!--?php echo View::factory('errors')--->bind('errors', $errors)?>
<!--?php endif;?--></pre>
<div class="require"></div>
<fieldset title="<?php echo __('Ваши данные');?><br />">
<legend><!--?php echo __('Ваши данные');?--></legend>
<ul>
	<li><!--?php echo Form::label('name', __('Ваше имя').':'.'<span--> *');?>
 <!--?php echo Form::input('name', Security::xss_clean(Arr::get($_POST, 'name', '')), array('id'=-->'name'));?></li>
	<li><!--?php echo Form::label('email', __('Ваш эл. адрес').':'.'<span--> *');?>
 <!--?php echo Form::input('email', Security::xss_clean(Arr::get($_POST, 'email', '')), array('id'=-->'email'));?></li>
	<li class="sex">
<div></div>
 <!--?php echo Form::label('your_sex_mail', __('муж').':');?-->
 <!--?php echo Form::radio('sex', 1, ((isset($_POST['sex']) AND $_POST['sex']==1) ? TRUE : FALSE), array('id'=-->'your_sex_mail'));?>
 <!--?php echo Form::label('your_sex_femail', __('жен').':');?-->
 <!--?php echo Form::radio('sex', 0, ((isset($_POST['sex']) AND $_POST['sex']==0) ? TRUE : FALSE), array('id'=-->'your_sex_femail'));?></li>
</ul>
</fieldset>
<fieldset title="<?php echo __('Ваш вопрос');?><br />">
<legend><!--?php echo __('Ваши вопрос');?--></legend>
<ul>
	<li><!--?php echo Form::label('type_error', __('Описание проблемы').':'.'<span--> *');?>
 <!--?php echo Form::select('type_error', $type_error, (isset($_POST['type_error']) ? $_POST['type_error'] : 0), array('id'=-->'type_error'));?></li>
	<li><!--?php echo Form::label('descr', __('Описание проблемы').':'.'<span--> *');?>
 <!--?php echo Form::textarea('descr', Security::xss_clean(Arr::get($_POST, 'descr', '')), array('id'=-->'descr'));?></li>
	<li><!--?php echo Form::label('img', __('Картинка с ошибкой').':');?-->
 <!--?php echo Form::file('img', array('id'=-->'img'));?></li>
	<li><!--?php echo Form::label('track', __('Отслеживать вопрос').':');?-->
 <!--?php echo Form::checkbox('track', 0, (isset($_POST['track']) ? TRUE : FALSE), array('id'=-->'track'));?></li>
</ul>
</fieldset>
<ul class="captcha">
	<li></li>
	<li><!--?php echo Form::label('captcha', __('Введите код').':'.'<span--> *');?>
 <!--?php echo Form::input('captcha', NULL, array('id'=-->'captcha'));?></li>
</ul>
<div class="buttons"></div>
<pre>
<!--?php echo Form::close();?-->

Небольшое описание.
$errors — это переменная которая в себе будет нести ошибки при валидации формы;
Security::xss_clean(Arr::get($_POST, ‘descr’, »)) — это часть чистит вводимые значение от XSS, а также подставляет дефолтное значение если нет значение текущей переменной, а данном случае descr.

Описание контроллера, валидации приведу в следующей статье.

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

Как убрать index.php из адресной строки используя фреймверк Kohana

Октябрь12
Быть красивой легко, загляните в салон-красоты http://salon-aquarelle.ru/.

kohanaСегодня просматривал ключевые слова по каким ко мне попадают на блог и увидел, что есть много вопросов касающихся исключению index.php из урла. Решил написать как это можно сделать.

Когда скачиваете фреймворк Kohana то в корне архива увидите файлик example.htaccess, переименовываем его в .htaccess.

Далее меняем путь к папке в которой установлен фреймворк, если это необходимо, по-дефолту там написано:

RewriteBase /kohana/
я заменил на 
RewriteBase / т.к. у меня весь фрейм лежит в корне

Далее идем в папку в папку application и находим файлик bootstrap.php — это настроечный файл для всего сайта, его изменения важны для сайта, в нем осуществляется первоначальная настройка модулей, роутинга и тд.

Находим в этом файле строчки

/**
 * Initialize Kohana, setting the default options.
 *
 * The following options are available:
 *
 * - string   base_url    path, and optionally domain, of your application   NULL
 * - string   index_file  name of your index file, usually "index.php"       index.php
 * - string   charset     internal character set used for input and output   utf-8
 * - string   cache_dir   set the internal cache directory                   APPPATH/cache
 * - boolean  errors      enable or disable error handling                   TRUE
 * - boolean  profile     enable or disable internal profiling               TRUE
 * - boolean  caching     enable or disable internal caching                 FALSE
 */
Kohana::init(array(
	'base_url'   => '/',
));

и вот чтобы избавиться от вида строки http://example.com/index.php/contoller/action/
на вид http://example.com/contoller/action/

изменяет инициализацию

Kohana::init(array(
    'base_url'   => '/',
    'index_file' =>'',
));

Удачи.

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

Как построить запрос типа WHERE MATCH AGAINST в Kohana3

Сентябрь2

Конструктор запросов

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

И вот случилось у меня некая трудность, как я думал в начале, с написание запроса в условиях которого содержится конструкция MATCH(col1, col2,…) AGAINST(»text search»). На данный момент ORM я не юзаю, т.к. не представляю как можно там получить записи при объединении нескольких таблиц, ил запись с таблицы со сложным условием, но то ли еще будет…

Немного отступлю от проблемы, вкратце расскажу, для непосвященных, что это за конструкция. Это конструкция позволяет делать полнотекстовый поиск в наборе указанных полей. Имеет некие настройки для поиска, ищет в указанных полях. Для работы нужна таблица только! типа MyISAM, а также индекс FULLTEXT на искомые поля. Поля должны быть типа VARCHAR, CHAR, TEXT. Более детально на официальной документации.

И так, нужно составить запрос типа:

SELECT * FROM `table` WHERE MATCH(`col1`) AGAINST("search text");

Можно поступить двумя способами.
1 Просто написать такой запрос и запихнуть его в конструкцию:

$data = DB::query(Database::SELECT, 'SELECT * FROM `table`
WHERE MATCH(`col1`) AGAINST("search text")')->execute()->as_array();

В переменной $data у нас будет результат запроса.

Это самый простой способ получения запроса, но иногда такого мало. Например у меня есть запрос на поиск, который включает себя много полей, разные условия поиска и еще разные условия сортировки. В таком случае уж трудно будет изменять запрос… (У меня в модели несколько методов наразные части запроса — очень удобно управлять запросом).

2 Более сложным, но более эффективным путем при решении сложных запросов лучше пользоваться конструктором запросов. Данный пример вверху можно записать так (пока без части с уловием):

$data = DB::select()->from('table')->execute()->as_array();

Теперь как же быть с условием? Для условий where() существует целый класс Database_Query_Builder_Where в нем содержаться куча методов для построения запросов с логикой AND и OR.
Сам запрос сводиться к 3-м обязательным параметрам:

$db->where('column', 'operator', 'value');
например WHERE id=2 будет записано в виде:
$db->where('id', '=', 2);

Причем если значение это integer кавычек не будет, а если string система сама поставит кавычки.

Вернемся к нашему выражению.

SELECT * FROM `table` WHERE MATCH(`col1`) AGAINST("search text")

Что же здесь у нас столбец, что оператор и что значение??? Поставив такое выражение

$db->where('MATCH(col1)', '', 'AGAINST ("search text")');

я получи вид

WHERE `MATCH(col1)` 'AGAINST(\"search text\")'

естественно запрос не сработал… Немного поразмыслив я попытался обратиться к классу DB::expr(), который позволяет вставить в запрос выражение «как есть», т.е. не обрамляет его символами ` . И вот что получилось:

$query_m = DB::expr(' MATCH(`col1`) ');
$query_a = DB::expr(' AGAINST(addslashes("search text")) ');

$db->where($query_m, '', $query_a);

Естественно при использовании класса DB::expr() приходиться самому заботиться об SQL-инъекциях добавляя ф-цию addslashes, в итоге получилось выражение вида:

WHERE MATCH(`col1`) AGAINST("search text")

И так, подводим итог всей этой ночной писанины:

Для использования в условиях запроса сложных выражений при использовании конструктора запросов, необходимо прибегать к помощи класса DB::expr().
Вот полный пример:

$query_m = DB::expr(' MATCH(col1) ');
$query_a = DB::expr(' AGAINST(addslashes("search text")) ');

DB::select()->from('table')->where($query_m, '', $query_a);

получим запрос вида:

SELECT * FROM `table` WHERE MATCH(col1) AGAINST("search text")

Также можно построить такой запрос:

SELECT * FROM `table` WHERE FIELD_IN_SET('1,2,4,5', `column`);

пишем

$f = DB::expr(' FIND_IN_SET('1,2,4,5', ');
$c = DB::expr(' `column` ');

DB::select()->from('table')->where($f, '', $c);

В итоге

SELECT * FROM `table` WHERE FIELD_IN_SET('1,2,4,5', `column`)

Всем удачи в работе!

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

Создание модуля на фреймворке Kohana

Июль14

В этой статье хочу рассказать как создать свой модуль на фреймворке Kohana.

Что такое модуль, описывается тут

Немного теории

Сам фреймворк имеет 3 основные папки:

application — тут храниться ваш проект
modules — тут модули
system — тут ядро

Все названия папок и файлов, новых и старых, пишутся с маленькой буквы, желательно латинского алфавита, т.к. в методе Kohana::auto_load() все файлы приводятся к нижнему регистру. На это стоит обратить внимание, т.к. на ОС *unix это вылезет и будут потом куча вопросов, а чего у меня (под ОС Windows) работает, а тут нет!!!!! Насчет латиницы, ну тут особых ограничений нет, но принято всё писать латиницей!

Создания модуля

Заходим в папку modules и создаем необходимую нам папку.

Для того, чтобы это было наглядней создадим модуль для получения валюты из центробанка РФ на текущий день (украинского НБУ, не нашел api). Назовем папку модуля — currency (в названии папки можно использовать знак _).

modules

currency

Далее нужно создать простой класс, для какой-то работы, то просто в этой папке его и создаем, например класс назовем Test, значит файл будет называться test.php (расширение должно быть такое, какое описано в index-файле вашего проекта, константа define(‘EXT’, ‘.php’);)

Если же будите использовать MVC паттерн, то надо создать особую структуру папок.

classes -эта папка для классов контроллера и моделей

controller — если есть контроллер, то он храниться тут (назв Controller_Name, файл name.php)

model — тут модель, если есть (Model_Name, name.php)

config — хранятся всяческие настройки для модуля

views — шаблоны для отображения

vendor — библиотеки сторонних производителей

init.php — файл, инициализируемый при подключении модуля

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

Как подключить модуль

Заходим в файл проекта — bootstrap.php и добавляем в описание модулей свой

Kohana::modules(array(
	'auth'       => MODPATH.'auth',       // Basic authentication
	'database'   => MODPATH.'database',   // Database access
	'image'      => MODPATH.'image',      // Image manipulation
	'orm'        => MODPATH.'orm',        // Object Relationship Mapping
	'pagination' => MODPATH.'pagination', // Paging of results
	'cache'      => MODPATH.'cache',      // Cache module
	'currency'	 => MODPATH.'currency',   // Подключения модуля получения валюты!!!!!
));

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

При загрузке сразу подключается файл init.php, если он есть, при инициализации модуля. Обычно в этом файле пишут роутер по которому будет доступна работа модуля, если такое требуется.

Переходим к практике

Структура папок описано, начнем писать.

Описываем файл инициализации init.php:

// Роутеры для работы с валютами
Route::set('currency', 'getcurrency(/<date>)', array('date'=>'([0-9\/]+)'))
    ->defaults(array(
            'controller' => 'currency',
            'action'     => 'index',
            'date'		 => NULL,
    )
);

Далее описываем работу с контроллером Controller_Currency (currency.php):

class Controller_Currency extends Controller {

	/**
	 * Получение данных по указанному дню
	 *
	 * @return void
	 */
	public function action_index()
	{
		$date = $this->request->param('date', date('d/m/Y'));

		$currency_model = new Model_Curr;
		$currency_data = $currency_model->get_currency_from_server($date);

		print_r($currency_data);
	}
}// End Currency Controller

Описываем работу модели Model_Curr (curr.php):

class Model_Curr extends Model {

	/**
	 * Получения данных с сервера ЦБРФ (http://www.cbr.ru/)
	 *
	 * @param string $date текущая дата
	 * @return string
	 */
	public function get_currency_from_server($date)
	{
		$allow_currency = array('EUR', 'USD');
		$link = "http://www.cbr.ru/scripts/XML_daily.asp?date_req=$date";
		$text = @file_get_contents($link);

		$xml = new SimpleXMLElement($text);

		foreach ($xml->Valute as $curr)
		{
			if(in_array($curr->CharCode, $allow_currency))
			{
				$currency[] = $date . ' ' . $curr->CharCode . ' ' . UTF8::str_ireplace(',', '.', (string)$curr->Value);
			}
		}

		return $currency;
	}
} // End Currency Model

В итоге набираем в строке адреса вашего браузера ссылку http://yourprojectname/getcurrency/14/06/2010

и на экране получаем ответ:

Array
(
    [0] => 24/03/2012 USD 29.4038
    [1] => 24/03/2012 EUR 38.8189
)

Демо тут

Данные по валюте получены. Модуль можно использовать в других проектах.

Подводные камни

При работе с модулями могут произойти некоторые проблемки.
1 Если имеется в проекте роутер с таким же урлом как в файле инициализации модуля, то роутер модуля будет замещен роутером проекта.
Для избегания, просто делаем другой роутер или ставим описание роутера проекта раньше чем описание загрузки модулей в файле bootstrap.php.

2 Если одинаковое название моделей, то модель будет вызвана из проекта, а не из модуля!!! Обойти эту проблему мне не удалось, т.к. само ядро ищет файл в проекте и если найдено поиск дальше не идет, а если нет, то ищется пока не будет найдено. Единственный выход — прописать другое название для модели.

UPD: Пересобрал модуль для версии Kohana 3.2, часть комментариев уже не валидны. Через время выложу на github.com

UPD2: Выложил всё на GitHub

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

Работа с командной строкой в Kohana3 (KO3)

Апрель22
Cтолешница из искусственного камня своими руками, большой выбор столещниц, умывальник и подоконников.

Всем привет!

Есть некоторые задачи которые выполняются не по воле пользователей (зашел в браузер, набрал сайт и получил сайт — выполнилась какая-то задача), а по воле программистов, самих серверов в определенное время (cron). Писать о работе планировщика заданий не буду, а опишу здесь как правильно работать с командной строкой в Kohana3. Пока пишу опираясь на ОС Windows, когда буду обновлять на сервере сделаю обновление, если будет нужен, о работе с командной строкой в *nix подобных системах.

Первым делом заходим в командную строку, для этого нажимаем клавишу ВИНДОВС (те кто не знают, клавиша с флагом корпорации) + R. В появившемся окне пишем ручками cmd и нажимаем enter. Простите, что так подробно, но есть «уникумы» которые и такое сделать не могут. Далее переходи на диск, где установлен PHP, для этого просто пишем D:(у меня он на диске D), затем пишем команду перехода к папке, где расположен PHP cd php5, нажимаем enter. Для проверки работы самого PHP пишем тестовую команду, узнаем версию PHP: php -v, если на экране отобразилась нужная информация, значит все у нас получилось. Все показано на рисунке:

Команды для работы с PHP в командной строке

Команды для работы с PHP в командной строке

Вторым делом, избавляемся от данных из супер глобального массива $_SERVER. Опытные программисты знают, что при работе с командной строкой, всех тех индексов которые есть при работе через браузер просто НЕТ.
Вот выдержка из документации:

$_SERVER is an array containing information such as headers, paths, and script locations. The entries in this array are created by the web server.

Я, например, об этом знал, но забыл 🙁 и потратил 2 часа чтобы от этого избавится. В основном, для получения полного пути к папке где лежит запускаемый скрипт, я использовал переменную $_SERVER[‘DOCUMENT_ROOT’], но в самой Kohana есть ведь константа DOCROOT, которая и дает нам то, что нужно!!!

Третьим делом удостоверяемся, что у нас последняя версия фреймверка, т.к. из-за одного маленького, необязательного, опционально аргумента одной из ф-ций которая используется в классе CLI работа велась НЕКОРРЕКТНО! Или, если нет желания менять свою старую версию, тогда в файле:

/system/classes/kohana/cli.php

необходимо найти строки:

if (strpos($opt, '='))
{
	// Separate the name and value
	list ($opt, $value) = explode('=', $opt);
}

и заменить на следующий фрагмент кода

if (strpos($opt, '='))
{
	// Separate the name and value
	list ($opt, $value) = explode('=', $opt, 2);
}

Ну и наконец, как же работать с командной строкой.

Для работы пишем в нашей открытой командной строке, для Виндовс, если используете &, то все данные надо брать в кавычки:

D:\php5>php путь/до/index.php --uri=путь/из/роутера
--get="maxnag[]=1&maxnag[]=2" --post="intik=ddd"

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

B наш скрипт прекрасно переключается на классы и методы, которые описаны в роутере и передает данные, в данном случае это два массива $_GET и $_POST.

Вот что получилось:

Результат работы скрипта через командную строку

Результат работы скрипта через командную строку

У командной строки есть опции, в данный момент времени их 4, все они должны начинаться с ==, ну и пробел перед первым равно.

uri — строка которая идет после домена, например http://examlpe.com/test/one/1, uri=test/one/1
method — из документации: string method: GET, POST, PUT, DELETE, etc
get — данные для супер глобального массива $_GET
post — данные для супер глобального массива $_POST

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

Теперь можно смело в файле-конфигурации планировщика задач писать команды для работы с фреймверком Kohana из командной строки.

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

Проверка существования файла-картинки перед выводом на экран в Kohana3

Январь19
Циркон удобрение лучше, что есть для почвы!

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

Во многих фреймверках имеются инструменты для работы с частыми HTML тегами, например для работы с тегом IMG во фреймворке Kohana имеется статический метод image, который располагается в классе HTML. Я рекомендую именно писать ссылки, картинки, элементы форм используя инструменты фреймверка, в чем выигрыш? Да в том, что можно управлять создаваемым тегом.

Вот реальный пример, когда мы в Kohana пишет тег image:

Html::image('/img/news/test.jpg', array('alt'=>'Testing'));

то при выводе на экран эта строка превратится в тег:

<img src="http://site.com/img/news/test.jpg" alt="Testing" />

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

http://nagaychenko.com/test.jpg

GET /test.jpg HTTP/1.1
Host: nagaychenko.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,uk;q=0.8,en-us;q=0.6,en;q=0.4,de;q=0.2
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: MANTIS_VIEW_ALL_COOKIE=4; MANTIS_PROJECT_COOKIE=1; 
Pragma: no-cache
Cache-Control: no-cache

HTTP/1.x 200 OK
Date: Thu, 21 Jan 2010 09:09:33 GMT
Server: Apache/2.2.14 (Unix) mod_ssl/2.2.14 OpenSSL/0.9.8e-fips-rhel5 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 PHP/5.2.11
Last-Modified: Thu, 21 Jan 2010 09:08:49 GMT
Etag: "3b90001-1a31b-47da90d10ea40"
Accept-Ranges: bytes
Content-Length: 107291
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: image/jpeg

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

	/**
	 * Creates a image link.
	 *
	 * @param   string   file name
	 * @param   array    default attributes
	 * @return  string
	 */
	public static function image($file, array $attributes = NULL, $index = FALSE)
	{
	    $file_from_another_server = FALSE;
	    
		if (strpos($file, '://') === FALSE)
		{
		    
		    $file_exists = (is_file($_SERVER['DOCUMENT_ROOT'].$file) AND file_exists($_SERVER['DOCUMENT_ROOT'].$file)) ? TRUE : FALSE;
			// Add the base URL
			$file = URL::base($index).trim($file,'/');
		}
		else
		{
		    $file_from_another_server = TRUE;
		}

		// Add the image link
		$attributes['src'] = $file;

		// провряем есть ли файл на ФС
		if ($file_exists)
		{
		    return '<img'.HTML::attributes($attributes).' />';
		}
		elseif($file_from_another_server)
		{
		    return '<img'.HTML::attributes($attributes).' />';
		}
	}

В коде я проверяю на присутствие именно файла, т.к. php-функция file_exists проверяет существование директории, т.е. написали вы просто путь и получите положительный ответ, но этоже не файл! Поэтому я еще и проверяю файл ли я передаю ф-циец is_file.

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

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

В помощь тебе, юзер.

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

 « Older Entries 

Облако тегов

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

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

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

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

Календарь

Ноябрь 2017
Пн Вт Ср Чт Пт Сб Вс
« Июл    
 12345
6789101112
13141516171819
20212223242526
27282930  

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