0%


PHPUnit 基本用法+实例详解
上一篇文章介绍了 PHPUnit 在 windows 的安装和配置。今天我们来介绍一下 PHPUnit 如何编写一些基本的测试用例。我也是在最近才开始慢慢使用的 PHPUnit , 用不足之处,欢迎指正。

编写规范

  1. 测试类一般以***Test 命名;
  2. 该类必须继承 PHPUnit_Framework_TestCase 类;
  3. 类里的测试用例方法一般以test开头,当然也可以通过@test注释来定义一个名字不为test开头的方法为测试方法;
  4. 测试方法中需要使用断言方法来断言实际传入的参数与期望的参数是否一致来达到测试的目的;
阅读全文 »


本篇文章介绍一下 PHPUnit 在 windows 下的安装和配置

准备

  • php 版本 php5.4.45
  • phpunit 版本 phpunit4.8.24
  • 操作系统:windows 7 (32)
  • php 安装(这里不再详细讲解)
  • phpunit 下载地址:https://phpunit.de/ 下载到文件 phpunit-4.8.24.phar
阅读全文 »


最近一直在写 go 语言,总结下go获取文件信息的方法

获取文件修改时间

1
2
3
4
fileInfo, _ := os.Stat("test.log")
//修改时间
modTime := fileInfo.ModTime()
fmt.Println(modTime)
阅读全文 »


数据结构

算法

排序

查找

过滤

字符串匹配

图遍历

贪心算法

一致性哈希

网络知识

TCP

UDP

KDP

HTTP

HTTPS

进程线程协程

进程

线程

协程

内存分配

Linux命令

中间件

MySQL

Redis

RabbitMQ

Nginx

Apache

Memcache

Sphinx

ELK

Go语言

基础

高级

内存分配

启动过程

并发机制

channel

其他

PHP

设计模式

项目总结

实现短网址项目

https://www.jianshu.com/p/43eea66a2235
https://www.cnblogs.com/lovekingly/p/5505308.html
https://www.jb51.net/article/136554.htm
https://www.cnblogs.com/flying1819/articles/8832640.html


PHP 的基本架构

  • Zend引擎:Zend 整体用纯C实现,是 PHP 的内核部分,它将 PHP 代码翻译(词法、语法解析等一系列编译过程)为可执行 opcode 的处理并实现相应的处理方法、实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的 api 方法供外部调用,是一切的核心,所有的外围功能均围绕 Zend 实现。
  • Extensions:围绕着 Zend 引擎,extensions 通过组件式的方式提供各种基础服务,我们常见的各种内置函数(如 array 系列)、标准库等都是通过 extension 来实现,用户也可以根据需要实现自己的 extension 以达到功能扩展、性能优化等目的(如贴吧正在使用的 PHP 中间层、富文本解析就是 extension 的典型应用)。
  • Sapi:Sapi 全称是 Server Application Programming Interface,也就是服务端应用编程接口,Sapi 通过一系列钩子函数,使得 PHP 可以和外围交互数据,这是 PHP 非常优雅和成功的一个设计,通过 sapi 成功的将PHP本身和上层应用解耦隔离,PHP 可以不再考虑如何针对不同应用进行兼容,而应用本身也可以针对自己的特点实现不同的处理方式。
    • apache2handler:这是以apache作为webserver,采用mod_PHP模式运行时候的处理方式,也是现在应用最广泛的一种。
    • cgi:这是webserver和PHP直接的另一种交互方式,也就是大名鼎鼎的fastcgi协议,在最近今年fastcgi+PHP得到越来越多的应用,也是异步webserver所唯一支持的方式。
    • cli:命令行调用的应用模式
  • 上层应用:这就是我们平时编写的 PHP 程序,通过不同的 sapi 方式得到各种各样的应用模式,如通过 webserver 实现 web 应用、在命令行下以脚本方式运行等等。

