Архив автора: Dimitry

Заметки на полях 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».

Zend Framework: пример помощников вида для интеграции ElRTE и ElFinder

Задача: унифицировать и упростить задачу инициализации текстового редактора ElRTE и файлового менеджера ElFinder.

Решение: использование специально для этого придуманных помощников вида (view helpers) в ZF.

Я использую ElRTE и ElFinder в административной панели сайта. Это наиболее удобные инструменты для визуального редактирования html-контента, а также для работы с файлами на серверами. Посему пример будет для модуля admin. Впрочем, сменив название модуля на Application, пример прекрасно подойдет для модуля по умолчанию (тот который в основной папке «/application/»).

Так что же надо сделать, чтобы получить возможность максимально легко включать эти инструменты на сайте, и при этом также легко обновлять их с помощью простой замены файлов на сервере или смены в одном единственном месте пути до редакторов? Поехали:

  1. Создаем модульное приложение на ZF с модулем «admin». Подробнее об этом можете прочитать в одной из предыдущих моих записях.
  2. Скачиваем и сохраняем в «/public/js/» ElRTE и ElFinder
  3. Настраиваем коннектор для ElFinder
  4. Создаем файл «/application/modules/admin/view/helpers/EnableElRTE.php» со следующим содержимым:
    <?php
    /**
     * Class inserts neccery code for initialize rich text editor ElRTE
     */
    class Admin_View_Helper_EnableElRTE extends Zend_View_Helper_Abstract{
    
    	public function enableElRTE() {
    		$elrte_base_uri = "/js/back-end/elrte-1.0rc4/";
    
    		$this->view->headLink()->appendStylesheet("{$elrte_base_uri}css/elrte.full.css");
    		$this->view->headScript()->appendFile("{$elrte_base_uri}js/elrte.min.js");
    		$this->view->headScript()->appendFile("{$elrte_base_uri}js/i18n/elrte.ru.js");
       	}
    }
    
  5. Создаем файл «/application/modules/admin/view/helpers/EnableElFinder.php»:
    <?php
    /**
     * Class inserts neccery code for initialize file manager ElFinder
     *
     * @author Dimitry Dushkin
     *
     */
    class Admin_View_Helper_EnableElFinder extends Zend_View_Helper_Abstract{
    
    	public function enableElFinder() {
    		$elfinder_base_uri = "/js/back-end/elfinder-1.1/";
    
    		$this->view->headLink()->appendStylesheet("{$elfinder_base_uri}css/elfinder.css");
    		$this->view->headScript()->appendFile("{$elfinder_base_uri}js/elfinder.min.js");
    
    		$this->view->headScript()->captureStart() ?>
    			var opts = {
    				lang : 'ru',
    				styleWithCss : false,
    				width	: 800,
    				height  : 200,
    				toolbar : 'normal',
    				fmAllow  : true,
    				fmOpen   : function(callback) {
    					$('<div id="myelfinder" />').elfinder({
    					   	url : '<? echo $elfinder_base_uri?>connectors/php/connector.php',
    						lang : 'ru',
    						dialog : { width : 900, modal : true, title : 'Файлы' }, // открываем в диалоговом окне
    						closeOnEditorCallback : true, // закрываем после выбора файла
    						editorCallback : callback
    		            })
    	       		}
    	        };
           	<?php $this->view->headScript()->captureEnd();
       	}
    }
    
  6. В представлении IndexAction IndexController’a модуля admin (по умолчанию такие штуки располагаются в «/application/modules/admin/view/scripts/index/index.phtml») пишем следующее:
    
    <? echo $this->enableElRTE();?>
    <? echo $this->enableElFinder();?>
    
    <script type="text/javascript">
         $().ready(function() {
              // создаем редактор
              $('.rte_textarea').elrte(opts);
         });
    </script>
    <form method="get">
         <textarea name="content" id="content" class="rte_textarea" rows="5" cols="45"></textarea>
    </form>
    
  7. В основном шаблоне («/application/layouts/scripts/index.phtml») при этом должно быть что-то типа такого (стандартный код для шаблонов ZF):
    <?php echo $this->doctype() ?>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    	<?php echo $this->headTitle() ?>
    	<?php echo $this->headMeta() ?>
    	<?php echo $this->headLink() ?>
    	<?php echo $this->headScript() ?>
    </head>
    <body>
             <?php echo $this->layout()->content ?>
    </body>
    </html>
    
  8. Удостоверяемся, что все работает как надо, пройдя по ссылке типа http://localhost/admin/index/index

