четверг, 8 ноября 2012 г.

Kohana 3: транслитерация кириллицы

Kohana уже доросла до версии 3.3, а проблема с транслитерацией кириллицы тянется уже довольно давно.

Использую следующие костыли/надстройки для решения этой проблемы.

В 'APPPATH/classes' создаём скрипт 'utf8.php' со следующим содержимым:

<?php defined('SYSPATH') OR die('No direct script access.');
class UTF8 extends Kohana_UTF8 {

 /**
  * @var  boolean  Does the server support UTF-8 natively?
  */
 public static $server_utf8 = NULL;

 /**
  * @var  array  List of called methods that have had their required file included.
  */
 public static $called = array();


 /**
  * Replaces special/accented UTF-8 characters by ASCII-7 "equivalents".
  *
  *     $ascii = UTF8::transliterate_to_ascii($utf8);
  *
  * @author  Andreas Gohr <andi@splitbrain.org>
  * @param   string   $str
  * @param   integer  $case
  * @return  string
  */
 public static function transliterate_to_ascii($str, $case = 0)
 {
  if ( ! isset(self::$called[__FUNCTION__]))
  {
   require APPPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;

   // Function has been called
   self::$called[__FUNCTION__] = TRUE;
  }

  return _transliterate_to_ascii($str, $case);
 }
}

В каталоге 'APPPATH' создадим каталог 'utf8' и поместим в него скрипт 'transliterate_to_ascii.php' со следующим содержимым:

<?php defined('SYSPATH') or die('No direct script access.');
/**
 * UTF8::transliterate_to_ascii
 *
 * @package    Kohana
 * @author     Kohana Team
 * @copyright  (c) 2007-2011 Kohana Team
 * @copyright  (c) 2005 Harry Fuecks
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
 */
