yii2基础(五)

Yii中的Gii与Url处理


代码自动生成

从1.1.2版本开始,Yii包含一个代码生成工具Gii。它取代了以前的yiic shell命令行。现在我们开始描述如何使用Gii和如何扩展Gii。

使用Gii
Gii是一个模块,必须在Yii Application里面存在,使用Gii,我们首先要修改应用配置文件开启Gii:
return array(
    ......
    'modules'=>array(
        'gii'=>array(
            'class'=>'system.gii.GiiModule',
            'password'=>'pick up a password here',
            'ipFilters'=>array('127.0.0.1','::1'), 
            // 'newFileMode'=>0666,
            // 'newDirMode'=>0777,
        ),
    ),
);
在上面,我们指定了gii模块的位置,可以给它设置一个密码,默认gii只允许localhost,如果我们需要增加一些信任的主机则需要设置ipFilters属性。   GiiModule::ipFilters  property as shown in the above code.
注:Gii主要是作为一个开发环境,如果移植到了生产环境,最好关掉此功能,不然会很不安全。

我们可以通过URL    http://hostname/path/to/index.php?r=gii . 来进入gii。
如果我们的Yii Application使用了path-format。这我们可以通过Url  http://hostname/path/to/index.php/gii . 进入Gii。但是我们需要增加以下 Url 规则在url rules里面:
'components'=>array(
    ......
    'urlManager'=>array(
        'urlFormat'=>'path',
        'rules'=>array(
            'gii'=>'gii',
            'gii/<controller:\w+>'=>'gii/<controller>',
            'gii/<controller:\w+>/<action:\w+>'=>'gii/<controller>/<action>',
            ...existing rules...
        ),
    ),
)


扩展Gii(Extending Gii)
有时候我们想要Gii生成的代码支持多种Language,所以我们必须来扩展它。
代码生成的文件保存在如下的目录里面: yii/framework/gii/generators/
model/                       the model generator root folder
   ModelCode.php             the code model used to generate code
   ModelGenerator.php        the code generation controller
   views/                    containing view scripts for the generator
      index.php              the default view script
   templates/                containing code template sets
      default/               the 'default' code template set
         model.php           the code template for generating model class code

寻找生成文件(Generator Search Path)
Gii被   GiiModule::generatorPaths 属性指定寻找代码生成文件,当我们自定义后,我们可以在Application Configuration 里面配置这个属性:
return array(
    'modules'=>array(
        'gii'=>array(
            'class'=>'system.gii.GiiModule',
            'generatorPaths'=>array(
                'application.gii',   // a path alias
            ),
        ),
    ),
);
上面的配置指定Gii在application.gii目录下面寻找生成文件,默认的路径是system.gii.generators。


URL Management(网址管理)
Web应用程序完整的URL管理包括两个方面。首先, 当用户请求约定的URL,应用程序需要解析 它变成可以理解的参数。第二,应用程序需求提供一种创造URL的方法,以便创建的URL应用程序可以理解的。对于Yii应用程序,这些通过 CUrlManager 辅助完成。


创建Url

虽然URL可被硬编码在控制器的视图(view)文件,但往往可以很灵活地动态创建它们:

$url=$this->createUrl($route,$params);

$this指的是控制器实例; $route指定请求的route 的要求;$params 列出了附加在网址中的GET参数。

默认情况下,URL以get格式使用createUrl 创建。例如,提供$route='post/read'$params=array('id'=>100) ,我们将获得以下网址:

/index.php?r=post/read&id=100

我们可以使上述网址看起来更简洁,更不言自明,通过采用所谓的'path格式,省去查询字符串和把GET参数加到路径信息,作为网址的一部分:

/index.php/post/read/id/100

要更改URL格式,我们应该配置urlManager应用元件,以便createUrl可以自动切换到新格式和应用程序可以正确理解新的网址:

array(
    ......
    'components'=>array(
        ......
        'urlManager'=>array(
            'urlFormat'=>'path',
        ),
    ),
);

请注意,我们不需要指定的urlManager元件的类,因为它在CWebApplication预声明为CUrlManager

注:提示:此网址通过createurl方法所产生的是一个相对地址。为了得到一个绝对的url ,我们可以用前缀yii: :app()->hostInfo,或调用createAbsoluteUrl 。


用户友好的Url(User Friendly Urls)

当用path格式URL,我们可以指定某些URL规则使我们的网址更用户友好性。例如,我们可以产生一个短短的URL/post/100 ,而不是冗长/index.php/post/read/id/100。网址创建和解析都是通过CUrlManager指定网址规则。

要指定的URL规则,我们必须设定 urlManager 应用元件的属性 rules
array(
    ......
    'components'=>array(
        ......
        'urlManager'=>array(
            'urlFormat'=>'path',
            'rules'=>array(
                'pattern1'=>'route1',
                'pattern2'=>'route2',
                'pattern3'=>'route3',
            ),
        ),
    ),
);

这些规则以一系列的路线格式对数组指定,每对对应于一个单一的规则。路线(route)的格式必须是有效的正则表达式,没有分隔符和修饰语。它是用于匹配网址的路径信息部分。还有route应指向一个有效的路线控制器。

