高性能的 PHP API 接口开发

学习与目标

  • API 的工程化建设及 API 性能

    • 如何对 API 项目中的公共技术抽离,建立有层级的 PHP API 项目
    • 如何做好异常处理及监控,保障 API 高效稳定地提供服务
    • 如何进行 API 性能测试
    • 如何定位性能瓶颈
    • 如何解决接口 QPS TPS 问题
  • PHP 常规实现的基本 API

    • 用户登录注册接口
    • 文章类别接口
    • 一些接口实现方法
    • 邮件接口
  • 第三方能力的整合系列接口

    • 短信发送
    • Push 消息
    • IP 地址转换
    • 支付
    • 其他
  • Yaf 框架的优点

    • 功能足够精简
    • 性能足够优秀
    • 适合做 API 层开发
    • 适合用 PHP 硬编码实现
  • 学习收获
    • 掌握 Restful API 接口的实现方法
    • 学习更多 PHP 基本语法、基本库的使用
    • 能够了解 API的分层实现方法
    • 与前端解耦,并行开发
    • 学会工程化抽离整理代码能力
    • 提升 API 性能问题的定位分析与解决能力

API 基础知识讲解

  • API 是无状态性的
  • API 的有状态性的实现方法
    • Session Cookie 实现
    • Json Web Token 实现
  • REST 与 SOAP、RPC 的区别
    • RPC 所谓的远程过程调用(面向方法)?
    • SOAP 所谓的面向服务的架构(面向消息)?
    • REST 所谓的面向资源?

周边知识与工具

  • API 常用传输格式:JSON
  • JSON 的可视化工具
  • API 调试工具:Postman

Yaf 框架

  • Yaf 的安装
    • wget “安装地址”
    • tar zxvf 安装包.tgz
    • phpize
    • ./configure –with-php-config=php-config
    • make
    • make test
    • make install

Yaf Codes Generator

1
php yaf_cg Sample

一些思考

  • 如何防止机器恶意注册与机器模拟用户登录
    • 提交特殊参数
    • 实现网站验证码
  • 如何实现密码找回功能
  • 如何实现数据分页效果
  • 如何收集发送出去的邮件的送达效果
  • 短信发送成功对账实现
  • 批量短信通知的场景如何高效发送
  • 在不能完全识别 IP 地址的时候如何做降级方案

文章类接口的实现

  • 文章信息的 CRUD
  • 让一个 API 接口做更多的事情
  • 文章类接口通用功能:分页功能的实现

Push 服务

  • 小米 Push
  • 华为 Push
  • 个推
  • 极光推送

IP 地址转换接口

  • 黑白名单
  • 访问来源统计
  • 小流量线上验证

API 接口的提炼

  • API 自测脚本
    • 作用
      • 项目部署上线前,验证调成过的项目代码
      • 有利于新的团队成员快速去了解代码功能
    • 安装
      • composer require curl/curl
  • API 公共 Lib 的抽离
    • SDK 抽离
    • Composer 管理
  • 建立数据操作层 DAO
  • 接口异常时的处理规范
    • 统一的 API 接口返回 - 错误信息字典
    • 统一的 API 异常处理 - 容错与降级
  • API 功能的整合
  • API 文档
    • PHPDocumentor

API 接口的性能

  • 接口性能信息收集
    • 关注接口的整体时间开销
      • grep ‘log_format’ ./ -r
    • Xhprof 收集 API 接口开销
  • API 时间开销定位与分析
  • API 上下游性能优化
    • MySQL 性能分析
    • 后端服务调优
      • 短信
      • 邮件
      • 微信推送
    • API 返回调优
  • API 服务稳定性 SLA
    • 服务监控
      • supervisor
      • supervisor 配置和使用
      • find / -name ‘supervisord.conf’
    • API 负载均衡
    • 服务报警

Vue.js 2.X 实践

项目脚手架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
vue init webpack vue-music
? Project name vue-music
? Project description Music Player
? Author luisedware <luisedware@qq.com>
? Vue build runtime
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Setup unit tests with Karma + Mocha? No
? Setup e2e tests with Nightwatch? No
cd vue-music
npm install
npm run dev

stylus

Stylus - 富有表现力的、动态的、健壮的CSS,安装 Stylus

1
2
npm install --save-dev stylus
npm install --save-dev stylus-loader

重写 import 路径别名

build/webpack.base.conf.js 文件下的 resolve 数组中,可以重写目录路径别名:

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
...,
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'src': resolve('src'),
'assets': resolve('src/assets')
},
symlinks: false
},
...

配置完成后需要重新运行 npm run dev

使用 vue-router 实现组件间的页面切换

首先安装相关依赖

1
2
3
4
5
6
// 解决 ES 语法转义
npm install babel-runtime --save
// 解决移动端点击 300 毫秒延迟问题
npm install fastclick --save
// 对 ES 6 API 进行转义
npm install babel-polyfill --save-dev

Vue Route

tag 属性定义渲染生成的 HTML 标签,class 属性定义渲染生成的标签的 HTML 类,to 属性定义 Vue 路由。

将 Vue 路由定义的内容渲染出来。

jsonp

jsonp 一般用于跨域

1
npm install jsonp --save

Promise

详解:https://developers.google.com/web/fundamentals/getting-started/primers/promises?hl=zh-cn
encodeURIComponent?

URL 拼接示例代码

1
2
3
4
5
6
7
8
9
/* 拼接 URL 参数 */
function param (data) {
let url = ''
for (let key in data) {
let value = data[key] !== undefined ? data[key] : ''
url += `&${key}=${encodeURIComponent(value)}`
}
return url ? url.substring(1) : ''
}

Object.assign

Vue.js slot

better-scroll

1
npm instal better-scroll --save

mounted

vue ref 引用


this.$refs.HelloWorld

判断是否存在 className,添加 className

Vue.js keep-alive 标签

axios 使用与架设服务器

npm install axios –save

vue.js watch 数据监听和更新 DOM

Vue-Lazyload

npm install vue-lazyload –save

loading 基础组件的开发

1
2
3
4
5
.loading-container
position: absolute
width: 100%
top: 50%
transform: translateY(-50%)

PHP 学习框架

入门概述(Introduction)

PHP 是一种被广泛应用的开放源代码的多用途脚本语言,它可嵌入到 HTML中,尤其适合 web 开发。

版本迭代(Version Iteration)

当前最新稳定版本是 PHP 7.1.X,相比 PHP 5.X 版本增加了不少新特性。PHP 引擎也大部分被重写,PHP 的运行速度也提到了极大的提升。建议使用 PHP 7.1.X 作为开发和生产的环境。

注释与代码分割(Comments & Split)

注释的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
// 注释方式一
/* 注释方式二 */
/**
* 注释方式三
*
* @param string name
*
* @return string
*/

代码分割的代码如下:

1
2
3
4
5
<?php
require("../path/file.php");
require_once("../path/file.php");
include("../path/file.php");
include_once("../path/file.php");

require()、require_once()、include()、include_once() 的异同:

  • 运行时
    • require()require_once()include()include_once() 的区别是带 once 后缀的会判断在这个文件之前是否已经加载过了文件,避免重复加载。
    • require()require_once() 加载文件时,如果出错,将会产生一个 E_COMPILE_ERROR 级别的错误,脚本将会终止运行。
    • include()include_once() 加载文件时,如果出错,只会产生一个 E_WARNING 级别的警告,脚本将会继续运行。
  • 性能上
    • include()include_once() 执行时,文件每次都要进行读取和评估。
    • require()require_once() 执行时,文件只处理一次。
    • include_once()require_once() 是在 include()require() 的基础上进步一步封装。
  • 返回值
    • 如果加载文件内使用 return 返回,那么 require()``、require_once() 加载文件成功会有该文件 return 值的返回,失败会产生致命错误。
    • 如果加载文件内使用 return 返回,那么 include()include_once() 加载文件成功会有该文件 return 值的返回,失败会抛出一个警告并返回 boolean(false)
    • 如果加载文件内没有使用 return 返回,那么 require()``、require_once()include()include_once() 加载文件成功会返回 int(1),失败分别会产生致命错误和抛出警告并返回 boolead(false)

构建与依赖管理(Builder & Dependence Manager)

PEAR

PEAR 是一个常用的依赖包管理器。PEAR 需要扩展包有专属的结构, 开发者在开发扩展包的时候要提前考虑为 PEAR 定制, 否则后面将无法使用 PEAR。

PEAR 安装扩展包的时候, 是全局安装的, 意味着一旦安装了某个扩展包, 同一台服务器上的所有项目都能用上, 当然, 好处是当多个项目共同使用同一个扩展包的同一个版本, 坏处是如果你需要使用不同版本的话, 就会产生冲突.

Composer 与 Packagist

Composer 是另一个常用的依赖包管理器。在 composer.json 文件中列出你项目所需的依赖包,加上一点简单的命令,Composer 将会自动帮你下载并设置你的项目依赖。Composer 有点类似于 Node.js 里的 NPM,或者 Ruby 世界里的 Bundler。

Packagist 是一个 Composer 官方的依赖包仓库,可以在 Packagist 上寻找想要的 PHP 依赖包。

语法基础(Syntax)

表达式(Expression)

变量(Variable)
  • 变量声明(Variable Declaration)
    • 基础变量
    • 预定义变量
    • 变量范围
    • 可变变量
    • 来自 PHP 之外的变量
  • 变量提升(Variable Hoisting)
  • 常量声明(Constants Declaration)
    • 语法
    • 预定义常量(魔术常量)

变量与常量声明(Variable Declaration)

1.基础变量

PHP 中的变量用一个美元符号后面跟变量名来表示。变量名是区分大小写的。
变量名与 PHP 中其它的标签一样遵循相同的规则。一个有效的变量名由字母或者下划线开头,后面跟上任意数量的字母,数字,或者下划线。按照正常的正则表达式,它将被表述为:‘[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff]*’

1
2
3
4
5
6
7
8
9
10
11
<?php
$username = "Luis Edware"; // 通过
$_username = "Luis Edware"; // 通过
$username1 = "a1"; // 通过
$user.name = "LuisEdware"; // 报错
$用户名称 = "七月十五九月初七"; // 通过
$UserName = "Ann Eason"; // 通过
$userName = "Leon"; // 通过
$user_name = "Cissy"; // 通过
$1 = 100; // 报错
$_100 = 100; // 通过

2.预定义变量

PHP 提供了大量的预定义变量。由于许多变量依赖于运行的服务器的版本和设置,及其它因素,所以并没有详细的说明文档。一些预定义变量在 PHP 以命令行形式运行时并不生效。PHP 中的许多预定义变量都是“超全局”,这意味它们在一个脚本的全部作用域中都可用。

  • $GLOBALS:获取全局变量
  • $_SERVER:获取服务器相关信息
  • $_GET:获取 HTTP GET 请求数据
  • $_POST:获取 HTTP POST 请求数据
  • $_FILES:获取 HTTP 上传文件请求数据
  • $_COOKIE:获取 COOKIE 数据
  • $_SESSION:获取 SESSION 数据
  • $_REQUEST:获取 HTTP 请求数据
1
2
3
4
<?php
foreach($_SERVER as $key => $server){
echo $key."\n";
}

3.变量范围

变量的范围即它定义的上下文背景(也就是它的生效范围)。大部分的 PHP 变量只有一个单独的范围。可以使用 global 关键字声明全局变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
$a = 1;
$b = 2;
function Sum()
{
global $a, $b;
$b = $a + $b;
}
Sum();
echo $b;
?>
<?php
$a = 1;
$b = 2;
function Sum()
{
// $GLOBALS 是一个关联数组,每一个变量为一个元素,键名对应变量名,值对应变量的内容
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}
Sum();
echo $b;
?>

也可以使用 static 关键字在声明静态变量。

1
2
3
4
5
6
7
8
9
10
11
<?php
function test(){
static $a = 0;
echo $a;
$a++;
}
test();
test();
test();
test(); // 输出结果为:0123

4.可变变量

有时候使用可变变量名是很方便的。就是说,一个变量的变量名可以动态的设置和使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$Bar = "a";
$Foo = "Bar";
$World = "Foo";
$Hello = "World";
$a = "Hello";
$a; //Returns Hello
$$a; //Returns World
$$$a; //Returns Foo
$$$$a; //Returns Bar
$$$$$a; //Returns a
$$$$$$a; //Returns Hello
$$$$$$$a; //Returns World

5.来自 PHP 之外的变量

  • HTML 表单(GET 和 POST)
    • 通过预定义变量 $_GET$_POST 获取。
    • 变量名中的点和空格被转换成下划线。例如 <input name="a.b" /> 变成了 $_REQUEST["a_b"]
  • HTTP Cookies
    • 如果要将多个值赋给一个 cookie 变量,必须将其赋成数组。

变量提升(Variable Hoisting)

因为 PHP 区别全局作用域、函数作用域和块级作用域,PHP 不存在变量提升这个特性。

1
2
3
4
5
6
7
8
<?php
$name = 10;
function test(){
echo $params;
$params = "Luis Edware";
}
test($name);

常量声明(Constants Declaration)

1.语法

可以用 define() 函数来定义常量,在 PHP 5.3.0 以后,可以使用 const 关键字在类定义之外定义常量。一个常量一旦被定义,就不能再改变或者取消定义。

1
2
3
4
5
6
7
8
<?php
define("LUIS", "Luis");
const EDWARE = "Edware";
echo LUIS.' '.EDWARE."\n";
const ARR = ['luis', 'edware'];
var_dump(ARR);

2.预定义常量(魔术常量)

  • __LINE__:文件中的当前行号。
  • __FILE__:文件的完整路径和文件名。
  • __DIR__:文件所在的目录。
  • __FUNCTION__:返回函数被定义时的名字(区分大小写)。
  • __CLASS__:返回该类被定义时的名字(区分大小写)。
  • __TRAIT__:返回 trait 被定义时的名字(区分大小写)。
  • __METHOD__:返回该方法被定义时的名字(区分大小写)。
  • __NAMESPACE__:当前命名空间的名称(区分大小写)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
namespace LuisEdware\Code;
$line = __LINE__."\n";
$filePath = __FILE__."\n";
$dirPath = __DIR__."\n";
function testFunc(){
return __FUNCTION__."\n";
}
trait TestTrait
{
public function testTrait(){
return __TRAIT__."\n";
}
}
class TestClass
{
use TestTrait;
public function __construct(){
return __CLASS__."\n";
}
public function testMethod(){
return __METHOD__."\n";
}
public function testNameSpace(){
return __NAMESPACE__."\n";
}
}
echo $line;
echo $filePath;
echo $dirPath;
echo testFunc();
$class = new TestClass;
echo $class->testMethod();
echo $class->testTrait();
echo $class->testNameSpace();
变量赋值(Assignment)
  • 传值与传引用(Pass-by-Value & Pass-by-Reference)
  • 连续赋值(Continuous Assignment)
  • 解构赋值(Destructuring Assignment)
  • 复合类型拷贝(Copy Composite DataTypes)

1.传值与传引用(Pass-by-Value & Pass-by-Reference)

PHP 支持四种标量值(标量值不能拆分为更小的单元):整型值(integer)、浮点数值(float)、字符串值(string)和布尔值(boolean)。PHP 也支持两种复合类型:数组(Array)和对象(Object)。PHP 还支持两种特殊类型:资源(resource)和空(null)

PHP 变量默认总是传值赋值。那也就是说,当将一个表达式的值赋予一个变量时,整个原始表达式的值被赋值到目标变量。这意味着,例如,当一个变量的值赋予另外一个变量时,改变其中一个变量的值,将不会影响到另外一个变量。PHP 也提供了另外一种方式给变量赋值:引用赋值。这意味着新的变量简单的引用了原始变量。改动新的变量将影响到原始变量,反之亦然。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$foo = 'Bob';
$bar = &$foo;
$bar = "My name is $bar";
echo $bar,$foo;
// 非法的引用赋值;引用没有名字的表达式
$bar = &(24 * 7);
// 非法的引用赋值;只有有名字的变量才可以引用赋值
function test()
{
return 25;
}
$bar = &test();

2.连续赋值(Continuous Assignment)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
$a = $b = $c = 200;
var_dump($a);
var_dump($b);
var_dump($c);
$foo = $bar = $baz = "LuisEdware\n";
echo $foo, $bar, $baz;
$arr1 = $arr2 = $arr3 = ['apple','baidu','coding'];
var_dump($arr1);
var_dump($arr2);
var_dump($arr3);
// 可以通过 () 来改变运算符的优先级
$a = ($b = 4) + 5;
echo $a."\n";
// 组合赋值
$a .= $b .= "foo";
echo $a."\n";
// 不用第三个变量,交换两个变量的值
$a = 666;
$b = 233;
$a ^= $b ^= $a ^= $b;
echo $a."\n",$b;

3.解构赋值(Destructuring Assignment)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$string = "七月十五九月初七,十二月初一九月二十九";
$arr = [
["id" => 1, "name" => 'Luis'],
["id" => 2, "name" => 'Edware'],
];
// list() style
list($foo, $bar) = explode(',', $string);
echo $foo."\n", $bar."\n";
list("id" => $id1, "name" => $name1) = $arr[0];
echo $id1." => ", $name1."\n";
// [] style
[$a, $b] = explode(',', $string);
echo $a."\n", $b."\n";
["id" => $id2, "name" => $name2] = $arr[1];
echo $id2." => ", $name2."\n";

4.复合类型拷贝(Copy Composite DataTypes)

对象复制可以通过 clone 关键字来完成,对象中的 __clone() 方法不能被直接调用。当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。