Получим что-то типа такого

семейное фото ElRTE и ElFinder

семейное фото ElRTE и ElFinder

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

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

В каждом помощнике мы обращаемся к стандартным помощникам вида (HeadScript Helper для добавления js-файлов и js-кода и HeadStyle Helper для добавления файлов стиля) через глобальный объект Zend_View.

Ссылки в записи

 

UPDATE: по твиттеру товарищ Вредный рассказал о своём усовершенствованном решении этого вопроса. Там предлагается подключать elRte и elFinder в описании формы.

Zend Framework: включение модульной архитектуры

К сожалению, не все рутинные задачи доступны с помощью Zend_Tool. Одна из таких задач — включение механизма модульной архитектуры, что на самом деле немного удивительно, т.к. команда на создание модуля со структурой по умолчанию существует.

Путь создания приложения с нуля с помощью Zend_Tool довольно прост:

  1. Удостоверяемся, что есть доступ к zf.sh (для Linux) или zf.bat (для Windows);
  2. Заходим в директорию, в которой хотим создать приложение, например так
    cd /home/user/data/www/example.ru/
    
  3. Cоздаем проект Zend Framework:
     zf create project example
     
  4. Включаем поддержку шаблонов (layout):
     zf enable layout
     
  5. Создаем, например, модуль video:
     zf create module video
     
  6. Создаем для него Index контроллер с действием IndexAction:
     zf create controller Index index-action-included video
     
  7. В файле /application/config/application.ini включаем поддержку модулей, вписав после тега [production] следующее:

    resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
    resources.modules[] =
  8. Для включения автозагрузки классов модуля (моделей или форм, например) в корне папки модуля (/application/modules/video/) создаем файл Bootstrap.php со следующим содержимым:
    <?php class Video_Bootstrap extends Zend_Application_Module_Bootstrap {}

Вот и всё, все дело в последнем шаге. К сожалению, без него стандартные правила направления запросов к модулю не работают, то есть запрос типа:

example.ru/video/index/index

вызовет ошибку, хотя должен был вызвать IndexAction в IndexController в модуле video.
Строка «resources.modules[] =» включает автозагрузку классов для модулей.

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

Ссылки в записи

Java: бинарный поиск

Цель: научится писать красивый и эффективный код.

Задача: перед использованием какого-либо алгоритма, реализация которого есть в библиотеках, написать самому его реализацию.

Решение:

недавно на хабре был замечательный пост про то, что в действительности даже простые алгоритмы типа дихотомии (бинарный поиск) могут написать только 10% программистов. В действительности этот пост оказался проверкой своих качеств как программиста для тех, кто его читал. И правда, многие в комментариях честно признавались, что написание этого алгоритма заняло у них от 30 до 90 минут. Решил и я попробовать, и спустя 40 минут нарисовалось следующее:

	public static int binarySearch(int numb, int[] array) {
		int border = (int) Math.floor(array.length/2);
		int current = border;
		int tmp = array[current];
		do {
			if (tmp == numb) {
				return current;
			}
			if (numb == array[current - border]) {
				return current-border;
			}
			
			if (numb == array[current + border - 1]) { // Тут внесена правка, см. комментарии
				return current+border;
			}

			border = (int) Math.floor(border/2);
			if (tmp < numb) {
				tmp = array[current + border];
				current += border;
			} else {
		        tmp = array[current - border];
		        current -= border;
			}
		} while (border != 0);
		return -1;
	}

