Aug 20

На днях вышла долгожданная новая версия достаточно популярного в узких кругах ORM - Propel 1.3.

Из интересных мне нововведений:

  • В основе Propel 1.3 лежит новый механизм работы с базами данных - PDO, который заменил Creole и привел к ощутимому повышению скорости работы.
  • Теперь не нужно вручную загружать классы Propel и свои модели - теперь это делается автоматически (через autoloading в PHP)
  • “Object Instance Pooling”, или, как его называет небезызвестный Фаулер - Identity Map. Причем, что самое интересное, это работает не только с retrieveByPk(), но и со всему doSelect*()-методами (в зависимости от переданных аргументов), что позволяет ускорить работу засчет отсутствия лишних запросов к бд, а также ненужности построения одинаковых обьектов несколько раз.
  • Связи один-к-одному теперь поддерживаются нативно (блин, хочу много-ко-многим!)
  • Куча мелких (и не очень) исправлений и улучшений

Поподробнее можно прочитать на RedoTheWeb, в также на офсайте Propel.

PS. Сам еще не юзал, но нужно будет попробовать. На мой взгляд, самый интересный момент связан с тем, можно ли переопределить фетчинг записей, например - выбирать только записи с is_hidden = false, да так, чтобы переопределять пришлось только одну функцию.

Update: Как написали на официальном блоге Symfony - “Object Instance Pooling is not an Identity Map because the database request is still needed”. Нужно будет изучить этот вопрос.

written by fxposter \\ tags: , ,

Aug 17

Если вы используете Symfony, планируете это делать или просто интересуетесь этим фреймворком - значит нам нужно поговорить. Мои контакты:

  • ICQ: 625585
  • Jabber: fxposter@gmail.com
  • Skype: fxposter

written by fxposter \\ tags: ,

Aug 15

Ссылко.

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

written by fxposter \\ tags: , ,

Jul 20

На этот пост меня сподвигла неправильная (на мой взгляд работа fillin-фильтра в Symfony). Итак, поехали.

DOM - это мощный компонент PHP для работы с Document Object Model. Почитать о его возможностях можно здесь (php manual). Я же хочу заострить внимание на том, что это расширение, в отличии от SimpleXML, например, может работать как с HTML, так и с XML.

DomDocument - один из классов компонента DOM, который отвечает за полный XML или HTML-документ.

И вот хотелось бы поговорить и показать, как этот DomDocument работает с кодировками и символами, отличными от латиницы.

Для начала - небольшое отступление: DomDocument я создаю вот так: new DomDocument('1.0', 'UTF-8'), указывая в качестве кодировки (”The encoding of the document as part of the XML declaration.”) UTF-8, так как, судя по моему опыту - указание кодировки здесь не дает вообще ничего.

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

Весь текст в скриптах в кодировке UTF-8.

Код класса “тестов”

class Test_DomDocument_HTML_Charset {
  protected $dom;

  protected $html =
        '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
        <html>
        <head>
          <meta http-equiv="Content-type" content="text/html; charset=UTF-8">
          <title>Тестовая страничка</title>
        </head>
        <body>
          <p>Привет</p>
        </body>
        </html>';

  protected $html_without_charset =
        '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
        <html>
        <head>
          <title>Тестовая страничка</title>
        </head>
        <body>
          <p>Привет</p>
        </body>
        </html>';

  protected function checkTestFunction($function)
  {
    return (strpos($function, 'test') === 0);
  }

  public function execute($callback) {
    $functions = get_class_methods(get_class($this));
    $functions = array_filter($functions, array($this, 'checkTestFunction'));
    foreach($functions as $function) {
      $this->setUp();
      $result = $this->$function();
      $callback($result, $function);
      $this->tearDown();
    }
  }

  protected function setUp() {
    $this->dom = new DomDocument('1.0', 'UTF-8');
  }

  protected function tearDown() {
  }

