侯体宗的博客 Laravel分块

石可破,丹可磨
  • 首页
  • laravel8仿版
  • beego仿版
  • go_聊天
  • 人生
  • 技术
  • php
  • 架构
  • 数据库
  • 更多
    • 文件下载
    • 匿名群聊
    • 群聊(进来吹会!)
    • 留言
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册
    • 设置栏目
    • 更多设置
    • 分割线
PHP程序员2021年最新面试题集-持续更新中...
管理员 发表于 php 中 2021-03-04 13:42:39 浏览量(22)

PHP中级开发

1. $a=[0,1,2,3]; $b=[1,2,3,4,5]; $a+=$b; $a值是多少?

知识点:【数组相加】

答案:[0,1,2,3,5];


2. HTTP 状态中302、403、 500代码含义?

知识点:【http协议理解】

答案:302:临时转移成功,请求的内容已转移到新位置(临时重定向) 403:禁止访问 500:服务器内部错误


3. echo、print_r、print、var_dump区别

知识点:【PHP函数基础】


4. 语句include和require的区别是什么?重复包含同一个文件分别会有什么提示?

知识点:【include 与require问题】

答案:

1、PHP程序执行到require()时,只会读取一次档案,故常放在程序开头,档案引入后PHP会将网页档重新编译,让引入档成为原先网页的一部分。 

2、PHP程序执行到include()时,每次皆会读取档案,故常用于流程控制的区段,如条件判断或循环中。


5. PHP7 与 PHP5 的区别

知识点:【PHP】

答案:

函数返回类型声明, null合并运算符, 常量数组, 匿名类, use语句可以导入多个统一namespace下的类, php7移除mysql扩展, 变量储存字节减少, 改善数组结构, 改进函数的调用机制。 优化参数传递环节


6. php中字符串处理函数列举5个,简述用途

知识点:【PHP函数】


7. 列举下使用过的框架,及其优缺点【PHP框架】

8. 有两个数组[1,2,5,11,32,15,77]和[99,32,15,5,1,77]两个数组,写段程序找出它们共同都拥有的数,

知识点:【PHP函数】

答案:array_intersect


9. 写条语句从user 表随机调取 1 条数据?

答案:select * from table order by rand() limit 1;


10. 设计一个商品分类表,写出sql语句查询某个分类的所有下级?

答案: id,name,fid;  sql:fid=xx


11. 写出一个类,包含面向对象三大特征和至少三个魔术方法?

知识点:【面向对象特性】【魔术方法】

class f {
public $a;
function aaa(){
echo '我是f类aaa';
}
function __construct($a){ $this->a=$a; } 
function __set($a){ $this->a=$a; }
function __get($a){ $this->a=$a; }
}
class son extends f{ 
//覆盖、重写 
function aaa(){ 
echo '我是son类aaa'; 
f::cry();//这里不会报错,能正确执行父类的cry(); 
} 
} 

PHP高级开发

1. 设计一个商品分类表,写出sql语句查询某个分类的所有上级?【mysql】

答案:请看 上面第10题


2. php中数组处理函数列举5个,简述用途【php函数】

答案:array_values| array_keys |in_array |array_key_exists |array_flip |array_reverse |array_unique |array_map


3. SQL语言数据定义(DDL)语句中有哪些操作关键字?【mysql】

答案:create | drop | alter


4. php7有哪些特点?描述下Trait的继承优先级?【Trait特性】

答案:在trait继承中,优先顺序依次是:来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。


5. php缓存可以使用哪些软件,各自特点是什么?【缓存数据库】

答案:使用redis,memcached等nosql数据库;

redis:适用于数据变化快且数据库大小可遇见(适合内存容量)的应用程序。例如:股票价格、数据分析、实时数据搜集、实时通讯。


6. 如何排查和优化查询比较慢的sql语句?【mysql 慢sql】

答案:开启慢查询,捕获超时sql进行定位优化 比如:explain


7. mysql数据库索引有哪些?什么情况下不适合建立索引?【mysql索引】

答案:普通索引 | 唯一索引 |主键索引 |组合索引| 全文索引

适合建索引

  1. 频繁作为where条件语句查询的字段

  2. 关联字段需要建立索引,例如外键字段,student表中的classid,   classes表中的schoolid 等

  3. 排序字段可以建立索引

  4. 分组字段可以建立索引,因为分组的前提是排序

  5. 统计字段可以建立索引,例如count(),max()

不适合建索引

  1.频繁更新的字段不适合建立索引

  2.where条件中用不到的字段不适合建立索引

  3.表数据可以确定比较少的不需要建索引

  4.数据重复且发布比较均匀的的字段不适合建索引(唯一性太差的字段不适合建立索引),例如性别,真假值

  5. 参与列计算的列不适合建索引


8. 是否用过mysql分库分表?使用了哪种策略, 如何解决增表,减表问题.【mysql 分库分表】

答案:水平切分(横向切分):是对同一个表中的数据进行切分,存储到不同的数据库(主机)之上。规则是根据表中数据的逻辑关系,按照某种条件拆分。

用的mysql-MERGE实现分表,读取分表规则用哈希

//code作为主键,对code做hash 
//return 表名
function get_hash_table($table,$code,$s=9){
      $hash = sprintf("%u", crc32($code));
      $hash1 = intval(fmod($hash, $s));
      return $table."_".$hash1;
}

9. 现需要实现高可用高性能架构,列出你需要用到的软件及其作用?

答案:主备架构,应用层跟静态资源分离

nginx(负载均衡) | redis(缓存,主从) | mysql(主从,读写分离) | cdn


10. 事务的其特性有哪些?mysql是否支持嵌套事务?【mysql事务】

答案:

在 MySQL 中只有 InnDB 引擎支持事务,它的四个特性如下:

原子性(Atomic):要么全部执行,要么全部不执行;

一致性(Consistency):事务的执行使得数据库从一种正确状态转化为另一种正确状态;

隔离性(Isolation):在事务正确提交之前,不允许把该事务对数据的任何改变提供给其他事务;

持久性(Durability):事务提交后,其结果永久保存在数据库中。


Mysql是不支持嵌套事务的,开启了一个事务的情况下,再开启一个事务,会隐式的提交上一个事务。


11. mysql数据库中innodb和myisam引擎的区别【mysql 引擎】

答案:

InnoDB: 

InnoDB 给 MySQL 提供了具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。

InnoDB 提供了行锁(locking on row level),提供与 Oracle 类型一致的不加锁读取(non-locking read in SELECTs)。这些特性均提高了多用户并发操作的性能表现。

在InnoDB表中不需要扩大锁定(lock escalation),因为 InnoDB 的列锁定(row level locks)适宜非常小的空间。

InnoDB 是 MySQL 上第一个提供外键约束(FOREIGN KEY constraints)的表引擎。

InnoDB 的设计目标是处理大容量数据库系统,它的 CPU 利用率是其它基于磁盘的关系数据库引擎所不能比的。在技术上,InnoDB 是一套放在 MySQL 后台的完整数据库系统,InnoDB 在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。 InnoDB 把数据和索引存放在表空间里,可能包含多个文件,这与其它的不一样,举例来说,在 MyISAM 中,表被存放在单独的文件中。

InnoDB 表的大小只受限于操作系统的文件大小,一般为 2 GB。

InnoDB 所有的表都保存在同一个数据文件 ibdata1 中(也可能是多个文件,或者是独立的表空间文件),相对来说比较不好备份,可以拷贝文件或用navicat for mysql。 


对于支持事务的InnoDB类型的表,影响速度的主要原因是AUTO COMMIT默认设置是打开的,而且程序没有显式调用BEGIN 开始事务,导致每插入一条都自动Commit,严重影响了速度。

可以在执行sql前调用begin,多条sql形成一个事物(即使autocommit打开也可以),将大大提高性能。 


MyISAM: 

每张MyISAM 表被存放在三个文件 :frm 文件存放表格定义。 数据文件是MYD (MYData) 。 索引文件是MYI (MYIndex) 引伸。 

因为MyISAM相对简单所以在效率上要优于InnoDB,小型应用使用MyISAM是不错的选择。

MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦。 

MyIASM是IASM表的新版本,有如下扩展: 

1. 二进制层次的可移植性。 

2. NULL列索引。 

3. 对变长行比ISAM表有更少的碎片。 

4. 支持大文件。 

5. 更好的索引压缩。 

6. 更好的键吗统计分布。 

7. 更好和更快的auto_increment处理。


12. 缓存穿透、缓存雪崩、缓存击穿的区别以及对应的解决方案?【缓存数据库】

答案:

缓存穿透:

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

解决方案:

1.接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;

2.从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击


缓存击穿:

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

解决方案:

1.设置热点数据永远不过期。

2.加互斥锁

缓存雪崩:

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,        

缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案:

1.缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

2.如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。

3.设置热点数据永远不过期。


13. 用php实现冒泡排序算法,使用php实现快速排序算法?

答案:

冒泡排序:
    /**
     * @param $arr
     * 冒泡排序算法的原理如下:
     * 1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。
     * 2.对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
     * 3.针对所有的元素重复以上的步骤,除了最后一个。
     * 4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
     */
    private function bubbleSort($arr)
    {
        //获取 长度
        $len = count($arr);
        //循环比较(相邻的两个元素,比较,交换)
        for ($k = 0; $k <= $len; $k++) {
            for ($j = $len - 1; $j > $k; $j--) {
                //比较
                if ($arr[$j] < $arr[$j - 1]) {
                    //交换
                    $temp = $arr[$j];
                    $arr[$j] = $arr[$j - 1];
                    $arr[$j - 1] = $temp;
                }
            }
        }
        return $arr;
    }
快速排序
  /** @param $arr
     * 快速排序算法原理如下:
     *  1.通过设置一个初始中间值,来将需要排序的数组分成3部分:小于中间值的左边,中间值,大于中间值的右边
     *  2.继续递归用相同的方式来排序左边和右边
     *  3.最后合并数组
     */
    function quick_sort($arr)
    {
      //先判断是否需要继续进行
      $length = count($arr);
      if($length <= 1){ return $arr; }
      $base_num = $arr[0];//选择一个标尺 选择第一个元素
      //初始化两个数组
      $left_array = array();//小于标尺的
      $right_array = array();//大于标尺的
      for($i=1; $i<$length; $i++){      
      //遍历 除了标尺外的所有元素,按照大小关系放入两个数组内
        if($base_num > $arr[$i]){
          //放入左边数组
          $left_array[] = $arr[$i];
        }else{
          //放入右边
          $right_array[] = $arr[$i];
        }
      }
      //再分别对 左边 和 右边的数组进行相同的排序处理方式
      //递归调用这个函数,并记录结果
      $left_array = quick_sort($left_array);
      $right_array = quick_sort($right_array);
      //合并左边 标尺 右边
      return array_merge($left_array, array($base_num), $right_array);
    }
    $arr = array(3,1,2);
    var_dump(quick_sort($arr));

14. 设置php错误级别,除通知错误都可以显示

答案:

error_reporting(E_ALL ^ E_NOTICE);


15. 安全对一套程序来说至关重要,请说说在开发中应该注意哪些安全机制?

答案:

1.使用验证码防止注册机灌水。

2.使用预处理,绑定参数,参数过滤转义 防止sql注入

3.使用token防止远程提交,使用token验证登录状态。


16. 作用域操作符::如何使用?都在哪些场合下使用?

答案:

1.调用类常量

2.调用静态方法(使用static修饰的类方法)


17. $this和self、parent这三个关键词分别代表什么?在哪些场合下使用?

答案:

$this 当前对象

self 当前类

parent 当前类的父类


$this 在当前类中使用,使用->调用属性和方法。

self 也在当前类中使用,不过需要使用::调用。

parent 在类中使用。


18. PHP的 数组底层实现原理

答案:PHP的数组是用链地址法的哈希结构去实现的,链表是双向链表,这样既可以动态分配数组空间,也可以通过key值去计算hash值去访问对应的元素,是一种非常高效的数据结构。


19. PHP的变量底层实现原理

https://zongscan.com/demo333/411.html


20. PHP的GC机制原理

答案:引用计数

每个php变量存在一个叫"zval"的变量容器中。包括四个字段:-name:字段值,-type:字段类型 ,-Is_ref:标识这个变量是否是属于引用集合变量,-refcount:表示指向这个zval变量容器的变量(也称符号即symbol)个数 。容器在”refcount“变成0时就被销毁。

当被变量引用时refcount+1,当变量撤掉时refcount-1,当计数器=0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。


21. Laravel的依赖注入实现原理

答案:主要运用了 PHP 反射 api 的 ReflectionMethod 类,在 PHP 运行状态中,扩展分析 PHP 程序


22. LUMEN的中间件原理

23. MYSQL B+TREE底层原理

答案:

B+树索引是B+树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引。B+树中的B代表平衡(balance),而不是二叉(binary),因为B+树是从最早的平衡二叉树演化而来的。在讲B+树之前必须先了解二叉查找树、平衡二叉树(AVLTree)和平衡多路查找树(B-Tree),B+树即由这些树逐步优化而来


24. 一条SQL语句在MySQL中如何执行的?

答案:

查询sql:

1.先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 sql 语句为 key 在内存中查询是否有结果,如果有直接缓存,如果没有,执行下一步。

2.通过分析器进行词法分析,提取 sql 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 tb_student,需要查询所有的列,查询条件是这个表的 id='1'。然后判断这个 sql 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。

3.优化器根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。

4.进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。

更新sql:

先查询如果有缓存,也是会用到缓存。然后拿到查询的语句,把要更改的字段更新,然后调用引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。•执行器收到通知后记录 binlog,然后调用引擎接口,提交 redo log 为提交状态。更新完成。


25. 什么是静态延迟绑定

答案:

在父类中获取子类的最终状态。在父类中,如果出现self关键字,被子类继承后,这个self值的还是父类而不是子类。

如果在父类中出现了self关键字,并且子类继承了含有self的这段代码,那么需要考虑静态延迟绑定。在父类中使用static代替self.


26. redis 持久化策略

答案:2种

1.rdb:快照形式是直接把内存中的数据保存到一个dump文件中,定时保存,保存策略

备注:该方式不能完全保证数据持久化,因为是定时保存,所以当redis服务down掉,就会丢失一部分数据,而且数据量大,写操作多的情况下,会引起大量的磁盘IO操作,会影响性能。

2.aof:把所有的对redis的服务器进行修改的命令都存到一个文件里,命令的集合

备注:该方式Redis会把每一个写请求都记录在一个日志文件里。在Redis重启时,会把AOF文件中记录的所有写操作顺序执行一遍,确保数据恢复到最新。


27. Redis淘汰策略

答案:6种

1.volatile-lru:从已设置过期时间的数据集(server. db[i]. expires)中挑选最近最少使用的数据淘汰。

2.volatile-ttl:从已设置过期时间的数据集(server. db[i]. expires)中挑选将要过期的数据淘汰。

3.volatile-random:从已设置过期时间的数据集(server. db[i]. expires)中任意选择数据淘汰。

4.allkeys-lru:从数据集(server. db[i]. dict)中挑选最近最少使用的数据淘汰。

5.allkeys-random:从数据集(server. db[i]. dict)中任意选择数据淘汰。

6.no-enviction(驱逐):禁止驱逐数据。


28. Redis高可用

答案:

Redis高可用常见的有两种方式:

1.主从复制(Replication-Sentinel模式)

2.Redis集群(Redis-Cluster模式)