function _transliterate_to_ascii($str, $case = 0)
{
 static $utf8_lower_accents = NULL;
 static $utf8_upper_accents = NULL;

 if ($case <= 0)
 {
  if ($utf8_lower_accents === NULL)
  {
   $utf8_lower_accents = array(
    'a' => 'a',  'o' => 'o',  'd' => 'd',  '?' => 'f',  'e' => 'e',  's' => 's',  'o' => 'o',
    '?' => 'ss', 'a' => 'a',  'r' => 'r',  '?' => 't',  'n' => 'n',  'a' => 'a',  'k' => 'k',
    's' => 's',  '?' => 'y',  'n' => 'n',  'l' => 'l',  'h' => 'h',  '?' => 'p',  'o' => 'o',
    'u' => 'u',  'e' => 'e',  'e' => 'e',  'c' => 'c',  '?' => 'w',  'c' => 'c',  'o' => 'o',
    '?' => 's',  'o' => 'o',  'g' => 'g',  't' => 't',  '?' => 's',  'e' => 'e',  'c' => 'c',
    's' => 's',  'i' => 'i',  'u' => 'u',  'c' => 'c',  'e' => 'e',  'w' => 'w',  '?' => 't',
    'u' => 'u',  'c' => 'c',  'o' => 'o',  'e' => 'e',  'y' => 'y',  'a' => 'a',  'l' => 'l',
    'u' => 'u',  'u' => 'u',  's' => 's',  'g' => 'g',  'l' => 'l',  '?' => 'f',  'z' => 'z',
    '?' => 'w',  '?' => 'b',  'a' => 'a',  'i' => 'i',  'i' => 'i',  '?' => 'd',  't' => 't',
    'r' => 'r',  'a' => 'a',  'i' => 'i',  'r' => 'r',  'e' => 'e',  'u' => 'u',  'o' => 'o',
    'e' => 'e',  'n' => 'n',  'n' => 'n',  'h' => 'h',  'g' => 'g',  'd' => 'd',  'j' => 'j',
    'y' => 'y',  'u' => 'u',  'u' => 'u',  'u' => 'u',  't' => 't',  'y' => 'y',  'o' => 'o',
    'a' => 'a',  'l' => 'l',  '?' => 'w',  'z' => 'z',  'i' => 'i',  'a' => 'a',  'g' => 'g',
    '?' => 'm',  'o' => 'o',  'i' => 'i',  'u' => 'u',  'i' => 'i',  'z' => 'z',  'a' => 'a',
    'u' => 'u',  '?' => 'th', '?' => 'dh', '?' => 'ae', 'µ' => 'u',  'e' => 'e',  '?' => 'i',
    'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e', 'ё' => 'yo', 'ж' => 'zh',
    'з' => 'z', 'и' => 'i', 'й' => 'j', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o',
    'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c',
    'ч' => 'ch', 'ш' => 'sh', 'щ' => 'shh', 'ъ' => '?', 'ы' => 'y', 'ь' => '?', 'э' => 'e', 'ю' => 'ju',
    'я' => 'ja',
   );
  }

  $str = str_replace(
   array_keys($utf8_lower_accents),
   array_values($utf8_lower_accents),
   $str
  );
 }

 if ($case >= 0)
 {
  if ($utf8_upper_accents === NULL)
  {
   $utf8_upper_accents = array(
    'A' => 'A',  'O' => 'O',  'D' => 'D',  '?' => 'F',  'E' => 'E',  'S' => 'S',  'O' => 'O',
    'A' => 'A',  'R' => 'R',  '?' => 'T',  'N' => 'N',  'A' => 'A',  'K' => 'K',  'E' => 'E',
    'S' => 'S',  '?' => 'Y',  'N' => 'N',  'L' => 'L',  'H' => 'H',  '?' => 'P',  'O' => 'O',
    'U' => 'U',  'E' => 'E',  'E' => 'E',  'C' => 'C',  '?' => 'W',  'C' => 'C',  'O' => 'O',
    '?' => 'S',  'O' => 'O',  'G' => 'G',  'T' => 'T',  '?' => 'S',  'E' => 'E',  'C' => 'C',
    'S' => 'S',  'I' => 'I',  'U' => 'U',  'C' => 'C',  'E' => 'E',  'W' => 'W',  '?' => 'T',
    'U' => 'U',  'C' => 'C',  'O' => 'O',  'E' => 'E',  'Y' => 'Y',  'A' => 'A',  'L' => 'L',
    'U' => 'U',  'U' => 'U',  'S' => 'S',  'G' => 'G',  'L' => 'L',  '?' => 'F',  'Z' => 'Z',
    '?' => 'W',  '?' => 'B',  'A' => 'A',  'I' => 'I',  'I' => 'I',  '?' => 'D',  'T' => 'T',
    'R' => 'R',  'A' => 'A',  'I' => 'I',  'R' => 'R',  'E' => 'E',  'U' => 'U',  'O' => 'O',
    'E' => 'E',  'N' => 'N',  'N' => 'N',  'H' => 'H',  'G' => 'G',  'D' => 'D',  'J' => 'J',
    'Y' => 'Y',  'U' => 'U',  'U' => 'U',  'U' => 'U',  'T' => 'T',  'Y' => 'Y',  'O' => 'O',
    'A' => 'A',  'L' => 'L',  '?' => 'W',  'Z' => 'Z',  'I' => 'I',  'A' => 'A',  'G' => 'G',
    '?' => 'M',  'O' => 'O',  'I' => 'I',  'U' => 'U',  'I' => 'I',  'Z' => 'Z',  'A' => 'A',
    'U' => 'U',  '?' => 'Th', '?' => 'Dh', '?' => 'Ae', 'I' => 'I',
    'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', 'Ё' => 'Yo', 'Ж' => 'Zh',
    'З' => 'Z', 'И' => 'I', 'Й' => 'J', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N', 'О' => 'O',
    'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 'Х' => 'Kh', 'Ц' => 'Tc',
    'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sch', 'Ъ' => '\'', 'Ы' => 'Y', 'Ь' => '', 'Э' => 'E', 'Ю' => 'Ju',
    'Я' => 'Ja'
   );
  }

  $str = str_replace(
   array_keys($utf8_upper_accents),
   array_values($utf8_upper_accents),
   $str
  );
 }

 return $str;
}

