Архив за месяц: Май 2009

Не PHP единым сыт человек, или как я побывал на YAPC::RUSSIA 2009

YAPC 2009

YAPC 2009

Те не многие люди, которые читают мой блог, при вопросе:

— «а о чем он??»  (я имею ввиду свой блог :-)),

скажут:

— «в основном автор пишет про php-фреймворки, в особенности про Yii и php вообще…»

(по крайней мере я надеюсь, что так скажут :-))

А дело, собственно вот в чем! Моим первым языком, на котором я написал что-то реальное был……Perl (я не беру в счет все эти джавы, питоны, «плюсы» на которых писались лабораторки в институте или которые изучались мной давным давно) . Мое знакомство с Perl-ом произошло около 2-х лет назад, и до сегодняшнего дня, я использую именно его  в своей работе.

— «Так почему же в этом блоге нет ни одной записи про Perl и все что его касается???»

Этот вопрос я задал себе после посещения первого дня YAPC::RUSSIA 2009.

К сожалению на второй день приехать не получилось :-(.

Вкратце о том, что там было и что для себя отметил лично я!

Прежде всего я узнал что «YAPC» произносится как «япси» а не «яписи» как я все время думал!

Анатолий Шарифулин из  RostovOnDon.pm сделал замечательный доклад о том как правильно подготавливать модули для публикации на CPAN.

Заграничный гость brain d foy сделал интересный доклад о создании mini-CPAN для собственных нужд.

Александр Лурье рассказал о том как пользоваться API Web Money  из Perl (Модуль Business::WebMoney).

Так же была великолепная серия блиц-докладов!

Под завершение было проведено обсуждение  будущего Perl-а. Во время этого обсуждения, уже знакомый нам  Анатолий Шарифулин обратился к аудитории с вопросом о положении Perl-а в Российской блогосфере, он упомянул о IronMan — своебразном конкурсе, смысл которого заключается в том, что необходимо создать блог, посвященный Perl и писать не менее одного поста каждые 8 дней. Читать подробнее.

Если честно, это выступление каким-то образом подействовало на меня, я решил завести свой отдельный блог , целиком и полностью посвященный языку Perl и всему что с ним связано (или буду писать в этом блоге — еще пока не решил)!  Скорее всего, под требования IronMan мой  блог не попадет, но думаю будет интересно!  Как коворится «коминг сун…»

И на последок….

use Perl or die;

— уж очень нравится мне эта фразочка _)))

Движок блога на Yii. Front и back разделение.

Предыдущие серии

1 Вступление

2 Yii, создаем свою CMS. База данных

Наверное в каждом веб-приложении возникает задача разделения, так называемой клиентской или public части — та часть веб-сайта или веб-приложения с которой работает пользователь и админ или back части —  часть сайта, с которой работают администраторы, контент-менеджеры и прочие пользователи, отвечающие за наполнение сайта. Сегодня я хочу поговорить о том как это сделать, при использовании фреймворка Yii и какой способ выбрал я при разработке Юпи!.

Критериями для выбора способа будут являться следующие:

  • простота реализации
  • необходимость выполнять дополнительные «тело движения» (править .htaccess, колдовать с роутингом и т.д.)

И так…

При использовании Yii можно поступить несколькими способами:

  1. Создать контроллер Admin или Back и всю логику админки реализовывать там;
  2. В каталоге Controllers создать подкаталог admin или back и все контроллеры и экшены для админки располагать там;
  3. Админка в виде отдельного модуля Yii;
  4. Создать два приложения — одно  для админской части, другое для клиентской;

Все четыре способа упорядочены по сложности их реализации и по необходимости выполнять дополнительные действия — т.е. писать код!

Кратко рассмотрим достоинства и недостатки каждого из них.

1  Создать контроллер Admin или Back и всю логику админки реализовывать там — наверное самый простой способ.

Достоинтва:

  • простота реализации;
  • модели используются всем приложением;

Недостатки:

  • если админка будет очень большая (с большим функционалом), наш контроллер «раздуется» до огромных размеров, что затруднит сопровождение проекта (эту проблему, отчасти может решить вынесение экшенов в отдельные классы);
  • нет отделения админки от остальных частей проекта;

2 В каталоге Controllers создать подкаталог admin или back и все контроллеры и экшены для админки располагать там — второй по сложности реализации способ, забегая вперед, скажу, что я выбрал именно его.

