ModulePluginManager
Package | framework.modules |
---|---|
Inheritance | class ModulePluginManager » ModuleORM » Module » LsObject |
Since | 2.0 |
Source Code | /framework/classes/modules/plugin_manager/PluginManager.class.php |
Protected Properties
Property | Type | Description | Defined By |
---|---|---|---|
_aBehaviors | Список поведений в виде готовых объектов, формируется автоматически | LsObject | |
aBehaviors | array | Список поведений | LsObject |
bIsInit | bool | Указывает на то, была ли проведенна инициализация модуля | Module |
oMapperORM | MapperORM | Объект маппера ORM | ModuleORM |
sPluginsDir | string | Путь к директории с плагинами | ModulePluginManager |
Public Methods
Method | Description | Defined By |
---|---|---|
ActivatePlugin() | Выполняет активацию плагина | ModulePluginManager |
AddBehaviorHook() | Добавляет хук поведения | LsObject |
ApplyPluginUpdate() | Выполняет актулизацию данных БД для плагина (применение миграций) | ModulePluginManager |
AttachBehavior() | Присоединяет поведение к объекту | LsObject |
CheckPluginsFileWritable() | Проверяет доступность файла plugins.dat на запись | ModulePluginManager |
DeactivatePlugin() | Выполняет деактивацию плагина | ModulePluginManager |
DeleteItemsByFilter() | Удаляет сущности по фильтру | ModuleORM |
DetachBehavior() | Отсоединяет поведение от объекта | LsObject |
GetAggregateFunctionByFilter() | Получить значение агрегирующей функции | ModuleORM |
GetBehavior() | Возвращает объект поведения по его имени | LsObject |
GetBehaviors() | Возвращает все объекты поведения | LsObject |
GetByFilter() | Получить сущность по фильтру | ModuleORM |
GetCountItemsByFilter() | Получить количество сущностей по фильтру | ModuleORM |
GetCountItemsByJoinEntity() | ModuleORM | |
GetItemsByArray() | Возвращает список сущностей по фильтру | ModuleORM |
GetItemsByFilter() | Получить список сущностей по фильтру | ModuleORM |
GetItemsByJoinEntity() | ModuleORM | |
GetPluginXmlInfo() | Возвращает XML объект описания плагина | ModulePluginManager |
GetPluginsActive() | Возвращает список активных плагинов | ModulePluginManager |
GetPluginsItems() | Возвращает список плагинов с XML описанием | ModulePluginManager |
GetUpdateNewFiles() | Возврашает список файлов миграций по версиям | ModulePluginManager |
Init() | Инициализация модуля | ModulePluginManager |
InstallPluginFromCatalogLS() | Выполняет установку плагина из магазина LS | ModulePluginManager |
InstallPluginFromDir() | Выполняет установку плагина из локальной директории | ModulePluginManager |
LoadTree() | Для сущностей со связью RELATION_TYPE_TREE возвращает список сущностей в виде дерева | ModuleORM |
RemoveBehaviorHook() | Удаляет хук поведения | LsObject |
RemovePlugin() | Удаляет физически плагин с сервера | ModulePluginManager |
RunBehaviorHook() | Запускает хук поведения на выполнение | LsObject |
SetInit() | Помечает модуль как инициализированный | Module |
Shutdown() | Метод срабатывает при завершении работы ядра | Module |
WriteActivePlugins() | Записывает список активных плагинов в файл PLUGINS.DAT | ModulePluginManager |
__call() | Ставим хук на вызов неизвестного метода и считаем что хотели вызвать метод какого либо модуля. | ModuleORM |
__clone() | Блокируем копирование/клонирование объекта | Module |
__construct() | Конструктор, запускается автоматически при создании объекта | LsObject |
__get() | Обработка доступа к объекты поведения | LsObject |
buildTree() | Построение дерева | ModuleORM |
isInit() | Возвращает значение флага инициализации модуля | Module |
Protected Methods
Method | Description | Defined By |
---|---|---|
PrepareBehaviors() | Инициализация поведений | LsObject |
PurgePluginUpdate() | Выполняет откат изменений плагина к БД | ModulePluginManager |
SortVersions() | Выполняет сортировку массива версий | ModulePluginManager |
Xlang() | Получает значение параметра из XML на основе языковой разметки | ModulePluginManager |
_AddEntity() | Добавление сущности в БД | ModuleORM |
_DeleteEntity() | Удаление сущности из БД | ModuleORM |
_GetAncestorsOfEntity() | Для сущности со связью RELATION_TYPE_TREE возвращает список всех предков | ModuleORM |
_GetChildrenOfEntity() | Для сущности со связью RELATION_TYPE_TREE возвращает список прямых потомков | ModuleORM |
_GetDescendantsOfEntity() | Для сущности со связью RELATION_TYPE_TREE возвращает список всех потомков | ModuleORM |
_GetParentOfEntity() | Для сущности со связью RELATION_TYPE_TREE возвращает предка | ModuleORM |
_LoadMapperORM() | Загрузка маппера ORM | ModuleORM |
_NormalizeEntityRootName() | Приводит название сущности к единому формату полного имени класса | ModuleORM |
_ReloadEntity() | Обновляет данные сущности из БД | ModuleORM |
_SaveEntity() | Сохранение сущности в БД | ModuleORM |
_ShowColumnsFrom() | Список полей сущности | ModuleORM |
_ShowPrimaryIndexFrom() | Primary индекс сущности | ModuleORM |
_UpdateEntity() | Обновление сущности в БД | ModuleORM |
_deleteManyToManyRelation() | Выполняет удаление всех связей many_to_many сущности | ModuleORM |
_setIndexesFromField() | Returns assotiative array, indexed by PRIMARY KEY or another field. | ModuleORM |
_setIndexesGroupField() | Возвращает сгруппированный массив по нужному полю | ModuleORM |
_setIndexesGroupJoinField() | Возвращает сгруппированный массив по нужному полю из данных таблицы связей | ModuleORM |
_updateManyToManyRelation() | Выполняет обновление связи many_to_many у сущности | ModuleORM |
Property Details
Путь к директории с плагинами
Method Details
public bool ActivatePlugin($sPlugin $sPlugin)
| ||
$sPlugin | $sPlugin | |
{return} | bool |
public function ActivatePlugin($sPlugin)
{
if (!$this->CheckPluginsFileWritable()) {
$this->Message_AddError($this->Lang_Get('admin.plugins.notices.activation_file_write_error'),
$this->Lang_Get('error'), true);
return false;
}
$sPlugin = strtolower($sPlugin);
/**
* Получаем xml информацию
*/
if (!$oXml = $this->GetPluginXmlInfo($sPlugin)) {
return false;
}
$sClassPlugin = 'Plugin' . func_camelize($sPlugin);
if (!class_exists($sClassPlugin)) {
return false;
}
$aPluginItemsActive = $this->GetPluginsActive();
$oPlugin = new $sClassPlugin;
if (in_array($sPlugin, $aPluginItemsActive)) {
$this->Message_AddError($this->Lang_Get('admin.plugins.notices.activation_already_error'),
$this->Lang_Get('error'), true);
return false;
}
/**
* Проверяем совместимость с версией LS
*/
if (defined('LS_VERSION')
and version_compare(LS_VERSION, (string)$oXml->requires->livestreet, '<')
) {
$this->Message_AddError(
$this->Lang_Get('admin.plugins.notices.activation_version_error',
array('version' => $oXml->requires->livestreet)),
$this->Lang_Get('error'), true
);
return false;
}
/**
* Проверяем наличие require-плагинов
*/
if ($oXml->requires->plugins) {
$iConflict = 0;
foreach ($oXml->requires->plugins->children() as $sReqPlugin) {
if (!in_array($sReqPlugin, $aPluginItemsActive)) {
$iConflict++;
$this->Message_AddError(
$this->Lang_Get('admin.plugins.notices.activation_requires_error',
array('plugin' => func_camelize($sReqPlugin))),
$this->Lang_Get('error'), true
);
}
}
if ($iConflict) {
return false;
}
}
/**
* Проверяем на конфликт делегатов
*/
$aPluginDelegates = $oPlugin->GetDelegates();
$aPluginInherits = $oPlugin->GetInherits();
$aAllDelegates = $this->Plugin_GetDelegatesAll();
$aAllInherits = $this->Plugin_GetInheritsAll();
/**
* Проверяем, не вступает ли данный плагин в конфликт с уже активированными
* (по поводу объявленных делегатов)
*/
$iConflict = 0;
foreach ($aAllDelegates as $sGroup => $aReplaceList) {
$iCount = 0;
if (isset($aPluginDelegates[$sGroup])
and is_array($aPluginDelegates[$sGroup])
and $iCount = count($aOverlap = array_intersect_key($aReplaceList, $aPluginDelegates[$sGroup]))
) {
$iConflict += $iCount;
foreach ($aOverlap as $sResource => $aConflict) {
$this->Message_AddError(
$this->Lang_Get('admin.plugins.notices.activation_overlap', array(
'resource' => $sResource,
'delegate' => $aConflict['delegate'],
'plugin' => $aConflict['sign']
)), $this->Lang_Get('error'), true
);
}
}
if (isset($aPluginInherits[$sGroup])
and is_array($aPluginInherits[$sGroup])
and $iCount = count($aOverlap = array_intersect_key($aReplaceList, $aPluginInherits[$sGroup]))
) {
$iConflict += $iCount;
foreach ($aOverlap as $sResource => $aConflict) {
$this->Message_AddError(
$this->Lang_Get('admin.plugins.notices.activation_overlap', array(
'resource' => $sResource,
'delegate' => $aConflict['delegate'],
'plugin' => $aConflict['sign']
)), $this->Lang_Get('error'), true
);
}
}
if ($iCount) {
return false;
}
}
/**
* Проверяем на конфликт с наследуемыми классами
*/
$iConflict = 0;
foreach ($aPluginDelegates as $sGroup => $aReplaceList) {
foreach ($aReplaceList as $sResource => $aConflict) {
if (isset($aAllInherits[$sResource])) {
$iConflict += count($aAllInherits[$sResource]['items']);
foreach ($aAllInherits[$sResource]['items'] as $aItem) {
$this->Message_AddError(
$this->Lang_Get('admin.plugins.notices.activation_overlap_inherit', array(
'resource' => $sResource,
'plugin' => $aItem['sign']
)),
$this->Lang_Get('error'), true
);
}
}
}
}
if ($iConflict) {
return false;
}
/**
* Кастомный функционал активации плагина
*/
if ($bResult = $oPlugin->Activate()) {
/**
* Выполняем актулизацию БД плагина - миграции
*/
$this->ApplyPluginUpdate($sPlugin);
/**
* Записываем в файл
*/
$aPluginItemsActive[] = $sPlugin;
if (!$this->WriteActivePlugins($aPluginItemsActive)) {
return false;
}
}
return $bResult;
}
Выполняет активацию плагина
public void ApplyPluginUpdate($sPlugin $sPlugin)
| ||
$sPlugin | $sPlugin |
public function ApplyPluginUpdate($sPlugin)
{
$sPlugin = strtolower($sPlugin);
/**
* Получаем текущую версию плагина из XML описания
*/
if (!$oXml = $this->GetPluginXmlInfo($sPlugin)) {
return;
}
$sVersionByFile = (string)$oXml->version;
/**
* Получаем текущую версию плагина из БД
*/
if ($oVersion = $this->GetVersionByCode($sPlugin)) {
$sVersionByDb = $oVersion->getVersion();
} else {
$sVersionByDb = null;
}
if ($sVersionByFile == $sVersionByDb) {
return;
}
if (!$oVersion) {
$oVersion = Engine::GetEntity('ModulePluginManager_EntityVersion');
$oVersion->setCode($sPlugin);
}
/**
* Получаем новые файлы обновлений
*/
$aVersionFiles = $this->GetUpdateNewFiles($sPlugin, $sVersionByDb);
foreach ($aVersionFiles as $sVersion => $aFiles) {
/**
* Выполняем файлы
*/
if ($aFiles) {
foreach ($aFiles as $aFile) {
require_once($aFile['path']);
$sClass = 'Plugin' . func_camelize($sPlugin) . '_Update_' . $aFile['name'];
$oUpdate = new $sClass;
$oUpdate->up();
/**
* Сохраняем в БД
*/
if (!$this->GetMigrationByCodeAndFile($sPlugin, $aFile['file'])) {
$oMigration = Engine::GetEntity('ModulePluginManager_EntityMigration');
$oMigration->setCode($sPlugin);
$oMigration->setVersion($sVersion);
$oMigration->setFile($aFile['file']);
$oMigration->Add();
}
}
}
}
/**
* Проставляем версию из описания плагина
*/
$oVersion->setVersion($sVersionByFile);
$oVersion->Save();
}
Выполняет актулизацию данных БД для плагина (применение миграций) Обратный по действию метод - PurgePluginUpdate (@see PurgePluginUpdate)
public bool CheckPluginsFileWritable()
| ||
{return} | bool |
public function CheckPluginsFileWritable()
{
return @is_writable($this->sPluginsDir . Config::Get('sys.plugins.activation_file'));
}
Проверяет доступность файла plugins.dat на запись
public bool DeactivatePlugin($sPlugin $sPlugin)
| ||
$sPlugin | $sPlugin | |
{return} | bool |
public function DeactivatePlugin($sPlugin)
{
if (!$this->CheckPluginsFileWritable()) {
$this->Message_AddError($this->Lang_Get('admin.plugins.notices.activation_file_write_error'),
$this->Lang_Get('error'), true);
return false;
}
$sPlugin = strtolower($sPlugin);
/**
* Получаем xml информацию
*/
if (!$oXml = $this->GetPluginXmlInfo($sPlugin)) {
return false;
}
$sClassPlugin = 'Plugin' . func_camelize($sPlugin);
if (!class_exists($sClassPlugin)) {
return false;
}
$aPluginItemsActive = $this->GetPluginsActive();
$oPlugin = new $sClassPlugin;
if (!in_array($sPlugin, $aPluginItemsActive)) {
$this->Message_AddError($this->Lang_Get('admin.plugins.notices.deactivation_already_error'),
$this->Lang_Get('error'), true);
return false;
}
/**
* Проверяем на зависимость других плагинов через опцию requires
*/
$iConflict = 0;
foreach ($aPluginItemsActive as $sPlugnCheck) {
foreach ($oXml->requires->plugins->children() as $sReqPlugin) {
if ($sReqPlugin == $sPlugin) {
$iConflict++;
$this->Message_AddError(
$this->Lang_Get('admin.plugins.notices.deactivation_requires_error',
array('plugin' => func_camelize($sPlugnCheck))),
$this->Lang_Get('error'), true
);
}
}
}
if ($iConflict) {
return false;
}
/**
* Кастомный функционал деактивации плагина
*/
if ($bResult = $oPlugin->Deactivate()) {
if (false !== ($iIndex = array_search($sPlugin, $aPluginItemsActive))) {
unset($aPluginItemsActive[$iIndex]);
if (!$this->WriteActivePlugins($aPluginItemsActive)) {
return false;
}
}
}
return $bResult;
}
Выполняет деактивацию плагина
public null|SimpleXMLElement GetPluginXmlInfo($sPlugin $sPlugin)
| ||
$sPlugin | $sPlugin | |
{return} | null|SimpleXMLElement |
public function GetPluginXmlInfo($sPlugin)
{
/**
* Считываем данные из XML файла описания
*/
$sPluginXML = $this->sPluginsDir . $sPlugin . '/plugin.xml';
if ($oXml = @simplexml_load_file($sPluginXML)) {
/**
* Обрабатываем данные, считанные из XML-описания
*/
$sLang = $this->Lang_GetLang();
$this->Xlang($oXml, 'name', $sLang);
$this->Xlang($oXml, 'author', $sLang);
$this->Xlang($oXml, 'description', $sLang);
$oXml->homepage = $this->Text_Parser((string)$oXml->homepage);
$oXml->settings = preg_replace('/{([^}]+)}/', Router::GetPath('$1'), $oXml->settings);
return $oXml;
}
return null;
}
Возвращает XML объект описания плагина
public array GetPluginsActive()
| ||
{return} | array |
public function GetPluginsActive()
{
return array_keys(Engine::getInstance()->GetPlugins());
}
Возвращает список активных плагинов
public array GetPluginsItems(array $aFilter=array (
))
| ||
$aFilter | array | |
{return} | array |
public function GetPluginsItems($aFilter = array())
{
$aPluginItemsReturn = array();
$aPluginCodes = func_list_plugins(true);
$aPluginItemsActive = $this->GetPluginsActive();
/**
* Получаем версии из БД для всех плагинов
*/
if ($aPluginCodes) {
$aVersionItems = $this->GetVersionItemsByFilter(array('code in' => $aPluginCodes, '#index-from' => 'code'));
} else {
$aVersionItems = array();
}
foreach ($aPluginCodes as $sPluginCode) {
/**
* Получаем из XML файла описания
*/
if ($oXml = $this->GetPluginXmlInfo($sPluginCode)) {
if (isset($aVersionItems[$sPluginCode])) {
$sVersionDb = $aVersionItems[$sPluginCode]->getVersion();
} else {
$sVersionDb = null;
}
$aInfo = array(
'code' => $sPluginCode,
'is_active' => in_array($sPluginCode, $aPluginItemsActive),
'property' => $oXml,
'apply_update' => (is_null($sVersionDb) or version_compare($sVersionDb, (string)$oXml->version,
'<')) ? true : false,
);
$aPluginItemsReturn[$sPluginCode] = $aInfo;
}
}
/**
* Если нужно сортировать плагины
*/
if (isset($aFilter['order'])) {
if ($aFilter['order'] == 'name') {
uasort($aPluginItemsReturn, function ($a, $b) {
if ((string)$a['property']->name->data == (string)$b['property']->name->data) {
return 0;
}
return ((string)$a['property']->name->data < (string)$b['property']->name->data) ? -1 : 1;
});
}
}
return $aPluginItemsReturn;
}
Возвращает список плагинов с XML описанием
public array GetUpdateNewFiles($sPlugin $sPlugin, null $sVersionFrom=NULL)
| ||
$sPlugin | $sPlugin | |
$sVersionFrom | null | |
{return} | array |
public function GetUpdateNewFiles($sPlugin, $sVersionFrom = null)
{
$sPluginDir = Plugin::GetPath($sPlugin) . 'update/';
/**
* Получаем список каталогов-версий в /update/
*/
$aVersions = array();
$aPaths = glob($sPluginDir . '*', GLOB_ONLYDIR);
if ($aPaths) {
foreach ($aPaths as $sPath) {
$aVersions[] = basename($sPath);
}
}
$aVersions = $this->SortVersions($aVersions);
/**
* Оставляем только новые версии
*/
if ($sVersionFrom and false !== ($iPos = array_search($sVersionFrom, $aVersions))) {
$aVersions = array_slice($aVersions, ++$iPos);
}
/**
* Получаем список файлов для каждой версии
*/
$aResultFiles = array();
foreach ($aVersions as $sVersion) {
$aResultFiles[$sVersion] = array();
$aFiles = glob($sPluginDir . "{$sVersion}/*");
if ($aFiles) {
foreach ($aFiles as $sFile) {
$aResultFiles[$sVersion][] = array(
'name' => basename($sFile, '.php'),
'file' => basename($sFile),
'path' => $sFile,
);
}
}
}
return $aResultFiles;
}
Возврашает список файлов миграций по версиям
public void Init()
|
public function Init()
{
parent::Init();
$this->sPluginsDir = Config::Get('path.application.plugins.server') . '/';
}
Инициализация модуля
public bool InstallPluginFromCatalogLS($sPlugin $sPlugin)
| ||
$sPlugin | $sPlugin | |
{return} | bool |
public function InstallPluginFromCatalogLS($sPlugin)
{
var_dump('install from catalog - ' . $sPlugin);
return true;
}
Выполняет установку плагина из магазина LS
public bool InstallPluginFromDir($sPlugin $sPlugin, $sDir $sDir)
| ||
$sPlugin | $sPlugin | |
$sDir | $sDir | |
{return} | bool |
public function InstallPluginFromDir($sPlugin, $sDir)
{
var_dump('install from catalog - ' . $sPlugin);
return true;
}
Выполняет установку плагина из локальной директории
protected void PurgePluginUpdate($sPlugin $sPlugin)
| ||
$sPlugin | $sPlugin |
protected function PurgePluginUpdate($sPlugin)
{
$sPlugin = strtolower($sPlugin);
$sPluginDir = Plugin::GetPath($sPlugin) . 'update/';
/**
* Получаем список выполненых миграций из БД
*/
$aMigrationItemsGroup = $this->GetMigrationItemsByFilter(array(
'code' => $sPlugin,
'#order' => array('file' => 'asc'),
'#index-group' => 'version'
));
$aMigrationItemsGroup = array_reverse($this->SortVersions($aMigrationItemsGroup, true), true);
foreach ($aMigrationItemsGroup as $sVersion => $aMigrationItems) {
foreach ($aMigrationItems as $oMigration) {
$sPath = $sPluginDir . $sVersion . '/' . $oMigration->getFile();
if (file_exists($sPath)) {
require_once($sPath);
$sClass = 'Plugin' . func_camelize($sPlugin) . '_Update_' . basename($oMigration->getFile(),
'.php');
$oUpdate = new $sClass;
$oUpdate->down();
}
/**
* Удаляем запись из БД
*/
$oMigration->Delete();
}
}
/**
* Удаляем версию
*/
if ($oVersion = $this->GetVersionByCode($sPlugin)) {
$oVersion->Delete();
}
}
Выполняет откат изменений плагина к БД Обратный по действию метод - ApplyPluginUpdate (@see ApplyPluginUpdate)
public bool RemovePlugin($sPlugin $sPlugin)
| ||
$sPlugin | $sPlugin | |
{return} | bool |
public function RemovePlugin($sPlugin)
{
$sPlugin = strtolower($sPlugin);
$aPluginItemsActive = $this->GetPluginsActive();
/**
* Если плагин активен, деактивируем его
*/
if (in_array($sPlugin, $aPluginItemsActive)) {
if (!$this->DeactivatePlugin($sPlugin)) {
return false;
}
}
$sClassPlugin = 'Plugin' . func_camelize($sPlugin);
$oPlugin = new $sClassPlugin;
/**
* Сначала очищаем данные БД от плагина, а затем выполняем кастомный метод удаления плагина
*/
if ($oPlugin->Remove()) {
/**
* Делаем откат изменений БД, которые делал плагин (откат миграций)
*/
$this->PurgePluginUpdate($sPlugin);
/**
* Удаляем директорию с плагином
*/
func_rmdir($this->sPluginsDir . $sPlugin);
return true;
}
return false;
}
Удаляет физически плагин с сервера
protected mixed SortVersions($aVersions $aVersions, bool $bUseKeys=false)
| ||
$aVersions | $aVersions | |
$bUseKeys | bool | |
{return} | mixed |
protected function SortVersions($aVersions, $bUseKeys = false)
{
$funcSort = function ($a, $b) {
if ($a == $b) {
return 0;
}
return version_compare($a, $b);
};
if ($bUseKeys) {
uksort($aVersions, $funcSort);
} else {
usort($aVersions, $funcSort);
}
return $aVersions;
}
Выполняет сортировку массива версий
public bool WriteActivePlugins(array|string $aPlugins)
| ||
$aPlugins | array|string | Список плагинов |
{return} | bool |
public function WriteActivePlugins($aPlugins)
{
if (!$this->CheckPluginsFileWritable()) {
return false;
}
if (!is_array($aPlugins)) {
$aPlugins = array($aPlugins);
}
$aPlugins = array_unique(array_map('trim', $aPlugins));
/**
* Записываем данные в файл PLUGINS.DAT
*/
if (@file_put_contents($this->sPluginsDir . Config::Get('sys.plugins.activation_file'),
implode(PHP_EOL, $aPlugins)) !== false
) {
/**
* Сбрасываем весь кеш, т.к. могут быть закешированы унаследованые плагинами сущности
*/
$this->Cache_Clean();
/**
* Очищаем компиленые шаблоны от Smarty
*/
$this->Viewer_ClearCompiledTemplates();
return true;
}
return false;
}
Записывает список активных плагинов в файл PLUGINS.DAT
protected void Xlang(SimpleXMLElement $oXml, string $sProperty, string $sLang)
| ||
$oXml | SimpleXMLElement | XML узел |
$sProperty | string | Свойство, которое нужно вернуть |
$sLang | string | Название языка |
protected function Xlang($oXml, $sProperty, $sLang)
{
$sProperty = trim($sProperty);
if (!count($data = $oXml->xpath("{$sProperty}/lang[@name='{$sLang}']"))) {
/**
* Пробуем получить язык в старом полном формате (ru -> russian)
*/
$sLangOld = Config::Get('module.lang.i18n_mapping.' . $sLang);
if (!$sLangOld or !count($data = $oXml->xpath("{$sProperty}/lang[@name='{$sLangOld}']"))) {
$data = $oXml->xpath("{$sProperty}/lang[@name='default']");
}
}
$oXml->$sProperty->data = $this->Text_Parser(trim((string)array_shift($data)));
}
Получает значение параметра из XML на основе языковой разметки