lost_cluster
13.08.2009 - 15:36
Добрый день уважаемые коллеги. Речь пойдет о самописной CMS и надеюсь это не явится оффтопиком.
Как известно, программирование CMS начинается с ее ядра (core). В ядре обычно исполнен функционал парсинга, обработка модулей, блоков и т. д. Именно об этом я и хочу поговорить с Вами.
Я создал CMS, которая меня удовлетворяет полностью, но я как человек самокритичный хотел бы услышать независимые мнения других программистов.
Поговорим о ядре. Здесь я поделюсь с Вами своими наработками, которые до этого я не встречал в других CMS. Возможно я выкладываю нечто новое, а возможно и нечто не пригодное для использования. Судить впрочем Вам.
И еще один момент. Если кого-нибудь, заинтересует судьба данной CMS, то я мог бы такому человеку предложить разработать администраторскую часть (она есть, но меня не устраивает).
В общем начнем.
Основной кусок ядра упрощен для наглядности (core.php):
PHP |
class core { var $content; //контент сайта function core() //конструктор { $this->content = NULL; } function parse_string($string, $st_pos) //рекурсивная функция парсинга. $st_pos - стартовая позиция сканирования { $fS_pos = strpos($string,'{', $st_pos); if ( $fS_pos !== false ) { $fE_pos = strpos($string, '}', $fS_pos + 1); if ( $fE_pos !== false ) { $fSeparator = substr($string, $fS_pos + 1, ($fE_pos - 1) - $fS_pos); $separator = $this->decode_separator($fSeparator); $string = preg_replace("/{".$fSeparator."}/", $separator, $string, 1); if ( strlen($separator) < strlen($fSeparator) ) $fE_pos -= strlen($fSeparator); $string = $this->parse_string($string, $fE_pos + 1); } } return $string; }
function decode_separator($separator) //замена сепараторов HTML кодом { //модуль? if ( file_exists("moduls/".$separator) ) { ob_start(); include("moduls/".$separator."/index.php"); $php_code = "$".$separator." = new ".$separator."();"; eval($php_code); $separator = ob_get_contents(); ob_end_clean(); }elseif ( file_exists("blocks/".$separator) ) //блок? { ob_start(); include("blocks/".$separator."/index.php"); $php_code = "$".$separator." = new ".$separator."();"; eval($php_code); $separator = ob_get_contents(); ob_end_clean(); } //поле базы данных? elseif ( ($this->row) AND array_key_exists($separator, $this->row) ) $separator = $this->row[$separator]; else //похоже это метод модуля/блока { ob_start(); $php_code = "$"."this->".$separator."();"; eval($php_code); $separator = ob_get_contents(); ob_end_clean(); } //в остальных случаях ошибка PHP. //ВНИМАНИЕ! ИМЕНА БЛОКОВ, МОДУЛЕЙ, МЕТОДОВ И ПОЛЕЙ БД НЕ ДОЛЖНЫ СОВПАДАТЬ! //ИСКЛЮЧЕНИЕ СОСТАВЛЯЮТ МЕТОДЫ И ПОЛЯ БД, ПРИ СОВПАДЕНИИ ИМЕН МЕТОДОВ И ПОЛЕЙ БД //ПРЕИМУЩЕСТВО ИМЕЕТ МЕТОД БЛОКА ИЛИ МОДУЛЯ return $separator; } function gogogo() //основная функция стартующая сайт { $this->content = $this->parse_string($this->read_file("skins/default/main.tpl"), 0); //парсим HTML echo $this->content; //вывод контента } } |
Вот в принципе и есть ядро CMS, для которой любой текст в фигурных скобках является командой для последующего интерпретирования в HTML код.
К примеру {title}, {search} и т. д.
Конечно, в полноценном коде учтены практически все мелочи, а так же имеется интерпретатор PHP кода, но не будем углубляться.
Разберем выше приведенный код:
На вход функции parse_string подается основной шаблон. Замена сепараторов (я их называю так) происходит рекурсивно, при этом функция не начинает сканирование шаблона с нулевой позиции.
Теперь собственно ядро ядра =) функция decode_separator:
Как видно из кода, функция анализирует (надеюсь можно так сказать) чем является сепаратор.
Если нет к примеру поля title в конкретной таблице базы данных, то логично предположить, что {title} не является командой для отображения запроса из базы данных. Если же нет файла модуля в определенной папке, то команда не является интерпретацией модуля, соответственно и с блоком то же самое, в итоге функция решает, что команда является методом текущего блока либо модуля.
На первый взгляд все кажется запутанным, но давайте разберемся, что есть модуль.
А модуль, это класс, родителем которого является класс ядра (class core).
Основной модуль формирования контента:
PHP |
class content extends core { function content() { $page = $this->get_page(); $sql = "SELECT * FROM pages"; $sql_where = " WHERE page_url = '".$page."' AND status > 0"; $this->db->query($sql.$sql_where); $this->row = $this->db->fetch_array(); if ( !$this->db->num_rows() ) $this->page_erorr(); else echo $this->row['dscr']; } } |
Теперь, чтобы показать верстку чего либо в определенной области страницы, необходимо в основном шаблоне поставить сепаратор вида {content}
Таким образом можно написать модуль любой сложности и для последующего интерпретирования указать его название в фигурных скобочках в любом месте любого шаблона.
С блоками все то же самое, только существует прежде модуль блоков, который отвечает за их вывод и формирование, а уже потом блоки по отдельности.
В общем система работает. По скорости вывода с другими CMS не тестировал. Но на глазок, страницы отображаются моментально.
Теперь хотел бы выслушать Ваши критические замечания.
Основной вопрос. Имеет ли место подобная методика используемая в ядре?
Спасибо!
Если кого-то заинтересует совместная работа над данной CMS, пишите в личку.
Спустя 7 минут, 44 секунды (13.08.2009 - 14:44) Han написал(а):
Не знаю как по мне так довольно нехило. Можно мне глянуть полный исходник?
Спустя 23 минуты, 15 секунд (13.08.2009 - 15:07) TMake написал(а):
Вообщем поглядел я ващу "CMS" вот что можно заметить:
- нет определений public protected private как для методов так и для переменных
- нет автоматического обнаружение и подключения новых модулей
- нет обработка ошибок
Спустя 6 минут, 30 секунд (13.08.2009 - 15:14) lost_cluster написал(а):
stepan
Я же написал, это основные функции ядра. Я вырвал их из полноценного core.php, а мелочи надеюсь все учтены в полном классе.
- нет определений public protected private как для методов так и для переменных
В 4x PHP такого понятия нет. Хотя на самом деле полноценная CMS будет работать только под 5x, но по привычке не стал расставлять private и т. д.
В общем суть не в этом, код работает.
- нет автоматического обнаружение и подключения новых модулей
Самое интересное, что обнаружение и подключение происходит так просто, что проще уже некуда. Обратите внимание на строчку в функции decode_separator, include("moduls/".$separator."/index.php"); То есть, если вы кладете в папку moduls/mymodul/ файл index.php (содержащий в себе модуль), а потом в любом месте шаблона пишите {mymodul} то система автоматически запускает этот модуль. Такие дела.
- нет обработка ошибок
В полном классе core все это есть.
Спустя 12 минут, 43 секунды (13.08.2009 - 15:26) TMake написал(а):
Цитата (lost_cluster @ 13.08.2009 - 12:14) |
В 4x PHP такого понятия нет. Хотя на самом деле полноценная CMS будет работать только под 5x, |
Если она предусмотрена под 5 версию значит стоит придерживаться правильного написания.
Цитата (lost_cluster @ 13.08.2009 - 12:14) |
В полном классе core все это есть. |
Что же тогда весь класс не выложили? мне интересно поглядеть...
Спустя 9 минут, 7 секунд (13.08.2009 - 15:36) lost_cluster написал(а):
Цитата (stepan @ 13.08.2009 - 12:26) |
Если она предусмотрена под 5 версию значит стоит придерживаться правильного написания. |
В принципе разницы особой нет, но я учту Ваш комментарий.
Цитата (stepan @ 13.08.2009 - 12:26) |
Что же тогда весь класс не выложили? мне интересно поглядеть... |
Если я выложу полноценное ядро, то нужно будет выкладывать дополнительно и модули и шаблоны, а так в любом случае наглядно все. Меня не вопросы ошибок интересуют, а конкретно сам метод обработки и интерпретации данных.
Спустя 2 часа, 31 минута, 10 секунд (13.08.2009 - 18:07) kirik написал(а):
Цитата (lost_cluster @ 13.08.2009 - 06:36) |
Имеет ли место подобная методика используемая в ядре? |
Почему нет? Ведь вы же применяете
Я подобную модульную систему делал, когда только начинал изучать пхп (конечно без ООП). Когда встал вопрос о гибкости, я ушел от этих фигурных скобок (у меня сепараторами служили символы процентов).
Для небольшого сайта вполне подойдет, а вот для крупного проекта не хватит гибкости.
Спустя 15 минут, 8 секунд (13.08.2009 - 18:22) lost_cluster написал(а):
Цитата (kirik @ 13.08.2009 - 15:22) |
Почему нет? Ведь вы же применяете |
Я применяю, но так как код ядра я не подсматривал в других CMS, то меня мучает вопрос рациональности моего кода. =)
Цитата (kirik @ 13.08.2009 - 15:22) |
Когда встал вопрос о гибкости, я ушел от этих фигурных скобок (у меня сепараторами служили символы процентов). |
А вот на счет гибкости. Что Вы имеете в виду? Желательно привести символический пример.
Я честно говоря не вижу разницы между '{' '}' и '%' '%' да хоть '$' '$' и такие встречал.
Цитата (kirik @ 13.08.2009 - 15:22) |
Для небольшого сайта вполне подойдет, а вот для крупного проекта не хватит гибкости. |
CMS-ка задумывалась изначально для создания сайтов уровня "бизнес", но по необходимости написания различных модулей стало ясно, что из данной CMS легко сделать классический портал, а может что и понаварочанней. Поэтому я и хочу понять, что в Вашем понимании - гибкость?
Спасибо!
Спустя 3 дня, 17 часов, 30 минут, 35 секунд (17.08.2009 - 11:52) AlexION написал(а):
Можно посмотреть всю CMS ? Желательно работающую уже на каком-нибудь маленьком примере...
![smile.gif](http://phpforum.ru/html/emoticons/smile.gif)
А то я покавырялся в коде, но получил небольшое представление о работе core )
Спустя 1 год, 4 месяца, 22 дня, 32 минуты, 36 секунд (9.01.2011 - 13:25) lost_cluster написал(а):
Созрел выложить рабочую версию ноу хау CMS.
http://narod.ru/disk/3019490001/drilllite.rar.htmlДля быстрой установки создайте базу
данных с именем dl_demo и пользователем dl_demo, без пароля.
Импортируйте дамп
базы dl_demo.
sql.
Название домена не важно, но для полной уверенности назовите папку drilllite.
Это лайт версия, подойдет для ознакомления с ядром, а также на этой версии спокойно можно написать сайт-визитку.
Демонстрационный шаблон стандартной трехколоночной табличной верстки, графика отсутствует.
Вход в админку: drilllite/control/
Логин: admin, пароль: demo
Жду комментарии и вопросы.
Спасибо.
Спустя 3 дня, 20 часов, 7 минут, 34 секунды (13.01.2011 - 09:33) Гость_Алексей написал(а):
lost_cluster
Очень даже не плохо. Довольно таки просто реализовано и при этом есть практически весь необходимый базовый функционал.
Хотя конечно есть небольшие недочёты. В частности, не плохо бы ввести префикс таблицы и возможность его менять. А то сейчас 2 сайта на одной базе создать не получится.
Ну и по безопасности можно ряд дополнений сделать... Скажем пароль к админке хранить в открытом виде в файле не самый лучший вариант. Да и вообще во всех файлах неплохо бы прописать защиту запуска извне, из серии:
в index.php пишем define('DRILL', 1); а после во всех подключаемых файлах if(! defined('DRILL')) вывод страницы 404.php
Ну и в текущей версии не увидел возможности создания древовидноо меню и соответственно в админке создание подменю и вывода...Это основа, такое должно быть даже в лайтовой версии)
Ну и ещё пара ошибок есть, в админке ошибку выводит, кое что пришлось подправить... И переменная запрещающая кеширование отдельных модулей сейчас неверно прописана
Как будет время попробую протестировать скорость работы и сравнить с другими ЦМС... Сами, кстати, такое тестирование не проводили?
Спустя 16 часов, 31 минута, 6 секунд (14.01.2011 - 02:04) lost_cluster написал(а):
Гость_Алексей
"Хотя конечно есть небольшие недочёты"
Они все известны и будут по необходимости исправлены, важно ядро, хочется понять есть ли смысл в подобной реализации, которая в других CMS не встречается.
"Ну и в текущей версии не увидел возможности создания древовидноо меню"
Есть модуль древовидного меню, но в полной версии, при этом некоторые операции даже в текущей версии включат в админке систему папок и файлов (дерево в общем).
Буду благодарен Вам за любой анализ на скорость и т. д. (кстати в core.php нужно снять знаки комментов с пары строчек и на страницах будет отображаться время вывода страницы в секундах).
Спустя 7 часов, 29 минут, 41 секунда (14.01.2011 - 09:33) linker написал(а):
Вот это
$php_code = "$".$separator." = new ".$separator."();";
eval($php_code);
заменяется обычным
${$separator} = new $separator();
Здесь нафига две переменные, какой сокровенный смысл в данном случае использовать абсолютно лишнюю $
sql_where?
$sql = "SELECT * FROM pages";
$sql_where = " WHERE page_url = '".$page."' AND status > 0";
Вот это вот все strpos(), strlen(), substr() и прочие абсолютно не будут работать в случае UTF-8, однобайтовыми кодировками уже мало кто пользуется. Мелочь, а не приятно
if ( ($this->row) AND array_key_exists($separator, $this->row) )
желательно
if ( ($this->row) && array_key_exists($separator, $this->row) )
Вот это
$php_code = "$"."this->".$separator."();";
eval($php_code);
также заменяется на
$this->$separator();
в целом ориентация на
php4 и win-1251 убивает систему в зародыше.
Спустя 5 часов, 50 минут, 27 секунд (14.01.2011 - 15:24) Гость_Алексей написал(а):
linker
"Вот это вот все strpos(), strlen(), substr() и прочие абсолютно не будут работать в случае UTF-8, однобайтовыми кодировками уже мало кто пользуется. Мелочь, а не приятно"
mbstring решит проблему) И всюду, где установлен
php5, mbstring ставится по умолчанию на сколько мне известно. Думаю прописать в ядре вместо strpos: mb_strpos и прочее, не составит труда.
Сейчас поставил на сайт кодировку UTF-8 и никаких проблем при использовании движка замечено не было, всё работает корректно.
lost_cluster
Как будет выводиться у вас подменю? И вообще дерево каталогов (меню)?
Что то так и не понял по какому принципу оно будет выводиться и какую структуру иметь.
Возможно ли в вашем движке путь к любой странице задавать как:
http://имя_сайта/имя_подменю/ ?
Или только
http://имя_сайта/имя_меню/имя_подменю/ ?
Ещё вопрос по ядру function drill, вот есть у нас страница в кеше, допустим существует файл с кешем, но чтоб этот файл вывести сперва всё-равно происходит чтение настроек set_drill_options, чтение текущей страницы set_page_struct , меню set_menus и прочее...Т.е. в любом случае все данные читаются...А смысл тогда кеширования?
А так мелочей править много...Повторяющегося кода хватает, при чём в самом ядре.
На счёт смысла реализации...Почему бы и нет? Думаю побыстрее чем ряд других cms и раз писали под себя, то вам будет удобно работать с этой системой, а это не мало важно.
Спустя 3 минуты, 18 секунд (14.01.2011 - 15:27) linker написал(а):
Гость_Алексей
Ну так это надо делать изначально, а не оставлять на потом, когда петух клюнет.
Спустя 17 минут, 46 секунд (14.01.2011 - 15:45) Forward написал(а):
Решил зарегестрироваться) (Алексей)
linker
Это всё-таки мелочь, которая легко правится в самом движке, а вы сразу пишите мол это убивает систему в зародыше. Как может убить систему то, что правится за минуту?
поставил кодировку utf-8 и всё работает...главное чтобы файлы шаблонов были так же в utf-8 )
Спустя 44 минуты, 57 секунд (14.01.2011 - 16:30) Basili4 написал(а):
ммм если код писался для пыхи 4 то переписать его под 5 сложно. Я точно говорю вот недавно правил с десяток скриптов.
Спустя 12 минут, 39 секунд (14.01.2011 - 16:42) linker написал(а):
Forward
А пользователям CMS вы тоже объяснять будете чего им там нужно поправить и где?
Спустя 36 минут, 55 секунд (14.01.2011 - 17:19) Forward написал(а):
linker
Я думаю текущая версия не предполагает под собой какое то широкое применение)
Автор хочет лишь узнать можно ли использовать такой подход при проектировании ядра или нет. Вот например реализация шаблонизатора в этой цмс удачна? (если не обращать внимание на проблемы с utf-8)
По мне так такой шаблонизатор должен быстрее того же Смарти работать.
А мелочей в коде хватает) Править и править
Спустя 3 дня, 1 час, 39 минут, 24 секунды (17.01.2011 - 18:59) Guest написал(а):
linker
Спасибо учту. Надеюсь Вы понимаете, что это вопрос оптимизации, а так как писалось это все одним человеком и ядро и админка, то иногда стояла задача заставить работать, а не блеснуть синтаксисом.
Цитата |
в целом ориентация на php4 и win-1251 убивает систему в зародыше. |
Очень категоричное заявление.
Во-первых, с чего вы взяли, что система ориентирована на php4? Во-вторых, всё достаточно просто преобразуется в UTF-8 и даже функции:
Цитата |
Вот это вот все strpos(), strlen(), substr() и прочие абсолютно не будут работать в случае UTF-8, однобайтовыми кодировками уже мало кто пользуется. Мелочь, а не приятно |
отлично себя чувствуют в случае UTF-8.
Forward
Цитата |
Как будет выводиться у вас подменю? И вообще дерево каталогов (меню)? |
В папке control\moduls\pages\ найдите файл index.
php и замените переменную $query следующим:
$query = SetQuery(array("modul"=>"pages",
"db_table"=>"drill_pages",
"db_cat"=>"drill_pages_cat",
"db_field"=>"db_name",
"db_sort"=>"name",
"db_order"=>"asc",
)
);
Это включит поддержку папок.
В файле controlpanel.
php замените контролы на вот эти:
$CPanel->AddControl('folder');
$CPanel->AddControl('file');
$CPanel->AddControl('divider');
$CPanel->AddControl('order_by_name');
$CPanel->AddControl('divider');
$CPanel->AddControl('filter');
$CPanel->AddControl('divider');
$params = array("db_name|Имя", "db_path|Ссылка");
$CPanel->AddControl('dl_field', $params);
Теперь вы сможете создавать папки.
За папки в модуле страниц отвечает таблица drill_pages_cat, впрочем в любом модуле префикс _cat в таблице говорит о том, что модуль поддерживает папки.
Для создания и вывода дерева у меня используется метод "материализованных путей", хотя и parent id также хранится в таблице.
Только второй вариант, но при необходимости можно внедрить систему алиасов.
Приведите пример, для чего это нужно?
Цитата |
Ещё вопрос по ядру function drill, вот есть у нас страница в кеше, допустим существует файл с кешем, но чтоб этот файл вывести сперва всё-равно происходит чтение настроек set_drill_options, чтение текущей страницы set_page_struct , меню set_menus и прочее...Т.е. в любом случае все данные читаются...А смысл тогда кеширования? |
set_drill_options так или иначе устанавливаться должен, в нём читается информация из базы, как раз указывающая на необходимость кеширования. Остальные функции мой косяк, нужно будет их убрать при использовании кеширования.
А смысл тогда кеширования?
Готовая HTML страница, не используется
функция преобразования сепараторов, классы модулей не создаются.
Чуть позже отвечу еще, нужно убегать.
Спустя 3 часа, 56 минут, 2 секунды (17.01.2011 - 22:55) linker написал(а):
Guest
strlen() от значения "Привет" в UTF-8 вернет только половину реальной длины этой строки т.д.
Спустя 18 часов, 9 минут, 59 секунд (18.01.2011 - 17:05) lost_cluster написал(а):
Цитата (linker @ 17.01.2011 - 19:55) |
Guest strlen() от значения "Привет" в UTF-8 вернет только половину реальной длины этой строки т.д. |
Не половину, а умножит на 2. Вместо 6 выдаст 12.
Решается проблема проще простого.
.htaccess
php_value mbstring.func_overload 2
php_value mbstring.internal_encoding UTF-8
Теперь я надеюсь ничего систему не убивает?
Спустя 10 минут, 51 секунда (19.01.2011 - 17:23) Forward написал(а):
Да, ещё... В любой CMS обязательно используйте префиксы таблиц, с возможностью изменения префикса в настройках сайта.
Сейчас 2 сайта на одной базе сделать не получится, а это весьма распространенная задача, на многих хостингах число БД сильно ограничено.
Чтобы сейчас создать второй сайт в той же базе, у вас во всём коде придётся префикс менять.
Спустя 35 минут, 16 секунд (19.01.2011 - 17:58) linker написал(а):
lost_cluster
Прошу прощения, обсчитался малясь. Систему не убьет использование функций расширения mbstring вместо их стандартных эквивалентов, на жуткий крайняк iconv() сойдет.
Спустя 1 день, 6 часов, 16 минут, 20 секунд (21.01.2011 - 00:14) lost_cluster написал(а):
Forward
Цитата |
Нужно например для той же раскрутки и получения дохода с сайта. |
Ну в общем-то я не поддерживаю подобный подход, но как полезную фенечку не отрицаю необходимость подобного.
Я думаю в будущем, после причесывания кода введу систему алиасов, это не так сложно.
Цитата |
Да и короткий путь до страницы вообще смотрится куда лучше нежели длинный. |
А как же каталоги? Намного изящней смотрится урл вида /catalog/odejda/kurtki/ нежели kurtiki.html, хотя можно и так catalog-odejda-kurtki.html.
На мой взгляд 1-й вариант самый корректный.
Цитата |
Да, ещё... В любой CMS обязательно используйте префиксы таблиц, с возможностью изменения префикса в настройках сайта. |
С этим совершенно согласен и в следующих версиях доработаю этот момент.
Спасибо за ваши замечания.
linker
Цитата |
Систему не убьет использование функций расширения mbstring вместо их стандартных эквивалентов, на жуткий крайняк iconv() сойдет. |
Самое простое решение я привел выше:
.htaccess
php_value mbstring.func_overload 2
php_value mbstring.internal_encoding UTF-8
В этом случае нет необходимости заменять strlen на mb_strlen.
Спустя 10 часов, 12 минут, 37 секунд (21.01.2011 - 10:27) linker написал(а):
lost_cluster
Не поверишь, но думаю, что большинству "человеков" в мире, как и мне собственно, абсолютно похрену как выглядит url в адресной строке (никто его не будет запоминать, для этого есть закладки/избранное). Все это бред заядлых ЧПУистов, для сдирания денег с клиентов за так называемые услуги СЕО.
Цитата |
Самое простое решение я привел выше: .htaccess php_value mbstring.func_overload 2 php_value mbstring.internal_encoding UTF-8
В этом случае нет необходимости заменять strlen на mb_strlen. |
![smile.gif](http://phpforum.ru/html/emoticons/smile.gif)
Чистой воды, наив.
Спустя 1 час, 4 минуты, 27 секунд (21.01.2011 - 11:31) lost_cluster написал(а):
linker
Цитата |
Не поверишь, но думаю, что большинству "человеков" в мире, как и мне собственно, абсолютно похрену как выглядит url |
Согласен с вами.
Цитата |
Чистой воды, наив. |
Это вы 1C расскажите, который сию конструкцию использует в bitrix ;D
Спустя 5 минут, 39 секунд (21.01.2011 - 11:37) linker написал(а):
lost_cluster
Ну битрикс известен своими говнокодами. Данная конструкция предназначена для настройки расширения mbstring - MultiByteString. Данное расширение предоставляет функции для работы со строками у которых многобайтовая кодировка (UTF-7, UTF-8, UTF-16). Все функции, которые относят к этому расширению начинаются с префикса mb_. Стандартные же функции php: strlen(), substr() и прочие никоим боком не относятся к этому расширению, а потому им пофигу, чего у вас и у битрикса написано в .htaccess
Спустя 6 часов, 36 минут, 16 секунд (21.01.2011 - 18:13) lost_cluster написал(а):
linker
А я бы на вашем месте проверил, не так-то и сложно. =)
Приведённая мной конструкция решает проблему со строковыми функциями при работе с кодировкой UTF-8. Нет необходимости использовать mb_
Просто протестируйте.
Спустя 1 день, 17 часов, 48 минут, 46 секунд (23.01.2011 - 12:02) linker написал(а):
lost_cluster
Да, пригляделся внимательнее и все понял, прошу прощения, что не заметил сразу.