Достоинства:

  • админка физически (на уровне каталога файловой системы) отделена от остальных частей проекта, причем это отделение касается не только каталога Controllers, но и каталога View;
  • модели используются всем приложением;

Недостатки:

я пока их не нашел 🙂

3 Админка в виде отдельного модуля Yii — по правде говоря, кода я начал разрабатывать Юпи! и столкнулся с проблемой разделения на back и front части — тогда еще не было поддержки модулей в Yii (она появилась в версии 1.0.3), поэтому данный способ я не рассматривал. Если кто-то пытался поступить таким образом — прошу рассказать о своем опыте в комментариях.

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

Достоинства они же, от части, являются и недостатками:

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

Для себя я выбрал способ №2, он, как мне кажется, наиболее удовлетворяет всем моим требованиям.

Таким образом мы имеем следующую структуру проекта.

Вопрос разделения приложения на 2 части поднимался на официальном форуме Yii, почитать можно тут.

Кроме такого вот разделения приложения на две части, для каждой из них я сделал свой базовый контроллер, таким образом все контроллеры клиентской части наследуются от YFrontMainController, а контроллеры админской части от YBackMainController. Введение новых базовых контроллеров позволяет:

  • реализовать  функционал, специфичный для каждой части приложения, в одном месте, что облегчает повторное использование и сопровождение;
  • как правило, для клиентской и адмиской частей используются разные лайауты (layouts), разделение контроллеров позволяет реализовать это очень просто (для этого достаточно переопределить свойство layout);

Например, для всех контроллеров админки, я применяю фильтр, проверяющий авторизацию пользователя, не имея базового контроллера, мне бы пришлось прописывать его во всех контроллерах админки. Так же применяется xss-фильтр, для всех данных, отправляемых из панели администрирования.

Скорее всего,  выбранное мной решение, не является идеальным и имеет скрытые (пока!) от меня недостатки, если Вы о таковых знаете — прошу высказываться в комментариях.

Удачного yii-кодинга, друзья!

Обсудить на форуме

Основной сайт Юпи! — http://yupe.ru

Исходный код — https://github.com/yupe/yupe

Присоединяйтесь!

Yii 1.0.5

Вчера вышла версия Yii 1.0.5. изменений, как всегда очень много. Для себя я выделил следующие:
— поддержка AR для Oracle (нужно будет протестировать)Полный список изменений http://www.yiiframework.com/files/CHANGELOG-1.0.5.txt

А вот тут есть интересная статейка снова о производительности:

http://tjwallace.ca/blog/2009/05/frameworks-and-web-servers-benchmarked/

обсудить

Yii контроллеры, экшены, фильтры и немного о производительности

Начнем с основ.

Я не ошибусь если предположу, что большинство современных фреймворков для разработки web-приложений основаны на паттерне MVC (модель-представление-контроллер). Не буду углубляться в суть этого подхода, интересующимся можно прочитать об этом тут. Yii не исключение. Для обработки запроса пользователя, необходимо создать контроллер, который может содержать несколько, так называемых «экшенов». Рассмотрим пример. Пусть, нам необходимо разработать функционал для работы с постами (допустим мы разрабатываем блог). Под «работой» я понимаю, то, что нам необходимо обеспечить возможность выполнения следующих операций — создание поста, изменение и удаление. Как правило конкретную работу выполняют как раз экшены, а контроллер служит своего рода объединяющим контейнером для них. В Yii контроллером является класс производный от CController, таким образом простейший контроллер может быть иметь следующий вид:

  class PostController extends CController
  {
   ...
  }

Yii накладывает несколько ограничений на объявление класса контроллера:

1. Наименование класса должно быть быть следующим <название_контроллера>Controller — окончание «Controller» — является обязательным. Пример: PostController.

2. Название файла, содержащего контроллер должно совпадать с наименованием класса. Пример: PostController.php.

Как говорилось выше, основные действия выполняют именно экшены, рассмотрим их реализацию в Yii.

Экшеном (action) может быть как метод контроллера, так и отдельный класс.

В простейшем и самом распространенном варианте, экшеном является метод контроллера. Здесь Yii так же накладывает свои правила:

1. Название метода должно содержать префикс «action».

Пример:

// создаем action - create
public function actionCreate()
{
   ....
}

И так мы имеем простейший контроллер, который имеет следующий вид:

class PostController extends CController
{
 public function actionCreate()
 {
  echo 'Вызван экшн create!';
 }
}

Сохраним данный код в файле PostController.php, и поместим его в каталог Controllers приложения.

