CSpeed представляет собой открытый C-языковой PHP-расширение, созданное специально для удобства разработчиков при работе над проектами. Это расширение упрощает работу с веб-приложениями и API. Разработчики могут просто скачать исходный код; в корневой директории есть папка "demo", которая позволяет быстро начать работу с этим фреймворком.
CSpeed разработана на основе PHP7.x, требования к окружению:
1. Linux/macOSX
2. PHP7.x
Новая версия v2.1.13 тестового варианта распределённой базы данных. Для использования требуется выгрузить исходный код ветки распределённой базы данных.
Сейчас уже имеется первоначальная модель:
Использует Bison & Flex для лексического и синтаксического анализа
Основа использует PDO для работы с базами данных
Не поддерживаемые возможности [будут добавлены в последующих версиях]:
1. SELECT запросы не поддерживают поиск по шаблону следующим образом:
SELECT *
WHERE column LIKE '%pattern%'
Если вам необходим поиск по шаблону, используйте заменяющий синтаксис:
SELECT tablename.*
2. Неподдерживаемый SELECT вложенный запрос, как:
SELECT name, age FROM (SELECT supjos.* FROM supjos) AS tmp
3. Неподдерживаемый массовый INSERT INTO
4. Неподдерживаемый SELECT запрос с альтернативным названием таблицы AS
```Поддерживаемые возможности:
```php
1. SELECT запросы
2. INSERT вставка
3. UPDATE обновление
4. DELETE удаление
При выполнении операций INSERT | UPDATE | DELETE обязательно указывать поле shardingKey, указанное в конфигурационном файле, иначе движок сообщит об ошибке:
INSERT операция:
SQL: 1064 'INSERT' команда должна содержать shardingKey..
DELETE операция:
DELETE команда должна содержать `shardingKey` условие WHERE.
UPDATE операция:
UPDATE команда должна содержать `shardingKey` условие WHERE.
Примеры:
// Инициализация модели распределённой базы данных
$adapter = new DbAdapter();
// Загрузка конфигурационного файла
// Конфигурация партиционирования таблиц и баз данных
$adapter->loadConfig([
/** Название партиционируемых таблиц */
'tables' => [
'supjos',
'blog'
],
/** Поле партиционирования */
'shardingKey' => 'id',
/** Размер горизонтального партиционирования */
'hSize' => 2,
/** Адаптеры баз данных для чтения/записи */
'readDb' => [
'db',
'db2'
],
/** Адаптеры баз данных для записи */
'writeDb' => [
'db2'
],
/** 1. Чтение/запись. 2. Все операции используют readDb. 3. Все операции используют writeDb. */
'balance' => 1,
/* Отображать ли ошибку, если адаптер базы данных отсутствует */
'displayDbNotExistError' => true
]);
$adapter->createCommand(
"SELECT supjos.id, supjos.name, supjos.age AS sage, blog.name AS bname FROM supjos, blog WHERE blog.id IN(1,2)"
);
```## Вывод результатов SQL ##
```php
print_r($adapter->execute());
CSpeed расширение доступно как на платформе GitHub, так и на платформе Gitee, пользователи могут скачать исходный код и установить его, следуя приведённым ниже инструкциям:
GitHub:
https://github.com/liqiongfan/cspeed
Gitee:
https://gitee.com/josinli/cspeed
Шаги установки:
1. /usr/local/php_to_path/bin/phpize
2. ./configure --with-php-config=/usr/local/php_to_path/bin/php-config
3. make install
После компиляции в конфигурационный файл php.ini
следует добавить следующие строки:
extension_dir = "/usr/local/php-7.1.8-nts/lib/php/extensions/no-debug-non-zts-20160303/"
extension=cspeed.so
Затем перезапустите сервис PHP-FPM
или Apache
:
4. systemctl restart php-fpm или systemctl restart httpd
По умолчанию CSpeed получает информацию о маршруте из параметров запроса сервера PATH-INFO и выполняет её парсинг. Однако пользователи также могут настроить получение информации о маршруте из URL с помощью параметров GET. Разработчики могут выполнить это через конфигурацию файла ini, как показано ниже:
core.path.info.symbol = __url ; Имя параметра GET для получения URL маршрута
core.path.info.mode = get ; Режим (PATH, PATH-INFO, GET, AUTO)
``````core.path.info.symbol``` используется для указания имени параметра GET, который будет использоваться для получения URL маршрута. Если настроен режим использования **URL** для получения параметров, пользователи могут использовать произвольные имена URL-маршрутов для выполнения парсинга маршрута. По умолчанию значение равно **__csurl**, но пользователи могут настроить этот параметр для гибкого управления конфигурацией фреймворка.```core.path.info.mode``` определяет режим парсинга маршрута **CSpeed**. По умолчанию используется режим **PATH-INFO**, но пользователи могут выбрать один из вариантов **GET, AUTO, PATH**. Для режима **GET** требуется настройка параметра ```core.path.info.symbol```. При выборе режима **AUTO** **CSpeed** автоматически выбирает между двумя режимами для парсинга маршрута. Если оба режима **PATH-INFO** и **URL** содержат информацию о маршруте, то приоритет отдается режиму **PATH-INFO**.
Ниже представлены примеры конфигураций **Nginx** для двух различных режимов:
**PATH-INFO**
```ini
location / {
if (!-e $request_filename) {
rewrite ^/(.*)$ /index.php/$1 last;
break;
}
}
локация ~ .php {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
**URL (CSpeed системой по умолчанию используется __csurl режим)**
```ini
локация / {
rewrite ^/(.*)$ /index.php?__csurl=/$1 last;
}
локация ~ \.php {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
По сравнению с PATH-INFO режимом, конфигурационные опции конфигурационного файла nginx в URL режиме становятся более простыми. Пользователи могут использовать этот режим, установив core.path.info.symbol
как _url
(по умолчанию в системе CSPEED это значение равно __csurl
) для совместимости с маршрутизацией фреймворка Phalcon.
location / {
rewrite ^/(.*)$ /index.php?__csurl=/$1 last;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
```## Основные характеристики ##
### 1. Поддержка JSON-RPC распределённой системы ###
**Код серверной части:**
```php
namespace app\modules\rpc\controllers;
class Goods extends \Cs\rpc\Server
{
function initialise()
{
// Обработка функций, связанных с обработкой запросов RPC
$this->handle($this);
}
// ----- Все внешние методы RPC
/**
* Каждый метод RPC принимает один параметр ($params) для получения данных от клиента.
* $params - массив, каждый элемент которого представляет собой параметр запроса.
* Чтобы вернуть данные клиенту, достаточно просто вернуть необходимую информацию.
*/
function listsRpc($params)
{
return ['версия' => '2.1.11', 'ядро' => 'CSpeed'];
}
/**
* Добавление товара
*/
function addRpc($params)
{
// TODO..
}
/**
* Удаление товара
*/
function deleteRpc($params)
{
// TODO...
}
// и так далее
}
Клиентская часть кода:
// Привязка адреса сервера RPC, обратите внимание, что адрес должен быть действительным и содержать префикс http или https
$goodsServer = new \Cs\rpc\Client("http://www.xxx.com/index/goods");
// $lists - это данные, полученные от сервера в соответствии с протоколом RPC, разработчики должны самостоятельно парсить эти данные, как показано ниже:
// {"jsonrpc":"2.0","id":1,"result":{"version":"2.1.11","name":"CSpeed"}}
$lists = $goodsServer->lists();
Передача одного параметра:```php
$goodsServer = new \Cs\rpc\Client("http://www.xxx.com/index/goods"); $lists = $goodsServer->lists(['id' => 11, 'category' => 'fruit']);
Внутри метода серверной части параметром является массив с указанными ниже переменными:
```php
namespace app\modules\rpc\controllers;
class Goods extends \Cs\rpc\Server
{
function initialise()
{
// Функция handle используется для привязки обработчика RPC к серверной части
$this->handle($this);
}
/**
* Параметр $params представляет собой массив следующего вида:
* $params = [
* [
* 'id' => 11,
* 'category' => 'фрукт'
* ]
* ]
*/
function listsRpc($params)
{
}
}
```
**Также можно передать несколько параметров:**
```php
$goodsServer = new \Cs\rpc\Client("http://www.xxx.com/index/goods");
$lists = $goodsServer->lists(11, 'cspeed');
```
Параметром внутри метода серверной части являются массив с указанными ниже переменными:
```php
namespace app\modules\rpc\controllers;
class Goods extends \Cs\rpc\Server
{
function initialise()
{
// Функция handle используется для привязки обработчика RPC к серверной части
$this->handle($this);
}
/**
* Параметр $params представляет собой массив следующего вида:
* $params = [
* 11,
* 'cspeed'
* ]
*/
function listsRpc($params)
{
}
}
```
### 2. Децентрализация фабрики: Cs\ObjectFactory ###
Когда система становится всё больше и сложнее, изменения даже небольшого функционала могут требовать множества правок или рефакторинга кода. В этом случае использование **файловой декомпозиции** может значительно упростить задачу, так как все последующие действия будут выполняться автоматически системой **CSpeed**, что делает процесс удобным и быстрым.
```Класс `Cs\ObjectFactory` имеет всего два метода:
```php
function __construct($factoryFilePath)
{
}
// $key представляет ключи rsa и aes в конфигурационном файле config.php
function getObject($key)
{
}
```
Конфигурация файла декомпозиции системы **CSpeed** представлена ниже [ **config.php** ]:
```php
``````markdown
возврат [
'rsa' => [
'класс' => '\app\models\User',
'параметры' => [
"привет", "CSpeed", "v2.1.11"
],
'свойства' => [
'имя' => '\app\models\Tools',
'два' => '\app\models\Tools'
],
'значения' => [
'abc' => new stdClass(),
'linux' => 'Linux',
'windows' => 'Windows',
'macos' => 'macOSX'
],
'атрибуты' => [
'частный' => false,
]
],
'aes' => [
'класс' => 'Cs\tool\Config',
'свойства' => [
'данные' => 'app\models\User'
]
]
];
```
**Пользователю требуется в конфигурационном файле вернуть массив в определённом формате**. Формат указан ниже:
Каждый элемент массива представляет собой **ассоциативную пару ключ-значение**, где ключ соответствует ID представления класса, а значение — это массив, который указывает, как следует инициализировать данный класс в CSpeed. Формат этого массива следующий:```php
'class' => 'класс объекта, полное имя класса с пространством имен'
'params' => 'параметры конструктора'
'properties' => 'свойства, используемые для внедрения зависимостей через методы setter, представлены в виде массива, каждый элемент которого соответствует одному методу setter.'
'values' => 'значения свойств, используемых при инициализации объекта класса'
'attrs' => 'тип свойств объекта класса, которые будут инициализированы, допустимые значения: private, protected, public'
```**Массив params**
```php
'params' => [
"hello", "CSpeed", "v2.1.11"
]
```
**Каждый элемент массива params представляет собой фактический параметр конструктора для соответствующего класса.**
**Массив properties**
```php
'properties' => [
'name' => '\app\models\Tools',
'two' => '\app\models\Tools'
]
```
**Каждый элемент массива properties соответствует одному методу setter, имя которого совпадает с ключом элемента, а значение — это полное имя класса, которое будет передано методу setter.**
```php
function setName(new \app\models\Tools());
function setTwo(new \app\models\Tools());
```
**Массив attrs**
Определяет тип свойств объекта класса, которые будут инициализированы.
```php
'attrs' => [
'private' => false,
]
```
Это означает, что свойство `private` объекта класса не будет инициализировано специальным значением (что не влияет на внутренние private свойства благодаря принципу инкапсуляции ООП), в то время как свойства `protected` и `public` будут инициализированы специальными значениями.
### 3. Механизм событий наблюдателей Cs\tool\Component ###
При разработке системы или модуля со множеством функциональностей может потребоваться обработка многих одинаковых бизнес-логик. Обычно такие задачи решаются следующими способами:
**1. Использование наследования, где родительский класс выполняет основную логику**
```php
class Common
{
function __construct()
{
// TODO...
}
}
class User extends Common
{
function __construct()
{
parent::__construct();
}
}
``````markdown
## 2. Использование механизма событий
```php
class Test
{
function __construct()
{
$this->on('before', function($obj) {
// TODO...
});
}
}
```
Фреймворк **CSpeed** поддерживает механизм событий наблюдателя через компонент **Cs\tool\Component**.
Класс **Cs\tool\Component** включает три метода:
```php
function on($eventName, $eventClosure);
function off($eventName, $eventClosure = NULL);
function trigger($eventName);
```
Классы, которые наследуются от **Cs\tool\Component** в рамках фреймворка **CSpeed**, следующие:
```php
Cs\mvc\Controller
Cs\mvc\Model
Cs\rpc\Server
```
Разработчики должны привязывать события внутри метода `initialise` этих трёх классов. **Обратите внимание:** если существует метод `initialise`, то фреймворк выполнит его начиная с родительского класса до текущего класса, то есть сначала будут выполнены методы `initialise` всех родительских классов, а затем метод `initialise` текущего класса.
Пример использования:
```php
namespace app\modules\admin\controllers;
class User extends \Cs\mvc\Controller
{
function initialise()
{
$this->on(self::EVENT_BEFORE_ACTION, function($object){
// TODO...
});
}
}
```
### 4. Единая точка входа для инициализации: Интерфейс Cs\BootInit ###
Пользователи могут определить класс **BootInit**, который не использует пространство имён и реализует интерфейс **Cs\BootInit**. Добавление файла этого класса в конфигурационный файл позволит системе автоматически выполнить всю необходимую инициализацию системы:
``````php
class BootInit implements Cs\BootInit
{
// TODO...
// Все методы, начинающиеся с указанного префикса, будут последовательно выполняться. Например, если в конфигурационном файле указана методическая метка '__init', все методы, начинающиеся с '__init', будут выполнены.
function __initDi($di, $router)
{
// TODO...
}
function __initRouter($di, $router)
{
// TODO...
}
function __initIOC($di, $router)
{
// TODO...
}
}
```
### 5. Поддержка переключения баз данных в моделях ###
В сложных проектах разработка на основе специфических требований обеспечивает высокую производительность и удобство работы команды. Иногда требуется разделение чтения и записи для баз данных, что позволяет повысить эффективность разработки путём организации кода в несколько уровней группировки. Пример структуры каталогов:
```plaintext
+app
++models
++read
++User
++Info
++write
++User
++Info
```
В **CSpeed** для достижения указанной архитектуры кода достаточно просто привязать соответствующую базу данных в конструкторе модели. Пример:
```php
namespace app\models\read;
class User extends \Cs\mvc\Model
{
function __construct()
{
// В конструкторе обязательно вызвать конструктор родительского класса для выполнения инициализации
parent::__construct();
// Установка текущей модели с использованием соединения с базой данных "read"
$this->setDb('read');
}
}
```При использовании метода `setDb` для внедрения базы данных, необходимо заранее внедрить соответствующие соединения с базами данных:
```php
$di->set('read', function(){
return new \Cs\db\pdo\Adapter([
'dsn' => 'mysql:host=localhost;port=3306;dbname=supjos',
'username' => 'root',
'password' => 'root',
'options' => [
\PDO::ATTR_PERSISTENT => true,
\PDO::ATTR_AUTOCOMMIT => 0
]
]);
});
```
### 6. Высокопроизводительные маршруты ###
Любой фреймворк должен иметь систему маршрутов, чтобы пользователи могли настраивать маршрут для выполнения специальных задач.
Фреймворк **CSpeed** поддерживает загрузку маршрутов из файла конфигурации и непосредственное добавление маршрутов.
#### 1. Загрузка маршрутов из файла конфигурации (INI)
Пример использования файла конфигурации `[router.ini]`:
```php
/a/b/c=/b/c/a
/d/e/:controller:=/aa/bb/$1
/cspeed/version/:action:=/cspeed/{$1}/do
/index/index/index=/say/index/index
```Каждая строка в файле маршрутов представляет собой правило маршрутизации. Обратите внимание, что каждое правило маршрутизации не должно содержать пробелы. Левая часть от знака равно (`=`) — это **источник маршрута**, а правая часть — это **маршрут переадресации**. Например, первое правило указывает, что при источнике маршрута `/a/b/c`, маршрут будет переадресован на `/b/c/a`. Второе правило указывает, что при источнике маршрута `/d/e/hello`, маршрут будет переадресован на `/cspeed/hello/do`, где `:controller:` является **предопределенным регулярным выражением**, которое эквивалентно регулярному выражению `([^0-9-][a-zA-Z0-9-]*)`, а `$1` представляет собой значение, найденное этим регулярным выражением.**CSpeed** использует следующие предопределенные регулярные выражения:
```php
:action:, :controller:, :module: - ([^0-9-][a-zA-Z0-9-]*)
:any: - ([^/]+)
:id: - ([0-9]+)
```
### 7. Поддержка командной строки
Примечание: Контроллеры, поддерживающие режим командной строки в фреймворке **CSpeed**, не используют пространства имён.
Для создания приложения CSpeed в режиме командной строки требуется всего два шага:
**1. Создайте входной файл [index.php]:**
```php
$task = new \Cs\console\Task();
// Также можно использовать параметризованный способ инициализации системы, с тем же форматом параметров, что и у Cs\App
$task = new \Cs\console\Task("xxx/xxx.ini", "dev");
// Выполнение командной строки
$task->run($argv[1]);
```
**2. Выполните следующую команду в каталоге bash:**
```php
php -f index.php hello/world/cspeed
```
чтобы запустить метод `cspeed` контроллера `world` модуля `hello`.
**Примечание:** В фреймворке CSpeed методы окончены либо словом `Action`, либо `Rpc`. Методы, заканчивающиеся на `Action`, являются маршрутными методами, а методы, заканчивающиеся на `Rpc`, представляют собой экспонируемыми методами сервера RPC.
### 8. Полностью развитый Active Record
При разработке приложений часто возникает необходимость избежать написания SQL-запросов вручную и минимизировать риск SQL-инъекций. Для этого используется методология, предоставляемая фреймворками, которая позволяет выполнять SQL-запросы за счет снижения производительности, но обеспечивает удобство и безопасность.Например, если есть таблица данных `www_product`, содержащая четыре поля: `time`, `name`, `version`, `id`, то модель может быть создана следующим образом:
```php
namespace app\models;
class Good extends \Cs\mvc\Model
{
// Не рекомендуется использовать конструктор, обязательно вызывайте конструктор родительского класса
function __construct()
{
parent::__construct();
}
// Используйте этот метод для выполнения начальной настройки
function initialise($object)
{
// TODO...
// Этот метод будет выполнен независимо от того, создаётся новый объект модели или получается существующий через find
// [Примечание]: Конструктор выполняется только при создании нового объекта модели.
// Метод принимает текущий объект класса (в данном случае $object представляет объект Good) как параметр
}
function tableName()
{
return 'www_product';
}
}
```
По умолчанию в фреймворке CSpeed: метод `one()` возвращает объект класса, а метод `all()` — массив объектов класса, каждый элемент которого представляет объект класса, что делает работу с Active Record более удобной. Если вам нужна выборка в виде массива значений, используйте метод `asArray()` перед вызовом `one()` или `all()`.
**CRUD операции**
**Добавление записи [C]:**
```php
$good = new \app\models\Good();
$good->time = date('Y-m-d H:i:s');
$good->name = 'CSpeed';
$good->version = '2.1.11';
$good->id = 1;
$good->has = 1;
```
```php
if ($good->save()) {
// TODO...
// Успешное добавление записи
} else {
// TODO...
// Добавление записи не удалось
}
```**Обновление записи[U]:**
```php
$good = \app\models\Good::find();
if ($good->where(['id' => 1])->save([
'name' => 'CSpeed-V2.1.11',
'version' => 'v2.1.11'
])) {
// TODO...
} else {
// TODO...
}
```
**Поиск записей[R]**
```php
$good = \app\models\Good::find();
// Возвращает объект
$lists = $good->orderBy(['id' => SORT_DESC, 'age' => SORT_ASC])->all();
// Возвращает массив
$lists = $good->orderBy('id DESC')->asArray()->all();
// Поиск одной записи
$lists = $good->orderBy(['id' => SORT_DESC, 'age' => SORT_ASC])->one();
// Возвращает массив
$lists = $good->orderBy(['id' => SORT_DESC, 'age' => SORT_ASC])->asArray()->one();
```
**Удаление записи[D]:**
```php
$good = \app\models\Good::find();
if ($good->where(['id' => 1])->delete()) {
// TODO...
} else {
// TODO...
}
```
### 9. Самый простой MVC-фреймворк ###
В **B/S** архитектуре разработки, **MVC** модель разработки несомненно является самой быстрой и эффективной. Современная разработка программного обеспечения стремится к повышению повторного использования кода, увеличению скорости разработки и снижению количества необходимых изменений при обслуживании.
## Высокая производительность CSpeed ##
**Команда тестирования:**
```bash
/usr/local/httpd-2.4.29/bin/ab -c100 -n100000 http://www.supjos.com/hello/cspeed/
```
**Результаты тестирования:**
```text
Concurrency Level: 100
Time taken for tests: 9.781 seconds
Complete requests: 100000
Failed requests: 0
Total transferred: 18500000 bytes
HTML transferred: 0 bytes
Requests per second: 10223.94 [#/sec] (mean)
Time per request: 9.781 [ms] (mean)
Time per request: 0.098 [ms] (mean, across all concurrent requests)
Transfer rate: 1847.10 [Kbytes/sec] received
```Время соединения (мс)
мин среднее[+/−sd] медиана макс
Подключение: 0 1 0,9 0 9
Обработка: 1 9 1,8 9 25
Ожидание: 1 9 1,8 9 24
Общее: 4 10 1,3 10 28
ПРЕДУПРЕЖДЕНИЕ: Среднее значение и медиана времени первоначального подключения не находятся в пределах нормальной дисперсии
Эти результаты, скорее всего, не являются надёжными.Процент запросов, обслуженных за определённое время (мс)
50% 10
66% 10
75% 10
80% 10
90% 11
95% 12
98% 13
99% 13
100% 28 (наиболее длительный запрос)
```## Приложение CSpeed использует конфигурационные файлы в формате INI ##
```ini
[core]
core.application = ../app ; каталог WEB
core.bootstrap = ../app/bootstrap.php ; путь к файлу с классом Bootstrap
core.bootstrap.class.name = BootInit ; имя класса Bootstrap
core.bootstrap.method.string = __init ; метод инициализации класса Bootstrap
core.router.modules = index, home, back ; регистрация модулей
core.router.default.module = index ; default module
core.router.default.controller = index ; default controller
core.router.default.action = index ; default action
core.view.ext = phtml ; расширение шаблонов
core.view.auto.render = on ; автоматическая отрисовка шаблонов, on/off
core.url.pattern = .html ; настройки псевдо-статических URL
core.debug.mode = on ; режим отладки, on/off
core.path.info.symbol = __csurl ; GET параметр для получения URL через PATH_INFO
core.path.info.mode = auto ; режим PATH_INFO, GET или AUTO
```[db]
db.master.dsn = 'mysql:host=localhost;port=3306;dbname=supjos' ; тип базы данных
db.master.username = root ; имя пользователя базы данных
db.master.password = 3333 ; пароль доступа к базе данных
[dev:core]
core.application = ../app ; каталог WEB
core.bootstrap = ../app/bootstrap.php ; указывает на директорию с классами bootstrap
core.bootstrap.class.name = BootInit ; имя класса для запуска
core.bootstrap.method.string = __init ; метод инициализации класса Bootstrap
core.router.modules = index, home, back ; регистрация нескольких модулей
core.router.default.module = index ; по умолчанию используется модуль index
core.router.default.controller = index ; контроллер по умолчанию - index
core.router.default.action = index ; действие по умолчанию - index
core.view.ext = phtml ; расширение файла представления
core.view.auto.render = on ; автоматическое отображение представлений, on, true, yes: включено, off, false, no: выключено
core.url.pattern = .html ; настройка псевдо-статических URL
core.debug.mode = on ; режим отладки, on, true, yes: включен, off, false, no: выключен
core.path.info.symbol = __csurl ; GET параметр для получения URL через PATH_INFO
core.path.info.mode = auto ; режим PATH_INFO, GET, AUTO
### Настройка базы данных [dev:db]```markdown
[dev:db]
db.master.dsn = 'mysql:host=localhost;port=3306;dbname=supjos'; # Тип базы данных
db.master.username = root; # Имя пользователя базы данных
db.master.password = 3333; # Пароль базы данных
```
## Подсказки кода в PhpStorm ##
Если пользователи хотят использовать функцию подсказок кода в **PhpStorm**, им достаточно скачать исходный код и следовать инструкциям, указанным в файле **README.md**.
## Общение через QQ-группу ##
https://jq.qq.com/?_wv=1027&k=5kSunAR
Приглашаем вас сделать star или оставить ценные отзывы и предложения. Ваши идеи помогут **CSpeed** развиваться.
Официальная QQ-группа расширения **CSpeed**: **605383362**
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )