login代码说明及踩坑记录
一、 yaf官方文档
http://www.laruence.com/manual/index.html
二、 代码目录及简单说明
代码下载地址: https://download.csdn.net/download/xululu123/10959535
带nginx和php的环境
├── app 【代码目录】--done
│ ├── actions 【具体action】--done
│ │ ├── Login.php --done
│ │ └── Register.php--done
│ ├── Bootstrap.php 【Bootstrap, 也叫做引导程序】-- done
│ ├── controller【控制器】--done
│ │ └── Api.php --done
│ ├── library【本地类库】-done
│ │ ├── login-done
│ │ │ ├── BaseAction.php-done
│ │ │ └── Route.php-done
│ │ ├── params-done
│ │ │ └── Validate.php-done
│ │ └── utils--done
│ │ ├── Helper.php-done
│ │ └── Mysql.php-done
│ └── models【model目录 一些具体的逻辑实现】--done
│ ├── data --done
│ │ └── User.php --done
│ └── login-done
│ ├── Login.php --done
│ └── Register.php --done
├── conf【配置目录】 --done
│ ├── ap.ini --done
│ ├── mysql.ini --done
│ └── params --done
│ ├── login.ini --done
│ └── register.ini --done
├── index.php【入口文件】-done
└── webroot【静态资源 js css目录】 --done
├── css --done
│ ├── bootstrap.css
│ ├── bootstrap.css.map
│ ├── bootstrap-grid.css
│ ├── bootstrap-grid.css.map
│ ├── bootstrap-grid.min.css
│ ├── bootstrap-grid.min.css.map
│ ├── bootstrap.min.css
│ ├── bootstrap.min.css.map
│ ├── bootstrap-reboot.css
│ ├── bootstrap-reboot.css.map
│ ├── bootstrap-reboot.min.css
│ ├── bootstrap-reboot.min.css.map
│ └── login.css --done
├── js --done
│ ├── bootstrap.bundle.js
│ ├── bootstrap.bundle.js.map
│ ├── bootstrap.bundle.min.js
│ ├── bootstrap.bundle.min.js.map
│ ├── bootstrap.js
│ ├── bootstrap.js.map
│ ├── bootstrap.min.js
│ └── bootstrap.min.js.map
└── login.html --done
三、 处理流程概述
后端接口处理流程,
views相关,另外说明
四、代码详细说明
3.1从index说起
index文件是所有请求的入口, 一般都借助于rewrite规则, 把所有的请求都重定向到这个入口文件
<?php
define("APP_PATH", dirname(__FILE__));
$app = new Yaf_Application(APP_PATH . '/conf/ap.ini','xll');//首先进行框架的初始化,Yaf_Application代表一个产品/项目, 是Yaf运行的主导者, 真正执行的主题. 它负责接收请求, 协调路由, 分发, 执行, 输出 ,
$app->bootstrap()->run();// Bootstrap, 也叫做引导程序. 它是Yaf提供的一个全局配置的入口, 在Bootstrap中, 你可以做很多全局自定义的工作,稍后详细说明. 此函数调用是可选的,可以直接run
坑1:配置文件必须为.ini命名 .conf报初始化失败
坑2:nginx配置:(另外说明)
server {
listen 8885;
server_name #自己补充;
root /home/work/code/login/webroot;
location /login/ {
root /home/work/code/login/;
# root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
include fastcgi_params;
}
location ~ .*\.(html)$
{
expires 12d;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|ttf|woff|woff2|svg|eot)$
{
expires 30d;
}
location ~ .*\.(js|css)?$
{
expires 12h;
}
location = /50x.html {
root html;
}
}
知识点三:ap配置文件
Yaf和用户共用一个配置空间, 也就是在Yaf_Application初始化时刻给出的配置文件中的配置. 作为区别, Yaf的配置项都以ap开头. Yaf的核心必不可少的配置项只有一个
名称 | 值类型 | 说明 |
---|---|---|
application.directory | String | 应用的绝对目录路径 |
其余可选配置(大概有个印象即可) 见附录
知识点四 关于ap配置文件的两点说明区分环境
1、有的配置在php.ini中也有,本地的会覆盖php.ini的。
2、Yaf通过在不同的环境中, 选取不同的配置节, 再结合配置可继承, 来实现一套配置适应多种环境(线上,测试,开发)
例如下面的配置文件有两个配置节,可以在php.ini中通过yaf.environ来决定读取哪个配置节,实现线上线下不同配置生效。同样适用于普通配置文件
$app = new Yaf_Application(APP_PATH . ‘/conf/ap.ini’); 这里可以不指定哪个环境
[test]
application.directory = APP_PATH'/app/'
application.view.ext = html
[online]
application.directory = APP_PATH'/app/'
application.view.ext = php
3.2 bootstrap
Bootstrap, 也叫做引导程序. 它是Yaf提供的一个全局配置的入口, 在Bootstrap中, 你可以做很多全局自定义的工作(可选)
当bootstrap被调用的时刻, Yaf_Application就会默认的在APPLICATION_PATH下, 寻找Bootstrap.php, 而这个文件中, 必须定义一个Bootstrap类, 而这个类也必须继承自Yaf_Bootstrap_Abstract.
实例化成功之后, 所有在Bootstrap类中定义的, 以_init开头的方法, 都会被依次调用 而这些方法都可以接受一个Yaf_Dispatcher实例作为参数.
例如
<?php
class Bootstrap extends Yaf_Bootstrap_Abstract {
public function _initView(Yaf_Dispatcher $dispatcher) {
$dispatcher->disableView();
}
public function _initRoute(Yaf_Dispatcher $dispatcher) {
$router = $dispatcher->getRouter();
$router->addRoute('abc', new Login_Route());
}
public function _initDefault(Yaf_Dispatcher $dispatcher) {
$dispatcher->setDefaultModule("Index")->setDefaultController("Api")->setDefaultAction("Index");
}
}
上面的路由后面会单独讲解,一般bootstrap中还会注册一些插件,然后在适当的实际, Yaf就会调用它,另外说明
关于disable view
关闭自动Render. 默认是开启的, 在动作执行完成以后, Yaf会自动render以动作名命名的视图模板文件.,详细的会另外说明
3.3 路由
路由器主要负责解析一个请求并且决定什么module、controller、action被请求;它同时也定义了一种方法来实现用户自定义路由,这也使得它成为最重要的一个MVC组组件.
一旦路由成功,路由器将会把解析出得到的信息传递给请求对象(Yaf_Request_Abstract object), 这些信息包括moduel、controller、action、用户params等. 然后派遣器(Yaf_Dispatcher)就会按照这些信息派遣正确的控制器动作. 路由器也有插件钩子,就是routerStartup和routerShutdown,他们在路由解析前后分别被调用.
这里只说明默认路由和自定义路由
默认路由 ==
默认的路由协议Yaf_Route_Static, 就是分析请求中的request_uri, 在去除掉base_uri以后, 获取到真正的负载路由信息的request_uri片段, 具体的策略是, 根据"/"对request_uri分段, 依次得到Module,Controller,Action, 在得到Module以后, 还需要根据Yaf_Application::$modules来判断Module是否是合法的Module, 如果不是, 则认为Module并没有体现在request_uri中, 而把原Module当做Controller, 原Controller当做Action: (request_uri base_uri 这里存在一点疑问,和nginx配置有点关系,另外说明 )
<?php
/**
* 对于请求request_uri为"/ap/foo/bar/dummy/1"
* base_uri为"/ap"
* 则最后参加路由的request_uri为"/foo/bar/dummy/1"
* 然后, 通过对URL分段, 得到如下分节
* foo, bar, dummy, 1
* 然后判断foo是不是一个合法的Module, 如果不是, 则认为结果如下:
*/
array(
'module' => '默认模块',
'controller' => 'foo',
'action' => 'bar',
'params' => array(
'dummy' => 1,
)
)
/**
* 而如果在配置文件中定义了ap.modules="Index,Foo",
* 则此处就会认为foo是一个合法模块, 则结果如下
*/
array(
'module' => 'foo',
'controller' => 'bar',
'action' => 'dummy',
'params' => array(
1 => NULL,
)
)
当只有一段路由信息的时候, 比如对于上面的例子, 请求的URI为/ap/foo, 则默认路由和下面要提到的Yaf_Route_Supervar会首先判断ap.action_prefer, 如果为真, 则把foo当做Action, 否则当做Controller
自定义路由
万一现在所有的路由协议都不能满足你的需求, 那么你可以自己实现你自己的路由协议, 你要做的是, 申明你的路由协议实现了Yaf_Route_Interface接口即可. 这样会比较灵活方便
步骤1:添加自定义路由:
在bootstrap的代码中,见上
步骤2:具体路由实现:
根本就是设置controller和action
这里还做了一些参数处理
记住一定要return true ,否则不生效
<?php
class Login_Route implements Yaf_Route_Interface{
public function route($request) {
$path = $request->getRequestUri();
$action = substr($path, strrpos($path, '/')+1);
$request->setControllerName('api');
$request->setActionName($action);
Yaf_Registry::set('action', $action);
$query = $request->getQuery();
$post = $request->getPost();
$phpinputArr = json_decode(file_get_contents("php://input"), true);
if(!empty($phpinputArr)) {
$post = array_merge($post, $phpinputArr);
}
if(!empty($post) && isset($post['data'])) {
if(is_array($post)) {
$post = array_merge($post, $post['data']);
}
unset($post['data']);
}
$params = array_merge($query, $post);
Utils_Helper::setParams($params);
return true;
}
public function assemble (array $info, array $query = NULL) {
}
}
步骤3:路由代码位置:
因为是公共的,所以放在了library下面,又因为是login特有的,新建立了login文件夹,所以类命名Login_Route ,在类的自动加载中本文章稍后说明
3.4 controller
Yaf_Controller_Abstract体系具有可扩展性, 可以通过继承已有的类, 来实现这个抽象类, 从而添加应用自己的应用逻辑.
上面的router讲controller设置为了api,action从请求的最后一个/截图的,对于请求 /login/login 那action即为login。
方式一:action写在controller内部
方式二:具体的动作分开定义
有些时候为了拆分比较大的Controller, 使得代码更加清晰和易于管理, Yaf支持将具体的动作分开定义. 每个动作都需要实现 Yaf_Action_Abstract 就可以通过定义Yaf_Controller_Abstract::$actions来指明那些动作对应于具体的那些分离的类. 比如:
<?php
/**
* @name Api_Controller
* @desc 主控制器,也是默认控制器
* @author
*/
class ApiController extends Yaf_Controller_Abstract {
public function init(){
// $request = $this->getRequest();
// echo $request->getControllerName();
// echo $request->getActionName();
// echo $request->getRequestUri();
}
public $actions = array(
'login' => 'actions/Login.php',
'register' => 'actions/Register.php',
);
}
controller中的init方法(可选)
controller可以实现init方法,会被自动调用,可以做一些action的公共操作或者一些初始化工作
3.5 action
文件命名
与controller中的action名字统一 loginAction 后缀模式,配置在php.ini yaf.name_suffix,注意首字母大写
文件路径
路径由controller中指定,路径不影响文件的命名
具体实现
都需要继承 Yaf_Action_Abstract,实现这个抽象类, 从而添加应用自己的应用逻辑
abstract Yaf_Action_Abstract extends Yaf_Action_Controller {
public abstract void execute ( void );
}
excute方法会被自动调用。自己的action需要实现这个方法,一般的逻辑获取参数,创建model,逻辑处理,返回。
因为action有很多公共的东西,故实现了一个baseAction,base继承Yaf_Action_Abstract,每个action继承base。base放在了libary的login目录,命名Login_BaseAction
<?php
class LoginAction extends Login_BaseAction {
}
<?php
class Login_BaseAction extends Yaf_Action_Abstract {
public $resmsg = '';
public $code = 0;
public function execute(){
try {
$pageRes = array();
if(!$ret = $this->checkParams()){
throw new Exception("params check error , msg is " . Params_Validate::$errmsg);
}
$this->resmsg = $this->_process();
}
catch(Exception $e) {
$this->resmsg = $e->getMessage();
$this->code = -1;
}
$this->apiResponse($pageRes);
}
public function checkParams() {
$config = Utils_Helper::getconf( "/conf/params/" .Yaf_Registry::get('action') . ".ini" );
$checkParamsRes = Params_Validate::validate($config, Utils_Helper::getParams());
return $checkParamsRes;
}
public function apiResponse($pageRes) {
$res = array(
'errcode' => $this->code,
'data' => $this->resmsg,
);
echo json_encode($res);
}
public function _process(){
$pageServName = sprintf("Login_%sModel", ucfirst(Yaf_Registry::get('action')));
if(class_exists($pageServName)) {
$pageServ = new $pageServName(Utils_Helper::getParams());
$res = $pageServ->excute();
return $res;
}
else {
throw new Exception("models not existed : " . $pageServName);
}
}
}
baseAction说明–写的一般,另外说明
1、继承Yaf_Action_Abstract,且需要实现execute方法,其余无强制要求。
2、不同action的model类,由action名字动态拼接。
3、参数校验采取配置方式–更完善的参数校验-另外说明,配置文件的获取-本文章稍后说明
3.6 models说明
此文件一般比较灵活,下面仅说明下
<?php
class Login_LoginModel{
private $params = null;
private $dataSrv = null;
public function __construct($params){
$this->params = $params;
$this->dataSrv = new Data_UserModel();
}
public function excute(){
$username = $this->params['username'];
$passwd = $this->params['passwd'];
$userInfo = $this->dataSrv->getUserByUsername($username);
if(empty($userInfo) || $passwd != $userInfo['password']) {
throw new Exception('login fail');
}
return "login success";
}
}
__construct构造方法,初始化相关参数和类
execute中进行相关逻辑处理
3.7 类的自动加载
目录映射规则:
1)model controller plugin
类型 | 后缀(或者前缀, 可以通过php.ini中ap.name_suffix来切换) | 映射路径 |
---|---|---|
控制器 | Controller | 默认模块下为{项目路径}/controllers/, 否则为{项目路径}/modules/{模块名}/controllers/ |
数据模型 | Model | {项目路径}/models/ |
插件 | Plugin | {项目路径}/plugins/ |
例如model类在new的时候,会自动到model目录寻找,model内可以再有子文件夹,文件命名需要符合规范,见下文
controller会到controllers目录寻找
2)类库
Yaf为了方便在一台服务器上部署的不同产品之间共享公司级别的共享库, 支持全局类和本地类两种加载方式.
全局类是指, 所有产品之间共享的类, 这些类库的路径是通过ap.library在php.ini(当然,如果PHP在编译的时候, 支持了with-config-file-scan-dir,那么也可以写在单独的ap.ini中)(php支持多个配置文件)
而本地类是指, 产品自身的类库, 这些类库的路径是通过在产品的配置文件中, 通过ap.library配置的,自己模块的。
为了防止类库调用的时候,本地类和全局类不知道调用哪个,两个方法:方法1:需要在ap配置文件中指定本地类库的namespace(版本原因,applicatin的配置说明中并未说明,我的代码中未指定,估计默认都是library)。
例如:applicatin.library.namespace = “Tool,Data” 。在library文件夹下创建与application.library.namespace声名的空间名称相同的文件夹,如Tool与Data等目录
方法2: Yaf_Loader来注册:public Yaf_Loader Yaf_Loader::registerLocalNamespace( mixed $local_name_prefix );
文件规则:
controller : 位于controller文件夹下,文件名大写与controllerName相同,类名 结合php.ini中ap.name_suffix,例如后缀模式 ApiController
action : 路径不限,和controller中指定一样即可,文件名大写与actionName相同,类名 结合php.ini中ap.name_suffix,例如后缀模式 LoginAction
model :位于models文件夹下,可以有子文件夹,命名需要和文件夹映射,例如目录结构为models/login/Login.php 类名为Login_LoginModel
plugin:位于plugins文件夹下,文件名大写,例如SafCallerPlugin
library:可以分子文件夹,一类类库放在同一个文件夹 , 命名需要和文件夹映射,例如目录结构为library/utils/mysql.php 类名为Utils_Mysql
3.8自定义配置文件读取
ap配置文件初始化的时候读取的
其余的自定义配置文件读取,可以用 Yaf_Config_Ini ,需要提供完整路径,返回为一个对象,
public static function getConf($path, $name=''){
$config = new Yaf_Config_Ini(APP_PATH . $path, $name);
return $config;
}
例如
$path为/conf/mysql.ini
$name为passport
$config->host 可以获取相应host配置
另外 配置数据等级结构通过用点或者句号(.)分离键值。一个节可以扩展或者通过在节的名称之后带一个冒号(:)和被继承的配置数据的节的名称来从另一个节继承。passport即为一节
3.9 login.htm简单前后台交互
非views视图文件,只是一个简单的html页面,简单说明前后端交互
利用bootstrap实现,
页面实现
1、下载bootstrap相关文件 https://v3.bootcss.com/getting-started/#download 下载用于生产环境的bootstrap
2、解压后放置在webroot目录(ngnix已经配置,见上方)
3、bootstrap相关组件 https://v3.bootcss.com/components/
实例精选 https://v3.bootcss.com/getting-started/#template 找到了登录实例 ,直接下载全部实例,将登录部分复制到login.html 并复制相应的login.css
4、按照上面网站说明在login.html 引用相关文件,对于下载的文件夹中存在的,改为本地相对路径
应该可以访问了,试一下,报错进行相应解决
5、给login.html添加相应js动作,与后端进行交互
js相关语法: https://www.jquery123.com/
<script type="text/javascript">
$(function() {
//单条点击
$("#loginSubmit").click(function() {
var username = $("#inputEmail").val();
var passwd = $("#inputPassword").val();
$.post("http://host::8885/login/login",
{
data:{"username":username,"passwd":passwd},
},
function(data,status){
alert("Data: " + data + "\nStatus: " + status);
});
});
});
</script>
<script type="text/javascript">
$(function() {
//单条点击
$("#registerSubmit").click(function() {
var username = $("#inputEmail").val();
var passwd = $("#inputPassword").val();
$.post("http:/host::8885/login/register",
{
data:{"username":username,"passwd":passwd},
},
function(data,status){
alert("Data: " + data + "\nStatus: " + status);
});
});
});
</script>
踩坑记录
获取 username和passwd放在了click上面, $(function()页面加载即会调用,但是此时这两个变量为空,click后的函数,click动作触发才会调用
附 yaf的可选配置
名称 | 值类型 | 默认值 | 说明 |
---|---|---|---|
application.ext | String | php | PHP脚本的扩展名 |
application.bootstrap | String | Bootstrapplication.php | Bootstrap路径(绝对路径) |
application.library | String | application.directory+"/library" | 本地(自身)类库的绝对目录地址 |
application.baseUri | String | NULL | 在路由中,需要忽略的路径前缀,一般不需要设置,Yaf会自动判断. |
application.dispatcher.defaultModule | String | index | 默认的模块 |
application.dispatcher.throwException | Bool | True | 在出错的时候,是否抛出异常 |
application.dispatcher.catchException | Bool | False | 是否使用默认的异常捕获Controller,如果开启,在有未捕获的异常的时候, 控制权会交给ErrorController的errorAction方法,可以通过$request->getException()获得此异常对象 |
application.dispatcher.defaultController | String | index | 默认的控制器 |
application.dispatcher.defaultAction | String | index | 默认的动作 |
application.view.ext | String | phtml | 视图模板扩展名 |
application.modules | String | Index | 声明存在的模块名,请注意,如果你要定义这个值,一定要定义Index Module |
application.system.* | String | * | 通过这个属性,可以修改yaf的runtime configure,比如application.system.lowcase_path, 但是请注意只有PHP_INI_ALL的配置项才可以在这里被修改,此选项从2.2.0开始引入 |
附php.ini相关yaf配置
选项名称 | 默认值 | 可修改范围 | 更新记录 |
---|---|---|---|
yaf.environ | product | PHP_INI_ALL | 环境名称, 当用INI作为Yaf的配置文件时, 这个指明了Yaf将要在INI配置中读取的节的名字 |
yaf.library | NULL | PHP_INI_ALL | 全局类库的目录路径 |
yaf.cache_config | 0 | PHP_INI_SYSTEM | 是否缓存配置文件(只针对INI配置文件生效), 打开此选项可在复杂配置的情况下提高性能 |
yaf.name_suffix | 1 | PHP_INI_ALL | 在处理Controller, Action, Plugin, Model的时候, 类名中关键信息是否是后缀式, 比如UserModel, 而在前缀模式下则是ModelUser |
yaf.name_separator | “” | PHP_INI_ALL | 在处理Controller, Action, Plugin, Model的时候, 前缀和名字之间的分隔符, 默认为空, 也就是UserPlugin, 加入设置为"_", 则判断的依据就会变成:“User_Plugin”, 这个主要是为了兼容ST已有的命名规范 |
yaf.forward_limit | 5 | PHP_INI_ALL | forward最大嵌套深度 |
yaf.use_namespace | 0 | PHP_INI_SYSTEM | 开启的情况下, Yaf将会使用命名空间方式注册自己的类, 比如Yaf_Application将会变成Yaf\Application |
yaf.use_spl_autoload | 0 | PHP_INI_ALL | 开启的情况下, Yaf在加载不成功的情况下, 会继续让PHP的自动加载函数加载, 从性能考虑, 除非特殊情况, 否则保持这个选项关闭 |