定义一个Error类,当系统找不到指定的控制器名称的时候,系统会尝试定位空控制器(Error),利用这个机制我们可以用来定制错误页面和进行URL的优化。 如:当输入正确的控制器以及方法名时是如下效果: 当控制器名乱输入时: 实现代码如下: 下面展示一些 代码片。
<?php namespace app\index\controller; use think\Request; class Error { public function index(Request $request) { //根据当前控制器名来判断要执行哪个的操作 $hello = $request->controller(); return $this->hello($hello); } //注意 hello方法 本身是 protected 方法 protected function hello($name) { //和$name这个hello相关的处理 return '你真是个靓仔哦' . $name; } }可以为某个或者某些操作指定前置执行的操作方法,设置 beforeActionList属性可以指定某个方法为其他方法的前置操作,数组键名为需要调用的前置方法名,无值的话为当前控制器下所有方法的前置方法。
['except' => '方法名,方法名']//表示这些方法不使用前置方法 ['only' => '方法名,方法名']//表示只有这些方法使用前置方法。实现代码如下: 下面展示一些代码片。
<?php namespace app\index\controller; use think\Controller; class Index extends Controller { //前置方法demo protected $beforeActionList = [ 'first', 'second' => ['except'=>'hello'], 'three' => ['only'=>'hello,data'], ]; protected function first() { echo 'first<br/>'; } protected function second() { echo 'second<br/>'; } protected function three() { echo 'three<br/>'; } public function hello() { return 'hello'; } public function data() { return 'data'; } }。
下面展示一些 内联代码片。
<?php namespace app\index\controller; use think\Controller; use think\Request; class Index extends Controller { public function index() { echo "获取URL信息:".'<br />'; $request = Request::instance(); // 获取当前域名 echo '获取当前域名domain: ' . $request->domain() . '<br/>'; // 获取当前入口文件 echo '获取当前入口文件file: ' . $request->baseFile() . '<br/>'; // 获取当前URL地址 不含域名 echo '获取当前URL地址 不含域名url: ' . $request->url() . '<br/>'; // 获取包含域名的完整URL地址 echo '获取包含域名的完整URL地址url with domain: ' . $request->url(true) . '<br/>'; // 获取当前URL地址 不含QUERY_STRING echo '获取当前URL地址 不含QUERY_STRINGurl without query: ' . $request->baseUrl() . '<br/>'; // 获取URL访问的ROOT地址 echo '获取URL访问的ROOT地址root:' . $request->root() . '<br/>'; // 获取URL访问的ROOT地址 echo '获取URL访问的ROOT地址root with domain: ' . $request->root(true) . '<br/>'; // 获取URL地址中的PATH_INFO信息 echo '获取URL地址中的PATH_INFO信息pathinfo: ' . $request->pathinfo() . '<br/>'; // 获取URL地址中的PATH_INFO信息 不含后缀 echo '获取URL地址中的PATH_INFO信息 不含后缀pathinfo: ' . $request->path() . '<br/>'; // 获取URL地址中的后缀信息 echo '获取URL地址中的后缀信息ext: ' . $request->ext() . '<br/>'; // 设置/获取 模块/控制器/操作名称 $request = Request::instance(); echo "当前模块名称是" . $request->module(); echo "当前控制器名称是" . $request->controller(); echo "当前操作名称是" . $request->action(); //获取请求参数 $request = Request::instance(); echo '请求方法:' . $request->method() . '<br/>'; echo '资源类型:' . $request->type() . '<br/>'; echo '访问地址:' . $request->ip() . '<br/>'; echo '是否AJax请求:' . var_export($request->isAjax(), true) . '<br/>'; echo '请求参数:'; dump($request->param()); echo '请求参数:仅包含name'; dump($request->only(['name'])); echo '请求参数:排除name'; dump($request->except(['name'])); // 获取路由和调度信息 $request = Request::instance(); echo '路由信息:'; dump($request->route()); echo '调度信息:'; dump($request->dispatch()); // 请求缓存 // 请求缓存的原理是第一次请求的时候会根据当前请求的缓存标识把响应输出的内容缓存起来并且设置HTTP缓存(如果判断已经存在请求缓存的话会直接读取请求缓存并且设置HTTP缓存),当第二次访问相同的请求标识的时候,会自动读取HTTP缓存(也就是浏览器缓存)内容而不是真实的调用请求方法,也就是说请求缓存是HTTP缓存+响应(数据)缓存的合体。 // // 如果你需求全局使用请求缓存的话,在应用配置中设置下面的两个配置参数: // // 'request_cache' => true, // 'request_cache_expire' => 600, // 上面的设置会开启全局请求缓存,默认的缓存标识为当前请求的URL地址(做md5编码处理),并且缓存有效期为600秒,也就是说10分钟之内的相同get请求(请求缓存只支持GET请求)会进行缓存,可以有效提升性能。 // 设置请求信息 // 如果某些环境下面获取的请求信息有误,可以手动设置这些信息参数,使用下面的方式: $request = Request::instance(); $request->root('index.php'); $request->pathinfo('index/index/hello'); } }下面展示一些 内联代码片。
<?php namespace app\index\controller; use think\Request; use think\Controller; class Time extends Controller { /** * 获取毫秒级别的时间戳 */ public function test() { echo "time函数:";echo"</br>"; echo time();echo"</br>"; //获取毫秒的时间戳 $time = explode ( " ", microtime () ); $time = $time[1] . ($time[0] * 1000); $time2 = explode( ".", $time ); $time = $time2[0]; echo "毫秒级时间戳:";echo"</br>"; return $time; } }下面展示一些 内联代码片。
<?php namespace app\index\controller; use think\Controller; class Color extends Controller { /** * 随机颜色获取器 */ function randomColor() { $str = '#'; for($i = 0 ; $i < 6 ; $i++) { $randNum = rand(0 , 15); switch ($randNum) { case 10: $randNum = 'A'; break; case 11: $randNum = 'B'; break; case 12: $randNum = 'C'; break; case 13: $randNum = 'D'; break; case 14: $randNum = 'E'; break; case 15: $randNum = 'F'; break; } $str .= $randNum; } dump($str) ; return "<span style='color:$str '>好好学习,天天向上</span>"; } }下面展示一些 内联代码片。
<?php namespace app\index\controller; use think\Controller; class Exchange extends Controller { /* * 数组转xml */ public function xmlToEncode($data) { $xml = $attr = ""; foreach($data as $key => $value) { if(is_numeric($key)) { $attr = " id='{$key}'"; $key = "item"; } $xml .= "<{$key}{$attr}>"; $xml .= is_array($value) ? self::xmlToEncode($value) : $value; $xml .= "</{$key}>\n"; } return $xml; } public function test(){ $arr=[]; $arr=[ '0'=>"hhh", '1'=>"好好学习", '2'=>"天天向上" ]; echo "数组转xml"; dump($this->xmlToEncode($arr)); } }下面展示一些 内联代码片。
<?php namespace app\index\controller; use think\Controller; class Replace extends Controller { public function hideStar($str) { //用户名、邮箱、手机账号中间字符串以*隐藏 if (strpos($str, '@')) { $email_array = explode("@", $str); $prevfix = (strlen($email_array[0]) < 4) ? "" : substr($str, 0, 3); //邮箱前缀 $count = 0; $str = preg_replace('/([\d\w+_-]{0,100})@/', '***@', $str, -1, $count); $rs = $prevfix . $str; } else { $pattern = '/(1[3458]{1}[0-9])[0-9]{4}([0-9]{4})/i'; if (preg_match($pattern, $str)) { $rs = preg_replace($pattern, '$1****$2', $str); // substr_replace($name,'****',3,4); } else { $rs = substr($str, 0, 3) . "***" . substr($str, -1); } } return $rs; } public function replace() { $account = "phphhhhhh.com"; $email = "1111111111111123@qq.com"; $phone = "18005152525"; echo $this->hideStar($account);echo"<br>"; echo $this->hideStar($email);echo"<br>"; echo $this->hideStar($phone); } }下面展示一些 内联代码片。
<?php namespace app\index\controller; use think\Controller; class Demo extends Controller { /* * 用php内置函数验证ip是否合法 */ public function domain(){ $ip="192.168.1.1"; if(filter_var($ip, FILTER_VALIDATE_IP)) { echo"it's valid"; } else { echo"it's not valid"; } } /* *纯php验证ip是否合法 */ function checkIp($ip){ $arr=explode('.',$ip); if(count($arr) != 4){ return false; }else{ for($i = 0;$i < 4;$i++){ if(($arr[$i] <'0') || ($arr[$i] > '255')){ return false; } } } return true; } public function ipcheck() { $result=$this->checkIp('127.0.0.1'); dump($result); } }下面展示一些 内联代码片。
<?php namespace app\index\controller; use think\Controller; class Qrcode extends Controller { public function qrcode(){ Vendor('phpqrcode.phpqrcode'); $value="好好学习,天天向上";//二维码内容 Vendor('phpqrcode.phpqrcode'); $errorCorrectionLevel = 'L'; //容错级别 $matrixPointSize = 5; //生成图片大小 //生成二维码图片 $filename = ROOT_PATH.'public/qrcode/images'.'.png'; // dump($filename); \QRcode::png($value, $filename, $errorCorrectionLevel, $matrixPointSize, 2); $QR = $filename; //已经生成的原始二维码图片文件 $QR = imagecreatefromstring(file_get_contents($QR)); //输出图片 imagepng($QR, 'qrcode.png'); imagedestroy($QR); $res='<img src="/tp5demo20201008/public/qrcode/images.png" alt="">'; $this->assign("res",$res); return $this->fetch(); } } <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui"> <title>二维码保存或分享</title> <style type="text/css"> body{ background-color: #f8f8f8; line-height: 1.3; } .weui-cells__title{ color: black; padding-left: 30px; margin-top: 1.66em; margin-bottom: 1.10em; } .qrcode{ text-align: center; } .code{ width: 200px; height: 200px; margin: 0 auto; } .code img{ width: 200px; height: 200px; } .tips{ color:#515151; font-size: 14px; } .tipstwo{ color:#df1101; font-size: 14px; } .bottom{ font-size: 12px; color:#999999; text-align: center; margin-top:40px; margin-bottom: 20px; } </style> </head> <body> <!--<div class="weui-cells__title">请长按二维码分享或保存</div>--> <div class="qrcode"> <div class="code">{$res}</div> </div> <div class="bottom">Copyright © 2019 hzod.hosp</div> </body> </html>
令牌桶算法比漏桶算法稍显复杂。首先,我们有一个固定容量的桶,桶里存放着令牌(token)。桶一开始是空的(可用token数为0),token以一个固定的速率r往桶里填充,直到达到桶的容量,多余的令牌将会被丢弃。每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通过。
下面展示一些 内联代码片。
class TokenBucketDemo{ private $last_req_time; //上次请求时间 public $capacity; //桶的容量 public $rate; //令牌放入的速度(个/秒) public $tokens; //当前可用令牌的数量 public function __construct(){ $this->last_req_time = time(); $this->capacity = 100; $this->rate = 20; $this->tokens = 100; //开始给100个令牌 } public function grant(){ $now = time(); $tokens = min($this->capacity,$this->tokens + ($now - $this->last_req_time) * $this->rate);// 计算桶里可用的令牌数 $this->tokens = $tokens; $this->last_req_time = $now; if($this->tokens < 1){ // 若剩余不到1个令牌,则拒绝 return false; }else{ // 还有令牌,领取1个令牌 $this->tokens -= 1; return true; } } } $m = new TokenBucketDemo(); $n_success = 0; for($i=0; $i < 500; $i++){ $rt = $m->grant(); if($rt){ $n_success ++; } if($i > 0 && $i % 100 == 0){//每发起100次后暂停1s echo '已发送',$i,', 成功 ', $n_success,', sleep'.PHP_EOL; sleep(1); } } echo '成功请求 '.$n_success.' 次';我们可以使用redis的队列作为令牌桶容器使用,使用lPush(入队),rPop(出队),实现令牌加入与消耗的操作。 TokenBucket.php 下面展示一些 内联代码片。
<?php /** * PHP基于Redis使用令牌桶算法实现接口限流,使用redis的队列作为令牌桶容器,入队(lPush)出队(rPop)作为令牌的加入与消耗操作。 * public add 加入令牌 * public get 获取令牌 * public reset 重设令牌桶 * private connect 创建redis连接 */ class TokenBucket{ // class start private $_config; // redis设定 private $_redis; // redis对象 private $_queue; // 令牌桶 private $_max; // 最大令牌数 /** * 初始化 * @param Array $config redis连接设定 */ public function __construct($config, $queue, $max){ $this->_config = $config; $this->_queue = $queue; $this->_max = $max; $this->_redis = $this->connect(); } /** * 加入令牌 * @param Int $num 加入的令牌数量 * @return Int 加入的数量 */ public function add($num=0){ // 当前剩余令牌数 $curnum = intval($this->_redis->lSize($this->_queue)); // 最大令牌数 $maxnum = intval($this->_max); // 计算最大可加入的令牌数量,不能超过最大令牌数 $num = $maxnum>=$curnum+$num? $num : $maxnum-$curnum; // 加入令牌 if($num>0){ $token = array_fill(0, $num, 1); $this->_redis->lPush($this->_queue, ...$token); return $num; } return 0; } /** * 获取令牌 * @return Boolean */ public function get(){ return $this->_redis->rPop($this->_queue)? true : false; } /** * 重设令牌桶,填满令牌 */ public function reset(){ $this->_redis->delete($this->_queue); $this->add($this->_max); } /** * 创建redis连接 * @return Link */ private function connect(){ try{ $redis = new Redis(); $redis->connect($this->_config['host'],$this->_config['port'],$this->_config['timeout'],$this->_config['reserved'],$this->_config['retry_interval']); if(empty($this->_config['auth'])){ $redis->auth($this->_config['auth']); } $redis->select($this->_config['index']); }catch(RedisException $e){ throw new Exception($e->getMessage()); return false; } return $redis; } } ?> 令牌的假如与消耗 <?php /** * 演示令牌加入与消耗 */ require 'TokenBucket.php'; // redis连接设定 $config = array( 'host' => 'localhost', 'port' => 6379, 'index' => 0, 'auth' => '', 'timeout' => 1, 'reserved' => NULL, 'retry_interval' => 100, ); // 令牌桶容器 $queue = 'mycontainer'; // 最大令牌数 $max = 5; // 创建TrafficShaper对象 $tokenBucket = new TokenBucket($config, $queue, $max); // 重设令牌桶,填满令牌 $tokenBucket->reset(); // 循环获取令牌,令牌桶内只有5个令牌,因此最后3次获取失败 for($i=0; $i<8; $i++){ var_dump($tokenBucket->get()); } // 加入10个令牌,最大令牌为5,因此只能加入5个 $add_num = $tokenBucket->add(10); var_dump($add_num); // 循环获取令牌,令牌桶内只有5个令牌,因此最后1次获取失败 for($i=0; $i<6; $i++){ var_dump($tokenBucket->get()); } ?>