Контроллеры и SkyGate - energy-coresky/air GitHub Wiki
В SKY-приложениях, классический роутинг отсутствует. Первых два параметра адреса запроса, определяют соответственно активный контроллер и действие в нем (метод контроллера). Если нужно, эту простую схему, можно изменить под любые требования системы адресации с помощью реврайтов Coresky. Для фильтрации, подготовки и ограничений внешних входных данных (в том числе postfields и "сырых" входящих данных приложений), используются небесные врата - SkyGate. Управление вратами производится с помощью удобной визуальной утилиты. Файлы контроллеров приложений компилируются также как и файлы представлений, и исполняются из папки var/gate. Компилированные файлы содержат оригинальный код контроллеров (код программиста из папки mvc), дополненный наследниками класса Guard (для врат), код в которых генерируется автоматически из данных полученных на этапе визуального редактирования.
Контроллеры и URL
Части семантических URL и query string, доступны в SKY приложениях сплошным потоком из $sky->_0
, $sky->_1
и т.д., смотрите рисунок:
Часть URL | Описание |
---|---|
1. указатель языка и/или мобильной версии | присутствуют при необходимости |
2. субдомен | то-же самое |
3. домен | имеется всегда, главная часть общего адреса запроса |
4. путь к проекту | константа PATH на продакшн обычно имеет значение "/", т.е. эта часть URL не используется. Но на DEV удобно помещать проекты в под-папки одного виртуального сервера, поэтому PATH обычно содержит имя проекта и папку www, ту которая подразумевается корнем HTTP сервера на продакшн. Она также часто называется public или public_html. В SKY-приложениях, ее можно переименовать как угодно. |
5. семантическая часть URL | $sky->surl, если не FALSE, то содержит части семантического URL. Его первые части могут определять контроллер, и возможно, действие (если не первый ключ query string). Для URL на рисунке выше, можно создать мастер контроллер c_page.php и действие в нем a_act(). |
6. строка запроса | Query String, все что после знака вопроса, заполняет массив $_GET в ядре PHP. При использовании семантических URL, может отсутствовать. Если отсутствует семантическая часть URL, то первые ключ-значение из $_GET, определяют соответственно контроллер и действие. |
В Coresky, любая обработка web-запроса обычно задействует два контроллера: мастер и общий. Хотя использование общего контроллера можно отключить совсем, если определить пустые методы head_y(..)
и tail_y(..)
в мастер-контроллере. Класс любого контроллера всегда наследует абстрактный класс Controller:
<?php
class c_page extends Controller
{
function head_y($action) {
}
. . .
function tail_y() {
}
}
Большинство имен контроллеров в Coresky приложениях, являются прото-именами данных из $sky->_0
с префиксом c_. Необходимо учитывать, что в случае использования rewrite, данные в $sky->_0
могут быть изменены в сравнении с внешним адресом запроса. Уточнение относительно семантического URL или query-string выполняется в SkyGate: для первого варианта установите чекбокс "Address has semantic part" и/или оставьте пустым поле "значение ключа". Контроллеры могут иметь три типа действий. Любые такие действия могут подготавливать переменные для шаблонов Jet или выполнять echo
в STDOUT:
- top-view - мастер действия, выполнение которых активируется непосредственно ядром, они всегда запускаются первыми. Такие действия не имеют смысла в общем контроллере. Только для этих действий настраиваются врата и только в них передаются внешние данные запроса из SkyGate.
- sub-view - дополнительные действия, активация которых происходит с помощью функции
view(..)
или директивы Jet @view(..). Возможно использовать вложенные вызовы. - blk-view - действия активируются при использовании функционала блоков Jet, имеющих указатели на хендлеры. Эти действия не создают объект класса MVC, в отличие от двух предыдущих.
Для реализации web-интерфейса "Development tools", код Coresky содержит контроллер w2/dev_c.php, который может быть задействован только при DEV==true
. Этот контроллер не использует реврайты или SkyGate, но использует трейт use HOOK_D;
. Код этого трейта дополняет код контроллера стандартными действиями. Например для отображения страницы "crash", если произошло фатальное завершение работы скрипта.
SKY-приложения, обычно имеют два контроллера с фиксированными именами и постфиксом:
- c_
$sky->_0
.php - мастер контроллеры с префиксом; - default_c.php - мастер контроллер с фиксированным именем, всегда использует трейт
use HOOK_D;
- common_c.php - общий контроллер, всегда использует трейт
use HOOK_C;
Его методы могут быть вызваны перед (и после) вызовом любого мастер действия top-view.
Необходимость специального мастер контроллера default_c.php обусловлена тремя причинами:
- действие
default_c::empty_a
определяет главную страницу, которая имеется в любом web-приложении; - этот контроллер использует
use HOOK_D;
, который дополняет код стандартными действиями. Наличие которых необходимо и достаточно для большинства приложений; - прото-имена top-view действий в этом контроллере соответствуют
$sky->_0
. Представьте, что необходимо простое действие для адреса http://example.net/test. Чтобы не делать отдельный маленький контроллер c_test.php, можно создать действиеdefault_c::a_test
. Наличие этого контроллера даёт возможность для выбора: создавать или нет отдельный контроллер для действия. Также, действиеdefault_c::default_a()
возможно определить только в этом контроллере.
Если, в общем случае, прото-имена контроллеров соответствуют $sky->_0
, то прото-имена действий соответствуют $sky->_1
(кроме действий в default_c и dev_c). Также имеются действия с фиксированными именами и постфиксом а не префиксом:
- a_
$sky->_1
и j_$sky->_1
- мастер действия с префиксом. - empty_a и empty_j - активируются, когда
$sky->_1
равно пустой строке - default_a и default_j - здесь "default" означает "всё остальное". Если необходимое действие не определено в контролере, но определено дефолтное, то оно будет задействовано.
Если определено действие default_c::default_a()
, то оно будет активировано для всех (любых) не CSN-AJAX запросов, для которых не будет найдено действие среди других определенных действий в других существующих контроллерах. Если имеется контроллер c_list.php и действие default_c::a_list()
, то последнее никогда не будет активировано. Ядро, всегда приоритетно пытается задействовать контроллер с префиксом.
В примерах Coresky приложений, часто используется реврайт, который задействует контроллер c_main.php с методом empty_a()
для главной страницы приложения. В этом контроллере, также рекомендуется размещать CSN-AJAX действия, которые должны быть доступны с различных страниц приложения (там где явно требуется указать контроллер, а не относительно, использовать "свой" контроллер).
Общий контроллер common_c.php, в отличие от других, не компилируется и исполняется из плана приложения, папки main/mvc, а не var/gate. Этот контроллер не содержит actions с пре/постфиксами "a", "j", но содержит методы head_y($action)
, error_y()
, tail_y()
, генерирующие переменные для layout (кроме error_y()
). Объект этого контроллера находится в MVC::$cc
, а объект мастер контроллера в MVC::$mc
. Вышеуказанные три метода, могут присутствовать и в мастер контроллерах, и из них можно вызвать соответствующие методы общего контроллера с помощью parent::
. С помощью метода error_y()
, можно настроить представление мягких ошибок (таких как 404) для всего приложения в общем контроллере или части приложения в мастер контроллерах. Также, в общем контроллере, удобно размещать sub-view и blk-view действия, которые должны быть доступны из разных мастер контроллеров.
Запуск sub-view и blk-view, всегда производится с помощью метода MVC::handle(..)
, как и поиск custom-директив для компилятора Jet. Этот метод, вначале пытается найти и запустить действие в "своём" мастер контроллере, в том, где был произведён запуск top-view. Если действие не найдено, то поиск продолжается в общем контроллере.
Специальные методы контроллеров
Контроллеры SKY-приложений содержат различные методы с однобуквенными префиксами и постфиксами, которые имеют специальное значение. Но только для top-view действий, использующих "a" и "j" будет сгенерирован код SkyGate в компилированных контроллерах.
Префикс или постфикс | Описание |
---|---|
a | top-view: обычные action, какие имеются в большинстве популярных фреймворк, в том числе Laravel. Могут обрабатывать, в том числе, ajax запросы (но не CSN-AJAX) |
j | top-view: специальные action для обработки CSN-AJAX, в котором имеется много дополнительного функционала для упрощения разработки. $sky-fly == 1 для этого случая. Помимо того, что должны присутствовать HTTP заголовки X_Action_J и X_Requested_With == xmlhttprequest, в запросах, всегда отсутствует семантическая часть адреса, и метод HTTP запроса всегда POST. |
x | sub-view: методы с префиксом x предназначены для действий sub-представлений. Префикс x назначается по умолчанию, если явно не указан никакой другой. Такие действия могут быть определены и в мастер и в общем контроллерах. |
r | sub-view или block действия, включающие механизм RED-LABEL. |
y | методы head_y($action) , tail_y() , error_y() могут быть определены и в мастер и в общем контроллерах. Методы head_y($action) , tail_y() выполняются соответственно до и после мастер-действия. Код в этих методах является общим для всех действий контроллера или всех действий приложения. Переменные в возвращенном массиве из этих методов, будут присвоены MVC::$_y и отражены, как правило, в layout. Если однобуквенный префикс явно не указан, в компилированном шаблоне Jet будет применен префикс $y_ . |
b | blk-view: рекомендуемый префикс для действий блоков Jet. Но для блоков, как и для sub-view, могут использоваться и другие буквы латинского алфавита. Для блоков нельзя использовать буквы, которые используются для действий top-view. Нельзя также использовать буквы "e" и "k", - первая используется для переменных итераторов-eVar, а вторая для переменных из SKY::$vars . Таким образом, доступные для блоков и sub-представлений буквы: b, c, d, f, g, h, i, l, m, n, o, p, q, r, s, t, u, v, w, x, z |
Шпаргалка для быстрого ориентирования, top-view flow:
Выполнение кода начинается с передачи управления методу ::head_y(..)
мастер контроллера. Если этот метод не определен, то родительский метод Controller::head_y(..)
запустит аналогичный метод общего контроллера. После этого запустится код SkyGate и только, если не было ошибок, управление будет передано мастер действию, смотрите код MVC::top()
. Такая архитектура нивелирует понятие "MiddleWare" и позволяет очень гибко корректировать протекание запроса на всех уровнях. В common_c::head_y(..)
, можно инициализировать класс USER, произвести проверку CSRF и выполнить прочие подобные действия. А в common_c::tail_y()
в некоторой позиции обычно написано:
if (!MVC::$layout)
return;
// далее следует основная генерация переменных для layout
Для мастер действия c_somecontr::a_someact(..)
, ядро автоматически предустановит шаблон представления somecontr.someact
, это подразумевает имя файла шаблона "_somecontr.jet" и маркер .someact, читайте подробнее в документации о шаблонизаторе Jet. Предустановка файла layout обычно выполняется в common_c::head_y(..)
кода приложения. В SKY-приложениях, довольно редко приходится изменять предустановленные layout и body шаблоны Jet, так как возможно создавать алиасы для маркеров. Но изменить предустановленный layout можно с помощью MVC::$layout и body с помощью MVC::body(..)
С помощью действий sub-view, можно изолировать части визуализаций, с целью их использования на разных страницах веб-приложения или повторения на одной странице. А мощная система манипулирования блоками кода представлений шаблонизатора Jet, позволяет производить специальные действия blk-view для визуализации блоков.
Трейты HOOK_C и HOOK_D
Как упоминалось ранее, трейт HOOK_D
содержит стандартные действия. Их можно переопределить, но они могут быть полезны для большинства веб-приложений:
- ::a_crash() - страница для визуализации фатальных ошибок. Используется в том случае, если без редиректа корректно отобразить ошибку невозможно из-за наличия непредвиденных данных в STDOUT;
- ::a_etc(..) - позволяет загружать файлы под управлением реврайтов. Активно используется в "dev-tools", но метод полезен и на продакшн;
- ::j_init(..) - после создания новой сессии, Coresky пытается сделать CSN-AJAX запрос, подтверждая, что клиент обрабатывает javascript. Выполняя эту цель, попутно на backend передается разрешение монитора и Timezone из браузера клиента. Если, после создания сессии произошел вызов этого действия, то можно предполагать что мы имеем дело с реальным человеком. Если это бот, то по крайней мере не простой, а такой, который умеет обрабатывать javascript.
Трейт HOOK_C
содержит хуки для настройки ядра Coresky:
- ::langs_h() - используется в многоязычных веб-приложениях;
- ::rewrite_h(..) - запускается всякий раз перед вызовом
MVC::top()
- ::head_h() - используется совместно с
HOOK_D::j_init(..)
; - ::user_h(..) - используется для создания объекта класса
User
; - ::dd_h(..) - вызывается всякий раз после создания соединений с реляционными базами данных;
- ::make_h(..) - вызывается для генерации кода "first-run";
Хуки трейта HOOK_C
позволяют перестраивать ядро Coresky таким образом, что имеется возможность создавать приложения произвольных типов: от микросервисов и API до сложных многоязычных веб-приложений. Подробная информация о методах трейтов имеется в документации, в соответствующих разделах.
Коротко о SkyGate
Визуальная утилита, сохраняет конфигурацию врат в хеш-массиве, в файле main/gate.php (который является частью кода приложения), а компилятор файлов контроллеров, подобно компилятору шаблонов Jet, использует эту информацию и дополняет код контроллеров автоматически сгенерированным кодом врат. Этот код, утилитой SkyGate, предоставляется "на лету", во время редактирования.
Если в SKY-приложении открыть страницу, для которой не существует действие, - будет показана 404 ошибка. Но если создать пустое действие и попытаться открыть такую страницу, будет показана ошибка SkyGate и предложено настроить врата (только в режиме DEV):
Для действий j HTTP метод фиксирован POST и выбирается утилитой автоматически, они не могут содержать семантическую часть в адресе запроса.
Для действий a HTTP методы необходимо выбрать руками. Если семантическая часть адреса запроса, содержит две или более составляющих - просто не заполняйте ключи в параметрах или используйте чекбокс "address has semantic part".
Для переменных внешних данных, нужно использовать регулярные выражения. Такие параметры (но не постоянные) передаются из врат в мастер-действия через массив PHP. Для одного параметра используется одна переменная, но их можно объединить в массив или объект, если определить "key name / value name". Для частей запроса которые могут присутствовать, но могут и отсутствовать во входных данных, можно использовать чекбокс NS (Not Strict).
Преимущества SkyGate
-
Для поиска роутера, подходящего к каждому HTTP запросу, всегда тратится процессорное время. SKY-Gate лишен этого недостатка, за исключением того, что подобное использование ресурсов, может быть необходимо для осуществления Coresky rewrites. Однако, часто, без реврайтов можно обойтись и алгоритм их работы более быстр.
-
Роутеры могут определять доступные HTTP методы и осуществлять фильтрацию данных в адресной части запроса, но не для postfields. SKY-Gate способен делать фильтрацию postfields, а также, концептуально, осуществлять полный комплекс мер по подготовке, фильтрации и т.д. любых входных данных. Код SKY-Gate генерируется один раз автоматически, во время компиляции контроллеров приложений. Рутинное программирование заменено визуальной настройкой с помощью веб-интерфейса, это предпочтительно.
-
Реврайты Coresky, которые являются логическим продолжением SkyGate, намного эффективнее решают сопутствующие проблемы адресаций в веб-приложениях (читайте ниже), чем это делается в системах с классическими роутерами. Хорошо, когда алгоритмы и процессор, "напрягаются" на DEV, а не на продакшн.
Red label
Этот механизм полностью игнорируется на продакшн, а в режиме DEV, имеется возможность отключить часть кода и вместо его представления, показать на странице блоки <DIV> с красным фоном и именем метки. Это бывает полезно во время разработки, когда мешает js-код, осуществляющий запросы на внешние ресурсы: счетчики, информеры, код Google analytics или в других подобных случаях.
Возвращаемые из контроллеров значения
Как упоминалось выше, возвращенный массив из методов head_y($action)
, tail_y()
, будет присвоен MVC::$_y
(переменные layout). Если массив возвращается из действий a, j или x (действия subview), он будет добавлен к $mvc_instance->_v
(переменные body).
Если вернуть null
- это не произведет никаких специальных действий.
Возвращенная строка из $mvc_instance->tail_y()
установит новый MVC::$layout
, а из других методов установит $mvc_instance->body
.
Возвращенный integer, например return 404;
активирует "мягкую" ошибку приложения.
return true;
- присвоит пустую строку и body и layout. return false;
- присвоит пустую строку только layout, смотрите код метода MVC::set(..)
.
Перед запуском действий в контроллерах, включается буферизация вывода в STDOUT с помощью ob_start()
поэтому, если нужно выдать большой файл, делать это нужно по схеме:
function a_action() {
. . .
ob_end_clean();
readfile("bigfile.zip");
throw new Stop;
}
Coresky rewrites
Стандартный файл реврайтов, можно сгенерировать набрав в консоле: sky rewrite
. Эта команда использует первые три реврайта из библиотеки.
Чтобы открыть визуальную утилиту для редактирования и тестирования реврайтов, кликните "Show map" в утилите SkyGate. Изменив реврайты, можно "поломать" работу dev-tools, поэтому будьте осторожны с R1 и R2:
При сохранении реврайтов, делается проверка на корректность PHP кода. Кроме того, чтобы актуализировать новые реврайты, нужно нажать "Drop All Cache".
Настроенные реврайты сохраняются в массиве, в файле main/rewrite.php. Этот файл, как и main/gate.php, main/wares.php является частью кода приложения, поэтому используется план main.app. Во время генерации файла var/cache/sky_plan.php, объединенный код реврайтов, записывается в SKY::$plans['main']['rewrite']
. Этот код выполняется при каждом не консольном протекании скрипта, в конце конструктора HEAVEN, перед запуском MVC::top()
. Для анализа внешнего, запрашиваемого адреса, можно использовать переменные:
$cnt # кол-во частей семантического URI
$surl # массив частей семантического URI
$uri # полный URI запроса, необходимо считать что $uri === URI (константа, только чтение)
$sky->_0, $sky->_1 и т.д. # сплошной поток частей запроса (только чтение)
$_GET # query string superglobal array
Вместо константы URI, необходимо использовать переменную $uri
, так как код реврайтов запускается много раз (за один клик), с виртуально измененным адресом запроса для получения карты внешних URI на странице "CORESKY REWRITES MAP". Виртуальное изменение адреса запроса, также делается во время тестов:
Чтобы сделать реврайт, для записи, необходимо использовать $_GET
или $surl
(по ссылке передаётся $sky->surl). Также, если реврайт изменил кол-во частей $surl
- изменяйте $cnt
, т.к. анализ этого значения, может понадобиться в последующих реврайтах.
Типы реврайтов
Реврайты бывают: терминальные, реверсивные, аддитивные, субстрактные и блокирующие.
Терминальные реврайты не осуществляют непосредственно реврайт, но прекращают обработку, так чтобы она не происходила в дальнейших реврайтах. Это позволяет упростить (оптимизировать) код других реврайтов. Пример:
if ('_' == $sky->_0[0]) # no rewrites for dev-tools
return;
Реверсивные реврайты, делают такой реврайт, что при их двойном использовании, получаем первоначальный URI. Примеры:
пример 1: /1/2/three => /1/three/2 => /1/2/three
пример 2: /x/y/page.html => /x/y/page => /x/y/page.html
пример 3: /dash-surl/x/y => /nodash/x/y => /dash-surl/x/y
PHP код для примера 3:
$rw = [
'dash-surl' => 'nodash',
];
if ($cnt) {
$rw += array_flip($rw);
$p =& $surl[0];
empty($rw[$p]) or $p = $rw[$p];
}
Такие реврайты использовать предпочтительно, так как сохраняется каноничность системы адресации (активация действий, как и до реврайта, возможна только по одному URI). К тому же, получение внешнего URI, на странице "CORESKY REWRITES MAP", гарантировано происходит только в 2 шага и без использования теста.
Аддитивные реврайты позволяют активировать контроллер/действие более чем по одному URI:
if ($cnt && 'x' == $surl[0])
$surl[0] = 'ctrl';
# can reach c_ctrl::... via two URI:
# /ctrl/... and /x/...
Чтобы получить внешние URI на странице "CORESKY REWRITES MAP", для таких реврайтов, нужно верно указать тест-подсказку. Для переменных частей URI, в тестах, нужно указывать {цифра}
(где "цифра" - номер переменного параметра) или записывать только начало URI (определяющее контроллер), смотрите примеры в библиотеке. Отсутствие нужного внешнего URI на странице "CORESKY REWRITES MAP" не влияет на работоспособность кода приложения. Но все внешние URI, которые показаны - проходят проверку на реальных реврайтах, с виртуально измененным адресом запроса.
Субстрактные реврайты, в основном, выбирают информацию из URI не для адресных целей. Такие реврайты, так-же как и аддитивные, увеличивают количество вариантов URI для активации действий. Примеры PHP кода, можно посмотреть в библиотеке реврайтов в утилите SkyGate:
# take language from URI:
/en/ctrl/action => /ctrl/action
# take page number from URI:
/ctrl/page-2/action => /ctrl/action
Все тесты должны начинаться с символа "/". Если указать пустую строку - тест не будет использоваться для поиска внешних URI. Если тестов не хватает - можно пустую строку ввести в поле реврайта, но заполнить тест. Это никоем образом не уменьшит производительность на продакшн.
Блокирующие реврайты, обычно используются совместно с аддитивными. Они блокируют активацию действия по прямому URI:
if ($cnt && 'ctrl' == $surl[0])
return $surl[0] = '404';
# cannot reach c_ctrl::... via /ctrl/... URI
Реврайты для production-продуктов и для i18n
Реврайты удобно использовать для wares, чтобы разрешать возможные коллизии в URI и именах контроллеров. Представьте ситуацию: имеется Coresky-приложение с множеством контроллеров. Разработчик захотел присоединить production-продукт "Forum" с ещё десятью контроллерами. Контроллер c_topic.php имеется и в приложении и в форуме. Нет проблем: если production-продукт умеет работать с изменяемой адресацией, реврайты можно сделать так:
/variable-forum-key/topic/action => /topic/action
Трейт HOOK_C, имеет статические свойства HOOK_C::$lg
, HOOK_C::$page
, HOOK_C::$ware
. В эти переменные, необходимо заносить дополнительную информацию, полученную реврайтами для языка страниц, номера страницы pagination и ware соответственно. Код для получения языка из сематического URL:
common_c::langs_h();
if ($cnt && in_array($surl[0], $sky->langs)) {
common_c::$lg = array_shift($surl);
$cnt--;
}
Пагинация
Функция ядра pagination(..)
, может помочь сделать пагинацию для любого типа адресации. Если, номер страницы необходимо указать в семантической части url, то нужно применить примерно такой реврайт:
if ($cnt > 2 && preg_match("/^page\-\d+$/", $surl[1])) {
common_c::$page = (int)substr($surl[1], 5);
array_splice($surl, 1, 1);
$cnt--;
}
Код выше модифицирует массив $surl
и заносит номер страницы в common_c::$page
. Если это проделано для страницы, где пагинация не используется, то код backend должен дать ответ 404. То-же самое, если пагинация используется, но указан несуществующий номер страницы. В будущем, этот функционал будет внесен в SkyGate, а сейчас это можно проделать разными способами. Например, сделать проверку так:
<?php
class common_c extends Controller
{
function head_y($action) {
. . . # when pagination do not used for the action
if (false !== self::$page && !$this->test_pagination($action))
return 404;
}
. . .
function tail_y() {
. . .
if (false !== self::$page) # when page not exist
return 404;
}
}
Заметим, что код в tail_y()
, необходимо применять для любого типа адресации, например, когда номер страницы указывается в query string. Альтернативно, такую проверку можно делать непосредственно в действии, где используется функция pagination(..)
.
assets
на DEV
Папки В режиме DEV, файлы статики (*.css и *.js), могут реально не находиться в папке public. А файлы статики DEV-wares никогда там не находятся, но браузер делает запросы в папку public. Если файл отсутствует, то запрос перехватится index.php, и с помощью реврайтов, Coresky вернет содержимое этих файлов браузеру из папок assets
.
В самом начале трассировки, всегда можно увидеть урл реального запроса и как он трактуется кодом Coresky после реврайта.
При разработке Coresky, одна копия кода, подключается к нескольким приложениям. Это касается и файлов sky.js, dev.js и т.д. Поэтому, на DEV, удобно использовать реврайты для файлов статики. По похожим причинам, удобно делать подобную обработку и для файлов статики DEV-продуктов.