每日一个 PHP 函数————array_change_key_case

因为已经有文档了,可能有些人觉得我写这个有些多余了。可是并不是每一个 PHPer 都会好好地去阅读文档,自然有一些函数可能都没有听说过(很不幸我也是这其中的一员)。我也希望能通过写这些文章,能够促使我完整地读完文档,同时,能够给其它的 PHPer 一个参考,“啊,原来还有这个函数” 的感觉。同时,我也希望我能通过写这些文章,去阅读各个函数的 C 语言实现。也实现自我驱动地学习。 函数原型 array array_change_key_case ( array $array [, int $case = CASE_LOWER ] ) 该函数的具体作用是,将一个数组中的所有的英文字母转换为大写或小写。 我们可以看到,这个函数接收两个参数,返回一个数组。第一个参数数组没有使用引用的方式,那么说明该函数并不会改变原数组,它会生成新的数组作为返回值。而第二个参数是可选的,它控制着该函数是转换成大写还是小写。默认是转化为小写。 函数使用 第二个参数 函数的第二个参数传入的是一个预定义常量,分别是 CASE_LOWER 和 CASE_UPPER,前者是将 key 转换成小写,也是函数的默认值;后者是将 key 转换成大写。 使用 $arr = [ ‘loWer’ => 1, ]; $toLower = array_change_key_case($arr, CASE_LOWER); // 我认为,不管它的默认值是什么,我们都要写上这第二个参数。我们的代码写出来,是给人看的,不是给机器看的。 // 所以我们的代码应当尽量多的包含语义。 $toUpper = array_change_key_case($arr, CASE_UPPER); var_dump($toLower); var_dump($toUpper); 坑 这个函数的使用,是有个坑的,这个坑就是,当转换之后,如果结果中有两个相同的 key,那么就会保留最后的那个。举个例子。 $arr = [ ‘key’ => 1, ‘kEy’ => 2, ‘keY’ => 3, ]; $toLower = array_change_key_case($arr, CASE_UPPER); var_dump($toLower); // [‘key’ => 3] 在这个例子中,我们发现,当执行转换之后,三个 key 变成相同的了,那么在这种情况下,只会保留最后一个元素作为 key。这里得到的数组是 [‘key’ => 3]。
Read more →

浅入理解 PHP 中的 Generator

何为 Generator 从 PHP 5.5 开始,PHP 加入了一个新的特性,那就是 Generator,中文译为生成器。生成器可以简单地用来实现对象的迭代,让我们先从官方的一个小例子说起。 xrange 在 PHP 中,我们都知道,有一个函数叫做 range,用来生成一个等差数列的数组,然后我们可以用这个数组进行 foreach 的迭代。具体就想这样。 foreach (range(1, 100, 2) as $num) { echo "{$num}\n"; } 这一段代码就会输出首项为 1,末项为 100,公差为 2 的等差数列。它的执行顺序是这样的。首先,range(1, 100, 2) 会生成一个数组,里面存了上面那样的一个等差数列,之后在 foreach 中对这个数组进行迭代。 那么,这样就会出现一个问题,如果我要生成 100 万个数字呢?那我们就要占用上百兆内存。虽然现在内存很便宜,但是我们也不能这么浪费内存嘛。那么这时,我们的生成器就可以排上用场了。考虑下面的代码。 function xrange($start, $limit, $step = 1) { while ($start <= $limit) { yield $start; $start += $step; } } foreach (xrange(1, 100, 2) as $num) { echo "{$num}\n"; } 这段代码所的出来的结果,和前面的那段代码一模一样,但是,它内部的原理是天翻地覆了。
Read more →

一个 PHPer 第一次用 Koa2 写 Node.js 的心路历程

