Конфигурации в Coresky - energy-coresky/air GitHub Wiki

В Coresky, конфигурации бывают статические и динамические. Статические кэшируются в плане cache, а источником данных, обычно, являются Yaml или JSON файлы. Иногда бывает необходимо иметь возможность быстро изменить конфигурацию на продакшн. Для таких целей можно использовать Ghost SQL, которые сохраняют информацию в реляционных базах данных, а изменение данных, возможно из web-интерфейса или консоли. Ghost SQL можно рассматривать как функционал не только для конфигураций, а как специализированный функционал для работы с реляционными базами данных, который можно использовать различными способами.

Файлы config.yaml

И SKY-приложения и SKY-продукты, обязательно содержат файл config.yaml в главной директории плана app. В консоли всегда можно проверить, как парсятся yaml-файлы. В команду y скрипта sky также можно передать inline-Yaml, если второй параметр содержит пробел:

# parse config.yaml
> sky y mercury
# parse inlne-yaml
> sky y "- # array(0 => null)"

В первой команде выше, второй аргумент - имя продукта, по умолчанию main, основное приложение. Третий - имя файла (в примере выше отсутствует), по умолчанию config.yaml.

Файлы config.yaml, являются ключевыми. Можно определить сколь угодно файлов конфигураций, но ссылка на каждый, должна присутствовать в указанном файле, в этом случае, всегда возможно получить доступ к кэшированным конфигурационным данным с помощью функции cfg(..). Ключ верхнего уровня определяет раздел конфигурации, и раздел core обязателен. Вся информация этого раздела (и приложений и подключенных продуктов), кэшируется в файле var/cache/sky_plan.php, который подгружается всегда. Большинство данных, указанных в нём необходимо для работы ядра Coresky:

core:
  timezone: $APP_TZ(UTC)
  define:
    DEV: $APP_DEV(false)
    DEBUG: 0
    DEV+DEBUG: 1 # 1, 2, 3 - verbosity level, 0 - debugging off
    _PUBLIC: hole.sky
    DEFAULT_LG: ''
  ini_set:
    log_errors: 0
    display_errors: 0
    DEV+display_errors: 1
  profiles:
    - Anonymous # pid=0 !!!
    - Root
    - Mia
  databases: {driver: sqlite3, dsn: hole.base}

auth: # mandatory in web for users
  crypt: false

test: ../.env

Для продуктов, подраздел plans также обязателен:

# this is an example of config.yaml from mercury ware
core:
  plans:
    app:
      type: dev
      require: SQLite3
    view: {path: $SELF/mvc}
    cfg: {path: $SELF/mvc}
  databases:
    driver: sqlite3
    dsn: $SELF/mercury.base
  test2: ok

test3: composer.json

Подразделы plans, define, timezone, ini_set интегрируются в файл кэша особым образом. Информацию других подразделов раздела core, как и информацию других разделов, всегда можно прочесть с помощью функции cfg(..):

echo cfg()->databases['dsn']; # ware=main, name=core by default
echo cfg('auth')->crypt # ware=main by default, name=auth
echo cfg(['mercury'])->test2 # name=core by default, ware=mercury

print_r(cfg('test')); # ware=main (application) by default
print_r(cfg(['mercury', 'test3'])); # return stdClass object

Кэш-файлы других разделов, отличных core, сохраняются в отдельных файлах в директории var/cache (если план cache работает с помощью драйвера dc_file, но возможно использование dc_redis):

var/cache/cfg_{ware}_{name}.php
# where
# {ware} - ware name
# {name} - section name

Кэш-файлы разделов config.yaml, значениями которых являются массивы, генерируются одновременно с генерацией файла var/cache/sky_plan.php. А разделы, значениями которых является строка, т.е. указатель на внешний файл, генерируются во время первого опроса данных. Если нужно, чтобы такой раздел сгенерировал кэш во время генерации основного кэш-файла, нужно добавить оператор @inc: test: @inc ../.env. Формат внешних файлов, определяется по расширению: .php php-файлы, .txt текстовые файлы, .yml, .yaml yaml-файлы, .json - json-файлы, в других случаях будет считаться, что формат файла - bang. В случае присоединения PHP-файлов, делается require, т.е. подразумевается, что они организованы по схеме:

<?php
return [ # array or scalar
  . . .
];

