您的位置:首页 > 编程语言 > PHP开发

yii2框架实现引导安装功能

2017-07-05 20:41 501 查看
最近有在学习yii框架,想着做一个小型的cms。

一个cms最开始的动作当然时安装引导,参考lulucms的代码,学习实现的结果如下。

第一步 安装介绍 (install.php?r=install/step_one)



第二步 环境检查 (install.php?r=install/step_two)



第三步 数据库配置 (install.php?r=install/step_three)



第四步 安装数据库表 (install.php?r=install/step_processing)



第五步 结果显示 (install.php?r=install/finish)



其它 已经安装提示 (install.php?r=install/stop)



实现思路:

1.安装模块独立出来,命名为install,目录结构和正常的backend应用差不多.



2.前台的入口文件判断是否已经安装,通过文件锁进行标识,如果没有安装过则执行安装应用的入口文件install.php,已经安装则正常进入FrontApplication应用

3.进入了应用分几步依次进行,先进行环境判断,再进行数据库信息填写,最后安装数据库文件,如果安装成功,则创建一个安装锁文件install.lock,代表已经安装过程序.

记录笔记

1.yii2如何创建一个install应用?

如果使用yii advanced高级模板,以backend应用的入口文件举栗子,代码是长这样子的

<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/../../common/config/bootstrap.php');
require(__DIR__ . '/../config/bootstrap.php');

$config = yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../../common/config/main.php'),
require(__DIR__ . '/../../common/config/main-local.php'),
require(__DIR__ . '/../config/main.php'),
require(__DIR__ . '/../config/main-local.php')
);

(new yii\web\Application($config))->run();


显然,应用是通过配置文件进行初始化的,因为自带的backend的入口文件位于 backend/web/index.php,所以它的配置文件位置就是
/../config/main.php


打开backend的主配置文件main.php 可以看到如下代码

<?php
$params = array_merge(
require(__DIR__ . '/../../common/config/params.php'),
require(__DIR__ . '/../../common/config/params-local.php'),
require(__DIR__ . '/params.php'),
require(__DIR__ . '/params-local.php')
);

return [
'id' => 'app-backend',
'basePath' => dirname(__DIR__),
'controllerNamespace' => 'backend\controllers',
'bootstrap' => ['log'],
'modules' => [],
'components' => [
'request' => [
'csrfParam' => '_csrf-backend',
],
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => true,
'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true],
],
'session' => [
// this is the name of the session cookie used for login on
4000
the backend
'name' => 'advanced-backend',
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
'errorHandler' => [
'errorAction' => 'site/error',
],
/*
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
],
],
*/
],
'params' => $params,
];


其中返回的数组中

‘id’ => ‘app-backend’ //作用是指定全局唯一的应用id号,

‘basePath’ => dirname(__DIR__) //指定backend应用的目录位置

‘controllerNamespace’ => ‘backend\controllers’ //指定控制器命名空间

‘bootstrap’ => [‘log’] //指定随应用启动而启用的组件

‘modules’ => [], //指定应用的子模块 可理解为应用中的应用

‘components’ => …. //往应用注册组件

‘params’ => $parms //用户本地的一些配置参数

到这里答案就很明显了,假设现在需要自定义一个新的独立的安装引导应用,也就是需要在根目录中建立一个文件夹 命名为install,在install下新建一个main.php,保存应用的关键配置信息



main.php 配置信息如下

<?php

return [
'id' => 'app-install',//指定模块名称
'language' => 'zh-CN',
'basePath' => dirname(__DIR__), //指定模块路径
'bootstrap' => ['log'],
'controllerNamespace' => 'install\controllers',//指定命名空间
'defaultRoute' => 'install/index', //默认路由
'components' => [
//错误处理
'errorHandler' => [
'errorAction' => 'install/index',
],
],

];


同时,公共配置 根目录/data/config/main.php 代码如下

