1133
本篇介绍了使用ThinkPHP接入qq互联实现第三方登录的方法,作为一个小案例来为各位讲解,希望对各位有帮助。ThinkPHP接入QQ互联实现登录的案例分析我的一个二级域名项目想在也想接入QQ第三方登录功能,该项目采用的thinkphp5框架开发的项目,在网上搜了一些接入的案例,个人觉得鱼龙混杂不太适合自己,现在自己重新在thinkphp5框架上开发这个功能,下面是详细的开发步骤。(推荐教程:thinkphp教程)第一步、下载QQ互联SDK,我们是基于thinkphp5框架下的,当然是要用PHP版的SDK,下载下来后的文件目录如下。第二步、将SDK主要目录上传到服务器合适的目录下,先说下SDK的主要的目录是API文件夹里面的class目录,当初为了做配置设置项测试,我上传了install文件夹,然后再开发环境配置了APP ID、APP Key以及callback_url,配置好之后会在API/comm文件夹中多处一个inc.php配置文件,最后再recorder类中会引用这个配置文件。可是在后面的 开发过程中我发现会报这个错The state does not match. You may be a victim of CSRF。后面我把qqlogin方法里面的 state放到session中,对官网的DEMO SDK已经完全失去信心了,不在用QQ互联全部的文件而是挑几个重要的类文件来做开发。后面想想官方给的SDK只是普通的PHP代码格式,我应用到thinkphp那很多东西都已经变了,最后我选择上个类文件,QC.php、URL.php、Oauth.php上传到extend/qqlogin目录下。在thinkphp5的项目中扩展类一般上传到extend文件夹下,如下图所示我上次的目录位置。第三步、改造上述三个类文件,因为QC.php是继承了Oauth.php,我们从后者改起,去掉require_once,加上命名空间如namespace qqlogin,首先看成员属性,类常量是腾讯平台的地址,不用管,原来有三个属性,recorder、error不需要,注释掉或直接删掉。下文同样,因为5个类文件我们只用到3个类文件,一个是报错类一个读取配置相关类。下面看Oauth.php成员属性、qqlogin跳转方法、qqcallback回调方法的,其他两个类文件没有太大的改大,按照上述规则改即可123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778<?php/* PHP SDK * @version 2.0.0 * @author connect@qq.com * @copyright © 2013, Tencent Corporation. All rights reserved. */namespace qqlogin;use qqlogin;class Oauth{ const VERSION = "2.0"; const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize"; const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token"; const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me"; // protected $recorder; public $urlUtils; // protected $error; function __construct(){ // $this->recorder = new Recorder(); $this->urlUtils = new URL(); // $this->error = new ErrorCase(); } public function qq_login(){ // $appid = $this->recorder->readInc("appid"); // $callback = $this->recorder->readInc("callback"); // $scope = $this->recorder->readInc("scope"); $appid = $this->appid; $callback = $this->callback; $scope = $this->scope; //-------生成唯一随机串防CSRF攻击 $state = md5(uniqid(rand(), TRUE)); // $this->recorder->write('state',$state); session('state',$state); //-------构造请求参数列表 $keysArr = array( "response_type" => "code", "client_id" => $appid, "redirect_uri" => $callback, "state" => $state, "scope" => $scope ); $login_url = $this->urlUtils->combineURL(self::GET_AUTH_CODE_URL, $keysArr); return $login_url; } public function qq_callback(){ // $state = $this->recorder->read("state"); //--------验证state防止CSRF攻击 if(input('state') != session('state')){ // $this->error->showError("30001"); exit('30001'); } //-------请求参数列表 $keysArr = array( "grant_type" => "authorization_code", "client_id" => $this->appid, "redirect_uri" => urlencode($this->callback), "client_secret" => $this->appkey, "code" => $_GET['code'] ); //------构造请求access_token的url $token_url = $this->urlUtils->combineURL(self::GET_ACCESS_TOKEN_URL, $keysArr); $response = $this->urlUtils->get_contents($token_url); if(strpos($response, "callback") !== false){ $lpos = strpos($response, "("); $rpos = strrpos($response, ")"); $response = substr($response, $lpos + 1, $rpos - $lpos -1); $msg = json_decode($response); // if(isset($msg->error)){ // $this->error->showError($msg->error, $msg->error_description); // } } $params = array(); parse_str($response, $params); // $this->recorder->write("access_token", $params["access_token"]); // return $params["access_token"]; session('access_token',$params["access_token"]); }}第四步、编写控制器调用函数和回调函数,同时检查回调地址是否正确(回调地址是当你添加QQ第三方登录QQ互联返回跳转的地址,该地址携带重要参数,能获取最后用户的数据),有些时候如果你在QQ互联填写的回调地址与你控制器的不一样,那么最后就会卡在那个QQ互联填写的回调地址那,如www.100txy.com/index.php?code=65B7668A4F1BBB71DD0DF52B55AC1FC1&state=804e921e18e3545ecdf690316639c067。下面是控制器方法12345678910111213141516use qqlogin\QC;// 处理qq登录 public function qqlogin(){ $qq = new QC(); $url = $qq->qq_login(); $this->redirect($url); } // qq登录回调函数 public function qqcallback(){ $qq = new QC(); $qq->qq_callback(); $qq->get_openid(); $qq = new QC(); $datas = $qq->get_user_info(); die(var_dump($datas));//为用户数据 }值得注意的是在回调函数里面要实例化两次QC才能拿到用户信息,第二次实例化的时候才有openid和access_token两个参数。更多Thinkphp教程,请关注PHP中文网!以上就是ThinkPHP接入QQ互联实现登录的案例分析的详细内容
0
0 1409天前
1023
本篇文章介绍了thinkphp框架实现点击图片刷新验证码的方法,希望对学习thinkphp的朋友有帮助!ThinkPHP实现点击图片刷新验证码本类验证码功能刷新功能,涉及到两个刷新,一个是点击验证码图片刷新,另一个是输错验证码刷新,当然自己刷新那就不要说了,那是肯定会刷新的。(推荐教程:thinkphp教程)thinkPHP框架里面内置了verify.class.php验证类,我们做验证码验证的时候就非常简单了,这里提供两种验证的格式,一个纯数字,一个是系统默认的,其中里面的方法都是写在login控制器中的方法,具体代码如下12345678//第一种默认的验证码public function captcha(){ $config = array( 'length' => 4, // 验证码位数 ); $Verify = new \Think\Verify($config); $Verify->entry(); }另外一种12345678910111213141516//第二种默认的验证码 function show_verify($config=''){ if($config==''){ $config=array( 'codeSet'=>'1234567890', 'fontSize'=>30, 'useCurve'=>false, 'imageH'=>60, 'imageW'=>240, 'length'=>4, 'fontttf'=>'4.ttf', ); } $verify=new \Think\Verify($config); return $verify->entry(); }显示验证码图片的HTML代码1234<div class="form-group"> <input type="text" class="form-control" placeholder="验证码" required="" name="verify" > <img class="verify" src="{:U('Admin/Login/show_verify')}" title="点击更换" onclick="this.src+='';"> </div>图片onclick事件就能完成验证码的刷新了更多ThinkPHP框架教程,请关注PHP中文网!以上就是ThinkPHP实现点击图片刷新验证码的详细内容
0
0 1409天前
1023
ThinkPHP防止XSS攻击的方法1 如果您的项目没有富文本编辑器 然后就可以使用全局过滤方法 在application下面的config配置文件 加上 htmlspecialchars12// 默认全局过滤方法 用逗号分隔多个'default_filter' => 'htmlspecialchars',如果有富文本编辑器的话 就不适合 使用这种防XSS攻击(推荐教程:thinkphp教程)那么使用 composer 安装插件来处理命令1composer require ezyang/htmlpurifier安装成功以后在application 下面的 common.php 放公共函数的地方添加如下代码1234567891011121314151617181920if (!function_exists('remove_xss')) { //使用htmlpurifier防范xss攻击 function remove_xss($string){ //composer安装的,不需要此步骤。相对index.php入口文件,引入HTMLPurifier.auto.php核心文件 // require_once './plugins/htmlpurifier/HTMLPurifier.auto.php'; // 生成配置对象 $cfg = HTMLPurifier_Config::createDefault(); // 以下就是配置: $cfg -> set('Core.Encoding', 'UTF-8'); // 设置允许使用的HTML标签 $cfg -> set('HTML.Allowed','div,b,strong,i,em,a[href|title],ul,ol,li,br,p[style],span[style],img[width|height|alt|src]'); // 设置允许出现的CSS样式属性 $cfg -> set('CSS.AllowedProperties', 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align'); // 设置a标签上是否允许使用target="_blank" $cfg -> set('HTML.TargetBlank', TRUE); // 使用配置生成过滤用的对象 $obj = new HTMLPurifier($cfg); // 过滤字符串 return $obj -> purify($string);}然后在 application目录下的config.php 配置文件把这个过滤方法改成那个方法名即可结合框架的使用 和插件的使用可以使用这个 上面的代码可以可以直接使用的也可以针对某个字段进行xss验证1 修改 command的文件把改成这个 'default_filter' => 'htmlspecialchars',2 然后在你要更改的字段 上面 修改成相关推荐:PHP视频教程,学习地址:https://www.php.cn/course/list/29/type/2.html以上就是ThinkPHP防止XSS攻击的方法的详细内容
0
0 1409天前
986
Thinkphp5模板继承和替换的问题案例同一个模块下的common继承问题,这里于index模块为例在index模块下有自己的common和模块主视图文件夹index,那么我index0里面继承了自己的base.html是这样的(推荐教程:thinkphp教程)123456789101112131415161718192021222324252627282930313233343536373839404142//base.html文件<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title> {block name="title"} 雷小天thinkPHP开发版 {/block} </title> <link rel="stylesheet" type="text/css" href="__CSS__/style.css"> <link rel="stylesheet" type="text/css" href="__LAYUI__/css/layui.css"> <script type="text/javascript" src="__LAYUI__/layui.js"></script> </head> <body> <div class="wrap"> <!-- 头部 --> <div class="header"> {include file="common/nav" /} </div> <!-- 中部 --> <div class="main"> <!-- 边栏 --> <div class="body"> {block name="body"} {/block} </div> <!-- 内容 --> <div class="sidebar"> {block name="sidebar"} {/block} </div> </div> <!-- 尾部 --> <div class="footer"> {block name="footer"} 默认值footer {/block} </div> </div> </body></html>下面是index0.html12345678910111213{extend name="common/base" /}{block name="title"} thinkPHP5 index页{/block}{block name="body"} <h1>这里是index body</h1>{/block}{block name="sidebar"} <h1>这里是index sidebar</h1>{/block}{block name="footer"} index_22{__block__}{/block}我在index0.html文件中有重新定义title,所有最后的title是thimkPHP5 index页,但值得注意的是我footer内容是index_22{__block__},而{__block__}指的是在模板base.html中同位置的默认值footer,所有最后footer的内容是index_22默认值footer。以上是同模块下的继承,还有一种是继承common模块的继承,这里于idnex模块下的view/index.html继承common模块下view下的base.html文件为例不同的模块继承方式也不同了,这里继承common模块下的继承方式为:{extend name="common@base" /},而在同模块下的继承是:{extend name="common/base" /}。而有些需求在base.html文件中还需要继承其他的模板,那么在base.html中可以这样继承: {include file="common@header" /}这个意思就是继承common下的view/header.html文件PHP中文网,大量MySQL视频教程,欢迎学习!以上就是Thinkphp5模板继承和替换的问题案例的详细内容
0
0 1409天前
1019
用户登陆功能是PHP程序设计中常见的需求,本文ThinkPHP实例主要完成注册成功后进入首页的代码编写,并告诉你是如何实现登录用户的功能。详解ThinkPHP登录功能实例代码具体步骤:第一步:在config.php文件中加上:1'USER_AUTH_KEY'=>'authId'示例如下:(推荐教程:thinkphp教程)1234567891011121314<?phpif(!defined('THINK_PATH')) exit();return array(// 定义数据库连接信息'DB_TYPE'=> 'mysql',// 指定数据库是mysql'DB_HOST'=> 'localhost','DB_NAME'=>'myuser', // 数据库名'DB_USER'=>'root','DB_PWD'=>'', //您的数据库连接密码'DB_PORT'=>'3306','DB_PREFIX'=>'think_',//数据表前缀'USER_AUTH_KEY'=>'authId');?>第二步:在AdminAction.class.php中的insert()代码中用:1Session::set(C('USER_AUTH_KEY'),$user);保存登录用户名到session。完整实现代码如下:123456789101112131415161718public function insert(){header('Content-Type:text/html; charset=utf-8');//防止出现乱码$user=$_POST['user'];$this->verifyCheck();$Pagemodel = D("user");$vo = $Pagemodel->create(); if(false === $vo) die($Pagemodel->getError());$topicid = $Pagemodel->add(); //add方法会返回新添加的记录的主键值if($topicid){ // www.jbxue.com//$_SESSION[C('USER_AUTH_KEY')]=$user;//不能用此句Session::set(C('USER_AUTH_KEY'),$user);//dump(Session::get('authId')); echo "<script>alert('数据库添加成功');location.href='http://127.0.0.1/zhuce/index.php/index';</script>";}else throw_exception("<script>alert('数据库添加失败');history.back();</script>");}第三步:在IndexAction.class.php文件中用if(!Session::is_set(C('USER_AUTH_KEY')))判断用户登录了没有。1Session::get(C('USER_AUTH_KEY'))是获取登录用户的名。具体代码如下:1234567891011121314public function index(){ //www.jbxue.comif(!Session::is_set(C('USER_AUTH_KEY')))//if(!isset($_SESSION['USER_AUTH_KEY'])||($_SESSION['USER_AUTH_KEY']==0))//不能用此句{$msg="用户没有登录"; }else{$msg=Session::get(C('USER_AUTH_KEY')).'欢迎你回来';}$this->assign('msg',$msg);$this->display(); }第四步:首页显示模板,代码如下:1234<body>{$msg}<br />这是我的首页</body>总结:登录代码都是围绕写session,判断session,读session展开。写session用:Session::set(C('USER_AUTH_KEY'),$user);判断session用:if(!Session::is_set(C('USER_AUTH_KEY')));读session用:Session::get(C('USER_AUTH_KEY'))PHP中文网,大量免费MySQL视频教程,欢迎在线学习!以上就是详解ThinkPHP登录功能实例代码的详细内容
0
0 1409天前
1118
本篇文章介绍了Thinkphp使用join联表查询的方法,具有一定的参考价值,希望对各位学习thinkphp的朋友有帮助!Thinkphp使用join联表查询的方法多表联查:12345678$user = M('user');$b_user = M('b_user');$c_user = M('c_user');$list = $user->alias('user')->where('user.user_type=1') ->join('b_user as b on b.b_userid = user.user_id') ->join('c_user as c on c.c_userid = b.b_userid') ->order('b.user_time') ->select();$user 表的 user_id 等于$b_user表的b_userid;$c_user表的 c_userid 等于$b_user表的b_userid;推荐学习:thinkphp教程以上就是Thinkphp使用join联表查询的方法的详细内容
0
0 1409天前
1159
主要的思想是利用call_user_func_array()和容器结合使用的。核心代码,理解都在注释中:1234567891011121314151617181920212223242526272829303132333435363738394041<?php//reqeuestFacade.php namespace facade{ class Request extends Facade{ public function getFacadeName(){ return 'request'; } } } ?> <?php//facade.phpnamespace facade{ class Facade{ public static function createFacade(){ $class = static::class; //在这个获取的$class其实是facade\reqeust //在这里利用static::得到getFacadeName,返回真正的request的变量名 $facadeClass = static::getFacadeName(); if ($facadeClass) { $class = $facadeClass; } elseif (isset(self::$bind[$class])) { $class = self::$bind[$class]; } //echo $class; 利用容器去获取reqeust,而不是facade\reqeust return \Container::get($class); } public static function __callStatic($method, $params) { return call_user_func_array([static::createFacade(), $method], $params); } }} ?>下面测试代码reqeust.php123456789101112<?php class Request{ public $name = 'Real Request'; public function sayName(){ echo $this->name; } } ?>test.php123456789101112<?php use facade\Request; include "Container.php"; include "Facade.php"; include "RequestFacade.php"; include "Request.php"; Request::sayName(); ?>推荐教程:thinkphp教程以上就是在thinkphp中实现facade的方法的详细内容
0
0 1409天前
1013
ThinkPHP where()ThinkPHP where()方法是 Model 类内置方法,用于设置数据库查询或者更新、删除等操作条件。where 方法支持以字符串、数组和对象方式来设置条件,该方法不能独立使用,必须与 select()、find()、delete() 等数据操作方法搭配使用。字符串方式字符串方式条件即以字符串的方式将条件作为 where() 方法的参数,例子:12$Dao = M("User");$List = $Dao->where('uid<10 AND email="Jack@163.com"')->find();实际执行的 SQL 为:1SELECT * FROM user WHERE uid<10 AND email="Jack@163.com" LIMIT 1字符串方式设定的条件即为实际 SQL 执行的条件,也是最接近原生 SQL 的方式,ThinkPHP 不会对条件做任何(类型上的)检查。数组方式在大多数情况下推荐使用索引数组或者对象来作为查询条件,因为这样会更加安全,详细参见:《ThinkPHP 类型检测》。使用数组方式的 where 条件例子:12345$Dao = M("User");// 构建查询数组$condition['uid'] = array('elt',10);$condition['email'] = "Jack@163.com";$List = $Dao->where($condition)->find();这个例子跟上面使用字符串方式的例子执行效果是一样的。使用对象where 方法也可以使用对象来设置查询或操作条件,可以使用任何对象。以 stdClass 内置对象为例:123456$Dao = M("User");// 定义查询条件$condition = new stdClass(); $condition->uid = array('elt',10);$condition->email = "Jack@163.com";$List = $Dao->where($condition)->find();使用对象方式和使用数组方式的条件效果是相同的,并且是可以互换的。推荐教程:thinkphp教程以上就是ThinkPHP where方法介绍的详细内容
0
0 1409天前
1148
在thinkphp里面我们如何跨模块调用?在开发过程中经常会在当前模块调用其他模块的方法,这个时候就涉及到跨模块调用,我们还可以了解到A和R两个快捷方法的使用。12$User = A("User"); // 实例化UserAction控制器对象$User->importUser(); // 调用User模块的importUser操作方法这里的A("User") 是一个快捷方法,和下面的代码等效:12import("@.Action.UserAction");$User = new UserAction();事实上,在这个例子里面还有比A方法更简单的调用方法,例如:1R("User","importUser"); // 远程调用UserAction控制器的importUser操作方法上面只是在当前项目中调用,如果你有需要在多个项目之间调用方法,一样可以完成:1234$User = A("User","App2"); // 实例化App2项目的UserAction控制器对象$User->importUser(); // 远程调用App2项目的UserAction控制器的importUser操作方法R("User","importUser","App2");我的一个例子:一个项目下面分了两个组:admin和homehome默认是组:实例化模块的时候(当前位置是在admin中IndexAction类的index方法中实例化)1234import("@.Action.Home.UserAction");$User=new UserAction();$User->show();$User->add();注:调用的方法必须是 public型的推荐教程:thinkphp教程以上就是thinkphp跨模块调用方法的详细内容
0
0 1409天前
1050
phpMailer 是一个非常强大的 ph p发送邮件类,可以设定发送邮件地址、回复地址、邮件主题、html网页,上传附件,并且使用起来非常方便。Thinkphp3.2 PHPMailer 发送邮件结合QQ企业邮箱发送邮件下载附件PHPMailer解压到ThinkPHP\Library\Vendor在Common文件夹新建function.php1234567891011121314151617181920212223/** * 邮件发送函数 */ function sendMail($to, $title, $content) { Vendor('PHPMailer.PHPMailerAutoload'); $mail = new PHPMailer(); //实例化 $mail->IsSMTP(); // 启用SMTP $mail->Host=C('MAIL_HOST'); //smtp服务器的名称(这里以QQ邮箱为例) $mail->SMTPAuth = C('MAIL_SMTPAUTH'); //启用smtp认证 $mail->Username = C('MAIL_USERNAME'); //你的邮箱名 $mail->Password = C('MAIL_PASSWORD') ; //邮箱密码 $mail->From = C('MAIL_FROM'); //发件人地址(也就是你的邮箱地址) $mail->FromName = C('MAIL_FROMNAME'); //发件人姓名 $mail->AddAddress($to,"尊敬的客户"); $mail->WordWrap = 50; //设置每行字符长度 $mail->IsHTML(C('MAIL_ISHTML')); // 是否HTML格式邮件 $mail->CharSet=C('MAIL_CHARSET'); //设置邮件编码 $mail->Subject =$title; //邮件主题 $mail->Body = $content; //邮件内容 $mail->AltBody = "这是一个纯文本的身体在非营利的HTML电子邮件客户端"; //邮件正文不支持HTML的备用显示 return($mail->Send()); }添加配置文件config.php123456789// 配置邮件发送服务器 'MAIL_HOST' =>'smtp.exmail.qq.com',//smtp服务器的名称 'MAIL_SMTPAUTH' =>TRUE, //启用smtp认证 'MAIL_USERNAME' =>'jufengjituan@gsjfjt.com',//你的邮箱名 'MAIL_FROM' =>'jufengjituan@gsjfjt.com',//发件人地址 'MAIL_FROMNAME'=>'聚丰集团',//发件人姓名 'MAIL_PASSWORD' =>'******',//邮箱密码 'MAIL_CHARSET' =>'utf-8',//设置邮件编码 'MAIL_ISHTML' =>TRUE, // 是否HTML格式邮件最后就是使用PHPMailer发送邮件123456<form action="__URL__/add" method="post" enctype="multipart/form-data"> 邮箱:<input type="text" id="mail" name="mail"/> 标题:<input type="text" id="title" name="title"/> 内容<input type="text" id="content" name="content"/> <input class="button" type="submit" value="发送" style="margin: 0 auto;display: block;"/> </form>123456public function add(){ if(SendMail($_POST['mail'],$_POST['title'],$_POST['content'])) $this->success('发送成功!'); else $this->error('发送失败');}PHPMailer下载地址:https://github.com/PHPMailer/PHPMailer推荐教程:thinkphp教程以上就是ThinkPHP利用PHPMailer发送邮件的详细内容
0
0 1409天前
1031
1160
在 ThinkPHP 3.0 中引入了扩展配置。扩展配置的优先级仅次于动态配置而高于惯例配置,项目配置等。项目配置文件在部署模式的时候会纳入编译缓存,也就是说编译后再修改项目配置文件就不会立刻生效,需要删除编译缓存后才能生效。扩展配置文件则不受此限制影响,即使在部署模式下面,修改配置后也可以实时生效。基于扩展配置的以上特性,通常扩展配置是为了某些特殊的需要,而从项目配置里面分离出来的一些配置信息,这样的目的是为了便于维护和管理。定义扩展配置扩展配置文件位于项目配置目录下(PS:这个比较重要),如 Conf/user.php,要启用扩展配置,首先需要在项目配置文件里面定义 LOAD_EXT_CONFIG 参数:123'LOAD_EXT_CONFIG'=>'user',// 还可以定义多个扩展配置文件'LOAD_EXT_CONFIG'=>'user,db',如上面的参数定义所示,扩展配置可以是一个或多个配置文件。编辑 Conf/user.php 文件,写入配置参数:123456<?phpreturn array( 'USER_TYPE' => 2, 'USER_AUTH_TYPE' => 1,);?>那么在操作方法里就可以通过 C 方法来读取扩展配置里的参数:1C('USER_TYPE')在项目配置文件中,也可以以二级配置方式加载加载扩展配置文件:1'LOAD_EXT_CONFIG'=>array('USER'=>'user','DB'=>'db'),那么同样的 user.php 扩展配置文件,要获取配置参数值的方式改变为:1C('USER.USER_TYPE')二级配置方式,可以避免大项目中参数冲突问。避免和系统内置配置文件发生冲突下面表格中列出的配置文件已经被 ThinkPHP 系统使用,在定义扩展配置文件时,请勿使用下面的文件名:推荐教程:thinkphp教程以上就是ThinkPHP扩展配置的详细内容
0
0 1409天前
1030
在开发中,如果一个新增或修改的表单,在后台完成数据库操作后我们设定的不是跳转到其他页面,还是返回本页面,这时点击浏览器的后退再提交或刷新页面,会导致form表单重复提交,即这条记录会被增加或修改两次。导致表单重复提交的原因是:第一次提交的表单会被缓存到内存中,直到页面下次提交或页面关闭或转向其他页面时才消失。在自调用返回时,内存中的数据依然在,这时页面中的判断提交的代码依然可以检测到提交的值,顾会产生重复提交的效果。可以用以下几个办法解决:方法1:最简单:页面提交后转到另一个页面而不是本页面,举个栗子,比如你的页面地址为1http://yourdomain.com/User/Index/login则该页面的表单action地址可以为另外的处理地址,如1<form action="{:U('User/Index/check_login')}" method="post">这样报错返回,或者用户点击回退按钮,还是会回到上一个地址,不过这种情况也不保险。还要搭配方法2,一起比较保险方法2:提交表单后提交按钮变灰/隐藏提交按钮这种方式一般是结合方法1来做的,通过JS来动态监听用户的点击动作,动态将按钮属性置成disabeld,即为灰色不可用。代码如下:HTML:12345<form action="{:U('User/Index/check_login')}" method="post"> <input type="text" name="username" value="" id="username" /> <input type="password" name="userpwd" id="userpwd" /> <input type="submit" name="login_btn" id="login_btn" value="登陆"/></form>JS:12345$().ready(function(){ $("#login_btn").on('click',function(){ $(this).attr('disabled',true); });});方法1+方法2 结合后,基本上90%以上的重复提交问题都能解决,但是大刘这里还是要说下第三种方法,即在服务端一劳永逸的解决这个问题方法3:使用隐藏随机TOKEN值的方法进行重复提交判断首先,在项目的functions.php中添加如下方法1234567891011121314151617181920//创建TOKENfunction createToken() { $code = chr(mt_rand(0xB0, 0xF7)) . chr(mt_rand(0xA1, 0xFE)) .chr(mt_rand(0xB0, 0xF7)) . chr(mt_rand(0xA1, 0xFE)) . chr(mt_rand(0xB0, 0xF7)) . chr(mt_rand(0xA1, 0xFE)); session('TOKEN', authcode($code));}//判断TOKENfunction checkToken($token) { if ($token == session('TOKEN')) { session('TOKEN', NULL); return TRUE; } else { return FALSE; }}/* 加密TOKEN */function authcode($str) { $key = "YOURKEY"; $str = substr(md5($str), 8, 10); return md5($key . $str);}在表单页面form中填入以下HTML代码HTML:1<input type="hidden" name="TOKEN" value="{:session('TOKEN')}" />在页面展示前调用creatToken()方法生成token,在相应控制器POST请求中 使用 checkToken() 进行判断是否重复提交1234567if(IS_POST){$post_token = I('post.TOKEN'); if(!checkToken($post_token)){ $this->error('请不要重复提交页面',U('User/Index/login')); }}基本上,这3个方法配合着使用,就能解决ThinkPHP开发中表单重复提交问题,当然,有同学说可以使用ThinkPHP的令牌环机制,这样其实就更简单了,TP会默认在表单中生成一个隐藏域,到时候判断这个隐藏域是否存在以及和session中的值是否想的即可,原理和方法3是一样的。推荐教程:thinkphp教程以上就是thinkphp防止重复提交表单的技巧的详细内容
0
0 1409天前
1105
1、去官网下载类库 “https://www.barcodebakery.com/en/download”,选择自己的版本下载推荐教程:thinkphp教程2、解压放到“E:\phpstudy\PHPTutorial\WWW\guahao\vendor\下”,其中class文件是所有的类文件,生成条形码就是调用文件夹里的类,font文件是字体,index.php是一个可选择条件生成条形码的功能,是主程序的入口,test_1D.php是给的生成条形码的例子,test_1D.html是对应的渲染条形码的页面3、我们可以直接使用官方给的例子(test_1D.php),复制到自己需要用的地方,然后根据自己的需求稍加改动即可,需要注意的是,加载第三方类库的路径需要改一下。生成条形码的php代码1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556<?phpnamespace app\index\controller;use think\Controller;/*** 条形码操作类*/class Barcode extends Controller{ public function createBarcode() { $class_dir = VENDOR_PATH.'barcode/class/'; // Including all required classes require_once($class_dir.'BCGFontFile.php'); require_once($class_dir.'BCGColor.php'); require_once($class_dir.'BCGDrawing.php'); require_once($class_dir.'BCGcode39.barcode.php'); // Loading Font // 注意font和class是同一级文件夹 $font = new \BCGFontFile(VENDOR_PATH.'barcode/font/Arial.ttf', 18);// The arguments are R, G, B for color. $color_black = new \BCGColor(0, 0, 0); $color_white = new \BCGColor(255, 255, 255); $drawException = null; try { $code = new \BCGcode39(); $code->setScale(2); // Resolution $code->setThickness(30); // Thickness $code->setForegroundColor($color_black); // Color of bars $code->setBackgroundColor($color_white); // Color of spaces $code->setFont($font); // Font (or 0) 0不显示文字 $text = isset($_GET['text']) ? $_GET['text'] : 'HELLO'; $code->parse($text); // Text } catch(Exception $exception) { $drawException = $exception; } /* Here is the list of the arguments 1 - Filename (empty : display on screen) 2 - Background color */ $drawing = new \BCGDrawing('', $color_white); if($drawException) { $drawing->drawException($drawException); } else { $drawing->setBarcode($code); $drawing->draw(); } // Header that says it is an image (remove it if you save the barcode to a file) header('Content-Type: image/png'); header('Content-Disposition: inline; filename="barcode.png"'); // Draw (or save) the image into PNG format. $drawing->finish(\BCGDrawing::IMG_FORMAT_PNG); } public function barcodedes() { return $this->fetch(); }}?>接受渲染条形码的Html代码1<img src="{:url('createBarcode')}">当然,src还可以携带参数,只需更改以下代码html代码1<img src="{:url('createBarcode',array('text'=>'123'))}">php代码把1$text = isset($_GET['text']) ? $_GET['text'] : 'HELLO';改成1$text = input('text'); //接收的参数4、如果想把条形码保存到本地,在实例化“BCGDrawing”的时候填写保存路径即可12345678910111213141516171819202122232425262728293031323334353637383940414243444546// 文件路径 $file_dir = 'uploads/barcode/'.date('Y-m-d'); if (!file_exists($file_dir)) { mkdir($file_dir,0755,true); } $imgUrl = $file_dir.'/'.time().'.png'; $class_dir = VENDOR_PATH.'barcode/class/'; // Including all required classes require_once($class_dir.'BCGFontFile.php'); require_once($class_dir.'BCGColor.php'); require_once($class_dir.'BCGDrawing.php'); require_once($class_dir.'BCGcode39.barcode.php'); // Loading Font // 注意font和class是同一级文件夹 $font = new \BCGFontFile(VENDOR_PATH.'barcode/font/Arial.ttf', 18); // Don't forget to sanitize user inputs // $text = isset($_GET['text']) ? $_GET['text'] : 'HELLO'; // The arguments are R, G, B for color. $color_black = new \BCGColor(0, 0, 0); $color_white = new \BCGColor(255, 255, 255); $drawException = null; try { $code = new \BCGcode39(); $code->setScale(2); // Resolution $code->setThickness(30); // Thickness $code->setForegroundColor($color_black); // Color of bars $code->setBackgroundColor($color_white); // Color of spaces $code->setFont($font); // Font (or 0) $text = input('text'); //接收的参数 $text = isset($text) ? $text :'无参数'; $code->parse($text); // Text } catch(Exception $exception) { $drawException = $exception; } /* Here is the list of the arguments 1 - Filename (empty : display on screen) 2 - Background color */ // 保存到本地 (路径,颜色)路径为空则表示显示到页面上 $drawing = new \BCGDrawing($imgUrl, $color_white); if($drawException) { $drawing->drawException($drawException); } else { $drawing->setBarcode($code); $drawing->draw(); } $drawing->finish(\BCGDrawing::IMG_FORMAT_PNG);5、生成条形码之后,怎么判定条形码是否能用呢?可以把条形码保存成图片到本地,打开官网“https://www.onlinebarcodereader.com/”,上传刚刚生成的条形码,如果解析出的参数跟你输入的一样,说明条形码可以用。以上就是thinkphp5 + barcode 生成条形码的方法的详细内容
0
0 1409天前
1034
类库是ThinkPHP的核心部分,而ThinkPHP又通过基类库的概念把所有系统类库都集中在一起管理。核心基类库包括完成框架的通用性开发而必须的基础类和常用工具类。thinkPHP运行流程从tp5/public/index.php入口文件进入,通过加载框架引导文件/tp5/thinkphp/start.php进行初始化常量,注册各种需要的机制,加载配置文件。准备完成后即可通过/tp5/thinkphp/library/think/App.php类run()方法执行。准备工作对框架进行修改,切忌修改核心代码。一般方法是在特定目录下进行修改,调用。ThinkPHP提供了需要引入其他修改的特定目录 tp5/extend;(当然也可以在入口文件重新定义EXTEND_PATH)来自定义修改目录。建议能不修改不要修改。示例具体需求:给thinkphp引入自定义的分页类Pagination.php。然后调用分页类进行业务代码的编写。方法1:利用命名空间自动加载类文件放置目录:tp5/extend/page/admin/Pagination.php根据目录,给类文件绑定命名空间(psr-4规则):123456<?php namespace page\admin; class pagination { ……………………………… }以上用通俗的话来说,就是自动加载扩展类库,需要使用命名空间,并且命名空间要和目录对应。根目录是extend目录下开始的目录名。调用1$page = new page\admin\pagination();或者12use page\admin\pagination;$page = new pagination();方法2:不利用命名空间如果类文件没有命名空间,则无法自动加载。必须使用Loader类进行手动加载123use \think\Loader;Loader::import('page.admin.pagination');$page = new pagination();推荐教程:thinkphp教程以上就是在thinkphp5中添加自己的类库的详细内容
0
0 1409天前
996
ThinkPHP作为PHP框架,是单一入口的,那么其原始的URL便不是那么友好,但ThinkPHP提供了各种机制来定制需要的URL格式,本文就来为大家介绍一下thinkphp中设置url格式的方法。ThinkPHP 作为 PHP 框架,是单一入口的,那么其原始的 URL 便不是那么友好。但 ThinkPHP 提供了各种机制来定制需要的 URL 格式,配合 Apache .htaccess 文件,更是可以定制出人性化的更利于 SEO 的 URL 地址来。.htaccess文件是 Apache 服务器中的一个配置文件,它负责相关目录下的网页配置。我们可以利用 .htaccess 文件的 Rewrite 规则来隐藏掉 ThinkPHP URL 中的 index.php 文件(即入口文件),这也是 ThinkPHP URL 伪静态的第一步。例如原来的 URL 为:1http://127.0.0.1/index.php/Index/insert去掉 index.php 之后变为:1http://127.0.0.1/Index/insert如此一来,就变成了 http://服务器地址/应用模块名称/操作名称[/变量参数] 的常见 URL 格式。更改 Apache httpd.conf 配置文件提示:如果在虚拟主机商配置,请直接配置第三、四步,因为支持 .htaccess 的空间已经配置好了前面两步。用编辑器打开 Apache 配置文件 httpd.conf(该文件位于 Apache 安装目录Apache2conf),并按如下步骤修改。一、加载了 mod_rewrite.so确认加载了 mod_rewrite.so 模块(将如下配置前的 # 号去掉):1LoadModule rewrite_module modules/mod_rewrite.so二、更改 AllowOverride 配置更改需要读取 .htaccess 文件的目录,将原来的目录注释掉:12#<Directory "C:/Program Files/Apache Group/Apache2/htdocs"><Directory E:/html/myapp>更改 AllowOverride None 为 AllowOverride FileInfo Options ,更改后的配置如下所示:1234#<Directory "C:/Program Files/Apache Group/Apache2/htdocs"><Directory E:/html/myapp> AllowOverride FileInfo Options</Directory>.htaccess 是基于目录来控制的,<Directory E:/html/myapp> 该句即表示需要读取 .htaccess 文件的目录,要根据实际具体 Apache 的解析目录来配置。虚拟主机如果提供 .htaccess 控制,一般都已经配置好了。三、添加 .htaccess 文件 Rewrite 规则在需要隐藏 index.php 的目录下(本教程中为 E:/html/myapp,也即入口文件所在目录)创建 .htaccess 文件,并写入如下规则代码:12345678910<IfModule mod_rewrite.c>RewriteEngine on #不显示index.php RewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] </IfModule>四、更改项目配置文件编辑项目配置文件 Conf/config.php ,将 URL 模式配置为 2(Rewrite模式):1'URL_MODEL'=>2,至此,各个配置已经完成。保存各配置文件后,重启 Apache 服务器并删除 Runtime 目录下的项目缓存文件,在浏览器访问隐藏 index.php 后的地址测试是否成功:1http://127.0.0.1/html/myapp/Index/index如果访问成功,那么利用 Apache .htaccess 文件的 Rewrite 规则隐藏 index.php 入口文件的配置就成功了。推荐教程:thinkphp教程以上就是thinkphp中设置伪静态的方法的详细内容
0
0 1409天前
1121
在用thinkphp的时候,通常会直接访问IndexController控制下面的Index方法.那它是通过什么设置的呢?ThinkPHP根目录设置在根目录下面的ThinkPHP/conf/convention.php找到123'DEFAULT_MODULE' => 'Home', // 默认模块'DEFAULT_CONTROLLER' => 'Index', // 默认控制器名称'DEFAULT_ACTION' => 'index', // 默认操作名称这三个的意思就是Home模块下面的Index控制器下面的index方法。配置文件设置在Common/conf/config.php中添加一段例如我想要访问Aplication下面的App控制下面的login方法123'DEFAULT_MODULE' => 'Aplication', // 默认模块'DEFAULT_CONTROLLER' => 'App', // 默认控制器名称'DEFAULT_ACTION' => 'login', // 默认操作名称同上面一样,就可以生效了。推荐教程:thinkphp教程以上就是thinkphp设置默认访问路径的方法的详细内容
0
0 1409天前
991
在ThinkPHP中系统提供以下几个查询方法的使用,方便于在后期需要做统计的使用:count() 表示查询表中总的记录数max() 表示查询某个字段的最大值min() 表示查询某个字段的最小值avg() 表示查询某个字段的平均值sum() 表示求出某个字段的总和一、count方法语法:1$model -> [where() -> ] count();案例:查询部门表中的总记录数。12345678910//count方法public function test(){ //实例化模型 $model = M('Dept'); //count方法 $result = $model -> count(); //打印 dump($result); }显示结果:返回值是字符的形式。sql跟踪信息中的结果:数据库中的信息:二、max方法语法:1$model -> max('字段名');案例:查询部门表中id最大的部门。在以后开发的时候会有一个应用是通过max方法查询最后注册会员的id。123456789//max方法public function test(){ //实例化模型 $model = M('Dept'); //max方法 $result = $model -> max('id'); //打印 dump($result);}显示结果:返回值是字符的形式。sql跟踪信息中的结果:数据库中的信息:三、min方法语法:1$model -> min('字段名')案例:查询部门表中id最小的部门。在以后开发的时候会有一个应用是通过min方法查询最早注册会员的id。123456789//min方法public function test(){ //实例化模型 $model = M('Dept'); //max方法 $result = $model -> min('id'); //打印 dump($result);}显示结果:返回值也是字符的形式。sql跟踪信息中的结果:数据库中的信息:四、avg方法语法:1$model -> avg('字段名');案例:求出部门表中id的平均值。123456789//avg方法public function test(){ //实例化模型 $model = M('Dept'); //max方法 $result = $model -> avg('id'); //打印 dump($result);}显示结果:返回值也是字符的形式。sql跟踪信息中的结果:数据库中的信息:五、sum方法语法:1$model -> sum('字段名');案例:查询字段id的总和。123456789//sum方法public function test(){ //实例化模型 $model = M('Dept'); //max方法 $result = $model -> sum('id'); //打印 dump($result);}显示结果:返回值同样是字符的形式。sql跟踪信息中的结果:数据库中的信息:推荐教程:thinkphp教程以上就是thinkphp中统计查询的方法介绍的详细内容
0
0 1409天前
1054
钩子是一种触发机制,如同一个陷阱,当程序运行到某个地方时就会触发这个陷阱,然后执行这个Hook,执行完成后,系统根据执行的不同结果继续进行。框架在\Think\Think->start()方法中调用Hook类中的import方法批量加载模式行为:默认为\Model\common.php中的配置文件,该文件定义行为扩展.2、通过Hook:listen()方法监听tag标记的行为.Hook类中定义一个数组tags:tag为key;Behavior行为是value.通过Hook:exec()执行该标记的行为插件。12345678910111213141516171819202122232425262728293031323334353637383940414243444546/** * 监听标签的插件 * @param string $tag 标签名称 * @param mixed $params 传入参数 * @return void */ static public function listen($tag, &$params=NULL) { if(isset(self::$tags[$tag])) { if(APP_DEBUG) { G($tag.'Start'); trace('[ '.$tag.' ] --START--','','INFO'); } foreach (self::$tags[$tag] as $name) { APP_DEBUG && G($name.'_start'); $result = self::exec($name, $tag,$params); if(APP_DEBUG){ G($name.'_end'); trace('Run '.$name.' [ RunTime:'.G($name.'_start',$name.'_end',6).'s ]','','INFO'); } if(false === $result) { // 如果返回false 则中断插件执行 return ; } } if(APP_DEBUG) { // 记录行为的执行日志 trace('[ '.$tag.' ] --END-- [ RunTime:'.G($tag.'Start',$tag.'End',6).'s ]','','INFO'); } } return; } /** * 执行某个插件 * @param string $name 插件名称 * @param string $tag 方法名(标签名) * @param Mixed $params 传入的参数 * @return void */ static public function exec($name, $tag,&$params=NULL) { if('Behavior' == substr($name,-8) ){ // 行为扩展必须用run入口方法 $tag = 'run'; } $addon = new $name(); return $addon->$tag($params); }推荐教程:thinkphp教程以上就是thinkphp钩子实现方法的详细内容
0
0 1409天前
863
1、方法一:v3.2.1①、ThinkPHP/Library/Behavior/CronRunBehavior.class.php文件在这里首先要说的就是这个自动执行任务文件,官方所给的这个文件存在BUG,我是用的是v3.2.1版本,后面的版本是否有改正大家可以尝试一下。12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152<?php/** * ======================================= * Created by WeiBang Technology. * Author: ZhiHua_W * Date: 2016/9/22 0005 * Time: 上午 11:12 * Project: ThinkPHP实现定时执行任务 * Power: 自动执行任务 * ======================================= */namespace Behavior; class CronRunBehavior{ public function run(&$params) { if (C('CRON_CONFIG_ON')) { $this->checkTime(); } } private function checkTime() { if (F('CRON_CONFIG')) { $crons = F('CRON_CONFIG'); } else if (C('CRON_CONFIG')) { $crons = C('CRON_CONFIG'); } if (!empty($crons) && is_array($crons)) { $update = false; $log = array(); foreach ($crons as $key => $cron) { if (empty($cron[2]) || $_SERVER['REQUEST_TIME'] > $cron[2]) { G('cronStart'); R($cron[0]); G('cronEnd'); $_useTime = G('cronStart', 'cronEnd', 6); $cron[2] = $_SERVER['REQUEST_TIME'] + $cron[1]; $crons[$key] = $cron; $log[] = 'Cron:' . $key . ' Runat ' . date('Y-m-d H:i:s') . ' Use ' . $_useTime . ' s ' . "\r\n"; $update = true; } } if ($update) { \Think\Log::write(implode('', $log)); F('CRON_CONFIG', $crons); } } }}②、tgs.php在Application/Common/Conf文件夹中新建tags.php文件,进行标签设置。123456<?php return array( //'配置项'=>'配置值' 'app_begin' =>array('Behavior\CronRunBehavior'),);③、config.php在Application/Common/Conf文件夹中的config.php文件进行自动运行配置。12345678<?phpreturn array( /* 自动运行配置 */ 'CRON_CONFIG_ON' => true, // 是否开启自动运行 'CRON_CONFIG' => array( '测试执行定时任务' => array('Home/Index/crons', '5', ''), //路径(格式同R)、间隔秒(0为一直运行)、指定一个开始时间 ),);④、IndexController.class.php在Application/Home/Controller/IndexController.class.php文件中进行定时执行任务的编写。123456789101112131415161718192021222324252627282930313233343536373839<?php/** * ======================================= * Created by WeiBang Technology. * Author: ZhiHua_W * Date: 2016/9/22 0005 * Time: 上午 11:20 * Project: ThinkPHP实现定时执行任务 * Power: 自动执行任务方法控制器 * ======================================= */namespace Home\Controller; use Think\Controller; class IndexController extends Controller{ /* public function index(){ $this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p>欢迎使用 <b>ThinkPHP</b>!</p></div><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script>','utf-8'); } */ public function index() { $contents = file_get_contents("test.txt"); //每次访问此路径将内容输出,查看内容的差别 var_dump($contents); exit; $this->assign("contents", $contents); $this->display(); } //定时执行的方法 public function crons() { //在文件中写入内容 file_put_contents("test.txt", date("Y-m-d H:i:s") . "执行定时任务!" . "\r\n<br>", FILE_APPEND); }}这样定时执行任务我们就写好了,每隔5秒我们访问任何项目的url,然后查看根目录下的test.txt文件就会发现里面的内容变化。注意:当你修改间隔时间时会发现没有生效,这是你需要将Runtime/Data文件夹下的缓存文件删除,间隔时间缓存存放在CRON_CONFIG.php文件中。2、方法2:v3.2.2此方法和方法一没有多大的区别。①、tags.php在/Application/Common/Conf目录下新建tags.php文件。(此和方法一处一样)123456<?php return array( //'配置项'=>'配置值' 'app_begin' =>array('Behavior\CronRunBehavior'),);②、crons.php在/Application/Common/Conf目录下新建crons.php文件。(此处和方法一有区别,注意区分。)1234567<?php return array( //myplan为我们计划定时执行的方法文件,2是间隔时间,nextruntime下次执行时间 //此文件位于/Application/Cron/目录下 'cron' => array('myplan', 2, nextruntime),);③、myplan.php在/Application/Common/目录下新建 Cron文件夹,里面新建文件myplan.php文件。123<?php echo date("Y-m-d H:i:s")."执行定时任务!" . "\r\n<br>";此时我们就可以访问项目的url,然后我们会发现在Application/Runtime/目录下生成了~crons.php文件,文件内容如下:1234567891011<?php return array ( 'cron' => array ( 0 => 'myplan', 1 => 60, 2 => 1398160322, ), ); ?>推荐教程:thinkphp教程以上就是thinkphp设置定时执行任务的方法的详细内容
0
0 1409天前