Архив метки: Zend Framework

Для каждого языка программирования свои задачи, или выводы «9 лет спустя»

Окидывая взглядом последние 9 лет практики программирования, я пришел к ряду интересных и, думаю, полезных выводов. Свою историю изучения языков программирования (далее «ЯП») проиллюстрировал ниже. Где-то отметил фреймворки на этих ЯП. Отмеченные фреймворки, как правило, давали новую мотивацию и открывали новые горизонты в изучении и использовании этих ЯП.

Языки программирования по жизни

Языки программирования по жизни

Вывод 1: для разных классов задач – разные языки программирования

В действительности, достаточно хорошо изучив какой-либо язык, кажется, что на нём надо писать не только профильные приложения. Но будьте осторожны! На этом пути вы можете впасть в состояние «вбивания гвоздей микроскопом». Так рождаются демоны (постоянно висящие процессы, типа серверов) на PHP, веб-сервера на JavaScript, веб-приложения на C++ и прочее. В качестве эксперимента – это прекрасно, но всё становится гораздо ужасней, когда что-то в силу своей популярности в другой области становится частым решением в других.

Хорошим примером иллюстрирующим первый вывод является node.js и Erlang. На node.js с радостью накинулось большое число веб-разработчиков, ведь все знают JavaScript! Попробовав домашние проекты на этой платформе, многие начали использовать node.js в реальных проектах и в итоге столкнулись с большими проблемами, решение которых затрачивало всё больше сил. Например, отказоустойчивость таких приложений. Стоит вылезть ошибке в одном из потоков выполнения, и весь сервер падает, если только вы не оградились везде, где только можно try-catch блоками. Или горизонтальное масштабирование. Тут вообще беда. И рождаются костыли-костыли-костыли.

Другое дело Erlang, который изначально «by-design» создавался для больших телекоммуникационных систем. Писать на нём серверное приложение — сплошное удовольствие. Хотите постоянно работающий сервер, которым можно управлять по telnet? Да легко! Держите встроенный пакет gen_tcp. Горизонтальное масштабирование существующего приложения? Вам скорей всего придется поменять пару строчек, где описывается передача сообщений другим функциям, — добавить в них отправку сообщений на другие ноды. Тоже и с отказоустойчивостью (привет супервизорам), обработкой бинарных данных и пр. пр.

Вывод 2 – не упускайте возможности изучать новые ЯП и/или их популярные фреймворки

Этот вывод вытекает из первого. Для каждой задачи есть свой подходящий ЯП или фреймворк, только вы о них можете пока  не знать. Поэтому необходимо как можно больше изучать новые ЯП и фреймворки.

Существует две основные когорты ЯП:

  1. Си-подобные: С++, С#, Java, PHP, JavaScript, ActionScript и др.
  2. LISP-подобные: Erlang, Python, Ruby, Perl и др.

Если вы знаете хотя бы один язык из каждого класса, то считайте, что вы знаете и все остальные ЯП.) Исключениями можно считать Assembler и языки-приколы, типа Brainfuck. Кстати, часто различить эти классы ЯП можно по наличию/отсутствию кортежей и обрамлению условных операций и циклов в фигурные скобки.

Я не стал разбивать на группы ООП, функциональные и процедурные парадигмы программирования, т.к. по большому счету почти любой подход можно использовать почти в любом языке (например, функциональный подход в Java). Другое дело, что язык изначально для этого не создавался (см. вывод 1;-) и такие извороты выглядят крайне противоестественно. Исключение Python. Он изначально разрабатывался мульти-парадигмальным.

Мой топ-рейтинг открытий типа: «А-а-а! Так вот с помощью чего это надо было делать!»:

1.  Zend Framework (на PHP)

Ну нафига я писал свою глючную библиотеку для работы с БД? О боги, а зачем я каждый раз придумывал новую архитектуру для каждого нового веб-приложения? MVC, Zend_Db, Zend_Action даруют возможность писать веб-приложения быстрее, а код делать надежным!

2.  Erlang

Боже! Ну почему я потратил кучу времени на написание этого простого демона на Java, если в Эрланге это можно было сделать парой десятков строчек, да и работало бы безотказно, да и масштабировать было бы проще!

3.  Octave

Так, надо бы быстрое преобразование Фурье… О, Matlab! Ну что, пошли на страшное преступление в ближайший торрент-трекер. Хотя ладно, вот есть бесплатная библиотечка для любимого PHP. Эхх…что-то как-то всё грустно и медленно. Octave? Octave… Octave! Мега-удобный синтаксис работы с матрицами, встроенные функции для кучи математических задач, да тут еще и графики с пол пинка строятся! Да оно еще совместимо с MatLab! А еще open-source!