  /**
   * Возвращает кодировку документа.
   * Используется документ, в котором не указана кодировка.
   *
   * @return string
   */
  protected function testWithoutCharset() {
    $this->dom->loadHTML($this->html_without_charset);
    return $this->dom->encoding;
  }

  /**
   * Возвращает документ, после обработки его DomDocument'ом.
   * Используется документ, в котором не указана кодировка.
   *
   * @return string
   */
  protected function testWithoutCharsetHtml() {
    $this->dom->loadHTML($this->html_without_charset);
    return $this->dom->saveHTML();
  }

  /**
   * Возвращает кодировку документа.
   * Используется документ, в котором указана кодировка.
   *
   * @return string
   */
  protected function testWithCharset() {
    $this->dom->loadHTML($this->html);
    return $this->dom->encoding;
  }

  /**
   * Возвращает документ, после обработки его DomDocument'ом.
   * Используется документ, в котором указана кодировка.
   *
   * @return string
   */
  protected function testWithCharsetHtml() {
    $this->dom->loadHTML($this->html);
    return $this->dom->saveHTML();
  }

}

Код, показывающий результаты

function echoHTMLResult($result, $function)
{
  echo "<h3>$function</h3>\n";
  echo '<pre><code>';
  if(is_string($result))
    $result = str_replace('<', '&lt;', str_replace('>', '&gt;', $result));
  var_dump($result);
  echo '</code></pre>';
}
<?php $test = new Test_DomDocument_HTML_Charset(); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
  <meta http-equiv="Content-type" content="text/html; charset=UTF-8">
  <title>Тестовая страничка</title>
</head>
<body>
<?php $test->execute('echoHTMLResult') ?>
</body>
</html>

Результаты

Выводы

Как можно понять из результатов - кодировка документа при использовании HTML определяется исключительно через тег meta, а точнее - через charset, который там указан:

<meta http-equiv="Content-type" content="text/html; charset=UTF-8">

При отсутствии указанного тега/charset’а в нём - $dom->encoding будет равен NULL (что можно с успехом использовать).

В общем-то всё, в ближайшие дни еще будет статья про кодировку в XML-документах, где всё немного интереснее.

written by fxposter \\ tags: , , ,

Jul 20

На работе пришлось столкнуться с очень не нравившейся мне ORM’кой собственного производства. Стал делать свою (ну не дурак ли, а? :)), наваял за 3 дня простенькую ORM, отображающую структуру таблиц на обьекты, не контроллируя типов. Итог получился примерно такой:

  • класс базы данных (относледовался от mysqli, pdo использовать было нельзя)
  • класс таблицы, хранящий в себе бд, и отвечающий за CRUD записей
  • класс записи, перенаправляющий методы CUD классу таблицы

Примерное использование:

$table = new ArticleTable();
$record = $table->fetchOneWhere("slug = 'hello'"); // получаем существующую запись
$record->name = 'Fucking Article!';
$record->save(); // вызывает insert/update в зависимости от того, новая ли это запись
$record = $table->create(); // создаем новую запись
$record->name = 'Fucking Article2!';
$record->slug = 'fucking_article';
// ...
$record->save();

И что-то мне это очень сильно напомнило, а именно - Zend_Db: Zend_Db_Table / Zend_Db_Table_Row. Недолго думая - выкинул нафиг свою систему и залил в проект кусок Zend Framework’а (если нужно - потом скажу, какие именно файлы нужны для полноценной работы всего компонента Zend_Db), а также решил почитать, что сейчас вообще есть в этой Zend_Db, а есть, как оказалось - довольно много:

  • Хорошая абстракция работы с бд
  • Классы записи/таблицы
  • Поддержка fetch’инга связанных обьектов
  • Поддержка many-to-many связей (этого даже в Propel нет)

На самом деле - еще есть вещи, коотрые бы можно было добавить, чтобы они работали автоматически:

  • Валидаторы в зависимости от типов полей таблицы
  • Возможность сразу fetch’ить данные из нескольких таблиц (точнее - получить такие данные довольно легко, но вот разбрасывать их по разным обьектам и связывать эти обьекты сейчас нужно ручками, если я не ошибаюсь, но опять же - это проблем не составляет)