Для запуска функции cfg(..), в консоли, можно использовать команду eval:

> sky eval "print_r(cfg('test'));"

Функция yml(..)

Помимо извлечения конфигурационных данных с помощью функции cfg, доступна к использованию функция yml. Эту функцию удобно применять для случаев, когда yaml-описания, используются для программирования, а не просто для извлечения кэшированных конфигурационных данных:

# inline yaml не кэшируется:
yml('+ @json @inc(timezones)';

# inline yaml кэшируется в файле var/cache/yml_dev_dev.cfg.php:
yml('dev.cfg', '+ @inc(dev_cfg)');

# при использовании данных, передаются переменные, подобно работе Jet:
yml('dev.form', '+ @inc(dev_form)', [
    'key' => $key,
    'val' => $val,
]);

Формат "Bang"

Функция PHP explode(..) разбивает строку в массив, где ключи - целочисленные значения. Функция Coresky bang(..) похожа на предыдущую, но имеет два разделителя и разбивает строку в массив, где ключи - строки. С помощью такого формата, обычно упаковывают данные в файлах .env. В идеалистическом смысле, алгоритм работы с bang довольно быстр. В файле main/w2/core.php, имеются функции:

# упаковка
unbang($array, $via1 = ' ', $via2 = "\n") : string
# распаковка
bang($string, $via1 = ' ', $via2 = "\n") : array

Части строк, не содержащие разделитель via1 игнорируются:

$my_env_file = '
# this is an comment
KEY1=VAL1=..
KEY2=VAL2
'; # unl(..) - UnixNewLine: \r\n => \n, \r => \n
print_r(bang(unl($my_env_file), '=')); # array (
#  'KEY1' => 'VAL1=..',
#  'KEY2' => 'VAL2',
#)

Этот формат широко используется в Coresky, например в парсере Yaml или функционале Ghost. Во втором случае используются разделители " ", "\n" (via1, via2), а значения эскейпятся, так, что можно сохранять любые символы. Смотрите пример, как упаковывается массив. Обратное преобразование, также проходит без проблем:

$array = [
  'k1' => "dat \n 1",
  'k2' => "dat2 \\",
];
# упаковывается в:
$string = "k1 dat \\n 1\nk2 dat2 \\\\";

Синтаксис Yaml парсера

Парсером можно воспользоваться в любом месте кода или сделать проверку синтаксиса Yaml:

# parse inline yaml string:
$php_array = YML::text('- test: data');
# parse from file:
$php_array = YML::file('/path/filename.yml');
# lint the Yaml-string:
$is_ok = YML::lint('+ the scalar');

Coresky Yaml парсер, в основном, поддерживает стандартный Yaml-формат, хотя некоторые вещи не поддерживаются и есть собственные дополнения. Изменения, с одной стороны способствуют тому, чтобы сделать поведение парсера более простым и предсказуемым. С другой, более эффективно интегрируют парсер в фреймворк и раскрывают всю потенциальную мощь Yaml описаний.

Поддерживаются: комментарии, стандартная иерархия с помощью отступов, дефисов, двоеточий, а также JSON-нотация. Неявный многострочный режим с помощью отступов, а также явный, с помощью символов | и >. Нельзя использовать символ табуляции для отступов. Неявная типизация сведена к минимуму:

bool: true|false only! yes/no isn't bool!
null: null|`empty_string` only! ~ isn't null!
empty string: ""|'' only!
int: 100|+100|-100 decimal numbers only, same like PHP decimals
float: 100.0|1e2 & with sign, same like PHP floats
string: value # all other is string or just quote string

Не поддерживаются: директивы, начинающиеся с символа %..., директивы: ---, .... Не поддерживается явная типизация, например !!int и неявная типизация, которая не включена в список поддерживаемой, например преобразование дат.

Дополнительный функционал Yaml

Стандартный multiple-yaml-document не поддерживается, но поддерживаются именованные множественные документы с помощью маркеров. Маркеры в yaml файлах, работают по той же схеме, что и маркеры компилятора представлений Jet (Coresky адаптация/модификация компилятора Laravel/Blade), и оператор @inc(..), работает по той же схеме. Это даёт возможность свободно применять принцип DRY и произвольно компоновать Yaml-данные:

#.run =========================
- @inc(.test)
- 2
#.run

#.test =========================
+ 123
#.test
# php code:
print_r(yml('+ @inc(run) filename.yml'));
# stdout: array(0 => 123, 1 => 2)

PHP поддерживает гибридные массивы со строковыми и целочисленными ключами, поэтому, разрешено совмещать такие ключи Yaml-нотации. Это даёт большие возможности для использования Yaml парсера в программировании, например можно выразительно и компактно описывать HTML-формы с помощью Yaml, смотрите пример в w2/__data.yaml. Ввиду того, что имеются операторы @inc и @path, для полноты по Тюрингу, в Yaml-нотации, к минусу добавлен плюс:

a:
  b: @hex 11
  x: ^y
+ d: # здесь + d: равносильно просто d: без плюса и отступа
  e: eee
  + @path(a)
  - @path(a)
  + @path(a.x)
  - @path(a.x)
# return php array:
['a' =>
    ['b' => 17, 'x' => '^y'],
 'd' => [
    'e' => 'eee',
    'b' => 17,
    'x' => '^y',
    '0' => ['b' => 17,'x' => '^y'],
    '^y' => null,
    '1' => '^y'
 ]
]

Второй пример Yaml: + scalar. Yaml-код на выходе - скаляр (строка), а не массив PHP. Когда плюс на топ уровне иерархии и значение - скаляр, парсинг останавливается!

Yaml-файлы могут содержать TAIL/tilda-комментарии/данные. Вначале парсинга, Yaml разбивается на части с помощью следующего кода PHP: $TAIL = explode("\n~\n", $input). Парсером, всегда разбирается только часть $TAIL[0]. В остальных частях хвоста, можно написать комментарии к файлу, без использования символа #, либо ссылаться на них в коде Yaml с помощью псевдопеременных $TAIL0 (основной Yaml), $TAIL или то-же самое $TAIL1, $TAIL2 и т.д., а также $TAILS. В последнем случае вернется массив хвостовых данных, но без $TAIL0:

+ $TAILS # int можно получить так: @dec @each $TAILS
- 555
+ $TAILS # здесь целочисленные ключи 0,1 изменятся на 3,4 !
- $TAILS
- $TAIL
- $TAIL2
~
123
~
2
# return php array:
[0 => '123', 1 => '2', # в $TAILS только строки !
 2 => 555,
 3 => '123', 4 => '2',
 5 => [0 => '123', 1 => '2'],
 6 => 123,
 7 => 2,
]

Псевдопеременные можно указать только вначале значений Yaml или JSON нотаций. Их имена соответствуют регулярному выражению (только верхний регистр): /\$[A-Z_\d]+. Кроме псевдопеременных группы $TAIL, имеется также $SELF - директория текущего файла. Продукты (ware) могут располагаться в разных местах и $SELF позволяет явно определять относительные пути.

Вместо явного значения, можно использовать подстановку переменных окружения (если есть скобки), например: $APP_DEV(false). Точнее будет производиться вызов метода Rare::env($key, $default), смотрите код в файле w2/rare.php. Если переменной окружения APP_DEV не существует, но существует файл .env в главной директории приложения, он будет считан и трактован как файл формата bang. Если и в нём не будет найдена переменная, будет подставлено значение по умолчанию, указанное в скобках - false. Имена переменных окружения могут быть только в верхнем регистре. Если необходимо указать в значении подобную строку - просто заключите её в кавычки. Также, в настройках можно использовать альтернативные DEV-конфигурации с помощью префиксов DEV+, смотрите пример:

core:
  define:
    DEV: $APP_DEV(false)
    TEST: "$JUST_STRING(false)" # строка в кавычках всегда просто строка
    DEBUG: 0
    DEV+DEBUG: 2 # альтернативное DEV значение
    'DEV+TEST': # строка в кавычках всегда просто строка
    file: $SELF/other.yml

Здесь, если DEV == true, то значением ключа DEBUG будет 2, а если DEV == false, то DEBUG будет равен 0. Псевдопеременная $SELF: если вначале любого значения указана такая переменная, то вместо неё, будет подставлен путь к текущему yaml-файлу. Но если в парсер имя файла не передано, то будет подставлено ???. Также можно ссылаться на глобальные константы (в основном это пути или константы определенные модулями PHP), и применять функции трансформации, например:

rss: $WWW/rss.xml
data: $DIR/var/data
pi: $M_PI # return 3.1415926535898
img: @base64 | # transformation function, same like !!binary in standard Yaml
  R0lGODdhDQAIAIAAAAAAANnZ2SwAAAAADQAIAAACF4SDGQ
  ar3xxbJ9p0qa7R0YxwzaFME1IAADs=

Известный модуль PECL yaml, позволяет использовать теги для значений Yaml, передавая callback-функции в функцию yaml_parse. В Coresky применяется более мощный функционал с использованием функций трансформации (операторов), которые начинаются с символа @, за которым может следовать значение. Операторы Yaml используют тот-же формат, что и операторы Jet. Имеется некоторое множество встроенных функций, смотрите раздел ниже. Они могут использоваться со скобками или без, как и в Jet. Если скобок или значения нет - передается null. Если скобка открылась и сразу закрылась, в $x передаётся пустая строка. При вызовах таких функций-операторов, им передаются следующие переменные:

  • $v - значение, следуемое за оператором
  • $x - параметр передаваемый оператору в круглых скобках
  • &$a - массив, в котором содержится распарсенная часть файла, к моменту, когда вызывается оператор
  • $has_var - булева переменная указывает на то, что значение, передаваемое в $v взято из переменной или указано явно

Приложения и продукты, могут дополнять список встроенных операторов, custom операторами. Они могут быть определены inline способом с помощью оператора @eval смотрите ниже. Также, их можно определить в общем или мастер контроллерах приложений, ровно как и custom операторы Jet. Однако следует знать, что такие операторы не подгружаются при работе в консоли:

. . .  # Controller file
    function yml_c() {
        YML::directive('addhash', fn($v) => "#$v");
    }
. . .

Также, custom операторы, можно определить в файлах mvc/yaml.php. В этом случае, они подгружаются при работе в консоли:

<?php

return [
    'addhash' => fn($v) => "#$v",
];

Для одного значения, можно указать несколько операторов и операторы могут применяться каскадно для всех значений под-массива. Порядок выполнения - справа налево и снизу вверх. Для частной отмены каскадного выполнения, можно использовать @deny:

# yaml:
one: @right( RR)
  two: @bin
    - 0b11
    - 101 # можно и без префикса 0b
    - @deny @left(LL ) 1010
  three: four
color: @left(#) @each @bang(. ) @sar(| +| ) > # not in cascade
  aliceblue.f0f8ff     antiquewhite.faebd7
  beige.f5f5dc         bisque.ffe4c4
# return php array:
['one' =>
  ['two' => [
      0 => '3 RR',
      1 => '5 RR'],
      2 => 'LL 1010',
   'three' => 'four RR',
  ],
 'color' => ['aliceblue' => '#f0f8ff', 'antiquewhite' => '#faebd7',
             'beige' => '#f5f5dc', 'bisque' => '#ffe4c4'],
]

Встроенные операторы

Трансформация значения:

@time - применить функцию PHP strtotime(..) : int|false. Обрабатывается значение, а в скобках можно указать baseTimestamp

@date - применить функцию PHP date_create_immutable(..) : DateTimeImmutable|false. Обрабатывается значение, а в скобках можно указать временную зону в формате, пригодном для функции PHP date_default_timezone_set(..), которое будет преобразовано в объект DateTimeZone

@ip - применить функцию PHP ip2long(..)

@hex2bin - применить функцию PHP hex2bin(..), байты можно разделять пробелом:

- @hex2bin >
  48 65 6C 6C 6F 20 77 6F 72 64
  21
array(0 => 'Hello word!')

@base64 - декодирование с помощью base64_decode(..). Эквивалентно !!binary стандартного Yaml.

@dec - все примеры десятичное integer:

- @dec 1234_5678_9012_3456 # card       >>> 1234567890123456
- @dec 1 000 000 000       # big number >>> 1000000000
- @dec 2-777-222-22-22     # phone      >>> 27772222222

@bin, @oct, @hex - трактовать значение, как integer в двоичной, восьмеричной или шестнадцатеричной системе счисления. Код бинарной функции: fn($v) => intval($v, 2)

@str - применить функцию PHP strval(..)

@json - применить функцию PHP json_encode(..)

@rot13 - применить функцию PHP str_rot13(..)

pwd: @rot13 arnaqreguny pelcgbtencul # :)
# parsed to PHP code:
['pwd' => 'neanderthal cryptography']

@object - сделать массив объектом класса stdClass

obj: @object @bang(=|) |
  a=1|b=2|c=3
# parsed to PHP code:
['obj' => (object)[
  'a' => '1',
  'b' => '2',
  'c' => '3',
]]

@left(string), @right(string) - дописать string слева или справа

@sar(DelimSrchDelimRepl) - SearchAndReplace применить функцию PHP preg_replace(..), при том что Delim - разделитель один произвольный символ:

sar: @sar(|=+| is ) a=============1, b====2
# parsed to PHP code:
['sar' => 'a is 1, b is 2']

Из строки в массив или наоборот:

@csv(delimiter) - разбивает строку с помощью функции PHP explode(..)

@space - разбить строку по пробельным символам с помощью Regexp. В скобках можно указать максимальное количество элементов массива

@split - разбить строку с помощью функции PHP preg_split(..). В скобках необходимо указать Regexp

@scan - применить функцию PHP sscanf(..). В скобках - формат

@match - строку в массив. Применить функцию PHP preg_match(..). В скобках - Regexp. $match[0] автоматически удаляется

@url - применить функцию PHP parse_url(..)

@bang - трансформирует строку в формате bang в массив. В скобках можно указать один или два разделителя

@range - применить функцию PHP range(..)

@join(delimiter) - из массива в строку. Используется функция PHP implode(..)

Специальные операторы:

@inc - вставить значение, представленное во внешнем файле или другом маркере текущего файла. Маркеры работают только с форматом Yaml. Поддерживается 5 форматов, которые распознаются по расширению файла: .php (1), .txt (2), .yml или .yaml (3), .json (4), любые другие (5) расширения файлов, трактуют файл, как bang-fomat. Номинально файл указывается в значении, а маркер в параметре оператора. Если скобок нет - включается весь файл, а не его часть. Адресация файлов может быть явной, когда ware не используется для адресации или относительной к файлам плана app. Примеры включений:

# файл не указан, а только маркер, значит имеется ввиду файл Coresky w2/__data.yaml
# адресация явная, так как код Coresky не принадлежит ни одному плану
- @inc(phpman) # ниже то-же самое
- @inc(phpman) $DIR_S/w2/__data.yaml

# если маркер начинается с точки, то файл "свой", т.е. текущий. Аналогично в Jet
# адресация явная, так как файл "свой", планы не имеют значения
- @inc(.inner)

# включить весь файл, адресация явная, планы не имеют значения, использована $SELF
+ @inc $SELF/../.env

# включить весь файл, адресация относительная, ware - текущий, fomat: bang
+ @inc ../.env

# включить весь файл, ware - задан явно: venus, адресация относительно плана app
+ @inc venus::composer.json

Для формата bang, после имени файла, можно через пробел указать нестандартный разделитель via1 через пробел:

env: @inc .env =
~
via1 delimiter changed to equals sign. Bang file format sample:
LOG_DIR=var/log
SESSION_TIME=3600
# strings will skipped when equals sign absent
LOCALE=en

@path(..) - альтернатива якорям-ссылкам. Копирует значение из уже построенной части массива, адрес указывается ключами объединенными точкой, например:

one:
  two: [1, 3]
three: @path(one.two.1) # this value will eq. to 3

@php - вставить PHP код, который можно написать в значении или параметре оператора, в скобках. Если этот оператор используется в JSON-нотации и в коде PHP есть запятые или другие символы, определяющие границы JSON-нотации, напишите код в скобках это исключит проблемы парсинга. Также код можно написать в $TAIL:

# yaml:
- string
- @php SomeClass::method([1, $var]) # no problem
- {a: @php(SomeClass::method([1, $var])), b: c} # no problem
# parsed to PHP cache:
return array(
  0 => 'string',
  1 => SomeClass::method([1, $var]),
  2 => array('a' => SomeClass::method([1, $var]), 'b' => 'c'),
)

@preflight - в компилированный Yaml, можно добавить PHP-preflight код. Если есть скобки, код будет выполняться в изолированной области видимости, которая реализуется с помощью Closure. Если скобок нет - изоляции нет. Нужно всегда использовать плюс:

# yaml:
+ @preflight($key, &$val) |
  return SomeClass::method($key, $val);
- @php OtherClass::method($__return)
# parsed to PHP cache:
$__return = call_user_func(function() use ($key, &$val) {
  return SomeClass::method($key, $val);
});

return array(
  0 => OtherClass::method($__return),
)

@eval - inline-custom-оператор. Код PHP оператора может быть в значении или параметре. Во втором случае, можно обработать значение этим кодом

@sql - выполнить SQL:

+ @sql(@select name, id from $_memory)
array(
  'ajax' => 3,
  . . .
  'init' => 12,
)

@ini_get - получение данных конфигурации PHP с помощью ini_get(..)

@deny - отменить каскадное выполнение

@each - если контекст ожидает скаляр, а на входе массив, можно использовать @each. Массив будет перебран с помощью PHP функции array_walk_recursive(..) для функции, которая будет выполняться после @each:

a: @eval("__$v")
  b: @each @csv( ) one two
#  b: @csv( ) one two <<<< error when @each absent
  c: [three, four] # OK
d: five
['a' => 
  ['b' => [0 => '__one', 1 => '__two'],
   'c' => [0 => '__three', 1 => '__four'],
  ],
 'd' => 'five', # not in cascade
]

Конфигурация Redis

Если план cache должен использовать Redis или другой драйвер, отличный от dc_file, то это нужно настроить в файле bootsrap.php, так как этот план будет использоваться для сохранения главного кэш-файла. Поместите в самый конец файла примерно такой код:

SKY::$plans['cache'] = ['path' => 'L/cache', 'driver' => 'redis', 'dsn' => 'localhost'];
SKY::$plans['gate'] = ['path' => 'L/gate', 'use' => 'cache'];
SKY::$plans['jet'] = ['path' => 'L/jet', 'use' => 'cache'];

Автоматическая рекомпиляция кэша

В режиме DEV, Coresky отслеживает время модификации файлов источников и если было изменение, делается рекомпиляция. Это касается компилированных файлов в планах Jet, Gate. Для статических файлов *.css и *.js изменяется индекс ключа в query string, который всегда передается для адресации таких файлов. Это принуждает браузер обновить кешируемые файлы. В режиме DEV, также отслеживается модификация файла config.yaml приложения. Кэш-файл sky_plan.php в плане cache, перекомпилируется автоматически при изменении config.yaml приложения. Однако аналогичные файлы продуктов не отслеживаются. Если вы изменили такой файл, необходимо удалить кэш руками, чтобы он заново создался:

> sky drop
> sky warm

На production, этот алгоритм не работает, время модификации файлов источников не отслеживается.

Призрачные SQL запросы

Если рассмотреть типичные, часто используемые алгоритмы работы с базами данных, то можно выделить один, который хорошо формализуется, а функциональная надстройка для него, позволит писать короткий и эффективный код приложений. В первую очередь, это касается конфигураций, которые хранятся в БД. Во вторых, представьте группу данных, которая может изменяться в различных местах веб-приложений и должна быть сохранена в БД. Чтобы исключить множественные UPDATE запросы, такие изменения можно сохранять в RAM и записать в БД один раз в конце обработки. В третьих, часто удобно работать с данными в БД как с переменными PHP, а чтение и сохранение таких данных в БД, может происходить автоматически.

Bang данные хранятся, в том числе, в ячейках БД типа TEXT:

var1 value1
var2 value2
...
varN valueN

При упаковке, символы переноса строк в значениях, заменяются на \r, \n, \t, \\ (только четыре символа). Представление данных удобно для чтения (изменения) программистом. Bang, как и упакованные другим способом данные, имеет следующие преимущества:

  • чтобы добавить или удалить переменную, не нужны дополнительные действия с таблицей, где хранятся такие данные (ALTER TABLE и т.п.), т.е. возможно flash-использование;
  • сохранение данных в одной ячейке, сохраняет сразу всю группу данных;
  • возможно исключение запроса обновления данных в БД, если данные не изменились, хотя алгоритмически присутствовала запись в переменную памяти SKY;

Весь функционал Ghost SQL находится в файле sky.php, смотрите: SKY::flush(..), SKY::ghost(..), SKY::__callStatic(..). Функционал Ghost SQL работает не только с памятью SKY, но и любыми другими колонками в таблицах. Функционал Ghost, в ядре CORESKY используется около 8 раз, в том числе для хранения системной конфигурации в БД.

Использование Ghost SQL

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

d - переменные для DEV-режима работы, хранится в var/mem/dev_vars.txt

s - системная конфигурация. Место хранения: memory.8

n - конфигурация для работы в консоле, реализация в классе Schedule. Место хранения: memory.9

a - конфигурация админ раздела. Место хранения: memory.10. Определяется в продукте (ware) RAS (Root-Admin Section) или альтернативных продуктах.

v - дополнительные свойства сессий посетителей. Место хранения: visitors.vmemo

u - дополнительные свойства авторизированных пользователей. Место хранения: users.umemo

i - используется в утилите SkyProject, см. class Install. Место хранения: memory.11

i,j - используется в утилите SkyLang, см. class Language. Код не используется совместно с SkyProject, поэтому возможно совмещение "i"

Замечание: планируется добавить возможность помещать данные Ghost в NO-SQL базы данных, например Redis, вместо реляционных.

Примеры использования:

# использование однобуквенного префикса:
echo $sky->s_online; # посетителей на сайте
$user->v_useragent = 'CORESKY'; # записать в сессию посетителя новый useragent

# то-же самое другим способом:
SKY::v('useragent', 'CORESKY');

Автоматическая запись в базу данных, происходит в SKY::shutdown(), если во время использования SKY::ghost(..), был передан шаблон SQL или Closure для обновления данных и, во время работы скрипта, был установлен внутренний флаг. Для функционала Ghost, используемого в ядре CORESKY, все необходимые действия выполнены. И для чтения-записи в БД, достаточно чтения-записи в(из) переменную PHP.

Для функционала Ghost SKY-приложений, можно использовать любые буквы латинского алфавита кроме d s n a v u, занятые ядром. Код DEV-продуктов, обычно блокирует код приложений, поэтому коллизий не будет и в таких продуктах, можно использовать все буквы SKY-приложений. Так как нагрузка DEV-продуктов минимальна, местом хранения следует выбирать файлы, расположенные в директориях этих продуктов. Код PROD-продуктов используется совместно с кодом приложения, поэтому нужно корректно интегрировать функционал Ghost в код приложений. В этом случае, во время установки продуктов, использующих Ghost, будет производиться проверка на отсутствие коллизий по имени буквы и месту хранения или выбор (настройка) буквы и места хранения. Такие продукты должны содержать класс, соответствующий имени продукта и статический метод ghost(..) в нём. Например, в продукте Ras имеется код, который используется в Root::_config():

<?php

class Ras
{
    static function ghost($form = false) {
        if ($form)
            return [];
        return ['a' => 10];
    }
. . .
}

Здесь "a" - занимаемая буква Ghost, 10 - ID ряда в таблице memory. Возможно, также передать форму в синтаксисе класса Form для настройки конфигурации. Она будет задействована в инструментах разработчика на странице Main / Config. На этой же странице, можно просмотреть всё текущее содержимое памяти.

Методы Ghost в классе SKY

Для чтения, записи и удаления переменных Ghost, используется магический метод SKY::__callStatic(..). В коде, в качестве имени метода используется буква - идентификатор Ghost. В примерах ниже, используется "s" - системная конфигурация:

SKY::s(string, scalar) - сохранить одну пару ключ-значение (bang)

SKY::s(array) - сохранить много пар ключ-значение (bang)

SKY::s(string) - вернуть одно значение (bang)

SKY::s(string, null) - удалить из БД одну пару (bang)

SKY::s(null, array) - провести запись в другие колонки (кроме памяти SKY), тип записи 2

SKY::s(null, string) - удалить одну колонку, тип записи 2

SKY::s() - вернуть true/false, - существует ли Ghost с данной буквой (в данном случае "s")

SKY::ghost($char, $original, $tpl = '', $flag = 0) - активация функционала призрачных SQL. Параметры метода:

$char - один символ (буква) для нового использования Ghost

$original - упакованная bang-строка, которая была считана из БД запросом SELECT. Также вариант использования: пустая строка.

$tpl - шаблон SQL или Closure для обновления данных в БД или файле. Если передать пустую строку, такое обновление не будет выполнено. Для функционала Ghost, в котором используется только лишь bang, необходимо передавать шаблон для sqlf(..), но когда используется второй тип записи, - шаблон для sql(..). Если был передан Closure, то callback функция будет вызвана, когда нужно обновить данные:

# Init DEV variables from file
$vars = Plan::mem_gq('dev_vars.txt');
SKY::ghost('d', $vars, fn($s) => Plan::mem_p(['main', 'dev_vars.txt'], $s));

SKY::flush(char, bool $return) - этот метод вызывается из SKY::shutdown() для всех случаев использования призрачных SQL и может выполнять реальное обновление данных в БД. Иногда этот метод полезен и в коде приложений. Первый параметр - буква (идентификатор) частного случая Ghost. Второй параметр - булева переменная. Если true - запрос обновления не будет выполнен, но будет возвращен этим методом.

Shutdown в коде приложений

Даже когда происходят фатальные ошибки приложений, выполнение скрипта продолжается в SKY::shutdown(), поэтому запись в БД, для функционала Ghost, будет произведена. Если в коде приложений, необходимо использование функции PHP register_shutdown_function(..), необходима уверенность, что выполнение Shutdown-кода приложения, будет происходить перед SKY::shutdown(). Поэтому вместо использования функции PHP, это необходимо кодировать следующим образом: $sky->shutdown[] = [$this, 'shutdown'];. Необходимо помнить, что фатальная ошибка, произошедшая во время выполнения функции shutdown(), полностью остановит выполнение скрипта и обновление данных для Ghost SQL может не произойти. Нужно либо не использовать shutdown() в коде приложений, либо тщательно проверять код пользовательской shutdown функции.

Класс XML

С помощью класса Coresky XML, возможно манипулирование XML/HTML данными, наподобие как в браузерах, с помощью Javascript DOMParser.parseFromString(..). Во время парсинга, может теряться информация о пробельных символах и кавычках, только в части перечислений атрибутов тегов, что для сути XML не имеет значения. Но tokenizer класса XML не теряет никакую информацию и используется, в том числе, для подсветки синтаксиса PHP/XML/HTML данных в классе Display. XML строка преобразовывается в связанную иерархическую структуру объектов типа stdClass, где каждый объект - один узел (node). Текстовые узлы, состоящие только из пробельных символов сохраняются. Такая архитектура позволяет делать любые манипуляции с XML, в том числе переформатирование, как это умеет делать tidy:

$xml = new XML('<div id=test>hello</div>');
$xml->pad = '  '; # base indent for beautifier
$id = $xml->byId('test'); # like .getElementById(..)
$id->inner('<b>word!</b>'); # like .innerHTML
echo $xml;
# output:
<div id="test">
  <b>word!</b>
</div>
# Example2:
$xml = new XML(file_get_contents('big.html'));
$q = $xml->query('div.clsname li a:last');
$q->inner(html('<b>word!</b>')); # like .innerTEXT
echo $xml;

Класс не сигнализирует об ошибках XML, подобно тому как браузеры пытаются визуализировать любой HTML, в том числе тот, который содержит ошибки и неточности. Класс XML имеет важные свойства:

  • ->in - строка, представляющая XML. Источник данных для ->parse(..) и приемник для ->__toString()

  • ->root - корневой узел, связанной иерархической структуры. Здесь, источник-приемник наоборот

  • ->selected - массив, выбранных узлов селекторами

Методы класса:

  • конструктор - инициализирует класс и подготавливает переменные

  • ->tokens(..) - генератор выдает токены, разбивая текстовый XML на входе

  • ->parse(..) - парсер конвертирует токены в связанные объекты PHP

  • ->__toString() - обратное действие: объекты PHP преобразовывается в строку XML

  • ->clone(..) - клонирует выбранный узел (с дочерней иерархией), с целью вставки в новое место

  • ->walk(..) - метод для стандартного прохода (сверху вниз, слева направо) по иерархической структуре, используется для разных целей

  • ->byId(..) - селектор подобно .getElementById( ) JS

  • ->byTag(..) - селектор подобно .getElementsByTagName( ) JS

  • ->byClass(..) - селектор подобно .getElementsByClassName( ) JS

  • ->query(..) - селектор подобно .querySelector( ) и .querySelectorAll( ) JS

  • ->inner(..) - заменяет внутреннюю часть, подобно .innerHTML. Для текстовой версии, необходимо использовать функцию Coresky html(..)

  • ->outer(..) - заменяет внешнюю часть, как .outerHTML

  • ->remove(..) - удаляет узлы вместе с дочерней иерархией

⚠️ **GitHub.com Fallback** ⚠️