29. http协议理解 

答案:

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写。HTTP 协议和 TCP/IP 协议族内的其他众多的协议相同, 用于客户端和服务器之间的通信。请求访问文本或图像等资源的一端称为客户端, 而提供资源响应的一端称为服务器端。 


30. TCP协议理解【三次握手,四次挥手】

答案:

三次握手:

1.Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。

2.Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack (number )=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。

3.Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

四次挥手:

1.Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。

2.Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。

3.Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。

4.Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。


31. 判断一个数是否为2的整数次幂 

答案:

0和1按位与运算的结果是0,所以凡是2的整数次幂和它本身减1的结果进行与运算,结果都必定是0。

反之,如果一个整数不是2的整数次幂,结果一定不是0!

对于一个整数n,只需要计算n&(n-1)的结果是不是0。这个方法的时间复杂度只有O(1)

公式判断:(num & num - 1) == 0?是:否;


32. linux 中存在一日志文件非常大,打开速度很慢,如何查找其中部分指定内容

答案:grep -n -e '关键词1' -e '关键词2' xxx.log


33. 负载均衡方式

答案:

1、轮询(默认)

每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。

2、weight

指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。

3、ip_hash

如果客户已经访问了某个服务器,当用户再次访问时,会将该请求通过哈希算法,自动定位到该服务器

4、fair(第三方)

按后端服务器的响应时间来分配请求,响应时间短的优先分配。

5、url_hash(第三方)

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。


34. 两数组直接相加,array_merge 与直接相加的区别

答案:

+:左边数组根据key覆盖右边数组,右边数组多出来的元素添加到左边数组尾部,array_merge反之


35. session与cookie区别

答案:

1.cookie数据是存储到客户端,而session是存储到服务端

2.在浏览器中,cookie存储的数据大小有限 ;而session没有大小限制,跟服务端的内存有关

3.cookie安全性差,可以通过拦截或本地文件可以对cookie进行攻击;而session运行在服务端,安全性高

4.如果浏览器禁用了cookie,那么cookie就不能使用,而session不能禁用


36. 遍历两个一个数组最后的结果

$a = [1,2,3];
foreach($a as &$v){
//啥也不干
}
foreach($a as $v){
//啥也不干
}
$a = ?[1,2,2];


37. PHP计算最大公约数

function baseDefine($m, $n) {
    if($m ==0 && $n == 0) {
        return false;
    }
    $min = min($m, $n);
    while($min >= 1) {
        if($m % $min == 0){
        if($n % $min ==0) {
            return $min;
            }
        }
        $min -= 1;
    }
    return $min;
}

38.

39.持续更新中....


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
PHP数组的底层实现原理浅析
管理员 发表于 php 中 2021-03-02 16:48:20 浏览量(13)

PHP数组的底层实现原理是:

1、哈希表,将不同的关键字映射到不同单元的一种数据结构;

2、链表,就是由不同的链表节点组成的一种数据结构;

3、php数组,使用链接法解决哈希冲突的方式。


一、哈希表

哈希表,顾名思义,即将不同的关键字映射到不同单元的一种数据结构。而将不同关键字映射到不同单元的方法就叫做哈希函数

理想情况下,经过哈希函数处理,关键字和单元是会进行一一对应的;但是如果关键字值足够多的情况下,就容易出现多个关键字映射到同一单元的情况,即出现哈希冲突

哈希冲突的解决方案,要么使用链接法,要么使用开放寻址法


链接法:

即当不同的关键字映射到同一单元时,在同一单元内使用链表来保存这些关键字


开放寻址法:

即当插入数据时,如果发现关键字被映射到的单元存在数据了,说明发生了冲突,就继续寻找下一个单元,直到找到可用单元为止

而因为开放寻址法方案属于占用其他关键字映射单元的位置,所以后续的关键字更容易出现哈希冲突,因此容易出现性能下降


二、链表

既然上面提到了链表,这里我们简单聊一下链表的基础知识。链表分为很多种类型,常用的数据结构包括:队列,栈,双向链表等

链表,就是由不同的链表节点组成的一种数据结构。链表节点一般由元素+指向下一节点的指针组成。而双向链表,顾名思义,则是由指向上一节点的指针+元素+指向下一节点的指针组成

对于数据结构的内容,我们不过多展开,我们之后会有专门的内容去详细介绍数据结构


三、php数组

php解决哈希冲突的方式是使用了链接法,所以php数组是由哈希表+链表实现,准确来说,是由哈希表+双向链表实现


四、内部结构-哈希表

HashTable结构体主要用来存放哈希表的基本信息

typedef struct _hashtable { 
    uint nTableSize;        // hash Bucket的大小,即哈希表的容量,最小为8,以2x增长。
    uint nTableMask;        // nTableSize-1 , 索引取值的优化
    uint nNumOfElements;    // hash Bucket中当前存在的元素个数,count()函数会直接返回此值 
    ulong nNextFreeElement; // 下一个可使用的数字键值
    Bucket *pInternalPointer;   // 当前遍历的指针(foreach比for快的原因之一)
    Bucket *pListHead;          // 存储整个哈希表的头元素指针
    Bucket *pListTail;          // 存储整个哈希表的尾元素指针
    Bucket **arBuckets;         // 存储hash数组
    dtor_func_t pDestructor;    // 在删除元素时执行的回调函数,用于资源的释放
    zend_bool persistent;       //指出了Bucket内存分配的方式。如果persisient为TRUE,则使用操作系统本身的内存分配函数为Bucket分配内存,否则使用PHP的内存分配函数。
    unsigned char nApplyCount; // 标记当前hash Bucket被递归访问的次数(防止多次递归)
    zend_bool bApplyProtection;// 标记当前hash桶允许不允许多次访问,不允许时,最多只能递归3次
#if ZEND_DEBUG
    int inconsistent;
#endif
} HashTable;


Bucket结构体则用于保存数据的具体内容

typedef struct bucket {
    ulong h;            // 对char *key进行hash后的值,或者是用户指定的数字索引值
    uint nKeyLength;    // hash关键字的长度,如果数组索引为数字,此值为0
    void *pData;        // 指向value,一般是用户数据的副本,如果是指针数据,则指向pDataPtr
    void *pDataPtr;     // 如果是指针数据,此值会指向真正的value,同时上面pData会指向此值
    struct bucket *pListNext;   // 指向整个哈希表的该单元的下一个元素
    struct bucket *pListLast;   // 指向整个哈希表的该单元的上一个元素
    struct bucket *pNext;       // 指向由于哈希冲突导致存放在同一个单元的链表中的下一个元素
    struct bucket *pLast;       // 指向由于哈希冲突导致存放在同一个单元的链表中的上一个元素
    // 保存当前值所对于的key字符串,这个字段只能定义在最后,实现变长结构体
    char arKey[1];              
} Bucket;


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
php7垃圾回收变量的GC机制详解
管理员 发表于 php 中 2021-02-26 15:46:07 浏览量(43)

在php中的变量占用的空间,是不需要我们手动回收的。内核帮我们处理了这一部分的工作。相比C,这大大方便了我们的操作。

php 的变量在底层的实现:

zval 的结构

// php 变量对于的c结构体
struct _zval_struct {
    zend_value value;
    union {
       ……
    } u1;
    union {
        ……
    } u2;
};

由于主要讲垃圾回收,所以在这里简单介绍下 u1 u2 联合体的功能

u1 结构比较复杂,我认为主要是用于识别变量类型

u2 这里面大多都是辅助字段,变量内部功能的实现、提升缓存友好性等等

接下来是我们的主角

zend_value 它也是结构体中内嵌的一个联合体

typedef union _zend_value {
    zend_long         lval;//整形
    double            dval;//浮点型
    zend_refcounted  *counted;//获取不同类型的gc头部
    zend_string      *str;//string字符串
    zend_array       *arr;//数组
    zend_object      *obj;//对象
    zend_resource    *res;//资源
    zend_reference   *ref;//是否是引用类型

   // 忽略下面的结构,与我们讨论无关

    zend_ast_ref     *ast;
    zval             *zv;
    void             *ptr;
    zend_class_entry *ce;
    zend_function    *func;
    struct {
        ZEND_ENDIAN_LOHI(
            uint32_t w1,
            uint32_t w2)
    } ww;
} zend_value;

在 zval的 value中就记录了引用计数zend_refcounted *counted这个类型,我们的垃圾回收机制也是基于此的。

typedef struct _zend_refcounted_h {
    uint32_t         refcount;          /* reference counter 32-bit */
    union {
        struct {
            ZEND_ENDIAN_LOHI_3(
                zend_uchar    type,
                zend_uchar    flags,    /* used for strings & objects */
                uint16_t      gc_info)  /* keeps GC root number (or 0) and color */
        } v;
        uint32_t type_info;
    } u;
} zend_refcounted_h;

所有的复杂类型的定义, 开始的时候都是zend_refcounted_h结构, 这个结构里除了引用计数以外, 还有GC相关的结构. 从而在做GC回收的时候, GC不需要关心具体类型是什么, 所有的它都可以当做zend_refcounted*结构来处理.


变量的自动回收

在php中 除了 array和object类型的变量,其余大部分是自动回收

php 普通变量的回收和 该变量的引用次数有关。

官方的例子:

$a = 1;
$b = $a;
xdebug_debug_zval('a');
$a =10;
xdebug_debug_zval('a');
unset($a);
xdebug_debug_zval('a');

结果:

a:
(refcount=2, is_ref=0),int 1
a:
(refcount=1, is_ref=0),int 10
a: no such symbol

可以看到 当$a =10 的时候 涉及到 php的COW(copy-on-write)机制,$b 会复制一份原先的 $a ,解除了他们之间的引用关系,所以a的引用次数(refcount)减少为1。

然后我们uset($a)之后 a的引用次数变为0。这就会被认为是垃圾变量,释放空间。

在举一个例子:

$a = [1];
$a[1] = &$a;
unset($a);

在 unset($a) 之前 $a 的类型为引用类型

a:
(refcount=2, is_ref=1),
array (size=2)
  0 => (refcount=1, is_ref=0),int 1
  1 => (refcount=2, is_ref=1),
    &array<

unset($a) 之后,我们unset操作时refcount 由2变为1,因为有内部引用指向 $a,所以在外部 其所占用的空间并不会被销毁。

然后我们的外部引用已经被中断了,我们也不能使用它。它就成了一个“孤儿”,在c语言中叫做野指针。在php中叫做循环引用。内存泄漏。想要销毁变量的话,只能等 php脚本结束。


循环引用造成的内存泄漏

为了清理这些垃圾,引入了两个准则

● 如果引用计数减少到零,所在变量容器将被清除(free),不属于垃圾

● 如果一个zval 的引用计数减少后还大于0,那么它会进入垃圾周期。其次,在一个垃圾周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾。

循环引用基本上只会出现在 数组和对象中,对象是因为它的本身就是引用


object和array的回收过程

php7的垃圾回收包含两个部分,一个是垃圾收集器,一个是垃圾回收算法。

垃圾收集器,把刚刚提到的,可能是垃圾的元素收集到回收池中 也就是把变量的 zend_refcount的信息 放在回收池中。 当回收池的值达到一定额度了,会进行统一处理。

处理的过程呢,就比较简单。

遍历回收池中的每一个变量,根据每一个变量,再遍历每一个成员,如果成员还有嵌套的话继续遍历。然后把所有成员的 做模拟的 refcount -1。如果此时外部的变量的 引用次数为 0 。那么可以视为垃圾,清楚。如果大于0,那么恢复引用次数,并从垃圾回收池中取出。


垃圾回收的原理

如果你这个变量不是垃圾,那么它的所有成员变量的引用减一之后,必然不会是总变量的引用为0。

例子:

说的比较死,不如举个例子。刚刷 sf.gg 的时候看到一道关于 GC 的题,我回答了一波。关于GC垃圾回收机制

题目如下:

$a = array('one');
$a[] = &$a;
$a[] = &$a;

结果:

a:(refcount=3,is_ref=1) = array(
0 => (refcount=1,is_ref=0)='one',
1 => (refcount=3,is_ref=1)=...
2 => (refcount=3,is_ref=1)=...
)

问:

那么该数组进行unset后肯定是个垃圾,那么比如我先放到buffer,refcount-1,那么refcount=2,然后再unset-1,refcount=1,

这样似乎虽然这个数组删除时会造成垃圾,但根据新的GC机制,只有当refcount=0时才会被认为是垃圾,那不是矛盾了吗?

那我数组里面是不是只要>=2的key引用了本身,都不会视为垃圾了?

答:

1、只要zval.value的refcount减一,然后缺其refcount的值不为0那么它就可能是垃圾,进入垃圾周期。

2、进入垃圾池遍历所有成员,包括其嵌套的成员,都对其做 refcount-1的操作,看外部的引用是否为0。

那么对于 题主的问题来说,

首先,你要想$a为垃圾,一定要先对 unset($a)操作,那么此时 $a的 refcount = 2

对于$a[0] refcount-1 不影响外部的$a,

$a[1] refcount-1 ,此时 $a的 refount=1

$a[2] refcount-1 ,此时 $a 的 refount=0 

模拟减结束,那么此变量被当成垃圾回收。


转:https://www.php.cn/topic/php7/425508.html

去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
在Laravel中进行类型转换详解
管理员 发表于 Laravel 中 2021-02-24 14:18:06 浏览量(24)

强制转换值意味着将其更改为(或确保已经)特定类型。

您可能熟悉的某些类型是Integer或Boolean。

简而言之,类型转换是一种将实体从一种数据类型更改为另一种数据类型的方法。

我为什么要关心类型转换:

因此,当您将数字存储在数据库中时-默认情况下,它们将以字符串形式返回。

但是类型转换允许您将它们转换为整数,实数,浮点数或布尔值,可以将数据库中的1和0转换为true和false


我们开始做吧 

因此,在您的模型中,您可以编写:

protected $casts =  [
    'status' => 'boolena',
]

它将status(1,0)转换为true或false。

因此,当您从表格中获取任何数据时,它将状态列转换为true或false。

您不需要编写任何类似这样的逻辑:

if($post-> status == 1){
    //显示状态为活动
}else{
    /显示状态为无效
}

数组和JSON转换

此外,如果表包含存储序列化JSON字符串的列,并且您希望在Eloquent模型上访问该特定列时将该属性自动反序列化为PHP数组,则应使用数组强制转换。


您可以这样做:

<?php
namespace App\Model\Post;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
   protected $fillable = ['title'];
   protected $casts = [
       'tags' => 'array',
   ];
}

因此,您可以将JSON标签数据存储到post表中,但是在获取帖子时,您可以将其自动转换为PHP数组,因此现在您可以避免创建tags表。


结论

如您所见,雄辩的属性转换具有将我们从不必要的重复逻辑中解放出来的巨大潜力,并且还使我们可以轻松地将数据存储在JSON中。


转:https://dev.to/ajayyadavexpo/type-casting-in-laravel-4npm


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
mysql数据库中事务的四个特征及四种隔离级别的浅析描述
管理员 发表于 数据库 中 2021-02-23 14:16:42 浏览量(23)

事务的四个特征(ACID)

事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。

1 、原子性。事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做


2 、一致性。事 务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。


3 、隔离性。一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。


4 、持续性。也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。


Mysql的四种隔离级别

SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。


1.Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。


2.Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。


3.Repeatable Read(可重读)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。


4.Serializable(可串行化)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

 

总结

这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。

例如:

脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。


不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。


幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
Linux awk 命令及统计nginx日志里访问次数最多的前十个IP
管理员 发表于 linux 中 2021-02-06 10:52:41 浏览量(55)

描述:

AWK 是一种处理文本文件的语言,是一个强大的文本分析工具。

之所以叫 AWK 是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。


语法:

awk [选项参数] 'script' var=value file(s)
或
awk [选项参数] -f scriptfile var=value file(s)

选项参数说明:

-F fs or --field-separator fs

指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F:。

-v var=value or --asign var=value

赋值一个用户定义变量。

-f scripfile or --file scriptfile

从脚本文件中读取awk命令。

-mf nnn and -mr nnn

对nnn值设置内在限制,-mf选项限制分配给nnn的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。

-W compact or --compat, -W traditional or --traditional

在兼容模式下运行awk。所以gawk的行为和标准的awk完全一样,所有的awk扩展都被忽略。

-W copyleft or --copyleft, -W copyright or --copyright

打印简短的版权信息。

-W help or --help, -W usage or --usage

打印全部awk选项和每个选项的简短说明。

-W lint or --lint

打印不能向传统unix平台移植的结构的警告。

-W lint-old or --lint-old

打印关于不能向传统unix平台移植的结构的警告。

-W posix

打开兼容模式。但有以下限制,不识别:/x、函数关键字、func、换码序列以及当fs是一个空格时,将新行作为一个域分隔符;操作符**和**=不能代替^和^=;fflush无效。

-W re-interval or --re-inerval

允许间隔正则表达式的使用,参考(grep中的Posix字符类),如括号表达式[[:alpha:]]。

-W source program-text or --source program-text

使用program-text作为源代码,可与-f命令混用。

-W version or --version

打印bug报告信息的版本。


具体用法及信息:

https://www.runoob.com/linux/linux-comm-awk.html


功能案例:统计nginx日志里访问次数最多的前十个IP

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr -k1 | head -n 10


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
论不要在mysql中使用[utf8]编码,如果要用请用[utf8mb4]
管理员 发表于 数据库 中 2021-02-04 11:11:42 浏览量(72)

mysql中请放弃utf8使用utf8mb4编码

MySQL 的 utf8 不是 UTF-8

「utf8」编码只支持每个字符占三个字节。真正的 UTF-8 编码是 - 每个人都使用,包括你自己 - 每个字符需要四个字节。

MySQL 的开发人员并没有修复这个 bug,但是他们在 2010 年发布了一个解决方案:一种叫做「utf8mb4」的新编码。

当然,他们并没有对外宣传这件事 (大概是因为这个 bug 太尴尬了),现在网上仍然有很多教程让我们使用「utf8」编码,但都是错的。


简而言之:

MySQL 的「utf8mb4」才是真正意义上的「UTF-8」。

MySQL 的「utf8」指的是「一种专有的编码」,这种编码有很多 Unicode 字符不能编码。

我在此做一个明确的声明:所有还在使用「utf8」编码的 MySQL 和 MariaDB 的用户实际上应当使用「utf8mb4」编码。而不应该继续使用「utf8」。


MySQL中 [utf8] / UTF-8 的历史故事

为什么 MySQL 的开发者使用这种不合理的「utf8」?我们可以通过 MySQL 的 commit 日志猜测一下。

MySQL 从 version 4.1 开始支持 UTF-8,大约是在 2003 的时候,比现在的 UTF-8 标准,RFC 3629 还要早。

而以前使用的是 RFC 2279 这套 UTF-8 标准,这套标准中每个字符 6 个字节。MySQL 开发者在 first pre-pre-release version of MySQL 4.1,也就是 2002 年 3 月 28 号实现 RFC 2279 标准。

然后又发生了一件神奇的事情,MySQL 的源代码在 9 月的版本中有一个字节的调整:「UTF8 最大仅支持 3 个字节序列」。

是谁发起了这个 commit?为什么发布这个 commit?我并不知晓。MySQL 的代码仓库似乎在采用 Git 作为版本控制后丢失了一些曾经的贡献者的名字(MySQL 过去的使用的是 BitKeeper 版本控制,像 Linux 内核一样)。MySQL 官方也没有在 2003 年 9 月的正式版发布时的邮件清单中解释这个变更。


但是我可以猜想。

我们说回 2002 年,MySQL 告诉用户,如果用户需要保证每条记录在表中有相同的字节长度的话,有一套 加速优化方案 可以使用。这套方案要求用户在定义字符字段的时候使用「CHAR」类型。一个「CHAR」类型的字段不管存储的数据内容是什么,存储的字符数量始终相同。如果存储的字符比字段规定的要少,会使用空格在结尾填充,直到数量匹配为止;如果存储的字符比字段规定的要多时,会截断多出来的字符。

当 MySQL 开发人员第一次尝试 UTF-8 时,就是还在使用每个字符 6 个字节这种方案时,他们似乎有些犹豫:一个 CHAR(1) 的列会使用 6 个字节长度;一个 CHAR(2) 的列会使用 12 个字节长度等等。


明确的说:他们最初的没有正式发布的做法是正确的,这种解决方案是有据可查且广泛适用的,任何一个只要是理解 UTF-8 编码的人都会认同这个方案。


但是很明显,MySQL 开发人员(或者是发行商)考虑到可能有一些用户会做这两件事情:

1.使用 CHAR 类型字段。(CHAR 字段现在基本没人使用了。但是在当时,MySQL 中使用 CHAR 字段会有更优的速度,但是在 2005 年之后却并非如此)

2.将 CHAR 字段列的编码设置为「utf8」。


我的猜测是 MySQL 开发人员为了方便那些既想优化空间和时间性能,又没能成功优化空间和时间性能的用户,废弃了原有的「utf8」编码。


然而没有人能得到自己想要的。想要优化时间和空间性能的用户仍旧在错误的使用「utf8」CHAR 字段,但是这些字段往往会很大,导致 MySQL 的速度比不使用 CHAR 的时候还要慢。而那些想要使用真正的 UTF-8 的用户错误的使用了「utf8」,导致他们不能存储「?」这类字符。


但是,一旦 MySQL 发布这个无用的字符编码,就再也不能修复它了:

因为这会令所有的用户都要重新构建每一个数据库。所以 MySQL 最终在 2010 年发布了真正的 UTF-8,不过使用了另一个名字「utf8mb4」。


总结

1.数据库系统有微妙的错误和异常,你可以通过数据库的选择避免许多错误。

2.如果你需要一个数据库, 不要使用 MySQL 或者 MariaDB。 使用 PostgreSQL。

3.如果你需要使用 MySQL 或 MariaDB,千万不要使用「UTF8」。当你想要 UTF-8 时,总是使用「UTF8Mb4」。现在去 转换你的数据库吧避免发生头痛的事。


转:https://medium.com/@adamhooper/in-mysql-never-use-utf8-use-utf8mb4-11761243e434


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
JWT源码实现逻辑详解
管理员 发表于 Laravel 中 2021-02-03 15:33:36 浏览量(84)

描述:

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519). 该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。


详细信息可查看什么是 JWT – JSON WEB TOKEN(https://www.jianshu.com/p/576dbf44b2ae),这里就不做过多介绍。

在项目中我使用了 tymon/jwt-auth 扩展包,所以根据对这个包进行源码分析,了解其具体的实现逻辑

通过集成这个包,我们可以在 config.auth.php 中修改看守器的驱动,将 driver 修改为 jwt

'guards' => [
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],

在用户登录,可通过 Auth::guard('api')->attempt(['email'=>$'email,'password'=>$password]); 校验用户登录并返回 token


其中校验密码的过程,我们一般将用户的密码通过 bcrypt () 函数进行加密,通过 bcrypt () 函数即使密码相同,生成的字符串也不相同。然后通过 password_verify () 函数验证密码是否和散列值匹配。


用户登录校验完成后,会返回如下的字符串,这个字符串就是 token, 它分为三个部分,第一部分我们称它为头部(header), 第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature),通过. 将字符串连接在一起。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9lbGVtZW50LXNob3AudGVzdFwvYWRtaW5cL2FwaVwvbG9naW4iLCJpYXQiOjE2MTE4OTAwNDUsImV4cCI6MTYxMjEwNjA0NSwibmJmIjoxNjExODkwMDQ1LCJqdGkiOiJMTnQ3N01yaXlNUDVKRmFaIiwic3ViIjoyLCJwcnYiOiJhMjNiNTczZGM3M2E0MDdlOGRlNTNiNDg2ZjM2ODg2YWRmNzBjNDgzIn0.B4bHALzb5lg0z-G2iU3wwiYb4r18-wUa0TVH_V1X1IE


通过查看源码实现,其中 encode () 用于生成 token,decode () 用于解析 token

/**
     * Create a JSON Web Token.
     *
     * @param  array  $payload
     *
     * @throws \Tymon\JWTAuth\Exceptions\JWTException
     *
     * @return string
     */
    public function encode(array $payload)
    {
        // Remove the signature on the builder instance first.
        $this->builder->unsign();
        try {
            foreach ($payload as $key => $value) {
                $this->builder->set($key, $value);
            }
            $this->builder->sign($this->signer, $this->getSigningKey());
        } catch (Exception $e) {
            throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);
        }
        return (string) $this->builder->getToken();
    }

继续查看生成 token 逻辑

/**
     * Returns the resultant token
     *
     * @return Token
     */
    public function getToken(Signer $signer = null, Key $key = null)
    {
        //为声明的加密算法,这里为HS256,可在./config/jwt.php中配置algo参数修改
        $signer = $signer ?: $this->signer;
        //token加密私钥,只存储在服务端,也是实现token最重要的一环,可在./config/jwt.php中配置secret参数修改
        //一般通过php artisan jwt:secret命令生成
        $key = $key ?: $this->key;
        if ($signer instanceof Signer) {
            //在token头部添加加密算法
            $signer->modifyHeader($this->headers);
        }
        //生成token的第一部分和第二部分
        $payload = [
            //将["typ" => "JWT","alg" => "HS256"]数组转为字符串并进行base64加密形成token的header
            $this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->headers)),
            //其中的$this->claims为token的载荷数组转为字符串并进行base64加密形成token的payload,内容包括
            //iss: jwt签发者
            //sub: jwt所面向的用户 
            //exp: jwt的过期时间,这个过期时间必须要大于签发时间
            //nbf: 定义在什么时间之前,该jwt都是不可用的.
            //iat: jwt的签发时间
            //jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
            $this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->claims))
        ];
        //生成签名,使用hash_hmac($this->getAlgorithm(), $payload, $key->getContent(), true)生成签名,其中$this->getAlgorithm()为加密算法HS256,$payload为token的第一,二部分,$key为加密私钥
        $signature = $this->createSignature($payload, $signer, $key);
        if ($signature !== null) {
            //将签名进行base64加密返回
            $payload[] = $this->encoder->base64UrlEncode($signature);
        }
        return new Token($this->headers, $this->claims, $signature, $payload);
    }

查看解析 token 的代码

/**
     * Decode a JSON Web Token.
     *
     * @param  string  $token
     *
     * @throws \Tymon\JWTAuth\Exceptions\JWTException
     *
     * @return array
     */
    public function decode($token)
    {
        try {
            //解析token,将token分割为三部分头部,负载,签名。对数据进行base64解码并json_decode转为数组,若解析失败抛出异常
            $jwt = $this->parser->parse($token);
        } catch (Exception $e) {
            throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e);
        }
        //使用hash_equals内置函数对token解析的签名与hash_hmac($this->getAlgorithm(), $payload, $key->getContent(), true)比较是否相同,若相同则表示认证通过,不同则直接抛出异常
        if (! $jwt->verify($this->signer, $this->getVerificationKey())) {
            throw new TokenInvalidException('Token Signature could not be verified.');
        }
        //认证通过返回payload的内容
        return (new Collection($jwt->getClaims()))->map(function ($claim) {
            return is_object($claim) ? $claim->getValue() : $claim;
        })->toArray();
    }


总结:

1.以上就是生成和解析 token 的大致逻辑,其中加密的关键还是在于服务器生成的私钥,若私钥流失,客户端就可以自己根据逻辑生成 token。

2.payload 要防止存放敏感信息,因为该部分是客户端可解密的部分。

3.想自己造轮子也知道逻辑。

4.面试的时候问到也不虚了。


转:https://learnku.com/articles/54009


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
Laravel内核分析-设计模式之观察者模式
管理员 发表于 Laravel 中 2021-01-27 11:08:03 浏览量(73)

观察者模式描述:

Laravel 的 Event 事件系统提供了一个简单的观察者模式实现,能够订阅和监听应用中发生的各种事件,在 PHP 的标准库 (SPL) 里甚至提供了三个接口 SplSubject, SplObserver, SplObjectStorage 来让开发者更容易地实现观察者模式,不过我还是想脱离 SPL 提供的接口和特定编程语言来说一下如何通过面向对象程序设计来实现观察者模式,示例是 PHP 代码不过用其他面向对象语言实现起来也是一样的。


模式定义:

1.观察者模式 (Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。

2.观察者模式又叫做发布 - 订阅(Publish/Subscribe)模式、模型 - 视图(Model/View)模式、源 - 监听器(Source/Listener)模式或从属者(Dependents)模式。

3.观察者模式的核心在于 Subject 和 Observer 接口,Subject (主题目标) 包含一个给定的状态,观察者 “订阅” 这个主题,将主题的当前状态通知观察者,每次给定状态改变时所有观察者都会得到通知。

4.发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。


模式结构说明:

1.png

Subject  目标抽象类 

ConcreteSubject 具体目标 

Observer 观察者抽象类 

ConcreteObserver 具体观察者


应用举例:

比如在设置用户 (主题) 的状态后要分别发送当前的状态描述信息给用户的邮箱和手机,我们可以使用两个观察者订阅用户的状态,一旦设置状态后主题就会通知的订阅了状态改变的观察者,在两个观察者里面我们可以分别来实现发送邮件信息和短信信息的功能。

1.抽象目标类

abstract class Subject
{
    protected $stateNow;
    protected $observers = [];
    public function attach(Observer $observer)
    {
        array_push($this->observers, $observer);
    }
    public function detach(Observer $ob)
    {
        $pos = 0;
        foreach ($this->observers as $viewer) {
            if ($viewer == $ob) {
                array_splice($this->observers, $pos, 1);
            }
            $pos++;
        }
    }
    public function notify()
    {
        foreach ($this->observers as $viewer) {
            $viewer->update($this);
        }
    }
}

在抽象类中 attach detach 和 notify 都是具体方法,这些是继承才能使用的方法,将由 Subject 的子类使用。


2.具体目标类

class ConcreteSubject extends Subject
{
    public function setState($state) 
    {
        $this->stateNow = $state;
        $this->notify();
    }
    public function getState()
    {
        return $this->stateNow;
    }
}

3.抽象观察者

abstract class Observer
{
    abstract public function update(Subject $subject);
}

在抽象观察者中,抽象方法 update 等待子类为它提供一个特定的实现。


4.具体观察者

class ConcreteObserverDT extends Observer
{
    private $currentState;
    public function update(Subject $subject)
    {
        $this->currentState = $subject->getState();
        echo '<div style="font-size:10px;">'. $this->currentState .'</div>';
    }
}
class ConcreteObserverPhone extends Observer
{
    private $currentState;
    public function update(Subject $subject)
    {
        $this->currentState = $subject->getState();
        echo '<div style="font-size:20px;">'. $this->currentState .'</div>';
    }
}

在例子中为了理解起来简单,我们只是根据不同的客户端设置了不同的内容样式,实际应用中可以真正的调用邮件和短信服务来发送信息。


5.使用观察者模式

class Client 
{
    public function __construct()
    {
        $sub = new ConcreteSubject();
        $obDT = new ConcreteObserverDT();
        $obPhone = new ConcreteObserverPhone();
        $sub->attach($obDT);
        $sub->attach($obPhone);
        $sub->setState('Hello World');
        $sub->notify();
    }
}
$worker = new Client();


何时使用观察者模式:

1.一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。

2.一个对象必须通知其他对象,而并不知道这些对象是谁。

3.基于事件触发机制来解耦复杂逻辑时,从整个逻辑的不同关键点抽象出不同的事件,主流程只需要关心最核心的逻辑并能正确地触发事件 (Subject),其余相关功能实现由观察者或者叫订阅者来完成。


总结:

1.观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。

2.模式包含四个角色:目标又称为主题,它是指被观察的对象;具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;观察者将对观察目标的改变做出反应;在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。

3.观察者模式的主要优点在于可以实现表示层和数据逻辑层的分离,并在观察目标和观察者之间建立一个抽象的耦合,支持广播通信;其主要缺点在于如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间,而且如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。


转:https://learnku.com/docs/laravel-kernel/design-mode-observer-mode/6919


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
Laravel内核分析-设计模式之装饰模式
管理员 发表于 Laravel 中 2021-01-27 10:57:04 浏览量(61)

装饰模式描述:

装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能。通常有两种方式可以实现给一个类或对象增加行为:


1.继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。

2.组合机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器 (Decorator)。


显然,为了扩展对象功能频繁修改父类或者派生子类这种方式并不可取。在面向对象的设计中,我们应该尽量使用对象组合,而不是对象继承来扩展和复用功能。装饰器模式就是基于对象组合的方式,可以很灵活的给对象添加所需要的功能。装饰器模式的本质就是动态组合。动态是手段,组合才是目的。总之,装饰模式是通过把复杂的功能简单化,分散化,然后在运行期间,根据需要来动态组合的这样一个模式。


装饰模式定义:

装饰模式 (Decorator Pattern) :动态地给一个对象增加一些额外的职责 (Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器 (Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为 “油漆工模式”,它是一种对象结构型模式。


装饰模式的优点:

1.装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。

2.可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。

3.通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。


模式结构和说明:

1.png

1.聚合关系用一条带空心菱形箭头的直线表示,上图表示 Component 聚合到 Decorator 上,或者说 Decorator 由 Component 组成。

2.继承关系用一条带空心箭头的直接表示

3.看懂 UML 类图请看这个文档:https://designpatterns.readthedocs.io/zh_CN/latest/read_uml.html


Component:

组件对象的接口,可以给这些对象动态的添加职责;

ConcreteComponent:

具体的组件对象,实现了组件接口。该对象通常就是被装饰器装饰的原始对象,可以给这个对象添加职责;

Decorator:

所有装饰器的父类,需要定义一个与 Component 接口一致的接口 (主要是为了实现装饰器功能的复用,即具体的装饰器 A 可以装饰另外一个具体的装饰器 B,因为装饰器类也是一个 Component),并持有一个 Component 对象,该对象其实就是被装饰的对象。如果不继承 Component 接口类,则只能为某个组件添加单一的功能,即装饰器对象不能再装饰其他的装饰器对象。

ConcreteDecorator:

具体的装饰器类,实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。


装饰器的示例代码:

1.Component 抽象类,可以给这些对象动态的添加职责

abstract class Component
{
    abstract public function operation();
}

2.Component 的实现类

class ConcreteComponent extends Component
{
    public function operation()
    {
        echo __CLASS__ .  '|' . __METHOD__ . "\r\n";
    }
}

3. 装饰器的抽象类,维持一个指向组件对象的接口对象, 并定义一个与组件接口一致的接口

abstract class Decorator extends Component
{
    /**
     * 持有Component的对象
     */
    protected $component;
    /**
     * 构造方法传入
     */
    public function __construct(Component $component)
    {
        $this->component = $component;
    }
    abstract public function operation();
}


4. 装饰器的具体实现类,向组件对象添加职责,beforeOperation (),afterOperation () 为前后添加的职责。

class ConcreteDecoratorA extends Decorator
{
    //在调用父类的operation方法的前置操作
    public function beforeOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }
    //在调用父类的operation方法的后置操作
    public function afterOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }
    public function operation()
    {
        $this->beforeOperation();
        $this->component->operation();//这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能
        $this->afterOperation();
    }
}
class ConcreteDecoratorB extends Decorator
{
    //在调用父类的operation方法的前置操作
    public function beforeOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }
    //在调用父类的operation方法的后置操作
    public function afterOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }
    public function operation()
    {
        $this->beforeOperation();
        $this->component->operation();//这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能
        $this->afterOperation();
    }
}


5. 客户端使用装饰器

class Client
{
    public function main()
    {
        $component = new ConcreteComponent();
        $decoratorA = new ConcreteDecoratorA($component);
        $decoratorB = new ConcreteDecoratorB($decoratorA);
        $decoratorB->operation();
    }
}
$client = new Client();
$client->main();


6. 运行结果

oncreteDecoratorB|ConcreteDecoratorB::beforeOperation
ConcreteDecoratorA|ConcreteDecoratorA::beforeOperation
ConcreteComponent|ConcreteComponent::operation
ConcreteDecoratorA|ConcreteDecoratorA::afterOperation
ConcreteDecoratorB|ConcreteDecoratorB::afterOperation

装饰模式需要注意的问题:

1.一个装饰类的接口必须与被装饰类的接口保持相同,对于客户端来说无论是装饰之前的对象还是装饰之后的对象都可以一致对待。

2.尽量保持具体组件类 ConcreteComponent 的轻量,不要把主逻辑之外的辅助逻辑和状态放在具体组件类中,可以通过装饰类对其进行扩展。 如果只有一个具体组件类而没有抽象组件类,那么抽象装饰类可以作为具体组件类的直接子类。


适用环境:

1.需要在不影响组件对象的情况下,以动态、透明的方式给对象添加职责。

2.当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时可以考虑使用装饰类。


转:https://learnku.com/docs/laravel-kernel/design-mode-decoration-mode/6918


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
Laravel内核分析-设计模式之外观模式
管理员 发表于 Laravel 中 2021-01-27 10:40:55 浏览量(47)

外观模式描述:

外观模式 (Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。

Laravel 中我们常用到的 Route、Redis、Auth 这些 Facade 就是外观模式的具体实现, 在 Laravel 中设计了多个外观类,每个外观类继承自统一的抽象外观类,在抽象外观类里提供了通过外观类访问其背后子系统的基础方法。

对于新的业务需求,不要修改原有外观类,而应该增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改源代码并更换外观类的目的。 

下面是一个简单的外观模式的例子,并没有引入抽象外观类,在介绍 Laravel Facade 的文章中我们会看到 Laravel 里提供了一个抽象外观类从而让我们能够方便的根据需要增加新子系统的外观类,并让外观类能够正确代理到其对应的子系统 (或者叫服务)。


模式结构:

外观模式包含如下角色:

1.Facade 外观角色

2.SubSystem 子系统角色

1.png

代码示例

<?php
class Client
{
    public function main()
    {
        (new Facade)->operation();
    }
}
class Facade
{
    private $systemA;
    private $systemB;
    public function __construct()
    {
        $this->systemA = new SystemA;
        $this->systemB = new SystemB;
    }
    public function operation()
    {
        $this->systemA->operationA();
        $this->systemB->operationB();
    }
}
class SystemA
{
    public function operationA()
    {
        //
    }
}
class SystemB
{
    public function operationB()
    {
        //
    }
}

模式分析:

根据 “单一职责原则”,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性,一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入一个外观对象,它为子系统的访问提供了一个简单而单一的入口。 - 外观模式也是 “迪米特法则” 的体现,通过引入一个新的外观类可以降低原有系统的复杂度,同时降低客户类与子系统类的耦合度。 - 外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。 - 外观模式的目的在于降低系统的复杂程度。 - 外观模式从很大程度上提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。


外观模式的缺点:

不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。

在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了 “开闭原则”。


模式扩展:

1.一个系统有多个外观类

在外观模式中,通常只需要一个外观类,并且此外观类只有一个实例,换言之它是一个单例类。在很多情况下为了节约系统资源,一般将外观类设计为单例类。当然这并不意味着在整个系统里只能有一个外观类,在一个系统中可以设计多个外观类,每个外观类都负责和一些特定的子系统交互,向用户提供相应的业务功能。


2.不要试图通过外观类为子系统增加新行为

不要通过继承一个外观类在子系统中加入新的行为,这种做法是错误的。外观模式的用意是为子系统提供一个集中化和简化的沟通渠道,而不是向子系统加入新的行为,新的行为的增加应该通过修改原有子系统类或增加新的子系统类来实现,不能通过外观类来实现。


3.抽象外观类的引入

外观模式最大的缺点在于违背了 “开闭原则”,当增加新的子系统或者移除子系统时需要修改外观类,可以通过引入抽象外观类在一定程度上解决该问题,客户端针对抽象外观类进行编程。对于新的业务需求,不修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改源代码并更换外观类的目的。



总结:

1.在外观模式中,外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。


2.外观模式包含两个角色:外观角色是在客户端直接调用的角色,在外观角色中可以知道相关的 (一个或者多个) 子系统的功能和责任,它将所有从客户端发来的请求委派到相应的子系统去,传递给相应的子系统对象处理;在软件系统中可以同时有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能。


3.外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。


4.外观模式主要优点在于对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易,它实现了子系统与客户之间的松耦合关系,并降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程;其缺点在于不能很好地限制客户使用子系统类,而且在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了 “开闭原则”。


5.外观模式适用情况包括:要为一个复杂子系统提供一个简单接口;客户程序与多个子系统之间存在很大的依赖性;在层次化结构中,需要定义系统中每一层的入口,使得层与层之间不直接产生联系。


转:https://learnku.com/docs/laravel-kernel/design-mode-appearance-mode/6917

去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
分享Laravel中的10个很方便且比较冷门的用法
管理员 发表于 Laravel 中 2021-01-19 16:45:41 浏览量(84)

Laravel的这些用法,你用的上没?

1. 在 find 方法中指定属性

User::find(1, ['name', 'email']);
User::findOrFail(1, ['name', 'email']);

2. Clone 一个 Model

用 replicate 方法可以克隆一个 Model

$user = User::find(1);
$newUser = $user->replicate();
$newUser->save();

3. 判断两个 Model 是否相同

检查两个 Model 的 ID 是否相同用 is 方法

$user = User::find(1);
$sameUser = User::find(1);
$diffUser = User::find(2);
$user->is($sameUser); // true
$user->is($diffUser); // false;

4. 重新加载一个 Model

$user = User::find(1);
$user->name; // 'Peter'
// 如果 name 更新过,比如由 peter 更新为 John
$user->refresh();
$user->name; // John

5. 加载新的 Model

$user = App\User::first();
$user->name;    // John
//
$updatedUser = $user->fresh(); 
$updatedUser->name;  // Peter
$user->name;    // John

6. 更新带关联的 Model

在更新关联的时候,使用 push 方法可以更新所有 Model

class User extends Model
{
   public function phone()
   {
   return $this->hasOne('App\Phone');
   }
}

$user = User::first();
$user->name = "Peter";
$user->phone->number = '1234567890';
$user->save(); // 只更新 User Model
$user->push(); // 更新 User 和 Phone Model

7. 自定义软删除字段

Laravel 默认使用 deleted_at 作为软删除字段,我们通过以下方式将 deleted_at 改成 is_deleted


class User extends Model
{
use SoftDeletes;
  
* deleted_at 字段.
*
* @var string
*/
const DELETED_AT = 'is_deleted';
}

或者使用访问器

class User extends Model
{
   use SoftDeletes;
   public function getDeletedAtColumn(){
   return 'is_deleted';
   }
}

8. 查询 Model 更改的属性

$user = User::first();
$user->name; // John
$user->name = 'Peter';
$user->save();
dd($user->getChanges());
// 输出:
[
 'name' => 'John',
 'updated_at' => '...'
]

9. 查询 Model 是否已更改

$user = User::first();
$user->name;    // John
$user->isDirty();  // false 
$user->name = 'Peter'; 
$user->isDirty();  // true
$user->getDirty();  // ['name' => 'Peter']
$user->save();   
$user->isDirty();  // false

getChanges () 与 getDirty () 的区别

getChanges () 方法用在 save () 方法之后输出结果集

getDirty () 方法用在 save () 方法之前输出结果集


10. 查询修改前的 Model 信息

$user = App\User::first();
$user->name;     //John
$user->name = "Peter";   //Peter
$user->getOriginal('name'); //John
$user->getOriginal();   //Original $user record


转:https://learnku.com/articles/53700


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
centos7环境安装go语言环境+beego框架运行使用流程步骤
管理员 发表于 Go 中 2021-01-07 14:41:08 浏览量(100)

一.安装go环境

1.下载go语言安装包

wget https://dl.google.com/go/go1.15.6.linux-amd64.tar.gz

2.解压至/use/local/ 目录下

tar -C /usr/local/ -xzf go1.15.6.linux-amd64.tar.gz

3.创建及编辑配置文件 指定go的PATH路径

vim /etc/profile.d/go.sh
输入配置:
export PATH=$PATH:/usr/local/go/bin

4.让配置文件生效

source /etc/profile.d/go.sh

5.go环境就安装完了,看看效果

[root@iz8vbcrus31oj2axurgpjyz ~]# go version
go version go1.15.6 linux/amd64

二.安装beego框架

1.创建go项目目录/home/goblog/src/

2.设置配置文件

vim /etc/profile.d/go.sh

输入配置:

export GOROOT=/usr/local/go
export GOPATH=/home/goblog
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOROOT/bin:$GOBIN
export GO111MODULE="on"
export GOPROXY="https://goproxy.io"

3.让配置文件生效

source /etc/profile.d/go.sh

4.下载beego包及bee包

[root@iz8vbcrus31oj2axurgpjyz ~]# cd /home/goblog/
[root@iz8vbcrus31oj2axurgpjyz goblog]# go get github.com/astaxie/beego
go: downloading github.com/astaxie/beego v1.12.3
go: github.com/astaxie/beego upgrade => v1.12.3
...
[root@iz8vbcrus31oj2axurgpjyz goblog]# go get github.com/beego/bee
go: downloading github.com/beego/bee v1.12.3
go: github.com/beego/bee upgrade => v1.12.3
...

4.创建go项目,使用bee

[root@iz8vbcrus31oj2axurgpjyz ~]# cd $GOPATH/src
[root@iz8vbcrus31oj2axurgpjyz src]# ll
total 0
[root@iz8vbcrus31oj2axurgpjyz src]# bee new test
2021/01/07 13:46:31 INFO     ▶ 0001 Getting bee latest version...
2021/01/07 13:46:32 WARN     ▶ 0002 Update available 1.12.0 ==> 2.0.2
...
2021/01/07 13:46:32 SUCCESS  ▶ 0007 New application successfully created!

5.运行beego框架

[root@iz8vbcrus31oj2axurgpjyz src]# cd test/
[root@iz8vbcrus31oj2axurgpjyz test]# go run main.go
go: finding module for package github.com/shiena/ansicolor
go: downloading gopkg.in/yaml.v2 v2.2.1
go: downloading github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18
go: found github.com/shiena/ansicolor in github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18
2021/01/07 13:48:45.473 [I] [asm_amd64.s:1374]  http server Running on http://:8080

6.配置nginx , 创建配置文件:/use/local/nginx/conf/vhost/goblog.conf ,并添加一下配置信息

server {
    listen       80;
    server_name  go.zongscan.com;
    charset utf-8;
    access_log  /home/wwwlogs/go.log;
    location /(css|js|fonts|img)/ {
        access_log off;
        expires 1d;
        root "/gblog/static";
        try_files $uri @backend;
    }
    location / {
        try_files /_not_exists_ @backend;
    }
    location @backend {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host            $http_host;
        proxy_pass http://127.0.0.1:8080;
    }
}

7.添加解析,如果有购买域名直接用,如果没有的话就本地解析,添加hosts文件信息

windows7: C:\Windows\System32\drivers\etc 

ip地址   go.zongscan.com

8.访问一下

1.png

去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
go+beego框架开发博客,该博文就告一段落
管理员 发表于 Go 中 2021-01-06 14:30:31 浏览量(97)

前段时间go语言+beego框架从零开始开发博客网站,基本已完成

主要功能:首页列表,文章详情,注册登录,等,其他的因为近期有事,后续有时间在继续学习开发。

源码我已放码云,有需要的可以自行下载。

源码:

https://gitee.com/houtizong/goblog


学习交流群:271315602 (PHP/GO语言|Laravel/TP/Beego)

去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
go+beego框架开发博客首页功能流程步骤
管理员 发表于 Go 中 2020-12-17 11:28:26 浏览量(165)

上一篇自定义函数也是铺垫开发首页的知识点,像首尾分离,分页等,后续首页功能开发的必须知识点会在这文中写出来


1.首先 路由 首页不用说默认是/ 根目录

  在routers目录router.go文件中添加

//首页
beego.Router("/", &controllers.IndexController{})

2.添加首页控制器

  在controllers目录下创建index.go文件 

  都是orm操作,首页我就没写model了,主要是方便,还有一个分页

2.1 分页 (这个单独写一下,其他的我就粘贴全部首页控制器代码了)

  在models目录下创建pages.go文件 专门处理分页页数值

package models
import (
"math"
_ "github.com/go-sql-driver/mysql"
)
//分页方法,根据传递过来的页数,每页数,总数,返回分页的内容 7个页数 前 1,2,3,4,5 后 的格式返回,小于5页返回具体页数
func Paginator(page, prepage int, nums int) map[string]interface{} {
var firstpage int //前一页地址
var lastpage int  //后一页地址
//根据nums总数,和prepage每页数量 生成分页总数
totalpages := int(math.Ceil(float64(nums) / float64(prepage))) //page总数
if page > totalpages {
page = totalpages
}
if page = totalpages-5 && totalpages > 5: //最后5页
start := totalpages - 5 + 1
firstpage = page - 1
lastpage = int(math.Min(float64(totalpages), float64(page+1)))
pages = make([]int, 5)
for i, _ := range pages {
pages[i] = start + i
}
case page >= 3 && totalpages > 5:
start := page - 3 + 1
pages = make([]int, 5)
firstpage = page - 3
for i, _ := range pages {
pages[i] = start + i
}
firstpage = page - 1
lastpage = page + 1
default:
pages = make([]int, int(math.Min(5, float64(totalpages))))
for i, _ := range pages {
pages[i] = i + 1
}
firstpage = int(math.Max(float64(1), float64(page-1)))
lastpage = page + 1
}
paginatorMap := make(map[string]interface{})
paginatorMap["pages"] = pages
paginatorMap["totalpages"] = totalpages
paginatorMap["firstpage"] = firstpage
paginatorMap["lastpage"] = lastpage
paginatorMap["currpage"] = page
return paginatorMap
}