Вроде всё. Общее впечатление - просто замечательная система. Использовать легко и приятно. :)

Пример:
Сначала пойдут мои супертипы слоя (кто читал Patters of EAA - поймет):

class Db_Table extends Zend_Db_Table_Abstract {
  /**
   * @return Zend_Db_Table_Rowset_Abstract
   */
  public function fetchAllBy($key, $value) {
    $where = $this->getAdapter()->quoteInto("$key = ?", $value);
    return $this->fetchAll($where);
  }

  /**
   * @return Zend_Db_Table_Row_Abstract
   */
  public function fetchRowBy($key, $value) {
    $where = $this->getAdapter()->quoteInto("$key = ?", $value);
    return $this->fetchRow($where);
  }

  public function __call($name, $arguments) {
    if(strpos($name, 'fetchRowBy') === 0) {
      array_unshift($arguments, substr($name, 10));
      return call_user_func_array(array($this, 'fetchRowBy'), $arguments);
    }

    if(strpos($name, 'fetchAllBy') === 0) {
      array_unshift($arguments, substr($name, 10));
      return call_user_func_array(array($this, 'fetchAllBy'), $arguments);
    }

    throw new Exception("Undefined method $name");
  }
}

class Db_Record extends Zend_Db_Table_Row_Abstract {
}

А теперь - пример использования:

class Item extends Db_Table {
  protected $_name = 'items';
  protected $_rowClass = 'ItemRecord';
  protected $_referenceMap = array(
      'Group' => array(
        'columns'       => 'groupid',
        'refTableClass' => 'Group',
        'refColumns'    => 'groupid',
      )
    );
}

class ItemRecord extends Db_Record {
}

class Group extends Db_Table {
  protected $_name = 'groups';
  protected $_rowClass = 'GroupRecord';
  protected $_dependentTables = array('Item');
}

class GroupRecord extends Db_Record {
}

$itemTable = new Item();
$item = $itemTable->fetchRowBySlug('hello');
$group = $item->findParentGroup();

Согласитель - всё просто и удобно, не так ли?

Для заинтересовавшихся - очень советую проштудировать полностью главу о Zend_Db из документации Zend Framework’а. А также - мой пост про Zend_Db_Table, посвященный его улучшению (правда, я не знаю, насколько он сейчас актуален, проверять нет времени :( ).

written by fxposter \\ tags: , ,

Jul 20

Собственно, о том, что вышла Symfony 1.1 писали многие, и вы об этом наверняка уже слышали. Но для тех, кто об этом еще не знает, повторю нововведения, а также выскажу о них своё [авторитетное :)] мнение:

  • Новая архитектура. Явное улучшение - компоненты теперь меньше зависят друг от друга.
  • Новая система конфигурирования. К счастью, конфигурация через YAML-файлы никуда не делась. Просто сама структура конфигов переделана.
  • “The new object-oriented form framework“. Звучит действительно круто. А смысл такой - генерация форм динамически, вместо статических хелперов (которые, кстати, остались), а также - вся работа с формами (указание полей, их валидаторов, настройка свойств показа формы и прочее)  теперь делается в одном месте, что очень удобно. Есть интеграция форм с Propel, например - автоматическая генерация форм по модели.
  • Новая система консольных комманд. Честно сказать - если вы не разрабатываете плагины, то разницу вы увидите только в том, что некоторые комманды поменяли своё название.
  • Новый парсер YAML. Разве что изменили выдачу инфы об ошибках. Ну и кое-что добавили. Как по мне - для конечного девелопера разница очень мала.
  • Теперь плагины - это обычные пакеты PEAR, со всеми вытекающими последствиями, из которых главным, на мой взгляд, является возможность указаниязависимостей от других плагинов.
  • Теперь можно выдавать различные вьюхи в зависимости от того, какоt значение принимает хедер Accept. Поподробнее - здесь.
  • Собствено, ORM теперь полностью вынесен в плагины. Symfony 1.0 содержала в себе Propel 1.2, и была возможность заменить его на Propel 1.3/Doctrine через плагины. Теперь же и Propel 1.2 вынесен в отдельный плагин. Скорее всего эта возможность пришла вследствии пункта №1.
  • Для того, чтобы приложения, написанные на Symfony 1.0 запускались на версии 1.1 был сделан sfCompat10Plugin, который, насколько я понял, в версии 1.2 уберут совсем. К сожалению, проекты всё таки прийдется дорабатывать, чтобы они работали на Symfony 1.1, подробнее об этом можно прочесть здесь.
  • Переписан класс routing’а. Теперь все пути кешируются + от этого класса можно отнаследоваться и переделать, от чего бы я не отказался.

