+68.62
419 читателей, 63 топика

Механизм обновления плагинов

В новой версии мы внедрили функционал миграций (обновлений) для плагинов. Функционал позволит разработчикам удобно выполнять обновления своих плагинов, в особенности, если эти обновления затрагивают изменения базы данных.

Как это работает
Пользователь заливает новую версию плагина в каталог /plugins/. Система сравнивает текущую версию плагина с версией этого плагина в БД. И если версия в БД старее, то в админке появляется кнопка «Применить обновление», которая автоматически запустит необходимые скрипты для обновления.


Скрипты (классы) для обновления у плагина находятся в каталоге /update/, который в свою очередь содержит каталоги с номерами версий. Для каждой версии можно создать несколько скриптов с произвольным названием, главное, что нужно помнить — названия файлов должно быть уникальным в рамках всего плагина для всех его версий.
Файл с обновлением представляет собой класс наследник от базового класса ModulePluginManager_EntityUpdate и название класса является производным от имени файла — Plugin[plugin_name]_Update_[file_name]. Например, для файла /update/1.0/CreateTable.php класс будет таким:
<?php

class PluginArticle_Update_CreateTable extends ModulePluginManager_EntityUpdate {
	/**
	 * Выполняется при обновлении версии
	 */
	public function up() {
		if (!$this->isTableExists('prefix_article')) {
			/**
			 * При активации выполняем SQL дамп
			 */
			$this->exportSQL(Plugin::GetPath(__CLASS__).'/dump.sql');
		}
	}

	/**
	 * Выполняется при откате версии
	 */
	public function down() {
		$this->exportSQLQuery('DROP TABLE prefix_article;');
	}
}

Как видно из примера, класс содержит два метода up и down. Первый выполняется при обновлении до версии, второй при откате версии. В этих методах может быть не только работа с БД, но и другая необходимая для обновления логика.

При удалении плагина из админки выполняется автоматический откат всех изменений всех версий (метод down).
Данный поход позволит «разгрузить» у плагинов методы Activate и Deactivate от лишней логики и дать удобный механизм обновления версий. Для корректной работы данного механизма, необходимо версии плагинов именовать используя вот этот стандарт — ru2.php.net/manual/ru/function.version-compare.php, он позволит правильно сортировать версии.

RBAC - управление правами на основе ролей

В новой версии появился новый модуль Rbac — модуль управления правами на основе ролей и разрешений (RBAC).
Суть его сводится к тому, что теперь в админке можно создавать различные роли, давать ролям необходимые права (разрешения) и назначать эти роли пользователям. Роли могут наследовать от других ролей. Таким образом можно очень гибко настраивать систему управления правами.



Читать дальше →

Центральный крон

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



В системный cron на сервере необходимо добавить скрипт — /application/utilities/cron/main.php с периодом 1 раз в 2 минуты.
Далее в специальном интерфейсе в админке можно управлять списком задач. По умолчанию созданы две задачи — очистка старого кеша и рассылка отложенных емайл сообщений. Там же можно в ручном режиме добавить новую задачу, в качестве вызываемого метода указывается полный синтаксис вызова метода модуля.

Центральный крон может работать в двух режимах — последовательное выполнение задач и параллельное (fork). Параллельный режим пока работает в тестовом режиме, т.к. до конца не протестирован. Выставляется режим в конфиге: $config['module']['cron']['use_fork'] = false;

Плагины могут добавлять в крон свои задачи. Для этого в главной файле плагина достаточно прописать специальные вызовы при активации и деактивации плагина.

public function Activate() {
    $this->Cron_CreateTask('Тестовая задача','PluginArticle_Main_RunTest',6,$this);
    return true;
}

public function Deactivate() {
    $this->Cron_RemoveTasksByPlugin($this);
    return true;
}

Краудсорсинговая документация

Пришла идея сделать документацию для разработчиков. Судя по всему, такая необходимость давно назрела. К сожалению, документация быстро устаревает, а бывает и совсем исчезает. Чтобы такого не произошло и её существование не сильно зависело от меня — решил разместить её на GitHub — оттуда уж точно никуда не денется. Надеюсь на вашу помощь в написании.

В общем, вот — livestreet.net

Универсальные категории

В новой версии LS появится функционал универсальных категорий.
Плагины смогут привязывать древовидные категории к любым объектам (полная интеграция с ORM), управления такими категориями доступно из админки. Из коробки категории будут работать для блогов (как сейчас на сайте livestreet.ru).