2.2 首页控制器全部代码

package controllers
import (
"fmt"
"gblog/models"
"strconv"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
)
type IndexController struct {
beego.Controller
}
func (c *IndexController) Get() {
c.Data["title"] = "Go编程_beego_常用PHP框架_前端后端分享交流-侯体宗的博客"
c.Data["keywords"] = "Go编程_beego_常用PHP框架_前端后端分享交流"
c.Data["description"] = "个人博客,侯体宗的博客,Go编程,beego框架,侯体宗的博客常用框架及前端后端分享交流"
o := orm.NewOrm()
//分类列表
var cats []orm.Params
catssql := `select cat.cat_id,cat.catname,count(art.cat_id) as num from cat inner join art on art.cat_id = cat.cat_id group by art.cat_id`
o.Raw(catssql).Values(&cats)
c.Data["cats"] = cats
//当前页码
page, err := c.GetInt("page")
if err != nil {
page = 1
println(err)
}
//文章总条数
var count []orm.Params
numssql := `select count(*) as count from art where is_state=0 and is_del=1 order by art_id desc`
o.Raw(numssql).Values(&count)
nums := count[0]["count"]
// 这里先显示转换,.(string) 把interface转换成string类型,再利用strconv.Atoi把string 转换成int
cnt, _ := strconv.Atoi(nums.(string))
//当前页 每页条数 总条数
var pageSize int
pageSize = 30
pages := models.Paginator(page, pageSize, cnt)
c.Data["pages"] = pages
//文章列表 分页数据
var arts []orm.Params
artssql := `select art_id,title,content,pubtime,view,cat_id from art where is_state=0 and is_del=1 order by art_id desc limit ?,?`
o.Raw(artssql, (page-1)*pageSize, pageSize).Values(&arts)
c.Data["arts"] = arts
//文章归档 格式:2017年01月
var artdates []orm.Params
artdatessql := `select pubtime,FROM_UNIXTIME( pubtime,'%Y-%m') as time ,count(*) as num FROM art where  is_state=0 and is_del=1 group by time`
o.Raw(artdatessql).Values(&artdates)
c.Data["artdates"] = artdates
//标签云
var tags []orm.Params
tagssql := `select tag_id,tagname FROM tag group by tagname order by tag_id desc limit 50`
o.Raw(tagssql).Values(&tags)
c.Data["tags"] = tags
fmt.Printf("%v", pages)
//c.Data["json"] = tags
//c.ServeJSON()
c.TplName = "index.html"
}

3.view  这包括首尾分离模板页面 官网介绍两种方式 一种是layout 还有就是下面这种

3.1 首尾分离模板页面 在views目录下创建pubcli目录 并在底下创建header/footer.html文件

{{template "public/header.html" .}}
中间内容 不要问为什么用这种 一个字熟 两个字习惯
{{template "public/footer.html" .}}

3.2 中间html 就是控制器渲染的 c.TplName = "index.html"

在views目录下创建index.html文件 全部代码我就不贴了,只贴分页列表段代码

{{template "public/header.html" .}}

<div class="row clearfix">
   <div class="col-md-8 column">
       <ul class="nav nav-tabs">
           <li class="active"><a href="/">最新</a></li>
           <li class=""><a href="?q=hot">最热</a></li>
           <li class="disabled"><a href="#">问答</a></li>
           <li class="dropdown pull-right">
               <a href="#" data-toggle="dropdown" class="dropdown-toggle">下拉<strong class="caret"></strong></a>
               <ul class="dropdown-menu">
                   <li><a href="#">操作</a></li>
                   <li><a href="#">设置栏目</a></li>
                   <li><a href="#">更多设置</a></li>
                   <li class="divider"></li>
                   <li><a href="#">分割线</a></li>
               </ul>
           </li>
       </ul>
       <div class="list-group">
           {{range $ind, $elem := .arts}}
           <a href="/art/390" class="list-group-item"><span style="float:right;color:#999;"><i class="icon ion-eye"></i> {{$elem.view}} | <small>{{FormatDate $elem.pubtime}}</small></span>{{$elem.title}}</a>
           {{end}}
       </div>
       <ul class="pagination">
           <li><a href="?page={{.pages.firstpage}}" class="btn btn-large"> 上一页 </a></li>
           <li><a href="">{{.pages.currpage}}</a></li>
           <li><a href="?page={{.pages.lastpage}}"> 下一页 </a></li>
           <li><a href="?page={{.pages.totalpages}}"> 尾页 </a></li>
       </ul>
   </div>
   <div class="col-md-4 column">
   <p class="h-block">
   <strong>该博客于2020-9-28日,后端基于laravel8框架开发<br>前端页面使用Bootstrap可视化布局系统自动生成</strong><br>
   <small>是我仿的原来我的TP5框架写的博客,比较粗糙,底下是入口</small><br>
   <a href="https://www.zongscan.com/">侯体宗的博客</a>
</p>


{{template "public/footer.html" .}}


首页基本展示完,效果就不贴图了,就是本网站的首页布局基本一样

后续就写登录功能的session/cookie的持久化


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
go+beego自定义公共函数编写及实现根据时间戳计算与当前时间的间距及格式化单位
管理员 发表于 Go 中 2020-12-16 17:42:58 浏览量(148)

学习go已经有好几天了,准备把我的博客网站用go语言的beego框架重构一下,首页基本已经出来,后面在慢慢写步骤过程,

今天就在用写首页的时候需要用到的自定义公共函数功能,我模板调用需要,所以记录一下


1.创建公共函数

公共函数也算是工具,新建文件夹utils,并在该文件夹下创建functions.go文件

//公共函数
package utils
import (
"math"
"sort"
"strconv"
"time"
"github.com/astaxie/beego"
)
func Init() {
beego.AddFuncMap("FormatDate", FormatDate) //模板中使用{{FormatDate $out}}或{{$out|FormatDate}}
//有新的函数就依次加入
}
//根据时间戳计算与当前时间的间距及格式化单位
func FormatDate(in string) (out string) {
timeUnix := time.Now().Unix() //当前时间戳
inn, _ := strconv.ParseInt(in, 10, 64)
outt := timeUnix - inn
f := map[string]string{
"1":        "秒",
"60":       "分钟",
"3600":     "小时",
"86400":    "天",
"604800":   "星期",
"2592000":  "个月",
"31536000": "年",
}
var keys []string
for k := range f {
keys = append(keys, k)
}
//sort.Strings(keys)
//数字字符串 排序
sort.Slice(keys, func(i, j int) bool {
numA, _ := strconv.Atoi(keys[i])
numB, _ := strconv.Atoi(keys[j])
return numA < numB
})
for _, k := range keys {
v2, _ := strconv.Atoi(k)
cc := math.Floor(float64(int(outt) / int(v2)))
if 0 != cc {
out = strconv.FormatFloat(cc, 'f', -1, 64) + f[k] + "前"
}
}
return
}

2.入口文件main.go中初始化公共函数文件


import (
	"gblog/utils"
)
func init() {
	utils.Init() //注册函数
	beego.BConfig.WebConfig.Session.SessionOn = true 
} 

3.view中使用 (用来格式化我的文章时间戳)

在我的views目录下的index.html文件里调用:{{FormatDate $elem.pubtime}}

<div class="list-group">
    {{range $ind, $elem := .arts}}
    <a href="/art/390" class="list-group-item">
<span style="float:right;color:#999;">
<i class="icon ion-eye"></i> {{$elem.view}} | 
<small>{{FormatDate $elem.pubtime}}</small></span>
{{$elem.title}}
</a>
    {{end}}
</div>

4.看看效果

1.png


ps: 都是利用下班时间,所以更新慢一点,

      后面跟着写首页控制器,模板,布局收尾分离,分页,等文章


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
beego框架中模型的创建及orm数据库操作并实现注册_登录_查询列表功能
管理员 发表于 Go 中 2020-12-11 14:06:02 浏览量(149)

学习go语言beego框架,话不多说,接着上一篇博文往下看

实现功能:model添加,注册/登录/查询列表等功能


1.mode创建 我这里以users表为例

在models目录下创建user.go文件作为user表的模型:

package models
import (
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
)
//全局连接实例
var (
db orm.Ormer
)
//由于model这个名字叫 Users 那么操作的表其实 users
type Users struct {
Id         int64
Name       string
Email      string
Password   string
Created_at string
}
//实例
func init() {
orm.Debug = true // 是否开启调试模式 调试模式下会打印出sql语句
orm.RegisterDataBase("default", "mysql", "root:Cbb_0721!@#$@tcp(192.168.1.140:3306)/laravel?charset=utf8", 30)
orm.RegisterModel(new(Users))
db = orm.NewOrm()
}
//方法 添加
func AddUser(users *Users) (int64, error) {
id, err := db.Insert(users)
return id, err
}


2.注册、登录、查询列表路由

在routers目录的router.go文件添加

package routers
import (
"gblog/controllers"
"github.com/astaxie/beego"
)
func init() {
beego.Router("/", &controllers.MainController{})
beego.Router("/user", &controllers.UserController{}, "get:Get")
beego.Router("/reg", &controllers.UserController{}, "get:Reg")
beego.Router("/register", &controllers.UserController{}, "post:Register")
beego.Router("/login", &controllers.UserController{}, "get:Login")
beego.Router("/info", &controllers.UserController{}, "post:Info")
beego.Router("/infolist", &controllers.UserController{}, "get:InfoList")
beego.BConfig.EnableGzip = true
beego.BConfig.RouterCaseSensitive = true
beego.BConfig.MaxMemory = 1 << 26
beego.BConfig.WebConfig.AutoRender = true
beego.BConfig.CopyRequestBody = true
}

3.注册/登录模板的创建,就是html/js/css代码

在view目录下创建register.html、login.html模板文件

register.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>
<h1>注册页面</h1>
<form method="post" action="/register">
    用户名:<input type="text" name="name"> <p></p>
    &nbsp &nbsp邮箱:<input type="text" name="email"> <p></p>
    &nbsp &nbsp密码:<input type="password" name="password"><p></p>
    <input type="submit" name="注册" style="margin-left: 190px;">
</form>
</body>
</html>

login.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<h1>登陆界面</h1>
<form method="post" action="/info">
    用户名:<input type="text" name="name"><p></p>
    &nbsp &nbsp密码:<input type="password" name="password"><p></p>
    <input type="submit" value="登陆" style="margin-left: 190px;">
</form>
</body>
</html>

4.注册/登录/查询列表 功能 (注意:功能我就不分开写了,里面都有备注,有其他框架基础的话看一眼就会觉得有点熟悉)

在controllers目录创建user.go文件作为user控制器

package controllers

import (
"gblog/models"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
)

// UserController  is a user control
type UserController struct {
beego.Controller
}

//注册页模板
func (c *UserController) Reg() {
c.TplName = "register.html" //只有加了这句,前端才会跳转到注册页面
}

//注册用户  模型添加数据
func (c *UserController) Register() {
//1.拿到数据
name := c.GetString("name")
email := c.GetString("email")
password := c.GetString("password")
//2.对数据进行校验
if name == "" && email == "" && password == "" {
beego.Info("数据不能为空")
c.Redirect("/reg", 302) //重定向函数,如果发生错误页面重新回到注册页,并返回错误码302
return
}
//24小时制
timeObj := time.Now()
var str = timeObj.Format("2006-01-02 15:04:05")
user := models.Users{Name: name, Email: email, Password: password, Created_at: str}
models.AddUser(&user)
c.Ctx.WriteString("注册成功!")
}

//登录页模板
func (c *UserController) Login() {
c.TplName = "login.html"
}