P.S. Под 'APPPATH' подразумеваю константу APPPATH определяемую в index.php:

define('APPPATH', realpath($application).DIRECTORY_SEPARATOR);

DX Auth: нашёлся на github'е

Некоторое время назад остро стояла проблема где скачать DX Auth, на http://dexcell.shinsengumiteam.com/dx_auth библиотека стала не доступна.

Несколько дней назад обнаружил свежую версию библиотеки на github'е: https://github.com/eyoosuf/DX-Auth

Там же но в ветке initial https://github.com/eyoosuf/DX-Auth/tree/initial можно найти версию 1.0.6.

Kohana 3: небольшая оптимизация ORM

ORM в Kohana очень удобная штука, но она постоянно выполняет запросы типа:

SHOW FULL COLUMNS FROM `users`

Запрос выполняется почти на каждое действие связанное с таблицей, в данном случае 'users'. Это происходит из-за того, что для выполнения различных операций с данными ORM необходимо знать структуру таблицы и наименование полей.

Что бы избавиться от таких запросов необходимо добавить модель описание структуры таблицы - массив $_table_columns. Думаю детально описывать стурктуру массива с данными не имеет смысла, так как в ниже приведённом примере всё хорошо понятно.

Например для модели User, расширим стандартную модель Model_Auth_User добавив описание столбцов таблицы:

<?php
defined('SYSPATH') OR die('No direct script access.');

class Model_User extends Model_Auth_User {

     protected $_table_columns     = array(
          'id' => array(
               'data_type'   => 'int',
               'is_nullable' => FALSE
          ),
          'email' => array(
               'data_type'   => 'string',
               'is_nullable' => FALSE
          ),
          'username' => array(
               'data_type'   => 'string',
               'is_nullable' => FALSE
          ),
          'password' => array(
               'data_type'   => 'string',
               'is_nullable' => FALSE
          ),
          'logins' => array(
               'data_type'   => 'int',
               'is_nullable' => TRUE
          ),
          'last_login' => array(
               'data_type'   => 'int',
               'is_nullable' => TRUE
          ));
}

Больше запросов SHOW FULL COLUMNS FROM `users` не будет, так как модель 'знает' поля таблицы и их свойства.

среда, 7 ноября 2012 г.

Ubuntu: как добавить/удалить пользователя в MySQL из консоли?

Бывает, что удалённая/виртуальная машина установлена и необходимо создать пользователей/базы данных и раздать им права, а ставить phpmyadmin не хочется. Или просто блеснуть умением работать с пользователя mysql из консоли перед коллегами ;)

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

mysql -u root -p

После ввода пароля мы окажемся в консоле mysql.

Создадим пользователя test с localhost и паролем 'password'.

create user 'test'@'localhost' identified by 'password';

Пользователь создан. Для проверки посмотрим список пользователей:

select user,host,password from mysql.user;

Создадим базу данных testdb.

create database testdb;

Дадим пользователю test полные права на базу testdb

grant all on testdb.* to 'test'@'localhost'

Удалим пользователя 'test'@'localhost'

drop user 'test'@'localhost'

Если вы создали пользователей для разных хостов и хотите всех удалить, то необходимо удалить их по одному через команду 'drop user' с указанием хоста

Внимание! Команда 'DROP USER' не закрывает автоматически все сессии открытые удаляемым пользователем. Если пользователь с открытой сессией был удалён, то удаление не будет иметь эффекта до закрытии сессии пользователем. После закрытия сессии следующая попытка входа в систему пользователем которого удалили не удастся.

Удаляем базу testdb

drop database testdb;

воскресенье, 4 ноября 2012 г.