Интеграция в плагинах
Интеграция функционала категорий основана на механизме поведений. Поэтому для начала необходимо в класс объекта (сущность, к которой привязываем категории) и класс модуля добавить описание поведений.
Для сущности:
	protected $aBehaviors=array(
		// Настройка категорий
		'category'=>array(
			'class'=>'ModuleCategory_BehaviorEntity',
			'target_type'=>'article',
			'form_field'=>'category',
			'multiple'=>true,
		),
	);

Для модуля:
	protected $aBehaviors=array(
		// Настройка категорий
		'category'=> array(
			'class'=>'ModuleCategory_BehaviorModule',
			'target_type'=>'article',
		),
	);


Читать дальше →

Новый функционал фреймворка - поведения (behavior)

В новой версии LS 2.0 появится функционал поведений (behaviors), который очень поход на подобный в Yii.
Суть сводится к тому, что теперь определенному типу объектов (пользователи, топики, блоги и т.п.) или конкретному объекту можно назначить через поведения определенный новый функционал.
Добавить новое поведение в объект достаточно просто, можно у объекта определить свойство $aBehaviors. Например:
	protected $aBehaviors=array(
		'category'=>'ModuleCategory_BehaviorCategory',
		'property'=>array(
			'class'=>'ModuleProperty_BehaviorPropertyEntity',
	        	'target_type'=>'article'
		)
	);

В этом примере добавляются два поведения — категории и дополнительные поля. В качестве ключа используется название поведение (далее по этому ключу можно обращаться к поведению), а в качестве значения идут параметры. Параметры идут произвольным массивом имя/значение, 'class' — это служебный параметр, означающий класс поведения. Если у повеления нет параметром, то его можно записать в короткой форме (смотри 'category'). Альтернативным (скорее дополнительным) способом добавления поведения является прямой вызов метода AttachBehavior, например:
$oTopic->AttachBehavior('property','ModuleProperty_BehaviorPropertyEntity');

// далее можно убрать поведение
$oTopic->DetachBehavior('property');


Читать дальше →

Работа в различных окружениях (devel, production и т.п.)

В новой версии LS мы изменили механизм работы определения окружения и загрузки соответствующих конфигов (config.local.php и config.stable.php). Если раньше эти конфиги всегда подгружались(просто перезаписывая данные), то сейчас подключается только нужный конфиг.

Логику работы мы взяли из популярного фреймворка Laravel. Теперь у нас есть в корне сайта каталог /bootstrap/ с файлом start.php. Именно в этом файле задается механизм определения текущего окружения:
$sEnv=Engine::DetectEnvironment(array(
                                 'production' => array('your-machine-name'),
                             ));

В метод DetectEnvironment передается список окружений (ключи массива) и в качестве значений список хост-неймов серверов. Текущий хост-нейм можно посмотреть в nix командой hostname. Как альтернатива в метод DetectEnvironment вместо списка можно передать функцию и определить в ней свою логику:
$sEnv=Engine::DetectEnvironment(function(){
    // здесь ваша логика
    return 'devel';
});

В зависимости от текущего окружения будет загружен необходимый конфиг (если он существует). Например, для окружения «production» загрузится /application/config/config.production.php Этот же механизм работает и для плагинов! Будет подгружен конфиг /application/config/plugins/[plugin_name]/config.production.php

По умолчанию (если не удалось определить окружение) подключается окружение «local». Дополнительно зарезервировано окружение «testing», которое используется для юнит-тестов. Активируется оно довольно просто — достаточно перед подключением файла /bootstrap/start.php создать переменную $bUseEnvironmentTesting=true;.

Обновление модулей кеширования и логирования

В разрабатываемой версии мы переработали модули Cache и Logger — код стал проще, меньше и более функционален.

Модуль кеширования (Cache)
Реализован новый принцип работы с разными бекендами, теперь каждый бекенд представляет из себя отдельную сущность (наследуется от ModuleCache_EntityBackend). Такой подход позволяет легко добавлять новые типы бекендов — достаточно создать сущность с нужным типом кеша, например, вот так выглядит XCache:
/**
 * Бекенд xcache
 */