Что, конечно, было довольно громоздко и не слишком быстро (и довольно грязно), но работало. Однако самое интересное произошло, когда я нашел исходные коды реализации этого алгоритма в стандартной библиотеке java.util.arrays:

    // Like public version, but without range checks.
    private static int binarySearch0(int[] a, int fromIndex, int toIndex,
				     int key) {
	int low = fromIndex;
	int high = toIndex - 1;

	while (low <= high) {
	    int mid = (low + high) >>> 1;
	    int midVal = a[mid];

	    if (midVal < key)
		low = mid + 1;
	    else if (midVal > key)
		high = mid - 1;
	    else
		return mid; // key found
	}
	return -(low + 1);  // key not found.
    }

Исходники находятся в архиве src.zip, который лежит в JDK (у меня это было в C:\Program Files\Java\jdk1.6.0_18).
Самое интересно (и после ставшее очевидным), что целочисленное деление на два можно выполнять простым поразрядным беззнаковым оператором сдвига вправо a >>> n, который смещает биты в числе a вправо на n разрядов. Более того, как совершенно верно заметил один пользователь в ЖЖ:

В оригинальной реализации даже не нужна проверка на переполнение, потому что, даже если происходит переполнение, потом мы сдвигаем сумму вправо на 1 бит, последний бит заполняя ноликом (потому что >>>) — а не просто деля получившуюся (возможно, отрицательную в результате переполнения) сумму пополам, таким образом, даже если числа были по размеру как близкие к максимуму int’ы, всё равно число посередине будет найдено корректно.

Вот такая вот история с простой моралью: чтобы расти дальше, необходимо учится у лучших и на своих ошибках.

Ссылки в записи

  • Запись на habrahabr.ru о том, что по исследованиям Дональда Кнута, только 10% программистов могут написать реализацию двоичного поиска

Apache: Разгоняем сайт

Цель: максимальная скорость работы веб-приложения.
Задача: включение кеширования на Apache сервере.
Решение:

  1. Создаем в корне сайта файл .htaccess
  2. Проверяем, какой модуль включен на хостинге (mod_headers или mod_expires):
    1. Вставляем код:
      ExpiresActive On
      ExpiresDefault A0
      
      # 1 YEAR
      <FilesMatch "\.(ico)$">
      ExpiresDefault A9030400
      </FilesMatch>
      
      # 6 month
      <FilesMatch "\.(flv|jpg|jpeg|png|gif|swf)$">
      ExpiresDefault A14515200
      </FilesMatch>
      
      # 3 HOUR
      <FilesMatch "\.(txt|xml|js|css)$">
      ExpiresDefault A10800"
      </FilesMatch>
      

      Если при загрузке сайта вылетает ошибка сервера, значит не включен mod_expires.

    2. Вставляем код
      <FilesMatch "\.(ico|gif|jpg|jpeg|png|flv|pdf)$">
        Header set Cache-Control "max-age=29030400"
      </FilesMatch>
      # WEEK
      <FilesMatch "\.(js|css|swf)$">
        Header set Cache-Control "max-age=604800"
      </FilesMatch>
      # 45 MIN
      <FilesMatch "\.(html|htm|txt)$">
        Header set Cache-Control "max-age=2700"
      </FilesMatch>
      

      Если при загрузке сайта вылетает ошибка сервера — не включен mod_headers.

  3. Если оба модуля подключены — оставляем вариант для mod_expires. Как утверждает Google, заголовок expires поддерживается бОльшим количеством браузеров.
  4. Если ни один модуль не включен и есть рутовски доступ к серверу по ssh — включаем модуль mod_exparies с помощью командной строки (создаем символическую ссылку и перезагружаем веб-сервер):
    ln -s /etc/apache2/mods-available/expires.load /etc/apache2/mods-enabled/expires.load
    /etc/init.d/apache2 restart
    

Вот и всё, довольно просто, правда?) Зато сколько пользы,)

Пояснение

И все-таки стоит пояснить, почему кеширование так важно сегодня.

