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.cominput(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/pathpathWithQueryString(): string- 获取带查询字符串的路径,如/the/path?x=1port(): int- 获取端口号query(string $key, $default = null): Lark\Request\Query- 获取GET的查询输入对象queryString(): string- 获取查询字符串,如x=1&y=2scheme(): string- 获取 URI 方案,如httpsession(): 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或nullallowed- 数组值必须是允许的[allowed => [...]]length- 数组项数必须是[length => x]max- 数组值不能超过最大值[max => x]min- 数组值不能低于最小值[min => x]notEmpty- 必须是非空arraynotNull- 必须是arrayunique- 数组值必须是唯一的
boolean(或bool)- 必须是boolean或nullnotNull- 必须是boolean
datetime- 必须是DateTime实例或nullnotNull- 必须是DateTime实例
dbdatetime- 必须是MongoDB\BSON\UTCDateTime实例或nullnotNull- 必须是MongoDB\BSON\UTCDateTime实例
float- 必须是float或nullbetween- 必须在两个值之间[between => [x, y]]max- 必须是最大值[max => x]min- 必须是最小值[min => x]notEmpty- 必须是大于零的floatnotNull- 必须是float
integer(或int) - 必须是integer或nullbetween- 必须在两个值之间[between => [x, y]]id- 当设置ENTITY_FLAG_ID标志时,必须是integermax- 必须是最大值[max => x]min- 必须是最小值[min => x]notEmpty- 必须是大于零的integernotNull- 必须是integer
number(或num) - 必须是数字或nullbetween- 必须在两个值之间[between => [x, y]]id- 当设置ENTITY_FLAG_ID标志时,必须是数字max- 必须是最大值[max => x]min- 必须是最小值[min => x]notEmpty- 必须是大于零的数字notNull- 必须是数字
object(或obj) - 必须是object或nullnotEmpty- 必须是非空的objectnotNull- 必须是object
string(或str) - 必须是string或nullallowed- 值必须是允许的[allowed => [...]]alnum- 必须只包含字母数字字符- 或,必须只包含字母数字字符和空格
[alnum => true] alpha- 必须只包含字母字符- 或,必须只包含字母字符和空格
[alpha => true] contains- 必须包含值[contains => x]- 或,必须包含值(不区分大小写)
[contains => [x, true]] email- 必须是有效的电子邮件地址hash- 哈希值必须相等(时间攻击安全)[hash => x]id- 当设置ENTITY_FLAG_ID标志时,必须是stringipv4- 必须是有效的 IPv4 地址ipv6- 必须是有效的 IPv6 地址json- 必须是有效的 JSONlength- 长度必须是字符数[length => x]regex- 值必须是正则表达式匹配[regex => x]max- 长度必须是最大字符数[max => x]min- 长度必须是最小字符数[min => x]notAllowed- 值必须是允许的[notAllowed => [...]]notEmpty- 必须是非空的stringnotNull- 必须是stringpassword- 密码必须匹配[password => x]url- 必须是有效的 URL
timestamp- 必须是时间戳或nullnotNull- 必须是时间戳
嵌套字段
可以使用 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()辅助函数。
有关调试器的更多信息,请参阅调试器。