Мы используем файлы cookie для улучшения работы сайта. Вы можете принять все cookies или настроить предпочтения. Подробнее в нашей Политике использования cookie-файлов.
Архитектура платформы
Обзор
Wepps - модульная PHP-платформа для создания веб-сайтов и REST API. Основана на принципах:
- Модульность через систему расширений
- Единая точка входа (Front Controller)
- Шаблонизация через Smarty
- Маршрутизация через Navigator
- ORM-подобный доступ к данным
Структура проекта
wepps/
├── index.php # Точка входа для frontend
├── config.php # Конфигурация (не в git)
├── install.php # Скрипт установки
├── _wepps/ # Админка (отдельная точка входа)
├── packages/ # Основной код платформы
│ ├── WeppsCore/ # Ядро системы
│ ├── WeppsAdmin/ # Административная панель
│ ├── WeppsExtensions/ # Расширения функционала
│ ├── vendor/ # Зависимости Composer
│ └── vendor_local/ # Сторонние библиотеки (не из Composer)
├── files/ # Загруженные файлы
│ ├── lists/ # Файлы по таблицам данных
│ └── tpl/ # Smarty кэш и компиляция
└── pic/ # Изображения (оригиналы и ресайзы)
Основные компоненты
1. WeppsCore - Ядро системы
Базовые классы и функциональность (описаны ключевые, полный список см. в packages/WeppsCore/):
Connect.php
Центральный класс для подключения к БД, сервисам и хранения конфигурации:
PDO и обертка:
Connect::$db // Прямой доступ к PDO
Connect::$instance // Обертка с удобными методами:
// - fetch() - выборка с кэшированием
// - query() - выполнение запроса
// - insert() - вставка с auto-Priority
// - prepare() - преобразование данных
// - cached() - управление кэшированием
Конфигурация из config.php:
Connect::$projectInfo // Info секция (название, версия и т.д.)
Connect::$projectDev // Dev секция (debug, http, adminUrl и т.д.)
Connect::$projectDB // DB секция (host, port, dbname, user и т.д.)
Connect::$projectServices // Services секция (memcached, wepps и т.д.)
Connect::$projectData // Временные данные (инициализируется как [])
Пример использования конфигурации:
// Проверка режима отладки
if (Connect::$projectDev['debug'] == 1) {
// Детальное логирование
}
// Получение Email разработчика проекта
$adminEmail = Connect::$projectDev['email'];
// Настройки Memcached
$memcachedHost = Connect::$projectServices['memcached']['host'];
$memcachedActive = Connect::$projectServices['memcached']['active'];
Navigator.php
Управление навигацией и маршрутизацией:
$navigator->path // Текущий раздел (объект)
Navigator::$pathItem // Раздел сайта/ITEM.html (статическое свойство)
$navigator->content // Данные раздела из s_Navigator
$navigator->parent // Подразделы родительского уровня
$navigator->child // Подразделы текущего раздела
$navigator->way // Путь до текущего раздела (хлебные крошки)
$navigator->nav // Навигация верхнего уровня
Extension.php
Базовый класс для всех расширений:
abstract class Extension {
// Абстрактный метод (обязателен к реализации)
abstract public function request(); // Обработка запроса расширения
// Основные свойства
public $navigator; // Навигатор (Navigator)
public $headers; // Управление CSS/JS (TemplateHeaders)
public $get; // Входные данные (массив)
public $page; // Текущая страница пагинации
public $rand; // Случайное число для версионирования
public $tpl; // Наименование шаблона
public $targetTpl; // Содержание шаблона (по умолчанию 'extension')
public $extensionData; // Флаги для Template (например, ['element'=>1]
// указывает, что обработана детальная страница)
// Вспомогательный метод
public function getItem($tableName, $condition = ''); // Получить элемент для детальной страницы
}
Data.php
ORM-подобная работа с таблицами БД:
$data = new Data('Products');
// Основные методы выборки
$items = $data->fetchmini($conditions, $onPage, $currentPage, $orderBy); // Простая выборка
$items = $data->fetch($conditions, $onPage, $currentPage, $orderBy); // С JOIN по схеме полей
// CRUD операции
$id = $data->add($row, $insertOnly); // Добавление записи
$data->set($id, $row, $settings); // Обновление записи
$data->remove($id); // Удаление записи
// Настройки запроса
$data->setFields('Id,Name,Price'); // Выбрать только определённые поля
$data->setConcat('CONCAT(Name, Price) total'); // Дополнительные поля
$data->setJoin('LEFT JOIN Brands...'); // Кастомные JOIN
$data->setParams([1, 'active']); // Параметры для prepared statements
$data->setGroup('t.CategoryId'); // GROUP BY
$data->setHaving('COUNT(*) > 5'); // HAVING
// Свойства после выборки
$data->count; // Количество записей
$data->paginator; // Данные пагинации
$data->sql; // Сформированный SQL (для отладки)
Users.php
Управление пользователями и аутентификацией:
$users = new Users(['login' => $login, 'password' => $password]);
// Авторизация пользователя
if ($users->signIn()) {
// Успешный вход, JWT токен установлен в cookie
} else {
$errors = $users->errors(); // ['login' => '...'] или ['password' => '...']
}
// Проверка аутентификации (из JWT токена в cookie или Bearer заголовке)
if ($users->getAuth()) {
// Пользователь авторизован
$currentUser = Connect::$projectData['user']; // Данные пользователя из s_Users
}
// Выход из системы (удаление токена)
$users->removeAuth();
// Генерация случайного пароля
$newPassword = $users->password(); // Например: "aT5k-2!mN"
2. WeppsAdmin - Административная панель
Модули администрирования:
- Admin/ - Главная страница админки
- Lists/ - Управление списками данных
- NavigatorAd/ - Управление навигацией
- ConfigExtensions/ - Системные расширения
- Updates/ - Система обновлений
3. WeppsExtensions - Расширения
Функциональные модули:
- Template/ - Базовые шаблоны и структура
- Products/ - Каталог товаров
- Cart/ - Корзина покупок
- News/ - Новости и статьи
- Gallery/ - Галерея изображений
- Profile/ - Личный кабинет
- Addons/ - Кастомные расширения для frontend
Жизненный цикл запроса
Frontend запрос
1. index.php
↓
2. configloader.php → загружает config.php
↓
3. Connect → подключение к БД и сервисам
↓
4. Navigator → определение текущего раздела из URL
↓
5. Template Extension → инициализация структуры страницы
↓
6. Content Extension → загрузка расширения раздела (Products, News и т.д.)
↓
7. Extension->request() → обработка бизнес-логики, установка $this->tpl
↓
8. Template → сборка финальной страницы (header, content, footer)
↓
9. Smarty → рендеринг собранного шаблона с данными
↓
10. HTML ответ
Admin запрос
1. _wepps/index.php
↓
2. configloader.php → загружает config.php (Memcached отключен для админки)
↓
3. Admin класс → инициализация, получение URL
↓
4. Проверка авторизации → Connect::$projectData['user']['ShowAdmin']
↓
5. Определение модуля по URL → Home/Lists/NavigatorAd/ConfigExtensions
↓
6. Подключение библиотек → jQuery, Select2, Bootstrap Icons
↓
7. Загрузка модуля → new WeppsAdmin\{Module}\{Module}
↓
8. Обработка AJAX/POST запросов в модуле
↓
9. Smarty → рендеринг Admin.tpl с данными модуля
↓
10. JSON/HTML ответ
Паттерны и соглашения
1. Наследование Extension
Все расширения наследуются от WeppsCore\Extension для:
- Доступа к базовым методам -
getItem()для детальных страниц - Доступа к свойствам -
$navigator,$headers,$get,$page - Возможности переопределения - метод
getItem()можно перегрузить для кастомной логики - Единообразия архитектуры - все расширения работают по одному паттерну
namespace WeppsExtensions\Products;
use WeppsCore\Extension;
class Products extends Extension {
public function request() {
// Для детальной страницы (если есть /catalog/item.html)
if (Navigator::$pathItem != '') {
$product = $this->getItem('Products', 't.IsActive=1');
$smarty = Smarty::getSmarty();
$smarty->assign('product', $product);
$this->tpl = 'packages/WeppsExtensions/Products/ProductsItem.tpl';
return;
}
// Для списка товаров
$data = new Data('Products');
$products = $data->fetch('t.IsActive=1', 20, $this->page, 't.Priority DESC');
// Получение Smarty и передача данных
$smarty = Smarty::getSmarty();
$smarty->assign('products', $products);
$smarty->assign('paginator', $data->paginator);
// Подключение стилей/скриптов (короткий путь /ext/ + версионирование)
$this->headers->css("/ext/Products/Products.{$this->rand}.css");
$this->headers->js("/ext/Products/Products.{$this->rand}.js");
// Установка шаблона (полный путь от корня)
$this->tpl = 'packages/WeppsExtensions/Products/Products.tpl';
}
}
2. Структура файлов расширения
MyExtension/
├── MyExtension.php # Основной класс с логикой
├── Request.php # AJAX-обработчик (опционально, для асинхронных запросов)
├── MyExtension.tpl # Smarty шаблон для списка
├── MyExtensionItem.tpl # Smarty шаблон для детальной страницы (опционально)
├── MyExtension.css # Стили
├── MyExtension.js # JavaScript
├── RequestFilters.tpl # Шаблон для AJAX-ответа (если есть Request.php)
├── RequestFilters.tpl.css # Стили для AJAX-шаблона
├── RequestFilters.tpl.js # JS для AJAX-шаблона
└── files/ # Статические файлы (изображения, документы)
Соглашения по именованию:
- Основные файлы - совпадают с именем класса:
MyExtension.php,MyExtension.tpl,MyExtension.css - AJAX-шаблоны - префикс
Request*:RequestMyFilters.tplдля асинхронной подгрузки
3. Guard Clause для обработки ошибок
Используйте ранний return для валидации:
// ❌ Плохо (глубокая вложенность)
if ($user) {
if ($permission) {
// много кода
} else {
return ['status' => 403];
}
} else {
return ['status' => 401];
}
// ✅ Хорошо (guard clause)
if (!$user) {
return ['status' => 401, 'message' => 'Unauthorized'];
}
if (!$permission) {
return ['status' => 403, 'message' => 'Forbidden'];
}
// основной код
4. Работа с базой данных
Через PDO (прямые запросы):
use WeppsCore\Connect;
// SELECT через PDO
$sth = Connect::$db->prepare("SELECT * FROM Products WHERE Id = ?");
$sth->execute([$id]);
$product = $sth->fetch(\PDO::FETCH_ASSOC);
// INSERT через PDO
$sth = Connect::$db->prepare("INSERT INTO Products (Name, Price) VALUES (?, ?)");
$sth->execute([$name, $price]);
$newId = Connect::$db->lastInsertId();
Через Connect::$instance (обертка с удобными методами):
use WeppsCore\Connect;
// SELECT с автоматическим кэшированием (join-запросы)
$products = Connect::$instance->fetch(
"SELECT p.*, b.Name as BrandName FROM Products p LEFT JOIN Brands b ON p.BrandId = b.Id WHERE p.IsActive = ?",
[1]
);
// INSERT с автоматическим Priority
$id = Connect::$instance->insert('Products', [
'Name' => 'Новый товар',
'Price' => 1500,
'IsActive' => 1
]);
// UPDATE через query()
$prepare = Connect::$instance->prepare([
'Name' => 'Обновленное название',
'Price' => 2000
]);
Connect::$instance->query(
"UPDATE Products SET {$prepare['update']} WHERE Id = ?",
array_merge($prepare['row'], [$id])
);
Через класс Data (ORM-подобный):
Важно: Класс Data работает на основе метаданных из системных таблиц:
s_Config- описание таблиц БДs_ConfigFields- описание полей таблиц (типы, связи, валидация)
Метод fetch() автоматически строит JOIN-запросы на основе типов полей (select, remote, file), определённых в s_ConfigFields. Использовать setJoin() нужно только для кастомных связей, не описанных в метаданных.
Кэширование: Если Memcached включен, все запросы с JOIN автоматически кэшируются (ключ = md5 от SQL + параметров).
use WeppsCore\Data;
$data = new Data('Products');
// Простая выборка (без автоматических JOIN)
$products = $data->fetchmini("IsActive=1 AND Price>100", 20, 1, "Priority DESC");
// Выборка с JOIN по схеме полей (автоматически подтягивает связанные таблицы и файлы)
$products = $data->fetch("t.IsActive=1", 20, 1, "t.Priority DESC");
// Настройка запроса
$data->setFields('Id,Name,Price'); // Выбрать только нужные поля
$data->setJoin('LEFT JOIN Brands b ON b.Id = t.BrandId');
$products = $data->fetch("t.IsActive=1");
// Добавление записи
$id = $data->add([
'Name' => 'Новый товар',
'Price' => 1500,
'IsActive' => 1
]);
// Обновление записи
$data->set($id, [
'Name' => 'Обновленное название',
'Price' => 2000
]);
// Удаление записи (и связанных файлов)
$data->remove($id);
// Пагинация (доступна после fetch/fetchmini)
$paginator = $data->paginator; // ['current' => 1, 'pages' => [1,2,3], 'next' => 2, ...]
$totalCount = $data->count; // Общее количество записей
5. Шаблонизация Smarty
{* Переменные *}
{$product.Name}
{$product.Price|number_format:0:',':' '} ₽
{* Циклы *}
{foreach $products as $product}
<div class="product">
<h3>{$product.Name}</h3>
<p>{$product.Price} ₽</p>
</div>
{/foreach}
{* Условия *}
{if $user}
Привет, {$user.Login}!
{else}
<a href="/login">Войти</a>
{/if}
База данных
Системные таблицы (префикс s_)
- s_Navigator - структура разделов сайта
- s_Extensions - зарегистрированные расширения
- s_ConfigExtensions - системные расширения админки
- s_Config - конфигурация таблиц данных
- s_ConfigFields - настройки полей таблиц
- s_Users - пользователи системы
- s_Files - файловое хранилище
Контентные таблицы
- Products - товары
- News - новости
- Gallery - галерея
- Contacts - контакты
- Brands - бренды
- и другие (создаются через админку)
Кэширование
Memcached
Конфигурация в config.php:
'Services' => [
'memcached' => [
'active' => true, // Включить/выключить кэширование
'host' => 'localhost',
'port' => 11211,
'expire' => 3600 // Время жизни кэша по умолчанию (секунды)
]
]
Автоматическое кэширование:
use WeppsCore\Connect;
// fetch() автоматически кэширует join-запросы (если memcached активен)
$products = Connect::$instance->fetch(
"SELECT p.*, b.Name FROM Products p LEFT JOIN Brands b ON p.BrandId = b.Id"
);
// Первый вызов - из БД, сохраняется в memcached
// Последующие - из кэша (ключ = md5(sql + params))
Управление кэшированием:
// Отключить кэширование для конкретных запросов
Connect::$instance->cached('no');
$data = Connect::$instance->fetch("SELECT * FROM Products");
// Включить обратно
Connect::$instance->cached('yes');
// Автоматический режим (из конфига)
Connect::$instance->cached('auto');
Прямой доступ к Memcached (через внутреннее свойство):
// Класс WeppsCore\Memcached предоставляет методы:
// - set($key, $value, $expire)
// - get($key)
// - delete($key)
// Доступен внутри Connect::$instance->memcached (приватное свойство)
Smarty кэш
Автоматический кэш шаблонов в files/tpl/compile/
Безопасность
Аутентификация
- JWT токены для API
- Session + cookies для веб-интерфейса
- Хеширование паролей через
password_hash()
Валидация данных
use WeppsCore\Validator;
// Все методы статические, возвращают '' при успехе или сообщение об ошибке
$errors = [];
// Проверка на непустоту
$errors['name'] = Validator::isNotEmpty($_POST['name'], 'Имя обязательно');
// Проверка email
$errors['email'] = Validator::isEmail($_POST['email'], 'Некорректный email');
// Проверка URL
$errors['site'] = Validator::isUrl($_POST['site'], 'Некорректный URL');
// Проверка телефона (10 цифр)
$errors['phone'] = Validator::isPhone($_POST['phone'], 'Телефон должен содержать 10 цифр');
// Проверка целого числа
$errors['age'] = Validator::isInt2($_POST['age'], 'Возраст должен быть числом');
// Проверка даты
$errors['date'] = Validator::isDate($_POST['date'], 'Некорректная дата');
// Другие методы: isFloat(), isString(), isLat(), isGuid(), isBarcode()
// Фильтрация ошибок (оставить только непустые)
$errors = array_filter($errors);
if (!empty($errors)) {
// Есть ошибки валидации
}
Подготовленные запросы
Всегда используйте prepared statements для защиты от SQL-инъекций:
// ✅ Безопасно
$sth = Connect::$db->prepare("SELECT * FROM Users WHERE Login = ?");
$sth->execute([$login]);
// ❌ Опасно!
$query = "SELECT * FROM Users WHERE Login = '$login'";
Расширение функциональности
Создание нового расширения
Через админку (рекомендуемый способ):
- Перейти в админку → список "Расширения" (
s_Extensions) - Создать новую запись с параметрами:
- Название - имя расширения (например,
MyExtension) - Создать файлы расширения - выбрать шаблон структуры:
1.0- простая структура (копируется из_Example10/)1.1- структура для списков (копируется из_Example11/)
- Название - имя расширения (например,
- При сохранении записи автоматически срабатывает триггер, который:
- Создает папку
WeppsExtensions/MyExtension/ - Копирует файлы из выбранного шаблона (
_Example10или_Example11) - Переименовывает классы и файлы под указанное название
- Создает папку
- Привязать расширение к разделу через
s_Navigator(полеExtension)
Шаблоны структуры:
_Example10- минималистичная структура (основной класс + шаблон)_Example11- расширенная структура для работы со списками (Data, CRUD, фильтры)
Подробнее: Разработка расширений
AI-Ready архитектура
Wepps Platform спроектирована с учётом работы с AI-инструментами (GitHub Copilot, ChatGPT, Claude и др.):
Почему платформа AI-ready?
-
Структурированная кодовая база
- Чёткие соглашения по именованию (
MyExtension.php,MyExtension.tpl,MyExtension.js) - Предсказуемые паттерны (наследование от
Extension, методrequest()) - Организованная структура папок
- Чёткие соглашения по именованию (
-
Полная документация в Markdown
- Все аспекты платформы задокументированы в
.docs/ - Примеры кода для типовых задач
- API reference с описанием всех методов
- Все аспекты платформы задокументированы в
-
Инструкции для AI-агентов
- Файл
.github/copilot-instructions.mdс описанием архитектуры, паттернов и best practices - Контекст для GitHub Copilot, Cursor AI и других инструментов
- Правила генерации кода в стиле платформы
- Файл
-
Унифицированная обработка запросов
- Стандартизированные AJAX-эндпоинты через
Request.php - Гибкий формат ответов: JSON для данных, HTML для шаблонов (через Smarty)
- Единый формат обработки действий (
actionпараметр)
- Стандартизированные AJAX-эндпоинты через
-
Типизированная схема БД
- Чёткие типы полей в
s_ConfigFields - Автоматическая валидация данных
- Предсказуемая структура таблиц
- Чёткие типы полей в
Преимущества для разработчика
- Быстрая генерация кода - AI понимает паттерны и может создавать новые расширения по аналогии
- Автодополнение контекста - AI предлагает правильные классы, методы и параметры
- Автоматический рефакторинг - AI может предложить улучшения с учётом best practices платформы
- Ускорение разработки - меньше ручного кода, больше фокуса на бизнес-логику
Пример работы с AI
AI может:
- Создать новое расширение на основе
_Example11 - Сгенерировать CRUD-операции для таблицы
- Написать валидацию данных по схеме
- Создать Request.php с обработкой всех actions
- Предложить оптимизации SQL-запросов
Благодаря чёткой архитектуре и документации, AI становится эффективным помощником в разработке на Wepps Platform.