Теперь мы можем обратиться к нашему экшену, используя следующий url:

http://localhost/testDrive/index.php?r=post/create

(тут я предполагаю что Yii-приложение расположено в каталоге testDrive, в корне web-сервера)

Параметр r, который имеет значение post/create, в терминах Yii, называется роутом. Как несложно догадаться, первая часть роута (post) — обозначает идентификатор контроллера (PostController), вторая часть (create) — идентификатор экшена (в данном случае мы вызываем экшн actionCreate, в контроллере PostController).

Кроме стандартного способа создания экшенов (как методов контроллера), Yii позволяет создавать экшены в виде отдельных классов (В Codeigniter и Kohana такого делать нельзя, Symfony, кажется, умеет так).

Экшеном является класс, унаследованный от CAction.

Пример:

// экшн выполненный в виде отдельного класса
class ActionShow extends CAction
{
  public function run()
  {
    //логика экшена располагается здесь
    echo "Экшн в виде отдельного класса!"
    .....
  }
}

Сохраним данный код в файле ActionShow.php и поместим его в каталог

/testDrive/protected/controllers/post/ (его необходимо создать).

Так как мы «вынесли» экшн в отдельный класс, необходимо как-то сообщить нашему контроллеру где искать класс экшена. Для этого необходимо переопределить метод actions контроллера, который должен вернуть массив, следующего вида:

‘actionName’ => ‘path.to.action.class’

Пример:

array(
'show' => 'application.controllers.post.ActionShow'
);

‘show’ — имя вызываемого экшена;

‘application.controllers.post.ActionShow’ — алиас пути, иными словами путь к файлу экшена в структуре приложения Yii (физически это путь к файлу /testDrive/protected/controllers/post/ActionShow.php)

Определив метод actions, наш контроллер будет иметь следующий вид:

class PostController extends CController
{
   public function actions()
  {
     return array(
                  'show' => 'application.controllers.post.ActionShow'
                );
  }
    public function actionCreate()
   {
       echo 'Вызван экшн create!';
   }
}

Теперь при обращении к url

http://localhost/testDrive/index.php?r=post/show

мы должны увидеть вызов метода run, класса ActionShow.

Возникает вопрос: «для чего выносить экшены в отдельные классы?»

Как отмечается в документации Yii, это позволяет организовать приложение по модульному принципу, таким образом один и тот же экшн, реализованный в виде отдельного класса, может быть использован несколькими контроллерами. Лично мне, тоже, гораздо удобнее работать с php файлом, в котором расположено минимум php-кода. Вынести экшн в отдельный класс это конечно же хорошо и удобно — но есть другая сторона вопроса — производительность. Как мне кажется (внутренности Yii я не смотрел), при рализации экшена как отдельного класса, на плечи Yii ложится несколько дополнительных задач, а именно:

1. Необходимо преобразовать alias пути в реальный путь до класса экшена.

2. Необходимо подключить файл экшена.

3. Необходимо создать экземпляр класса экшена.

4. Необходимо выполнить метод run().

Мне было интересно какой и способов реализации экшенов работает быстрее и насколько. Для этого я провел небольшое тестирование.

Был написан следующий контроллер:

/**
* Description of TestActionController
*
* @author andrey
*/
class TestActionController extends CController
{
   //put your code here
   public function actions()
    {
        return array(
                   'outer' => 'application.controllers.outer.OuterAction'
               );
    }
    public function actionInline()
   {
       print "inline action...";
   }
}

Внешний экшн имеет вот такой вид:

/**
* Description of OuterAction
*
* @author andrey
*/
class OuterAction extends CAction
{
    //put your code here
    public function run()
   {
      print "outer action...";
   }
}

Для тестирования, утилита ab (apache bench) обращалась к следущим url:

http://localhost/yii/index.php/TestAction/outer  — для тестирования экшена, выполненного в виде отдельного класса

http://localhost/yii/index.php/TestAction/inline — для тестирование экшена, выполнненого в виде метода контроллера

Тестирование проводилось на моей домашней машине: core2duo 2.2, 2GB — ОП, Ubuntu 8.10, Apache 2.2.9, php 5.2.6.

Команда для тестирования ab -t 10 -c 10 url.

И вот такие получились результаты.

экшн как метод контроллера ~ 92-95 rps

экшн как отдельный класс ~ 87-90 rps

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