注意: 引用和拷贝的区别是:拷贝是将原来的变量内容复制下来,拷贝后的变量与原来的变量使用各自的内存,互不干扰。
引用相当于是变量的别名,其实就是用不同的名字访问同一个变量内容。当改变其中一个变量的值时,另一个也跟着发生变化。

对于 PHP 对象来说,简单的赋值并不能实现克隆,因为对象是通过伪引用传递的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Example{
private $a = 1;
public function plus() {
$this->a ++;
}
public function foo() {
echo "a={$this->a}\n";
}
}
$obj = new Example;
$obj->foo();
$obj2 = $obj;
$obj2->plus();
$obj2->foo(); // 输出a=2
$obj->foo(); // 输出a=2

PHP 提供了 clone 关键字来实现对象的克隆,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Example{
private $a = 1;
public function plus() {
$this->a ++;
}
public function foo() {
echo "a={$this->a}\n";
}
}
$obj = new Example();
$obj2 = clone $obj;
$obj->foo();
$obj2->plus();
$obj2->foo();

当复制完成时, 如果定义了 __clone() 方法, 则新创建的对象中的 __clone() 方法会被调用,
可用于修改属性的值,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
class Example{
private $a = 1;
private $instance_no; // instance 编号
static public $all = 0; // instance 总数
public function foo() {
echo "a={$this->a}";
}
public function bar() {
echo "instance={$this->instance_no}";
}
public function __construct() {
// 实例号+1
$this->instance_no = ++self::$all;
}
public function __clone() {
// 实例号+1
$this->instance_no = ++self::$all;
}
}
$obj = new Example();
$obj->foo();
$obj->bar();
$obj2 = clone $obj;
$obj2->foo();
$obj2->bar();

在 PHP 中,克隆会把对象的所有属性进行克隆,但是如果一个对象中包含引用属性,那么克隆之后,在新生成的对象中这个属性仍然是
指向于原来变量的引用,这个成为浅克隆,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
class Example{
private $a = 1;
public $obj;
public function plus() {
$this->a ++;
}
public function foo() {
echo "a={$this->a}\n";
}
}
class Example2{
public $b = 1;
public function plus() {
$this->b ++;
}
public function foo() {
echo "b={$this->b}\n";
}
}
$obj = new Example;
$obj->obj = new Example2;
$obj2 = clone $obj;
// 修改对象的普通属性
$obj->plus();
$obj->foo(); // 输出 a=2
$obj2->foo(); // 输出 a=1
// 修改对象的引用属性
$obj->obj->plus();
$obj->obj->foo(); // 输出 b=2
$obj2->obj->foo(); // 输出 b=2

PHP 如果需要实现深克隆,需要自己实现对引用属性的完全复制,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php
class Example{
private $a = 1;
public $obj;
public function plus() {
$this->a ++;
}
public function foo() {
echo "a={$this->a}\n";
}
public function __clone(){
// 当存在对象数组的时候,可以使用以下代码实现深克隆
foreach($this as $key => $value){
if (is_object($$value) || (is_array($$value))) {
$this->{$key} = unserialize(serialize($$value));
}
}
}
}
class Example2{
public $b = 1;
public function plus() {
$this->b ++;
}
public function foo() {
echo "b={$this->b}\n";
}
}
$obj = new Example;
$obj->obj = new Example2;
$obj2 = clone $obj;
// 修改对象的普通属性
$obj->plus();
$obj->foo(); // 输出 a=2
$obj2->foo(); // 输出 a=1
// 修改对象的引用属性
$obj->obj->plus();
$obj->obj->foo(); // 输出 b=2
$obj2->obj->foo(); // 输出 b=1

运算符(Operator)

运算符是可以通过给出的一或多个值(用编程行话来说,表达式)来产生另一个值(因而整个结构成为一个表达式)的东西。

  • 基本运算符(Basic Operators)
    • 算术运算符(Assignment Operator)
    • 比较运算符(Comparison Operator)
    • 错误控制运算符(Error Control Operator)
    • 执行运算符(Execution Operator)
    • 递增/递减运算符(Incrementing / Decrementing Operator)
    • 逻辑运算符(Logical Operator)
    • 字符串运算符(String Operator)
    • 数组运算符(Array operator)
    • 类型运算符(Type Operator)
  • 位运算符(Bit Operator)
  • 运算符重载(Operator Overloading)

1.基本运算符(Basic Operators)

1.1.算术运算符(Assignment Operator)

例子 名称 结果
-$a 取反 $a 的负值。
$a + $b 加法 $a 和 $b 的和。
$a - $b 减法 $a 和 $b 的差。
$a * $b 乘法 $a 和 $b 的积。
$a / $b 除法 $a 除以 $b 的商。
$a % $b 取模 $a 除以 $b 的余数。
$a ** $b $a 的 $b 次平方

1.2.比较运算符(Comparison Operator)

例子 名称 结果
$a == $b 等于 TRUE,如果类型转换后 $a 等于 $b。
$a === $b 全等 TRUE,如果 $a 等于 $b,并且它们的类型也相同。
$a != $b 不等 TRUE,如果类型转换后 $a 不等于 $b。
$a <> $b 不等 TRUE,如果类型转换后 $a 不等于 $b。
$a !== $b 不全等 TRUE,如果 $a 不等于 $b,或者它们的类型不同。
$a < $b 小与 TRUE,如果 $a 严格小于 $b。
$a > $b 大于 TRUE,如果 $a 严格大于 $b。
$a <= $b 小于等于 TRUE,如果 $a 小于或者等于 $b。
$a >= $b 大于等于 TRUE,如果 $a 大于或者等于 $b。
$a <=> $b 结合比较运算符 当 $a 小于、等于、大于 $b 时 分别返回一个小于、等于、大于 0 的 integer 值。 PHP 7 开始提供.
$a ?? $b ?? $c NULL 合并操作符 从左往右第一个存在且不为 NULL 的操作数。如果都没有定义且不为 NULL,则返回 NULL。PHP 7 开始提供。

1.3.错误控制运算符(Error Control Operator)

PHP 支持一个错误控制运算符:@。当将其放置在一个 PHP 表达式之前,该表达式可能产生的任何错误信息都被忽略掉。

1.4.执行运算符(Execution Operator)