<?php
return [
//第三方库加载路径
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
//运行缓存文件路径
'runtimePath' => dirname(dirname(__DIR__)) . '/data/runtime',

//组件配置
'components' => [
//缓存组件
'cache' => [
'class' => 'yii\caching\FileCache',
],
//session组件
'session' => [
// this is the name of the session cookie used for login on the frontend
'name' => 'advanced-frontend',
],
//日志组件
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
//错误处理组件
'errorHandler' => [
'errorAction' => 'site/error',
],
//资源管理组件
'assetManager' => [
'basePath' => '@webroot/frontend/assets',
'baseUrl'=>'@web/frontend/assets',
'bundles' => [
// you can override AssetBundle configs here
],
'linkAssets' => true,
// ...
],
'request' => [
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey' => '6_NpujNa4RDqhQsB9IfERWwD4F9GWbls',
],
],
];


入口文件 install.php存放在根目录下

<?php
use yii\web\Application;
use source\libs\Common;

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require(__DIR__ . '/vendor/autoload.php');
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/common/config/bootstrap.php');
require (__DIR__. '/source/config/overwrite.php');

$config = yii\helpers\ArrayHelper::merge(
//公共组件
require(__DIR__ . '/data/config/main.php'),
//模块独立组件
require(__DIR__ . '/install/config/main.php')
);

(new Application($config))->run();


可以看到,除了添加公共配置文件外,还添加了应用主配置文作为应用最终的配置信息。

在上方包含的bootstrap.php文件中,它的作用是起别名,因为yii框架自身的类加载机制依赖别名,所以在bootstrap.php中需要给新增的install应用添加别名,指定install的目录路径,bootstrap最终代码如下

<?php
Yii::setAlias('@common', dirname(__DIR__));
Yii::setAlias('@frontend', dirname(dirname(__DIR__)) . '/frontend');
Yii::setAlias('@backend', dirname(dirname(__DIR__)) . '/backend');
Yii::setAlias('@console', dirname(dirname(__DIR__)) . '/console');

Yii::setAlias('@source', dirname(dirname(__DIR__)).'/source');
Yii::setAlias("@install",dirname(dirname(__DIR__)).'/install');
Yii::setAlias("@data",dirname(dirname(__DIR__)).'/data');


假设已经给install建立好了默认的控制器文件,那么此时访问index.php,不出意外的话就会跳转到index.php?r=install/index,如果成功,就说明应用成功的跑起来了。

2.如何不改动yii框架源码的情况下重载yii的类

场景:install模块检查用户系统环境时需要使用到yii2的FileHelper类,来检测文件夹的可读可写,需要重载此类,而又不想直接改Yii的源代码。

解决方法: 通过Yii::$classMap 改变类与路径的映射关系,在入口文件中引入以下文件,命名为overwrite.php

<?php
$alias = '@source/helpers/FileHelper.php';
Yii::$classMap['yii\helpers\FileHelper'] = $alias ;


原理: 因为在Yii2源码 yii2\BaseYii中,自动加载机制如下

public static function autoload($className)
{
//先判断映射中是否存在
if (isset(static::$classMap[$className])) {
$classFile = static::$classMap[$className];
if ($classFile[0] === '@') {
$classFile = static::getAlias($classFile);
}
//判断是否指定命名空间
} elseif (strpos($className, '\\') !== false) {
$classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false);
if ($classFile === false || !is_file($classFile)) {
return;
}
} else {
return;
}

include($classFile);
//抛出异常
if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) {
throw new UnknownClassException("Unable to find '$className' in file: $classFile. Namespace missing?");
}
}


3.关于PHP函数的收获

a.获取运行PHP当前系统
PHP_OS,是常量

b.获取php.ini的配置信息 如获取最大上传空间限制
get_cfg_var('upload_max_filesize')

c.获取磁盘剩余空间
disk_free_space()函数

d.判断是否可写
is_writable(filename)

e.获取PHP版本号
PHP_VERSION 常量

f.判断某个类是否存在
class_exists()

