Программисты действуют рационально,
лишь тогда, когда другие способы исчерпаны
(Законы Мерфи для программистов)
лишь тогда, когда другие способы исчерпаны
(Законы Мерфи для программистов)
Часть 1
Порадовавшись успешной установке сайта я поспешил удалить согласно инструкции папки install и migrate, чтобы запустить сайт. Но радость была не долгой, так как первое, что появилось при запуске было:
Fatal error: Call to undefined function get_called_class() in \core\classes\plugin.class.php on line 22
Конечно всем уже очень интересно, что же там, в 22 строке файла plugin.class.php
Там формируется конфиг плагина:
$this->config = array_merge($this->config, $this->inCore->loadPluginConfig(get_called_class()));
И здесь опять это самое "позднее статическое связывание". Функция get_called_class(), появившаяся в PHP 5.3, как раз и передаёт имя класса, из которого было осуществлено обращение к классу cmsPlugin.
Обращение идёт из класса плагина, Имя которого совпадает с Именем самого плагина. То есть, для плагина, к примеру, p_demo содаётся класс p_demo и из него выполняется обращение в класс cmsPlugin. Сделано это не только из соображений красоты и удобства восприятия, но и потому что функции loadPluginConfig() необходимо знать Имя плагина для которого она будет загружать конфиг.
То есть ситуация такая: get_called_class() передаёт Имя класса, который, по сути, является Именем плагина, для которого требуется получить конфиг.
НО! Если посмотреть внимательно на класс cmsPlugin, то сразу можно увидеть, что у него в распоряжении есть массив $info, который очень кстати содержит Имя плагина! Заменяем им функцию get_called_class() и всё заработало:
$this->config = array_merge($this->config, $this->inCore->loadPluginConfig($this->info['plugin']));
Забавно, что в другой функции этого же файла plugin.class.php, именно так и было сделано:
public function saveConfig() {
$this->inCore->savePluginConfig( $this->info['plugin'], $this->config );
}
НУ А ТЕПЕРЬ ДЛЯ СКУЧАЮЩЕГО ЧИТАТЕЛЯ САМОЕ ИНТЕРЕСНОЕ!
Пока я разбирался с этой проблемкой, меня не покидала мысль: "А зачем вообще такие сложности!?"
Ну действительно! Из класса плагина обращаемся в какой-то другой класс, там загружаем конфиг, возвращаем его обратно...
Почему просто в классе плагина не загрузить его же конфиг? Например вот так:
$this->inCore = cmsCore::getInstance();
$cfg = explode("\n",$this->inCore->plugins['p_demo']['config']);
foreach ($cfg as $nom => $str) {
$pos = strpos($str,':');
if($pos > 0){$name = substr($str, 0, $pos); $value = substr($str, $pos+2); $this->config[$name] = rtrim ($value, "\r");}
}
Скажете, что это как-то не изящно!?
Тогда я покажу, как загрузка конфига для плагина происходит сейчас!
1)
Класс плагина создаётся не сам по себе, а с родительским классом cmsPlugin
2)
В конструкторе плагина ( public function __construct() в файле plugin.php ) сначала прописывается информация о плагине и его конфиг по-умолчанию, после чего запускается конструктор родительского класса cmsPlugin ( public function __construct() в файле plugin.class.php )
3)
Из родительского класса cmsPlugin идёт обращение в класс cmsCore к функции loadPluginConfig() за получением конфига
4)
В классе cmsCore есть конфиги всех плагинов, но они записаны в виде строк, как хранятся в базе данных, поэтому чтобы преобразовать строку в массив из функции loadPluginConfig() происходит обращение к функции yamlToArray()
5)
Та в свою очередь запускает функцию includeFile()
6)
Функция includeFile() подключает небольшой файлик: /includes/spyc/spyc.php с кодом в 1000 строк! и запускает расположенную в нём функцию __YAMLLoad()
7)
В __YAMLLoad() создаётся экземпляр класса Spyc и запускается функция __load()
8)
Функция __load() отправляет процесс в функцию loadFromSource(), которая определяет источник данных, которые надо преобразовать в массив.
9)
Так как в нашем случае источником данных является строка, вызывается функция loadFromString()
10)
loadFromString() преобразует строку конфига, которая была получена в cmsCore и всё это время передавалась по цепочке, в массив и возвращает его в __load()
11)
Это ещё не победа. Полученный массив индексный, а нужен ассоциативный. __load() передаёт его в функцию loadWithSource() где, наконец-то формируется необходимый конфиг для нашего плагина!!!
На сколько изящно это происходит? Вот так это происходит:
private function loadWithSource($Source) {
if (empty ($Source)) return array();
if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
$array = syck_load (implode ('', $Source));
return is_array($array)? $array: array();
}
$this->path = array();
$this->result = array();
$cnt = count($Source);
for ($i = 0; $i < $cnt; $i++) {
$line = $Source[$i];
$this->indent = strlen($line) — strlen(ltrim($line));
$tempPath = $this->getParentPathByIndent($this->indent);
$line = self::stripIndent($line, $this->indent);
if (self::isComment($line)) continue;
if (self::isEmpty($line)) continue;
$this->path = $tempPath;
$literalBlockStyle = self::startsLiteralBlock($line);
if ($literalBlockStyle) {
$line = rtrim ($line, $literalBlockStyle. " \n");
$literalBlock = '';
$line .= $this->LiteralPlaceHolder;
$literal_block_indent = strlen($Source[$i+1]) — strlen(ltrim($Source[$i+1]));
while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
$literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent);
}
$i--;
}
while (++$i < $cnt && self::greedilyNeedNextLine($line)) {
$line = rtrim ($line, " \n\t\r"). ' '. ltrim ($Source[$i], " \t");
}
$i--;
if (strpos ($line, '#')) {
if (strpos ($line, '"') === false && strpos ($line, "'") === false)
$line = preg_replace('/\s+#(.+)$/','',$line);
}
$lineArray = $this->_parseLine($line);
if ($literalBlockStyle)
$lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
$this->addArray($lineArray, $this->indent);
foreach ($this->delayedPath as $indent => $delayedPath)
$this->path[$indent] = $delayedPath;
$this->delayedPath = array();
}
return $this->result;
}
Здесь обращения ещё в 11 функций:
getParentPathByIndent()
stripIndent()
isComment()
isEmpty()
startsLiteralBlock()
literalBlockContinues()
addLiteralLine()
greedilyNeedNextLine()
_parseLine()
revertLiteralPlaceHolder()
addArray()
if (empty ($Source)) return array();
if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
$array = syck_load (implode ('', $Source));
return is_array($array)? $array: array();
}
$this->path = array();
$this->result = array();
$cnt = count($Source);
for ($i = 0; $i < $cnt; $i++) {
$line = $Source[$i];
$this->indent = strlen($line) — strlen(ltrim($line));
$tempPath = $this->getParentPathByIndent($this->indent);
$line = self::stripIndent($line, $this->indent);
if (self::isComment($line)) continue;
if (self::isEmpty($line)) continue;
$this->path = $tempPath;
$literalBlockStyle = self::startsLiteralBlock($line);
if ($literalBlockStyle) {
$line = rtrim ($line, $literalBlockStyle. " \n");
$literalBlock = '';
$line .= $this->LiteralPlaceHolder;
$literal_block_indent = strlen($Source[$i+1]) — strlen(ltrim($Source[$i+1]));
while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
$literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent);
}
$i--;
}
while (++$i < $cnt && self::greedilyNeedNextLine($line)) {
$line = rtrim ($line, " \n\t\r"). ' '. ltrim ($Source[$i], " \t");
}
$i--;
if (strpos ($line, '#')) {
if (strpos ($line, '"') === false && strpos ($line, "'") === false)
$line = preg_replace('/\s+#(.+)$/','',$line);
}
$lineArray = $this->_parseLine($line);
if ($literalBlockStyle)
$lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
$this->addArray($lineArray, $this->indent);
foreach ($this->delayedPath as $indent => $delayedPath)
$this->path[$indent] = $delayedPath;
$this->delayedPath = array();
}
return $this->result;
}
Здесь обращения ещё в 11 функций:
getParentPathByIndent()
stripIndent()
isComment()
isEmpty()
startsLiteralBlock()
literalBlockContinues()
addLiteralLine()
greedilyNeedNextLine()
_parseLine()
revertLiteralPlaceHolder()
addArray()
12)
Полученный результат по цепочке возвращается в класс cmsPlugin.
13)
В cmsPlugin полученный конфиг совмещается с конфигом по-умолчанию. И вот в таком виде плагин наконец-то получает свой долгожданный конфиг!
Миссия выполнена!
Ещё раз напомню, что всё это — всего лишь получение информации о настройках плагина, которая хранится в одной единственной ячейке базы данных. Так сложно — это реально необходимо!??
to be continue...
Реклама #
Олег с клещами 4 года назад #
Abobo 4 года назад #
ничего не делать...
Андрей 4 года назад #
и, как я понимаю, на безопасности кода это не сказывается?
зато сказывается на удалении кучи ненужных операций, что ускоряет работу движка?