PHP 执行过程

  • 第一步:启动 web 服务器,如果是 apache, apache 通过 mod_php5.so 调用 sapi 接口启动 php 解释器,如果是 nginx, nginx 调用 php-fpm(php fast-cgi进程管理器)启动 php-fpm,php 内核调用各个扩展的初始化方法,使之处于激活状态
  • 第二部:当发生请求时候, SAPI 将控制权交给 php 核心层, php 设置了本次请求的变量
  • 第三部:php 核心层调用 zend 引擎将 php 源代码编译成 opcode 码,并在 zend 虚拟机运行得出结果,将结果返回给 php 核心层
    • 词法分析
    • 语法分析
    • 生成 opcode 码
    • 执行 opcode 码
  • 第四部:php 核心层将返回结果通过 sapi 返回给 web 服务器,web 服务器将结果渲染在浏览器

PHP SAPI 生命周期

PHP开始执行以后会经过两个主要的阶段:

  • 处理请求之前的开始阶段
    • 第一个过程是模块初始化阶段(MINIT),在整个SAPI生命周期内 (例如 Apache 启动以后的整个生命周期内或者命令行程序整个执行过程中), 该过程只进行一次。
    • 第二个过程是模块激活阶段(RINIT),该过程发生在请求阶段,例如通过 url 请求某个页面,则在每次请求之前都会进行模块激活(RINIT请求开始)。

请求处理完后就进入了结束阶段,一般脚本执行到末尾或者通过调用 exit() 或 die() 函数, PHP 都将进入结束阶段。和开始阶段对应,结束阶段也分为两个环节。

  • 请求之后的结束阶段
    • 一个在请求结束后停用模块(RSHUTDOWN,对应RINIT)
    • 一个在 SAPI 生命周期结束(Web服务器退出或者命令行脚本执行完毕退出)时关闭模块(MSHUTDOWN,对应MINIT)

例如执行 test.php

1
php -f test.php

单进程 SAPI 生命周期

调用每个模块的初始化前,初始化过程:

  • 初始化若干全局变量
  • 初始化若干常量
  • 初始化Zend引擎和核心组件
  • 解析 php.ini
  • 全局操作函数的初始化
  • 初始化静态构建的模块和共享模块(MINIT)

多进程SAPI生命周期

通常PHP是编译为apache的一个模块来处理PHP请求。Apache一般会采用多进程模式, Apache启动后会fork出多个子进程,每个进程的内存空间独立,每个子进程都会经过开始和结束环节, 不过每个进程
的开始阶段只在进程fork出来以来后进行,在整个进程的生命周期内可能会处理多个请求。 只有在Apache关闭或者进程被结束之后才会进行关闭阶段,在这两个阶段之间会随着每个请求重复请求开始-请求关闭的
环节。

多线程的SAPI生命周期

多线程模式和多进程中的某个进程类似,不同的是在整个进程的生命周期内会并行的重复着 请求开始-
请求关闭的环节

Apache 加载 PHP 模块

当PHP需要在Apache服务器下运行时,一般来说,它可以mod_php5模块的形式集成, 此时mod_php5模块的作用是接收Apache传递过来的PHP⽂件请求,并处理这些请求, 然后将处理后的结果返
回给Apache。如果我们在Apache启动前在其配置⽂件中配置好了PHP模块(mod_php5), PHP模块通过注册apache2的ap_hook_post_config挂钩,在Apache启动的时候启动此模块以接受PHP文件的请求。

  • 静态加载

    1
    LoadModule php5_module modules/mod_php5.so
  • 动态加载
    如果需要在服务器运行时加载模块, 可以通过发送信号HUP或者AP_SIG_GRACEFUL给服务器,一旦接受到该信号,Apache将重新装载模块, ⽽不需要重新启动服务
    器。

Apache 运行过程

Apache的运行分为启动阶段和运行阶段。

启动阶段

Apache为了获得系统资源最⼤的使用权限,将以特权用户root(*nix系统)或超级管理员Administrator(Windows系统)完成启动, 并且整个过程处于一
个单进程单线程的环境中。 这个阶段包括配置⽂件解析(如http.conf⽂件)、模块加载(如mod_php,mod_perl)和系统资源初始化(例如⽇志⽂件、共享内存段、数据库连接等)等⼯作。
Apache的启动阶段执行了⼤量的初始化操作,并且将许多⽐较慢或者花费⽐较⾼的操作都集中在这个阶段完成,以减少了后⾯处理请求服务的压⼒。