4.  KnockoutJS (на JavaScript).

Как же мне надоели эти простыни с кодом, запутанная логика работы с объектами на странице, эти бесконечные onSomeEvent функции. Как бы хотелось MVC, но вот только на клиенте. Чтобы вот есть у тебя модель данных и при её изменении автоматически всё само менялось на странице. Добавил к ней объект «Задача», а на странице возьми и появись соответствующий блок для новой задачи.

KnockoutJS, как ты вовремя!

5.  Django (на Python)

Да-да, Zend Framework, ты не плох, но ты… рутинный. Каждый раз надо писать свою админку, прикручивать авторизацию, свои однотипные операции добавления, редактирования и удаления сущностей, расписывать формы ввода данных, писать однотипные виды для типичных задач, типа показа по 10 новостей на одной странице и разбивках остальных новостей по страницам. Copy-paste, copy-paste…

Ммм… что-то зачастили на хабре со своими дифирамбами во славу Django и Python.  Да давно уже поют, который год. Что ж они всё никак не угомонятся?) Ну попробуем и мы.

Так-так… значит я раскомментирую вот тут и вот тут, еще одна команда в консоли и… у меня готова полноценная админка?! Вы издеваетесь?) Так просто! А вот еще бы хотелось, чтобы в админке было удобно приписывать к разным авторам разные статьи, чтобы было красивая связь многим-ко-многим. Добавить одну строку в модель?! Ну-у, у вас совсем нет совести! Зачем же я столько времени тратил на эту рутину в зенде?»

Для этой записи хватит выводов, а то, ведь, до конца не дочитаете!) Оставим еще парочку на будущее.)

Zend Framework: кеширование всего сайта с помощью Zend_Cache

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

Решение: использование Zend_Cache. Создаем папку ‘/application/data/cache’ с правами 776. В файле ‘/public/index.php’ после установки путей (функции set_include_path) пишем следующий код:

//// ======= caching code =============
//check if ajax, change GET request to make different cache
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 
	($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')) {
   $_GET['request_type_'] = 'ajax';
}
require_once 'Zend/Cache.php';
$frontendOptions = array('lifetime' => 600,
                              'automatic_serialization' => true,
                              'debug_header' => false,
                                   'default_options' => array(
                                   'cache_with_cookie_variables'     => true, //кешировать даже если присутствуют куки
                                   'make_id_with_cookie_variables'     => false, //в генерации id кеша не использовать куки
                              )
                         );
$backendOptions = array('cache_dir' => APPLICATION_PATH . '/data/cache');

$cache = Zend_Cache::factory('Page',
                             'File',
                             $frontendOptions,
                             $backendOptions);

$cache->start();
//// ======= end caching code =============

И… всё! Наслаждаемся увеличением скорости обработки скриптов как минимум в двое. У меня скрипт главной страницы отрабатывал в среднем за 160 мс, теперь за 70 мс.

Что произошло?

Zend_Cache состоит из набора классов фронт-энда и бек-энда.

Фронт-энд отвечает за объекты кеширования, как-то: отдельные кусочки кода, возвращаемые значения функций, объекты, целые страницы. Мы как раз используем последний – Zend_Cache_Frontend_Page – он позволяет кешировать весь сайт, работает на основе ob_start() и ob_end().

Как могли заметить самые замечательные читатели, для фронт-энда мы задаем ряд параметров. Самые интересные касаются куки и описаны в коде. Сделано это потому, что на сайте кроме как для Google Analytics куки нигде не используются, поэтому нет смысла генерировать для каждого пользователя свой кеш. Конечно, если у вас на сайте будет контент, меняющийся в зависимости от залогиненого пользователя, то о таком кешировании стоит забыть или использовать более производительный бек-энд, а лучше подумать о кешировании на местах.

Бек-энды отвечают за место хранения кеша: просто файлы в папке, sqlite, memcache и многие другие. Мы используем самый простой, но всегда доступный бек-энд – хранение кеша в файлах (Zend_Cache_Backend_File).

Ссылки

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

Zend Framework & Linux: шелл-скрипт для автоматического обновления

Задача: автоматизировать процесс обновления Zend Framework для всех приложений на сервере.