//单条数据查询
func (c *UserController) Info() {
//获取get值
name := c.GetString("name")
password := c.GetString("password")
//2.判断数据是否合法
if name == "" || password == "" {
beego.Info("输入数据不合法")
c.TplName = "login.html"
return
}
//3.查询账号密码是否正确
o := orm.NewOrm()
user := models.Users{}
user.Name = name
user.Password = password
err := o.Read(&user, "Name", "Password")
if err != nil {
beego.Info("查询失败")
c.TplName = "login.html"
return
}
//4.跳转
c.Ctx.WriteString("登陆成功,老铁欢迎您")
c.Data["json"] = user
c.ServeJSON()
c.StopRun()
}
//使用原生sql进行列表数据查询
func (c *UserController) InfoList() {
o := orm.NewOrm()
var maps []orm.Params
sql := `select * from users`
o.Raw(sql).Values(&maps)
c.Data["json"] = maps
c.ServeJSON()
}

5.最后看看效果

注册:

1.png

2.png

3.png

登录:

4.png

5.png

查询列表: (就的数据集 这个就不截图了有点长)

url:http://127.0.0.1:8080/infolist

[
  {
    "created_at": "2020-09-25 03:37:38",
    "current_team_id": null,
    "email": "514224527@qq.com",
    "email_verified_at": null,
    "id": "1",
    "name": "admin",
    "password": "$2y$10$QKXE9t9l65cb3Vztes6S8eh/7uCG4jEigt8/MAKbgHEl8sLrxPfwC",
    "profile_photo_path": "profile-photos/726sxzkRKwn1lszPOApcL4gYrRTFlhC39MEc1Ku6.gif",
    "remember_token": null,
    "subscribe": "1",
    "two_factor_recovery_codes": null,
    "two_factor_secret": null,
    "updated_at": "2020-09-30 02:15:43"
  },
  {
    "created_at": "2020-09-30 06:00:25",
    "current_team_id": null,
    "email": "51422452@qq.com",
    "email_verified_at": null,
    "id": "2",
    "name": "站长小号",
    "password": "$2y$10$jkNk1ZD5THGCP5vmOcWd.e2rVqADywFAOgCgAJRdnBS4TZQC94jBe",
    "profile_photo_path": null,
    "remember_token": null,
    "subscribe": "0",
    "two_factor_recovery_codes": null,
    "two_factor_secret": null,
    "updated_at": "2020-09-30 06:00:25"
  },
  {
    "created_at": "2020-09-30 06:00:25",
    "current_team_id": null,
    "email": "5142245@qq.com",
    "email_verified_at": null,
    "id": "3",
    "name": "站长二号",
    "password": "$2y$10$jkNk1ZD5THGCP5vmOcWd.e2rVqADywFAOgCgAJRdnBS4TZQC94jBe",
    "profile_photo_path": null,
    "remember_token": null,
    "subscribe": "0",
    "two_factor_recovery_codes": null,
    "two_factor_secret": null,
    "updated_at": "2020-09-30 06:00:25"
  },
  {
    "created_at": "2020-12-11 13:52:18",
    "current_team_id": null,
    "email": "test@qq.com",
    "email_verified_at": null,
    "id": "7",
    "name": "测试",
    "password": "123456",
    "profile_photo_path": null,
    "remember_token": null,
    "subscribe": "0",
    "two_factor_recovery_codes": null,
    "two_factor_secret": null,
    "updated_at": null
  }
]


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
beego框架中控制器的创建及路由设置
管理员 发表于 Go 中 2020-12-09 14:39:08 浏览量(188)

看着框架的目录结构,熟悉的单词controllers|models|views,说明beego是一个典型的MVC架构,入口文件就是main.go

ps:感觉有点熟悉,是否想到了什么比如:laravel、tp

D:\goblog\src\gblog>dir
 驱动器 D 中的卷是 软件
 卷的序列号是 0008-8176
 D:\goblog\src\gblog 的目录
2020\12\09 周三  13:58    <DIR>          .
2020\12\09 周三  13:58    <DIR>          ..
2020\12\07 周一  17:31    <DIR>          conf
2020\12\08 周二  17:36    <DIR>          controllers
2020\12\09 周三  13:58        13,679,616 gblog.exe
2020\12\07 周一  17:32               195 go.mod
2020\12\07 周一  17:32             5,732 go.sum
2020\12\07 周一  17:31               103 main.go
2020\12\07 周一  17:31    <DIR>          models
2020\12\07 周一  17:31    <DIR>          routers
2020\12\07 周一  17:31    <DIR>          static
2020\12\07 周一  17:31    <DIR>          tests
2020\12\07 周一  17:31    <DIR>          views
               4 个文件     13,685,646 字节
               9 个目录 116,609,089,536 可用字节


这是我刚新下载的beego框架,可以接上一篇博文,进入正题:

从创建控制器并在里面添加方法及设置路由并在浏览器上访问它,这样的流程给它跑一遍。


1.创建控制器,在controllers目录添加控制器文件test.go

package controllers

import (
"github.com/astaxie/beego"
)

// TestController  is a test control
type TestController struct {
beego.Controller
}

func (c *TestController) Test() {
c.Ctx.WriteString(`我是test控制器中的test方法`)
}

2.设置路由在routers目录中router.go文件

package routers

import (
"gblog/controllers"
"github.com/astaxie/beego"
)

func init() {
beego.Router("/", &controllers.MainController{})
/*测试控制器路由*/
beego.Router("/test", &controllers.TestController{}, "get:Test")
beego.BConfig.EnableGzip = true
beego.BConfig.RouterCaseSensitive = true
beego.BConfig.MaxMemory = 1 << 26
beego.BConfig.WebConfig.AutoRender = true
beego.BConfig.CopyRequestBody = true
}

3.重新编译重启服务,我这是在LiteIDE编辑器中直接对入口文件Ctrl+r,看看效果

1.png

去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
windows7+go语言+Beego框架安装使用
管理员 发表于 Go 中 2020-12-07 18:23:47 浏览量(145)

学习go语言,不管三七二十一先找个star高的框架体验一下


1.beego安装

接上一篇文章,git啥都要装这我就不说了,我go环境已经装好,1.15.5

go get github.com/astaxie/beego //git命令下载beego
go get github.com/beego/bee     //git命令下载bee工具


GO111MODULE=on //开启

env -w GO111MODULE=on


把GOPATH的bin目录添加到环境变量中

C:\Users\Administrator>bee
Bee is a Fast and Flexible tool for managing your Beego Web Application.
USAGE
    bee command [arguments]
AVAILABLE COMMANDS
    version     Prints the current Bee version
    migrate     Runs database migrations
    api         Creates a Beego API application
    bale        Transforms non-Go files to Go source files
    fix         Fixes your application by making it compatible with newer versio
ns of Beego
    pro         Source code generator
    dlv         Start a debugging session using Delve
    dockerize   Generates a Dockerfile for your Beego application
    generate    Source code generator
    hprose      Creates an RPC application based on Hprose and Beego frameworks
    new         Creates a Beego application
    pack        Compresses a Beego application into a single file
    rs          Run customized scripts
    run         Run the application by starting a local development server
    server      serving static content over HTTP on port
    update      Update Bee
Use bee help [command] for more information about a command.
ADDITIONAL HELP TOPICS
Use bee help [topic] for more information about that topic.

bee的一些介绍

new 命令:

new命令创建一个Web项目,我们在命令下执行bee new <项目名>就可以创建一个新的项目。

api命令:

上面的new命令是用来创建新Web项目,不过很多用户使用beego来开发API应用,所以api命令是用来创建API应用


2.执行命令创建项目 进入D:\goblog\src>目录

D:\goblog\src\gblog>dir
 驱动器 D 中的卷是 软件
 卷的序列号是 0008-8176
 D:\goblog\src\gblog 的目录
2020\12\07 周一  18:00    <DIR>          .
2020\12\07 周一  18:00    <DIR>          ..
2020\12\07 周一  17:31    <DIR>          conf
2020\12\07 周一  17:31    <DIR>          controllers
2020\12\07 周一  18:00        13,658,624 gblog.exe
2020\12\07 周一  17:32               195 go.mod
2020\12\07 周一  17:32             5,732 go.sum
2020\12\07 周一  17:31               103 main.go
2020\12\07 周一  17:31    <DIR>          models
2020\12\07 周一  17:31    <DIR>          routers
2020\12\07 周一  17:31    <DIR>          static
2020\12\07 周一  17:31    <DIR>          tests
2020\12\07 周一  17:31    <DIR>          views
               4 个文件     13,664,654 字节
               9 个目录 116,428,435,456 可用字节


3.运行 我这里直接用ide 按Ctrl+r运行  看效果咯

1.png

浏览器:

2.png

去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
windows7+go语言环境安装
管理员 发表于 Go 中 2020-12-07 18:05:38 浏览量(168)

开始学习go语言,环境安装第一篇

1.准备安装包

1.go1.15.5.windows-amd64.msi (自行下载或者到网站底部QQ群文件里面下载)

2.ide,我这用liteide (同一一样)


2.安装完之后的查看


查看go版本

D:\>go version
go version go1.15.5 windows/amd64


查看go配置

D:\>go env
set GO111MODULE=on
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\Administrator\AppData\Local\go-build
set GOENV=C:\Users\Administrator\AppData\Roaming\go\env
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=D:\goblog\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=D:\goblog
set GOPRIVATE=
set GOPROXY=https://mirrors.aliyun.com/goproxy/
set GOROOT=D:\Go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=D:\Go\pkg\tool\windows_amd64
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=NUL
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessag
e-length=0 -fdebug-prefix-map=C:\Users\ADMINI~1\AppData\Local\Temp\go-build75744
9770=/tmp/go-build -gno-record-gcc-switches

所以在安装完Go之后,我们最好还是检查一些所有的环境变量是否正常。主要的环境变量有以下几个:

GOROOT:Go的安装目录

GOPATH:用于存放Go语言Package的目录,这个目录不能在Go的安装目录中

GOBIN:Go二进制文件存放目录,写成%GOROOT%\bin就好

GOOS:操作系统

GOARCH:指定系统环境,i386表示x86,amd64表示x64

PATH:需要将%GOBIN%加在PATH变量的最后,方便在命令行下运行Go


3.搭建完开发环境之后,接着使用LiteIDE编辑器来运行go,

1.打开应用程序,“查看”-->"编辑当前环境",确认GOROOT变量与GO安装路径一致;

2.“查看”-->"管理GOPATH"(默认的就是安装go的时候,随意指定的那个路径);

3.新建小项目,选择上一步添加的GOPATH,模板为Go1 Command Project,自己写上名称,点击OK-->Yes


完事了


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
windows7+laravel8+Elasticsearch7.9.3环境搭建步骤及使用
管理员 发表于 Laravel 中 2020-11-27 16:54:39 浏览量(202)

因为要用Elasticsearch测试一下数据,所以在windows7电脑上搭建一回,顺便记录一下增加一篇博文

开始准备必要条件:

1.elasticsearch-7.9.3-windows-x86_64.zip,我在官网下载最新的,几k每秒捶胸口,最后在是在群里问群友要的;

2.laravel8,已经装好,phpStudy环境已跑起,用的是博客laravel8仿栏目的环境,有兴趣的看一下我之前的博文有记录


一.把Elasticsearch跑起来,解压直接运行文件:D:\elasticsearch-7.9.3\bin\elasticsearch.bat

cmd.exe

...
}, term: 5, version: 51, reason: Publication{term=5, version=51}
[2020-11-27T13:51:08,003][INFO ][o.e.h.AbstractHttpServerTransport] [WD2VNW3O8RK
H2QI] publish_address {127.0.0.1:9200}, bound_addresses {127.0.0.1:9200}, {[::1]
:9200}
[2020-11-27T13:51:08,004][INFO ][o.e.n.Node               ] [WD2VNW3O8RKH2QI] st
arted
[2020-11-27T13:51:08,155][INFO ][o.e.l.LicenseService     ] [WD2VNW3O8RKH2QI] li
cense [09bedffd-1635-497c-889b-0efb6071476b] mode [basic] - valid
[2020-11-27T13:51:08,158][INFO ][o.e.x.s.s.SecurityStatusChangeListener] [WD2VNW
3O8RKH2QI] Active license is now [BASIC]; Security is disabled
[2020-11-27T13:51:08,167][INFO ][o.e.g.GatewayService     ] [WD2VNW3O8RKH2QI] re
covered [2] indices into cluster_state
[2020-11-27T13:51:09,216][INFO ][o.e.c.r.a.AllocationService] [WD2VNW3O8RKH2QI]
Cluster health status changed from [RED] to [YELLOW] (reason: [shards started [[
name][0]]]).

启动完:

1.png

二.安装elasticsearch扩展,我这直接安装最新的,注意composer的时候记得转阿里云镜像源,不然要卡死

composer require elasticsearch/elasticsearch

三.laravel8中配置文件 .env、config/database.php 

1. .env 在底部添加

ES_HOSTS=127.0.0.1:9200

2. config/database.php 也在底部添加

'elasticsearch' => [
    // Elasticsearch 支持多台服务器负载均衡,因此这里是一个数组
    'hosts' => explode(',', env('ES_HOSTS')),
]

四.将elasticsearch对象注入到laravel容器中 在laravel项目文件

App\Provider\AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema; //add fixed sql
use Elasticsearch\ClientBuilder as ESClientBuilder;
class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     * @return void
     */
    public function register()
    {
        // 注册一个名为 es 的单例
        $this->app->singleton('es', function () {
            // 从配置文件读取 Elasticsearch 服务器列表
            $builder = ESClientBuilder::create()->setHosts(config('database.elasticsearch.hosts'));
            // 如果是开发环境
            if (app()->environment() === 'local') {
                // 配置日志,Elasticsearch 的请求和返回数据将打印到日志文件中,方便我们调试
                $builder->setLogger(app('log')->driver());
            }
            return $builder->build();
        });
    }
    /**
     * Bootstrap any application services.
     * @return void
     */
    public function boot()
    {
        //
        Schema::defaultStringLength(191); //add fixed sql
    }
}

五.laravel项目中使用elasticsearch

1.routes\api.php文件中添加  (路由功能描述在控制器里面)

//测试路由 Elasticsearch
use App\Http\Controllers\API\TestController;
Route::get('test', [TestController::class, 'test']);
Route::get('testinsert', [TestController::class, 'testinsert']);
Route::get('testmysqllist', [TestController::class, 'testmysqllist']);

2.添加测试控制器文件 app\Http\Constrollers\API\TestController.php

<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use Elasticsearch\ClientBuilder;
class TestController extends Controller
{
    public $client = null;
    public function __construct() {
        $this-> client = ClientBuilder::create()->build();
    }
    
    //根据索引名(表名),跟id取出数据
    public function test()
    {
        $a = app('es')->get(['index' => 'name', 'id' => 2]);
        dd($a);
    }
    //从数据库表中取出数据 对应库表 存入Elasticsearch中(当然我这是测试就没有对应库表)
    public function testinsert()
    {
        $id = 2;
        $info = User::where(['id' => 2])->first()->toArray();
        $params = [
            'index' => 'name',
            'type' => 'title',
            'id' => $id,
            'body' => ['testField' => $info]
        ];
        $response = $this->client->index($params);
        dd($response);
    }
    //打印Elasticsearch中所有索引数据
    public function testmysqllist()
    {
        $client = ClientBuilder::create()->build();
        $data = [
            'type' => 'title'
        ];
        $response = $client->search($data);
        dd($response['hits']['hits']);
        return $response['hits']['hits'];
    }
}