А теперь посмотрите и скажите - что из этого может реально пригодится? ИМХО:

  • Form Framework
  • Зависимости в плагинах
  • Различные вьюхи в зависимости от того, какоt значение принимает хедер Accept

Причем последние два пункта лично я пока не использовал бы нигде. Просто нет необходимости. К чему это я клоню - этот релиз чуть ли не официально был признан “промежуточным” - минимум новых интересных фич и очень сильная переработка кода, причем зачастую внутреннего, для облегчения дальнейшей поддержки и добавления новых фич в будущем. В частности - в версии 1.2 нововведений меньше, но они гораздо больше коснуться конечных пользователей фреймворка (коснуться - в хорошем смысле). Кстати говоря, версия 1.2 выйдет уже в октябре, через 3 месяца… Но это уже несколько друга история, о которой мы поговорим, скорее всего завтра.

written by fxposter \\ tags: ,

Jun 12

После прихода на работу сегодня меня “обрадовали” следующими словами - “сноси винду!”. Я уж часом подумал, что меня выгоняют за регулярные прогулы работы из-за универа в последнее время. :) Всё оказалось намного проще - следующая фраза звучала примерно так - “переходим на Linux”. После вопроса “а нафига, собственно?”, мне поведали довольно интересные новости.

Оказывается, в последнее время участились визиты людей, проверяющих ПО на лицензионность в IT’шные, и, наверное, не только, фирмы. Эти визиты, по слухам, оборачивались в солидные прибавки к зарплате нашей дорогой милиции и прилегающим структурам… Штрафы для крупных фирм, которых “поймали на горячем”, опять-таки - по слухам, сопоставимы с ценами однокомнатных квартир (для тех, кто не в теме - среднестатистическая однокомнатная квартира у нас сейчас стоит ~50-60 килобаксов).

Так что сегодня у меня вторая половина рабочего дня прошла под эгидой “linux - наше всё” - я ставил Ubuntu, обновлял её, ставил нужные пакеты… Эх… Не хочется, но, судя по всему, прийдется…

PS. Мне-то еще ничего. А у нас люди на .NET работают - теперь из заказчиков выбивают Windows, Visual Studio и Office. :)

written by fxposter \\ tags: , ,

Jun 11

Вышел второй релиз-кандидат Symfony 1.1. Улучшений немного, по сравнению с предыдущими бетами и rc, что, несомненно, радует - версия 1.1 становиться всё стабильнее и стабильнее - уже и релиз не за горами.

Тем временем Fabien (главный разработчик Symfony) разродился кучкой постов:

written by fxposter \\ tags: ,

Apr 08

Что бы вы предпочли - datepicker или 3 select’а для дня, месяца и года?

У нас на проекте мнения разделились, потому решил устроить опрос…

written by fxposter

Mar 29

Собственно, продолжим начатое

Сегодня мы займемся установкой Symfony на ваш компьютер. Первым делом идем на офсайт и смотрим страницу, описывающую процесс установки! Несмотря на наличие этой страницы, мне всё же задают вопросы - “как ставить”, “какой способ установки выбрать” - судя по всему, такая статья лишней не будет.