Ubuntu Server: не реагирует на сигнал "Завершение работы"

Ubuntu Server 8.04.04 under VirtualBox
Ubuntu Server установленный в виртуальной машине (Oracle VirtualBox) не срабатывает на команду «Завершение работы», а логинится в каждую машину и шатдаунить в ручную довольно утомительно. Впрочем, это относить и к Ubuntu Server установленному на реальное железо.
Причина отсутствия реакции на «Завершени работы» или нажитае кнопки «Power» - не установленный демон отслеживающий события ACPI.
Для решения проблемы устанавливаем демон ACPI:
sudo apt-get install acpid
Проблема решена, теперь сервер срабатывает на сигнал "Завершение работы" и корректно работает команда:
sudo shutdown -P now

пятница, 2 ноября 2012 г.

Ubuntu: vsftpd в не показывает .htaccess и другие dotfiles

Настраивал сервер в виртуальной машине c Ubuntu 8.04.4 LTE и столкнулся со следующей проблемой: vsftpd не отображает файлы начинающиеся с точки 'dotfiles'.

Начал искать параметр отвечающий за отображение файлов начинающихся с '.'. Несколько раз бегло пролистал файл конфигурации... Затем прочитал весь файл с комментариями и не нашёл ни одного упоминания dot files.

Тут на помощь приходит 'man'

man vsftpd.conf
force_dot_files
    If  activated,  files  and  directories  starting with . will be
    shown in directory listings even if the "a" flag was not used by
    the client. This override excludes the "." and ".." entries.

    Default: NO

По умолчанию значение параметра 'NO'.

Дальше проще.

Открываем в текстовом редакторе с правами суперюзера файл конфигурации (/etc/vsftpd.conf)

sudo vim /etc/vsftpd.conf

или

sudo nano /etc/vsftpd.conf

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

force_dot_files=YES

Сохраняем файл и перезапускаем vsftpd.

sudo /etc/init.d/vsftpd restart

воскресенье, 19 августа 2012 г.

Восстановление пароля в Alt Linux.

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

Нашлось решение

В меню загрузчика пропишите в параметрах init=/bin/sh

Загрузится консоль. Выполните в ней команды:

mount / -o rw,remount
passwd

Указываем новый пароль для рута или другого пользователя passwd user_login

Далее выполните команды:

mount / -o ro,remount
reboot

Можно логиниться с новым паролем...

Восстановление пароля администратора

вторник, 17 апреля 2012 г.

Планы

В ближайшее время планирую обновить и продолжить статьи по Kohana 3.2 и начинать готовиться к Kohana 3.3 и соответствующими проблемами с совместимостью, ибо опять изменения

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

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

Решил проблему с мучениями административного интерфейса, перевёл на twitter bootstrap. Быстро прототипизируется, удобно, можно оптимизировать для мобильных устройств. Всё лишнее можно вырезать. CSS собирается из less, собственно повод по ковырять с less. Плагины на js с алертами, модальными окнама, прогресс-барами, выпадающими списками, табами и т.д.

Перешёл с ant на phing, если потратить несколько часов на написание и отладку build.xml для проекта, то потраченное время окупиться с лихвой. Проект сам бэкапиться, кэш отчищается, sass/scss пересобирается, все js и css файлы минифицируются, создаётся дамп базы, скрипты проверяются на соответствию стандарту кодирования, документация генерируется, файлы для размещения складываются в отдельный каталог для выкладывания на продакшен, девелоперские конфиги меняются на "боевые". Выкладываю в ручную, хотя и это можно делигировать phing'у.

Начав изучать Zend Framework наткнулся на несколько внятных статей по юнит-тестированию, в которых описываются не какие-то абстрактные классы или объекты с абстрактными вызовами, а весьма конкретные контроллеры приложения с соответствующими тестами на них и объясняется что такое code coverage... После этого понял, что во всех проектах использовал ручное тестирование. Постараюсь и эту тему раскрыть, ибо полезное, важно и удобное, но в документации чёрт ногу сломит, никто внятно не может объяснить зачем это нужно и как использовать.

