lark / framework
Lark 框架
Requires
- mongodb/mongodb: ^1.9
README
Lark 是一个专为开发 REST API 设计的现代、轻量级应用框架。
安装
要求
- PHP 8
- PHP 扩展
- 必需
- [...]
- 可选
- curl - 如果使用
Lark\Http\Client
Composer 安装
composer require lark/framework
路由
路由器用于分发路由操作和中间件。
// bootstrap // ... // define routes router() // get([route], [action]) ->get('/', function() {}); // run app app()->run();
路由
有多种定义路由的方式。
// routes for HTTP specific methods: router()->delete('/route', function(){}); router()->get('/route', function(){}); router()->head('/route', function(){}); router()->options('/route', function(){}); router()->patch('/route', function(){}); router()->post('/route', function(){}); router()->put('/route', function(){}); // route for all HTTP methods router()->all('/route', function(){}); // route for multiple HTTP methods router()->route(['GET', 'POST'], '/route', function(){}); // a wildcard route "*" can be used to match any route router()->get('*', function(){}); // all HTTP GET methods router()->all('*', function(){}); // all HTTP methods (all requests) router()->route(['GET', 'POST'], '*', function(){}); // all HTTP GET and POST methods
正则表达式路由
正则表达式路由使用 PCRE 模式进行路由匹配。
// match all routes that begin with "/api" router()->get('/api.*?', function(){});
路由分组
可以使用路由分组来简化定义相似的路由。
router() ->group('/api/users') // group([base-route]) ->get('/', function(){}) // "/api/users" ->get('/active', function(){}); // "/api/users/active"
路由分组加载
路由分组可以定义在 路由文件 中,这些文件在路由期间被加载(惰性加载路由)。
// bootstrap routes directory // ... router()->load([ // [base-route] => [file] '/api/users' => 'users' ]); // in routes directory file "users.php" defines routes // the group('/api/users') method does not need to be called (handled by load() method) router() ->get('/', function(){}) // "/api/users" ->get('/active', function(){}); // "/api/users/active"
在路由文件内部,router()
只应调用一次,以避免错误的路由未匹配错误。
// incorrect: router()->bind(function(){}); router()->get('/', function(){}); // correct: router() ->bind(function(){}) ->get('/', function(){});
路由控制器
class MyController implements Lark\Router\RouteControllerInterface { public function bind(Router $router): void { $router->get('/users', function(){}); // "/api/users" } } // in routes file router() ->group('/api') ->controller(new MyController);
路由操作
路由操作是在匹配到路由时执行的。路由操作可以是可调用的函数(Closure
)或包含 [class, method]
的数组。第一个匹配的路由是唯一将被执行的路由操作。
// function will be called on route match router()->get('/example-html', function(): string { return 'hello'; // return string to output as html }); router()->get('/example-json', function(): array { return ['message' => 'hello']; // return array|stdClass to output as JSON // will auto add header "Content-Type: application/json" // and response body will be: // {"message": "hello"} }); // class method "App\Controller\ExampleController::hello()" will be called on route match router()->get('/example2', [App\Controller\ExampleController::class, 'hello']);
路由未找到操作
如果没有找到匹配的路由,可以定义一个未找到的操作。HTTP 响应状态码自动设置为 404
。
router()->notFound(function(string $requestMethod, string $requestPath){});
如果没有定义未找到操作,将抛出 Lark\Router\NotFoundException
。
路由参数
命名参数
路由命名参数是必需的参数,不使用正则表达式。允许多个命名参数。
router()->get('/users/{id}', function($id){});
可选命名参数
路由可选命名参数是不使用正则表达式的可选参数。可选命名参数只能用于路由的末尾。允许多个可选命名参数。
router()->get('/users/{id}/{groupId?}', function($id, $groupId = null){});
在这个例子中,groupId
参数是可选的,因此路由 /users/5
和 /users/5/10
都会匹配。
正则表达式参数
可以使用 PCRE 模式定义参数。允许多个正则表达式参数。
// match digits router()->get('/users/(\d+)', function(int $id){}); // or match alphanumeric with length of 8 router()->get('/users/([a-z0-9]{8})', function(string $id) {});
中间件
中间件是在调用路由操作之前执行的单个或多个操作。中间件操作可以始终执行或仅在路由匹配时执行。中间件必须在定义路由之前定义。中间件操作的结构与 路由操作 相同。将 Lark\Request $req
和 Lark\Response $res
参数传递给所有中间件操作。
// executed always router()->bind(function(Lark\Request $req, Lark\Response $res){}); // executed if any route is matched router()->matched(function(Lark\Request $req, Lark\Response $res){}); // define routes // ...
可以设置多个中间件操作。
// single action router()->bind(function(){}); // multiple actions router()->bind(function(){}, [MyClass::class, 'myMethod']); // array of actions router()->bind([ function(){}, function(){} ]);
路由中间件
仅当路由匹配时,才会执行特定的路由中间件操作。
// method([methods], [route], [...actions]) router()->map(['GET'], '/api.*?', function(){}); router()->get('/api/users', function(){});
如果 HTTP 请求是 /api/users
,则中间件操作和路由操作都会执行。
中间件执行顺序
中间件始终按照以下顺序执行
- 始终执行(《router()->bind(...)》)
- 在匹配的路由上执行映射(《router()->map(...)》)
- 在匹配的路由上执行(《router()->matched(...)》)
- 中间件之后(《router()->after(...)》)
路由组中间件
可以将中间件定义为仅在特定路由组中使用。仅当匹配组路由时,才会执行路由组中间件操作。
router() // group([base-route], [...actions]) ->group('/api/users', function(){}) ->get('/', function(){}) // "/api/users" ->get('/{id}', function($id){}) // "/api/users/{id}"
中间件之后
即使路由不存在,中间件之后也会在调用路由操作之后始终运行。
router()->after(function(){}, [MyClass::class, 'myMethod']);
日志记录
Lark\Logger
用于日志记录。可用的辅助函数为 logger()
。
logger('channel')->critical('message', [context]); logger('channel')->debug('message', [context]); logger('channel')->error('message', [context]); logger('channel')->info('message', [context]); logger('channel')->warning('message', [context]);
日志记录信息级别记录示例。
// bootstrap log handler app()->logHandler = new App\LogHandler; Lark\Logger::handler(app()->logHandler); // ... // log info level record logger('user')->info('User has been authorized', ['userId' => $user->id]); // ... // output log example print_r( app()->logHandler->close() );
可以将全局上下文添加到所有日志记录中发送的上下文中。
Lark\Logger::globalContext(['sessionId' => $session->id]); // ... logger('user')->info('User has signed out', ['userId' => $user->id]); // context is: ['sessionId' => x, 'userId' => y]
异常处理
可以使用异常处理器来处理异常。
// bootstrap // ... // define routes // ... try { // run app app()->run(); } catch (Throwable $th) { new App\ExceptionHandler($th); }
示例 App\ExceptionHandler
类。
namespace App; use Throwable; class ExceptionHandler { public function __construct(Throwable $th) { \Lark\Exception::handle($th, function (array $info) use ($th) { $code = $th->getCode(); if (!$code) { $code = 500; } // log error // ... // respond with error res() ->code($code) ->json($info); // --or-- continue to throw exception throw $th; }); } }
调试器
Lark\Debugger
可用于调试。可用的辅助函数有 debug()
和 x()
。
use Lark\Debugger; // append debugger info Debugger::append(['some' => 'info']) ->name('Test info') // this will be displayed as title (optional) ->group('test'); // this will group info together (optional) Debugger::append(['more' => 'info']) ->name('More test info') ->group('test'); Debugger::dump(); // dump all debugger info and exit // or use: // x(); // dump all debugger info and exit
配置与绑定
可以使用 use()
方法设置框架配置和绑定。
调试
启用 Lark 内部附加调试信息以进行调试转储。
app()->use('debug.dump', true);
启用 Lark 内部调试日志。
app()->use('debug.log', true);
数据库连接
使用语法 db.connection.[connectionId]
注册数据库连接,并使用语法 [connectionId]$[database]$[collection]
访问。
// setup default MongoDB database connection with connectionId "default" // the first registered connection is always the default connection // regardless of connectionId app()->use('db.connection.default', [ 'hosts' => ['127.0.0.1'], 'username' => 'test', 'password' => 'secret', 'replicaSet' => 'rsNameHere', // (optional) // options can override any global database options // (optional, see "Database Global Options" below) 'options' => [] ]); // register second connection with connectionId "myconn" app()->use('db.connection.myconn', [...]); // ... // use default connection (no connectionId required): $db = db('dbName$collectionName'); // or: $db = db('dbName', 'collectionName'); // use non-default connection (connectionId required): $db2 = db('myconn$dbName$collectionName'); // or: $db = db('myConn2', 'dbName', 'collectionName');
数据库全局选项
可以使用 db.options
设置数据库全局选项。下面列出了所有默认选项值。
use MongoDB\Driver\ReadConcern; use MongoDB\Driver\WriteConcern; app()->use('db.options', [ 'db.allow' => [], // allow access to only specific databases 'db.deny' => ['admin', 'config', 'local'], // restrict access to databases 'debug.dump' => false, // will include all database calls/context in debugger dumper 'debug.log' => false, // add debug level database messages to logger 'find.limit' => 1_000, // find "limit" for find options 'read.concern' => new ReadConcern, // MongoDB read concern 'write.concern' => new WriteConcern(WriteConcern::MAJORITY) // MongoDB write concern ]);
有关写入关注点的更多信息,请参阅 MongoDB 文档 和 PHP 文档。
数据库会话
可以使用 Lark\Model
对象将会话存储在数据库中。
app()->use('db.session', new App\Model\Session);
验证器自定义规则
可以使用 validator.rule.[type].[ruleClassName]
注册自定义验证器规则。
app()->use('validator.rule.string.beginWithEndWith', App\Validator\BeginWithEndWith::class);
环境变量与配置
Lark\Env
用于应用程序环境变量和配置。可用的辅助函数为 env()
。
示例读取 PATH
环境变量。
$path = env('PATH'); // or use default value "/my/path" if environment variable does not exist $path2 = env('PATH2', '/my/path'); // for required environment vars do not use a default value argument // which will throw exception if the environment var does not exist $path2 = env('PATH2'); // Lark\Exception exception thrown: Invalid env variable key "PATH2"
示例 .env
文件。
DB_USER=myuser
DB_PWD=secret
示例 .env
文件使用。
// load from file (bootstrap) Lark\Env::getInstance()->load(DIR_ROOT . '/.env'); $dbUser = env('DB_USER'); // myuser $dbPassword = env('DB_PWD'); // secret
其他 Lark\Env
方法:fromArray(array $array)
、has(string $key): bool
和 toArray(): array
。
请求
Lark\Request
提供带有输入清理的 HTTP 请求数据。可用的辅助函数是 req()
。
// example request: // POST /example // Content-Type: application/json // {"name": "Test", "contact": {"email": "test@example.com"}} $data = req()->json(); // get all as object/array (no auto sanitizing) // request JSON must be an array or 400 response is sent $data = req()->jsonArray(); // request JSON must be an object or 400 response is sent $data = req()->jsonObject();
如果 JSON 方法不存在 HTTP 头
Content-Type: application/json
,将发送自动响应,HTTP 状态码为400
,JSON 主体为{"message": "Invalid JSON: [reason]"}
。
也可以通过清理访问单个 JSON 字段。
// get individual field $name = req()->jsonField('name')->string(); if(req()->jsonField('contact.email')->has()) { $email = req()->jsonField('contact.email')->email(); }
示例 POST
请求(Content-Type: application/x-www-form-urlencoded
)。
if(req()->isMethod('POST')) { $name = req()->input('name')->string(); if(req()->input('email')->has()) { $email = req()->input('email')->email(); } }
示例 GET
请求。
// request "/?id=5&name=Shay" print_r([ 'id' => req()->query('id')->integer(), // use "default" as value if query "name" does not exist 'name' => req()->query('name', 'default')->string() ]); // Array ( [id] => 5 [name] => Shay )
请求 Cookie 示例。
if(req()->cookie('myCookie')->has()) { var_dump( req()->cookie('myCookie')->string() ); }
请求会话
Lark\Request\Session
用于管理会话。
app()->session->set('user.id', 5); // creates session data: [user => [id => 5]] // ... if(app()->session->has('user.id')) { $userId = app()->session->get('user.id'); }
可以使用
Lark\Database\Session::handler()
将会话存储在数据库中。
Lark\Request\SessionFlash
可用于存储短期数据,数据在通过以下请求设置时可用,例如
app()->session()->flash()->set('userError', 'Invalid session'); // redirect, then use message echo app()->session()->flash()->get('userError'); // message is no longer available on next request
请求方法
body(bool $convertHtmlEntities = true): string
- 获取原始请求体数据contentType(): string
- 获取内容类型cookie(string $key, $default = null): Lark\Request\Cookie
- 获取 Cookie 输入对象hasHeader(string $key): bool
- 检查是否存在头键header(string $key): string
- 获取头值headers(): array
- 获取所有头host(): string
- 获取 HTTP 主机值,如www.example.com
input(string $key, $default = null): Lark\Request\Input
- 获取POST
的输入对象ipAddress(): string
- 获取 IP 地址isContentType(string $contentType): bool
- 验证请求内容类型isMethod(string $method): bool
- 验证请求方法isSecure(): bool
- 检查请求是否安全(HTTPS)json():
- 获取 JSON 请求体jsonArray(): array
- 获取 JSON 请求体,必须是数组或返回 HTTP 状态码400
的响应jsonField(string $field, $default = null): Lark\Request\Json
- 获取 JSON 请求字段对象jsonObject(): array
- 获取 JSON 请求体,必须是对象或返回 HTTP 状态码400
的响应method(): string
- 获取请求方法path(): string
- 获取路径,如/the/path
pathWithQueryString(): string
- 获取带查询字符串的路径,如/the/path?x=1
port(): int
- 获取端口号query(string $key, $default = null): Lark\Request\Query
- 获取GET
的查询输入对象queryString(): string
- 获取查询字符串,如x=1&y=2
scheme(): string
- 获取 URI 方案,如http
session(): Lark\Request\Session
- 获取会话对象uri(): string
- 获取 URI,如http://example.com/example?key=x
请求输入方法
输入方法包括请求输入对象的方法:Cookie
、Input
和 Query
。
email(array $options = [])
- 值获取器,作为电子邮件进行清理float(array $options = ['flags' => FILTER_FLAG_ALLOW_FRACTION])
- 值获取器,作为浮点数进行清理has(): bool
- 检查是否存在键integer(array $options = [])
- 值获取器,作为整数进行清理string(array $options = [])
- 值获取器,作为字符串进行清理url(array $options = [])
- 值获取器,作为 URL 进行清理
会话方法
会话方法 clear()
、get()
、has()
和 set()
都使用点符号表示法进行键,例如:set('user.isActive', 1)
等同于:[user => [isActive => 1]]
。
clear(string $key)
- 清除键static cookieOptions(array $options)
- 设置cookie选项- 默认选项为:
['lifetime' => 0, 'path' => '/', 'domain' => '', 'secure' => false, 'httponly' => false]
- 默认选项为:
destroy()
- 销毁会话static exists(): bool
- 检查会话是否启用并且会话存在get(string $key)
- 值获取器has(string $key): bool
- 检查键是否存在id(): ?string
- 会话ID获取器isSession(): bool
- 检查会话是否存在set(string $key, $value)
- 键/值设置器toArray(): array
- 会话数组获取器
响应
Lark\Response
用于控制HTTP响应。
// set header, status code 200, content-type and send JSON response res() ->header('X-Test', 'value') ->code(Lark\Response::HTTP_OK) ->contentType('application/json') // not required when using json() ->json(['ok' => true]); // {"ok": true}
响应方法
cacheOff(): Lark\Response
- 使用缓存控制禁用缓存contentType(string $contentType): Lark\Response
- 设置content-typecookie($key, $value, $expires, $path, $domain, $secure, $httpOnly): bool
- 设置cookiecookieClear(string $key, string $path = '/'): bool
- 删除cookieheader(string $key, $value): Lark\Response
- 设置headerheaderClear(string $key): Lark\Response
- 删除header键headers(array $headers): Lark\Response
- 使用数组设置headersjson($data)
- 以JSON有效载荷响应(headers中的content-type设置为application/json
)redirect(string $location, bool $statusCode301 = false)
- 发送重定向send($data)
- 以原始数据有效载荷响应code(int $code): Lark\Response
- 设置响应状态码
数据库
Lark\Database
用于访问MongoDB数据库和集合实例。提供辅助函数 db()
。
// bootstrap // setup default MongoDB database connection with connectionId "default" app()->use('db.connection.default', [...]); // register second connection with connectionId "myconn" app()->use('db.connection.myconn', [...]); // ... // get Database object instance $db = db('myDb$users');
插入文档
// insert documents $docIds = $db->insert([ ['name' => 'Test', 'role' => 'admin'], ['name' => 'Test2', 'role' => 'admin'] ]); // Array ( [0] => 62ba4fd034faaf6fc132ef54 [1] => 62ba4fd034faaf6fc132ef55 ) // insert single document $docId = $db->insertOne(['name' => 'Test3', 'role' => 'readonly']);
查找文档
// find documents $docs = $db->find(['role' => 'admin']); // Array ( [0] => Array ( [id] => 62ba4fd034faaf6fc132ef54 [name] => Test [role] => admin ) // [1] => Array ( [id] => 62ba4fd034faaf6fc132ef55 [name] => Test2 [role] => admin ) ) // find documents with "name" staring with "Test" $docs = $db->find(['name' => ['$regex' => '^Test']]); // find documents by IDs $docs = $db->findIds(['62ba4fd034faaf6fc132ef54', '62ba4fd034faaf6fc132ef55']); // find single document $doc = $db->findOne(['name' => 'Test2']); // find single document by ID $doc = $db->findId('62ba4fd034faaf6fc132ef54');
更新文档
// update documents $affected = $db->update(['role' => 'admin'], ['role' => 'admin2']); // update bulk $docIds = $db->updateBulk([ ['id' => '62ba4fd034faaf6fc132ef55', 'role' => 'admin'], [...] ]); // Array ( [0] => 62ba4fd034faaf6fc132ef55 [1] => ... ) // update single document by ID $newDoc = $db->updateId('62ba4fd034faaf6fc132ef55', ['role' => 'admin2']); // update single document $newDoc = $db->updateOne(['name' => 'Test2'], ['role' => 'admin']);
默认情况下,更新方法使用$set
运算符进行更新,例如['$set' => ['role' => 'admin']]
。此运算符可以更改,例如
// increment visits by 1 $newDoc = $db->updateOne(['name' => 'Test2'], ['visits' => 1], operator: '$inc');
替换文档
// replace bulk $docIds = $db->replaceBulk([ ['id' => '62ba4fd034faaf6fc132ef55', 'name' => 'Test222'], [...] ]); // Array ( [0] => 62ba4fd034faaf6fc132ef55 [1] => ... ) // replace single document by ID $newDoc = $db->replaceId('62ba4fd034faaf6fc132ef55', ['name' => 'Test2222', 'role' => 'admin']); // replace single document $newDoc = $db->replaceOne(['name' => 'Test2222'], ['name' => 'Test2', 'role' => 'admin']);
删除文档
// delete documents (note: filter cannot be empty) $affected = $db->delete(['role' => 'admin']); // delete documents by IDs $affected = $db->deleteIds(['62ba4fd034faaf6fc132ef54', '62ba4fd034faaf6fc132ef55']); // delete single document $affected = $db->deleteOne(['name' => 'Test2']); // delete all documents in collection $affected = $db->deleteAll();
集合字段方法
// create a new field // set default value to empty array $affected = $db->collectionField('tags')->create([]); // delete a field $affected = $db->collectionField('tags')->delete(); // check if a field exists on all documents $exists = $db->collectionField('tags')->exists(); // check if a field exists on any document $exists = $db->collectionField('tags')->exists(false); // remove value "mytag" from field "tags" array $affected = $db->collectionField('tags')->pull( ['id' => '62ba4fd034faaf6fc132ef54'], 'mytag' ); // append values "mytag1" and "mytag2" to field "tags" array // these values will only be appended if they // don't already exists in the array // use $unique=false to always append $affected = $db->collectionField('tags')->push( ['id' => '62ba4fd034faaf6fc132ef54'], ['mytag1', 'mytag2'] ); // rename a field $affected = $db->collectionField('tags')->rename('tagsNew');
使用点表示法表示嵌套字段名称,如
field1.field2
。
数据库方法
collectionField(string $field): Database\Field
- 集合字段对象获取器count(array $filter = [], array $options = []): int
- 计算匹配过滤器条件的文档数delete(array $filter, array $options = []): int
- 删除匹配过滤器的文档deleteAll(array $options = []): int
- 删除所有文档deleteIds(array $ids, array $options = []): int
- 根据ID删除文档deleteOne(array $filter, array $options = []): int
- 删除匹配过滤器条件的单个文档drop(): bool
- 删除集合exists(): bool
- 检查集合是否存在find(array $filter = [], array $options = []): array
- 查找匹配过滤器的文档findId($id, array $options = []): ?array
- 通过ID查找文档findIds(array $ids, array $options = []): array
- 通过ID查找文档findOne(array $filter = [], array $options = []): ?array
- 查找匹配过滤器条件的单个文档has(array $filter, array $options = []): bool
- 检查是否存在匹配过滤器的文档hasIds(array $ids, array $options = []): bool
- 检查是否存在具有指定ID的文档insert(array $documents, array $options = []): array
- 插入文档insertOne($document, array $options = []): ?string
- 插入单个文档ping(): bool
- ping命令replaceBulk(array $documents, array $options = []): int
- 批量替换replaceId($id, $document, array $options = []): int
- 替换单个文档replaceOne(array $filter, $document, array $options = []): int
- 替换单个文档update(array $filter, $update, array $options = []): int
- 更新匹配过滤器条件的文档updateBulk(array $documents, array $options = []): int
- 批量更新updateId($id, $update, array $options = []): int
- 通过ID更新文档updateOne(array $filter, $update, array $options = []): int
- 更新单个匹配过滤器条件的文档
数据库字段方法
create($defaultValue = null): int
- 创建带有默认值的字段delete(): int
- 删除集合字段exists(bool $allDocs = true): bool
- 检查字段是否存在或检查字段是否在任何文档中存在(如果!$allDocs
)pull(array $filter, $value): int
- 从字段数组中移除值push(array $filter, $value, $unique = true): int
- 将值追加到字段数组,如果$unique
为真,则只有当值不在字段数组中时才会追加rename(string $newName): int
- 重命名字段
模式
Lark\Schema
用于创建用于创建实体、实体验证和数据库集合创建的架构。
use Lark\Schema; $schema = new Schema([ // create an index when creating a database collection '$index' => [ 'name' => 1, 'age' => 1, '$name' => 'idxNameAge' ], // or create multiple indexes // '$indexes' => [ // ['username' => 1, '$name' => 'idxUsername', '$unique' => true], // ['name' => 1, 'age' => 1, '$name' => 'idxNameAge'] // ], // auto database projection (filter password by default) '$filter' => ['password' => 0], // schema fields 'name' => ['string', 'notEmpty'], 'username' => ['string', 'notEmpty'], 'password' => ['string', 'notEmpty'], 'age' => ['int', 'notEmpty'], 'isAdmin' => ['bool', 'notNull', ['default' => false]] ]);
架构使用 验证类型与规则 进行字段定义。
在
$index
和$indexes
中的选项是任何以$
开头的字段,如$unique
,更多选项可以在 MongoDB 文档 中找到。
默认字段值也可以动态设置。对于嵌套字段,使用点表示法,如 field.nestedfield
。
$schema->default('isAdmin', false);
可以使用字段值回调。对于嵌套字段,使用点表示法,如 field.nestedfield
。
$schema->apply('name', function($name): string { return strtoupper($name); });
字段架构导入
可以将架构文件导入为架构字段。首先,在架构文件中创建部分架构,例如:[DIR_SCHEMAS]/partials/users.info.php
。
<?php return [ 'object', [ 'fields' => [ 'age' => 'int', 'tags' => 'array' ] ] ];
然后,使用字段名和文件添加导入。
$schema = new Schema([ '$import' => [ // field => file (in schemas directory) 'info' => 'partials/users.info' ], 'name' => ['string', 'notEmpty'], // field for schema import (optional, does not need to be set here) 'info' => null ]);
打印 $schema->toArray()
将输出
Array
(
[name] => Array
(
[0] => string
[1] => notEmpty
)
[info] => Array
(
[0] => object
[1] => Array
(
[fields] => Array
(
[age] => int
[tags] => array
)
)
)
)
嵌套字段(使用点表示法)也可以使用。
$schema = new Schema([ '$import' => [ // field => file (in schemas directory) 'info.1.fields' => 'partials/users.info' ], 'name' => ['string', 'notEmpty'], 'info' => [ 'object', ['fields' => null] ] ]);
示例部分架构在:[DIR_SCHEMAS]/partials/users.info.php
<?php return [ 'age' => 'int', 'tags' => 'array' ];
模型
Lark\Model
是一个模型:一种简化数据库调用和创建/验证实体的方法。
namespace App\Model; use App\Model; use Lark\Schema; class User extends Model { const DBS = 'default$app$users'; public static function &schema(): Schema { return parent::schema([ 'name' => ['string', 'notEmpty'], 'age' => ['int', 'notEmpty'], 'isAdmin' => ['bool', 'notNull', ['default' => false]] ]); } }
App\Model\User
类可以用于创建实体和验证。
$user = (new App\Model\User)->make([ 'name' => 'Bob', 'age' => 25 ]); var_dump($user); // array(3) { ["name"]=> string(3) "Bob" ["age"]=> int(25) ["isAdmin"]=> bool(false) } // or an array can be used $user = (new App\Model\User)->makeArray([ ['name' => 'Bob', 'age' => 25], ['name' => 'Jane', 'age' => 21] ]);
可以使用 $mode
参数来更改验证器模式,例如使用 replace+id
或 update+id
要求文档ID。
// schema: ['id' => ['string', 'id'], 'name' => ['string', 'notEmpty']] $user = (new App\Model\User)->make([ 'name' => 'Bob' ], 'update+id'); // throws Lark\Validator\ValidatorException: // Validation failed: "User.id" must be a string
可以使用 $mode
参数来允许 update
或 update+id
与部分文档一起使用的缺失字段。
$user = (new App\Model\User)->make([ 'name' => 'Bob' ], 'update'); var_dump($user); // array(1) { ["name"]=> string(3) "Bob" }
可以使用 Model::db()
方法来访问模型数据库集合(必须设置 Model::DBS
)。
// ... class Model extends Model { const DBS = 'default$app$users'; public function get(string $id): ?array { return $this->db()->findId($id); } } // get user document $user = (new App\Model\User)->get('62ba4fd034faaf6fc132ef55'); // external calls: get documents $docs = (new \App\Model\User)->db()->find(['role' => 'admin']);
重要:模型类不应在其
Model::__construct()
方法中包含任何必需参数,因为模型在使用模型/数据库绑定时会自动实例化,并且任何必需参数都不会存在。
模型架构方法
可以使用多种方式使用 Model::schema()
方法。默认情况下,该方法将使用 Model::SCHEMA
架构文件常量从文件中加载架构。
创建架构的另一种方法是覆盖父方法并传递架构数组。
class ExampleModel extends Model { public static function &schema(): Schema { return parent::schema([ 'id' => ['string', 'id'], // ... ]); } }
上述方法缓存架构对象,因此当再次调用架构方法时,它将返回引用的 Schema
对象。
还可以传递回调来访问由父方法创建的 Schema
对象,例如
class ExampleModel extends Model { const SCHEMA = 'users.php'; public static function &schema(): Schema { return parent::schema(function(Schema &$schema) { $schema->apply('name', function($name) { return strtoupper($name); }); }); } }
模型数据库查询
可以使用模型 Lark\Database\Query
类来使用查询参数。
use Lark\Database\Query; use App\Model\User; $query = new Query(new User, [ 'name' => 'test' ]); // Database::find() $results = $query->find(); // Database::count() $count = $query->count();
查询选择器
查询选择器可以用作查询参数。匹配字段与字段值
$query = [ 'name' => 'test' ];
MongoDB 比较选择器 $eq
、$gt
、$gte
、$in
、$lt
、$lte
、$ne
和 $nin
可以使用如下
$query = [ 'age' => ['$gte' => 18] ];
使用 $in
选择器
$query = [ 'name' => ['$in' => ['test', 'test2', 'test3']] ];
使用多个选择器
$query = [ 'age' => ['$gt' => 20, '$lt' => 100] ];
查询选项
默认情况下,使用多个选择器的查询将执行逻辑AND
操作。可以使用$or
选项来使用逻辑OR
操作。
$query = [ // age is greater than 20 OR less than 100 'age' => ['$gt' => 20, '$lt' => 100], '$or' => true ];
可以使用$filter
(或$projection
)选项来过滤从数据库返回的文档字段。
$query = [ // only include fields "id" and "name" for each document '$filter' => ['id' => 1, 'name' => 1], 'name' => 'test', 'age' => ['$gte' => 18] ]; // or fields can be excluded for each document $query = [ // exclude fields "age" and "info" for each document '$filter' => ['age' => 0, 'info' => 0] ];
可以使用$page
选项进行分页。
// fetch first page $query = [ '$page' => 1 ]; // fetch second page $query = [ '$page' => 2 ];
默认情况下,每页文档的限制由数据库选项
find.limit
确定。
$page
选项的默认文档排序顺序为["id" => 1]
,这可以使用$sort
选项进行覆盖。
可以使用$limit
选项来设置返回的文档数量,或在使用$page
选项时覆盖默认的每页文档数量。
$query = [ '$limit' => 100 ];
$limit
选项的值不能超过数据库选项find.limit
的值。
可以使用$sort
选项来设置文档的排序顺序。
// sort by "name" ASC and "age" DESC $query = [ '$sort' => ['name' => 1, 'age' => -1] ];
可以使用$skip
选项来设置查询跳过的值。
$query = [ '$skip' => 10 ];
当与
$page
选项一起使用时,$skip
选项将始终被覆盖。
自动创建和更新字段值
创建和更新的字段值可以用于自动设置带有创建和更新日期/时间的字段。示例模式
[ '$created' => 'createdAt', '$updated' => 'updatedAt', 'name' => ['string', 'notNull'], 'createdAt' => ['timestamp', 'notNull'], 'updatedAt' => ['timestamp', 'notNull'] ]
现在createdAt
和updatedAt
字段将自动设置为当前时间戳(time()
)。默认情况下,值可以设置为timestamp
,也可以设置为datetime
用于DateTime
或dbdatetime
用于MongoDB\BSON\UTCDateTime
,示例
[ '$created' => [ 'createdAt' => 'dbdatetime' ], // ... ]
在上面的示例中,createdAt
字段将只设置一次(使用模式默认值),而updatedAt
字段将在每次创建文档时设置。
数据库模型模式约束
数据库模型模式约束可以用作数据库约束,例如验证外键和通过引用删除文档。
引用外键约束
$refs.fk
约束验证外键,可以在任何模型模式中设置,并与Database
方法:insert()
、insertOne()
、replaceBulk()
、replaceId()
、replaceOne()
、update()
、updateBulk()
、updateId()
和updateOne()
一起使用。
class UserLog extends Model { const DBS = 'default$app$users.log'; public static function &schema(): Schema { return parent::schema([ '$refs' => [ 'fk' => [ // collection => [localField => foreignField, ...] 'users' => ['userId' => 'id'] ] ], 'id' => ['string', 'id'], 'userId' => ['string', 'notEmpty'], 'message' => ['string', 'notEmpty'] ]); } }
users.log
中的示例文档
{
"id": "abc",
"userId": "62ba4fd034faaf6fc132ef54",
"message": "test"
}
现在当调用模型数据库的插入/替换/更新方法时,上述$refs.fk
约束将验证集合users.log
的字段userId
值是否存在为users
集合字段id
(_id
)的外键。
如果外键约束验证失败,将抛出带有类似消息的
Lark\Database\Constraint\DatabaseConstraintException
异常:“插入或更新文档失败,外键约束“userId”失败”。
$refs.fk
外键字段(foreignField
)必须始终是MongoDBObjectId
,任何其他类型的外键验证都将失败。
$refs.fk
约束将始终验证外键,即使本地字段值为null
,但可以通过在本地字段名前使用nullable$
前缀来禁用此功能,例如nullable$userId
,这意味着所有本地字段null值将不会进行外键验证。
$refs.fk
约束还可以用于数组中的外键数组
// class UserGroup (model) $schema = new Schema([ '$refs' => [ 'fk' => [ // collection => [localField => foreignField, ...] 'users' => ['users.$' => 'id'] ] ], // ... ]);
users.groups
中的示例文档
{
"id": "abc",
"name": "group name",
"users": ["62ba4fd034faaf6fc132ef54", "62ba4fd034faaf6fc132ef55"]
}
现在当调用模型数据库的插入/替换/更新方法时,上述$refs.fk
约束将验证集合users.groups
字段users
数组中的每个值是否存在为users
集合字段id
(_id
)的外键。
$refs.fk
约束还可以用于数组中的具有外键的对象
// class UserAllowed (model) $schema = new Schema([ '$refs' => [ 'fk' => [ // collection => [localField => foreignField, ...] 'users' => ['users.$.id' => 'id'] ] ], // ... ]);
users.allowed
中的示例文档
{
"id": "abc",
"role": "admin"
"users": [
{"id": "62ba4fd034faaf6fc132ef54", "name": "test"}
{"id": "62ba4fd034faaf6fc132ef55", "name": "test2"}
]
}
现在当调用模型数据库的插入/替换/更新方法时,上述的$refs.fk
约束将验证集合users.allowed
字段的users
数组,以确保每个对象字段的id
值存在于users
集合的id
(_id
)字段中。
$refs.fk
约束可以与多个集合和字段一起使用。
$schema = new Schema([ '$refs' => [ 'fk' => [ // collection => [localField => foreignField, ...] 'users' => [ 'userId' => 'id', 'users.$' => 'id', 'usersAllowed.$.id' => 'id' ] ] ], // ... ]);
$refs.fk
约束也可以与同一模型一起使用。
// class User (model) $schema = new Schema([ '$refs' => [ 'fk' => [ // allow managerId to be null (no manager) // verify FK users.id exists when users.managerId exists 'users' => ['nullable$managerId' => 'id'] ] ], 'id' => ['string', 'id'], 'managerId' => 'string' ]);
清除约束
$refs.clear
约束允许清除字段值,可以在任何模型模式中设置,并与Database::deleteIds()
方法一起使用。
class User extends Model { const DBS = 'default$app$users'; public static function &schema(): Schema { return parent::schema([ '$refs' => [ 'clear' => [ // collection => [foreign fields] 'users.log' => ['userId'] ] ], 'id' => ['string', 'id'], 'name' => ['string', 'notEmpty'] ]); } }
users.log
中的示例文档
{
"id": "abc",
"userId": "62ba4fd034faaf6fc132ef54",
"message": "test"
}
现在当调用模型数据库方法deleteIds()
时,上述的$refs.clear
约束将触发数据库清除(更新操作),以清除users.log
集合中所有具有userId: {$in: [ids]}
的文档的userId
字段。
MongoDB shell中的等效操作为
db.users.delete( { _id: { $in: [ids] } } ) db.users.log.updateMany( { userId: { $in: [ids] } }, { $set: { userId: null } } )
删除约束
$refs.delete
约束允许删除文档,可以在任何模型模式中设置,并与Database::deleteIds()
方法一起使用。
class User extends Model { const DBS = 'default$app$users'; public static function &schema(): Schema { return parent::schema([ '$refs' => [ 'delete' => [ // collection => [foreign fields] 'users.log' => ['userId'] ] ], 'id' => ['string', 'id'], 'name' => ['string', 'notEmpty'] ]); } }
users.log
中的示例文档
{
"id": "abc",
"userId": "62ba4fd034faaf6fc132ef54",
"message": "test"
}
现在当调用模型数据库方法deleteIds()
时,上述的$refs.delete
约束将触发数据库删除操作,删除users.log
集合中所有具有userId: {$in: [ids]}
的文档。
MongoDB shell中的等效操作为
db.users.delete( { _id: { $in: [ids] } } ) db.users.log.delete( { userId: { $in: [ids] } } )
$refs.delete
约束也可以用于从数组中拉取($pullAll
) ID。
$schema = new Schema([ '$refs' => [ 'delete' => [ // collection => [foreign fields] 'users.groups' => ['users.$'] ] ], // ... ]);
users.groups
中的示例文档
{
"id": "abc",
"name": "group name",
"users": ["62ba4fd034faaf6fc132ef54", "62ba4fd034faaf6fc132ef55"]
}
现在当调用模型数据库方法deleteIds()
时,上述的$refs.delete
约束将触发数据库更新操作,从集合users.groups
字段的users
中拉取所有ID。
MongoDB shell中的等效操作为
db.users.delete( { _id: { $in: [ids] } } ) db.users.groups.updateMany( { users: { $in: [ids] } }, { $pullAll: { users: [ids] } }, { multi:true } )
注意:即使在一个文档的字段上从数组中拉取了多个值,MongoDB仍然会返回
modifiedCount: 1
。
$refs.delete
约束还可以用于根据对象字段值从数组中拉取($pull
)对象。
$schema = new Schema([ '$refs' => [ 'delete' => [ // collection => [foreign fields] 'users.allowed' => ['users.$.id'] ] ], // ... ]);
users.allowed
中的示例文档
{
"id": "abc",
"role": "admin"
"users": [
{"id": "62ba4fd034faaf6fc132ef54", "name": "test"}
{"id": "62ba4fd034faaf6fc132ef55", "name": "test2"}
]
}
现在当调用模型数据库方法deleteIds()
时,上述的$refs.delete
约束将触发数据库更新操作,从集合users.groups
字段的users
中根据对象字段id
值拉取所有对象。
MongoDB shell中的等效操作为
db.users.delete( { _id: { $in: [ids] } } ) db.users.allowed.updateMany( { users.id: { $in: [ids] } }, { $pull: { users: { id: { $in: [ids] } } } }, { multi:true } )
注意:即使在一个文档的字段上从数组中拉取了多个对象,MongoDB仍然会返回
modifiedCount: 1
。
$refs.delete
约束可以与多个集合和字段一起使用。
$schema = new Schema([ '$refs' => [ 'delete' => [ // collection => [foreign fields] 'users.log' => ['userId', 'userId2'], 'users.groups' => ['users.$'], 'users.allowed' => ['users.$.id'] ] ], // ... ]);
验证器
Lark\Validator
用于验证和创建实体。
use Lark\Validator; $isValid = (new Validator([ // data 'name' => 'Bob', 'age' => 25 ], [ // schema 'name' => ['string', 'notEmpty'], 'age' => ['int', 'notNull'], 'phone' => null, // no type (any type allowed), optional 'title' => 'string' // string, optional ]))->validate(); // true
可以在验证过程中使用断言。
(new Validator([ 'name' => null ], [ 'name' => ['string', 'notNull'] ]))->assert(); // throws Lark\Validator\ValidatorException: // Validation failed: "name" must be a string
使用验证创建实体。
// validation will pass because no field is required var_dump( (new Validator([], [ 'name' => ['string'], 'age' => ['int'] ]))->make() ); // array(2) { ["name"]=> NULL ["age"]=> NULL }
验证类型与规则
规则notNull
和notEmpty
,以及有时id
,是不允许值null
的所有类型的规则。规则voidable
可以用于任何可能缺失的字段。
- 任何类型(默认)- 允许任何类型
notNull
- 值不能为null
array
(或arr
)- 值可以是array
或null
allowed
- 数组值必须是允许的[allowed => [...]]
length
- 数组项数必须是[length => x]
max
- 数组值不能超过最大值[max => x]
min
- 数组值不能低于最小值[min => x]
notEmpty
- 必须是非空array
notNull
- 必须是array
unique
- 数组值必须是唯一的
boolean
(或bool
)- 必须是boolean
或null
notNull
- 必须是boolean
datetime
- 必须是DateTime
实例或null
notNull
- 必须是DateTime
实例
dbdatetime
- 必须是MongoDB\BSON\UTCDateTime
实例或null
notNull
- 必须是MongoDB\BSON\UTCDateTime
实例
float
- 必须是float
或null
between
- 必须在两个值之间[between => [x, y]]
max
- 必须是最大值[max => x]
min
- 必须是最小值[min => x]
notEmpty
- 必须是大于零的float
notNull
- 必须是float
integer
(或int
) - 必须是integer
或null
between
- 必须在两个值之间[between => [x, y]]
id
- 当设置ENTITY_FLAG_ID
标志时,必须是integer
max
- 必须是最大值[max => x]
min
- 必须是最小值[min => x]
notEmpty
- 必须是大于零的integer
notNull
- 必须是integer
number
(或num
) - 必须是数字或null
between
- 必须在两个值之间[between => [x, y]]
id
- 当设置ENTITY_FLAG_ID
标志时,必须是数字max
- 必须是最大值[max => x]
min
- 必须是最小值[min => x]
notEmpty
- 必须是大于零的数字notNull
- 必须是数字
object
(或obj
) - 必须是object
或null
notEmpty
- 必须是非空的object
notNull
- 必须是object
string
(或str
) - 必须是string
或null
allowed
- 值必须是允许的[allowed => [...]]
alnum
- 必须只包含字母数字字符- 或,必须只包含字母数字字符和空格
[alnum => true]
alpha
- 必须只包含字母字符- 或,必须只包含字母字符和空格
[alpha => true]
contains
- 必须包含值[contains => x]
- 或,必须包含值(不区分大小写)
[contains => [x, true]]
email
- 必须是有效的电子邮件地址hash
- 哈希值必须相等(时间攻击安全)[hash => x]
id
- 当设置ENTITY_FLAG_ID
标志时,必须是string
ipv4
- 必须是有效的 IPv4 地址ipv6
- 必须是有效的 IPv6 地址json
- 必须是有效的 JSONlength
- 长度必须是字符数[length => x]
regex
- 值必须是正则表达式匹配[regex => x]
max
- 长度必须是最大字符数[max => x]
min
- 长度必须是最小字符数[min => x]
notAllowed
- 值必须是允许的[notAllowed => [...]]
notEmpty
- 必须是非空的string
notNull
- 必须是string
password
- 密码必须匹配[password => x]
url
- 必须是有效的 URL
timestamp
- 必须是时间戳或null
notNull
- 必须是时间戳
嵌套字段
可以使用 fields
属性定义嵌套字段。
$isValid = (new Validator([ // data 'name' => 'Bob', 'contact' => [ 'email' => 'bob@example.com', 'phone' => [ 'cell' => '555-5555', 'office' => '555-6666' ] ] ], [ // schema 'name' => ['string', 'notEmpty'], 'contact' => [ 'array', [ 'fields' => [ 'email' => ['string', 'email'], 'phone' => [ 'array', [ 'fields' => [ 'cell' => 'string', 'office' => 'string' ] ] ] ] ] ] ]))->validate(); // true
嵌套模式
可以使用 schema:array
或 schema:object
属性定义数组数组的嵌套模式或对象。
$isValid = (new Validator([ 'name' => 'test', 'tags' => [ // these must be arrays because "schema:array" is used // if "schema:object" is used these must be objects ['id' => '1', 'name' => 'test2'], ['id' => 2, 'name' => 'test3'], ] ], [ 'name' => ['string', 'notEmpty'], 'tags' => [ 'array', 'notEmpty', [ 'schema:array' => [ 'id' => ['int', 'notNull'], 'name' => 'string' ] ] ] ]))->assert(); // throws Lark\Validator\ValidatorException: // Validation failed: "tags.0.id" must be an integer or null
在上面的示例中,如果
schema:array
或schema:object
属性之前没有使用notEmpty
模式规则,并且数组或对象数组为空,则不会验证/断言任何规则。
不允许在嵌套模式对象或数组中包含部分文档。
断言回调
可以使用 assert()
方法与回调一起使用。
(new Validator([ 'name' => null ], [ 'name' => ['string', 'notNull'] ]))->assert(function(string $field, string $message, string $name = null){ // handle error //... // return true to halt // return false to continue to throw validation exception return true; });
自定义验证规则
可以创建自定义验证规则。
// validator.rule.[type].[name] app()->use('validator.rule.string.beginWithEndWith', App\Validator\BeginWithEndWith::class); // App\Validator\MyRule class: namespace App\Validator; class BeginWithEndWith extends \Lark\Validator\Rule { private string $beginWith; private string $endWith; protected string $message = 'must begin with value and end with value'; public function __construct(string $beginWith, string $endWith) { $this->beginWith = $beginWith; $this->endWith = $endWith; } public function validate($value): bool { $beginsWith = substr($value, 0, strlen($this->beginWith)); $endsWith = substr($value, -(strlen($this->endWith))); return $beginsWith === $this->beginWith && $endsWith === $this->endWith; } } // validation example (new Validator([ 'alias' => '123testXYZ' ], [ 'alias' => ['string', ['beginWithEndWith' => ['123', 'XYZ']]] ]))->validate(); // true
也可以覆盖现有规则。
// validator.rule.[type].[name] // overwrite existing string rule "email" app()->use('validator.rule.string.email', 'App\\Validator\\Email'); // App\Validator\Email class: namespace App\Validator; class Email extends \Lark\Validator\TypeString\Email { public function validate($value): bool { // must be valid email and domain "example.com" return parent::validate($value) && preg_match('/@example\.com$/i', $value) === 1; } } // validation example (new Validator([ 'email' => 'test@example.com' ], [ 'email' => ['string', 'email'] ]))->validate(); // true
过滤器
Lark\Filter
用于过滤值。
$cleanStr = filter()->string($str);
按数组键过滤。
$arr = ["one" => 1, "two" => 2, "three" => 3]; // exclude filter print_r( filter()->keys($arr, ["two" => 0]) ); // Array ( [one] => 1 [three] => 3 ) // include filter print_r( filter()->keys($arr, ["one" => 1, "two" => 1]) ); // Array ( [one] => 1 [two] => 2 )
过滤方法
email($value, array $options = []): string
- 使用电子邮件过滤器净化值float($value, array $options = ['flags' => FILTER_FLAG_ALLOW_FRACTION]): float
- 使用浮点过滤器对值进行清理integer($value, array $options = []): int
- 使用整数过滤器对值进行清理keys(array $array, array $filter): array
- 根据包含或排除过滤器过滤键string($value, array $options = ['flags' => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH]): string
- 使用字符串过滤器对值进行清理url($value, array $options = []): string
- 使用URL过滤器对值进行清理
HTTP 客户端
Lark\Http\Client
是一个HTTP客户端。
use Lark\Http\Client; $client = new Client; try { $res = $client->get('http://example.com'); $headers = $client->headers(); $statusCode = $client->statusCode(); if($statusCode === 200) { // ok } else { // handle } } catch (Lark\Http\HttpException $ex) { // handle request/curl error }
提供各种HTTP方法。
// DELETE request $client->delete('http://example.com', ['field1' => 'value']); // GET request $client->get('http://example.com', ['param' => 1]); // http://example.com?param=1 // HEAD request $client->head('http://example.com'); // OPTIONS request $client->options('http://example.com'); // PATCH request $client->patch('http://example.com', ['field1' => 'value']); // POST request $client->post('http://example.com', ['field1' => 'value']); // PUT request $client->put('http://example.com', ['field1' => 'value']);
字符串也可以用来发送JSON。
$client = new Client([ 'headers' => ['content-type' => 'application/json'] ]); // POST request with JSON string $client->post('http://example.com', json_encode(['field1' => 'value']));
可以为所有方法设置选项(将覆盖默认选项)。
use Lark\Http\Client; $client = new Client(['url' => 'http://example.com', 'timeout' => 8]); $res = $client->get('/api/items'); // http://example.com/api/items $res2 = $client->post('/api/items', ['name' => 'My Item']);
可以为单个方法设置选项(将覆盖所有方法的默认选项和选项)。
$res = $client->get('/api/items', ['timeout' => 5]);
可以为 curl
设置选项。
use Lark\Http\Client; $client = new Client([ 'curl' => [ CURLOPT_RESOLVE => ['test.loc:127.0.0.1'] ] ]);
HTTP客户端选项
curl
- 使用CURLOPT_[...]
选项设置curl
选项headers
- 设置HTTP头,可以使用两种方法设置['headers' => ['My-Header' => 'value']]
['headers' => ['My-Header: value']]
port
- 设置自定义端口号proxy
- 使用HTTP代理redirects
- 允许重定向timeout
- 连接和执行的秒数超时url
- 请求方法的基URLverify
- 验证对等证书和通用名称
命令行界面
Lark\Cli
用于创建命令行应用程序。
// bootstrap // ... $cli = Lark\Cli::getInstance(); // add command $cli->command('files', 'Print files in directory') ->arg('dir', 'Read directory') ->action(function(string $dir) { // print files in directory $dir // optional, exit with any code by returning an int // return 1; // same as $cli->exit(1); }); // or use class/method: // ->action([MyClass::class, 'methodName']) // run app $cli->run($_SERVER['argv']);
可以为命令设置参数和选项,每个参数和选项都有可选设置。
// set global option (separate from command options) $cli->option('-d, --debug', 'Enable debug mode', function() { // enable here }); $cli->command('files', 'Print files in directory') ->arg('dir', 'Read directory') // required by default // set another optional argument that can have multiple values (array) ->arg('subdirs', 'Read subdirectories', ['optional', 'array']) // add option for output file ->option('-o, --outputfile', 'Output to file') // option test ->option('-t, --test', 'Run test', ['optional']) // add command action ->action(function(string $dir, ?array $subdirs, ?string $outputfile, ?bool $isTest) { var_dump($dir, $subdirs, $outputfile, $isTest); }); // $ php ./app/cli.php files mydir subdir1 subdir2 --outputfile=/my/file -t // string(5) "mydir" // array(2) { [0] => string(7) "subdir1" [1] => string(7) "subdir2" } // string(8) "/my/file" // bool(true)
CLI Lark\Cli\Output
类用于输出和样式化输出。
$o = $cli->output(); // output green text $o->colorGreen->echo('This is green text'); // use multiple styles $o->colorBlue->styleUnderline->echo('More text'); // style methods for common styles $o->error('Error'); // red background $o->info('Info'); // blue text $o->ok('Success'); // green text $o->warn('Warning'); // yellow text $o->dim('Muted'); // dim text // custom style methods can be registered $o::register('blink', function ($text, $end = PHP_EOL) use ($out) { $out->styleBlink; $out->echo($text, $end); }); $o->bink('Blinking text'); // blinking text // override existing style methods $o::register('error', function ($text, $end = PHP_EOL) use ($out) { $out->colorRed; // text color red (instead of bg red) $out->stderr($text, $end); // send to stderr }); $o->error('Oops'); // red text
可以使用 grid()
输出方法来均匀地间隔列。
$data = [ [1, "one"], [2, "two"], [100, "one hundred"], [3, "three"], ]; $out->grid($data, ['indent' => 2]);
上面的例子将输出
1 one
2 two
100 one hundred
3 three
使用 confirm()
进行确认。
// "Continue? (y/N)" if($cli->confirm("Continue?")) // ... // or yes by default: "Continue? (Y/n)" if($cli->confirm("Continue?", true)) // ...
使用 input()
进行输入。
// "Enter value [DEFAULT]:" $val = $cli->input("Enter value:", "DEFAULT"); // if no value is entered the value would be "DEFAULT"
CLI方法
abort($status = 0)
- 显示命令中止消息并退出应用程序command(string $name, string $description, array $aliases = []): Command
- 注册命令confirm(string $question, bool $isDefaultYes = false)
- 确认是/否exit($status = 0)
- 退出应用程序header(callable $callback)
- 注册用于help()
方法的头部回调help()
- 显示帮助(由Cli
自动调用)input(string $text, $default = null)
- 输入option(string $option, string $description, callable $action)
- 设置全局选项output()
- CLIOutput
对象获取器run()
- 运行CLI应用程序
CLI命令方法
action($callbackOrClassArray): Command
- 设置命令操作arg(string $arg, string $description, array $options = []): Command
- 设置参数- 选项
array
- 具有多个值的参数(必须放在参数列表的最后一个位置)default
- 默认值,例如:['default' => 'the value']
optional
- 参数是可选的
- 选项
option(string $option, string $description = '', array $options = []): Command
- 设置选项- 选项
default
- 默认值,例如:['default' => 'the value']
- 选项
CLI输出属性
bgBlack
- 设置背景为黑色bgBlue
- 设置背景为蓝色bgCyan
- 设置背景为青色bgGray
- 设置背景为灰色bgGreen
- 设置背景为绿色bgPurple
- 设置背景为紫色bgRed
- 设置背景为红色bgWhite
- 设置背景为白色bgYellow
- 设置背景为黄色bgLigthBlue
- 设置背景为浅蓝色bgLightCyan
- 设置背景为浅青色bgLightGray
- 设置背景为浅灰色bgLightGreen
- 设置背景为浅绿色bgLightPurple
- 设置背景为浅紫色bgLightRed
- 设置背景为浅红色bgLightYellow
- 设置背景为浅黄色colorBlack
- 设置颜色为黑色colorBlue
- 设置颜色为蓝色colorCyan
- 设置颜色为青色colorGray
- 设置颜色为灰色colorGreen
- 设置颜色为绿色colorPurple
- 设置颜色为紫色colorRed
- 设置颜色为红色colorWhite
- 设置颜色为白色colorYellow
- 设置颜色为黄色colorLightBlue
- 设置颜色为浅蓝色colorLightCyan
- 设置颜色为浅青色colorLightGray
- 设置颜色为浅灰色colorLightGreen
- 设置颜色为浅绿色colorLightPurple
- 设置颜色为浅紫色colorLightRed
- 设置颜色为浅红色colorLightYellow
- 设置颜色为浅黄色styleBlink
- 设置闪烁效果styleBold
- 设置加粗效果styleDim
- 设置暗淡效果styleHidden
- 设置隐藏效果styleInvert
- 设置反转效果styleUnderline
- 设置下划线效果
CLI 输出方法
dim(string $text, string $end = PHP_EOL): Output
- 打印暗淡风格文本echo(string $text = '', string $end = PHP_EOL): Output
- 将文本打印到 stdouterror(string $text, string $end = PHP_EOL): Output
- 打印错误文本grid(array $data, array $options = []): Output
- 打印网格- 选项
indent
- 缩进空格数padding
- 列填充(默认:4
)style
- 将样式应用于列,如['style' => ['name' => 'colorBlue']]
- 选项
info(string $text, string $end = PHP_EOL): Output
- 打印信息文本ok(string $text, string $end = PHP_EOL): Output
- 打印成功文本warn(string $text, string $end = PHP_EOL): Output
- 打印警告文本static register(string $name, callable $callback)
- 注册样式方法stderr(string $text, string $end = PHP_EOL): Output
- 输出到 stderrstdout(string $text = '', string $end = PHP_EOL): Output
- 输出到 stdoutstyleIndent(int $number): Output
- 缩进样式
文件
Lark\File
用于处理文件。
use Lark\File; $file = new File('./my-file.txt'); if($file->write('contents')) { // ... } $contents = $file->read();
Lark\Json\File
用于处理 JSON 文件。
use Lark\Exception as LarkException; use Lark\Json\File as JsonFile; $file = new JsonFile('./my-file.json'); $file->write(['name' => 'test']); try { $value = $file->read(); } catch(LarkException $ex) { // exception is throw on JSON decode error echo 'Failed to decode JSON file: ' . $ex->getMessage(); }
文件方法
delete(): bool
- 删除文件exists(): bool
- 检查文件是否存在existsOrException()
- 如果文件不存在则抛出异常path(): string
- 获取文件路径read()
- 读取文件内容write($data, $append = false, $lock = true): bool
- 写入文件内容
计时器
Lark\Timer
作为计时器使用。
$timer = new Lark\Timer; usleep(500000); echo $timer->elapsed(); // 0.5001s sleep(1); echo $timer->elapsed(); // 1.5014s sleep(2); // get elapsed since last Timer::elapsed() // or Timer::elapsedSinceLast() was invoked echo $timer->elapsedSinceLast(); // 2.0003s echo $timer->elapsed(); // 3.5018s
助手
助手是全局助手函数。
助手 app()
使用 app()
函数访问主 App
实例。
app()->use('[...]');
助手 db()
db()
函数是数据库集合实例助手。
// when using default connection ID // "[database]$[collection]" $db = db('app$users'); // or "database", "collection" $db = db('app', 'users'); // when using non-default connection ID // "[connectionId].[database].[collection]" $db = db('myDb$app$users') // or "connectionId", "database", "collection" $db = db('myDb', 'app', 'users'); // when using a App\Model class with DBS (database string) $db = db(App\Model\User::class);
更多请参阅 数据库连接。
助手 dbdatetime()
dbdatetime()
函数返回一个 MongoDB\BSON\UTCDateTime
对象。
$dbDt = dbdatetime(); $dt = $dbDt->toDateTime(); // DateTime object // with milliseconds $dbDt = dbdatetime(strtotime('-1 day') * 1000);
助手 debug()
debug()
函数是调试和日志助手。当调用 debug()
函数时,将附加调试信息并发送到日志记录器 (Logger::debug()
)。
debug('test'); // same as: // Debugger::append('test'); // (new Logger)->debug('test'); // title/name can be used: debug('test', ['info' => 'here'); // same as: // Debugger::append(['info' => 'here'])->name('test'); // (new Logger)->debug('test', ['info' => 'here']); // title/name and group/channel can be used: debug('test', ['info' => 'here'], 'name'); // same as: // Debugger::append(['info' => 'here'])->name('test')->group('name'); // (new Logger('name'))->debug('test', ['info' => 'here']);
另请参阅
x()
助手函数。
助手 env()
env()
函数是环境变量助手。
$dbName = env('DB_NAME');
更多请参阅 环境变量 & 配置。
助手 f()
f()
函数返回一个格式化的字符串。
echo f('First value: {}, second value: {}', 'one', 'two'); // First value: one, second value: two // placeholder names can be used echo f('Name: {name}, age: {age}', 'Test', 25); // Name: Test, age: 25 // array keys and placeholder names can be used // regardless of key/value order echo f('Name: {name}, age: {age}', ['age' => 25, 'name' => 'Test']); // Name: Test, age: 25
助手 halt()
halt()
函数可用于立即返回 HTTP 响应状态代码和可选的 JSON 消息。
halt(404, 'Resource not found'); // returns HTTP response status code 404 // with JSON body {"message": "Resource not found"} // use custom JSON response halt(500, ['error' => 'message', 'context' => ['test']]); // halt without message halt(500);
助手 logger()
使用 logger()
函数获取 Logger
实例。
logger('channel')->info('message', ['context']);
有关日志记录的更多信息,请参阅日志记录。
辅助函数 p()
p()
函数输出格式化的(HTML/CLI)变量。
p('test', 'this'); p(['my' => 'array']);
辅助函数 pa()
pa()
函数是一个变量打印器。
pa('test', 'this', ['and' => 'this'], 'end'); // test this // Array // ( // [and] => this // ) // end
辅助函数 req()
使用 req()
函数访问 Lark\Request
实例。
var_dump( req()->path() ); // string(1) "/"
有关请求的更多信息,请参阅请求。
辅助函数 res()
使用 res()
函数访问 Lark\Response
实例。
res()->contentType('application/json');
有关响应的更多信息,请参阅响应。
辅助函数 router()
使用 router()
函数访问 Lark\Router
实例。
router()->get('/', function() { return 'home'; });
有关路由的更多信息,请参阅路由。
辅助函数 x()
x()
函数是一个调试器和转储辅助工具。当调用 x()
函数(或 Lark\Debugger::dump()
)时,将转储所有调试器信息对象并停止执行。
x('value', ['test' => 'this']);
还可以查看
debug()
辅助函数。
有关调试器的更多信息,请参阅调试器。