Решение:

  1. Создаем папку типа /var/www/libraries/ZF-last-stable/, которую и будем указывать для всех приложений в /public/index.php
  2. Создаем файл update_ZF_1.x.sh с таким содержимым:
    #!/bin/bash
    echo "Downloading new stable ZF";
    zendname="ZendFramework-1.11.1";
    wget "http://framework.zend.com/releases/$zendname/$zendname-minimal.tar.gz";
    echo "Unpacking"
    tar -xvzf "$zendname-minimal.tar.gz";
    echo "Deleting original directroy!";
    rm -r ZF-last-stable;
    mv "$zendname-minimal" "ZF-last-stable";
    exit
    
  3. Вызываем скрипт sh /var/www/libraries/update_ZF_1.x.sh

Что мы натворили: в переменной zendname мы указываем нужную версию. Архив с этой версией скачивается, распаковывается, удаляется папка zf-last-stable,  папку с разархивированным ZF переименовываем в zf-last-stable.

Получилось, правда, полуавтоматическое обновление, новую версию всё-равно придется указывать вручную. Из вариантов совсем автоматических можно использовать svn export «uri SVN репозитария с последней стабильной версией», но тогда будет неверно работать Zend_Version.

Zend Framework: идеальное веб-приложение (ч. 2 из 2)

Краткое содержание предыдущих серий: в первой части ничего неподозревающий автор вышел на, казалось бы, радужную тропинку идеального веб-приложения, однако, чем дальше он продвигался в лес, тем толще… тьфу.

В прошлой части мы:

  1. Создали структуру БД
  2. С помощью ZF Tool создали файловую структуру приложения (основной костяк, добавили модуль с рядом действий, моделей и форм, экспортировали таблицы из БД в классы приложения)
  3. Прошлись по основным паттернам

Читать далее

Zend Framework: идеальное веб-приложение (ч. 1 из 2)

Часть вторая

Задача: написать веб-приложение с помощью Zend Framework 1.10.x, которое идеально использовало бы возможности фреймворка для определенного функционала.

Лирическое отступление: конечно, по определению невозможно создать что-то идеальное, поскольку идеальное – это совокупность субъективных образов объективной реальности, а раз программистов много и у всех свои мнения по разным вопросам, то и договориться до идеального видиния веб-приложения не получится. Однако, ведь, всё-равно хочется сделать что-то по-настоящему хорошее и масимально близко к идеалу, правда?) И в действительности к стандартизации, идеализации кода приложений всё дело и идет. Яркий пример тому относительно недавно появившиеся тенденции к использованию паттернов и фреймворков.

Так к чему это всё? В n-ый раз перечитывая нередко меняющийся Zend Framework Quickstart, я заметил, что у них постепенно получается четкая и абсолютно прозрачная схема разработки веб-приложения. Для каждого случая, будь-то работа с формами или выемка данных из БД, есть свой паттерн, общепринятое решение (ну ладно, не всеобщее, а принятое умными дядьками из Zend). Значит, теоретически вполне возможно привести культуру написания кода к такому уровню, что программисты будут писать одинаковый, абсолютно правильный и понятный каждому код, свободный от уязвимостей и ошибок. Увидев постановку задачи, два абсолютно разных программиста в уме уже разметят будущую архитектуру веб-приложения, наборы классов и интерфейсов, и эти архитектуры будут абсолютно идентичны. Вроде Java Beans именно для этого и придумали, да? Для мира php я пока такого не встречал.

Скажете, это будет слишком грустно и неинтересно? Достигнув определенного уровня знания, программирование превратиться в рутину, однообразное складывание одних и тех же кирпичиков, в банальное ремесло? Как знать! Из простых каменных блоков строят и пирамиды в Египте, и великие китайские стены. В конце концов, важнее всего качественный и надежный результат. Впрочем, хватит лирики.)

Что же будет в этой статье? В ней приводится пример веб-приложения, в котором я попробую максимально верно использовать ZF и небольшой набор паттернов для решения простых и рутинных проблем, типа выемки данных из БД, запихивания их в формы, отображения списков и прочее. Конечно, это только моё видиние решения, и чувствуется, что некоторые паттерны я использую не совсем так, и вообще, логика обработки запросов должна быть не в том маппере, а вот в той модели и пр. пр. пр…)

Что будет в приложении:

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

Что будет «под капотом»:

  • Модели и мапперы для основных сущностей (статей и авторов);
  • Отдельные классы-шлюзы для работы с таблицами в БД;
  • Связка классов-шлюзов для выполнения агрегирующих запросов (в случае связей один-ко-многим, многие-ко-многим).