PHP 支持一个执行运算符:反引号(``)。注意这不是单引号!PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回(即,可以赋给一个变量而不是简单地丢弃到标准输出)。使用反引号运算符“`”的效果与函数 shell_exec() 相同。

1
2
3
4
<?php
$output = `ls -al`;
echo "<pre>$output</pre>";
?>

1.5.递增/递减运算符(Incrementing / Decrementing Operator)

例子 名称 效果
++$a 前加 $a 的值加一,然后返回 $a。
$a++ 后加 返回 $a,然后将 $a 的值加一。
–$a 前减 $a 的值减一, 然后返回 $a。
$a– 后减 返回 $a,然后将 $a 的值减一。

一个简单的示例脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
echo "<h3>Postincrement</h3>";
$a = 5;
echo "Should be 5: " . $a++ . "\n";
echo "Should be 6: " . $a . "\n";
echo "<h3>Preincrement</h3>";
$a = 5;
echo "Should be 6: " . ++$a . "\n";
echo "Should be 6: " . $a . "\n";
echo "<h3>Postdecrement</h3>";
$a = 5;
echo "Should be 5: " . $a-- . "\n";
echo "Should be 4: " . $a . "\n";
echo "<h3>Predecrement</h3>";
$a = 5;
echo "Should be 4: " . --$a . "\n";
echo "Should be 4: " . $a . "\n";

1.6.逻辑运算符(Logical Operator)

例子 名称 结果
$a and $b And(逻辑与) TRUE,如果 $a 和 $b 都为 TRUE。
$a or $b Or(逻辑或) TRUE,如果 $a 或 $b 任一为 TRUE。
$a xor $b Xor(逻辑异或) TRUE,如果 $a 或 $b 任一为 TRUE,但不同时是。
! $a Not(逻辑非) TRUE,如果 $a 不为 TRUE。
$a && $b And(逻辑与) TRUE,如果 $a 和 $b 都为 TRUE。
$a $b Or(逻辑或) TRUE,如果 $a 或 $b 任一为 TRUE。

1.7.字符串运算符(String Operator)

有两个字符串(string)运算符。第一个是连接运算符(“.”),它返回其左右参数连接后的字符串。
第二个是连接赋值运算符(“.=”),它将右边参数附加到左边的参数之后。代码如下:

1
2
3
4
5
6
<?php
$a = "Hello ";
$b = $a . "World!"; // now $b contains "Hello World!"
$a = "Hello ";
$a .= "World!"; // now $a contains "Hello World!"

1.8.数组运算符(Array operator)

例子 名称 结果
$a + $b 联合 $a 和 $b 的联合。
$a == $b 相等 如果 $a 和 $b 具有相同的键/值对则为 TRUE。
$a === $b 全等 如果 $a 和 $b 具有相同的键/值对并且顺序和类型都相同则为 TRUE。
$a != $b 不等 如果 $a 不等于 $b 则为 TRUE。
$a <> $b 不等 如果 $a 不等于 $b 则为 TRUE。
$a !== $b 不全等 如果 $a 不全等于 $b 则为 TRUE。

1.9.对象类型运算符(Object Type Operator)

instanceof 用于确定一个 PHP 变量是否属于某一类 class 的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class ParentClass
{
}
class MyClass extends ParentClass
{
}
$a = new MyClass;
var_dump($a instanceof MyClass);
var_dump($a instanceof ParentClass);

2.位运算符(Bit Operator)

例子 名称 结果
$a & $b And(按位与) 将把 $a 和 $b 中都为 1 的位设为 1。
$a $b Or(按位或) 将把 $a 和 $b 中任何一个为 1 的位设为 1。
$a ^ $b Xor(按位异或) 将把 $a 和 $b 中一个为 1 另一个为 0 的位设为 1。
~ $a Not(按位取反) 将 $a 中为 0 的位设为 1,反之亦然。
$a << $b Shift left(左移) 将 $a 中的位向左移动 $b 次(每一次移动都表示“乘以 2”)。
$a >> $b Shift right(右移) 将 $a 中的位向右移动 $b 次(每一次移动都表示“除以 2”)。

3.运算符重载(Operator Overloading)

在计算机程序设计中,运算符重载是多态的一种。这里,运算符被当作多态函数,他们的行为随着其参数类型的不同而不同。
简而言之,运算符重载使程序员能够根据运算子类型的不同来决定运算符功能的不同。PHP 支持运算符重载。

PHP 5.6 之后,GMP 支持运算符重载,并且造型成数值类型,这使得使用 GMP 的代码更加直观。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$a = gmp_init(42);
$b = gmp_init(17);
if (version_compare(PHP_VERSION, '5.6', '<')) {
echo gmp_intval(gmp_add($a, $b)), PHP_EOL;
echo gmp_intval(gmp_add($a, 17)), PHP_EOL;
echo gmp_intval(gmp_add(42, $b)), PHP_EOL;
} else {
echo $a + $b, PHP_EOL;
echo $a + 17, PHP_EOL;
echo 42 + $b, PHP_EOL;
}

4.运算符优先级(Operator Precedence)

1
2
递增/递减 > ! > 算术运算符 > 大小比较 > (不)相等比较 > 引用 >
位运算符 ^ > 位运算符 |> 逻辑与 > 逻辑或 > 三目 > 赋值 > and > xor > or

  • 算术运算符(Assignment Operator)
    • 比较运算符(Comparison Operator)
    • 错误控制运算符(Error Control Operator)
    • 执行运算符(Execution Operator)
    • 递增/递减运算符(Incrementing / Decrementing Operator)
    • 逻辑运算符(Logical Operator)
    • 字符串运算符(String Operator)
    • 数组运算符(Array operator)
    • 类型运算符(Type Operator)
匿名函数与闭包(Anonymous functions & Closure)
  • 闭包和匿名函数在 PHP 5.3 中被引入。
  • 闭包是指在创建时封装函数周围状态的函数,即使闭包所在的环境不存在了,闭包封装的状态依然存在。
  • 匿名函数其实就是没有名称的函数,匿名函数可以赋值给变量,还能像其他任何 PHP 对象那样传递。最经常用作回调函数参数的值。
  • 匿名函数和闭包目前是通过 Closure 类来实现的。

创建简单的闭包

1
2
3
4
5
6
<?php
$closure = function($name){
return sprintf('hello %s', $name);
};
echo $closure("Luis Edware");

之所以能够调用 $closure 变量,是因为这个变量的值是一个闭包,而且闭包对象实现了 __invoke() 魔术方法。
只要变量名后有(),PHP 就会查找并调用 __invoke() 方法。

1
2
3
4
5
6
7
8
9
10
<?php
class testClosureClass
{
public function __invoke()
{
echo "Hello World";
}
}
$closure = new testClosureClass;
$closure(); // 输出 Hello World

PHP 闭包一般当做函数或方法的回调来使用,如下:

1
2
3
4
5
<?php
$numbersPlusOne = array_map(function($number){
return $number+1;
},[1,2,3]);
var_export($numbersPlusOne);

PHP 闭包不会像 JavaScript 闭包那样自动封装应用的状态。
在 PHP 中,必须手动调用闭包对象的 bindTo() 方法或者使用 use 关键字,把状态附加到 PHP 闭包上。

以下是使用 use 关键字附加闭包的状态

1
2
3
4
5
6
7
8
9
10
11
12
<?php
function enclosePerson($name)
{
return function ($doCommand) use ($name) {
return sprintf('%s , %s', $name, $doCommand);
}
}
//把字符串“Clay”封装在闭包中
$clay = enclosePerson('Clay');
//传入参数,调用闭包
echo $clay('get me sweat tea!'); // Clay, get me sweat tea!

以下是使用 bindTo() 方法附加闭包的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
class App
{
protected $routes = [];
protected $responseStatus = '200 OK';
protected $responseContentType = 'text/html';
protected $responseBody = 'Hello world';
public function addRoute($routePath, $routeCallback)
{
$this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__);
}
public function dispatch($currentPath)
{
foreach($this->routes as $routePath => $callback) {
if ($routePath === $currentPath) {
$callback();
}
}
header('HTTP/1.1' . $this->responseStatus);
header('Content-type: ' . $this->responseContentType);
header('Content-length: ' . mb_strlen($this->responseBody));
echo $this->responseBody;
}
}
$app = new App();
$app->addRoute('/users/1', function () {
$this->responseContentType = 'application/json;charset=utf8';
$this->responseBody = '{"name" : "Luis Edware", "phone" : "14089950522"}';
});
$app->dispatch('/users/1');

我们可以通过 bindTo() 方法的第一个参数把 Closure 对象的内部状态绑定到其他对象上。
可以通过 bindTo() 方法的第二个参数指定绑定闭包的那个对象所属的 PHP 类。

惰性求值(Lazy Evaluation)

// TODO

流程控制和异常处理(ControlFlow)

分支选择(Branch)

PHP 提供了一些流程控制的替代语法,包括 ifwhileforforeachswitch
替代语法的基本形式是把左花括号 { 换成冒号 :,把右花括号 } 分别换成 endif;endwhile;endfor;endforeach; 以及 endswitch;

  • if
  • switch

if 结构是很多语言包括 PHP 在内最重要的特性之一,它允许按照条件执行代码片段。PHP 的 if 结构和 C 语言相似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
$a = 1;
if ($a > $b)
echo "a is bigger than b";
if ($a > $b) {
echo "a is bigger than b";
$b = $a;
}
if ($a > $b) {
echo "a is greater than b";
} else {
echo "a is NOT greater than b";
}
if ($a > $b) {
echo "a is bigger than b";
} elseif ($a == $b) {
echo "a is equal to b";
} else {
echo "a is smaller than b";
}
if($a > $b):
echo $a." is greater than ".$b;
elseif($a == $b): // 注意使用了一个单词的 elseif
echo $a." equals ".$b;
else:
echo $a." is neither greater than or equal to ".$b;
endif;
?>

php if 和 HTML / JavaScript / CSS 混合使用:

1
2
3
4
5
6
7
8
9
<?php if (condition): ?>
html code to run if condition is true
<?php else: ?>
html code to run if condition is false
<?php endif ?>

switch 语句类似于具有同一个表达式的一系列 if 语句。
很多场合下需要把同一个变量(或表达式)与很多不同的值比较,并根据它等于哪个值来执行不同的代码。switch 是松散比较。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
// 整型的 switch 结构
switch ($integer) {
case 0:
echo "integer equals 0";
break;
case 1:
echo "integer equals 1";
break;
case 2:
echo "integer equals 2";
break;
}
// 字符串型的 switch 结构
switch ($string) {
case "apple":
echo "string is apple";
break;
case "bar":
echo "string is bar";
break;
case "cake":
echo "string is cake";
break;
}

switch 语句一行接一行地执行(实际上是语句接语句)。开始时没有代码被执行。仅当一个 case 语句中的值和 switch 表达式的值匹配时 PHP 才开始执行语句,直到 switch 的程序段结束或者遇到第一个 break 语句为止。如果不在 case 的语句段最后写上 break 的话,PHP 将继续执行下一个 case 中的语句段。

1
2
3
4
5
6
7
8
9
<?php
switch ($i) {
case 0:
echo "i equals 0";
case 1:
echo "i equals 1";
case 2:
echo "i equals 2";
}

这里如果 $i 等于 0,PHP 将执行所有的 echo 语句!如果 $i 等于 1,PHP 将执行后面两条 echo 语句。只有当 $i 等于 2 时,才会得到“预期”的结果——只显示“i equals 2”。

循环(Loop)
  • for
  • foreach
  • while
  • do-while
  • break / continue
  • goto

1.for

for 循环是 PHP 中最复杂的循环结构。for 循环的语法如下:

1
2
for (expr1; expr2; expr3)
statement

  • expr1 在循环开始前无条件求值(并执行)一次。
  • expr2 在每次循环开始前求值。如果值为 true,则继续循环,执行嵌套的循环语句。如果值为 false,则终止循环。
  • expr3 在每次循环之后被求值(并执行)。

for 示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
// Example 1
for ($i = 1; $i <= 10; $i++) {
echo $i;
}
// Example 2
for ($i = 1; ; $i++) {
if ($i > 10) {
break;
}
echo $i;
}
// Example 3
for ($i = 1, $j = 0; $i <= 10; $j += $i, print $i, $i++);
// Example 4
$people = [
['name' => 'Kalle', 'salt' => 856412],
['name' => 'Pierre', 'salt' => 215863],
];
for($i = 0, $size = count($people); $i < $size; ++$i)
{
$people[$i]['salt'] = rand(000000, 999999);
}

2.foreach

foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,
如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。

foreach 的示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?php
// Example 1 while 和 foreach 实现相同功能的代码
$arr = array("apple", "baidu", "coding");
reset($arr);
while (list(, $value) = each($arr)) {
echo "Value: $value<br>\n";
}
foreach ($arr as $value) {
echo "Value: $value<br />\n";
}
// Example 2 while 和 foreach 实现相同功能的代码
$arr = array("one", "two", "three");
reset($arr);
while (list($key, $value) = each($arr)) {
echo "Key: $key; Value: $value<br />\n";
}
foreach ($arr as $key => $value) {
echo "Key: $key; Value: $value<br />\n";
}
// Example 3 多位数组遍历
$a = array();
$a[0][0] = "a";
$a[0][1] = "b";
$a[1][0] = "y";
$a[1][1] = "z";
foreach ($a as $v1) {
foreach ($v1 as $v2) {
echo "$v2\n";
}
}
// Example 4 用 list() 给嵌套的数组解包 1
$array = [
[1, 2],
[3, 4],
];
foreach ($array as list($a, $b)) {
echo "A: $a; B: $b\n";
}
// Example 5 用 list() 给嵌套的数组解包 2
$array = [
[
'username' => "Luis.Edward",
'phone' => "13405871293",
"address" => "广东省中山市",
],
[
'username' => "Ann.Eason",
'phone' => "15048773920",
"address" => "广东省中山市",
]
];
foreach($array as list('username' => $username,'phone' => $phone)){
echo "$username 's phone is $phone\n";
}
// Example 6 使用 foreach 的替代语法
$names = ['Luis', 'Edware', 'Ann', 'Eason'];
foreach($names as $value):
echo "$value\n";
endforeach;

3.while

while 循环是 PHP 中最简单的循环类型。只要 while 表达式的值为 true 就重复执行嵌套中的循环语句。
表达式的值在每次开始循环时检查,所以即使这个值在循环语句中改变了,语句也不会停止执行,直到本次循环结束。

while 的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// Example 1
$i = 1;
while ($i <= 10) {
echo $i++;
}
// Example 2
$i = 1;
while ($i <= 10):
print $i;
$i++;
endwhile;

4.do-while

do-while 循环和 while 循环非常相似,区别在于表达式的值是在每次循环结束时检查而不是开始时。
和一般的 while 循环主要的区别是 do-while 的循环语句保证会执行一次

do-while 的实例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
// Example 1 do-while 只执行一次
$i = 0;
do {
echo $i;
} while ($i > 0);
// Example 2 do-while 执行 5 次
$i = 0;
do {
$i++;
if($i > 5){
break;
}
echo "i is not big enough\n";
} while(1);

5.break / continue

  • break 结束当前 forforeachwhiledo-while 或者 switch 结构的执行。
  • continueforforeachwhiledo-while 或者 switch 结构用用来跳过本次循环中剩余的代码并在条件求值为真时开始执行下一次循环。

break / continue 的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
$arr = array('one', 'two', 'three', 'four', 'stop', 'five');
while (list (, $val) = each($arr)) {
if($val != 'stop'){
continue;
}
if ($val == 'stop') {
echo "$val\n";
break; /* You could also write 'break 1;' here. */
}
echo "$val<br />\n";
}
// 多层 continue
$i = 0;
while ($i++ < 5) {
echo "Outer<br />\n";
while (1) {
echo "Middle<br />\n";
while (1) {
echo "Inner<br />\n";
continue 3;
}
echo "This never gets output.<br />\n";
}
echo "Neither does this.<br />\n";
}

6.goto

goto 操作符可以用来跳转到程序中的另一位置。该目标位置可以用目标名称加上冒号来标记,而跳转指令是 goto 之后接上目标位置的标记。PHP 中的 goto 有一定限制,目标位置只能位于同一个文件和作用域,也就是说无法跳出一个函数或类方法,也无法跳入到另一个函数。也无法跳入到任何循环或者 switch 结构中。

goto 的实例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
// Example 1
goto a;
echo 'Foo';
a:
echo 'Bar';
// Example 2
for($i=0,$j=50; $i<100; $i++) {
while($j--) {
if($j==17) goto end;
}
}
echo "i = $i";
end:
echo 'j hit 17';

错误处理(ErrorHandling)
  • 错误和异常定义与类型(Error & Exception)
  • 错误和异常抛出(Throw)
  • 错误和异常捕获(Try-Catch-Finally)
  • 错误和异常的部署配置(Deploy & Configuration)

1.错误和异常定义与类型(Error & Exception)

PHP 7 改变了大多数错误的报告方式。不同于传统(PHP 5)的错误报告机制,现在大多数错误被作为 Error 异常抛出。而异常是 Exception 类的对象,
在遇到无法修复的状况时抛出。例如,远程 API 无响应,数据库查询失败,或者无法满足前置条件。这些状况被归纳为异常状况。出现问题时,异常常用于主动出击,委托职责,异常还可以用于防守,预测潜在的问题,减轻其影响。

这种 Error 异常可以像 Exception 异常一样被第一个匹配的 try / catch 块所捕获。如果没有匹配的 catch 块,
则调用异常处理函数(事先通过 set_exception_handler() 注册)进行处理。
如果尚未注册异常处理函数,则按照传统方式处理:被报告为一个致命错误(Fatal Error)。

Error 类并非继承自 Exception 类,所以不能用 catch (Exception $e) { ... } 来捕获 Error。在 PHP 7 以后,可以使用 Throwable 类来捕获 Error,或者
你也可以用 catch (Error $e) { ... },或者通过注册异常处理函数( set_exception_handler())来捕获 Error

2.错误和异常抛出(Throw)

错误和异常实例化时可以赋值给变量,不过一定要把错误或异常抛出。抛出错误或异常后代码会立即停止执行,
后续的 PHP 代码都不会运行。抛出错误或异常的方式是使用 throw 关键字,后面要跟着要抛出的 Throwable 实例

1
2
3
4
5
6
7
<?php
// Throwable 能够捕获 Exception 和 Error
throw new Throwable("Something went wrong. Time for lunch!");
// Exception 能够捕获 Exception,不能捕获 Error
throw new Exception("Something went wrong. Time for dinner!");
// Error 能够捕获 Error,不能捕获 Exception
throw new Error("Something went wrong. Time for Sleep!");

3.错误和异常捕获(Try-Catch-Finally)

拦截并处理潜在错误或异常的方式是,把可能抛出异常的代码放在 try / catch 块中,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
// Example 1
try {
$pdo = new PDO('mysql://host=wrong_host;dbname=wrong_name');
} catch (PDOException $e) {
$code = $e->getCode();
$message = $e->getMessage();
echo 'Something went wrong.Check back soon, please';
exit;
}
// Example 2
try {
throw new Exception("Not a PDO exception\n");
$pdo = new PDO('mysql://host=wrong_host;dbname=wrong_name');
} catch (PDOException $e) {
echo 'Caught PDO exception';
} catch (Throwable $t) {
//处理其他异常
echo $t->getMessage();
} finally {
//这里的代码始终都会执行
echo 'Always do this';
}

4.错误和异常的部署配置(Deploy & Configuration)

// TODO

函数(Function)

函数定义(Function Definition)

任何有效的 PHP 代码都有可能出现在函数内部,甚至包括其它函数和类定义。

函数名和 PHP 中的其它标识符命名规则相同。有效的函数名以字母或下划线打头,后面跟字母,数字或下划线。可以用正则表达式表示为:[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff]*

PHP 中的所有函数和类都具有全局作用域,可以定义在一个函数之内而在之外调用,反之亦然。

PHP 不支持函数重载,也不可能取消定义或者重定义已声明的函数。

PHP 函数示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
// Example #1 函数中的函数
function foo()
{
function bar()
{
echo "I don't exist until foo() is called.\n";
}
}
/* 现在还不能调用bar()函数,因为它还不存在 */
foo();
/* 现在可以调用bar()函数了,因为foo()函数
的执行使得bar()函数变为已定义的函数 */
bar();
// Example #2 递归函数
function recursion($a)
{
if($a < 20){
echo "$a\n";
recursion(++$a);
}
}
/* 要避免递归函数 / 方法调用超过 100 - 200 层,因为可能
会使堆栈崩溃从而使当前脚本终止。无限递归可视为编程错误 */

函数参数(Function Args)
  • 默认参数(Default Arguments)
  • 必要参数(Required Arguments)
  • 可变参数(Variable Arguments)

1.默认参数

函数可以定义 C++ 风格的标量参数默认值,默认值必须是常量表达式,
不能是诸如变量,类成员,或者函数调用等,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
// Example #1 标量类型默认参数
function makecoffee($type = "cappuccino")
{
return "Making a cup of $type.\n";
}
echo makecoffee(); // 输出 Making a cup of cappuccino.
echo makecoffee(null); // 输出 Making a cup of .
echo makecoffee("espresso"); // 输出 Making a cup of espresso.
// Example #2 非标量类型作为默认参数
function makecoffee($types = ["cappuccino"], $coffeeMaker = NULL)
{
$device = is_null($coffeeMaker) ? "hands" : $coffeeMaker;
return "Making a cup of ".join(", ", $types)." with $device.\n";
}
echo makecoffee();
echo makecoffee(["cappuccino", "lavazza"], "teapot");
// Example #3 默认参数类型严格模式
declare(strict_types=1);
function sum(int $a, int $b) {
return $a + $b;
}
try {
var_dump(sum(1, 2)); // 通过
var_dump(sum(1.5, 2.5)); // 报错
} catch (TypeError $e) {
echo 'Error: '.$e->getMessage();
}

2.必要参数

PHP 函数的必要参数示例代码如下:

1
2
3
4
5
6
7
8
9
<?php
// 使用该函数时,必须传递两个参数
$a = "Luis";
$b = " Edward";
function requireArgs($a, $b)
{
return $a.$b;
}
echo requireArgs($a, $b);

3.可变参数

PHP 在用户自定义函数中支持可变数量的参数列表。在 PHP 5.6 及以上的版本中,由 ... 语法实现;在 PHP 5.5 及更早版本中,使用函数 func_num_args()func_get_arg(),和 func_get_args() 来实现,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
// Example #1 使用 ... 访问函数参数
function sum(...$numbers) {
$acc = 0;
foreach ($numbers as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4);
// 使用 func_get_args() 访问函数参数
function sum() {
$acc = 0;
foreach (func_get_args() as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4);
// Example #2 使用 ... 提供函数参数
function add($a, $b) {
return $a + $b;
}
echo add(...[1, 2])."\n";
$a = [1, 2];
echo add(...$a);
// Example #3 类型声明可变参数
function total_intervals($unit, DateInterval ...$intervals) {
$time = 0;
foreach ($intervals as $interval) {
$time += $interval->$unit;
}
return $time;
}
$a = new DateInterval('P1D');
$b = new DateInterval('P2D');
echo total_intervals('d', $a, $b).' days';
// 报错
echo total_intervals('d', null);

函数调用(Function Call)
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// 普通函数调用
function test_normal_function()
{
return true;
}
test_normal_function();
// 匿名函数调用
$closure = function(){
return true;
};
$closure();
装饰器(Decorator)

PHP 目前不存在装饰器的语法糖,需要通过手动编写或设计模式来实现装饰器,实例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
// Example #1 函数实现装饰器的效果
function text(){
echo "hello World";
}
function leet_text(){
echo text().", Luis Edward\n";
}
leet_text();
// Example #2 OOP 实现装饰器的效果
class Text {
protected $string;
/**
* @param string $string
*/
public function __construct($string) {
$this->string = $string;
}
public function __toString() {
return $this->string;
}
}
class LeetText {
protected $text;
/**
* @param Text $text A Text object.
*/
public function __construct($text) {
$this->text = $text;
}
public function __toString() {
return strtr($this->text->__toString(), 'eilto', '31170');
}
}
$text = new LeetText(new Text('Hello world'));
echo $text;

迭代器与生成器(Iterator & Generator)

参考文献

1.迭代器

PHP 迭代器的实现离不开一个无法被单独实现的基本抽象接口:Traversable (遍历接口),它必须由 IteratorAggregateIterator 接口实现。
Traversable 接口没有任何方法,它的作用仅仅是作为所有可遍历类的基本接口。

迭代是指反复执行一个过程,每执行一次叫做一次迭代。例如 foreach 遍历一个 array 变量,foreach 实际上在每一次迭代过程都会调用该对象的一个方法,让数组在自己内部进行一次变动(迭代),随后通过另一个方法取出当前数组对象的键和值。这样一个通过外部遍历其内部数据的对象就是一个迭代器对象,其遵循的统一的访问接口就是一个 迭代器接口

PHP 迭代器接口摘要:

1
2
3
4
5
6
7
8
9
<?php
Iterator extends Traversable
{
abstract public mixed current (void)
abstract public scalar key (void)
abstract public void next (void)
abstract public void rewind (void)
abstract public boolean valid (void)
}

  • Iterator::current:返回当前元素
  • Iterator::key:返回当前元素的键
  • Iterator::next:向前移动到下一个元素
  • Iterator::rewind:返回到迭代器的第一个元素
  • Iterator::valid:检查当前位置是否有效

PHP 迭代器的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?php
class myIterator implements Iterator
{
private $position = 0;
private $array = [
"firstElement",
"secondElement",
"lastElement",
];
public function __construct()
{
$this->position = 0;
}
function rewind()
{
var_dump(__METHOD__);
$this->position = 0;
}
function current()
{
var_dump(__METHOD__);
return $this->array[$this->position];
}
function key()
{
var_dump(__METHOD__);
return $this->position;
}
function next()
{
var_dump(__METHOD__);
++$this->position;
}
function valid()
{
var_dump(__METHOD__);
return isset($this->array[$this->position]);
}
}
$it = new myIterator;
foreach ($it as $key => $value) {
var_dump($key, $value);
echo "\n";
}

PHP 聚合式迭代器接口摘要:

1
2
3
4
5
6
<?php
IteratorAggregate extends Traversable
{
/* 获取一个外部迭代器 */
abstract public Traversable getIterator (void);
}

PHP 聚合式迭代器接口的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php
// Example #1
class myObject implements IteratorAggregate
{
public $property1 = "Public property one";
public $property2 = "Public property two";
public $property3 = "Public property three";
public function __construct() {
$this->property4 = "last property";
}
public function getIterator() {
return new ArrayIterator($this);
}
}
$obj = new myObject;
foreach($obj as $key => $value) {
echo "$key => $value\n";
}
// Example #2
class Collection implements IteratorAggregate
{
private $items = [];
public function __construct($items = [])
{
$this->items = $items;
}
public function getIterator()
{
return (function () {
reset($this->items);
while(list($key, $val) = each($this->items)) {
yield $key => $val;
}
})();
}
}
$data = [ 'A', 'B', 'C', 'D' ];
$collection = new Collection($data);
foreach ($collection as $key => $val) {
echo sprintf("[%s] => %s\n", $key, $val);
}

PHP 数组式访问(ArrayAccess)接口摘要:

1
2
3
4
5
6
7
8
<?php
ArrayAccess
{
abstract public boolean offsetExists (mixed $offset)
abstract public mixed offsetGet (mixed $offset)
abstract public void offsetSet (mixed $offset, mixed $value)
abstract public void offsetUnset (mixed $offset)
}

  • ArrayAccess::offsetExists:检查一个偏移位置是否存在
  • ArrayAccess::offsetGet:获取一个偏移位置的值
  • ArrayAccess::offsetSet:设置一个偏移位置的值
  • ArrayAccess::offsetUnset:复位一个偏移位置的值

PHP 数组式访问(ArrayAccess)接口的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<?php
// Example #1 Basic Usage
class obj implements arrayaccess
{
private $container = [];
public function __construct()
{
$this->container = [
"one" => 1,
"two" => 2,
"three" => 3,
];
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}
public function offsetGet($offset)
{
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
$obj = new obj;
var_dump(isset($obj["two"]));
var_dump($obj["two"]);
unset($obj["two"]);
var_dump(isset($obj["two"]));
$obj["two"] = "A value";
var_dump($obj["two"]);
$obj[] = 'Append 1';
$obj[] = 'Append 2';
$obj[] = 'Append 3';
print_r($obj);
// Example #2 使用访问数组的方式去访问对象
<?php
/**
* ArrayAndObjectAccess
* Yes you can access class as array and the same time as object
*
* @author Yousef Ismaeil <cliprz@gmail.com>
*/
class ArrayAndObjectAccess implements ArrayAccess
{
/**
* Data
*
* @var array
* @access private
*/
private $data = [];
/**
* Get a data by key
*
* @param $key string The key data to retrieve
*
* @access public
*/
public function &__get($key)
{
return $this->data[$key];
}
/**
* Assigns a value to the specified data
*
* @param $key string The data key to assign the value to
* @param $value mixed The value to set
*
* @access public
*/
public function __set($key, $value)
{
$this->data[$key] = $value;
}
/**
* Whether or not an data exists by key
*
* @param string An data key to check for
*
* @access public
* @return boolean
* @abstracting ArrayAccess
*/
public function __isset($key)
{
return isset($this->data[$key]);
}
/**
* Unset an data by key
*
* @param $key string The key to unset
*
* @access public
*/
public function __unset($key)
{
unset($this->data[$key]);
}
/**
* Assigns a value to the specified offset
*
* @param $offset string The offset to assign the value to
* @param mixed
*
* @access public
* @abstracting ArrayAccess
*/
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->data[] = $value;
} else {
$this->data[$offset] = $value;
}
}
/**
* Unset an offset
*
* @param $offset string The offset to unset
*
* @access public
* @abstracting ArrayAccess
*/
public function offsetUnset($offset)
{
if ($this->offsetExists($offset)) {
unset($this->data[$offset]);
}
}
/**
* Whether or not an offset exists
*
* @param $offset string An offset to check for
*
* @access public
* @return boolean
* @abstracting ArrayAccess
*/
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
/**
* Returns the value at specified offset
*
* @param $offset string The offset to retrieve
*
* @access public
* @return mixed
* @abstracting ArrayAccess
*/
public function offsetGet($offset)
{
return $this->offsetExists($offset) ? $this->data[$offset] : null;
}
}
$foo = new ArrayAndObjectAccess();
// Set data as array and object
$foo->firstName = 'Luis';
$foo->lastName = 'Edward';
// Call as object
echo 'firstName as object ' . $foo->firstName . "\n";
// Call as array
echo 'lastName as array ' . $foo['lastName'] . "\n";
// Reset as array
$foo['firstName'] = 'AnnEason';
$foo[] = 'Luis Edward';
echo $foo['firstName'] . "\n";
var_export($foo);

2.生成器

生成器提供了一种更容易的方法来实现简单对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。

生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,
或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是,
生成器可以根据需要 yield 多次,以便生成需要迭代的值。

一个简单的例子就是使用生成器来重新实现 range() 函数。 标准的 range() 函数需要在内存中生成一个数组包含每一个在它范围内的值,
然后返回该数组, 结果就是会产生多个很大的数组。 比如,调用 range(0, 1000000) 将导致内存占用超过 100 MB。

做为一种替代方法, 我们可以实现一个 xrange() 生成器, 只需要足够的内存来创建 Iterator 对象并在内部跟踪生成器的当前状态,这样只需要不到 1K 字节的内存,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
function xrange($start, $limit, $step = 1)
{
if ($start < $limit) {
if ($step <= 0) {
throw new LogicException('Step must be +ve');
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('Step must be -ve');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
/*
* 注意下面range()和xrange()输出的结果是一样的。
*/
echo 'Single digit odd numbers from range(): ';
foreach (range(1, 9, 2) as $number) {
echo "$number ";
}
echo "\n";
echo 'Single digit odd numbers from xrange(): ';
foreach (xrange(1, 9, 2) as $number) {
echo "$number ";
}

一个生成器函数看起来像一个普通的函数,不同的是普通函数返回一个值,而一个生成器可以 yield 生成许多它所需要的值。

当一个生成器被调用的时候,它返回一个可以被遍历的对象.当你遍历这个对象的时候,PHP 将会在每次需要值的时候调用生成器函数,
并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。

一旦不再需要产生更多的值,生成器函数可以简单退出,而调用生成器的代码还可以继续执行,就像一个数组已经被遍历完了。

生成器函数的核心是 yield 关键字。它最简单的调用形式看起来像一个 return 申明,不同之处在于普通 return 会返回值并终止函数的执行,
yield 会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
// Example #1 Basic Usage
function gen_one_to_three()
{
for ($i = 1; $i <= 3; $i++) {
//注意变量$i的值在不同的yield之间是保持传递的。
yield $i;
}
}
$generator = gen_one_to_three();
foreach ($generator as $value) {
echo "$value\n";
}
// Example #2 指定键名来生成值
$input = <<<'EOF'
1;PHP;Likes dollar signs
2;Python;Likes whitespace
3;Ruby;Likes blocks
EOF;
function input_parser($input)
{
foreach (explode("\n", $input) as $line) {
$value = explode(';', $line);
$key = array_shift($value);
yield $key => $value;
}
}
foreach (input_parser($input) as $id => $fields) {
echo "$id:\n";
echo " $fields[0]\n";
echo " $fields[1]\n";
}

在 PHP 7 中,生成器委托可以允许用户通过 yield from 关键字从其他生成器、数组或 Traverable 对象 yield 值。
然后外部生成器将会从内部生成器、Traverable 对象、数组 yield 所有值,直到所有值都被 yield 完毕,外部生成器才会继续执行。

如果生成器与 yield 一起使用,则表达式的 yield 将返回内部生成器返回的任何值。

PHP yield form 的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?php
// Example #1 Basic Usage of yield from
function count_to_ten()
{
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from seven_eight();
yield 9;
yield 10;
}
function seven_eight()
{
yield 7;
yield from eight();
}
function eight()
{
yield 8;
}
foreach (count_to_ten() as $num) {
echo "$num ";
}
// Example #2 yield from and return values
function count_to_ten2()
{
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from seven_eight();
return yield from nine_ten();
}
function seven_eight()
{
yield 7;
yield from eight();
}
function eight()
{
yield 8;
}
function nine_ten()
{
yield 9;
return 10;
}
$gen = count_to_ten();
foreach ($gen as $num) {
echo "$num ";
}
echo $gen->getReturn();

yield 会返回一个 Generator 对象,Generator 类实现了 Iterator 接口,
从本质上来讲,生成器也属于迭代器的一种。Generator 类摘要如下:

1
2
3
4
5
6
7
8
9
10
11
<?php
Generator implements Iterator {
public mixed current (void)
public mixed key (void)
public void next (void)
public void rewind (void)
public mixed send (mixed $value)
public void throw (Exception $exception)
public bool valid (void)
public void __wakeup (void)
}

  • Generator::current:返回当前产生的值
  • Generator::key:返回当前产生的键
  • Generator::next:生成器继续执行
  • Generator::rewind:重置迭代器
  • Generator::send:向生成器中传入一个值
  • Generator::throw:向生成器中抛入一个异常
  • Generator::valid:检查迭代器是否被关闭
  • Generator::__wakeup:序列化回调

类与对象(Class & Object)

类定义(Class Definition)
  • 类属性定义(Class Property Definition)
  • 类方法定义(Class Methods Definition)
    • 构造函数(Constructor Function)
    • 析构函数(Destructor Function)
    • Getter / Setter
  • 访问控制(Access Control)
    • 访问修饰符(Access Modifiers)
    • 私有数据(Private)
对象(Object)
  • 实例化(Instantiation)
  • 单例模式(Singleton Pattern)
  • this
类继承(Inheritance)
内部类(Inner Class)
反射/自省(Reflect)

输入输出流(IO Stream)

模块(Modularity)

数据结构(Data Structure)

类型与值判断(Type Judgement)

引用或值相等(Reference/Value Equality)
动态类型检查(Runtime Type Checking)
常用属性判断(Property Checking)
  • 空间占用(Size / Length)
  • 存在性(Existing)

基本类型(Basic)

数值类型(Numeric)
  • 随机数(Random)
  • 科学计算(Scientific Computing)
  • 类型转换(Type Conversion)
空类型(Nullable)
布尔类型(Bool)
可选类型(Optional)
枚举类型(Enum)

字符串类型(String)

增删复替(Manipulation)
  • 格式化字符串(Format)
索引遍历(Index & Traversal)
类型编码(type & encode)*
  • 字符集
  • URL/HTML
  • 拼音
模式匹配(Pattern Match)
  • 正则表达式(RegEx)
  • 字符串校验(Valid)
  • 模糊搜索(Fuzzy Search)

时间与日期(DateTime)

时间解析(Parse)
  • 时间戳(TimeStamp)
  • 时间日期字符串(DateTimeString)
  • 时区转换(TimeZone)
  • 时间比较(Comparison)
时间展示(Display)
  • 格式化(Format)
  • 相对格式(Relative Format)
  • 时长(Duration)
  • i18n(国际化)
时间操作(Manipulation)
  • 读取与设置(Get & Set)
  • 时间增减(Add & Subtract)
  • 时间偏移计算(Diff)
日历(Calendar)

序列类型(IndexedCollection)

数组(Array)
  • 增删复替(Manipulation)
  • 索引遍历(Index & Traversal)
  • 转换(Transform)
    • map/flatMap
    • reduce
    • filter
    • sort
集合(Set)

索引类型(KeyedCollection)

映射
  • 增删复替(Manipulation)
  • 索引遍历(Index & Traversal)

不可变对象(Immutable)

流(Stream)

序列化与反序列化(Serialization)

  • Json
  • XML
  • CSV

功能(Functionality)

存储(Storage)

缓存(Cache)
数据库(DataBase)
  • MySQL
  • Redis
  • MongoDB
文件系统(FileSystem)
  • FilePath(文件寻址)
  • Monitor(文件监控)

网络(Network)

网络管理(Network Management)
套接字(Socket)
HTTP 访问(HTTP Client)
  • URI 处理工具集(URI Handler)
  • 请求构造(Request Generator)
  • 请求执行(Request Executor)
  • 响应解析(Responsive)
  • 复杂请求管理(Advanced Request)
WebSocket
远程调用(RPC)

系统进程(SysProc)

切面编程(AOP)
并发编程(ConcurrentProgramming)
  • 线程(Thread)
    • 线程池(ThreadPool)
    • 协程(Coroutines)
  • 并发控制(ConcurrencyControl)
    • 锁(Lock)
    • 锁与事务(Atomic & Transaction)
    • 数据一致性(Consistency)
  • 异步模式(AsynchronousPattern)
    • 回调(Callback)
    • Promise / Future /Defer
    • Generator
    • Async / Await
系统调用(System)
响应式编程(Reactive)
  • RxExtension
  • Actor
  • 流式 API(StreamAPI)

工程实践(Engineering Practices)

样式指南(StyleGuide)

  • 代码风格与约定(CodeStyle)
    • 命名约定(NamingConventions)
    • 文档与注释(Documentation/Comments)
  • 项目/模块架构(Architecture)
  • 语法检查(Lint)

设计模式(DesignPattern)

  • SOLID
  • Observable

性能优化&代码优化(Performance & CodeEfficiency)

测试与发布(Test & Release)

调试(Debug)
Test(测试)
  • 断言(Assert)
  • Mock & Stub
  • TestRunner
发布(Release)
日志(Log)

进阶(Advanced)

泛型编程(Generics)

元编程(Meta Programming)

反射与代理(Reflection & Proxy)
代码生成(Code Generation)
依赖注入/控制反转(DI/IOP)

函数式编程(Functional Programming)

响应式编程(Reactive Programming)

EventStream
TFRP
RxExtension
Actor

内存管理(Memory Management)

内存结构(Memory Structure)
内存分配(Memory Allocation)
垃圾回收(Garbage Collection)

数据结构与算法(Algo Data Structure)

编程语言学习框架

入门概述(Introduction)

版本迭代(Version Iteration)

注释与代码分割(Comments & Split)

构建与依赖管理(Builder & Dependence Manager)

语法基础(Syntax)

表达式(Expression)

变量(Variable)
  • 变量与常量声明(Variable Declaration)
  • 变量提升(Variable Hoisting)
变量赋值(Assignment)
  • 传值与传引用(Pass-by-Value & Pass-by-Reference)
  • 连续赋值(Continuous Assignment)
  • 解构赋值(Destructuring Assignment)
  • 复合类型拷贝(Copy Composite DataTypes)
运算符(Operator)
  • 基本运算符(Basic)
  • 位运算符(Bit Operator)
  • 运算符重载(Operator Overloading)
作用域与闭包(Scope & Closure)
惰性求值(Lazy Evaluation)

流程控制和异常处理(ControlFlow)

分支选择(Branch)
  • if
  • switch
循环(Loop)
  • for
    • for-in
    • for-of
    • forEach
  • while
  • break / continue
错误处理(ErrorHandling)
  • 异常定义与类型(Error)
  • 异常抛出(Throw)
  • 异常捕获(Try-Catch-Finally)

函数(Function)

函数定义(Function Definition)
  • 匿名函数(Lambda Function)
函数参数(Function Args)
  • 默认参数(Default Args)
  • 必要参数(Required Args)
  • 外部参数(External Args)
  • 不定参数
函数调用(Function Call)
装饰器(Decorator)
迭代器与生成器(Iterator & Generator)

类与对象(Class & Object)

类定义(Class Definition)
  • 类属性定义(Class Property Definition)
  • 类方法定义(Class Methods Definition)
    • 构造函数(Constructor Function)
    • 析构函数(Destructor Function)
    • Getter / Setter
  • 访问控制(Access Control)
    • 访问修饰符(Access Modifiers)
    • 私有数据(Private)
对象(Object)
  • 实例化(Instantiation)
  • 单例模式(Singleton Pattern)
  • this
类继承(Inheritance)
内部类(Inner Class)
反射/自省(Reflect)

输入输出流(IOStream)

模块(Modularity)

数据结构(DataStructure)

类型与值判断(Type Judgement)

引用或值相等(Reference/Value Equality)
动态类型检查(Runtime Type Checking)
常用属性判断(Property Checking)
  • 空间占用(Size / Length)
  • 存在性(Existing)

基本类型(Basic)

数值类型(Numeric)
  • 随机数(Random)
  • 科学计算(Scientific Computing)
  • 类型转换(Type Conversion)
空类型(Nullable)
布尔类型(Bool)
可选类型(Optional)
枚举类型(Enum)

字符串类型(String)

增删复替(Manipulation)
  • 格式化字符串(Format)
索引遍历(Index & Traversal)
类型编码(type & encode)*
  • 字符集
  • URL/HTML
  • 拼音
模式匹配(Pattern Match)
  • 正则表达式(RegEx)
  • 字符串校验(Valid)
  • 模糊搜索(Fuzzy Search)

时间与日期(DateTime)

时间解析(Parse)
  • 时间戳(TimeStamp)
  • 时间日期字符串(DateTimeString)
  • 时区转换(TimeZone)
  • 时间比较(Comparison)
时间展示(Display)
  • 格式化(Format)
  • 相对格式(Relative Format)
  • 时长(Duration)
  • i18n(国际化)
时间操作(Manipulation)
  • 读取与设置(Get & Set)
  • 时间增减(Add & Subtract)
  • 时间偏移计算(Diff)
日历(Calendar)

序列类型(IndexedCollection)

数组(Array)
  • 增删复替(Manipulation)
  • 索引遍历(Index & Traversal)
  • 转换(Transform)
    • map/flatMap
    • reduce
    • filter
    • sort
集合(Set)

索引类型(KeyedCollection)

映射
  • 增删复替(Manipulation)
  • 索引遍历(Index & Traversal)

不可变对象(Immutable)

流(Stream)

序列化与反序列化(Serialization)

  • Json
  • XML
  • CSV

功能(Functionality)

存储(Storage)

缓存(Cache)
数据库(DataBase)
  • MySQL
  • Redis
  • MongoDB
文件系统(FileSystem)
  • FilePath(文件寻址)
  • Monitor(文件监控)

网络(Network)

网络管理(Network Management)
套接字(Socket)
HTTP 访问(HTTP Client)
  • URI 处理工具集(URI Handler)
  • 请求构造(Request Generator)
  • 请求执行(Request Executor)
  • 响应解析(Responsive)
  • 复杂请求管理(Advanced Request)
WebSocket
远程调用(RPC)

系统进程(SysProc)

切面编程(AOP)
并发编程(ConcurrentProgramming)
  • 线程(Thread)
    • 线程池(ThreadPool)
    • 协程(Coroutines)
  • 并发控制(ConcurrencyControl)
    • 锁(Lock)
    • 锁与事务(Atomic & Transaction)
    • 数据一致性(Consistency)
  • 异步模式(AsynchronousPattern)
    • 回调(Callback)
    • Promise / Future /Defer
    • Generator
    • Async / Await
系统调用(System)
响应式编程(Reactive)
  • RxExtension
  • Actor
  • 流逝 API(StreamAPI)

工程实践(Engineering Practices)

样式指南(StyleGuide)

  • 代码风格与约定(CodeStyle)
    • 命名约定(NamingConventions)
    • 文档与注释(Documentation/Comments)
  • 项目/模块架构(Architecture)
  • 语法检查(Lint)

设计模式(DesignPattern)

  • SOLID
  • Observable

性能优化&代码优化(Performance & CodeEfficiency)

测试与发布(Test & Release)

调试(Debug)
Test(测试)
  • 断言(Assert)
  • Mock & Stub
  • TestRunner
发布(Release)
日志(Log)

进阶(Advanced)

泛型编程(Generics)

元编程(Meta Programming)

反射与代理(Reflection & Proxy)
代码生成(Code Generation)
依赖注入/控制反转(DI/IOP)

函数式编程(Functional Programming)

响应式编程(Reactive Programming)

EventStream
TFRP
RxExtension
Actor

内存管理(Memory Management)

内存结构(Memory Structure)
内存分配(Memory Allocation)
垃圾回收(Garbage Collection)

数据结构与算法(Algo Data Structure)

学习数据建模

数据模型简介

数据模型的概念

数据模型是一组由符号、文本组成的集合,用以准确表达信息景观,达到有效交流、沟通的目的。

数据模型建立条件如下:

  • 只包含类型:无需显示具体数值,需要显示数据对应的概念或类型。
  • 包含相互作用:需要抓住不同概念、类型间的相互作用。
  • 简介的交流媒介:显示各个类型,并用简单有效的符号表达它们之间的相互作用。

数据模型的价值

数据建模是构建应用程序的必要组成部分。数据模型之所以如此重要,是因为它所带来的两大核心价值:交流性和精确性。

数据建模的交流性

基于数据建模的交流,并非只是在建模结束后才开始的。事实上,伴随着数据建模进程,需要更多的交流和知识分享,即交流沟通在数据建模前、数据建模中和数据建模后都同样具有价值。

  • 数据建模前的交流
  • 数据建模中的交流
  • 数据建模后的交流
数据建模的精确性

数据建模的精确性指的是阅读模型时,其中的每一个符号和条目都是清晰、无二义性的。换言之,如果我看到模型中的某一个符号并说:“我看见了 A”,那么另外一个看到这个符号的人就不可能说“我看见了 B”。由于数据模型引入了精确性,所以无需试图花费宝贵的时间来解释模型,相反,时间可以用来讨论、验证一些与建立某一模型相关的概念。

导致数据模型的精确性降低的情况:

  • 弱定义
  • 伪数据
  • 模糊或缺失的标签

数据模型的作用

  • 理解已有应用程序
  • 风险管理
  • 了解业务
  • 培训团队成员

数据模型的设置

数据模型的范围设置

  • 部门(工程)
  • 组织(应用程序)
  • 行业

数据模型的抽象设置

  • 在业务云中
  • 在数据库云中
  • 在地面上

数据模型的时间范围

  • 当前
  • 未来

数据模型要素

实体

实体的概念

一个实体表示的是对业务非常重要或值得获取的事物及与之相关的信息集合。每个实体都有一个名词或名词词组定义,并符合六大种类之一:谁、什么、何时、何地、为何、如何。

实体的类型

数据模型之美在于你可以根据不用的受众把相同的信息以不同的细节水平呈现出来。

  • 概念:高层次的业务流程的解决方案或应用程序频繁定义的范围和重要术语。
  • 逻辑:业务流程的详细解决方案或应用程序。
  • 物理:应用程序详细的技术解决方案。

实体的类型有

  • 强类型:可以独立存在的实体
  • 弱类型:至少依赖于一个其他实体的实体

属性

属性的概念

属性是一则相对独立的信息,其值用以识别、描述、评估实体实例。

属性的类型

属性也可以在概念、逻辑、物理等层次上加以描述

  • 概念:对业务起着基本且又关键影响的概念。
  • 逻辑:属性对于业务解决方案都有不同程度的贡献,并且与任何软、硬件技术无关。
  • 物理:与物理数据模型对应的属性可以被理解为一个物理“容器”,用来存储数据。

域的概念

域是某一个属性所有可能取值的集合。域中往往还包含一组验证标准,使得域可以被多个属性使用。如果属性与某个域关联,那么该属性的取值绝对不能超出该域。

域的类型
  • 格式域
  • 列表域
  • 范围域
域的作用
  • 插入数据前,通过域的检查来提高数据质量。
  • 数据模型的交流性更强。
  • 使得新建模型、维护现有模型变得更有效率。

关系

关系的概念

数据模型中的规则即为关系,关系被表示成一条连接两个实体的线段,用来说明实体间的规则或导航路径。

关系的类型

规则可以是数据规则,也可以是行为规则。数据规则指示数据间如何关联,行为规则指示当属性包含有某特定值时,需要采取什么操作。

  • 数据规则
    • 结构完整型(基数规则)- SI
      • 基数概念:基数指定了一种可以被实施的数据规则。
    • 参照完整型 - RI
  • 行为规则

标签是出现在关系线上的动词。标签应该在可能的情况下尽量详细,来确保数据模型的准确度。

使用子类型可以将一些类似的属性或将一些相似且有关系的实体的关系进行分组。

键的概念

键由一个或多个属性构成,其目的在于实施规则,有效检索数据,而且允许从一个实体导航至另一个实体。

键的类型
  • 候选键
  • 代理键
  • 主键
  • 备用键
  • 外键
  • 辅助键
  • 组合键

候选键是一个或多个可以唯一标识实体实例的属性。其具备以下 4 个基本特征:

  • 唯一性
  • 强制性
  • 非异变性
  • 最小化

代理键是数据表的唯一标识符,它通常由一个固定大小的、无人工干预的、系统自动产生的计数器生成,代理键不具备任务业务含义。

主键是在众多候选键中首选出来唯一标识实体的选项。

备用键也同样是候选键,具备唯一性、稳定性和强制性和最小化,即使有些备用键没有被选择充当主键,但它仍然可以被使用,以检索特定的实体实例。

外键是可以与其他实体产生关联的一个或多个属性。(或者在递归关系中,连接同一个实体的情况下,有可能存在来自同一个实体的两个实例相互关联)

辅助键是经常被访问的,或者需要被快速检索到的一个或多个属性。辅助键又被称为非唯一性索引或倒排入口。辅助键无需是唯一的、稳定的,而且也不要求必须拥有值。

由多个属性组成的键称为组合键。

概念、逻辑和物理数据模型

TypeScript 入门

学习 TypeScript 的优势

  • 支持 ES6 规范
  • 强大的 IDE 支持:WebStorm
  • Angular 2~4 的开发语言

安装 TypeScript 的开发环境

  • 什么是 compiler?
    • 编译器
  • 为什么需要 compiler?
    • 需要将 TypeScript 转化为 JavaScript 运行
  • TypeScript 在线 compiler
  • TypeScript 本地 compiler
    • npm install -g typescript
    • tsc –version

TypeScript 概念、语法和特性介绍

字符串新特性

多行字符串

1
2
3
4
5
6
7
// TypeScript
var string = `aaa
bbb
ccc`;
// JavaScript
var string = "aaa\n"+"bbb\n"+"ccc\n";

字符串模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// TypeScript
var name = "LuisEdware";
var getName = function(){
return "LuisEdware";
}
console.log(`Hello ${name}`);
console.log(`Hello ${getName()}`);
console.log(`<div>
<span>${name}</span>
<span>${getName()}</span>
</div>
`);
// JavaScript
var name = "LuisEdware";
var getName = function () {
return "LuisEdware";
};
console.log("Hello " + name);
console.log("Hello " + getName());
console.log("<div>\n<span>" + name + "</span>\n<span>" + getName() + "</span>\n</div>\n");

自动拆分字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// TypeScript
function test(template, name, age) {
console.log(template);
console.log(name);
console.log(age);
}
var name = "AnnEason";
var getAge = "";
test`hello my name is ${name}, i'm ${getAge}`;
// JavaScript
function test(template, name, age) {
console.log(template);
console.log(name);
console.log(age);
}
var name = "AnnEason";
var getAge = "";
(_a = ["hello my name is ", ", i'm ", ""], _a.raw = ["hello my name is ", ", i'm ", ""], test(_a, name, getAge));
var _a;

