php 图片验证码生成代码

第一种方法:

首先在 index.php 页面

    function identity(){
         $this->load->view('index',$data);    
    }
    
    
    function codes(){
        if($_POST['code'] == $_SESSION['code']){
            echo 'ok';
        }else{
            echo 'no';
        }
     }
    
    function create_code(){
       session_start();
        //生成验证码图片
        ob_clean(); 
        //header("Content-type:image/png");
        //全数字
        $str="1,2,3,4,5,6,7,8,9,0,a,b,c,d,e,f";//要显示的字符,自己可以增加或删除
        $list=explode(",",$str);
        $r=count($list)-1;
        $laststr="";
        for($i=0;$i<4;$i++){
                $randnum=mt_rand(0,$r);
                $laststr .= $list[$randnum];//取出字符,组合成要显示的字符串
            }
        $_SESSION['code']=$laststr; //将字符串放入SESSION中
        //echo $laststr;
        $img=imagecreate(28,28);//生成图片
        $black=imagecolorallocate($img,0,0,0);  //  设置颜色
        $white=imagecolorallocate($img,255,255,255);
        $gray=imagecolorallocate($img,200,200,200);
        $red=imagecolorallocate($img,255,0,0);
        $cl = imagefill($img,0,0,$red);//给图片填充颜色
        
        //将验证法放入图片
        imagestring($img,4,10,8,$laststr,$black);//将验证码放到图片上
        for ($i=0;$i<8;$i++){
                $lineColor        = imagecolorallocate($img,rand(0,255),rand(0,255),rand(0,255));
                imageline ($img,rand(0,$x),0,rand(0,$x),$y,$lineColor);
        }
        //干扰点
        for ($i=0;$i<250;$i++){
                imagesetpixel($img,rand(0,$x),rand(0,$y),$fontColor);
        }
        $aa = imagepng($img);
        $bb = imagedestroy($img);      
        //echo $aa;
    }

在视图页面 index.php

 <form action="/index/codes" method="post">
   <label for="yzmai" class="input-tips2">验证码:</label>
   <input type="text" name="code"  class="inputstyle2"  maxlength="10"  /><br/>
   <img id="code" src="/index/create_code" alt="看不清楚,换一张" style="cursor: pointer; vertical-align:middle;" onClick="create_code()"/>
   <button type="submit">提交</button>
 </form>
<script>
function create_code(){
    document.getElementById('code').src = '/index/create_code/'+Math.random()*10000;
}
</script>

可以去尝试下看看

第二种方法:

       header("content-type:image/png");
       ob_clean(); 
        $img=imagecreate(44,18);
        
        $bg=imagecolorallocate($img,245,245,245);//填充背景色
        imagefill($img,0,0,$bg);//本函数将图片坐标 (x,y) 所在的区域着色。参数 col 表示欲涂上的颜色。
        $vcode=" ";
        for($i=0;$i<4;$i++)
        {
         $font_c=imagecolorallocate($img,6,6,6);//文本颜色
         $num=rand(1,9);
         $vcode=$vcode.$num;
         imagestring($img,5,$i*10,1,$num,$font_c);//水平地画一行字符串
        }
        session_start();
        $_SESSION['vcode']=$vcode;
        imagepng($img); //imagepng — 以 PNG 格式将图像输出到浏览器或文件
        imagedestroy($img);//销毁图像

PHP解压缩zip文件

1、使用PHP执行文件解压缩zip文件,前提条件,一定要确定服务器开启了zip拓展

2、封装的方法如下:

实例代码

 1 <?php 
 2 /**
 3  * 压缩文件
 4  * @param array $files 待压缩文件 array('d:/test/1.txt','d:/test/2.jpg');【文件地址为绝对路径】
 5  * @param string $filePath 输出文件路径 【绝对文件地址】 如 d:/test/new.zip
 6  * @return string|bool
 7  */
 8 function zip($files, $filePath) {
 9     //检查参数
10     if (empty($files) || empty($filePath)) {
11         return false;
12     }
13 
14     //压缩文件
15     $zip = new ZipArchive();
16     $zip->open($filePath, ZipArchive::CREATE);
17     foreach ($files as $key => $file) {
18         //检查文件是否存在
19         if (!file_exists($file)) {
20             return false;
21         }
22         $zip->addFile($file, basename($file));
23     }
24     $zip->close();
25 
26     return true;
27 }
28 
29 /**
30  * zip解压方法
31  * @param string $filePath 压缩包所在地址 【绝对文件地址】d:/test/123.zip
32  * @param string $path 解压路径 【绝对文件目录路径】d:/test
33  * @return bool
34  */
35 function unzip($filePath, $path) {
36     if (empty($path) || empty($filePath)) {
37         return false;
38     }
39 
40     $zip = new ZipArchive();
41 
42     if ($zip->open($filePath) === true) {
43         $zip->extractTo($path);
44         $zip->close();
45         return true;
46     } else {
47         return false;
48     }
49 }
50 ?>
复制代码