学了一段时间的 js 了,突然想实践一下。正好公司有个小的项目要做,就顺手拿 Koa2 来做了。真是不做不知道,做了想不到。踩了一堆新手坑。 初次接触 Koa2 在知道 Koa2 之前,我也了解过 Express,可惜并没有实战用过。后来大家都说 Koa 是一个比 Express 更牛X的东西,于是在好(作)奇(死)心作祟下,直接去用 Koa2 了。后来证明的确是作死,原本用 PHP 一天就能写完东西,愣是让我搞了三天。 安装 最近 Node.js V8 发布了,原生支持 async 和 await 调用了,所以直接把 Node.js 升级了一下。 根据 Koa2 的教程,安装很简单,我是使用的 yarn 的(还真是比 npm 快)。 yarn add koa 默认就装了 Koa 2.2。然后装完了,其实我是一脸懵逼的,文档上说这样用。 const Koa = require(‘koa’) const app = new Koa() // response app.use(ctx => { ctx.body = ‘Hello Koa’ }) app.listen(3000) 我照着代码写了下来,的确成功了。可是,难不成我要把所有的逻辑写在 app.use 里? 中间件 我感觉我受到了惊吓,吓得我赶紧往下看文档。原来 Koa2 是一个中间件模型。app.
Read more →

理解 Go 语言中的方法和接收者

0x01 前言 Go 语言的语法实在有些不一样,与其它面向对象语言相比,Go 的方法似乎有些晦涩。 0x02 方法的定义 在 Go 语言里,方法和函数只差了一个,那就是方法在 func 和标识符之间多了一个参数。 type user struct { name string, email string, } //这是函数的定义 func notify(email string) { fmt.Println("Email is %s", email) } //这是方法的定义 func (u user) notify(email string) { fmt.Println("Email is %d", email) } 我们可以看到,方法是在 func 和 notify 之间多了一个 user 类型的参数 u,这个 u 就称作接收者。 0x03 接收者 接收者有两种,一种是值接收者,一种是指针接收者。顾名思义,值接收者,是接收者的类型是一个值,是一个副本,方法内部无法对其真正的接收者做更改;指针接收者,接收者的类型是一个指针,是接收者的引用,对这个引用的修改之间影响真正的接收者。像上面一样定义方法,将 user 改成 *user 就是指针接收者。 接收者与对象 相信有很多人看到这个接收者之后都很苦恼,到底这个接收者是什么,是干什么用的。我们在学习一门新的语言的时候,都讲究触类旁通,和我们已经了解的语言作对比。那么我们就通过拿 Go 和其它带有类的面向对象的语言做对比来搞清楚接收者是什么。这里我们用 php 来举例子。 在 php 中,我们要定义一个方法,首先是要定义一个类。
Read more →

从零开始学习 Go —— 安装

0x01 设置 Go 环境 要安装并顺利使用 Go,第一步就是要设置 Go 的环境。 需要设置的 Go 的环境变量,一共有三个。 GOROOT Go 语言的源码以及安装目录。 GOPATH Go 语言的开发目录,目录可以有多个,但是,当我们执行 go get 命令的时候,如未指定目录,会默认保存在第一个目录下。 GOROOT_BOOTSTRAP 这个目录在安装 Go 1.5 版本及之后的版本时需要设置。由于在 1.4 版本后,Go 编译器实现了自举,即通过 1.4 版本来编译安装之后版本的编译器。如果不设置该环境变量的话,会产生这样一个错误 Set $GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4.。 除此之外,还需要配置 PATH 环境变量到 Go 的二进制程序目录。 我们需要在 ~/.bash_profile 中添加下面的代码(我把所有的 Go 语言相关的东西都放在了 ~/.golang 下面了): export GOROOT=$HOME/.golang/go export GOPATH=$HOME/.golang/path export PATH=$PATH:$HOME/.golang/go/bin export GOROOT_BOOTSTRAP=$HOME/.golang/go1.4 0x02 安装 Go 我们有两种方式下载 Go,一个是直接下载源码,另一个是通过 GitHub 克隆项目,个人推荐选择第二种,地址:GayHub。
Read more →

为什么Swoole可以加速php