运行阶段

Apache主要⼯作是处理用户的服务请求。 在这个阶段,Apache放弃特权用户级别,使用普通权限,这主要是基于安全性的考虑,防⽌由于代码的缺陷引起的安全漏洞。 Apache对HTTP的请求
可以分为连接、处理和断开连接三个⼤的阶段。

PHP 的几种运行方式

https://phachon.com/2016/07/29/php-run-type/

PHP 程序的执行过程

  1. 如上例中, 传递给php程序需要执行的⽂件, php程序完成基本的准备⼯作后启动PHP及Zend引
    擎,加载注册的扩展模块。
  2. 初始化完成后读取脚本⽂件,Zend引擎对脚本⽂件进行词法分析,语法分析。然后编译成opcode执
    行。 如过安装了apc之类的opcode缓存, 编译环节可能会被跳过⽽直接从缓存中读取opcode执
    行。

词法分析

PHP的词法分析器是通过 lex 生成的, 词法规则⽂件在$PHP_SRC/Zend/zend_language_scanner.l, 这一阶段lex会会将源代码按照词法规则切分一个一个
的标记(token)。PHP中提供了一个函数 token_get_all(), 该函数接收一个字符串参数, 返回一个按照词法
规则切分好的数组。

PHP 变量类型及存储结构

变量的值存储到以下所⽰ zval 结构体中。变量的 key 和指向 zval 的指针存储在符号表里。 zval 结构体定义在Zend/zend.h⽂件,其结构如下:

1
2
3
4
5
6
7
8
9
typedef struct _zval_struct zval;

struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};

变量的值存储在 zvalue_value 联合体中

1
2
3
4
5
6
7
8
9
10
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value;

使用联合体⽽不是用结构体是出于空间利用率的考虑,因为一个变量同时只能属于一种类型。 如果使用结构体的话将会不必要的浪费空间,⽽PHP中的所有逻辑都围绕变量来进行的,这样的话, 内存浪费将是⼗分⼤的。这种做法成本⼩但收益⾮常⼤。

PHP 哈希表实现

PHP内核中的哈希表是⼗分重要的数据结构,PHP的⼤部分的语⾔特性都是基于哈希表实现的,例如:变量的作用域、函数表、类的属性、⽅法等,Zend引擎内部的很多数据都是保存在哈希表中的。

PHP中的哈希表是使用拉链法来解决冲突的,具体点讲就是使用链表来存储哈希到同一个槽位的数据, Zend为了保存数据之间的关系使用了双向列表来链接元素。

PHP 哈希表实现中的数据结构,PHP使用两个数据结构来实现哈希表,HashTable结构体用于保存整个哈希表需要的基本信息,⽽ Bucket 结构体用于保存具体的数据内容

  • HashTable 结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct _hashtable {
uint nTableSize; //表长度,并非元素个数
uint nTableMask;//表的掩码,始终等于nTableSize-1
uint nNumOfElements;//存储的元素个数
ulong nNextFreeElement;//指向下一个空的元素位置
Bucket *pInternalPointer;//foreach循环时,用来记录当前遍历到的元素位置
Bucket *pListHead;
Bucket *pListTail;
Bucket **arBuckets;//存储的元素数组
dtor_func_t pDestructor;//析构函数
zend_bool persistent;//是否持久保存。从这可以发现,PHP数组是可以实现持久保存在内存中的,而无需每次请求都重新加载。
unsigned char nApplyCount;
zend_bool bApplyProtection;
} HashTable;
  • Bucket 结构
1
2
3
4
5
6
7
8
9
10
11
typedef struct bucket {
ulong h; //数组索引
uint nKeyLength; //字符串索引的长度
void *pData; //实际数据的存储地址
void *pDataPtr; //引入的数据存储地址
struct bucket *pListNext;
struct bucket *pListLast;
struct bucket *pNext; //双向链表的下一个元素的地址
struct bucket *pLast;//双向链表的下一个元素地址
char arKey[1]; /* Must be last element */
} Bucket;