class ModuleCache_EntityBackendXcache extends ModuleCache_EntityBackend {
	/**
	 * Проверяет доступность использования текущего бекенда
	 *
	 * @return mixed
	 */
	public function IsAvailable() {
		if (extension_loaded('xcache')) {
			return true;
		}
		return 'The xcache extension must be loaded for using this backend!';
	}
	/**
	 * Проверяет доступность использование мульти-get запросов к кешу (указывать сразу несколько ключей)
	 *
	 * @return mixed
	 */
	public function IsAllowMultiGet() {
		return true;
	}
	/**
	 * Инициализация бекенда
	 *
	 * @param array $aParams
	 *
	 * @return mixed
	 */
	public function Init($aParams=array()) {
		require_once(LS_DKCACHE_PATH.'Cache/Backend/TagEmuWrapper.php');
		require_once(LS_DKCACHE_PATH.'Zend/Cache/Backend/Xcache.php');
		$aConfig=Config::Get('xcache');

		$oCahe = new Zend_Cache_Backend_Xcache(is_array($aConfig) ? $aConfig : array());
		if (isset($aParams['stats_callback'])) {
			$this->oCacheBackend=new Dklab_Cache_Backend_TagEmuWrapper(new Dklab_Cache_Backend_Profiler($oCahe,$aParams['stats_callback']));
		} else {
			$this->oCacheBackend=new Dklab_Cache_Backend_TagEmuWrapper($oCahe);
		}
	}
	/**
	 * Получить значение из кеша
	 *
	 * @param string $sName	Имя ключа
	 * @return mixed|bool
	 */
	public function Get($sName) {
		return $this->oCacheBackend->load($sName);
	}
	/**
	 * Записать значение в кеш
	 *
	 * @param  mixed  $mData	Данные для хранения в кеше
	 * @param  string $sName	Имя ключа
	 * @param  array  $aTags	Список тегов, для возможности удалять сразу несколько кешей по тегу
	 * @param  int|bool    $iTimeLife	Время жизни кеша в секундах
	 * @return bool
	 */
	public function Set($mData,$sName,$aTags=array(),$iTimeLife=false) {
		return $this->oCacheBackend->save($mData,$sName,$aTags,$iTimeLife);
	}
	/**
	 * Удаляет значение из кеша по ключу(имени)
	 *
	 * @param string $sName	Имя ключа
	 * @return bool
	 */
	public function Delete($sName) {
		return $this->oCacheBackend->remove($sName);
	}
	/**
	 * Чистит кеши
	 *
	 * @param string $cMode	Режим очистки кеша
	 * @param array $aTags	Список тегов, актуально для режима Zend_Cache::CLEANING_MODE_MATCHING_TAG
	 * @return bool
	 */
	public function Clean($cMode=Zend_Cache::CLEANING_MODE_ALL,$aTags=array()) {
		return $this->oCacheBackend->clean($cMode,$aTags);
	}
}

На данный момент в стандартном комплекте идет поддержка File, XCache и Memcached. Также добавлены дополнительные бекенды — FileOrm и Life. FileOrm служит для отдельного файлового кеширования схем таблиц в БД, автоматически применяется в функционале ORM. Life — это сохранение данных на время выполнения скрипта, т.е. аналог паттерна Registry. Применяется он в методах GetLife и SetLife.

Читать дальше →

Новый модуль для работы с изображениями и модуль FS

В разрабатываемой новой версии мы обновили модуль Image для работы с изображениями.
Теперь он основан на библиотеке Imagine и предоставляет удобные методы для манипуляции с изображениями. Дополнительной особенностью является поддержка разных PHP библиотек (драйверов): gd, imagick и gmagick. Нужный драйвер устанавливается в конфиге $config['module']['image']['driver']='imagick';

/**
 * Получаем параметры
 */
$aParams=$this->Image_BuildParams('gallery');
/**
 * Создаем объект изображения
 */
if(!$oImage=$this->Image_Open($sFileSource,$aParams)) {
	$sMsgError=$this->Image_GetLastError();
}


Читать дальше →

Дополнительные поля - использование в плагинах

В новой разрабатываемой версии LiveStreet есть функционал дополнительных (пользовательских) полей. Сейчас он задействован для топиков. Суть сводится к удобной возможности добавлять в различные объекты новые поля разных типов.

Сейчас поддерживаются следующие типы полей:
  • Целое число
  • Дробное число
  • Строка
  • Текст
  • Чекбокс
  • Дата
  • Выпадающий список
  • Теги
  • Ссылка на видео
  • Файл
  • Изображение

Постепенно мы наращиваем функционал и количество типов.
За весь функционал по полям отвечает модуль Property. Рассмотрим пример подключения функционала дополнительных полей к плагину «Статьи» на базе ORM.

Читать дальше →