前言 最近在研究Swoole,原来一直听别人在说Swoole可以加速,一直都是懵逼的。在研究了Swoole之后,我有了一些自己的理解。 PHP-CGI 的黑历史 对于 PHP 处理网络请求,大家基本上也都是再用 CGI 的方式来做的。那么,什么是 CGI 呢。 CGI CGI,全称 Common Gateway Interface,中文称作公共网关接口。也许有很多人认为 CGI 是一个程序,没错,曾经的我也是这么认为的。直到我从《图解HTTP》开始细细地研究HTTP协议之后,我才知道,原来 CGI 是一种协议。任何编程语言,都可以实现 CGI,所以任何语言都可以作为网站的后台语言(扯远了)。 PHP-CGI 上面说了,CGI 是一个协议,所以,PHP 有自己对 CGI 的实现,那就是 PHP-CGI。可是呢,随着技术的发展,人们开始意识到,PHP-CGI 的性能不是那么尽如人意。我们知道,PHP 在运行的时候,是依赖配置文件 php.ini的。所以,每当 PHP-CGI 开始工作的时候,它是完完全全的一个新进程,它需要重新加载配置文件并初始化,这就造成了很大的资源和时间的浪费。 FastCGI 那么,怎么才能避免这种浪费呢,聪明的程序员们想出了另外一种方法:我们为什么不预先加载好配置,然后,每一个执行的任务只需要复制当前的进程,不就能避免上面的浪费了么。于是,FastCGI 便横空出世。 FastCGI,全称 Fast Common Gateway Interface,中文译作快速公共网管接口。没错,这又是个协议。当然,这个协议并不是因为 PHP 才有的。 Apache (httpd) 几乎所有的 Web 容器都实现了 FastCGI 的功能。首先是 httpd。对于 PHP 来说,httpd 是通过自身来实现一个 FastCGI 的模块的。它会预先加载好 php.ini 文件中的配置。待到有请求进入需要 PHP 处理时,PHP 就不需要再对 php.ini 重新加载了。这也就是每改动过 php.ini 后都要重启 httpd 服务的原因。 Nginx 与 php-fpm php-fpm 也是 FastCGI 的一种实现。通常我们是将 Nginx 的 PHP 处理部分代理到 php-fpm 的端口上,交给 php-fpm 来处理。而 php-fpm 同样是通过预先加载配置,然后给到子进程的方式的,它会对进程做一些管理。
Read more →

TCP 状态转移

在《Linux 高性能服务器编程》中,有下面这样的状态转移图。 TCP 的各个状态 客户端 建立连接(三次握手) SYN_SENT 在客户端发送第一个同步报文段(第一次握手)之后,就会进入这个状态。 ESTABLISHED 在收到服务端发送的确认和同步报文段(第二次握手)后,客户端只需要发送出一个确认报文段(第三次握手),就算完成了三次握手了,即客户端认为连接已经建立。 断开连接(四次挥手) FIN_WAIT_1 TCP 断开连接时,需要进行四次挥手。当客户端主动关闭连接,发出第一个 FIN 报文段(第一次挥手)后,即进入此状态。 FIN_WAIT_2 服务端在收到第一个 FIN 报文段后,回复给客户端确认报文段(第二次挥手)后的状态。其实 FINE_WAIT 的含义,就是等待服务端发来的 FIN 报文段。 TIME_WAIT 在客户端收到服务端的 FIN 报文(第三次挥手)后,返回给服务端确认报文段(第四次挥手)后的状态。 服务端 建立连接(三次握手) CLOSED 一个假想的起点和终点,不是一个实际的状态。 LISTEN 在程序启动后,等待客户端连接的状态。 SYN_RCVD 在每一个 TCP 连接建立时,都要进行三次握手,这个状态表示服务端接收到客户端发来的同步报文段(第一次握手),并且向客户端发送了确认同步报文段(第二次握手)之后的状态,在这个状态时,其实连接已经经历了两次握手。 ESTABLISHED 收到客户端发来的确认报文段(第三次握手),即三次握手成功后,双方正式建立连接的状态。 断开连接(四次挥手) CLOSE_WAIT 在收到客户端主动断开连接的 FIN 报文段(第一次挥手)后,返回给客户端确认报文段(第二次挥手)后的状态。 LAST_ACK 服务端发送 FIN 报文段(第三次挥手)后的状态。在该状态之后,收到确认报文段(第四次握手),连接就关闭了。 时序图 对于书中的状态转移图,有一些复杂和抽象。我根据自己的分析,画出了一份时序图。
Read more →