php实现ZIP压缩文件解压缩(转)

测试使用了两个办法都可以实现:

第一个:需要开启配置php_aip.dll

复制代码
<?php
//需开启配置 php_zip.dll
//phpinfo();
header("Content-type:text/html;charset=utf-8");

function get_zip_originalsize($filename, $path) {
  //先判断待解压的文件是否存在
  if(!file_exists($filename)){
    die("文件 $filename 不存在!");
  }
  $starttime = explode(' ',microtime()); //解压开始的时间

  //将文件名和路径转成windows系统默认的gb2312编码,否则将会读取不到
  $filename = iconv("utf-8","gb2312",$filename);
  $path = iconv("utf-8","gb2312",$path);
  //打开压缩包
  $resource = zip_open($filename);
  $i = 1;
  //遍历读取压缩包里面的一个个文件
  while ($dir_resource = zip_read($resource)) {
    //如果能打开则继续
    if (zip_entry_open($resource,$dir_resource)) {
      //获取当前项目的名称,即压缩包里面当前对应的文件名
      $file_name = $path.zip_entry_name($dir_resource);
      //以最后一个“/”分割,再用字符串截取出路径部分
      $file_path = substr($file_name,0,strrpos($file_name, "/"));
      //如果路径不存在,则创建一个目录,true表示可以创建多级目录
      if(!is_dir($file_path)){
        mkdir($file_path,0777,true);
      }
      //如果不是目录,则写入文件
      if(!is_dir($file_name)){
        //读取这个文件
        $file_size = zip_entry_filesize($dir_resource);
        //最大读取6M,如果文件过大,跳过解压,继续下一个
        if($file_size<(1024*1024*30)){
          $file_content = zip_entry_read($dir_resource,$file_size);
          file_put_contents($file_name,$file_content);
        }else{
          echo "<p> ".$i++." 此文件已被跳过,原因:文件过大, -> ".iconv("gb2312","utf-8",$file_name)." </p>";
        }
      }
      //关闭当前
      zip_entry_close($dir_resource);
    }
  }
  //关闭压缩包
  zip_close($resource);
  $endtime = explode(' ',microtime()); //解压结束的时间
  $thistime = $endtime[0]+$endtime[1]-($starttime[0]+$starttime[1]);
  $thistime = round($thistime,3); //保留3为小数
  echo "<p>解压完毕!,本次解压花费:$thistime 秒。</p>";
}

$size = get_zip_originalsize('../../textaa.zip','../../ffff/');
复制代码

上述方法中:$filename:文件名称,是要解压的文件名称,包括相对于方法的路径,$path表示的是:解压到(什么目录下,以/结束)

第二个方法是:使用pclzip自带的类,项目过程中因为路径的问题采用的第一种方法

复制代码
<?php
/*
php 从zip压缩文件中提取文件
*/
$zip = new ZipArchive;

if ($zip->open('test.zip') === TRUE) {//中文文件名要使用ANSI编码的文件格式
  $zip->extractTo('foldername');//提取全部文件
  //$zip->extractTo('/my/destination/dir/', array('pear_item.gif', 'testfromfile.php'));//提取部分文件
  $zip->close();
  echo 'ok';
} else {
  echo 'failed';
}
复制代码

tp5.1.x 使用队列扩展(think-queue)入门用法

说明

队列(think-queue)是tp5.1.x的一个扩展,需要先安装才能使用

以下是基础用法,本文是按照数据库驱动模式进行

一、安装队列(think-queue)扩展

注:think-queue 最新的版本需要 tp6.x 的支持,所以本文的安装版本为 2.0.4

composer require topthink/think-queue 2.0.4