参数新特性

参数类型,在参数名称后面使用冒号来指定参数类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var username: string = "Luis Edware";
# 编译报错、JavaScript 执行正常
username = 13;
var age: number = 13;
var man: boolean = true;
function test(name: string): string{
return "";
}
test(username);
# 编译报错,需要提供字符串类型的参数
test(age);

默认参数,在参数声明后面使用等号来指定参数的默认值

1
2
3
4
5
6
7
8
function test(a: string, b: string, c:string = "jojo")
{
console.log(a);
console.log(b);
console.log(c);
}
test('xxx','yyy','zzz');
test('xxx','yyy');

可选参数,在方法的参数声明后面使用问好来标明此参数为可选参数

1
2
3
4
5
6
function test(a: string, b?: string, c:string = 'jojo')
{
console.log(a);
console.log(b.length);
console.log(c);
}

函数新特性

Rest and Spread 操作符,用来声明任意数量的方法参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function func1(...args)
{
args.forEach(function(arg){
console.log(arg);
})
}
func1(1,2,3);
func1(7,8,9,10,11);
function func2(a,b,c){
console.log(a);
console.log(b);
console.log(c);
}
var args = [1,2];
func2(...args);
var args2 = [7,8,9,10,11];
func2(...args2);

generator 函数,控制函数的执行过程,手工暂停和回复代码执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function* getStockPrice(stock){
while(true){
yield Math.random()*100;
}
}
var priceGenerator = getStockPrice("IBM");
var limitPrice = 15;
var price = 100;
while(price > limitPrice){
price = priceGenerator.next().value;
console.log(`the generator return ${price}`);
}
console.log(`buying at ${price}`);