Не так давно Google объявил, что теперь скорость работы сайта учитывается при поисковой выдачи. Это очень правильный шаг на встречу не только пользователям интернета, но и хорошее подспорье разработчикам, поскольку кеширование позволяет загружаться сайтам быстрее и уменьшает трафик, что снижает нагрузку на сервер.

Чтобы проверить скорость работы своего сайта удобней всего пользоваться фирменным дополнением к Firebug от Google— PageSpeed.

Скриншон работы PageSpeed

Скриншот работы PageSpeed

Проведя быстрый тест, PageSpeed сообщит о проблемах производительности сайта и порекомендует пути их решения. Хорошая штука.)

Также есть аналогичный инструмент от Yahoo! — YSlow. Очень удобная программка, я ей тоже пользуюсь — бывает, он замечает то, что PageSpeed упустил.

Ссылки в записи

Теперь буду добавлять к статьям этот блок со ссылками на используемые ресурсы в записи. Такая штука более академична что ли.)

  • Рекомендации Google по включению механизмов кеширования
  • Подробная справочная статья по включению кеширования в Apache
  • Заявление Google о включении учета скорости работы сайта в поисковом ранжировании
  • Firebug — один из удобнейших инструментов веб-разработчика, расширение для FireFox
  • PageSpeed — дополнение к Firebug от Google для выявления проблем производительности сайта
  • YSlow — дополнение к Firebug от Yahoo!  для выявления проблем производительности сайта

Дизайн: jQuery плагин "Удобная кнопка удаления"

Собрался с силами и таки написал реализацию концепта более юзабельной кнопки удаления.)

Как это выглядит

Как это работает можно посмотреть на отдельной странице плагина. Там же можно скачать исходные коды и минифицированную версию плагина.

Как это устроено

Для начала примем, что кнопка удаления — это ссылка-кнопка с GET-параметрами для удаления какого-либо объекта из базы данных. Ссылка будет что-то типа такой: «http://example.ru/?action=delete&item_id=9000«.

При инициализации старая кнопка удаляется и на её месте с помощью абсолютного позиционирования размещается новая кнопка-ссылка со ссылкой для удаления, указанной в параметрах. Также добавляется скрытый элемент — панель с диалогом подтверждения удаления.

При нажатии на кнопку удаления из-под неё выдвигается панель с диалогом подтверждения удаления. Размещение элементов друг над другом возможно благодаря использованию связки CSS-аттрибутов: у кнопки удаления

position: absolute; z-index: 10

и у панели

position: absolute; z-index: 1

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

В посты из серии «заметки на полях» я собираю по две-три проблемы с решениями и их выкладываю. Чем больше заняла проблема времени для решения, тем больше вероятность её попадания в отдельный пост.)

Отсылка писем redmine’ом без smtp-сервера

Чтобы redmine мог посылать письма с помощью Action Mailer БЕЗ smtp сервера, в настройках надо просто изменить способ доставки на sendmail.

config.action_mailer.delivery_method = :sendmail

Включение панели Ant в Zend Studio

Ant — крайне удобный инструмент для сборки проектов, который в основном используется в Java, но и для PHP-программистов он тоже может быть очень полезен: с помощью него можно «собирать» и сжимать (минимизировать) все js-файлы в один, при этом используя какой-либо популярный плагин для сжатия кодов.  Также Ant может быть использован для аналогичных целей и для CSS-файлов. Хорошая статья по теме со скринкастом есть у Лебедева.

Однако, счастливые пользователи Zend Studio on Eclipse могут не обнаружить столь полезной панели для работы с Ant, хотя сам плагин там и присутствует. Эту панель необходимо активировать:

  • Заходим в File ? New project…
  • Щелкаем на Show All Wizards.
  • Вводим в верхнем тестовом поле «ant»
  • Выбираем «Java Project from Existing Ant Buildfile»
  • Спросят «Включить ли вам Ant Development?!?» со спокойной совестью жмем «Yes»

Всё, дальше можно даже не создавать проект. В Window ? Show View ? Other… вы теперь сможете найти панель Ant.