четверг, 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