destructuring 析构表达式,通过表达式将对象或数组斋戒成任意数量的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function getStock(){
return {
code: "IBM",
price: 1000
}
}
// ES5
var stock = getStock();
var code = stock.code;
var price = stock.price;
// TypeScript
var {code,price} = getStock();
console.log(code);
console.log(price);
var {code: codex, price} = getStock();
console.log(codex);
console.log(price);
// TypeScript 获取对象中对象的值
function getStock2(){
return {
code: "IBM",
price: {
price1: 100,
price2: 200
}
}
}
var {code, price: {price2}} = getStock();
console.log(codex);
console.log(price);
// 数组析构表达式
var array1 = [1,2,3,4];
var [number1, number2] = array1;
var [,,number3,number4] = array1;
console.log(number1);
console.log(number2);
console.log(number3);
console.log(number4);
var [number1,number2, ...others] = array1;
console.log(number1);
console.log(number2);
console.log(others);
// 方法析构表达式
var array1 = [1,2,3,4];
function doSomething([number1,number2, ...others]){
console.log(number1);
console.log(number2);
console.log(others);
}
doSomething(array1);

表达式和循环

箭头表达式:用来声明匿名函数,消除传统命名函数的 this 指针问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 一行不要写大括号
var sum = (arg1, arg2) => arg1 + arg2;
// 多行需要写大括号
var sum2 = (arg1, arg2) => {
return arg1 + arg2;
}
// example - 1
var myArray = [1, 2, 3, 4, 5];
console.log(myArray.filter(value => value % 2 == 0));
// example - 2
function getStock(name: string) {
this.name = name;
var that = this;
setInterval(function () {
// this 指向 setInterval 作用域,this.name 无效
console.log("name is :" + that.name);
},1000);
}
var stock1 = new getStock("Hello")
function getStock2(name: string) {
this.name = name;
setInterval(() => {
console.log("name is :" + this.name);
}, 1000);
}
var stock2 = new getStock2("World");

循环,forEach(), for in 和 for of

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var myArray = [1, 2, 3, 4];
myArray.desc = "for number";
// 循环数组,会把 desc 属性忽略掉
myArray.forEach(value => console.log(value));
// 循环 key
for (let key in myArray) {
console.log(key);
}
// 循环 value
for (let value of myArray) {
console.log(value);
}

面向对象特性

类,类是 TypeScript 的核心,使用 TypeScript 开发时,大部分代码都是写在类里面的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Person{
public name;
private fish;
// 构造函数
constructor(public food:string) {
console.log('Hello');
}
// 修饰符
public eat() {
console.log(`My name is ${this.name},I am eating ${this.food}.`);
}
}
var p1 = new Person("egg");
p1.name = "Luis Edware";
p1.eat();
// 继承
class Employee extends Person{
public code: string;
public constructor(public food: string, code:string) {
super(food);
this.code = code;
}
public work() {
super.eat();
this.doWork();
}
private doWork() {
console.log(`I am Working with ${this.code}`);
}
}
var e1 = new Employee("apple", "Hello World");
e1.name = "Ann Eason";
e1.work();

泛型,参数化的类型,一般用来限制集合的内容

1
2
3
4
5
var workers: Array<Person> = [];
workers[0] = new Person("zhangsan");
workers[1] = new Employee("lisi", "hell world");
// 报错,只能放入对象为 Person 类型的数据
workers[2] = 3;

接口,用来建立某种代码约定,使得其它开发者在调用某个方法或创建新的类时必须遵循接口所定义的代码约定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface IPerson{
name: string;
age: number;
}
class Person{
constructor(public config: IPerson) {
}
}
var p1 = new Person({ name: "hello", age: 123 });
interface Animal{
eat();
}
// 实现接口,必须实现它所声明的函数
class Sheep implements Animal {
eat() {
console.log("I am eating something");
}
}

模块,模块可以帮助开发者将代码分割为可重用的单元。开发者可以自己决定将模块中的哪些资源(类、方法、变量)暴露出去供外部使用,哪些资源只在模块内使用。在 TypeScript 中,一个文件就是一个模块。export 和 import 关键字支撑模块的使用。export 对外暴露,import 对内导入。

1
2
3
4
5
export class Person{
}
import {Person} from './person.tc';

注解,注解为程序的元素(类,方法,变量)加上更直观更明了的说明,这些说明信息与程序的业务逻辑无关,而是供指定的工具或框架使用的。

1
2
3
4
5
6
7
8
9
10
11
12
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
// 当实例化 AppComponent 的时候,Angular 框架应该去加载 templateUrl 属性所指的页面和 styleUrls 属性所指的层叠样式表。
export class AppComponent {
title = 'app works!';
}

类型定义文件(*.d.ts),用来帮助开发者在 TypeScript 中使用已有的 JavaScript 的工具包,如:jQuery,常用的类型文件可以从 Github 项目的 DefinitelyTyped/DefinitelyTyped 获取。或者使用工具 typings/typings。

「复盘」Yii RESTful API 脚手架

安装 Yii 高级模板

Yii 安装文档

1
2
composer global require "fxp/composer-asset-plugin:^1.2.0"
composer create-project --prefer-dist yiisoft/yii2-app-advanced Yii2-RESTful-API-Scaffolding

初始化项目

在项目目录执行命令如下:

1
2
cd Yii2-RESTful-API-Scaffolding
php init

构建 RESTful API 项目目录

构建 RESTful API 项目目录,执行命令如下:

1
2
3
4
5
6
7
8
9
10
11
# 复制目录 frontend 为 api
cp -rf frontend api
# 复制目录 environments/dev/frontend 为 environments/dev/api
cp -rf environments/dev/frontend environments/dev/api
# 复制目录 environments/prod/frontend 为 environments/prod/api
cp -rf environments/prod/frontend environments/prod/api
# 给 api/runtime 和 api/web/assets 目录赋予读写权限
sudo chmod -R 777 api/runtime api/web/assets

接着将所有命名空间为 namespace frontend\ 替换为 namespace api\,将所有加载引用 use frontend\ 替换为 use api\

然后打开文件 common/config/bootstrap.php 并增删代码如下:

1
2
3
4
5
6
7
<?php
// 增加代码
Yii::setAlias('api', dirname(dirname(__DIR__)) . '/api');
// 删除代码
Yii::setAlias('@frontend', dirname(dirname(__DIR__)) . '/frontend');
Yii::setAlias('@backend', dirname(dirname(__DIR__)) . '/backend');

清理 RESTful API 项目,执行命令如下:

1
2
3
4
5
6
7
rm -rf backend
rm -rf frontend
rm -rf api/views
rm -rf api/assets
rm -rf api/web/css
rm -rf api/models/*
rm -rf api/controllers/SiteController.php

修改配置文件

打开文件 api/config/main.php,把文本 frontend 替换为 api,并添加配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
'components' => [
'request' => [
'parsers' => [
'application/json' => 'yii\web\JsonParser',
]
],
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => 'users' ,
'pluralize' => false
],
],
]
]

打开文件 common/config/main-local.php,添加配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
return [
'components' => [
...,
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=api',
'username' => 'root',
'password' => '123456',
'charset' => 'utf8',
],
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
'viewPath' => '@common/mail',
// send all mails to a file by default. You have to set
// 'useFileTransport' to false and configure a transport
// for the mailer to send real emails.
'useFileTransport' => true,
],
],
];

访问域名

配置虚拟主机,并在本机 hosts 文件加入文本 127.0.0.1 yii2-restful-api-scaffolding.app。之后重启 Apache 并访问域名 yii2-restful-api-scaffolding.app/gii

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<VirtualHost *:80>
ServerName yii2-restful-api-scaffolding.app
DocumentRoot "/Users/LuisEdware/Git/Yii2-RESTful-API-Scaffolding/api/web/"
<Directory "/Users/LuisEdware/Git/Yii2-RESTful-API-Scaffolding/api/web/">
# use mod_rewrite for pretty URL support
RewriteEngine on
# If a directory or a file exists, use the request directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Otherwise forward the request to index.php
RewriteRule . index.php
# use index.php as index file
DirectoryIndex index.php
# ...other settings...
# Apache 2.4
Require all granted
## Apache 2.2
# Order allow,deny
# Allow from all
</Directory>
</VirtualHost>

新增数据库迁移文件

执行命令如下:

1
2
3
4
php yii migrate/create create_goods_table
php yii migrate/create insert_goods_table
php yii migrate/create create_goods_class_table
php yii migrate/create insert_goods_class_table

create_goods_table 迁移文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
use yii\db\Migration;
/**
* Handles the creation of table `goods`.
*/
class m170710_231631_create_goods_table extends Migration
{
/**
* @inheritdoc
*/
public function up()
{
$this->createTable('goods', [
'goodsId' => $this->primaryKey(),
'goodsName' => $this->string()->notNull(),
'goodsPrice' => $this->float()->notNull(),
'classId' => $this->integer()->notNull(),
]);
echo "m170710_231631_create_goods_table create table success.\n";
}
/**
* @inheritdoc
*/
public function down()
{
$this->dropTable('goods');
echo "m170710_231631_create_goods_table drop table success.\n";
}
}

