@Purpose
2017-03-12T08:08:02.000000Z
字数 7463
阅读 1202
YII学习笔记
lazy loading技术,yii加载技术依赖于php的spl_autoload_register(),注册一个自己的自动加载函数(autoloader),并插入到自动加载函数栈的最前面,确保yii的autoloader会被最先调用,类自动加载的这个机制的引入要从入口文件
index.php开始说起。在框架启动最开始时,入口脚本index.php(basic/web)会执行Yii.php文件,这时候就注册了自动加载的函数autoload,以后PHP程序在执行的时候,遇到一些不认识的类的时候,就会通过autoload函数去自动加载各种各样的类的信息,以此来完成延迟加载,进一步提高程序的执行效率。
<?phpdefined('YII_DEBUG')or define('YII_DEBUG',false);defined('YII_ENV')or define('YII_ENV','prod');//这个是第三方的autoloaderrequire(__DIR__.'/../vendor/autoload.php');//这个是yii的Autoloader,放在最后面,确保 其插入的autoloader会放在最前面require(__DIR__.'/../vendor/yiisoft/yii2/Yii.php');require(__DIR__.'/../../common/config/aliases.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'));$application=new yii\web\Application($config);$application->run();//这个文件主要看点在于第三方的autoloader与Yii实现的autoloader的顺序,不管第三方的代码是如何使用spl_autoloader_register()来注册资金的autoloader的。只有yii的代码在最后面,就可以确保其可以将自己的autoloader插入到整个 autoloader栈的最前面,从而在需要时最先被调用
/*****lazy_loading/index.php*****///Yii框架也根据这种机制实现了类的延迟加载,做得更加地道(使用spl_autoload_register()自动完成类的加载)。//第五步、所以在最后,当PHP发现实例化时不认识Class1,它会把Class1的名字传递给my_loader函数的$class参数当中,那么$class变量就代表了Class1。(同理,当PHP实例化Class2时也如此)function my_loader($class){//第三步、但是以下的代码不仅加载了Class1,还加载了Class2,导致类还是会被多余加载。怎样解决这个问题?/***require('class\Class1.php');********require('class\Class2.php');***///第四步、所以可以通过$class对require代码进行优化:把Class1改为$class变量,因为\具有转义的意思,所以需要2个\\来代表1个\。require('class\\'.$class.'.php');}//第二步、这是由于PHP准备报错时,这个函数告诉PHP先不报错,先去运行一下my_loader。然后PHP真的去运行my_loader函数,把里面的Class1类文件加载进来了。也就不会报错了。spl_autoload_register(my_loader);$is_girl = $_GET['sex'] == 0 ? ture : false;if ($is_girl) {echo 'this is a girl!';$Class1 = new Class1;//第一步、当PHP运行到这一行,不认识Class1并且在这里也没有加载类文件,这时候PHP八成会报错,但实际上并没有报错。}else{echo 'not a girl!';$Class2 = new Class2;}/*****lazy_loading/class/Class1.php*****/class Class1{}/*****lazy_loading/class/Class2.php*****/class Class2{}?>
Yii框架也提供了类的映射表机制去进一步地更快加载类,可以通过使用
Yii::$classMap,对延迟加载机制进行优化,是典型的空间换时间的做法(常用的类,不建议不常用的类也放在$classMap中,会让在这个数组里查找相应的类的速率降低同时会占用更大的内存)
//使用类的映射表去加载Order类,使用\Yii全局类里的$classMap['参数key:加载的类的全名','']数组,因为是要根据它的名字去加载它的绝对路径,所以这个数组的值就是Order类所在类文件的绝对路径。\Yii::$classMap['app\models\Order'] = 'C:\wamp\www\mooc\yii\basic\modelsx\Order.php';//实例化Order活动记录$order = new Order;
index.php请求交给应用主体后,应用主体实例化后会加载一系列组件(如session,response等),应用主体加载后会交给控制器使用。
组件的延迟加载就是:应用主体并没有加载进来,而是在控制器真正使用时才被加载进来,又app初始化时加载延迟到控制器真正使用时,如:
$session=\Yii::$app->session;
session实际不存在,只有调用时,才加载,流程是:
当访问session时,触发PHP的魔术方法__get()方法,方法中将组件加载,实现了延迟加载/初始化
比如用户给Yii框架的项目发送了一个请求,index.php入口脚本文件最先处理这个请求->再把请求交给应用主体app处理(在处理请求之前,把它自己给实例化出来,实例化过程当中会去加载组件<-组件components[包含:session/request/response...组件])->app加载完组件之后再把请求交给Controller处理(控制器在处理请求时可以使用app加载过来的组件)。
那么所谓的组件延迟加载就是:看起来好像是app预先加载了components里的组件,然后在Controller中直接拿过来用。实际上app并没有真正的加载components里的组件,而是在Controller里真正使用到某一个组件(如session)时才加载进来,也就是说把这个组件的加载过程由app的初始化延迟到Controller真正的使用某一个组件时。
//获取缓存组件$cache=\YII::$app->cache;//往缓存中写入数据add$cache->add('key1','hello world');//修改数据set$cache->set('key1','hahhaha');//删除数据delete$cache->delete('key1');//只能删除一条//清空缓存flush$cache->flush();//读缓存get$data=$cache->get('key1');var_dump($data);
//获取缓存组件$cache=\YII::$app->cache;//有效期设置15秒$cache->add('key','value',15); //add新增不覆盖$cache->set('key1','value',15); //set不论有没有,直接覆盖//读取缓存echo $cache->get('key');
//文件依赖 如果hw.txt发生修改 缓存失效或为false$dependency = new \yii\caching\FileDependency(['fileName'=>'hw.txt']);$cache->add('file_key','hello depend', 3000, $dependency);//表达式依赖 表达式的值发生变化则缓存失效$dependency = new \yii\caching\ExpressionDependency(['expression'=>'\YII::$app->request->get("name")']);$cache->add('expression_key', 'hello cache', 3000, $dependency);//DB依赖 数据表数据修改则缓存失效$dependency = new \yii\caching\DbDependency(['sql'=>'select count(*) from yii.order'])$cache->add('db_key', 'hello cache', 3000, $dependency);
//控制器中开启return $this->render('view_file_name');//在视图文件中设置if ($this->beginCache('cache_name')) {/***...html code...***/$this->endCache();}
由调用方法可知$this->beginCache('name')负责判断及输出缓存内容
若无缓存,则返回true,进入到if的代码块内;
然后由$this->endCache()捕捉if代码块内的输出并缓存;
下次再运行至此处时,beginCache将输出缓存并返回false,以跳过if代码块
//前端页面的区域(不经常动的)//缓存时间$duration = 15;if($this->beginCache('cache_div',['duration'=>$duration])){<div id="cache_div"></div>}$this->endCache();//缓存依赖$dependency = ['class'=>'yii\caching\FileDependency','fileName'=>'hw.txt'];if($this->beginCache('cache_div',['dependency'=>$dependency])){<div id="cache_div"></div>}$this->endCache();//缓存开关$enabled = false;if($this->beginCache('cache_div',['enabled'=>$enabled])){<div id="cache_div"></div>}$this->endCache();
<!--HelloController.php-->return $this->renderPartial('cache');<!--basic/views/hello/cache.php--><!-- 片段缓存嵌套 --><!-- 片段缓存的嵌套使用有时会产生一些问题:比如给内外的beginCache都设上一个缓存有效时间duration。第1次刷新浏览器时,outer和inner里的内容都被缓存了;在20内,如果此时修改了inner里的缓存内容,再次刷新浏览器,inner里修改后的缓存内容不会被显示,这是因为在20秒有效期内,PHP只解析了outer的缓存(没过期直接抛出缓存数据),没有去解析inner的缓存(尽管inner缓存的有效期已过并且缓存内容修改了);只有当outer的20秒有效期过期,此时刷新浏览器,inner里被修改过的缓存内容才会被显示出来。--><?php if($this->beginCache('cache_div',['duration'=>20])){?><div id='cache_outer_div'><div>这里是外层,待会会被缓存</div></div><?php if($this->beginCache('cache_inner_div',['duration'=>1])){?><div id='cache_inner_div'><div>这里是内层,待会会被缓存</div></div><?php$this->endCache();}?><?php$this->endCache();}?>
//behavior会先于action操作被YII框架先执行public function behaviors(){echo '1';return [['class'=>'yii\filters\PageCache',//开启页面缓存'duration'=>1000,//缓存时间'only'=>['index','test']//指定缓存哪些方法'dependency'=>['class'=>'yii\caching\DbDependency','fileName'=>'hw.txt']//缓存的依赖方式]];}public function actionIndex(){echo '2';}public function actionTest(){echo '2';}
last-modify是个时间,用来区别是否有修改,但还是会存在文件实质上没有被修改,但修改时间(last-modify)变化的情况,此时就要根据etag来区分是否真正有修改.
在浏览器端,通过请求头信息last-modified,etag来判断是文件是否发生变化,若无无变化,则使用状态码304(Not Modified),通知浏览器使用缓存
<?phpnamespace app\controllers;use yii\web\Controller;use app\models\Customer;use app\models\Order;class HelloController extends Controller{/*** 先于操作来执行* 1.F是怎么告诉L把数据缓存在浏览器本地的呢?* HttpCache类决定了L要不要缓存F发送给它的数据。F发送数据给L是以响应的方式,响应分为响应头和响应体2部分* HttpCache在响应发送给L之前,会往响应头存放一些缓存相关的信息(Cache-Control:"public,max-age=3600")* 然后L在接收到响应之后会根据缓存相关信息(Cache-Control)来决定把数据缓存在L这边*/public function behaviors(){//行为相关的方法return [['class'=>'yii\filters\HttpCache',//时间标识'lastModified'=>function(){return 1432817565;//返回一个时间戳}//内容标识'etagSeed'=>function(){return 'etagseed2';//返回字符串}]];}/*** 自己设置的last_modified的时间戳,使得两次请求携带的last_modified时间戳是一样的,* 即使修改了文件的数据信息,自定义设置的last_modified没有修改* (在未自定义设置last_modified的情况下,last_modified的时间戳是会自动修改为修改时的时间戳的)*/public function actionIndex(){return $this->renderPartial('index');}}
http缓存实例
<?phpnamespace app\controllers;use yii\web\Controller;use app\models\Customer;use app\models\Order;class HelloController extends Controller{public function behaviors(){//行为相关的方法return [['class'=>'yii\filters\HttpCache','lastModified'=>function(){//根据浏览器的数据和服务器的数据修改日期是否一样return filemtime('hw.txt');//取最后一次修改时间,返回一个时间戳},'etagSeed'=>function(){//判断浏览器的数据和服务器的数据内容是否一样$fp = fopen('hw.txt','r');$title = fgets($fp);//取第一行数据fclose($fp);return $title;//和返回数据内容相关的字符串}]];}public function actionIndex(){$content = file_get_contents('hw.txt');//把文件全部数据读出来return $this->renderPartial('index',['new'=>$content]);}}/*********************************************************************/views/hello/index.php<div><div><?=$new;?></div></div>