Существует 3 основных способа установки Symfony на ваш компьютер:

  1. Скачать архив
  2. Установить из SVN
  3. Установить через PEAR

Установка путем скачивания архива

Скачиваем архив (symfony 1.1 beta 2) с официального сайта, и распаковываем в любую директорию (не обязательно в php’шный include_path). После чего фреймворком уже можно пользоваться. Но есть неудобства - для того, чтобы пользоваться консольными командами - прийдется каждый раз прописывать в консоли путь к файлу “symfony.bat” (или “symfony”, в зависимости от того - пользуетесь вы ОС на основе *nix или Windows), этот файл находится в ./data/bin. Для того, чтобы убрать этот недостаток:

  • под Windows - пропишите путь к “symfony.bat” в переменную PATH (помочь вам сделать это может Google).
  • под *nix - запустите “sudo ln -s /<путь к директории, куда вы распаковали архив>/data/bin/symfony /usr/bin/symfony”

После этого можете пользоваться в консоли командами типа “symfony init-project” и т.д.

Плюсы: простота - кроме скачивания и распаковывания архива ничего не нужно делать; если на компьютере, на который вы собираетесь устанавливать Symfony, нет интернета - достаточно просто принести архив на этот компьютер.

Минусы: отсутствие автоматизации - для обновления Symfony прийдется самостоятельно повторять процедуру.

Установка из SVN

Если вы не знакомы с SVN - лучше выберите первый вариант. Процедуру прописывания путей к ./data/bin/symfony прийдется повторять и здесь. Для установки - перейдите в директорию, куда хотите установить symfony и введите:

svn checkout http://svn.symfony-project.com/branches/1.1

в *nix-ОС, либо проделайте подобную процедуру (checkout) через TortoiseSVN под Windows - объяснять не буду - у TortoiseSVN интуитивно-понятный интерфейс.

Плюсы: можно удобно обновляться до самой последней версии с помощью возможностей SVN; если на компьютере, на который вы собираетесь устанавливать Symfony, нет интернета - достаточно просто принести директорию, куда вы “установили” Symfony на этот компьютер.

Минусы: в SVN может находится не совсем стабильная версия (стабильные версии хранятся в http://svn.symfony-project.com/tags/); способ сложнее для тех, кто не умеет работать с SVN.

Установка через PEAR

Если у вас не установлен PEAR - тогда вам пора обратится к Google.

Установка очень проста (перевод доки с офсайта):

Введите в коммандной строке:

$ pear channel-discover pear.symfony-project.com

Для установки стабильного релиза (на данный момент это 1.0.12), введите:

$ pear install symfony/symfony

Для установки бета-версии (на данный момент - 1.1 beta 2, т.е. то, что нам нужно), введите:

$ pear install symfony/symfony-beta

Для обновления Symfony воспользуйтесь следующими командами:

$ pear upgrade symfony/symfony

или

$ pear upgrade symfony/symfony-beta

Плюсы: очень простая установка, а также отсутствие производить махинации с переменной PATH/символическими ссылками; очень удобное и простое обновление.

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

Есть и еще один способ:

Установка sandbox’а

Sandbox - это “пустое” приложение Symfony вместе с самим фреймворком. Скачать его можно отсюда. Архив распаковываем (!!!) в директорию сервера, например: localhost у вас настроен на директорию c:\www\. Вы распаковали архив в директорию c:\www\test\ таким образом, что директория web у вас содержится в c:\www\test\web\. После этого запускаем сервер и заходим на адрес http://localhost/test/web/ и видим начальную страничку, сгенерированную Symfony.

Я сам в случае с Symfony 1.1 воспользуюсь именно sandbox’ом.

Ссылки:

  • У Макса появилась отличнейшая статья о том, что сейчас наблюдается в рунете с блогами с SMO. Знать бы, как с этим бороться…
  • Тем, кто использует Propel в Symfony -появился отличный плагин, упрощающий написание запросов через Criteria - sfPropelFinder.

written by fxposter \\ tags: ,