二、创建数据表

CREATE TABLE `prefix_jobs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `queue` varchar(255) NOT NULL,
  `payload` longtext NOT NULL,
  `attempts` tinyint(3) unsigned NOT NULL,
  `reserved` tinyint(3) unsigned NOT NULL,
  `reserved_at` int(10) unsigned DEFAULT NULL,
  `available_at` int(10) unsigned NOT NULL,
  `created_at` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码

三、修改配置文件,添加对应的驱动配置

文件位置:根目录/config/queue.php

return [
    'connector' => 'Database',   // 数据库驱动
    'expire'    => 60,           // 任务的过期时间,默认为60秒; 若要禁用,则设置为 null
    'default'   => 'default',    // 默认的队列名称
    'table'     => 'jobs',       // 存储消息的表名,不带前缀
    'dsn'       => [],
]
复制代码

配置文件中的 expire 参数说明
expire 参数指的是任务的过期时间, 单位为秒。
过期的任务,其准确的定义是:
   1.任务的状态为执行中
   2.任务的开始执行的时刻 + expire > 当前时刻
expire 不为null 时 ,thinkphp-queue 会在每次获取下一个任务之前检查并重发过期(执行超时)的任务。
expire 为null 时,thinkphp-queue 不会检查过期的任务,性能相对较高一点。但是需要注意:
这些执行超时的任务会一直留在消息队列中,需要开发者另行处理(删除或者重发)!

四、消息的创建与推送

我们在业务控制器中创建一个新的消息,并推送到队列中
1.创建一个新的控制器: \application\index\controller\JobTest.php
2.在控制器中新增一个方法:test()
3.代码如下

<?php
namespace app\admin\controller;
use think\Queue;
use think\Controller;
class JobTest extends Controller
{
    public function test()
    {
        // 1.当前任务将由哪个类来负责处理。
        // 当轮到该任务时,系统将生成一个该类的实例,并调用其 fire 方法
        $jobHandlerClassName = 'app\jobs\JobTest';
        
        // 2.当前任务归属的队列名称,如果为新队列,会自动创建
        $jobQueueName = "helloJobQueue";
        
        // 3.当前任务所需的业务数据, 不能为 resource 类型,其他类型最终将转化为json形式的字符串
        // (jobData 为对象时,存储其public属性的键值对 )
        $jobData = ['ts' => time(), 'bizId' => uniqid(), 'a' => 1];
        
        // 4.将该任务推送到消息队列,等待对应的消费者去执行
        $isPushed = Queue::push($jobHandlerClassName, $jobData, $jobQueueName);
        
        // database 驱动时,返回值为 1|false;redis 驱动时,返回值为 随机字符串|false
        if( $isPushed !== false ){
            echo date('Y-m-d H:i:s') . " a new Hello Job is Pushed to the MQ" . "<br>";
        }else{
            echo 'Oops, something went wrong.';
        }
    }
}
复制代码

五、消息的消费与删除

我们创建一个消费者类,用于处理队列中的任务
1.新增一个消费者类:\application\jobs\JobTest.php

<?php
namespace app\jobs;
use think\queue\Job;
use app\common\model\JobsTest as JobsTestModel;
class JobTest
{
    /**
     * fire方法是消息队列默认调用的方法
     * @param Job            $job      当前的任务对象
     * @param array|mixed    $data     发布任务时自定义的数据
     */
    public function fire(Job $job, $data)
    {
        // 此处做一些 check,提前判断是否需要执行
        $isJobStillNeedToBeDone = $this->checkDatabaseToSeeIfJobNeedToBeDone($data);
        if(! $isJobStillNeedToBeDone){
            $job->delete();
            return;
        }
        // 执行逻辑处理(即:你需要该消息队列做什么)
        $isJobDone = $this->doHelloJob($data);
        if ($isJobDone) {
            // 如果任务执行成功,记得删除任务
            $job->delete();
        } else {
            // 通过这个方法可以检查这个任务已经重试了几次了
            if ($job->attempts() > 3) {
                $job->delete();
                // 也可以重新发布这个任务
                //$job->release(2); // $delay为延迟时间,表示该任务延迟2秒后再执行
            }
        }
    }
    
    /**
     * 有些消息在到达消费者时,可能已经不再需要执行了
     * @param $data 发布任务时自定义的数据
     * @return bool 任务执行的结果
     */
    private function checkDatabaseToSeeIfJobNeedToBeDone($data){
        return true;
    }
    
    /**
     * 根据消息中的数据进行实际的业务处理...
     * @param $data
     * @return bool
     */
    private function doHelloJob($data)
    {
        // TODO 该处为实际业务逻辑,即:对消息中的数据进行处理
        $model = new JobsTestModel();
        $inData = [
            'uniqId' => $data['uniqId'],
            'time' => $data['ts'],
            'content' => '队列成功的插入数据'
        ];
        $res = $model->save($inData);
        if (! $res) {
            return false;
        }
        return true;
    }
}
复制代码

至此,我们的相关代码已经完成

六、调试/测试

1.我们创建一张数据表 jobs_test

CREATE TABLE `st_jobs_test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uniqId` varchar(255) DEFAULT NULL,
  `time` varchar(255) DEFAULT NULL,
  `content` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
复制代码

2.发布任务:执行消息创建方法 http://xxx/admin/job_test/test

如果运行成功,可以看到页面返回显示:”2020-08-07 10:53:00 a new Hello Job is Pushed to the MQ”
此时我们查看数据表 jobs 中的数据:

idqueuepayloadattemptsreservedreserved_atavailable_atcreated_at
6helloJobQueue{“job”:”app\jobs\JobTest”,”data”:{“ts”:1596769329,”uniqId”:”5f2cc4317c6a3″}}0015967693291596769329
7helloJobQueue{“job”:”app\jobs\JobTest”,”data”:{“ts”:1596769332,”uniqId”:”5f2cc4349c42d”}}0015967693321596769332
8helloJobQueue{“job”:”app\jobs\JobTest”,”data”:{“ts”:1596769334,”uniqId”:”5f2cc4367f8f1″}}0015967693341596769334
9helloJobQueue{“job”:”app\jobs\JobTest”,”data”:{“ts”:1596769335,”uniqId”:”5f2cc437cfcaf”}}0015967693351596769335
10helloJobQueue{“job”:”app\jobs\JobTest”,”data”:{“ts”:1596769337,”uniqId”:”5f2cc439086dd”}}0015967693371596769337

3.处理任务
切换到终端窗口,执行命令:php think queue:work --queue helloJobQueue
如果执行成功,可以看到窗口返回 “Processed: app\jobs\JobTest”
此时我们查看数据库
表 jobs 中的消息已经被消费了一条,而 jobs_test 中新增了一条消费后的数据

iduniqIdtimecontent
65f2cc4317c6a31596769329队列成功的插入数据

至此,我们成功地经历了一个消息的 创建 -> 推送 -> 消费 -> 删除 的基本流程

【附录】

tp5.1.x 队列的使用文档
github.com/coolseven/n…

作者:joker丶牧羊人
链接:https://juejin.cn/post/6858171582511054855
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

tp5.1 + think-queue + supervisor队列使用

最近学习使用了tp官方的的消息队列,我的项目用的是 TP5.1 框架,消息队列用的think-queue消息队列,结合 supervisor 进程管理使队列进程常驻。在这里记录一下顺便分享给大家,下面逻辑是加入队列、消费队列和写入数据库。

一、tp5.1的安装方法
用 composer 安装最新稳定版本

composer create-project topthink/think 5.1.*
1
二、添加think-queue扩展
composer安装

composer require topthink/think-queue
1
think-queue 的官方链接

配置

配置文件位于项目根目录下的 config/queue.php,添加如下内容:

return [
    'connector'  => 'Redis',            // 可选驱动类型:sync(默认)、Redis、database、topthink等其他自定义类型
    'expire'     => 60,                    // 任务的过期时间,默认为60秒; 若要禁用,则设置为 null
    'default'    => 'default',            // 默认的队列名称
    'host'       => '127.0.0.1',        // redis 主机ip
    'port'       => 6379,                // redis 端口
    'password'   => '',                 // redis 连接密码
    'select'     => 0,                    // 使用哪一个 db,默认为 db0
    'timeout'    => 0,                    // redis连接的超时时间
    'persistent' => false,                // 是否是长连接
]; 

1
2
3
4
5
6
7
8
9
10
11
我配置了redis驱动,大家可以根据自己的情况配置参数。

创建数据库表

创建一张表,用于展示消费队列写入数据库的操作。

CREATE TABLE test (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
data text,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
1
2
3
4
5
创建消息队列任务

以index模块为例,创建一个 /app/index/job/Task.php 文件(job目录也是手动创建,一定要注意的是:TP5.1里面链接数据库一定要用model,否则将会不定期的出现MYSQL链接超时,将会导致supervisor的子进程卡死,自动重启也无效;原因是没能释放连接,也没有找到Db手动释放的方法),代码如下:

namespace app\index\job;

use think\queue\Job;

class Task{

public function fire(Job $job,$data){
    //任务失败(重复)3次以上,删除该任务
    if($job->attempts() > 3){
        //TODO
        ....
        //删除
        $job->delete();
    }else{
         //写入数据库
        $res = db('test')->insert(['data'=>json_encode($data)]);
        if(!$res){
            $delay = 1;//$delay为延迟时间
            //重新发布该任务
            $job->release($delay);
        }else{
            //成功之后
            $job->delete();
        }
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
入队列(生产任务)

1). 有push()和later()两种方法,前者是立即执行,后者是延迟$delay秒后执行。

$job = ‘app\index\job\Task’;//调用的任务名
$data = [];//传入的数据
$queue = ‘group1’;//队列名,可以理解为组名

//push()方法是立即执行
Queue::push($job, $data, $queue);

//later()方法是延迟 $delay 秒之后再执行
$delay = 10;//延迟时间
Queue::later($delay, $job, $data, $queue);
1
2
3
4
5
6
7
8
9
10
2). 调用later()方法,将该任务分配到group1队列里,延迟10秒执行

use think\Queue;

class Index{
public function index(){
Queue::later(10,’app\index\job\Task’,[‘arg1’=>1,’arg2’=>2],’group1’);
}
}
1
2
3
4
5
6
7
3). 调用之后,我们通过redis的远程管理工具会发现指定db库的队列里有对应的数据,说明完成了入队列

1) {“job”:”app\index\job\Task”,”data”:{“name”:”group1″,”data”:[]},”id”:”90BWJfkfgIISzK67cWg99mDxB428GV3A”,”attempts”:1}
1
出队列(消费任务)

在项目根目录执行命令

php think queue:work –queue group1
1
之后(其中group1为队列名),数据库成功写入一条数据。接下来开始安装和配置supervisor来守护该进程不断的执行任务。

三、supervisor的安装和配置
yum安装supervisor

yum install epel-release

yum install supervisor

//设置成开机自动启动

systemctl enable supervisord

1
2
3
4
5
配置

在这里我创建了一个命名为supervisor的目录用于存放supervisor和队列的日志文件以及include的配置文件,其目录结构为:
/var/supervisor/log/ #可以自定义
/run/ #可以自定义
/conf/ #可以自定义
1
2
3
然后找到/etc/supervisord.conf配置文件,编辑如下信息:
; 将supervisor.sock 的路径换成如下

[unix_http_server]

file=/var/supervisor/run/supervisor.sock ; (the path to the socket file)

; 将supervisord.log 和 supervisord.pid 的路径换成如下

[supervisord]

logfile=/var/supervisor/log/supervisord.log ; (main log file;default $CWD/supervisord.log)
pidfile=/var/supervisor/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)

; 将supervisor.sock 的路径换成如下

[supervisorctl]

serverurl=unix:///var/supervisor/run/supervisor.sock ; use a unix:// URL for a unix socket

; 将最底部的files路径换成如下

[include]

files = /var/supervisor/conf/*.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在/var/supervisor/conf目录里创建一个.conf文件,这里命名为queue_work.conf,内容如下:

[program:queue_worker]

;项目名称
directory = /opt/www/tp5.1 ; 程序的启动目录,项目根目录的上一级
command = php think queue:work –queue queueName –daemon ; 启动命令 queueName就是队列名
process_name=%(program_name)s_%(process_num)02d
numprocs = 3 ; 开启的进程数量
autostart = true ; 在 supervisord 启动的时候也自动启动
startsecs = 5 ; 启动 5 秒后没有异常退出,就当作已经正常启动了
autorestart = true ; 程序异常退出后自动重启
startretries = 3 ; 启动失败自动重试次数,默认是 3
user = root ; 用哪个用户启动
redirect_stderr = true ; 把 stderr 重定向到 stdout,默认 false
stdout_logfile_maxbytes = 50MB ; stdout 日志文件大小,默认 50MB
stdout_logfile_backups = 20 ; stdout 日志文件备份数
; stdout 日志文件,需要手动创建目录(supervisord 会自动创建日志文件)
stdout_logfile = /var/supervisor/log/queue_worker.log
loglevel=info
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
对于index这个单模块而言,不同的业务逻辑为了区分可能会存在多个队列名,这种情况将多个队列名用逗号拼接起来:
command = php think queue:work –queue queueName1,queueName2 –daemon ;
1
重启

systemctl stop supervisord

systemctl start supervisord

1
2

systemctl restart supervisord

1
调用方法,成功写入数据库。
————————————————
版权声明:本文为CSDN博主「牛转乾坤~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34856247/article/details/86741533

Tp5使用命令行模式,自定义指令

https://blog.csdn.net/benben0729/article/details/82286450

第一步,创建一个自定义命令类文件,新建application/common/command/Hello.php

<?phpnamespace app\common\command; use think\console\Command;use think\console\Input;use think\console\input\Argument;use think\console\input\Option;use think\console\Output; class Hello extends Command{    protected function configure()    {        $this->setName('hello')        	->addArgument('name', Argument::OPTIONAL, "your name")            ->addOption('city', null, Option::VALUE_REQUIRED, 'city name')        	->setDescription('Say Hello');    }     protected function execute(Input $input, Output $output)    {    	$name = trim($input->getArgument('name'));      	$name = $name ?: 'thinkphp'; 		if ($input->hasOption('city')) {        	$city = PHP_EOL . 'From ' . $input->getOption('city');        } else {        	$city = '';        }                $output->writeln("Hello," . $name . '!' . $city);    }}

这个文件定义了一个叫hello的命令,并设置了一个name参数和一个city选项。

第二步,配置application/command.php文件

<?phpreturn [    'app\common\command\Hello',];

V5.1.24+版本开始,你可以定义为下面的方式提高性能。

<?phpreturn [	// 指令名 =》完整的类名    'hello'	=>	'app\common\command\Hello',];

第三步,测试-命令帮助-命令行下运行

php think

输出

Think Console version 0.1 Usage:  command [options] [arguments] Options:  -h, --help            Display this help message  -V, --version         Display this console version  -q, --quiet           Do not output any message      --ansi            Force ANSI output      --no-ansi         Disable ANSI output  -n, --no-interaction  Do not ask any interactive question  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands:  build              Build Application Dirs  clear              Clear runtime file  hello              Say Hello   help               Displays help for a command  list               Lists commands make  make:controller    Create a new resource controller class  make:model         Create a new model class optimize  optimize:autoload  Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.  optimize:config    Build config and common file cache.  optimize:schema    Build database schema cache. 

第四步,运行hello命令

php think hello

输出

Hello thinkphp!

添加命令参数

php think hello liuchen

输出

Hello liuchen!

添加city选项

php think hello liuchen --city shanghai

输出

Hello thinkphp!From shanghai

注意看参数和选项的调用区别

在控制器中调用命令

支持在控制器的操作方法中直接调用命令,例如:

<?phpnamespace app\index\controller; use think\Console;use think\Controller; class Index extends Controller{    public function hello($name)    {        $output = Console::call('hello ' . $name);         return $output->fetch();    }}

访问该操作方法后,例如:

http://tp5.com/index/index/hello/name/thinkphp

页面会输出

Hello thinkphp!

一旦在控制器中调用命令,并不会生成cli命令行日志,而是在普通的web日志中记录。

快速生成指令(V5.1.24+

V5.1.24+版本开始,你可以通过命令行指令快速生成一条指令,包括指令类文件,例如:

php think make:command First first

First表示类名,实际生成的指令类则是app\command\First(自动生成的指令类的命名空间默认都是 app\command)。

first表示指令名,建议统一使用小写,如果不传入该参数,则默认用类名的小写作为指令名。

为了让指令可以无需定义自动调用,需要在你的应用配置的console.php配置文件中,增加下面的配置参数:

'auto_path' => env('app_path') . 'command/',

配置后, 你可以在命令行测试下刚才新生成的指令。

php think first

输出结果为:

first

你可以编辑app\command\First类,完成实际的指令任务。

注意,如果你生成了指定命名空间的指令,但又不是你配置的自动加载目录,那么仍然需要在command.php 文件中定义指令。

如果需要生成一个指定的命名空间,可以使用:

php think make:command app\index\Second second

转自:看云https://www.kancloud.cn/manual/thinkphp5_1/354146

PHP用CURL发送Content-type为application/json的POST请求方法

PHP发送JSON POST
/**
 * Curl版本
 * 使用方法:
 * $post_string = "app=request&version=beta";
 * request_by_curl('http://www.waitalone.cn/restServer.php', $post_string);
 */
function json_post($url, $data = NULL)
{

    $curl = curl_init();

    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    if(!$data){
        return 'data is null';
    }
    if(is_array($data))
    {
        $data = json_encode($data);
    }
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    curl_setopt($curl, CURLOPT_HEADER, 0);
    curl_setopt($curl, CURLOPT_HTTPHEADER,array(
        'Content-Type: application/json; charset=utf-8',
        'Content-Length:' . strlen($data),
        'Cache-Control: no-cache',
        'Pragma: no-cache'
    ));
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    $res = curl_exec($curl);
    $errorno = curl_errno($curl);
    if ($errorno) {
        return $errorno;
    }
    curl_close($curl);
    return $res;

}

PHP接受JSON POST
$data = json_decode(file_get_contents('php://input'), true);

swoole 如何实现 websocket 客户端的功能?

workerman 的 websocket 客服端是这样实现的:(很简单,很容易就使用了,和前端差不多)

http://doc.workerman.net/315306

swoole 如何实现这样相同的功能呢?

看了一下的一些文档,始终无法理解:

https://github.com/matyhtf/framework/blob/master/libs/Swoole/Client/WebSocket.php

https://wiki.swoole.com/wiki/page/p-http_client.html

如果只是获取的话,比较简单:

<?php
$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);

$client->on('connect', function($cli) {
    $cli->send("GET / HTTP/1.1\r\n\r\n");
});
$client->on('receive', function($cli, $data) {
    echo "Received: " . $data;
});
$client->on('error', function($cli) {
    echo "Connect failed\r\n\r\n";
});
$client->on('close', function($cli) {
    echo "Connection close\r\n\r\n";
});

$client->connect('xxx.xxx.xxx.xxx', 1234, true);

但是我这里想要实现的是类似 workerman 那样的,可以一直连接。

WebSocket\Server

1.7.9增加了内置的WebSocket服务器支持,通过几行PHP代码就可以写出一个异步非阻塞多进程的WebSocket服务器。

$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);

$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
    echo "server: handshake success with fd{$request->fd}\n";
});

$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
    echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
    $server->push($frame->fd, "this is server");
});

$server->on('close', function ($ser, $fd) {
    echo "client {$fd} closed\n";
});

$server->start();

onRequest回调

WebSocket\Server 继承自 Http\Server

  • 设置了onRequest回调,WebSocket\Server也可以同时作为http服务器
  • 未设置onRequest回调,WebSocket\Server收到http请求后会返回http 400错误页面
  • 如果想通过接收http触发所有websocket的推送,需要注意作用域的问题,面向过程请使用globalWebSocket\Server进行引用,面向对象可以把WebSocket\Server设置成一个成员属性

1、面向过程代码

$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
        echo "server: handshake success with fd{$request->fd}\n";
    });
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
        echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
        $server->push($frame->fd, "this is server");
    });
$server->on('close', function ($ser, $fd) {
        echo "client {$fd} closed\n";
    });
$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) {
    global $server;//调用外部的server
    // $server->connections 遍历所有websocket连接用户的fd,给所有用户推送
    foreach ($server->connections as $fd) {
        // 需要先判断是否是正确的websocket连接,否则有可能会push失败
        if ($server->isEstablished($fd)) {
            $server->push($fd, $request->get['message']);
        }
    }
});
$server->start();

2、面向对象代码

class WebsocketTest {
    public $server;
    public function __construct() {
        $this->server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
        $this->server->on('open', function (swoole_websocket_server $server, $request) {
            echo "server: handshake success with fd{$request->fd}\n";
        });
        $this->server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
            echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
            $server->push($frame->fd, "this is server");
        });
        $this->server->on('close', function ($ser, $fd) {
            echo "client {$fd} closed\n";
        });
        $this->server->on('request', function ($request, $response) {
            // 接收http请求从get获取message参数的值,给用户推送
            // $this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送
            foreach ($this->server->connections as $fd) {
                // 需要先判断是否是正确的websocket连接,否则有可能会push失败
                if ($this->server->isEstablished($fd)) {
                    $this->server->push($fd, $request->get['message']);
                }
            }
        });
        $this->server->start();
    }
}
new WebsocketTest();

客户端

  • Chrome/Firefox/高版本IE/Safari等浏览器内置了JS语言的WebSocket客户端
  • 微信小程序开发框架内置的WebSocket客户端
  • 异步的PHP程序中可以使用Swoole\Http\Client作为WebSocket客户端
  • apache/php-fpm或其他同步阻塞的PHP程序中可以使用swoole/framework提供的同步WebSocket客户端
  • WebSocket客户端不能与WebSocket服务器通信

swoole 的websocket 连接

介绍下两种连接websocket服务器的方法

1 通过访问http服务器然后访问我们的html页面连接我们的websouket服务器,这样我们就需要3个文件。1 http.php  2 ws.php 3 ws.html 简单贴下我的图,也是参考官方文档写的

http.php

<?php
/**

  • Created by PhpStorm.
  • User: xyj
  • Date: 18-9-9
  • Time: 下午4:31
    */

//实例化
$http_server = new swoole_http_server(‘0.0.0.0’,9501);

//服务器配置
$http_server->set(
[
‘enable_static_handler’ => true,
‘document_root’ => ‘/www/wwwroot/test_project/swoole’,
]
);

$http_server->on(‘request’,function($request ,$response){
//print_r($request->get);
//设置响应头信息
$response->cookie(‘xyj’,’hello’,86400);
//服务器返回信息
$response->end(‘http_server’ . json_encode($request->get));
});

$http_server->start();
ws.php

<?php
/**

  • Created by PhpStorm.
  • User: xyj
  • Date: 18-9-9
  • Time: 下午5:02
    */

//创建websocket服务器对象,监听0.0.0.0:9502端口
$ws = new swoole_websocket_server(“0.0.0.0”, 9502);

$ws->set(
[
‘enable_static_handler’ => true,
‘document_root’ => ‘/www/wwwroot/test_project/swoole’,
]
);
//监听WebSocket连接打开事件
$ws->on(‘open’, function ($ws, $request) {
var_dump($request->fd, $request->get, $request->server);
$ws->push($request->fd, “hello, welcome\n”);
});

//监听WebSocket消息事件
$ws->on(‘message’, function ($ws, $frame) {
echo “Message: {$frame->data}\n”;
$ws->push($frame->fd, “server: {$frame->data}”);
});

//监听WebSocket连接关闭事件
$ws->on(‘close’, function ($ws, $fd) {
echo “client-{$fd} is closed\n”;
});

$ws->start();
ws.html



Document

hello swoole 测试

如果是在本地测试环境运行那么wsServer = ‘ws://127.0.0.1:9502’,如果是外网 那么127.0.0.1 就要改成你浏览器上所访问的地址,这里的端口必须要和你websocket 服务器端口一致否则无法访问的

准备工作做好了那么我们就开始访问下websocket,首先在终端 运行http.php 和 ws.php, 正常情况下是这样如果遇到错误一般常见的就是端口被占用,如果是swoole扩展没装好或者是语法错误,那么请在仔细的读啃下官方文档。

如果是端口被占用我们可以使用 lsof -i:9501 命令查看 端口信息, 在kill之前请先认真的看下 该端口是否是重要端口,不要盲目的kill 。

还有一种就是我们开启过http.php 在关闭后 可能被占用的端口没有及时的释放,当我们再次php http.php的时候他也会抛出端口被占用的错误,这里我们可以用 这个命令 netstat -ntlp 来查看tcp监听的端口,其父进程 kill 然后在运行就ok了,继续使用这个命令来查看tcp端口如图

走到这里我们的两个服务器都已经开启了 。我们就可以通过浏览器访问了,在浏览器上输入地址:9501 , 此时这里的端口是要个http服务器的端口一致而不是websocket的端口,访问成功会出现如下图

————————————————
版权声明:本文为CSDN博主「君子有攸往」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiao199306/article/details/82621140