p.s. Что меня бесит в Zend coding standards, это 4 пробела вместо табуляции, я понимаю в python блоки выделяются парными пробелами и замена табуляцией не прокатит на уровне компилятора, но в php может быть табуляция или любое количество пробелов.

Решение: добавил в начало скриптов // vim:ts=4:sts=4:sw=4:et

воскресенье, 20 марта 2011 г.

Ubuntu: Устанавливаем и настраиваем MySQL

Немного о установке и настройке MySQL сервера на Ubuntu.

Сам процесс установки не является чем-то сложным (если не рассматривать сборку из исходников связки Apache + PHP + MySQL, хотя и это несложно :) ) устанавливаются пакеты и немного правятся файлы конфигурации.

среда, 9 февраля 2011 г.

Kohana 3.1: Руководство по обновлению и миграции

Изменения в классе Request

Класс Request был разделён на два класс Request и Response. Для установки ответа вы использовали:

$this->request->response = 'foo';

Оно было изменено на:

$this->response->body('foo');

Некоторые свойста существовавшие в классе Request были преобразованы в методы класса:

Request::$controller -> Request::controller()
Request::$action -> Request::action()
Request::$directory -> Request::directory()
Request::$uri -> Request::uri()

Request::instance() был заменен на Request::current() и Request::initial(). Обычно вы будете использовать Request::current(), но если вы уверены что хотите оригинальный запрос (когда запущен HMVC), используйте Request::initial()


Изменения в классе Validation

Класс проверки был улучшен для включения поддержки "контекста", из-за этого изменился API. Кроме того класс был разделён: ядро логики проверки теперь отделено от встроенных правил проверки. Новый класс ядра называется Validation, а правила расположены в классе Valid.


Проверка контекста

Класс проверки теперь содержит поддержку "контекста". Это позволило объединить методы rule() и callback(), и в теперь это просто метод rule() который может использоваться в обоих случаях.

Старый вариант использования:

rule('password', 'matches', array('repeat_password'))

Новый вариант использования:

rule('password', 'matches', array(':validation', 'password', 'repeat_password'))

Третий параметр содержит все параметры которые должны пройти проверку правилом. Если взглянуть на Valid::matches(), то увидим:

public static function matches($array, $field, $match)

:validation первый параметр, 'password' - второй (поле которое мы хотим проверить) и 'repeat_password' это третье (которое мы хотим сравнить)

:validation это специальная переменная "контекста" которая указывает классу Validation о замене текущего класса проверки. Таким образом правила matches() не отличаются от callback() в работе, но более мощные. Есть и другие контекстные переменные:

:validation - Объект проверки

:field - Имя поля (rule('username', 'min_length', array(':field', 4)))

:value - Значение поля

Можно использовать любую php функцию, если она возвращает логическое значение.


Куки salt

Класс Cookie вызывает исключение, если salt не установлена, по умолчанию salt не установлена. Вы должны убедиться, что установили salt в файле инициализации (bootstrap.php):

Cookie::$salt = 'foobar';

Или определить расширение класса Cookie в вашем приложении:

class Cookie extends Kohana_Cookie
{
    public static $salt = 'foobar';
}

Изменение конструктора контроллера

Если по какой-то причине вы хотите изменить свой конструктор контроллера, он изменился на:

public function __construct(Request $request, Response $response)

Изменения index.php и bootstrap.php

Основным изменением стало удаление запроса из bootstrap.php и перенос его в index.php. Это позволит использовать один файл инициализации для выполнения тестирования. Причина изменения в том, что инициализации выполняет только установку среды, она не должна запускать её.


Обработка 404

Теперь Kohana имеет встроенную поддержку исключения для 404 и других кодов статуса HTTP. Если вы использовали ReflectionException для обнаружения 404, то сейчас должны использовать Http_Exception_404. Подробности обработка ошибок