规则可以绑定少量的GET参数。这些出现在规则格式的GET参数,以一种特殊令牌格式表现如下:

'pattern1'=>array('route1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)
在上面的rules中,列出了许多选项,在1.1.0的版本中,存在以下选项:
  • urlSuffix: Url路径的后缀.,默认是NULL,使用这个选项 CUrlManager::urlSuffix.

  • caseSensitive: 是否是大小写敏感,默认是空,使用这个选项CUrlManager::caseSensitive.

  • defaultParams:rules默认提供的GET 参数(name => value).当这个选项设置后,对请求的URL,会把这个指定的值注入$_GET中。

  • matchValue:在创建一个URL的时候,是否这个GET参数的值在rule里面符合相应的子规则。默认是NULL。




使用命名参数(Using Named Parameters)
<ParamName:ParamPattern>

ParamName表示GET参数名字,可选项ParamPattern表示将用于匹配GET参数值的正则表达式。当生成一个网址(URL)时,这些参数令牌将被相应的参数值替换;当解析一个网址时,相应的GET参数将通过解析结果来生成。

我们使用一些例子来解释网址工作规则。我们假设我们的规则(在urlManager的rules里面设置)包括如下三个:

array(
    'posts'=>'post/list',
    'post/<id:\d+>'=>'post/read',
    'post/<year:\d{4}>/<title>'=>'post/read',
)
  • 调用$this->createUrl('post/list')生成/index.php/posts。第一个规则适用。

  • 调用$this->createUrl('post/read',array('id'=>100))生成/index.php/post/100。第二个规则适用。

  • 调用$this->createUrl('post/read',array('year'=>2008,'title'=>'a sample post'))生成/index.php/post/2008/a%20sample%20post。第三个规则适用。

  • 调用$this->createUrl('post/read')产生/index.php/post/read。请注意,没有规则适用。

如果GET参数传递到createUrl是以上所要求的一项规则,其他参数将出现在查询字符串。例如,如果我们调用$this->createUrl('post/read',array('id'=>100,'year'=>2008)) ,我们将获得/index.php/post/100?year=2008。为了使这些额外参数出现在路径信息的一部分,我们应该给规则附加/*。 因此,该规则post/<id:\d+>/* ,我们可以获取网址/index.php/post/100/year/2008 。

正如我们提到的,URL规则的其他用途是解析请求网址。当然,这是URL生成的一个逆过程。例如, 当用户请求 /index.php/post/100 ,上面例子的第二个规则将适用来解析路线 post/read和GET参数 array('id'=>100)(可通过 $_GET获得) 。


隐藏index.php入口文件

还有一点,我们可以做进一步清理我们的网址,即在URL中藏匿index.php入口脚本。这就要求我们配置Web服务器,以及urlManager应用程序元件。

我们首先需要配置Web服务器,这样一个URL没有入口脚本仍然可以处理入口脚本。如果是 Apache HTTP server ,可以通过打开网址重写引擎和指定一些重写规则。这两个操作可以在包含入口脚本的目录下的 .htaccess 文件里实现。下面是一个示例:(注意,前提是请确定服务器的AllowOverride All)
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# otherwise forward it to index.php
RewriteRule . index.php
 
然后,我们设定 urlManager 元件的 showScriptName 属性为  false

Faking URL Suffix(伪造URL后缀)

我们还可以添加一些网址的后缀。例如,我们可以用 /post/100.html 来替代 /post/100  。这使得它看起来更像一个静态网页URL。为了做到这一点,只需配置 urlManager 元件的 urlSuffix 属性为你所喜欢的后缀。
'urlManager'=>array(
      'urlFormat'=>'path',
      'showScriptName' => false,
      'urlSuffix' => '.html',
),




使用自定义URL规则设置类

注意: Yii从1.1.8版本起支持自定义URL规则类

默认情况下,每个URL规则都通过CUrlManager来声明为一个CUrlRule对象,这个对象会解析当前请求并根据具体的规则来生成URL。 虽然CUrlRule可以处理大部分URL格式,但在某些特殊情况下仍旧有改进余地。

比如,在一个汽车销售网站上,可能会需要支持类似/Manufacturer/Model这样的URL格式, 其中Manufacturer 和 Model 都各自对应数据库中的一个表。此时CUrlRule就无能为力了。

我们可以通过继承CUrlRule的方式来创造一个新的URL规则类。并且使用这个类解析一个或者多个规则。 以上面提到的汽车销售网站为例,我们可以声明下面的URL规则。

array(
    // 一个标准的URL规则,将 '/' 对应到 'site/index'
    '' => 'site/index',
 
    // 一个标准的URL规则,将 '/login' 对应到 'site/login', 等等
    '<action:(login|logout|about)>' => 'site/<action>',
 
    // 一个自定义URL规则,用来处理 '/Manufacturer/Model'
    array(
        'class' => 'application.components.CarUrlRule',
        'connectionID' => 'db',
    ),
 
    // 一个标准的URL规则,用来处理 'post/update' 等
    '<controller:\w+>/<action:\w+>' => '<controller>/<action>',
),

从以上可以看到,我们自定义了一个URL规则类CarUrlRule来处理类似/Manufacturer/Model这样的URL规则。 这个类可以这么写:

class CarUrlRule extends CBaseUrlRule{
    public $connectionID = 'db';
 
    public function createUrl($manager,$route,$params,$ampersand)
    {
        if ($route==='car/index')
        {
            if (isset($params['manufacturer'], $params['model']))
                return $params['manufacturer'] . '/' . $params['model'];
            else if (isset($params['manufacturer']))
                return $params['manufacturer'];
        }
        return false;  // this rule does not apply
    }
 
    public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)
    {
        if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches))
        {
            // check $matches[1] and $matches[3] to see
            // if they match a manufacturer and a model in the database
            // If so, set $_GET['manufacturer'] and/or $_GET['model']
            // and return 'car/index'
        }
        return false;  // this rule does not apply
    }}

自定义URL规则类必须实现在CBaseUrlRule中定义的两个接口。

除了这种典型用法,自定义URL规则类还可以有其他的用途。比如,我们可以写一个规则类来记录有关URL解析和UEL创建的请求。 这对于正在开发中的网站来说很有用。我们还可以写一个规则类来在其他URL规则都匹配失败的时候显示一个自定义404页面。 注意,这种用法要求规则类在所有其他规则的最后声明。



Yii 有一个内置的验证/授权(auth)框架,用起来很方便,还能对其进行自定义,使其符合特殊的需求。
Yii auth 框架的核心是一个预定义的  用户(user)应用组件  它是一个实现了  IWebUser  接口的对象。 此用户组件代表当前用户的持久性认证信息。我们可以通过 Yii::app()->user 在任何地方访问它。
使用此用户组件,我们可以通过  CWebUser::isGuest  检查检查一个用户是否登陆; 可以  登录(login)  或  注销(logout)  一个用户;我们可以通过 CWebUser::checkAccess 检查此用户是否可以执行特定的操作;还可以获取此用户的 唯一标识(unique identifier) 及其他持久性身份信息。


定义身份类 (Defining Identity Class) 

不同的类可能实现不同的验证方式(例如:OpenID,LDAP)。最好是继承 CUserIdentity,此类是居于用户名和密码的验证方式。定义身份类的主要工作是实现IUserIdentity::authenticate方法。在用户会话中根据需要,身份类可能需要定义别的身份信息

应用实例

下面的例子,我们使用Active Record来验证提供的用户名、密码和数据库的用户表是否吻合。我们通过重写getId函数来返回验证过程中获得的_id变量(缺省的实现则是返回用户名)。在验证过程中,我们还借助CBaseUserIdentity::setState函数把获得的title信息存成一个状态。

  1. 实现这个方法 authenticate() 来使用数据库数据进行验证。
  2. 重写CUserIdentity::getId() 方法可以返回 _id 属性, 因为默认是返回username而不是id。
  3. 使用 setState() (CBaseUserIdentity::setState) 方法来设置一个信息保存起来,以便检索后面的请求。
  4. 下面的这个类默认存在在protected/components/UserIdentity.php
class UserIdentity extends CUserIdentity{
    private $_id;
    public function authenticate()
    {
        $record=User::model()->findByAttributes(array('username'=>$this->username));
        if($record===null)
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        else if($record->password!==md5($this->password))
            $this->errorCode=self::ERROR_PASSWORD_INVALID;
        else
        {
            $this->_id=$record->id;
            $this->setState('title', $record->title);
            $this->errorCode=self::ERROR_NONE;
        }
        return !$this->errorCode;
    }
 
    public function getId()
    {
        return $this->_id;
    }} 

作为状态存储的信息(通过调用 CBaseUserIdentity::setState )将被传递给 CWebUser 。而后者则把这些信息存放在一个永久存储媒介上(如session)。我们可以把这些信息当作 CWebUser 的属性来使用。例如,为了获得当前用户的 title 信息,我们可以使用 Yii::app()->user->title (这项功能是在1.0.3版本引入的。在之前的版本里,我们需要使用 Yii::app()->user->getState('title') )。


登录和注销(Login and Logout)

使用身份类和用户部件,我们方便的实现登录和注销。

// 使用提供的用户名和密码登录用户$identity=new UserIdentity($username,$password);
if($identity->authenticate())
    Yii::app()->user->login($identity);
else
    echo $identity->errorMessage;
......
// 注销当前用户Yii::app()->user->logout(); 
如果验证成功,Yii::app()->user->login($identity); 这句话传递了一个身份信息($identity)给   CWebUser::login  方法,login方法将会保存这个信息到一个永久的存储器中(例如PHP的SESSION)。
不管用户有没有被认证,我们都可以用一个简单的办法在整个Application中检查处理,使用 Yii::app()->user->isGuest . 如果它返回false表示未登陆,如果返回true则表示已经把身份信息存储到了存储器中。

缺省情况下,用户将根据session configuration完成一序列inactivity动作后注销。设置用户部件的allowAutoLogin属性为true和在CWebUser::login方法中设置一个持续时间参数来改变这个行为。即使用户关闭浏览器,此用户将保留用户登陆状态时间为被设置的持续时间之久。前提是用户的浏览器接受cookies。

// 保留用户登陆状态时间7天// 确保用户部件的allowAutoLogin被设置为true。Yii::app()->user->login($identity,3600*24*7);



访问控制过滤器(Access Control Filter)
访问控制过滤器是检查当前用户是否能执行访问的controller action的初步授权模式。这种授权模式基于用户名,客户IP地址和访问类型。
class PostController extends CController{
    ......
    public function filters()
    {
        return array(
            'accessControl',
        );
    }}

在上面,设置的access control过滤器将应用于PostController里每个动作。过滤器具体的授权规则通过重载控制器的CController::accessRules方法来指定。

class PostController extends CController{
    ......
    public function accessRules()
    {
        return array(
            array('deny',
                'actions'=>array('create', 'edit'),
                'users'=>array('?'),
            ),
            array('allow',
                'actions'=>array('delete'),
                'roles'=>array('admin'),
            ),
            array('deny',
                'actions'=>array('delete'),
                'users'=>array('*'),
            ),
        );
    }}

上面设定了三个规则,每个用个数组表示。数组的第一个元素不是'allow'就是'deny',其他的是名-值成对形式设置规则参数的。上面的规则这样理解:createedit动作不能被匿名执行;delete动作可以被admin角色的用户执行;delete动作不能被任何人执行。

访问规则是一个一个按照设定的顺序一个一个来执行判断的。和当前判断模式(例如:用户名、角色、客户端IP、地址)相匹配的第一条规则决定授权的结果。如果这个规则是 allow,则动作可执行;如果是 deny,不能执行;如果没有规则匹配,动作可以执行。

为了确保某类动作在没允许情况下不被执行,设置一个匹配所有人的deny规则在最后,类似如下:

return array(
    // ... 别的规则...
    // 以下匹配所有人规则拒绝'delete'动作
    array('deny',
        'action'=>'delete',
    ),
);

因为如果没有设置规则匹配动作,动作缺省会被执行。

访问规则通过如下的上下文参数设置:

  • actions: 设置哪个动作匹配此规则。

  • users: 设置哪个用户匹配此规则。 此当前用户的name 被用来匹配. 三种设定字符在这里可以用:

    • *: 任何用户,包括匿名和验证通过的用户。
    • ?: 匿名用户。
    • @: 验证通过的用户。
  • roles: 设定哪个角色匹配此规则。 这里用到了将在后面描述的role-based access control技术。In particular, the rule is applied if CWebUser::checkAccess returns true for one of the roles.提示,用户角色应该被设置成allow规则,因为角色代表能做某些事情。

  • ips: 设定哪个客户端IP匹配此规则。

  • verbs: 设定哪种请求类型(例如:GETPOST)匹配此规则。

  • expression: 设定一个PHP表达式。它的值用来表明这条规则是否适用。在表达式,你可以使用一个叫$user的变量,它代表的是Yii::app()->user。这个选项是在1.0.3版本里引入的。



授权用户处理结果(Handing Authorization Result)

当授权失败,即,用户不允许执行此动作,以下的两种可能将会产生:

  • 如果用户没有登录和在用户部件中配置了loginUrl,浏览器将重定位网页到此配置URL。

  • 否则一个错误代码401的HTTP例外将显示。

当配置loginUrl 属性,可以用相对和绝对URL。还可以使用数组通过CWebApplication::createUrl来生成URL。第一个元素将设置route 为登录控制器动作,其他为名-值成对形式的GET参数。如下,

array(
    ......
    'components'=>array(
        'user'=>array(
            // 这实际上是默认值
            'loginUrl'=>array('site/login'),
        ),
    ),
)

如果浏览器重定位到登录页面,而且登录成功,我们将重定位浏览器到引起验证失败的页面。我们怎么知道这个值呢?我们可以通过用户部件的returnUrl 属性获得。我们因此可以用如下执行重定向:

Yii::app()->request->redirect(Yii::app()->user->returnUrl);


基于角色的访问控制(Role-Based Access Control)
Yii 通过其  authManager  组件实现了分等级的 RBAC 结构。 在下文中,我们将首先介绍在此结构中用到的主要概念。然后讲解怎样定义用于授权的数据。在最后,我们看看如何利用这些授权数据执行访问检查。

在 Yii 的 RBAC 中,一个基本的概念是  授权项目(authorization item) 。 一个授权项目就是一个做某件事的许可(例如新帖发布,用户管理)。根据其粒度和目标受众, 授权项目可分为  操作(operations) 任务(tasks)  和  角色(roles) 。 一个角色由若干任务组成,一个任务由若干操作组成, 而一个操作就是一个许可,不可再分。 例如,我们有一个系统,它有一个  管理员  角色,它由  帖子管理  和  用户管理  任务组成。 用户管理  任务可以包含 创建用户 修改用户  和  删除用户  操作组成。 为保持灵活性,Yii 还允许一个角色包含其他角色或操作,一个任务可以包含其他操作,一个操作可以包括其他操作。

通过授权项目,我们可以构建一个  授权等级体系  。在等级体系中,如果项目  A  由另外的项目  B  组成。 则  A  就是 B  的父项目。 一个授权项目可以有多个子项目,也可以有多个父项目。因此,授权等级体系是一个偏序图(partial-order graph)结构而不是一种树状结构。 在这种等级体系中,角色项目位于最顶层,操作项目位于最底层,而任务项目位于两者之间。

在一个控制器动作中,我们想检查当前用户是否可以删除指定的帖子。 利用 RBAC 等级体系和分配,可以很容易做到这一点。如下:
if(Yii::app()->user->checkAccess('deletePost'))
{
   
}

配置授权管理器(Authorization Manager)
在我们准备定义一个授权等级体系并执行访问权限检查之前, 我们需要配置一下  authManager  应用组件。 Yii 提供了两种授权管理器:  CPhpAuthManager  和  CDbAuthManager 。前者将授权数据存储在一个 PHP 脚本文件中而后者存储在数据库中。 配置  authManager  应用组件时,我们需要指定使用哪个授权管理器组件类, 以及所选授权管理器组件的初始化属性值。例如:
return array(
    'components'=>array(
        'db'=>array(
            'class'=>'CDbConnection',
            'connectionString'=>'sqlite:path/to/file.db',
        ),
        'authManager'=>array(
            'class'=>'CDbAuthManager',
            'connectionID'=>'db',
        ),
    ),
);

然后,我们便可以使用 Yii::app()->authManager 访问 authManager 应用组件。


定义授权等级体系

定义授权等级体总共分三步:定义授权项目,建立授权项目之间的关系,还要分配角色给用户。 authManager 应用组件提供了用于完成这三项任务的一系列 API 。

要定义一个授权项目,可调用下列方法之一,具体取决于项目的类型:

建立授权项目之后,我们就可以调用下列方法建立授权项目之间的关系:

最后,我们调用下列方法将角色分配给用户。

下面的代码演示了使用 Yii 提供的 API 构建一个授权体系的例子:

$auth=Yii::app()->authManager;
 
$auth->createOperation('createPost','create a post');
$auth->createOperation('readPost','read a post');
$auth->createOperation('updatePost','update a post');
$auth->createOperation('deletePost','delete a post');
 
$bizRule='return Yii::app()->user->id==$params["post"]->authID;';
$task=$auth->createTask('updateOwnPost','update a post by author himself',$bizRule);
$task->addChild('updatePost');
 
$role=$auth->createRole('reader');
$role->addChild('readPost');
 
$role=$auth->createRole('author');
$role->addChild('reader');
$role->addChild('createPost');
$role->addChild('updateOwnPost');
 
$role=$auth->createRole('editor');
$role->addChild('reader');
$role->addChild('updatePost');
 
$role=$auth->createRole('admin');
$role->addChild('editor');
$role->addChild('author');
$role->addChild('deletePost');
 
$auth->assign('reader','readerA');
$auth->assign('author','authorB');
$auth->assign('editor','editorC');
$auth->assign('admin','adminD');

建立此授权等级体系后,authManager 组件(例如 CPhpAuthManagerCDbAuthManager) 就会自动加载授权项目。因此,我们只需要运行上述代码一次,并不需要在每个请求中都要运行。


使用业务规则
在定义授权等级体系时,我们可以将  业务规则  关联到一个角色,一个任务,或者一个操作。 我们也可以在为一个用户分配角色时关联一个业务规则。 一个业务规则就是一段 PHP 代码,在我们执行权限检查时被执行。 代码返回的值用来决定是否将角色或分配应用到当前用户。 在上面的例子中,我们把一条业务规则关联到了 updateOwnPost  任务。 在业务规则中,我们简单的检查了当前用户的 ID 是否与指定帖子的作者 ID 相同。 $params  数组中的帖子(post)信息由开发者在执行权限检查时提供。

权限检查

要执行权限检查,我们首先需要知道授权项目的名字。 例如,要检查当前用户是否可以创建帖子,我们需要检查他是否拥有 createPost 所表示的权限。 然后我们调用 CWebUser::checkAccess 执行权限检查:

if(Yii::app()->user->checkAccess('createPost')){
 }

如果授权规则关联了一条需要额外参数的业务规则,我们也可以传递给它。例如,要检查一个用户是否可以更新帖子, 我们可以通过 $params 传递帖子的数据:

$params=array('post'=>$post);
if(Yii::app()->user->checkAccess('updateOwnPost',$params)){
 }


使用默认角色

注意: 默认角色功能从 1.0.3 版本起可用。

许多 Web 程序需要一些可以分配给系统中所有或大多数用户的比较特殊的角色。 例如,我们可能想要分配一些权限给所有已通过身份验证的用户。如果我们特意指定并存储这些角色分配,就会引起很多维护上的麻烦。 我们可以利用 默认角色 解决这个问题。

默认角色就是一个隐式分配给每个用户的角色,这些用户包括通过身份验证的用户和游客。 我们不需要显式地将其分配给一个用户。 当 CWebUser::checkAccess 被调用时,将会首先检查默认的角色,就像它已经被分配给这个用户一样。

默认角色必须定义在 CAuthManager::defaultRoles 属性中。 例如,下面的配置声明了两个角色为默认角色:authenticated 和 guest

return array(
    'components'=>array(
        'authManager'=>array(
            'class'=>'CDbAuthManager',
            'defaultRoles'=>array('authenticated', 'guest'),
        ),
    ),
);

由于默认角色会被分配给每个用户,它通常需要关联一个业务规则以确定角色是否真的要应用到用户。 例如,下面的代码定义了两个角色, authenticated 和 guest,很高效地分别应用到了已通过身份验证的用户和游客用户。

$bizRule='return !Yii::app()->user->isGuest;';
$auth->createRole('authenticated', 'authenticated user', $bizRule);
 
$bizRule='return Yii::app()->user->isGuest;';
$auth->createRole('guest', 'guest user', $bizRule);



Yii中的主题、日志、性能分析以及错误处理


Theming(主题)

在Yii,每个主题由一个目录代表,包含view文件,layout文件和相关的资源文件,如图片, CSS文件, JavaScript文件等。主题的名字就是他的目录名字。全部主题都放在在同一目录WebRoot/themes下 。在任何时候,只有一个主题可以被激活。


提示:默认的主题根目录WebRoot/themes可被配置成其他的。只需要配置themeManager应用部件的属性basePathbaseUrl为你所要的值。要激活一个主题,设置Web应用程序的属性theme为你所要的名字。可以在application configuration中配置或者在执行过程中在控制器的动作里面修改。

主题目录里面内容的组织方式和application base path目录下的组织方式一样。例如,所有的view文件必须位于views下 ,布局view文件在views/layouts下 ,和系统view文件在views/system下。例如,如果我们要替换PostControllercreate view文件为classic主题下,我们将保存新的view文件为WebRoot/themes/classic/views/post/create.php

对于在module里面的控制器view文件,相应主题view文件将被放在views目录下。例如,如果上述的PostController是在一个命名为forum的模块里 ,我们应该保存create view 文件为WebRoot/themes/classic/views/forum/post/create.php 。如果 forum模块嵌套在另一个名为support模块里 ,那么view文件应为WebRoot/themes/classic/views/support/forum/post/create.php 。

注:由于views目录可能包含安全敏感数据,应当配置以防止被网络用户访问。

当我们调用renderrenderPartial显示视图,相应的view文件以及布局文件将在当前激活的主题里寻找。如果发现,这些文件将被render渲染。否则,就后退到viewPathlayoutPath 所指定的预设位置寻找。

baseurl属性:

在一个主题的视图,我们经常需要链接其他主题资源文件。例如,我们可能要显示一个在主题下images目录里的图像文件。使用当前激活主题的baseurl属性,我们就可以为此图像文件生成如下url,

yii: :app()->theme->baseUrl . '/images/FileName.gif'



下面是2个主题的文件目录列表:

WebRoot/

    assets

    protected/

        .htaccess

        components/

        controllers/

        models/

        views/

            layouts/

                main.php

            site/

                index.php

    themes/

        basic/

            views/

                .htaccess

                layouts/

                    main.php

                site/

                    index.php

        fancy/

            views/

                .htaccess

                layouts/

                    main.php

                site/

                    index.php

在配置文件里面(注意这个配置文件就是main.php,俗称应用配置,而不需要在组件里面配置什么),我们可以这样写:

return array(

    'theme'=>'basic',

    ......

);

配置完成就会使basic主题生效,应用将会使用这个布局themes/basic/views/layouts, 并且site index也会换成主题下面的themes/basic/views/site. 如果在主题里面没有发现view文件,这它会继续在protected/views目录下面进行寻找渲染。



主题小部件(Theme Widgets)

从1.1.5版本开始,views可以使用widget主题。特别当我们调用CWidget::render() 来显示一个widget试图时,Yii会尝试寻找主题文件和Widget文件夹下的view文件。


对于主题试图xyz来说,如果显示它的widget的类名是Foo,我们首先应该在当前的主题下创建一个Foo的文件夹(必须和Widget类名一样). If the widget class is namespaced (available in PHP 5.3.0 or above), such as \app\widgets\Foo, we should create a folder namedapp_widgets_Foo. That is, we replace the namespace separators with the underscore characters.


We then create a view file named xyz.php under the newly created folder. To this end, we should have a filethemes/basic/views/Foo/xyz.php, which will be used by the widget to replace its original view, if the currently active theme is basic.



日志记录

1. 信息记录 

信息可以通过 Yii::log 或 Yii::trace 记录。其 区别是后者只在当应用程序运行在 调试模式(debug mode) 中时才会记录信息。

Yii::log($message,$level$category);

Yii::trace($message,$category);

当记录信息时,我们需要指定它的分类和级别 分类是一段格式类似于 路径别名 的字符串。 例如,如果一条信息是在 CController 中记录的,我们可以使用 system.web.CController 作为分类。信息级别应该是下列值中的一种:

  • trace: 这是在 Yii::trace 中使用的级别。它用于在开发中 跟踪程序的执行流程。
  • info: 这个用于记录普通的信息。
  • profile: 这个是性能概述(profile)。下面马上会有更详细的说明。
  • warning: 这个用于警告(warning)信息。
  • error: 这个用于致命错误(fatal error)信息。


2. 信息路由 

通过 Yii::log 或 Yii::trace 记录的信息是保存在内存中的。 我们通常需要将它们显示到浏览器窗口中,或者将他们保存到一些 持久存储例如文件、Email中。这个就叫作 信息路由,例如, 发送信息到不同的目的地。

在 Yii 中,信息路由是由一个叫做 CLogRouter 的应用组件管理的。 它负责管理一系列称作 日志路由 的东西。每个日志路由 代表一个单独的日志目的地。通过一个日志路由发送的信息会被他们的级别和分类过滤。

要使用信息路由,我们需要安装并预加载一个 CLogRouter 应用组件。我们也还需要配置它的 routes 属性为我们想要的那些日志路由。 下面的代码演示了一个所需的 应用配置 示例:

array(

    ......

    'preload'=>array('log'),

    'components'=>array(

        ......

        'log'=>array(

            'class'=>'CLogRouter',

            'routes'=>array(

                array(

                    'class'=>'CFileLogRoute',

                    'levels'=>'trace, info',

                    'categories'=>'system.*',

                ),

                array(

                    'class'=>'CEmailLogRoute',

                    'levels'=>'error, warning',

                    'emails'=>'admin@example.com',

                ),

            ),

        ),

    ),

)

在上面的例子中,我们定义了两个日志路由。第一个是 CFileLogRoute ,它会把信息保存在位于应用程序 runtime 目录中的一个文件中。 而且只有级别为 trace 或 info 、分类以 system. 开头的信息才会被保存。 第二个路由是 CEmailLogRoute ,它会将信息发送到指定的 email 地址,且只有级别为 error 或 warning 的才会发送。

在 Yii 中,有下列几种日志路由可用:

注:信息路由发生在当前请求周期最后的 onEndRequest 事件触发时。 要显式终止当前请求过程,请调用 CApplication::end() (我测试出来是使用Yii::app()->end();退出) 而不是使用 die() 或exit(),因为 CApplication::end() 将会触发 onEndRequest 事件, 这样信息才会被顺利地记录。


3. 信息过滤 

正如我们所提到的,信息可以在他们被发送到一个日志路由之前通过它们的级别和分类过滤。 这是通过设置对应日志路由的 levels 和 categories 属性完成的。 多个级别或分类应使用逗号连接。

由于信息分类是类似 xxx.yyy.zzz 格式的,我们可以将其视为一个分类层级。 具体地,我们说 xxx 是 xxx.yyy的父级,而xxx.yyy 又是 xxx.yyy.zzz 的父级。 这样我们就可以使用 xxx.* 表示分类 xxx 及其所有的子级和孙级分类

4. 记录上下文信息 


从版本 1.0.6 起,我们可以设置记录附加的上下文信息, 比如 PHP 的预定义变量(例如 $_GET$_SERVER),session ID,用户名等。 这是通过指定一个日志路由的 CLogRoute::filter属性为一个合适的日志过滤规则实现的。


框架可以使用一个 CLogFilter 来处理日志的上下文信息,CLogFilter 默认可以记录一些变量例如$_GET,$_SERVER 。 但是我们可以配置它使它可以记录更多的信息如 session ID, username 等等。

下面的配置告诉我们如何配置一个记录上下文信息的日志。注意其实每个log route可以有它们自己的log filter。默认log route是没有log filter的。

array(

    ......

    'preload'=>array('log'),

    'components'=>array(

        ......

        'log'=>array(

            'class'=>'CLogRouter',

            'routes'=>array(

                array(

                    'class'=>'CFileLogRoute',

                    'levels'=>'error',

                    'filter'=>'CLogFilter',

                ),

                ...other log routes...

            ),

        ),

    ),

)



5. 性能分析(Performance Profiling)

性能分析是一种特别的日志消息,它可以测量一些指定的code blocks 或者找出哪些是影响性能的代码。

使用性能分析,我们仅需要指定出哪些code blocks需要被分析,然后再代码块的开头和结尾插入相应的方法,例如:

Yii::beginProfile('blockID');

...code blockbeing profiled...

Yii::endProfile('blockID');

blockID是唯一的,指向这个代码块的。为了显示分析结果,我们需要安装 CLogRouter 应用组件的 CProfileLogRoute log route功能。这个CProfileLogRoute和我们配置普通的信息routes是一样的。 CProfileLogRoute route 将会显示性能测试结果在当前页面的最下面。

main.php的配置:

log'=>array(

               'class'=>'CLogRouter',

               'routes'=>array(

                    array(

                         'class'=>'CProfileLogRoute',

                    ),

                    array(

                         'class'=>'CFileLogRoute',

                         'levels'=>'error, warning',

                    ),

               ),

          ),


Controller里面的代码块:

public function actionIndex()

     {

          // renders the view file 'protected/views/site/index.php'

          // using the default layout 'protected/views/layouts/main.php'

         

          Yii::beginProfile('index');

          echo 123;

          $this->render('index',array('callback'=>'xiaozhe'));

          echo 345;

          Yii::endProfile('index');

    }

6. SQL分析(Profiling SQL Executions)

一般在一个应用里面,SQL的执行时一个主要的性能瓶颈,所以使用SQL分析是非常有用的。我们可以手动的插入beginProfile and endProfile 语句在适当的地方来测量SQL语句执行花费的时间,从1.0.6开始,Yii提供一个更加系统的方法来解决这个问题。

我们可以在应用配置(Application Config)里面设置 CDbConnection::enableProfiling 为true,这样的话每个SQL执行命令都会被分析。分析结果可以使用CProfileLogRoute来显示出来。我们也可以调用CDbConnection::getStats() 来检索所有的SQL执行以及花费的时间(在controller里面可以这样用:$str = Yii::app()->db->getStats(); var_dump($str);)。

例如:

main.php中配置CDbConnection:

     'db'=>array(

               'connectionString' => 'MySQL:host=localhost;dbname=testdrive',

               'emulatePrepare' => true,

               'username' => 'root',

               'password' => 'xiaozhe',

               'charset' => 'utf8',

               'tablePrefix' => 'tbl_',


               //主要是下面的2句话

               'enableProfiling'=>true,

               'enableParamLogging'=>true,

          ),


main.php中配置Log来显示数据:

log'=>array(

               'class'=>'CLogRouter',

               'routes'=>array(

                    //数据库的信息也是CProfileLogRoute来显示

                    array(

                         'class'=>'CProfileLogRoute',

                    ),

                    array(

                         'class'=>'CFileLogRoute',

                         'levels'=>'error, warning',

                    ),

               ),

          ),


最后在Controller里面进行代码块选定:

     public function actionIndex()

     {

          Yii::beginProfile('id');

         

         

          $dataProvider=new CActiveDataProvider('Post');

          $this->render('index',array(

               'dataProvider'=>$dataProvider,

          ));


          Yii::endProfile('id');

     }





错误处理 

提示: 错误处理器的注册是在应用中的constructor方法中进行的,使用了PHP函数set_exception_handler 和set_error_handler。 如果你不想让Yii来处理错误和异常,你可以在入口文件中定义YII_ENABLE_ERROR_HANDLER和YII_ENABLE_EXCEPTION_HANDLER为false.

 

默认情况下,在触发onError事件(或onException事件)的时候,errorHandler(或exceptionHandler)将被触发。如果错误或者异常未被任何事件所处理,那么就需要运行errorHandler组件来处理了。

 


1. 引发异常

在Yii中引发异常和在普通PHP文件中没什么两样。你可以使用下面的代码来抛出异常:

throw new ExceptionClass('错误信息');

Yii定义了两个异常类:CExceptionCHttpException。前者是一个通用的异常类,而后者用于对最终用户显示异常信息。同时,后者有一个statusCode属性来代表HTTP状态码。异常的类型决定了显示效果,下面会细说。

// 如果提交的ID是无效的 

throw new CHttpException(404,'此页面不存在');




2. 显示错误 

当一个错误被转发给组件CErrorHandler的时候,它会选择合适的视图来显示错误。如果这个错误要显示给最终用户的(比如说一个CHttpException)那么会使用名为errorXXX的视图来显示错误。这个XXX代表着HTTP错误码(比如说400,404,500等)。如果这是个内部错误,应该只能被开发者看到,那么将使用的视图名是exception。在后一种中,将会显示完整的调用栈信息和错误行信息。

CErrorHandler会搜索合适的视图来显示错误信息,搜索的顺序如下:

  1. WebRoot/themes/ThemeName/views/system: 在当前主题视图下的system目录中。
  2. WebRoot/protected/views/system: 在应用的默认视图的system目录中。
  3. yii/framework/views: 在Yii提供的标准视图目录中。

因此,如果你想要自定义错误显示,可以直接在system视图目录中或者主题的system视图目录中创建一个视图文件。每个视图文件都是一个包含许多HTML代码的普通PHP文件。参考框架的view目录下的文件,可以获得更多信息。



3. 使用一个动作来处理错误 

Yii也可以使用控制器 动作来处理错误显示。实现的方法是在应用的配置文件中配置一个错误处理器。

return array(

    ......

    'components'=>array(

        'errorHandler'=>array(

            'errorAction'=>'site/error',

        ),

    ),

)

上面的代码中,我们配置了CErrorHandler::errorAction属性,属性值是一个路由site/error。这个路由指向SiteController中的error。当然,你也可以使用其他的路由。


我们可以这样来编写error动作:

public functionactionError()

{

    if($error=Yii::app()->errorHandler->error)

        $this->render('error',$error);

}

在这个动作中,首先从CErrorHandler::error中取得详细的错误信息。如果取得的信息非空,就使用CErrorHandler::error返回的信息来渲染error视图。CErrorHandler::error返回的信息是一个数组,结构如下:

  • code: HTTP 状态码(比如 403, 500);
  • type: 错误类型(比如 CHttpExceptionPHP Error);
  • message: 错误信息;
  • file: 发生错误的PHP文件名;
  • line: 错误所在的行;
  • trace: 错误的调用栈信息;
  • source: 发生错误的代码的上下文。

4. 消息记录 

一个error级别的错误信息会在错误发生时候被记录。如果这个错误是由PHP warning 或 notice引发的,那么这个消息将会被记录在php这个分类中;如果错误信息是由未捕获的异常所引起的,那么分类将是exception.ExceptionClassName(对于CHttpException来说,它的statusCode也将被追加到分类名中)。开发者可以使用这些记录来监测应用执行时候的错误信息。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值