create_goods_class_talbe 迁移文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
use yii\db\Migration;
/**
* Handles the creation of table `goods_class`.
*/
class m170711_011146_create_goods_class_table extends Migration
{
/**
* @inheritdoc
*/
public function up()
{
$this->createTable('goods_class', [
'goodsClassId' => $this->primaryKey(),
'goodsClassName' => $this->string()
]);
echo "m170711_011146_create_goods_class_table create table success.\n";
}
/**
* @inheritdoc
*/
public function down()
{
$this->dropTable('goods_class');
echo "m170711_011146_create_goods_class_table drop table success.\n";
}
}

insert_goods_table 迁移文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
use yii\db\Migration;
class m170711_011318_insert_data_to_goods_table extends Migration
{
public function up()
{
for ($i=1; $i < 6; $i++) {
$faker = Faker\Factory::create();
$this->insert('goods', [
'goodsId' => $i,
'goodsName' => $faker->name,
'goodsPrice' => $faker->randomFloat(2, 1, 1000),
'classId' => $i
]);
}
echo "m170711_011318_insert_data_to_goods_table insert data success.\n";
}
public function down()
{
$this->truncateTable('goods');
echo "m170711_011318_insert_data_to_goods_table cannot be reverted.\n";
}
}

insert_goods_class_talbe 迁移文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
use yii\db\Migration;
class m170711_011335_insert_data_to_goods_class_talbe extends Migration
{
public function up()
{
for ($i=1; $i < 6; $i++) {
$faker = Faker\Factory::create();
$this->insert('goods_class', [
'goodsClassId' => $i,
'goodsClassName' => $faker->name,
]);
}
echo "m170711_011335_insert_data_to_goods_class_talbe insert data success.\n";
}
public function down()
{
$this->truncateTable('goods_class');
echo "m170711_011335_insert_data_to_goods_class_talbe had remove all rows.\n";
}
}

接着在执行 php yii migrate,生成数据表并填充数据。

新增模块

打开域名 http://yii2-restful-api-scaffolding.app/gii/module,新增模块信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 给 api/modules 目录赋予读写权限
sudo chmod -R 777 api/modules
# Module Class
api\modules\v1\ApiModule
# Module ID
v1
# 勾选生成文件
modules/v1/ApiModule.php
# 在 api/config/main.php 配置文件加入配置项如下
'modules' => [
'v1' => [
'class' => 'api\modules\v1\ApiModule',
],
],

新增基础控制器

打开域名 http://yii2-restful-api-scaffolding.app/gii/controller,新增控制器信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 给 api/controllers 目录赋予读写权限
sudo chmod -R 777 api/controllers
# Controller Class
api\controllers\BaseController
# Action IDs
清空
# View Path
清空
# Base Class
yii\rest\ActiveController
# Code Template
默认

新增 api/modules/v1/controllers 目录,并新增控制器如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mkdir api/modules/v1/controllers
sudo chmod -R 777 api/modules/v1/controllers
# Controller Class,依次新增
api\modules\v1\controllers\GoodsController
api\modules\v1\controllers\GoodsClassController
# Action IDs
清空
# View Path
清空
# Base Class
yii\rest\ActiveController
# Code Template
默认

新增模型

打开域名 http://yii2-restful-api-scaffolding.app/gii/models,新增模型信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
# 给 api/models 赋予读写权限
sudo chmod -R 777 api/models
# Table Name
goods
goods_class
# Class Name
Goods
GoodsClass
# 其余选项默认

注册 ApiModule 启动引导

打开文件 api/config/main.php,在数组索引为 bootstrap 新增 v1 模块组件,修改文件 api/modules/v1/ApiModule.php 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
namespace api\modules\v1;
use yii\base\Module;
use yii\base\BootstrapInterface;
/**
* v1 module definition class
*/
class ApiModule extends Module implements BootstrapInterface
{
/**
* @inheritdoc
*/
public $controllerNamespace = 'api\modules\v1\controllers';
/**
* @inheritdoc
*/
public function init()
{
parent::init();
// custom initialization code goes here
}
public function bootstrap($app)
{
// 定义 RESTful API 路由
$app->getUrlManager()->addRules([
[
'class' => 'yii\rest\UrlRule',
'controller' => 'v1/goods'
],
[
'class' => 'yii\rest\UrlRule',
'controller' => 'v1/goods-class',
'extraPatterns' => [
'GET <id:\d+>/goods' => 'get-goods-by-goods-id'
]
],
]);
}
}

访问路由 http://yii2-restful-api-scaffolding.app/v1/goods 就可以看到接口返回数据

编写接口测试文档(一)

首先执行 composer require --prefer-dist light/yii2-swagger "~1.0.4" --dev,安装文档生成 Package。

然后使用 Gii 生成 swagger 模块组件

  • Module Class:api\modules\swagger\SwaggerModule
  • Module ID:swagger
  • Code File
    • modules/swagger/SwaggerModule.php
    • modules/swagger/controllers/DefaultController.php

然后在配置文件 api/config/main.php 的引导启动配置项加入 swagger 模块组件的 id 和模块组件配置

1
2
3
4
5
6
7
8
'modules' => [
'v1' => [
'class' => 'api\modules\v1\Module',
],
'swagger' => [
'class' => 'api\modules\swagger\SwaggerModule',
],
],

打开文件 api/modules/swagger/SwaggerModule.php,修改代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
namespace api\modules\swagger;
/**
* swagger module definition class
*/
class SwaggerModule extends \yii\base\Module implements \yii\base\BootstrapInterface
{
/**
* @inheritdoc
*/
public $controllerNamespace = 'api\modules\swagger\controllers';
/**
* @inheritdoc
*/
public function init()
{
parent::init();
// custom initialization code goes here
}
public function bootstrap($app)
{
// 定义路由
$app->getUrlManager()->addRules([
[
'class' => 'yii\web\UrlRule',
'pattern' => $this->id,
'route' => $this->id . '/default/doc',
],
[
'class' => 'yii\web\UrlRule',
'pattern' => $this->id . '/<action:\w+>',
'route' => $this->id . '/default/<action>',
],
]);
}
}

打开文件 api/modules/swagger/controllers/DefaultController.php,修改代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
namespace api\modules\swagger\controllers;
use Yii;
use yii\helpers\Url;
use yii\web\Controller;
/**
* Default controller for the `swagger` module
*/
class DefaultController extends Controller
{
public function actions()
{
return [
//文档预览地址
'doc' => [
'class' => 'light\swagger\SwaggerAction',
'restUrl' => Url::to(['api'], true),
],
//看到上面配置的*restUrl*了么,没错, 它就是指向这个地址
'api' => [
'class' => 'light\swagger\SwaggerApiAction',
//这里配置需要扫描的目录,不支持 yii 的 alias,所以需要这里直接获取到真实地址
'scanDir' => [
Yii::getAlias('@api/modules/v1/documents/'),
],
// 'api_key' => '',
],
];
}
}

新建目录 api/modules/v1/documents/controllers,新增文档注释文件 controllers/BaseControllerDocument.phpcontrollers/GoodsControllerDocument.php,代码如下:

BaseControllerDocument.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
/**
* @SWG\Swagger(
* swagger="2.0",
* @SWG\Info(
* title="Yii RESTful API(标题)",
* description="Yii RESTful API(描述)",
* termsOfService="七月十五九月初七",
* version="1.0.0(版本号)",
* @SWG\Contact(
* email="luisedware@gmail.com(邮件)",
* ),
* @SWG\License(
* name="MIT",
* url="http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT"
* ),
* ),
* host="yii2-restful-api-scaffolding.app/",
* basePath="v1/",
* schemes={"http"},
* produces={"application/json"},
* consumes={"application/json"},
* )
*/

GoodsControllerDocument.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
/**
* @SWG\Get(
* tags={"GoodsController「商品管理」"},
* path="/goods",
* summary="列出所有商品",
* operationId="index",
* description="列出所有商品",
* @SWG\Response(
* response=200,
* description="Successful Operation",
* )
* )
*/

使用 Vue.js 2.0 开发后台

目录

  • vue-cli 安装与使用

vue-cli 安装与使用

vue-cli 是 Vue.js 官方发布的 Vue.js 项目脚手架,使用 vue-cli 可以快速创建 Vue.js 项目,执行安装命令如下:

1
npm install -g vue-cli

vue-cli 用法如下,其中 template-name 是模块打包器名称,project-name 是项目名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 语法
vue init <template-name> <project-name>
# 例子
vue init webpack vue-element-website-backend
# 初始化过程
? Project name (vue-element-website-backend)
? Project description (A Vue.js project)
? Author luisedware <XXXXXXXXXXX@qq.com>
? Vue build (Use arrow keys)
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Setup unit tests with Karma + Mocha? Yes
? Setup e2e tests with Nightwatch? Yes
# 执行命令完成安装
cd vue-element-website-backend
npm install
npm run dev

安装完成后,在 Github 新建 repository,并把 Vue.js 项目 push 上去,执行命令如下:

1
2
3
4
5
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/luisedware/vue-element-website-backend.git
git push -u origin master

LNMP 中常用的 Linux 命令

目录

ps:报告当前系统的进程状态

ps 命令用于报告当前系统的进程状态。可以搭配 kill 命令随时中断、删除不必要的程序。ps 命令是最基本同时也是非常强大的进程查看命令,使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等,总之大部分信息都是可以通过执行该命令得到的。

ps 语法

1
ps (选项)

ps 选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
a 显示所有进程
-a 显示同一终端下的所有程序
-A 显示所有进程
c 显示进程的真实名称
-N 反向选择
-e 等于“-A”
e 显示环境变量
f 显示程序间的关系
-H 显示树状结构
r 显示当前终端的进程
T 显示当前终端的所有程序
u 指定用户的所有进程
-au 显示较详细的资讯
-aux 显示所有包含其他使用者的行程
-C<命令> 列出指定命令的状况
--lines<行数> 每页显示的行数
--width<字符数> 每页显示的字符数
--help 显示帮助信息
--version 显示版本显示

ps 实例

ps 这个命令的结果一般会很长,为了便于查看,可以结合 less 命令和管道来使用

显示所有进程信息:ps -A
1
2
3
4
5
6
7
8
9
10
PID TTY TIME CMD
1 ?? 0:09.53 /sbin/launchd
49 ?? 0:00.65 /usr/sbin/syslogd
50 ?? 0:01.81 /usr/libexec/UserEventAgent (System)
52 ?? 0:00.17 /System/Library/PrivateFrameworks/Uninstall.framework/Resources/uninstalld
53 ?? 0:03.00 /usr/libexec/kextd
54 ?? 0:02.52 /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framewor
56 ?? 0:00.16 /System/Library/PrivateFrameworks/MediaRemote.framework/Support/mediaremoted
60 ?? 0:00.21 /System/Library/CoreServices/appleeventsd --server
61 ?? 0:01.16 /usr/libexec/configd
显示指定用户进程信息:ps -u LuisEdware
1
2
3
4
5
6
7
8
9
UID PID TTY TIME CMD
501 297 ?? 0:00.58 /usr/libexec/UserEventAgent (Aqua)
501 299 ?? 0:04.05 /usr/sbin/distnoted agent
501 301 ?? 0:00.98 /usr/sbin/cfprefsd agent
501 302 ?? 0:00.96 /System/Library/Frameworks/CoreTelephony.framework/Support/CommCenter -L
501 304 ?? 0:03.09 /usr/libexec/trustd --agent
501 305 ?? 0:00.65 /usr/libexec/lsd
501 307 ?? 0:01.31 /usr/sbin/usernoted
501 311 ?? 0:00.43 /usr/libexec/sharingd
查找进程并显示进程所有信息::ps -ef | grep httpd
1
2
3
4
5
6
7
8
9
10
11
0 1825 1 0 8:43上午 ?? 0:00.16 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
1 1876 1825 0 8:43上午 ?? 0:01.11 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
1 1877 1825 0 8:43上午 ?? 0:00.25 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
1 1878 1825 0 8:43上午 ?? 0:00.17 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
1 1879 1825 0 8:43上午 ?? 0:00.18 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
1 1880 1825 0 8:43上午 ?? 0:00.26 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
1 1992 1825 0 8:46上午 ?? 0:00.16 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
1 1995 1825 0 8:46上午 ?? 0:00.25 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
1 1996 1825 0 8:46上午 ?? 0:00.16 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
1 1997 1825 0 8:46上午 ?? 0:00.30 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
1 2038 1825 0 8:55上午 ?? 0:00.19 /usr/local/Cellar/httpd24/2.4.25/bin/httpd -k start
将目前属于自己这次登入的 PID 与相关信息列示出来:ps -l
1
2
3
4
5
6
7
8
UID PID PPID F CPU PRI NI SZ RSS WCHAN S ADDR TTY TIME CMD
501 911 909 4006 0 31 0 2481960 2976 - Ss 0 ttys000 0:00.04 /Users/LuisE
501 913 912 4006 0 31 0 2461752 1624 - S 0 ttys000 0:00.34 -zsh
501 1624 913 4006 0 31 0 3129968 15188 - S+ 0 ttys000 0:01.39 npm
501 1752 1624 4006 0 31 0 3382196 268376 - S+ 0 ttys000 39:16.48 node build/d
501 1816 1752 4006 0 31 0 2500188 3920 - S+ 0 ttys000 0:00.07 open -W http
501 1625 909 4006 0 31 0 2481960 3044 - Ss 0 ttys001 0:00.04 /Users/LuisE
501 1627 1626 4006 0 31 0 2462840 4408 - S 0 ttys001 0:00.47 -zsh
列出目前所有的正在内存当中的程序:ps aux
1
2
3
4
5
6
7
8
9
10
11
12
13
14
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
LuisEdware 1752 62.5 3.2 3382196 268440 s000 S+ 8:42上午 40:41.18 node build/dev-server.js
LuisEdware 899 4.9 2.9 4328408 243804 ?? S 8:39上午 4:44.41 /Applications/QQ.app/Contents/MacOS/QQ
_windowserver 194 3.2 1.4 4104716 121620 ?? Ss 8:30上午 6:44.72 /System/Library/PrivateFrameworks/SkyLight.framework/Resources/WindowServer -daemon
LuisEdware 909 2.6 0.9 3843120 75844 ?? S 8:42上午 1:05.74 /Users/LuisEdware/Applications/iTerm.app/Contents/MacOS/iTerm2
LuisEdware 2281 1.5 4.1 4300024 343468 ?? S 9:14上午 1:57.11 /Applications/NeteaseMusic.app/Contents/MacOS/NeteaseMusic
_coreaudiod 214 0.7 0.1 2474372 8044 ?? Ss 8:30上午 0:19.79 /usr/sbin/coreaudiod
root 1 0.3 0.2 2492048 12796 ?? Ss 8:30上午 0:12.43 /sbin/launchd
root 93 0.3 0.1 2511440 12104 ?? Ss 8:30上午 0:03.20 /usr/libexec/opendirectoryd
root 118 0.3 0.0 2470524 2016 ?? Ss 8:30上午 0:02.05 /usr/sbin/notifyd
LuisEdware 2219 0.2 1.6 3206280 132620 ?? S 8:58上午 0:28.23 /Applications/Sublime Text.app/Contents/MacOS/plugin_host 2218
LuisEdware 407 0.2 0.4 2687024 32212 ?? R 8:30上午 0:04.06 /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
LuisEdware 665 0.1 0.5 2717352 41656 ?? S 8:30上午 0:21.83 /Applications/MacPomodoroTimer.app/Contents/MacOS/MacPomodoroTimer -psn_0_254014
LuisEdware 2218 0.1 2.3 4088360 195860 ?? S 8:58上午 1:19.86 /Applications/Sublime Text.app/Contents/MacOS/Sublime Text
根据 CPU 使用来升序排序:ps -aux --sort -pcpu
1
2
3
4
5
6
7
8
9
10
11
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
redis 1296 0.1 0.5 142692 5596 ? Ssl 3月30 100:40 /usr/bin/redis-server 127.0.0.1:6379
root 17921 0.1 1.3 125652 13828 ? Ssl 6月05 1:57 /usr/local/aegis/aegis_client/aegis_10_23/AliYunDun
root 1 0.0 0.2 190544 2772 ? Ss 3月30 3:18 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
root 2 0.0 0.0 0 0 ? S 3月30 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? S 3月30 0:07 [ksoftirqd/0]
root 5 0.0 0.0 0 0 ? S< 3月30 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? S 3月30 0:17 [kworker/u30:0]
root 7 0.0 0.0 0 0 ? S 3月30 0:00 [migration/0]
root 8 0.0 0.0 0 0 ? S 3月30 0:00 [rcu_bh]
root 9 0.0 0.0 0 0 ? S 3月30 29:07 [rcu_sched]
根据内存使用来升序排序:ps -aux --sort -pmem
1
2
3
4
5
6
7
8
9
10
11
12
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
mysql 1071 0.0 10.1 1197928 102632 ? Sl 3月30 41:46 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mariadb/mariadb.log --pid-file=/var/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock
root 335 0.0 4.2 106532 42852 ? Ss 3月30 1:13 /usr/lib/systemd/systemd-journald
git 1322 0.0 3.1 200676 31512 ? Sl 3月30 13:14 /home/git/gogs/gogs web
www 5099 0.0 2.1 430056 22168 ? S 5月30 0:20 php-fpm: pool www
www 5097 0.0 2.1 430056 22164 ? S 5月30 0:21 php-fpm: pool www
www 5098 0.0 2.1 430056 22144 ? S 5月30 0:21 php-fpm: pool www
www 7762 0.0 2.1 430056 22116 ? S 5月31 0:19 php-fpm: pool www
root 797 0.0 2.1 514268 21916 ? Ssl 3月30 0:25 /usr/sbin/rsyslogd -n
www 5100 0.0 1.9 428008 20060 ? S 5月30 0:20 php-fpm: pool www
www 5096 0.0 1.9 428032 20044 ? S 5月30 0:20 php-fpm: pool www
root 5094 0.0 1.3 418136 13832 ? Ss 5月30 0:31 php-fpm: master process (/etc/php-fpm.conf)