Хотелось бы сказать еще несколько слов о фильтрах. Согласно документации фильтр — это код, который может выполняться до и/или после выполнения любого экшена. Как и сами экшены — фильтры могут быть реализованы двумя способами:

  • как метод контролера
  • как отдельный класс

При реализации фильтра как метода контроллера, необходимо создать метод с префиксом «filter», пример:

// фильтр как метод контроллера
public function filterInAction($filterChain)
{
  // код фильтра
  .......
  $filterChain->run();
}

Особенности:

1. Префикс «filter» — обязателен.

2. Модификатор доступа для метода фильтра — public.

3. Строка $filterChain->run() — обязательна для выполнения цепочки фильтров и запрошенного экшена.
4. Встроенные фильтры могут выполняться только перед выполнением экшена (я не нашел способа выполнить встроенный фильтр после экшена)

Для того что-бы контроллер «знал» о необходимости выполнения фильтра — необходимо переопределить его метод «filters», пример:

public function filters()
{
   return array(
       'InAction'  // указываем название метода фильтра БЕЗ префикса "filter"
   );
}

Данный код говорит о том, что фильтр «InAction» — будет выполнен при обращеннии к любому экшену контроллера. Для того что бы исключить некоторые экшены, необходимо переписать метод вот так:

 public function filters()
 {
     return array(
         'InAction - inline'  // указываем название метода фильтра БЕЗ префикса "filter"
     );
 }

фрагмент строки «- inline» — говорит о том, что экшн «inline» не будет обработан фильтром.

Второй способ реализации фильтров — в виде отдельного класса. Для этого необходимо создать класс, производный от CFilter и переопределить его методы.

Пример:

 class OuterFilter extends CFilter
 {
    public function preFilter($filterChain)
    {
       // код выполняемый перед экшеном
       .....
       return true;
   }
   public function postFilter($filterChain)
  {
    // код выполняемый после экшена
    .....
  }
}

Особенности:

1. Оба метода должны принимать один параметр — $filterChain

2. Для того что бы продолжилось выполнение экшена или следующего в цепочке фильтра, метод preFilter  должен вернуть true (если вернуть false — выполнение прекращается…)

Для использования внешнего фильтра, так как же и при использовании «встроенного» — необходимо переопределить метод filters контроллера.

Пример:

 public function filters()
 {
     return array(
       'InAction - inline',
       array('application.filters.OuterFilter')
     );
 }

Обратите внимание на то, что  при указании «внешнего» фильтра, элементом массива фильтров, является так же массив, а не строка, как при объявлении «внутренних» фильтров. Используя такой подход можно передавать дополнительные параметры в фильтр, например вот так:

public function filters()
{
    return array(
    'InAction - inline',
     array(
          'application.filters.OuterFilter',
          'clean' => 'all'
     )
 );
}

В этом примере, мы передаем параметр «clean» со значением «all».

Так же как и при работе со «встроенными» фильтрами, можно указывать какие

экшены контроллера стоит обрабатывать, а какие нет.

// пример - исключаем экшн "inline" из обработки фильтром "OuterFilter"
public function filters()
 {
   return array(
     'InAction - inline',
     array(
         'application.filters.OuterFilter - inline',
         'clean' => 'all'
    )
 );
}

При использовании внешних фильтров, так же как и при использовании внешних экшенов, Yii должен проделать некоторую дополнительную работу.

1. Необходимо преобразовать alias пути в реальный путь до класса фильтра.

2. Необходимо подключить файл фильтра.

3. Необходимо создать экземпляр класса фильтра.

4. Необходимо выполнить методы preFilter() и postFiltrer().

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

Встроенный экшн + встроенный фильтр ~  89 — 90 rps;

Встроенный экшн + внешний фильтр       ~  87 — 89 rps;

Внешний экшн + встроенный фильр         ~  84 — 88 rps;

Внешний экшн + внешний фильр             ~ 87 — 90 rps;

Применение внешних фильтров дает следующие преимущества:

1. Код становится более модульным, упрощается повторное использование

2. Внешние фильтры, в отличии от встроенных, позволяют выполнять действия как до, так и после выполнения экшена.

Выводы.

Лично для себя я решил следующее:

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

Каждый выбирает свой способ реализации, надеюсь, эта статья поможет Вам сделать выбор!

Удачного yii-кодинга!

Читайте так же:

Yii, пишем фильтр для предотвращения XSS атак

Обсудить на форуме

Основной сайт Юпи! — http://yupe.ru

Исходный код — https://github.com/yupe/yupe

Присоединяйтесь!