Ну поехали.)

Читать далее

Zend Framework: включение профайлера для FirePHP

Задача: включить профилирование всех запросов к БД.

Решение: в основном файле загрузчика приложения (типа «/application/Bootstrap.php»)  добавить следующий код:

    /**
     * FirePHP profiler enable
     */
    protected function _initProfiler() {
    	$this->bootstrap('db');
        $db = $this->getPluginResource('db');
    	$profiler = new Zend_Db_Profiler_Firebug('All DB Queries');
		$profiler->setEnabled(true);
		$db->getDbAdapter()->setProfiler($profiler);
    }

Up!

В комментариях Roman подсказал еще более удобный способ включения профайлера через конфигурационный файл («/application/configs/application.ini»):

resources.db.params.profiler.enabled = "true"
resources.db.params.profiler.class = "Zend_Db_Profiler_Firebug"

Ссылки по теме

  • FirePHP — дополнение к FireBug, которое позволяет писать в консоль FireBug.

PS: по секрету скажу, что скоро будет добрая статья про один из самых удобнейших способов работы с БД с помощью Zend_Db. Приглашенные звезды: паттерны data mapper и model, класс Zend_Form. Мы разберемся, как делать запросы типа $row->getAuthorsByPaper()  в действительном отсутствии такого метода!)

Заметки на полях 2

Включение code hinting с ASDoc для OSMF

Задача: включить в Flash Builder подсказки с кодом с включенными в них описанием классов и их свойств для работы с Open-Source Media Framework (OSMF).

Решение:

  1. Скачиваем исходники OSMF
  2. Разархивируем их на компьютер
  3. В Flash Builder выбираем Import > Existing projects into workspace
  4. Щелкаем «Browse» и находим место, куда распаковали архив с OSMF (например, «C:\osmf_source_v1-0\framework\OSMF»)
  5. Теперь создаем или выбираем существующий проект, в котором нужна поддержка OSMF. Выбираем его свойства (Properties).
  6. Находим вкладку «Library path» (для чисто AS проектов вкладка будет в пункте «ActionScript Build Path»)
  7. Щелкаем «Add project…»
  8. Выбираем «OSMF»

That’s all, folks.)

Сервис по сборке Zend Framework в один файл

Когда неудобно таскать с собой оригинальный Zend Framework с его тысячами файлов, то лучшим решением будет использовать версию ZF, размещенную в одном файле. Для этой цели существует сервис по сборке ZF в один файл — ZF Packer.

Советую полностью исключать из сборки Zend_Test, т.к. он будет требовать установленный PHPUnit.

Еще говорят, что использование такой сборки поднимает производительность, т.к. весь файл перетечет в ОЗУ, но у меня на боевом сервере наблюдался обратный эффект: время отклика увеличилось в два раза.

Zend Framework: объединение кнопок в форме в одну линию в Zend_Form

Задача: объединить, сгенерированные Zend_Form, кнопки «Обновить» и «Удалить», так чтобы они располагались на одной линии.

Решение: использование декораторов и DisplayGroup.

		$this->addElement (new Zend_Form_Element_Submit('submit', array(
            	'label'	=> 'Обновить',
        		'class'	=>	'ui-state-default ui-corner-all ui-button',
				'decorators'	=>	array('ViewHelper')
		  	))
		);

        $this->addElement (new Zend_Form_Element_Button('delete', array(
            	'label'	=> 'Удалить',
        		'id'	=>	'delete-button',
        		'class'	=>	'ui-state-default ui-corner-all ui-button',
        		'decorators'	=>	array('ViewHelper')
        	))
        );

       $this->addDisplayGroup(array('submit', 'delete'), 'submitButtons', array(
	        'decorators' => array(
	            'FormElements',
	            array('HtmlTag', array('tag' => 'div', 'class' => 'form-buttons')),
	        ),
	   ));

Здесь $this — это потомок класса Zend_Form, т.е. class Application_Model_Form_User extends Zend_Form.
Получится что-то типа такого:

Пример интерфейса с кнопками в одну линию в Zend_Form

Пример интерфейса с кнопками в одну линию в Zend_Form

Как это работает

В объявлении каждого элемента мы оставляем только один декоратор, который выводит только сам элемент. Эти элементы без декораторов мы добавляем в одну визуальную группу на основе div и для возможности стилизации в CSS добавляем группе класс «form-buttons».