3.看看 testmysqllist方法的 效果

array:4 [▼
  0 => array:5 [▼
    "_index" => "name"
    "_type" => "title"
    "_id" => "3"
    "_score" => 1.0
    "_source" => array:1 [▼
      "testField" => array:10 [▼
        "id" => 3
        "name" => "站长二号"
        "email" => "5142245@qq.com"
        "email_verified_at" => null
        "current_team_id" => null
        "profile_photo_path" => null
        "created_at" => "2020-09-30T06:00:25.000000Z"
        "updated_at" => "2020-09-30T06:00:25.000000Z"
        "subscribe" => 0
        "profile_photo_url" => "https://ui-avatars.com/api/?name=%E7%AB%99%E9%95%BF%E4%BA%8C%E5%8F%B7&color=7F9CF5&background=EBF4FF"
      ]
    ]
  ]
  1 => array:5 [▼
    "_index" => "name"
    "_type" => "title"
    "_id" => "2"
    "_score" => 1.0
    "_source" => array:1 [▼
      "testField" => array:10 [▼
        "id" => 2
        "name" => "站长小号"
        "email" => "houtizong@dingtalk.com"
        "email_verified_at" => null
        "current_team_id" => null
        "profile_photo_path" => null
        "created_at" => "2020-09-30T06:00:25.000000Z"
        "updated_at" => "2020-09-30T06:00:25.000000Z"
        "subscribe" => 1
        "profile_photo_url" => "https://ui-avatars.com/api/?name=%E7%AB%99%E9%95%BF%E5%B0%8F%E5%8F%B7&color=7F9CF5&background=EBF4FF"
      ]
    ]
  ]
  2 => array:5 [▼
    "_index" => "name1"
    "_type" => "title"
    "_id" => "1"
    "_score" => 1.0
    "_source" => array:1 [▶]
  ]
]

六.cmd.exe中测试

D:\phpStudy\WWW\larabg>php artisan tinker
Psy Shell v0.10.4 (PHP 7.3.22 — cli) by Justin Hileman
>>> app('es')->info();
=> [
     "name" => "WD2VNW3O8RKH2QI",
     "cluster_name" => "elasticsearch",
     "cluster_uuid" => "dupYVfwPSPiDMFoDg3fWlg",
     "version" => [
       "number" => "7.9.3",
       "build_flavor" => "default",
       "build_type" => "zip",
       "build_hash" => "c4138e51121ef06a6404866cddc601906fe5c868",
       "build_date" => "2020-10-16T10:36:16.141335Z",
       "build_snapshot" => false,
       "lucene_version" => "8.6.2",
       "minimum_wire_compatibility_version" => "6.8.0",
       "minimum_index_compatibility_version" => "6.0.0-beta1",
     ],
     "tagline" => "You Know, for Search",
   ]
>>> exit;
Exit:  Goodbye
>>> app('es')->get(['index' => 'name', 'id' => 1])
Elasticsearch/Common/Exceptions/Missing404Exception with message '{"_index":"nam
e","_type":"_doc","_id":"1","found":false}'
>>> app('es')->get(['index' => 'name', 'id' => 2])
=> [
     "_index" => "name",
     "_type" => "_doc",
     "_id" => "2",
     "_version" => 2,
     "_seq_no" => 9,
     "_primary_term" => 2,
     "found" => true,
     "_source" => [
       "testField" => [
         "id" => 2,
         "name" => "站长小号",
         "email" => "houtizong@dingtalk.com",
         "email_verified_at" => null,
         "current_team_id" => null,
         "profile_photo_path" => null,
         "created_at" => "2020-09-30T06:00:25.000000Z",
         "updated_at" => "2020-09-30T06:00:25.000000Z",
         "subscribe" => 1,
         "profile_photo_url" => "https://ui-avatars.com/api/?name=%E7%AB%99%E9%9
5%BF%E5%B0%8F%E5%8F%B7&color=7F9CF5&background=EBF4FF",
       ],
     ],
   ]


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
laravel运行原理之底层源码浅析
管理员 发表于 Laravel 中 2020-11-25 17:37:20 浏览量(174)

一、运行原理描述

laravel的入口文件 index.php

1、引入自动加载 autoload.php

2、创建应用实例,并同时完成了:

基本绑定($this、容器类Container等等)、
基本服务提供者的注册(Event、log、routing)、
核心类别名的注册(比如db、auth、config、router等)

3、开始Http请求的处理

make方法从容器中解析指定的值为实际的类,比如$app->make(Illuminate\Contracts\Http\Kernel::class) 解析出 App\Http\Http.php
handle方法对http请求进行处理
实际上是handle中的sendRequestThroughRouter处理的http请求
首先,将request绑定到共享实例
然后执行bootstarp方法,运行给定的引导类数组$bootstrappers,这里很关键,包括了加载配置文件、环境变量、服务提供者(config/app.php中的providers)、门面、异常处理、引导提供者
之后,进入管道模式,经过中间件的处理过滤后,再进行用户请求的分发
在请求分发时,首先,查找与给定请求匹配的路由,然后执行runRoute方法,实际处理请求的是runRoute 方法中的runRouteWithinStack
然后,经过runRouteWithinStack中的run方法,将请求分配到实际的控制器中,并得到响应结果

4、将处理结果返回

 

二、详细源码分析

1、注册自动加载器,实现文件的自动加载

require __dir__.'/../vendor/autoload.php';

2、创建应用容器实例Application(该实例继承自容器类Container),并绑定核心(web、命令行、异常),以便在需要时解析它们

$app = require_once __DIR__.'/../bootstrap/app.php';
#app.php
<?php
// 创建Laravel实例 【3】
$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
// 绑定Web端kernel
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
// 绑定命令行kernel
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);
// 绑定异常处理kernel
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);
// 返回应用实例
return $app;

3、在创建应用实例(Application.php)的构造函数中,将基本绑定注册到容器中,并注册了所有的基本服务提供者,以及在容器中注册核心类别名

Application.php
public function __construct($basePath = null)
{   
    // 将基本绑定注册到容器中【3.1】
    $this->registerBaseBindings();
    // 注册所有基本服务提供者【3.2】
    $this->registerBaseServiceProviders();
    // 在容器中注册核心类别名【3.3】
    $this->registerCoreContainerAliases();
}

 3.1、将基本绑定注册到容器中

 static::setInstance($this);
 $this->instance('app', $this);
 $this->instance(Container::class, $this);
 $this->singleton(Mix::class);
 $this->instance(PackageManifest::class, new PackageManifest(
     new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
));
# 注:instance方法为将...注册为共享实例,singleton方法为将...注册为共享绑定

 3.2、注册所有基本服务提供者(事件、日志、路由)

protected function registerBaseServiceProviders()
{
    $this->register(new EventServiceProvider($this));
    $this->register(new LogServiceProvider($this));
    $this->register(new RoutingServiceProvider($this));
}

 3.3、在容器中注册核心类别名

4、上面完成了类的自动加载、服务提供者注册、核心类的绑定、以及基本注册的绑定

5、开始解析http请求

index.php
// 5.1
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
// 5.2
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

    5.1 make方法是从容器解析给定值

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
中的Illuminate\Contracts\Http\Kernel::class 是在index.php 中的$app = require_once __DIR__.'/../bootstrap/app.php';这里面进行绑定的,实际指向的就是App\Http\Kernel::class这个类

    5.2 这里对http请求进行处理


$response = $kernel->handle(

    $request = Illuminate\Http\Request::capture()

);

进入$kernel所代表的类App\Http\Kernel.php中,我们可以看到其实里面只是定义了一些中间件相关的内容,并没有handle方法

<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
    ];
    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];
    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];
    /**
     * The priority-sorted list of middleware.
     *
     * This forces the listed middleware to always be in the given order.
     *
     * @var array
     */
    protected $middlewarePriority = [
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\Authenticate::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];
}

因此,我们再到它的父类use Illuminate\Foundation\Http\Kernel as HttpKernel;中找handle方法,可以看到handle方法是这样的

public function handle($request)
{
    try {
        // 方法欺骗,不用关注这里
        $request->enableHttpMethodParameterOverride();
        // 最核心的处理http请求的地方【6】
        $response = $this->sendRequestThroughRouter($request);
    } catch (Exception $e) {
        $this->reportException($e);
        $response = $this->renderException($request, $e);
    } catch (Throwable $e) {
        $this->reportException($e = new FatalThrowableError($e));
        $response = $this->renderException($request, $e);
    }
    $this->app['events']->dispatch(
        new Events\RequestHandled($request, $response)
    );
    return $response;
}

6、处理http请求(将request绑定到共享实例,并使用管道模式处理用户请求)

vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php的handle方法
// 最核心的处理http请求的地方
$response = $this->sendRequestThroughRouter($request);
进入sendRequestThroughRouter方法,
protected function sendRequestThroughRouter($request)
{
    // 将请求$request绑定到共享实例
    $this->app->instance('request', $request);
    // 将请求request从已解析的门面实例中清除(因为已经绑定到共享实例中了,没必要再浪费资源了)
    Facade::clearResolvedInstance('request');
    // 引导应用程序进行HTTP请求
    $this->bootstrap();【7、8】
    // 进入管道模式,经过中间件,然后处理用户的请求【9、10】
    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
}

7、在bootstrap方法中,运行给定的引导类数组$bootstrappers,加载配置文件、环境变量、服务提供者、门面、异常处理、引导提供者,非常重要的一步

位置在vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php

/**
 * Bootstrap the application for HTTP requests.
 *
 * @return void
 */
public function bootstrap()
{
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers());
    }
}
 
 /**
 * 运行给定的引导类数组
 *
 * @param  string[]  $bootstrappers
 * @return void
 */
public function bootstrapWith(array $bootstrappers)
{
    $this->hasBeenBootstrapped = true;
    foreach ($bootstrappers as $bootstrapper) {
        $this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]);
        $this->make($bootstrapper)->bootstrap($this);
        $this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]);
    }
}
 /**
 * Get the bootstrap classes for the application.
 *
 * @return array
 */
protected function bootstrappers()
{
    return $this->bootstrappers;
}
/**
 * 应用程序的引导类
 *
 * @var array
 */
protected $bootstrappers = [
    // 加载环境变量
    \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
    // 加载config配置文件【重点】
    \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
    // 加载异常处理
    \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
    // 加载门面注册
    \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
    // 加载在config/app.php中的providers数组里所定义的服务【8 重点】
    \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
    // 记载引导提供者
    \Illuminate\Foundation\Bootstrap\BootProviders::class,
];
 
8、加载config/app.php中的providers数组里所定义的服务
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
/**
 * 自己添加的服务提供者
 */
\App\Providers\HelperServiceProvider::class,

可以看到,关于常用的 Redis、session、queue、auth、database、Route 等服务都是在这里进行加载的

9、使用管道模式处理用户请求,先经过中间件进行处理

return (new Pipeline($this->app))
    ->send($request)
    // 如果没有为程序禁用中间件,则加载中间件(位置在app/Http/Kernel.php的$middleware属性)
    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
    ->then($this->dispatchToRouter());
}
app/Http/Kernel.php
/**
 * 应用程序的全局HTTP中间件
 *
 * These middleware are run during every request to your application.
 *
 * @var array
 */
protected $middleware = [
    \App\Http\Middleware\TrustProxies::class,
    \App\Http\Middleware\CheckForMaintenanceMode::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];

10、经过中间件处理后,再进行请求分发(包括查找匹配路由)

    10.1 通过中间件/路由器发送给定的请求

/**
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
 protected function sendRequestThroughRouter($request)
{
    ...
    return (new Pipeline($this->app))
        ...
        // 进行请求分发
        ->then($this->dispatchToRouter());
}

    10.2 获取路由调度程序回调

 /**
 * @return \Closure
 */
protected function dispatchToRouter()
{
    return function ($request) {
        $this->app->instance('request', $request);
        // 将请求发送到应用程序
        return $this->router->dispatch($request);
    };
}

 10.3 将请求发送到应用程序

 /**
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
 */
 public function dispatch(Request $request)
{
    $this->currentRequest = $request;
    return $this->dispatchToRoute($request);
}

 10.4 将请求分派到路由并返回响应【重点在runRoute方法】

 /**
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
 */
public function dispatchToRoute(Request $request)
{   
    // 
    return $this->runRoute($request, $this->findRoute($request));
}

  10.5 查找与给定请求匹配的路由

/**
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Routing\Route
 */
protected function findRoute($request)
{
    $this->current = $route = $this->routes->match($request);
    $this->container->instance(Route::class, $route);
    return $route;
}

 10.6 查找与给定请求匹配的第一条路由

/**
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Routing\Route
 *
 * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
 */
public function match(Request $request)
{
    // 获取用户的请求类型(get、post、delete、put),然后根据请求类型选择对应的路由
    $routes = $this->get($request->getMethod());
    // 匹配路由
    $route = $this->matchAgainstRoutes($routes, $request);
    if (! is_null($route)) {
        return $route->bind($request);
    }
    $others = $this->checkForAlternateVerbs($request);
    if (count($others) > 0) {
        return $this->getRouteForMethods($request, $others);
    }
    throw new NotFoundHttpException;
}

 到现在,已经找到与请求相匹配的路由了,之后将运行了,也就是10.4 中的runRoute 方法

    10.4 将请求分派到路由并返回响应

 /**
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
 */
public function dispatchToRoute(Request $request)
{   
    return $this->runRoute($request, $this->findRoute($request));
}

 10.7 返回给定路线的响应

 /**
 * @param  \Illuminate\Http\Request  $request
 * @param  \Illuminate\Routing\Route  $route
 * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
 */
protected function runRoute(Request $request, Route $route)
{
    $request->setRouteResolver(function () use ($route) {
        return $route;
    });
    $this->events->dispatch(new Events\RouteMatched($route, $request));
    return $this->prepareResponse($request,
        $this->runRouteWithinStack($route, $request)
    );
}

    10.8 在栈中运行路由

 /**
 * Run the given route within a Stack "onion" instance.
 * @param  \Illuminate\Routing\Route  $route
 * @param  \Illuminate\Http\Request  $request
 * @return mixed
 */
protected function runRouteWithinStack(Route $route, Request $request)
{
    $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                            $this->container->make('middleware.disable') === true;
    $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
    return (new Pipeline($this->container))
        ->send($request)
        ->through($middleware)
        ->then(function ($request) use ($route) {
            return $this->prepareResponse(
                $request, $route->run()
            );
        });
}

11、运行路由并返回响应[重点]

可以看到,10.7 中有一个方法是prepareResponse,该方法是从给定值创建响应实例,而 runRouteWithinStack 方法则是在栈中运行路由,也就是说,http的请求和响应都将在这里完成。


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
windows7下搭建Vue.js开发环境完整详细流程步骤
管理员 发表于 架构 中 2020-11-19 18:22:28 浏览量(221)

1.安装node.js  (注意版本,现在最新版本已经不支持windows7,微软也不维护了)

node.js的官方地址为:https://nodejs.org/en/download/。

根据windows版本后,选择要下载的安装包,windows7要选择12.x版本

node.js选择版本下载页面:https://nodejs.org/en/download/releases/

我这里下载的版本是:node-v12.19.1-x64.msi

下载完毕,按照windows一般应用程序,一路next就可以安装成功,建议不要安装在系统盘(如C:)。


我的安装目录:D:\nodejs      cmd里面测试一下