ps 输出列信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
F 代表当前进程的旗标 (flag), 4 代表使用者为 Super User
S 代表当前进程的状态 (STAT),关于各 STAT 的意义将在内文介绍
PRI 优先执行等级
NI 当前是 Nice 值,在下一小节我们会持续介绍
ADDR 当前进程所在的内存位置。如果是个 Running 的程序,一般就是 "-"
USER 该进程属于那个使用者账号的
PID 该进程的 ID
PPID 当前进程的父级进程的 ID
C 当前进程使用 CPU 资源的百分比
%CPU 当前进程使用 CPU 资源的百分比
SZ 当前进程使用内存资源的大小
%MEM 当前进程所占用的物理内存百分比
VSZ 当前进程使用掉的虚拟内存量 (Kbytes)
RSS 当前进程占用的固定的内存量 (Kbytes)
TTY 当前进程是在哪个终端机上面运作,若与终端机无关,则显示 ?,另外, tty1-tty6 是本机上面的登入者程序,若为 pts/0 等等的,则表示为由网络连接进主机的程序。
STAT 当前进程目前的状态,主要的状态有
R 当前进程目前正在运作,或者是可被运作
S 当前进程目前正在睡眠当中 (可说是 idle 状态),但可被某些讯号 (signal) 唤醒。
T 当前进程目前正在侦测或者是停止了
Z 当前进程应该已经终止,但是其父进程却无法正常的终止他,造成 zombie (疆尸) 进程的状态
STARTED 当前进程被触发启动的时间
TIME 当前进程实际使用 CPU 运作的时间
CMD 当前进程所运行的命令
COMMAND 当前进程所运行的命令
WCHAN 判断当前进程是否在运作,若为 - 表示正在运作

lsof:显示Linux系统当前已打开的所有文件列表

lsof 命令用于查看你进程开打的文件、打开文件的进程,进程打开的端口(TCP、UDP)、找回/恢复删除的文件,是十分方便的系统监视工具,因为 lsof 命令需要访问核心内存和各种文件,所以需要 root 用户执行。

在 Linux 环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。所以如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,无论这个文件的本质如何,该文件描述符为应用程序与基础操作系统之间的交互提供了通用接口。因为应用程序打开文件的描述符列表提供了大量关于这个应用程序本身的信息,因此通过 lsof 工具能够查看这个列表对系统监测以及排错将是很有帮助的。

lsof 语法

1
lsof (选项)

lsof 选项

1
2
3
4
5
6
7
8
9
10
11
12
-a 列出打开文件存在的进程;
-c<进程名> 列出指定进程所打开的文件;
-g 列出GID号进程详情;
-d<文件号> 列出占用该文件号的进程;
+d<目录> 列出目录下被打开的文件;
+D<目录> 递归列出目录下被打开的文件;
-n<目录> 列出使用NFS的文件;
-i<条件> 列出符合条件的进程;
-p<进程号> 列出指定进程号所打开的文件;
-u 列出UID号进程详情;
-h 显示帮助信息;
-v 显示版本信息。

lsof 实例

查找某个文件相关的进程:lsof /bin/bash
1
2
3
4
[root@git ~]lsof /bin/bash
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld_sa 898 mysql txt REG 202,1 960472 397902 /usr/bin/bash
bash 20116 root txt REG 202,1 960472 397902 /usr/bin/bash
列出某个用户打开的文件信息:lsof -u git
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@git ~]lsof -u git
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
gogs 1322 git cwd DIR 202,1 4096 268706 /home/git/gogs
gogs 1322 git rtd DIR 202,1 4096 2 /
gogs 1322 git txt REG 202,1 31081412 268950 /home/git/gogs/gogs
gogs 1322 git mem REG 202,1 23968 401117 /usr/lib64/libcap-ng.so.0.0.0
gogs 1322 git mem REG 202,1 122936 398733 /usr/lib64/libaudit.so.1.0.0
gogs 1322 git mem REG 202,1 2118128 398763 /usr/lib64/libc-2.17.so
gogs 1322 git mem REG 202,1 61672 401115 /usr/lib64/libpam.so.0.83.1
gogs 1322 git mem REG 202,1 143944 399086 /usr/lib64/libpthread-2.17.so
gogs 1322 git mem REG 202,1 19776 401099 /usr/lib64/libdl-2.17.so
gogs 1322 git mem REG 202,1 155464 397967 /usr/lib64/ld-2.17.so
gogs 1322 git 0r CHR 1,3 0t0 4913 /dev/null
gogs 1322 git 1w REG 202,1 101689 266937 /home/git/gogs/log/gogs.log.2017-03-30
gogs 1322 git 2w REG 202,1 101689 266937 /home/git/gogs/log/gogs.log.2017-03-30
gogs 1322 git 3w REG 202,1 15097 272572 /home/git/gogs/log/gogs.log
gogs 1322 git 4u REG 202,1 0 268956 /home/git/gogs/log/xorm.log
gogs 1322 git 5u IPv6 16091 0t0 TCP *:hbci (LISTEN)
gogs 1322 git 6u a_inode 0,9 0 4909 [eventpoll]
gogs 1322 git 9u IPv4 3291232 0t0 TCP localhost:53538->localhost:mysql (ESTABLISHED)
gogs 1322 git 10r CHR 1,9 0t0 4918 /dev/urandom
gogs 1322 git 12u IPv4 2948194 0t0 TCP localhost:46354->localhost:mysql (ESTABLISHED)
列出某个程序进程所打开的文件信息:lsof -c php-fpm | less
1
2
3
4
5
6
7
8
9
10
11
12
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
php-fpm 5094 root cwd DIR 202,1 4096 2 /
php-fpm 5094 root rtd DIR 202,1 4096 2 /
php-fpm 5094 root txt REG 202,1 4826352 267066 /usr/sbin/php-fpm
php-fpm 5094 root mem REG 202,1 62184 399121 /usr/lib64/libnss_files-2.17.so
php-fpm 5094 root mem REG 202,1 87392 789157 /usr/lib64/php/modules/sockets.so
php-fpm 5094 root mem REG 202,1 315464 789173 /usr/lib64/php/modules/soap.so
php-fpm 5094 root mem REG 202,1 58400 789156 /usr/lib64/php/modules/simplexml.so
php-fpm 5094 root mem REG 202,1 15664 789155 /usr/lib64/php/modules/shmop.so
php-fpm 5094 root mem REG 202,1 469616 789206 /usr/lib64/php/modules/redis.so
php-fpm 5094 root mem REG 202,1 32920 789199 /usr/lib64/php/modules/posix.so
php-fpm 5094 root mem REG 202,1 268096 789154 /usr/lib64/php/modules/phar.so
列出某个用户以及某个进程所打开的文件信息lsof -u git -c mysql | less
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
mysqld 1071 mysql 22u REG 202,1 5120 1181363 /var/lib/mysql/mysql/proxies_priv.MYI
mysqld 1071 mysql 23u REG 202,1 1386 1181364 /var/lib/mysql/mysql/proxies_priv.MYD
mysqld 1071 mysql 24u REG 202,1 4096 1181312 /var/lib/mysql/mysql/tables_priv.MYI
mysqld 1071 mysql 25u REG 202,1 0 1181313 /var/lib/mysql/mysql/tables_priv.MYD
mysqld 1071 mysql 26u REG 202,1 4096 1181315 /var/lib/mysql/mysql/columns_priv.MYI
mysqld 1071 mysql 27u REG 202,1 0 1181316 /var/lib/mysql/mysql/columns_priv.MYD
mysqld 1071 mysql 28u REG 202,1 4096 1181348 /var/lib/mysql/mysql/procs_priv.MYI
mysqld 1071 mysql 29u REG 202,1 0 1181349 /var/lib/mysql/mysql/procs_priv.MYD
mysqld 1071 mysql 30u REG 202,1 1024 1181309 /var/lib/mysql/mysql/servers.MYI
mysqld 1071 mysql 31u REG 202,1 0 1181310 /var/lib/mysql/mysql/servers.MYD
mysqld 1071 mysql 32u REG 202,1 2048 1181357 /var/lib/mysql/mysql/event.MYI
mysqld 1071 mysql 33u REG 202,1 0 1181358 /var/lib/mysql/mysql/event.MYD
mysqld 1071 mysql 36u IPv4 3291234 0t0 TCP localhost:mysql->localhost:53538 (ESTABLISHED)
mysqld 1071 mysql 38u IPv4 2948195 0t0 TCP localhost:mysql->localhost:46354 (ESTABLISHED)
gogs 1322 git cwd DIR 202,1 4096 268706 /home/git/gogs
gogs 1322 git rtd DIR 202,1 4096 2 /
gogs 1322 git txt REG 202,1 31081412 268950 /home/git/gogs/gogs
gogs 1322 git mem REG 202,1 23968 401117 /usr/lib64/libcap-ng.so.0.0.0
gogs 1322 git mem REG 202,1 122936 398733 /usr/lib64/libaudit.so.1.0.0
gogs 1322 git mem REG 202,1 2118128 398763 /usr/lib64/libc-2.17.so
gogs 1322 git mem REG 202,1 61672 401115 /usr/lib64/libpam.so.0.83.1
gogs 1322 git mem REG 202,1 143944 399086 /usr/lib64/libpthread-2.17.so
gogs 1322 git mem REG 202,1 19776 401099 /usr/lib64/libdl-2.17.so
gogs 1322 git mem REG 202,1 155464 397967 /usr/lib64/ld-2.17.so
gogs 1322 git 0r CHR 1,3 0t0 4913 /dev/null
gogs 1322 git 1w REG 202,1 101689 266937 /home/git/gogs/log/gogs.log.2017-03-30
通过某个进程号显示该进程打开的文件:lsof -p 5086
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@git ~]lsof -p 5086
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 5086 nginx cwd DIR 202,1 4096 2 /
nginx 5086 nginx rtd DIR 202,1 4096 2 /
nginx 5086 nginx txt REG 202,1 1101400 266975 /usr/sbin/nginx
nginx 5086 nginx mem REG 202,1 23976 658526 /usr/lib64/perl5/vendor_perl/auto/nginx/nginx.so
nginx 5086 nginx mem REG 202,1 80440 402994 /usr/lib64/nginx/modules/ngx_stream_module.so
nginx 5086 nginx mem REG 202,1 101176 402993 /usr/lib64/nginx/modules/ngx_mail_module.so
nginx 5086 nginx mem REG 202,1 19384 399089 /usr/lib64/libgpg-error.so.0.10.0
nginx 5086 nginx mem REG 202,1 534768 401338 /usr/lib64/libgcrypt.so.11.8.2
nginx 5086 nginx mem REG 202,1 157424 399152 /usr/lib64/liblzma.so.5.2.2
nginx 5086 nginx mem REG 202,1 87368 402949 /usr/lib64/libexslt.so.0.8.17
nginx 5086 nginx mem REG 202,1 258344 402952 /usr/lib64/libxslt.so.1.1.28
nginx 5086 nginx mem REG 202,1 1509376 398848 /usr/lib64/libxml2.so.2.9.1
nginx 5086 nginx mem REG 202,1 24560 402989 /usr/lib64/nginx/modules/ngx_http_xslt_filter_module.so
列出所有的网络连接:lsof -i
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@git ~]lsof -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ntpd 424 ntp 16u IPv4 11419 0t0 UDP *:ntp
ntpd 424 ntp 17u IPv6 11581 0t0 UDP *:ntp
ntpd 424 ntp 18u IPv4 11693 0t0 UDP localhost:ntp
ntpd 424 ntp 20u IPv4 12594 0t0 UDP iZ94yebfltlZ:ntp
ntpd 424 ntp 21u IPv4 14475 0t0 UDP git.cowcat.cc:ntp
sshd 821 root 3u IPv4 13479 0t0 TCP *:ssh (LISTEN)
mysqld 1071 mysql 14u IPv4 14746 0t0 TCP *:mysql (LISTEN)
mysqld 1071 mysql 36u IPv4 3291234 0t0 TCP localhost:mysql->localhost:53538 (ESTABLISHED)
mysqld 1071 mysql 38u IPv4 2948195 0t0 TCP localhost:mysql->localhost:46354 (ESTABLISHED)
beanstalk 1284 beanstalkd 3u IPv4 15728 0t0 TCP *:11300 (LISTEN)
redis-ser 1296 redis 4u IPv4 15883 0t0 TCP localhost:6379 (LISTEN)
gogs 1322 git 5u IPv6 16091 0t0 TCP *:hbci (LISTEN)
gogs 1322 git 9u IPv4 3291232 0t0 TCP localhost:53538->localhost:mysql (ESTABLISHED)
gogs 1322 git 12u IPv4 2948194 0t0 TCP localhost:46354->localhost:mysql (ESTABLISHED)
nginx 5085 root 10u IPv4 3027466 0t0 TCP *:http (LISTEN)
nginx 5085 root 11u IPv6 3027467 0t0 TCP *:http (LISTEN)
nginx 5086 nginx 10u IPv4 3027466 0t0 TCP *:http (LISTEN)
nginx 5086 nginx 11u IPv6 3027467 0t0 TCP *:http (LISTEN)
php-fpm 5094 root 6u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 5096 www 0u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 5097 www 0u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 5098 www 0u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 5099 www 0u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 5100 www 0u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
列出所有 TCP 网络连接信息:lsof -i tcp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@git ~]lsof -i tcp
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sshd 821 root 3u IPv4 13479 0t0 TCP *:ssh (LISTEN)
mysqld 1071 mysql 14u IPv4 14746 0t0 TCP *:mysql (LISTEN)
mysqld 1071 mysql 36u IPv4 3291234 0t0 TCP localhost:mysql->localhost:53538 (ESTABLISHED)
mysqld 1071 mysql 38u IPv4 2948195 0t0 TCP localhost:mysql->localhost:46354 (ESTABLISHED)
beanstalk 1284 beanstalkd 3u IPv4 15728 0t0 TCP *:11300 (LISTEN)
redis-ser 1296 redis 4u IPv4 15883 0t0 TCP localhost:6379 (LISTEN)
gogs 1322 git 5u IPv6 16091 0t0 TCP *:hbci (LISTEN)
gogs 1322 git 9u IPv4 3291232 0t0 TCP localhost:53538->localhost:mysql (ESTABLISHED)
gogs 1322 git 12u IPv4 2948194 0t0 TCP localhost:46354->localhost:mysql (ESTABLISHED)
nginx 5085 root 10u IPv4 3027466 0t0 TCP *:http (LISTEN)
nginx 5085 root 11u IPv6 3027467 0t0 TCP *:http (LISTEN)
nginx 5086 nginx 10u IPv4 3027466 0t0 TCP *:http (LISTEN)
nginx 5086 nginx 11u IPv6 3027467 0t0 TCP *:http (LISTEN)
php-fpm 5094 root 6u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 5096 www 0u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 5097 www 0u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 5098 www 0u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 5099 www 0u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 5100 www 0u IPv4 3027595 0t0 TCP localhost:cslistener (LISTEN)
列出谁在使用某个端口:lsof -i
1
2
3
4
5
6
7
[root@git ~]lsof -i :3306
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 1071 mysql 14u IPv4 14746 0t0 TCP *:mysql (LISTEN)
mysqld 1071 mysql 36u IPv4 3291234 0t0 TCP localhost:mysql->localhost:53538 (ESTABLISHED)
mysqld 1071 mysql 38u IPv4 2948195 0t0 TCP localhost:mysql->localhost:46354 (ESTABLISHED)
gogs 1322 git 9u IPv4 3291232 0t0 TCP localhost:53538->localhost:mysql (ESTABLISHED)
gogs 1322 git 12u IPv4 2948194 0t0 TCP localhost:46354->localhost:mysql (ESTABLISHED)
列出某个用户的所有活跃的网络端口:lsof -a -u git -i
1
2
3
4
5
[root@git ~]lsof -a -u git -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
gogs 1322 git 5u IPv6 16091 0t0 TCP *:hbci (LISTEN)
gogs 1322 git 9u IPv4 3291232 0t0 TCP localhost:53538->localhost:mysql (ESTABLISHED)
gogs 1322 git 12u IPv4 2948194 0t0 TCP localhost:46354->localhost:mysql (ESTABLISHED)
根据文件描述列出对应的文件信息:lsof -d 1
1
2
3
4
5
6
7
8
9
[root@git ~]lsof -d 1
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root 1u CHR 1,3 0t0 4913 /dev/null
systemd-j 335 root 1w CHR 1,3 0t0 4913 /dev/null
systemd-u 353 root 1u unix 0xffff8800365cdc00 0t0 10448 socket
systemd-l 420 root 1u unix 0xffff880036f57c00 0t0 11213 socket
dbus-daem 423 dbus 1u unix 0xffff88003a1c4800 0t0 11275 socket
ntpd 424 ntp 1u CHR 1,3 0t0 4913 /dev/null
crond 430 root 1u unix 0xffff88003b0d9800 0t0 11872 socket