g.判断某个扩展是否加载
extension_loaded()

h.把变量打印处理 其返回的表示是合法的PHP代码
var_export($var)
用在用户填写了数据库配置信息,然后把数组变为合法的php代码,把这些代码写入文件中.


[b]4.如何绑定事件 ?[/b]

在填写完数据库信息后,点击下一步,先渲染出界面,同时动态的输出安装过程的信息,而不是程序执行到最后一步才渲染页面,这样等待的时间长。这就使用到yii2的绑定.

InstallController
继承的控制器是
BaseController
,而
BaseController
继承的是yii\web\Controller;

在BaseController中的init初始化方法中,进行对
EVENT_AFTER_SEND
事件的绑定,
EVENT_AFTER_SEND
是指在动作完成后才会被触发的动作。

Response是yii2框架中的HTTP响应组件,

public function init()
{
parent::init();
//绑定yii EVENT_AFTER_SEND事件
Yii::$app->response->on(Response::EVENT_AFTER_SEND,[$this,'afterResponse']);
}


所有的子类继承BaseController后,通过建立afterResponse方法就能在方法执行后触发一些自定义的行为动作.在InstallController中

//绑定了EVENT_AFTER_SEND事件
public function afterResponse()
{
//如果当前的请求方法为processing
if (Yii::$app->requestedAction->id == 'processing') {
//那么这个方法执行后 就执行下面的_installing方法
$this->_installing();
}
}


[b]5.yii2如何检测数据库是否连接成功 ?[/b]

//$this->InstallForm是表单模型
$config = [
'dsn'=>"mysql:host={$this->InstallForm->dbHost};dbname={$this->InstallForm->dbName}",
'username' => $this->InstallForm->dbUser,
'password' => $this->InstallForm->dbPassword
];

$db = new Connection($config);
try {
$db->open();
$result = $db->isActive? ['status'=>true,'msg'=>'数据库连接成功']:['status'=>false,'msg'=>'数据库连接失败'];
}catch (Exception $e) {
$db->close();
$result = ['status'=>false,'msg'=>$this->getDbError($e->getMessage(),[
'dbHost'=>$this->InstallForm->dbHost,
'dbName'=>$this->InstallForm->dbName
])];
}


6.如何使用yii\db\Connection执行sql语句

//Connection为yii\db\Connection
try
{
$db = new Connection($dbConfig);
Lychee::getApp()->set('db',$db);

$db->createCommand("USE {$this->InstallForm->dbName}")->execute();
$db->createCommand("SET NAMES 'utf8'")->execute();

self::_showLog("准备初始化数据库",true);
return $db;
}
catch(\Exception $e)
{
var_dump($e->getMessage());
$error = self::getDbError($e->getMessage(),[
'dbHost' => $this->InstallForm->dbHost,
'dbName' => $this->InstallForm->dbName
]);

self::_showLog($error,false);
return false;
}


7.如何利用缓冲动态输出安装过程

$str = "安装过程数据";
echo $str;
ob_flush();
flush();


8.如果已经安装过 如何防止再进入页面进行安装

//使用beforeAction进行检查 $action->id为当前的动作名称
public function beforeAction($action)
{
if ($action->id == 'stop' || $action->id == 'finish') {
return parent::beforeAction($action);
}
if (file_exists(Yii::getAlias('@data/install.lock')))   {
return $this->redirect(['stop']);
}
return parent::beforeAction($action);
}


9.如何使用数据库操作事务

//任何一个语句失败将会回滚
$db = Yii::$app->get('db');
$transaction =  $db->beginTransaction();
try {
$db->createCommand($sql)->execute();
$transaction->commit();
return true;
}catch(\Exception $e) {
$transaction->rollBack();
echo $e->getMessage();
$this->_showLog("建立数据库表时出错",false);
return false;
}


9.如果还有的话 再来补充

长风破浪会有时,直挂云帆济沧海
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  yii cms 安装引导