【Re从零开始学习计算机网络】TCP与IP的区别

TCP 是 TCP/IP 协议族的重要协议之一,它和同是 TCP/IP 协议族的重要协议 IP 协议有下面这些区别。 网络层次 IP 协议管理的是逻辑上的网络连接,它是工作在 OSI 的第 3 层网络层的协议。由于离应用层比较远,故大多数情况下,它对应用程序是透明的。 TCP 协议管理的是两个终端数据流的传输,它是工作在 OSI 的第4层传输层的协议。相比于 IP 协议,TCP 协议更接近应用层,在应用程序中有更强的可操作性。 连接管理的方式 在 IP 协议中,连接双方主要靠 IP 数据报中的源 IP 和目的 IP 来识别对方。 在 TCP 协议中,连接双方主要靠 TCP 报文段中的源端口号和目的点口号来识别对方,管理连接,控制两个方向的数据流。 连接状态 IP 协议是一个无连接、不可靠的的网络连接。当计算机的 IP 数据报输出子模块将 IP 数据报发送出去后,该 IP 数据报就不再存在于 IP 发送队列中了。这就意味着,IP 模块无法保证所发送的 IP 数据报准确无误地将数据报发送给目的端。 TCP 协议是一个面向连接、可靠的网络服务。当计算机将 TCP 报文段发送给 IP 模块经由 IP 数据报输出子模块发送出去后,该 TCP 报文段依旧会暂存于 TCP 内核发送缓冲区,直到接收到对方发送的确认报文。如果没有收到对方发送的确认报文,TCP 会将暂存于 TCP 内核发送缓冲区的数据封装重新发送,直到确认发送成功为止。
Read more →

【Re:从零开始学习网络】IP路由与转发

Internet Protocol IP 全称为 Internet Protocol,顾名思义,是工作在网络层的一种协议。既然已经有了 MAC 地址,IP 地址就作为一种给网络赋予层次、方便管理的协议而存在。而网络层次的精髓,在于 IP 的路由与转发。 IP 模块 所有支持 IP 的主机或者路由器,都含有处理 IP 的 IP 模块。 IP 模块的工作有两个,一个是处理来自数据链路层的数据,根据自身是否允许路由以及 IP 数据报是否发给自己来进行路由。另一个是封装上层协议并且发送给下层。而路由过程呢,就是前者,判断外来的 IP 数据报是否应该转发。如果转发模块不允许转发 IP 数据报,并且该 IP 数据报不是发给本机的,则转发模块将会丢弃该 IP 数据报。 另外,当该 IP 数据报设置了源站路由选项,则转发模块也会根据路由选项中的 IP 来判断是丢弃还是转发。 一般来说,路由器多用于转发 IP 数据报,而主机多用于接受 IP 数据报,即转发模块不允许转发。 要实现 IP 的路由,则必须要在每一个主机、路由器中存储一张路由表,这样才会更加快捷、方便地知道该 IP 数据报的下一跳是哪一个主机(路由器)。 路由表 下面是我的电脑的路由表: 看一下这张路由表,第一个是 default,说明这一条记录是默认路由,即该主机所在的网络的网关。如果一个 IP 数据报的目的 IP 地址没有存在这张路由表中,则默认发给 default,由它来转发该 IP 数据报。 下面的几项中,有一些 Gateway 中是 link#4 的。这里不同的主机显示的可能不一样,这个就相当于 * ,这个标志,说明其对应的 IP 与本机在同一个网络内,在数据转发模块接收到来自上层协议且目的 IP 为其中一个时,默认直接发送,而不需要进行路由。
Read more →

数据段、数据报、数据包、帧的区别与联系

Read more →