lsof 输出列信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
COMMAND:进程的名称
PID:进程标识符
PPID:父进程标识符(需要指定-R参数)
USER:进程所有者
PGID:进程所属组
FD:文件描述符,应用程序通过文件描述符识别该文件。如cwd、txt等:
cwd:表示 current work dirctory,即:应用程序的当前工作目录,这是该应用程序启动的目录,除非它本身对这个目录进行更改
txt :该类型的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序
lnn:library references (AIX);
er:FD information error (see NAME column);
jld:jail directory (FreeBSD);
ltx:shared library text (code and data);
mxx :hex memory-mapped type number xx.
m86:DOS Merge mapped file;
mem:memory-mapped file;
mmap:memory-mapped device;
pd:parent directory;
rtd:root directory;
tr:kernel trace file (OpenBSD);
v86 VP/ix mapped file;
0:表示标准输入
1:表示标准输出
2:表示标准错误,一般在标准输出、标准错误、标准输入后还跟着文件状态模式:r、w、u等
u:表示该文件被打开并处于读取/写入模式
r:表示该文件被打开并处于只读模式
w:表示该文件被打开并处于
空格:表示该文件的状态模式为 unknow,且没有锁定
-:表示该文件的状态模式为 unknow,且被锁定,同时在文件状态模式后面,还跟着相关的锁
N:for a Solaris NFS lock of unknown type;
r:for read lock on part of the file;
R:for a read lock on the entire file;
w:for a write lock on part of the file;(文件的部分写锁)
W:for a write lock on the entire file;(整个文件的写锁)
u:for a read and write lock of any length;
U:for a lock of unknown type;
x:for an SCO OpenServer Xenix lock on part of the file;
X:for an SCO OpenServer Xenix lock on the entire file;
space:if there is no lock.
TYPE:文件类型,如 DIR、REG 等,常见的文件类型:
DIR:表示目录
CHR:表示字符类型
BLK:块设备类型
UNIX: UNIX 域套接字
FIFO:先进先出 (FIFO) 队列
IPv4:网际协议 (IP) 套接字
DEVICE:指定磁盘的名称
SIZE:文件的大小
NODE:索引节点(文件在磁盘上的标识)
NAME:打开文件的确切名称

top:显示或管理执行中的程序

top 命令可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。通过 top 命令所提供的互动式界面,用热键可以管理。

top 语法

1
top (选项)

top 选项

1
2
3
4
5
6
7
8
9
10
-b 以批处理模式操作
-c 显示完整的治命令
-d 屏幕刷新间隔时间
-I 忽略失效过程
-s 保密模式
-S 累积模式
-i<时间> 设置间隔时间
-u<用户名> 指定用户名
-p<进程号> 指定进程
-n<次数> 循环显示的次数

top 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
top - 17:20:24 up 69 days, 23:59, 1 user, load average: 0.00, 0.01, 0.05
Tasks: 104 total, 1 running, 103 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1014984 total, 82768 free, 619980 used, 312236 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 233544 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5788 root 20 0 157576 2164 1532 R 0.3 0.2 0:00.03 top
1 root 20 0 190544 2764 1588 S 0.0 0.3 3:25.26 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.02 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:08.42 ksoftirqd/0
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
6 root 20 0 0 0 0 S 0.0 0.0 0:18.55 kworker/u30:0

top 输出列信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
top - 17:20:24 当前系统时间
up 69 days, 23:59 系统已经运行了 69 天 23 小时 59 分
1 user 当前系统在线用户人数
load average: 0.00, 系统负载
0.01,
0.05
Tasks: 104 total 总进程数
1 running 正在运行的进程数
103 sleeping 正在休眠的进程数
0 stopped 停止运行的进程数
0 zombie 已经僵死的进程数
Cpu(s): 0.0 us 用户空间占用 CPU 百分比
0.3 sy 内核空间占用 CPU 百分比
0.0 ni 用户进程空间内改变过优先级的进程占用 CPU 百分比
99.7 id 空闲 CPU 百分比
0.0 wa 等待 I/O 的 CPU 时间百分比
0.0 hi CPU 硬中断时间百分比
0.0 si CPU 软中断时间百分比
0.0 st
KiB Mem : 1014984 total 物理内存总量
82768 free 空闲内存总量
619980 used 使用中的物理内存总量
312236 buff/cache 用作内核缓存的内存量
KiB Swap: 0 total, 交换区总量
0 free, 空闲交换区总量
0 used. 使用的交换区总量
233544 avail Mem 缓冲的交换区总量
PID 进程ID
USER 进程所有者
PR 进程优先级,越小越优先执行
NI
VIRT 进程占用的虚拟内存
RES 进程占用的物理内存
SHR 进程使用的共享内存
S 进程的状态。S 表示休眠,R 表示正在运行,Z 表示僵死状态,N 表示该进程优先值为负数
%CPU 进程占用 CPU 的使用率
%MEM 进程使用的物理内存和总内存的百分比
TIME+ 该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。
COMMAND 进程启动命令名称

top 交互命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
h 显示帮助画面,给出一些简短的命令总结说明;
k 终止一个进程;
i 忽略闲置和僵死进程,这是一个开关式命令;
q 退出程序;
r 重新安排一个进程的优先级别;
S 切换到累计模式;
s 改变两次刷新之间的延迟时间(单位为s),如果有小数,就换算成ms。输入0值则系统将不断刷新,默认值是5s;
f 或者 F 从当前显示中添加或者删除项目;
o 或者 O 变显示项目的顺序;
l 切换显示平均负载和启动时间信息;
m 切换显示内存信息;
t 切换显示进程和CPU状态信息;
c 切换显示命令名称和完整命令行;
M 根据驻留内存大小进行排序;
P 根据CPU使用百分比大小进行排序;
T 根据时间/累计时间进行排序;
w 将当前设置写入~/.toprc文件中。

vmstat:显示虚拟内存状态

vmstat 命令的含义为显示虚拟内存状态(“Viryual Memor Statics”),但是它可以报告关于进程、内存、I/O等系统整体运行状态。

语法

1
vmstat(选项)(参数)

选项

1
2
3
4
5
6
7
8
-a:显示活动内页;
-f:显示启动后创建的进程总数;
-m:显示slab信息;
-n:头信息仅显示一次;
-s:以表格方式显示事件计数器和内存状态;
-d:报告磁盘状态;
-p:显示指定的硬盘分区状态;
-S:输出信息的单位。

参数

  • 事件间隔:状态信息刷新的时间间隔
  • 次数:显示报告的次数

实例

1
2
3
4
5
6
7
8
9
10
[root@git ~]# vmstat 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 260224 123132 367448 0 0 0 2 7 5 0 0 100 0 0
0 0 0 260224 123132 367480 0 0 0 0 141 291 0 0 100 0 0
0 0 0 260224 123140 367472 0 0 0 4 132 280 0 0 99 0 0
0 0 0 260224 123140 367480 0 0 0 0 155 322 1 0 99 0 0
0 0 0 260224 123140 367480 0 0 0 0 132 282 0 0 100 0 0
0 0 0 260224 123140 367480 0 0 0 0 132 279 0 0 100 0 0
0 0 0 260224 123140 367480 0 0 0 0 125 274 0 0 100 0 0

Procs(进程)

r: 运行队列中进程数量,这个值也可以判断是否需要增加CPU。(长期大于1)
b: 等待IO的进程数量。
Memory(内存)

swpd: 使用虚拟内存大小,如果swpd的值不为0,但是SI,SO的值长期为0,这种情况不会影响系统性能。
free: 空闲物理内存大小。
buff: 用作缓冲的内存大小。
cache: 用作缓存的内存大小,如果cache的值大的时候,说明cache处的文件数多,如果频繁访问到的文件都能被cache处,那么磁盘的读IO bi会非常小。
Swap

si: 每秒从交换区写到内存的大小,由磁盘调入内存。
so: 每秒写入交换区的内存大小,由内存调入磁盘。
注意:内存够用的时候,这2个值都是0,如果这2个值长期大于0时,系统性能会受到影响,磁盘IO和CPU资源都会被消耗。有些朋友看到空闲内存(free)很少的或接近于0时,就认为内存不够用了,不能光看这一点,还要结合si和so,如果free很少,但是si和so也很少(大多时候是0),那么不用担心,系统性能这时不会受到影响的。

IO(现在的Linux版本块的大小为1kb)

bi: 每秒读取的块数
bo: 每秒写入的块数
注意:随机磁盘读写的时候,这2个值越大(如超出1024k),能看到CPU在IO等待的值也会越大。

system(系统)

in: 每秒中断数,包括时钟中断。
cs: 每秒上下文切换数。
注意:上面2个值越大,会看到由内核消耗的CPU时间会越大。

CPU(以百分比表示)

us: 用户进程执行时间百分比(user time)
us的值比较高时,说明用户进程消耗的CPU时间多,但是如果长期超50%的使用,那么我们就该考虑优化程序算法或者进行加速。

sy: 内核系统进程执行时间百分比(system time)
sy的值高时,说明系统内核消耗的CPU资源多,这并不是良性表现,我们应该检查原因。

wa: IO等待时间百分比
wa的值高时,说明IO等待比较严重,这可能由于磁盘大量作随机访问造成,也有可能磁盘出现瓶颈(块操作)。

id: 空闲时间百分比

netstat:查看Linux中网络系统状态信息

TODO

sar:系统运行状态统计工具

sar 命令是 Linux 下系统运行状态统计工具,它将指定的操作系统状态计数器显示到标准输出设备。sar 工具将对系统当前的状态进行取样,然后通过计算数据和比例来表达系统的当前运行状态。它的特点是可以连续对系统取样,获得大量的取样数据。取样数据和分析的结果都可以存入文件,使用它时消耗的系统资源很小。

sar 语法

1
sar(选项)(参数)

sar 选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-A:显示所有的报告信息;
-b:显示I/O速率;
-B:显示换页状态;
-c:显示进程创建活动;
-d:显示每个块设备的状态;
-e:设置显示报告的结束时间;
-f:从指定文件提取报告;
-i:设状态信息刷新的间隔时间;
-P:报告每个CPU的状态;
-R:显示内存状态;
-u:显示CPU利用率;
-v:显示索引节点,文件和其他内核表的状态;
-w:显示交换分区状态;
-x:显示给定进程的状态。

sar 参数

  • 间隔时间:每次报告的间隔时间(秒);
  • 次数:显示报告的次数。

sar 实例

ss:获取 Socket 统计信息

mysqldumpslow 参数讲解与基础使用

目录

认识 mysqldumpslow

mysqldumpslow 是一个针对于 MySQL 慢查询的命令行程序。在配置 MySQL 相关参数后,可以通过 mysqldumpslow 查找出查询较慢的 SQL 语句。

进入 MySQL 命令行,执行命令 mysql> show variables like "%query%" ;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mysql> mysql> show variables like "%quer%" ;
+----------------------------------------+-----------------------------------------------------+
| Variable_name | Value |
+----------------------------------------+-----------------------------------------------------+
| binlog_rows_query_log_events | OFF |
| ft_query_expansion_limit | 20 |
| have_query_cache | YES |
| log_queries_not_using_indexes | OFF |
| log_throttle_queries_not_using_indexes | 0 |
| long_query_time | 10.000000 |
| query_alloc_block_size | 8192 |
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 1048576 |
| query_cache_type | OFF |
| query_cache_wlock_invalidate | OFF |
| query_prealloc_size | 8192 |
| slow_query_log | OFF |
| slow_query_log_file | /usr/local/var/mysql/luyiyuandeMacBook-Pro-slow.log |
+----------------------------------------+-----------------------------------------------------+
15 rows in set (0.01 sec)

与 mysqldumpslow 相关的配置变量

  • slow_query_log:是否开启慢查询日志
  • long_query_time:是否设置慢查询的 SQL 执行规定时间
  • slow_query_log_file:设置慢查询日志记录位置
  • log_queries_not_using_indexes:是否设置了把没有索引的记录到慢查询日志

配置变量设置格式如下:

1
2
3
4
5
6
7
8
9
10
11
# 开启慢查询日志
set global slow_query_log=on;
# 设置没有索引的记录到慢查询日志
set global log_queries_not_using_indexes=on;
# 设置到慢查询日志的 SQL 执行时间(1 代表 1 秒)
set global long_query_time=1;
# 设置慢查询日志的存放位置
set global slow_query_log_file="/Users/LuisEdware/Code/output/mysql-slow.log";

设置完毕后,不要重启 MySQL 服务,否则设置会失效,如果想要配置持久生效,需要在 my.ini 配置文件编辑上述变量。

mysqldumpslow 的命令参数

执行命令 mysqldumpslow --help,显示命令参数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Usage: mysqldumpslow [ OPTS... ] [ LOGS... ]
Parse and summarize the MySQL slow query log. Options are
--verbose verbose
--debug debug
--help write this text to standard output
-v verbose
-d debug
-s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default
al: average lock time
ar: average rows sent
at: average query time
c: count
l: lock time
r: rows sent
t: query time
-r reverse the sort order (largest last instead of first)
-t NUM just show the top n queries
-a don't abstract all numbers to N and strings to 'S'
-n NUM abstract numbers with at least n digits within names
-g PATTERN grep: only consider stmts that include this string
-h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard),
default is '*', i.e. match all
-i NAME name of server instance (if using mysql.server startup script)
-l don't subtract lock time from total time

命令参数意义如下:

  • -v、–verbose

    在详细模式下运行,打印有关该程序的更多信息。

  • -d、–debug

    在调试模式下运行。

  • –help

    显示帮助信息并退出程序

  • -s [sort_type]

    sort_type 是信息排序的依据

    • al:按平均锁定时间排序
    • ar:按平均返回行数排序
    • at:按平均查询时间排序
    • c:按计数排序
    • l:按锁定时间排序
    • r:按返回函数排序
    • t:按查询时间排序
  • -r 「reverse the sort order (largest last instead of first)」

    倒序信息排序

  • -t NUM「just show the top n queries」

    只显示前 n 个查询

  • -a 「Do not abstract all numbers to N and strings to ‘S’.」

    TODO

  • -n NUM 「abstract numbers with at least n digits within names」

    TODO

  • -g PATTERN 「grep: only consider stmts that include this string」

    根据字符串筛选慢查询日志

  • -h HOSTNAME 「hostname of db server for -slow.log filename (can be wildcard), default is ‘‘, i.e. match all」

    根据服务器名称选择慢查询日志

  • -i NAME 「name of server instance (if using mysql.server startup script)」

    根据服务器 MySQL 实例名称选择慢查询日志

  • -l 「don’t subtract lock time from total time」

    不要将总时间减去锁定时间

mysqldumpslow 的结果参数

使用 Vim 打开慢查询日志 mysql-slow.log,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/usr/local/opt/mysql/bin/mysqld, Version: 5.7.18 (Homebrew). started with:
Tcp port: 3306 Unix socket: /tmp/mysql.sock
Time Id Command Argument
/usr/local/opt/mysql/bin/mysqld, Version: 5.7.18 (Homebrew). started with:
Tcp port: 3306 Unix socket: /tmp/mysql.sock
Time Id Command Argument
# Time: 2017-06-03T06:47:46.502825Z
# User@Host: root[root] @ localhost [] Id: 3
# Query_time: 0.195360 Lock_time: 0.000131 Rows_sent: 10000 Rows_examined: 10000
use bingoshuiguo;
SET timestamp=1496472466;
select * from z_order limit 10000;
# Time: 2017-06-03T06:48:27.030315Z
# User@Host: root[root] @ localhost [] Id: 3
# Query_time: 1.896889 Lock_time: 0.000823 Rows_sent: 100000 Rows_examined: 100000
SET timestamp=1496472507;
select * from z_order limit 100000;
# Time: 2017-06-03T06:53:37.786379Z
# User@Host: root[root] @ localhost [] Id: 3
# Query_time: 3.456264 Lock_time: 0.008454 Rows_sent: 100000 Rows_examined: 200000
SET timestamp=1496472817;
select * from z_order left join z_league on z_order.league_id = z_league.id limit 100000;
# Time: 2017-06-03T07:03:25.615137Z
# User@Host: root[root] @ localhost [] Id: 3
# Query_time: 3.837932 Lock_time: 0.000648 Rows_sent: 100000 Rows_examined: 200000
SET timestamp=1496473405;
select * from z_order left join z_league on z_order.league_id = z_league.id limit 100000;

其中参数如下:

  • SQL 的执行时间:# Time: 2017-06-03T06:53:37.786379Z
  • SQL 的执行主机:# User@Host: root[root] @ localhost [] Id: 3
  • SQL 的执行信息:# Query_time: 3.456264 Lock_time: 0.008454 Rows_sent: 100000 Rows_examined: 200000
  • SQL 的执行时间:SET timestamp=1496472817;
  • SQL 的执行内容:select * from z_order left join z_league on z_order.league_id = z_league.id limit 100000;

执行 mysqldumpslow 的命令 mysqldumpslow mysql-slow.log,查看内容如下:

1
2
3
4
5
6
Reading mysql slow query log from mysql-slow.log
Count: 2 Time=3.64s (7s) Lock=0.00s (0s) Rows=100000.0 (200000), root[root]@localhost
select * from z_order left join z_league on z_order.league_id = z_league.id limit N
Count: 2 Time=1.05s (2s) Lock=0.00s (0s) Rows=55000.0 (110000), root[root]@localhost
select * from z_order limit N

  • Count:出现次数,
  • Time:执行最长时间和累计总耗费时间
  • Lock:等待锁的时间
  • Rows:返回客户端行总数和扫描行总数