# Yls **Repository Path**: focrs/Yls ## Basic Information - **Project Name**: Yls - **Description**: 嗨,我的名字叫叶绿素,英文名字Y-e-s,是一个用世界上最好的语言实现的服务端API框架,不是MVC式或RESTful式的实现,只是为灵活简约式开发提供另一种可选办法,也许你会喜欢也说不定的哦! - **Primary Language**: PHP - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2017-06-22 - **Last Updated**: 2022-05-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #Yls Y-e-s, 一种愉悦的开发方式。 > - [Yls印象](#Yls印象) > - [api.php文件](#api-php文件) > -     [数组返回](#数组返回) > -     [函数式返回](#函数式返回) > - [版本控制](#版本控制) > - 数据库 > -     [链式操作](doc/db.md) > -     [CURD](doc/curd.md) > -     [条件构造器](doc/condition.md) > - [数据验证](doc/validate.md) > - [未来草稿](#未来草稿) ## Yls印象 Yls客户端和服务端均采用JSON格式数据进行传输。使用Yls,你的API组织结构看起来像这样: ``` 1.0 |-user |-list |-api.php |-@id |-api.php |-like |-api.php |-@like.id |-api.php |-avatar |-api.php |-message |-api.php |-search |-api.php 2.0 |-user |-api.php // 2.0版对user接口进行了增量改写 |-a 3.0 |-search |-api.php // 3.0版对search接口进行了增量改写 ``` 你将可以像这样调用API: ```php require '../Yls/Core/Client.php'; $url = 'http://yls.focrs.com/demo/index.php'; // Yls服务地址 $id = 'web'; // Yls分配的账号 $key = '29d03b2cd1df263770ead73c6e541322'; // Yls分配的私钥 $version = '1.0'; // API版本号(统一设置) $Client = new Yls\Core\Client($url, $id, $key, $version); // 单个API查询 list($list) = $Client->Query([ 'api' => 'user/list:read', // 调用 /user/api.php中的read()方法 ]); // 带参数的单API查询 list($list) = $Client->Query([ 'api' => 'user/list:read', 'param' => ['name' => ['like' =>'Yls']] // 请求user中name包含Yls的记录 ]); // 多参数的API查询 list($list) = $Client->Query([ 'api' => 'user/list:read', 'param' => ['gender' => 'male', 'age' => ['gt' => 18]] // 请求user中gender为male,age大于18的记录 ]); // @魔法映射 list($user) = $Client->Query([ // 此时API目录中并没有user/1的目录,此时会自动映射到调用/user/@id, 'api' => 'user/1:read' // 同时@id的值将自动变成1保存在Yls中,通过Yls::Instance()->At('id')可访问 ]); // 深层@魔法映射 list($result) = $Client->Query([ // 调用/user/@id/like/@like.id/api.php中的read()方法, 'api' => 'user/1/like/10:read' // 同时@id(=1)和@like.id(=10)的值分别通过Yls::Instance()->At('id') ]); // 和Yls::Instance()->At('like.id')可访问 // 多API联查,减少网络请求次数,一次性搞定数据查询 list($user, $message) = $Client->Query([ 'api' => 'user/1:read', 'param' => ['page' => 2] ], [ 'api' => 'user/1/message:read', 'version' => '2.0' // API内也可以自由指定版本号 ]); // Api参数预绑定查询,API联查时,提供类似数据库存储过程中的预定于变量功能 list($user, $search, ...) = $Client->Query([ 'api' => 'user/1:read', 'bind' => ['::name' => '.name'] // 将用户name属性绑定到::name中 ], [ 'api' => 'search:read', 'param' => ['keyword' => '::name'] // 将上一步Api获取的::name值传给当前Api ], ...); // API级事务机制 list($user, $avatar) = $Client->Query( 'atomic' => true, // 原子操作 'query' => [[ 'api' => 'user/1:update', 'param' => ['name' => 'Yls'] ], [ 'api' => 'user/1/avatar:update', 'param' => ['avatar' => 'http://t.cn/RoCIvbk'] // 头像很美 ]] ); ``` ## api.php文件 每个目录下的api.php文件包含了当前路由下的对外接口CURD方法(create, update, read, delete)的实现,每个方法都应该有返回值,即使没有返回数据,也应该有一个是否处理成功的bool值。 ### 数组返回 ```php return [ 'create' => function() { return $result; }, 'update' => function() { return $result; }, 'read' => function() { return $result; }, 'delete' => function() { return $result; } ]; ``` ### 函数式返回 ```php return function() { // 可在此匿名函数内部定义一些共用方法 $GetInfo = function() { return ['id' => 1, 'name' => 'Yls']}; return [ 'create' => function() { return $result; }, 'update' => function() { return $result; }, 'read' => function() { return $GetInfo(); }, 'delete' => function() { return $result; } ]; }; ``` 这种方式是为了考虑同一api.php文件中实现CURD操作会有大概率的代码冗余,而使用匿名函数可以在内部自由定义一些公共匿名方法,从而达到精简代码的目的。可能有同学会问,这里把api.php定义成一个类不就能解决这些问题吗?比如: ```php class Api { function create() { return $result; }, function update() { return $result; }, ... } ``` 但是在类的命名问题上犯难了:每个类名必须不能重复,这破坏了设计上的一致性规则,而且即便引入命名空间解决了冲突问题,但又无法引入@魔法功能,所以干脆通过匿名函数或直接return来解决取名老大难的问题啦!绝对没有搞事情的意思 :tw-1f62b: ## 版本控制 Yls通过增量性覆盖达到调用最新接口的目的,Yls会根据客户端发起的请求找到相应的Api版本,如果在当前版本找不到请求的接口,则会尝试去上一个版本中找,以此类推向上回溯,直到找到对应的接口。 ## 未来草稿 未来会实现的一些功能特性,备忘。 - DB链式操作; - CURD原子操作; - 接口参数约束验证; - 查询条件自动拼接; ### 查询条件自动拼接 两个数据库:web, setting, 配置文件db部分配置如下: ```php 'db' => [ 'default' => [ // 默认连接配置 'dsn' => 'mysql:dbname=yls;host=127.0.0.1;charset=utf8', 'username' => '******', 'password' => '******', 'prefix' => 'Y', // 表前缀 'create' => 'created:datetime', // 创建记录时将字段created自动赋值为当前时间(YYYY-mm-dd HH:ii:ss) 'update' => 'updated:datetime' // 更新记录时将字段updated更新为当前时间(YYYY-mm-dd HH:ii:ss) ], 'focrs' => [ // 北京机房连接 'dsn' => 'mysql:dbname=focrs;host=127.0.0.1;charset=utf8', 'username' => '******', 'password' => '******', 'prefix' => 'F', 'create' => 'created:timestamp', // 创建记录时将字段created自动赋值为当前UNIX时间戳(1501059706) 'update' => 'updated:timestamp' // 更新记录时将字段updated更新为当前UNIX时间戳(1501059706) ], ], ``` | 数据库 | 表 | 说明 | | -------- | -----: | ----: | | yls | YUser | 对应default配置 | | focrs | FUser | 对应focrs配置 | ``` 客户端发起请求 ```php list($list) = $Client->Query([ 'api' => 'user/list:read', 'param' => ['NameSearch' => 'Yls'] // 构造条件: YUser.name LIKE "%Yls%" OR YUser.nick LIKE "%Yls%" ]); ``` DB插入或更新以数组方式传递的数据时,为区分特殊字段使用内置函数或特性而不应该自动加双引号,应将其表达式用{}包裹起来 ```php $data = [ 'id' => 1, 'hit' => '{hit + 1}', 'timestamp' => 'NOW()' ]; Yls::Db()->From('~News')->update($data); ``` ### Auth2.0统一登录授权 [http://www.imooc.com/learn/557](http://www.imooc.com/learn/557) 发送登录信息(name,password)到登录验证接口,成功则返回token,获取当前用户信息需进一步调用 /my/info/token/012AF3B==接口, ``` Client | Server ------------------------------------|-------------------------------------------- POST /login { | name: 'Yls', | password: 'y-e-s' | } ==> | 返回包含用户id和时间戳的加密token | <== token: 1E23F0A2 GET /my?token=1E23F0A2 ==> | 返回用户基本信息 | <== {id: 1, name: 'Yls', avatar: '/path/to/avatar.jpg', ...} PUT /my/avatar?token=1E23F0A2 ==> | 返回更新成功后的用户头像地址 | <== {avatar: '/new/path/to/avatar.jpg'} ```