PHP中初始化一个空数组或不足8个元素的数组,都会被创建8个长度的 HashTable。同样创建100个元素的数组,也会被分配128长度的HashTable。依次类推。

PHP 哈希算法

PHP内核哈希表的散列函数很简单,直接使用 (HashTable->nTableSize & HashTable->nTableMask)的结果作为散列函数的实现。这样做的目的可能也是为了降低Hash算法的复杂度和提高性能。

PHP 对字符串索引处理方式

与数字索引相比,只是多了一步将字符串转换为整型。用到的算法是time33,就是对字符串的每个字符转换为ASCII码乘上33并且相加得到的结果。

PHP7 中 HashTable 优化

blog

PHP7 变量存储的优化

内存管理机制

PHP的内存管理可以被看作是分层(hierarchical)的。 它分为三层:存储层(storage)、堆层(heap)和接⼝层(emalloc/efree)。 存储层通过 malloc()、mmap() 等函数向系统真正的申请内存,并通过 free() 函数释放所申请的内存。 存储层通常申请的内存块都⽐较⼤,这⾥申请的内存⼤并不是指
storage层结构所需要的内存⼤, 只是堆层通过调用存储层的分配⽅法时,其以⼤块⼤块的⽅式申请的内存,存储层的作用是将内存分配的⽅式对堆层透明化。

初始化内存管理顺序:

  • 初始化 storage 层的分配方案
  • 初始化 heap 堆层

PHP中的内存管理主要⼯作就是维护三个列表:⼩块内存列表(free_buckets)、 ⼤块内存列表(large_free_buckets)和剩余内存列表(rest_buckets)
从内存分配的过程中可以看出,内存块查找判断顺序依次是⼩块内存列表,⼤块内存列表,剩余内存列表。
ZendMM向系统进行的内存申请,并不是有需要时向系统即时申请, ⽽是由ZendMM的最底层(heap层)先向系统申请一⼤块的内存,通过对上⾯三种列表的填充, 建立一个类似于内存池的管理机制。 在程序运行需要使用
内存的时候,ZendMM会在内存池中分配相应的内存供使用。 这样做的好处是避免了PHP向系统频繁的内存申请操作

垃圾回收

引用计数、引用计数的问题。如何解决。


问题

js 控制表单重新刷新

1
2
location.href = url;
location.reload();

测试时发现谷歌,360均正常,但是在火狐浏览器,刷新完之后,表单的数据还在,并没有清除,刚开始以为是浏览器的设置问题。查找资料后找到一些解决办法

阅读全文 »


简介

一个简单的 web 聊天室, 采用 node.js 编写,基于 express + mysql + socket 实现的在线多人web 聊天系统,包括用户的登陆注册,用户的个人信息修改,目的是为了更加深入学习了解 node.js 和 websocket 技术,给初学者一个练习的小项目。有兴趣的同学可以继续完善(用户的头像上传,创建聊天群,消息保存等)

阅读全文 »


简介

工作工程中,不论是开发还是运维,都会遇到各种各样的日志,主要包括系统日志、应用程序日志和安全日志,对于开发人员来说,查看日志,可以实时查看程序的运行错误,以及性能分析,通常,一个大中型的应用程序会被部署到多台服务器,那日志文件也会分散到不同的机器上,这样查看日志难道要一台一台去查看?显然是太麻烦了,开源的日志分析系统 ELK 完美的解决了这个问题。
ELK 并不是一个独立的系统,她是由 ElasticSearch、Logstash、Kibana 三个开源的工具组成。

  • ElasticSearch
    ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
  • Logstash
    Logstash 是一个开源的日志分析、收集工具,并将日志存储以供以后使用。
  • Kibana
    Kibana 是一个为 Logstash 和 ElasticSearch 提供的日志分析的 Web 接口。可使用它对日志进行高效的搜索、可视化、分析等各种操作。
阅读全文 »