yii2框架实现引导安装功能

来源:互联网 发布:游戏服装设计软件 编辑:程序博客网 时间:2024/06/17 10:30

最近有在学习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应用的入口文件举栗子,代码是长这样子的

<?phpdefined('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 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 配置信息如下

<?phpreturn [    '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 代码如下

<?phpreturn [    //第三方库加载路径    '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存放在根目录下

<?phpuse 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最终代码如下

<?phpYii::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代码,把这些代码写入文件中.

4.如何绑定事件 ?

在填写完数据库信息后,点击下一步,先渲染出界面,同时动态的输出安装过程的信息,而不是程序执行到最后一步才渲染页面,这样等待的时间长。这就使用到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();       }    }

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

//$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\Connectiontry       {           $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.如果还有的话 再来补充

长风破浪会有时,直挂云帆济沧海

原创粉丝点击