Microsoft Windows [版本 6.1.7601]
版权所有 (c) 2009 Microsoft Corporation。保留所有权利。
C:\Users\Administrator>node -v
v12.19.1
C:\Users\Administrator>npm -v
6.14.8

2、设置global和cache路径,设置路径能够把通过npm安装的模块集中在一起,便于管理。

(1)在nodejs的安装目录下,新建node_global和node_cache两个文件夹,如安装目录为“D:\nodejs\”

(2)在cmd里面用命令

npm config set prefix "D:\nodejs\node_global"
npm config set cache "D:\nodejs\node_cache"

设置global和cache, 设置成功后,后续用命令npm install  -g XXX安装,模块就在D:\nodejs\node_global\node_modules里。

3、设置环境变量 ,说明:设置环境变量可以使得住任意目录下都可以使用cnpm、vue等命令,而不需要输入全路径。

(1)修改用户变量PATH:把"D:\nodejs\node_global"加到后面。

(2)新增系统变量NODE_PATH:设置成“D:\nodejs\node_global\node_modules”。

4、安装cnpm

说明:由于许多npm包都在国外,用淘宝的镜像服务器,对依赖的module进行安装。参考网址为:http://npm.taobao.org/

安装命令为

npm install -g cnpm --registry=https://registry.npm.taobao.org

5、用cnpm安装vue

安装命令为

cnpm install vue -g

6、安装vue命令行工具

安装命令为

cnpm install vue-cli -g

7.创建项目

(1)cd命令进入将要新建工程的目录,如“D:\nodejs”  用命令

vue init webpack mytest

创建一个基于 webpack 模板的新项目,工程名为"mytest"。

(2)cd命令进入mytest目录

(3)安装该工程依赖的模块    用命令

cnpm install

安装该工程依赖的模块,这些模块将被安装在:mytest\node_module目录下,node_module文件夹会被新建,而且根据package.json的配置下载该项目的modules。

8.运行项目,测试该项目是否能够正常工作,用nodejs来启动

在cmd里面

cd D:\nodejs\mytest
cnpm run dev


cmd命令行里面环境安装,完整的步骤命令历史记录

 卷的序列号是 0008-8176
 D:\nodejs 的目录
2020\11\19 周四  17:27    <DIR>          .
2020\11\19 周四  17:27    <DIR>          ..
2020\11\03 周二  23:13             3,032 install_tools.bat
2020\11\19 周四  17:12        19,865,600 node-v12.19.1-x64.msi
2020\11\16 周一  10:57        30,126,240 node.exe
2020\02\14 周五  19:04               702 nodevars.bat
2020\11\19 周四  17:40    <DIR>          node_cache
2020\02\14 周五  19:04             8,969 node_etw_provider.man
2020\11\19 周四  17:41    <DIR>          node_global
2020\11\19 周四  17:24    <DIR>          node_modules
2020\02\14 周五  19:04               930 npm
2020\02\14 周五  19:04               483 npm.cmd
2020\02\14 周五  19:04               922 npx
2020\02\14 周五  19:04               539 npx.cmd
               9 个文件     50,007,417 字节
               5 个目录 118,443,216,896 可用字节
D:\nodejs>vue init webpack mytest
? Project name mytest
? Project description A Vue.js project
? Author houtizong <514224527@qq.com>
? Vue build (Use arrow keys)
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests Yes
? Pick a test runner jest
? Setup e2e tests with Nightwatch? Yes
? Should we run `npm install` for you after the project has been created? (recom
? Should we run `npm install` for you after the project has been created? (recom
mended) npm
   vue-cli · Generated "mytest".
# Installing project dependencies ...
# ========================
...
added 1819 packages from 1114 contributors and audited 1827 packages in 274.834s
41 packages are looking for funding
  run `npm fund` for details
found 101 vulnerabilities (76 low, 9 moderate, 15 high, 1 critical)
  run `npm audit fix` to fix them, or `npm audit` for details
Running eslint --fix to comply with chosen preset rules...
# ========================
> mytest@1.0.0 lint D:\nodejs\mytest
> eslint --ext .js,.vue src test/unit test/e2e/specs "--fix"
# Project initialization finished!
# ========================
To get started:
  cd mytest
  npm run dev
Documentation can be found at https://vuejs-templates.github.io/webpack
D:\nodejs>cd mytest
D:\nodejs\mytest>cnpm install
√ Installed 58 packages
√ Linked 0 latest versions
√ Run 0 scripts
√ All packages installed (used 26ms(network 21ms), speed 0B/s, json 0(0B), tarb
all 0B)
D:\nodejs\mytest>cnpm run dev
> mytest@1.0.0 dev D:\nodejs\mytest
> webpack-dev-server --inline --progress --config build/webpack.dev.conf.js
 10% building modules 0/1 modules 1 active ... webpack/hot/dev-server ./src/main
...
 95% emitting
 DONE  Compiled successfully in 3355ms                         5:51:02 ├F10: PM
┤
 I  Your application is running here: http://localhost:8080

看看效果图:

服务

1.png

浏览器

2.png



去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
Laravel Eloquent ORM中 chunk () 与 chunkById () 详解及选择
管理员 发表于 Laravel 中 2020-11-19 14:57:49 浏览量(219)

随着应用程序的扩展,使用 Laravel Eloquent 处理大量数据库记录可能变得越来越困难。 导致内存不足异常并总体上降低应用程序速度。 这是为什么?

从数据库中获取结果时,您又将数据拉到内存中。 以这个代码片段为例

Post::all()->each(function ($post) {
    // ...
});

这将导致以下查询,将 posts 表中的所有记录加载到内存中

select * from posts;

通常对于具有少量记录的表,这是绝对可以接受的。 但是,随着成千上万的帖子的积累,您最终将开始遇到 Web 服务器的内存资源限制。

分块

Laravel 中常见的方法是使用 Eloquent 的 (通过 BuildsQuery) chunk() 一种方法,该方法获取固定数量的记录,将较大的集合分解为更多的可消耗块。

Post::chunk(1000, function ($post) {
    // ...
});

尽管这看起来不错,但有很多改进和要注意的地方。

首先,设想以下情形:您正在从数据库中获取 Post 记录以更新也在 where 子句中使用的属性

Post::where('published_at', '<', now())->chunk(1000, function ($post) {
    
    $post->update('published_at', now());
});


尽管是人为的,但它例证了一个非常现实的问题,其中这样的查询将导致无限循环,因为在下一次执行查询时,published_at 属性将始终小于 now()(假设精度为秒 使用 MySQL 的 timestamp 列类型或类似名称)。

其次,存在查询性能及其对数据库服务器的影响的问题。 上面的代码将导致类似于以下内容的查询

select * from posts order by posts.id asc limit 1000 offset 9000

由于删除的记录和附加的查询约束,MySQL 无法直接转到偏移量,因此,此查询必须有效地选择前 10,000 条记录,以仅返回最后选择的 1,000 条记录。 可以想象,这无法很好地扩展到成千上万行。 这将导致数据库服务器使用不必要的资源,从而降低了应用程序中所有其他查询的速度。

大块… 但是更好!

为了防止无法预料的陷阱并提高数据库服务器性能,我们可以使用 Eloquent chunkById 的方法

Post::where('published_at', '<', now())->chunkById(1000, function ($post) {
    $post->update('published_at', now());
});

上面的代码段将导致类似于以下内容的查询

select * from posts where published_at < '2019-09-11 12:00:00' and id > 9000 order by id asc limit 1000

为什么将此方法视为 “更好”?

a)它允许 MySQL 完全跳过前 9000 条记录(假设是顺序记录)

b)由于 where 子句中的 id 约束,我们将不再重新选择已经更新的记录


深入探讨 BuildsQueries 特性的 chunkById 方法,我们 [看到](github.com/laravel/framework/blob/...) 存储的最后一条记录的 ID(请记住,我们按 ID 升序排列)被存储并在下一个要运行的查询中用作参数。

转:https://dev.to/ryancco/faster-eloquent-chunking-3lc0

去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
简单的了解一下laravel集合的用法
管理员 发表于 Laravel 中 2020-11-18 17:26:05 浏览量(159)

集合通过 Illuminate\Support\Collection 进行实例,Laravel的内核大部分的参数传递都用到了集合,但这并不代表集合就是好的。Laravel作为快捷并优雅的开发框架,是有他一定的道理所在的,并非因他的路由、DB、监听器等等。当你需要处理一组数组时,你可能就需要它帮助你快捷的解决实际问题。


实现代码

1.创建集合

$collection = collect([1,2,3]); 

显而易见,这是一部非常简单的操作,请打住你想说“这种操作很复杂”的话,它更类似与早起PHP5.x的版本的声明方式。

例如:$collection = array(1,3);

laravel对于collection也没有做任何复杂的事情,想了解更多的laravel集合知识请搜索:Laravel源码解析之集合


2.如果你想将集合转换为数据,其使用方法也非常的简单

collect([1,3])->all();
效果:[1,3]

在不过与考虑性能的情况下,可以使用Laravel集合,毕竟它将帮你完成数组操作的百分之九十的工作。


例如:我们需要通过一个水平线切分数组,将其分为2个及以上的数组个数。看集合的操作

$collection = collect([1,3,4,5,6,7]);
$chunks = $collection->chunk(4);
$chunks->toArray();
效果:[[1,4],[5,7]]

并且有些还根据sql语句的查询方式来设计的方法,可用方法:

所有方法都可以通过链式访问的形式优雅的操作数组。而且,几乎所有的方法都会返回一个新的 Collection 实例,允许你在必要时保存集合的原始副本

all
average
avg
chunk
collapse
combine
concat
contains
containsStrict
count
crossJoin
dd
diff
diffAssoc
diffKeys
dump
each
eachSpread
every
except
filter
first
firstWhere
flatMap
flatten
flip
forget
forPage
get
groupBy
has
implode
intersect
intersectByKeys
isEmpty
isNotEmpty
keyBy
keys
last
macro
make
map
mapInto
mapSpread
mapToGroups
mapWithKeys
max
median
merge
min
mode
nth
only
pad
partition
pipe
pluck
pop
prepend
pull
push
put
random
reduce
reject
reverse
search
shift
shuffle
slice
some
sort
sortBy
sortByDesc
sortKeys
sortKeysDesc
splice
split
sum
take
tap
times
toArray
toJson
transform
union
unique
uniqueStrict
unless
unlessEmpty
unlessNotEmpty
unwrap
values
when
whenEmpty
whenNotEmpty
where
whereStrict
whereBetween
whereIn
whereInStrict
whereInstanceOf
whereNotBetween
whereNotIn
whereNotInStrict
wrap
zip

想了解更多laravel集合用法请自行搜索或跳转至:https://learnku.com/docs/laravel/5.8/collections/3916#method-all


去评论 目前还没人评论,做第一个吃螃蟹的人吧!!!
    • «
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • »
  • 分类目录
  • 人生 (119)
  • 技术 (46)
  • linux (23)
  • blog从零开始 (9)
  • php (48)
  • 架构 (14)
  • 前端 (22)
  • TP(3/5) (14)
  • 数据库 (29)
  • 微信 (2)
  • Laravel (56)
  • Redis (3)
  • Docker (2)
  • Go (8)
  • 最热文章
  • 国内最新最便捷的使用Google方法,无需翻墙,直接输入 (11711)
  • 国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解 (5608)
  • laravel-admin 添加Excel导入功能 (2545)
  • nginx中500,501,502,503,504,50 (1547)
  • laravel-admin表单提交两级联动功能编写 (1337)
  • freenom域名解析步骤 (1297)
  • 建议,想法,思路,来水! (1289)
  • ES调优,对系统设置,配置文件,索引设置等参数详解并设置 (1182)
  • laravel+Guzzle配合正则实现爬虫功能爬取或批 (1069)
  • laravel中whereJsonContains跟wh (1042)
  • 近期文章
  • PHP程序员2021年最...(0个评论)
  • PHP数组的底层实现原理...(0个评论)
  • php7垃圾回收变量的G...(0个评论)
  • 在Laravel中进行类...(0个评论)
  • mysql数据库中事务的...(0个评论)
  • Linux awk 命令...(0个评论)
  • 论不要在mysql中使用...(0个评论)
  • JWT源码实现逻辑详解...(0个评论)
  • Laravel内核分析-...(0个评论)
  • Laravel内核分析-...(0个评论)
  • Laravel内核分析-...(0个评论)
  • 分享Laravel中的1...(0个评论)
  • centos7环境安装g...(0个评论)
  • go+beego框架开...(0个评论)
  • go+beego框架开发...(0个评论)
  • 近期评论
  • 请教 在

    国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解中评论

    你好,我也遇到了安装完右...

  • Test11 在

    laravel查询构造器中whereNotKey,whereKey,firstWhere用法详解中评论

    <script&g...

  • 博主 在

    国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解中评论

    @西瓜: 每一步都操作完...

  • 西瓜 在

    国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解中评论

    你好 我安装完右上角没有...

  • 博主 在

    laravel-admin 添加Excel导入功能中评论

    这应该算是比较详细了吧,...

  • 唐伯虎 在

    laravel-admin 添加Excel导入功能中评论

    咋导入excel

    ...
  • 博主 在

    国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解中评论

    希望能帮到你 老铁

  • 疾风神术 在

    国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解中评论

    我知道了,是没有打开开发...

  • 文章归档
  • 2016-10 (34)
  • 2016-11 (21)
  • 2017-06 (5)
  • 2017-07 (11)
  • 2017-08 (6)
  • 2017-09 (7)
  • 2017-10 (11)
  • 2017-11 (4)
  • 2017-12 (3)
  • 2018-01 (9)
  • 2018-02 (2)
  • 2018-03 (2)
  • 2018-04 (1)
  • 2018-05 (3)
  • 2018-06 (1)
  • 2018-10 (1)
  • 2018-11 (1)
  • 2020-03 (5)
  • 2020-04 (85)
  • 2020-05 (42)
  • 2020-06 (35)
  • 2020-07 (22)
  • 2020-08 (11)
  • 2020-09 (14)
  • 2020-10 (7)
  • 2020-11 (8)
  • 2020-12 (6)
  • 2021-01 (6)
  • 2021-02 (6)
  • 2021-03 (2)
    go+beego框架开发博客 222 aaa ab压力测试工具 ajax anguler author baidu Base64和Base64URL bbb beego控制器创建 Beego框架安装 beego注册_登录_查询 blog bt ccc CDH5集群搭建 cdn被刷 centos centos6.5 centos7+go环境+beego框架 Centos7免密登录 centos7搭建svn centos7编译安装Swoole cnpm COM cookie crontab定时任务 crontab,shell css dit docker命令 domain easyui easyui-datebox Eloquent优化技巧 ES调优 es迁移 extJS4.2 Facebook爬虫 go+beego开发博客首页 go+beego自定义公共函数 go语言环境安装 harbor header html https,ssl jdk环境安装 jq js
  • 友情链接
  • 侯体宗的博客
  • 烟雨瑟瑟的博客
  • 友链位
Top
  • 友情链接
  • 侯体宗的博客
  • 三防加固笔记本
  • 澜溪博客
  • 心中hope
  • 徒步认知的博客
  • 陈大剩博客
  • 赵波的博客
  • 佘春晓的博客
  • 自动友链系统

Auther ·HouTiZong© 2009-2020 zongscan.com 版权所有ICP证: 粤ICP备20027696号 PHP交流群 也可以扫右边的二维码

侯体宗的博客