在 Yii 2 中使用 CodeCeption

安装 Yii2

使用 Composer 安装 Yii 2 高级模板项目,Yii 安装文档

1
2
3
4
5
6
7
8
# 安装 Yii
composer global require "fxp/composer-asset-plugin:^1.2.0"
composer create-project --prefer-dist yiisoft/yii2-app-advanced yii-application
# Yii 初始化
cd yii-application
composer install
yii init

配置 Apache VirtualHost

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
<VirtualHost *:80>
ServerName frontend.dev
DocumentRoot "/path/to/yii-application/frontend/web/"
<Directory "/path/to/yii-application/frontend/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>
<VirtualHost *:80>
ServerName backend.dev
DocumentRoot "/path/to/yii-application/backend/web/"
<Directory "/path/to/yii-application/backend/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>

安装 Codeception

如果在项目的 vendor 目录看到 codeception 目录,代表 Yii 集成了 codeception package,不需要单独安装 codeception。

执行以下命令安装 Codeception package

1
2
3
4
5
6
7
# Codeception
sudo composer global require "codeception/codeception=*"
sudo composer global require "codeception/specify=*"
sudo composer global require "codeception/verify=*"
# Facker 用于虚拟数据生成
sudo composer require --dev "yiisoft/yii2-faker=*"

将 codeception 命令配置进环境变量中

1
2
3
4
5
6
7
8
vim .zshrc
# 如果在 Composer 安装 Codeception,alias 路径如下
alias codecept="/Users/用户名称/.composer/vendor/codeception/codeception/codecept"
# 如果在项目根目录下安装 Codeception,alias 路径如下
alias codecept="/Users/用户名称/项目路径/vendor/codeception/base/codecept"
source .zshrc

在项目中使用 codeception

1
2
3
4
cd frontend/
codecept bootstrap
codecept build
codecept run

单元测试(Unit Test)

单元测试是针对程序模块来进行正确性检验的测试工作,可以通过单元测试来确认深藏在代码里面的某些功能仍然可用, 单元测试能消除程序单元的不可靠性。

Codeception 的单元测试功能是基于 PHPUnit 之上的, 你可以照样写 PHPUnit 的测试代码, Codeception 一样能运行。PHPUnit 官方文档

创建单元测试

使用 Codeception 的生成器来执行以下命令生成单元测试

1
2
cd frontend
codecept generate:test unit SimpleExample

上述命令会在 tests/unit 目录创建一个 ExampleTest 文件,代码如下:

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
<?php
class SimpleExampleTest extends \Codeception\Test\Unit
{
/**
* @var \UnitTester
*/
protected $tester;
protected $stack;
protected function _before()
{
$this->stack = [];
}
protected function _after()
{
}
// tests array push and array pop
public function testArrayPushAndArrayPop()
{
$this->stack = [];
$this->assertEquals(0, count($this->stack));
array_push($this->stack, 'foo');
$this->assertEquals('foo', $this->stack[count($this->stack) - 1]);
$this->assertEquals(1, count($this->stack));
$value = array_pop($this->stack);
$this->assertEquals('foo', $value);
$this->assertEquals(0, count($this->stack));
}
}

测试依赖

通过 @depends 让测试方法之间进行依赖关联

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 DependsExampleTest extends \Codeception\Test\Unit
{
/**
* @var \UnitTester
*/
protected $tester;
public function testEmpty()
{
$stack = [];
$this->assertEquals(0, count($stack));
return $stack;
}
/**
* @param $array
*
* @return array
*
* @depends testEmpty
*/
public function testPush(array $array)
{
array_push($array, 'LuisEdward');
$this->assertEquals('LuisEdward', $array[count($array) - 1]);
$this->assertNotEquals('Leon', $array[count($array) - 1]);
$this->assertEquals(1, count($array));
return $array;
}
/**
* @param $arrayAliasNames
*
* @return array
*
* @depends testPush
*/
public function testPop(array $arrayAliasNames)
{
$this->assertEquals('LuisEdward', array_pop($arrayAliasNames));
$this->assertEmpty($arrayAliasNames);
}
protected function _before()
{
}
protected function _after()
{
}
}
抛出异常

通过 @expectedException 或方法 $this->expectException() 在测试实例中抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
namespace frontend\tests;
class ExceptionTest extends \Codeception\Test\Unit
{
public function testException()
{
$this->expectException(InvalidArgumentException::class);
}
/**
* @expectedException InvalidArgumentException
*/
public function testExceptionTow()
{
}
}
断点输出

通过方法 $this->expectOutputString() 生成一个预期的输出。当实际输出和预期输出不一致时,则测试不通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
use PHPUnit\Framework\TestCase;
class OutputTest extends TestCase
{
public function testExpectFooActualFoo()
{
$this->expectOutputString('foo');
print 'foo';
}
public function testExpectBarActualBaz()
{
$this->expectOutputString('bar');
print 'baz';
}
}

Codeception 的更多 Unit Tests 信息请参考链接:

创建接口测试(RESTFUL API TEST)

创建 RESTFUL API 测试配置文件,新增文件 api.suite.yml 和文件夹 api

1
codecept generate:suite api

修改配置文件 api.suite.yml 如下:

1
2
3
4
5
6
7
actor: ApiTester
modules:
enabled:
- REST:
url: http://workerbee.app/v1/
depends: PhpBrowser
part: Json

创建 RESTFUL API 测试文件

1
codecept generate:cept api UserSignIn

修改生成文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$I = new ApiTester($scenario);
$I->haveHttpHeader('Content-Type', 'application/x-www-form-urlencoded');
$I->haveHttpHeader('Accept', 'application/json');
$I->sendPOST('user/sign-in', ['username' => 'admin', 'pwd' => '123456']);
$I->seeResponseIsJson();
$I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK);
$I->seeResponseMatchesJsonType([
"id" => 'integer',
"username" => "string",
"password_hash" => "string",
"access_token" => "string",
"email" => "string:email",
"status" => 'integer',
"role" => "string",
"created" => 'integer',
"updated" => 'integer',
"mobile" => 'string||null||integer',
]);

常用的 REST API 如下:

  • wantTo():配置测试标题
  • sendGET():发送 HTTP GET 请求
  • sendPUT():发送 HTTP PUT 请求
  • sendPOST():发送 HTTP POST 请求
  • sendDELETE():发送 HTTP DELETE 请求
  • haveHttpHeader():设置 HTTP 请求头
  • seeHttpHeader():检查 HTTP 请求头
  • seeResponseCodeIs():检查响应状态码
  • seeResponseContains():检查响应内容
  • seeResponseContainsJson():检查响应数据类型是否为 JSON 和核对响应内容
  • seeResponseIsJson():检查响应是否返回 JSON 数据
  • seeResponseJsonMatchesJsonPath():使用 JSON 数据去检查响应内容
  • seeResponseMatchesJsonType():检查响应数据的数据类型

Codeception 的更多 RESTFUL API 信息请参考链接:

数据库测试(DB TEST)

常用的 DB TEST API 如下:

  • seeInDatabase()
  • dontSeeInDatabase()
  • haveInDatabase()
  • grabFromDatabase()

示例代码如下:

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
// 检查 User 表是否存在 'username' 为 'Davert', 'email' 为 'davert@mail.com' 的数据,存在则报错
$I->dontSeeInDatabase('user', ['username' => 'Davert', 'email' => 'davert@mail.com']);
// 检查 User 表是否存在 'username' 为 'okirlin', 'email' 为 'brady.renner@rutherford.com' 的数据,不存在则报错
$I->seeInDatabase('user', ['username' => 'okirlin', 'email' => 'brady.renner@rutherford.com']);
// 检查 User 表是否能够新增以下数据,无法新增则报错
$I->haveInDatabase('user',
[
'username' => 'miles',
'email' => 'miles@davis.com',
'auth_key' => 'EdKfXrx88weFMV0vIxuTMWKgfK2tS321',
'password_hash' => '$2y$13$g5nv41Px7VBqhS3hVsVN2.MKfgT3jFdkXEsMC4rQJLfaMa7VaJqL2',
'password_reset_token' => '4BSNyiZNAuxjs5Mty990c47sVrgllIi_1487317115',
'email' => '506510463@qq.com',
'role' => 10,
'status' => 0,
'created_at' => 1391885313,
'updated_at' => 1391885313,
]);
// 按照正序获取一条 username = miles 的数据的 email 字段值
$mail = $I->grabFromDatabase('user', 'email', array('username' => 'troy.becker'));
var_dump($mail);exit;

Codeception 的更多 DB TEST API 信息请参考链接:

LuisEdware 版 PHP 编程规范(遵循 PSR-1/PSR-2)

目录

  1. 八荣八耻
  2. 代码风格
  3. 目录与文件
  4. 函数、成员属性、成员方法
  5. 数据表和字段
  6. 常量和配置
  7. 示例代码
  8. 其他约定
  9. 参考链接

八荣八耻

  • 以「动手实践」为荣, 以「只看不练」为耻;
  • 以「打印日志」为荣, 以「单步跟踪」为耻;
  • 以「空格缩进」为荣, 以「制表缩进」为耻;
  • 以「单元测试」为荣, 以「人工测试」为耻;
  • 以「模块复用」为荣, 以「复制粘贴」为耻;
  • 以「多态应用」为荣, 以「分支判断」为耻;
  • 以「优雅高效」为荣, 以「冗余拖沓」为耻;
  • 以「总结分享」为荣, 以「跪求其解」为耻。

代码风格

编程开发时,需要遵循 PSR 规范如下:

目录与文件

  • 目录使用小写加下划线;
  • 类库、函数文件统一以 .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
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
<?php
/**
* 符合 RSR-1,RSR-2 的编程实例
*
* @Date 2017/02/17 18:00
* @Author 七月十五九月初七
* @License 广东七月十五九月初七科技有限公司
* @Copyright Copyright (c) 2017 PSR-1.PSR-2 Ltd
*/
namespace PSR; // 顶部命名空间
use Yii; // 空一行 use 引入类
/**
* 类描述
* 类名必须大写开头驼峰.
*/
abstract class ExampleClass // {}必须换行
{
/**
* 常量描述
*
* @var string
*/
const THIS_IS_A_CONST = ''; // 常量全部大写下划线分割
/**
* 属性描述
*
* @var string
*/
public $nameTest = ''; // 属性名称建议开头小写驼峰,成员属性必须添加public(不能省略), private, protected修饰符
/**
* 属性描述
*
* @var string
*/
private $_privateNameTest = ''; // 类私有成员属性,【个人建议】下划线小写开头驼峰
/**
* 构造函数
*
* 构造函数描述
*
* @param string $value 形参名称/描述
*/
public function __construct($value = '')// 成员方法必须添加public(不能省略), private, protected修饰符
{// {}必须换行
$this->nameTest = new TestClass();
// 链式操作
$this->nameTest->functionOne()->functionTwo()->functionThree();
// 一段代码逻辑执行完毕 换行
// code...
}
/**
* 成员方法名称
*
* 成员方法描述
*
* @param string $value 形参名称/描述
*
* @return 返回值类型 返回值描述
*/
public static function staticFunction($value = '')// static位于修饰符之后
{
// code...
}
/**
* 成员方法名称
*
* 成员方法描述
*
* @param string $value 形参名称/描述
*
* @return 返回值类型 返回值描述
* 返回值类型:string,array,object,mixed(多种,不确定的),void(无返回值)
*/
public function testFunction($value = '')// 成员方法必须小写开头驼峰
{
// code...
}
/**
* 成员方法名称
*
* 成员方法描述
*
* @param string $value 形参名称/描述
*
* @return 返回值类型 返回值描述
*/
abstract public function abstractFunction($value = '');
// abstract 位于修饰符之前
/**
* 成员方法名称.
* 成员方法描述
*
* @param string $value 形参名称/描述
*
* @return 返回值类型 返回值描述
*/
final public function finalFunction($value = '')// final 位于修饰符之前
{
// code...
}
/**
* 成员方法名称
*
* 成员方法描述
*
* @param string $paramOne 形参名称/描述
* @param string $paramTwo 形参名称/描述
* @param string $paramThree 形参名称/描述
* @param string $paramFour 形参名称/描述
* @param string $paramFive 形参名称/描述
* @param string $paramSix 形参名称/描述
*
* @return 返回值类型 返回值描述
*/
public function tooLangFunction(
$paramOne = '', // 变量命名可小写开头驼峰或者下划线命名
$paramTwo = '',
$paramThree = '',
$paramFour = '',
$paramFive = '',
$paramSix = ''
)// 参数过多换行
{
if ($paramOne === $paramTwo) {// 控制结构=>后加空格,同{一行,(右边和)左边不加空格
// code...
}
if ($paramTwo === $paramThree) {
} elseif ($paramFour === $paramFive) {
} else {
}
switch ($paramThree) {
case 'three':
// code...
break;
case 'one':
case 'two':
case 'four':
case 'five':
// code...
break;
default:
// code...
break;
}
do {
// code...
} while ($paramFour <= 10);
while ($paramFive <= 10) {
// code...
}
for ($i = 0; $i < $paramSix; $i++) {
// code...
}
}
/**
* 成员方法名称
*
* 成员方法描述
*
* @param string $value 形参名称/描述
*
* @return 返回值类型 返回值描述
*/
private function _privateTestFunction($value = '')// 私有成员方法【个人建议】下划线小写开头驼峰
{
// code...
}
}

其他约定

团队约定:

  • 统一在 MacOS 下使用 PHPStorm 项目开发
  • 使用相同版本的 Homestead 进行开发环境部署
  • 使用相同版本的 PHPStorm
  • 使用统一设置的 PHPStorm 快捷键
  • 使用相同设置的 PHPStorm 忽略文件列表
  • _ide_helper.php 加入版本库
  • 使用统一规范的代码格式化配置
  • 使用统一标准的命名方式

执行目标:

  • 减少沟通成本
  • 减少代码冲突
  • 新人快速培训
  • 快速稳定完成任务
  • 无惊无险又到六点

使用 PHPStorm 的优点

  • 丰富易配的调试
  • 快捷安全的重构
  • 明确可控的跳转
  • 丰富稳定的工具
  • 健壮的语言支持

参考链接

PHP Codebook(九)

9.1 处理表单输入

问题

希望使用一个 HTML 页面提交表单,然后在同一个页面中处理这个表单中输入的数据

实现

使用 $_SERVER[‘REQUEST_METHOD’] 变量来确定请求是用 get 还是 post 方法提交的。

9.2 验证表单输入:必填域

问题

希望确保必须为一个表单元素提供一个值。例如,希望保证一个文本框不为空

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
if (!(filter_has_var(INPUT_GET, 'flavor')
&& (strlen(filter_input(INPUT_GET, 'flavor'))))
) {
print "You must enter your favorite ice cream flavor.\n";
} else {
echo $_GET['flavor'];
}
if ((filter_has_var(INPUT_GET, 'color'))
&& (strlen(filter_input(INPUT_GET, 'color', FILTER_SANITIZE_STRING)) <= 5)
) {
print "Color must be more than 5 characters.";
}
if (!(filter_has_var(INPUT_GET, 'choices'))
&& filter_input(INPUT_GET, 'choices', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY)
) {
print "You must select some choices.\n";
}
?>

9.3 验证表单输入:数字

问题

希望确保在一个表单输入框中输入了一个数。

实现

1
2
3
4
5
6
7
8
9
10
<?php
$age = filter_input(INPUT_GET, 'age', FILTER_VALIDATE_INT);
if ($age === false) {
print "Submitted age is invalid.";
}
$price = filter_input(INPUT_GET, 'price', FILTER_VALIDATE_FLOAT);
if ($price === false) {
print "Submitted price is invalid.";
}

9.4 验证表单输入:email 地址

问题

希望知道用户提供的一个 email 地址是否合法

实现

1
2
3
4
5
<?php
$email = filter_input(INPUT_GET, 'email', FILTER_VALIDATE_EMAIL);
if ($email === false) {
print "Submitted email is invalid.";
}

9.16 处理变量名中包含点号的远程变量

问题

希望处理一个变量,变量名中有一个点号,不过提交表单时,无法在 $_GET 或者 $_POST 中找到这个变量。

实现

将变量名中的点号替换为一个下划线。

PHP Codebook(六)

6.4 使用命名参数

问题

希望按名为函数指定参数,而不是通过函数调用时的位置来指定。

实现

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 image1($params)
{
if (!isset($params['src'])) {
$image['src'] = 'cow.png';
}
if (!isset($params['alt'])) {
$image['alt'] = 'Milk Factory';
}
if (!isset($params['height'])) {
$image['height'] = '100';
}
if (!isset($params['width'])) {
$image['width'] = '50';
}
return '<img src="' . $image['src'] . '" alt="' . $image['alt'] . '" width="' . $image['width'] . '" height="' . $image['height'] . '">';
}
function image2($params)
{
$defaults = [
'src' => 'cow.png',
'alt' => 'milk factory',
'width' => 100,
'height' => 50,
];
$params = array_merge($defaults, $params);
return '<img src="' . $image['src'] . '" alt="' . $image['alt'] . '" width="' . $image['width'] . '" height="' . $image['height'] . '">';
}
?>

6.6 创建参数个数可变的函数

问题

希望定义一个参数个数可变的函数

实现

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
<?php
function mean($numbers)
{
$sum = 0;
$size = count($numbers);
for ($i = 0; $i < $size; $i++) {
$sum += $numbers[$i];
}
$average = $sum / $size;
return $average;
}
echo mean([96, 93, 98, 98]) . "\n";
function mean1()
{
$sum = 0;
$size = func_num_args();
for ($i = 0; $i < $size; $i++) {
$sum += func_get_arg($i);
}
$average = $sum / $size;
return $average;
}
echo mean1(96, 93, 98, 98) . "\n";
function mean2()
{
$sum = 0;
$size = func_num_args();
foreach (func_get_args() as $arg) {
$sum += $arg;
}
$average = $sum / $size;
return $average;
}
echo mean2(96, 93, 98, 98) . "\n";

6.7 按引用返回值

问题

希望按引用返回一个值,而不是按值返回。这样就无需为变量建立一个重复的副本。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
function &array_find_value($needle, &$haystack)
{
foreach ($haystack as $key => $value) {
if ($needle == $value) {
return $haystack[$key];
}
}
}
$names = ['Ann Eason', 'Luis Edware', 'Ivan Tomic', 'RouniFul'];
$prince =& array_find_value('Ann Eason', $names);
$prince = "梁非凡";
print_r($names);

6.8 返回多个值

问题

希望从函数返回多个值。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
function array_stats($values)
{
$min = min($values);
$max = max($values);
$mean = array_sum($values) / count($values);
return [$min, $max, $mean];
}
$values = range(1, 100);
list($min, $max, $mean) = array_stats($values);
echo sprintf("min is %d,max is %d,mean is %d", $min, $max, $mean);

6.10 返回失败

问题

希望从函数中指示失败

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
function lookup($name)
{
if (empty($name)) {
return false;
}
}
$name = false;
if (false !== lookup($name)) {
}

6.11 调用可变函数

问题

希望根据一个变量的值来调用不同的函数

实现

1
2
3
4
5
6
7
8
9
10
11
<?php
function get_file($fileName)
{
return file_get_contents($fileName);
}
$function = 'get_file';
$fileName = 'graphic.png';
call_user_func($function, $fileName);

PHP Codebook(四)

4.1 指定并非从元素 0 开始的数组

问题

希望一步为一个数组赋多个元素,不过不希望第一个元素的索引为0

实现

1
2
3
4
5
6
7
8
<?php
$presidents = [1 => 'Washington', 'Adams', 'Lincoln', 'Jefferson'];
var_dump($presidents);
$fruits[1] = 'apple';
$fruits[] = 'banana';
var_dump($fruits);

4.3 数组初始化为一个整数范围

问题

希望将一系列连续的整数赋至一个数组

实现

1
2
<?php
$cards = range(1,52);

4.4 迭代处理数组

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
$array = range(0, 12);
foreach ($array as $item) {
echo $item . "\n";
}
foreach ($array as $key => $value) {
echo $value . "\n";
}
for ($key = 0, $size = count($array); $key < $size; $key++) {
echo $array[$key] . "\n";
}
// 将内部指针重置为数组起始位置
reset($array);
while (list($key, $value) = each($array)) {
echo $value . "\n";
}
array_map(function($value) {
return $value;
}, $array);

4.5 从数组删除元素

问题

希望从一个数组删除一个或多个数组

实现

1
2
3
4
5
6
7
8
9
<?php
$array = range(0, 12);
// 删除元素
unset($array[0]);
// 删除多个连续的元素,array_splice 会自动对数组重新建立索引,避免留出空位。
array_splice($array, 1, 9);
var_dump($array);

4.6 改变数组大小

问题

希望改变一个数组的大小,使它大于或小于目前的大小

实现

1
2
3
4
5
6
7
8
9
<?php
// array_pad() 第一个
$array = ['apple', 'banana', 'cocount'];
$array = array_pad($array, 4, 'dates');
print_r($array);
$array = array_pad($array, -10, 'zucchini');
print_r($array);

4.7 将数组追加到另一个数组

问题

希望把两个数组合并为一个数组

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
// 合并只使用数值键的数组时,数组将重新编号,以保证值不会丢失。
$array1 = ['hello', 'world'];
$array2 = ['luis', 'edware', 'hello', 'world'];
$array3 = array_merge($array1, $array2);
print_r($array3);
// + 操作符也可以用来合并数组
print_r($array1 + $array2);
// 倘若两个数组有重复的键,第二数组会覆盖这些重复键的值
$array1 = ['A' => 'apple', 'B' => 'banana', 'O' => 'orange'];
$array2 = ['A' => 'Luis', 'C' => 'Imooc'];
print_r(array_merge($array1, $array2));

4.8 将数组转换为字符串

问题

希望将一个数组转换为一个格式化的字符串

实现

1
2
3
4
5
6
<?php
$array = range(0, 9);
$string1 = join(',', $array);
$string2 = implode(',', $array);
print_r($string1 . "\n");
print_r($string2 . "\n");

4.10 检查一个键是否存在数组中

问题

想知道一个数组是否包含某个键

实现

1
2
3
4
5
6
7
8
9
10
<?php
$array = ['a' => 'apple', 'b' => 'banana', 'c' => 'cao ni ma'];
if (array_key_exists('a', $array)) {
echo $array['a'] . "\n";
}
if (isset($array['a'])) {
echo $array['c'] . "\n";
}

4.11 检查一个元素是否在数组中

问题

希望知道一个数组是否包含某个值

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$array = ['a' => 'apple', 'b' => 'banana', 'c' => 'cao ni ma'];
if (in_array('apple', $array)) {
echo 'Own it';
} else {
echo 'Need it';
}
$array = ['Emma', 'Pride and Prejudice', 'Northhanger Abbey'];
$array = array_flip($array);
if (array_key_exists('Emma', $array)) {
echo 'Own it';
} else {
echo 'Need it';
}

4.12 查找一个值在数组中的位置

问题

希望知道一个值是否在数组中。如果这个值确实在数组中,希望知道它的键。

实现

1
2
3
4
5
6
7
8
9
<?php
// 如果一个值在数组中多次出现,array_serach() 只能保证返回其中一个实例,而不一定是第一个实例。
$position = ['a'=>'apple','banana','orange','b'=>'not bad'];
if(($result = array_search('123', $position)) !== false){
echo $result;
}else{
echo 'false';
}

4.13 查找通过某个测试的元素

问题

希望找出数组中满足某些需求的元素

实现

1
2
3
4
5
6
7
8
9
10
11
<?php
$array = range(0, 20,2);
$result = array_filter($array,function($value){
if($value%2 === 0){
return true;
}else {
return false;
}
});
?>

4.14 查找数组中最大值或最小值元素

问题

希望找出数组中有最大值或最小值的元素。

实现

1
2
3
4
5
<?php
$array = range(1, 100);
echo max($array) . "\n";
echo min($array) . "\n";

4.15 反转数组

问题

希望反转数组中元素的顺序

实现

1
2
3
4
5
6
7
8
9
<?php
$array = range(1, 100);
$array = array_reverse($array);
print_r($array);
$fruits = ['a' => 'Apple', 'b' => 'Banana', 'o' => 'orange'];
$fruits = array_reverse($fruits);
print_r($fruits);

4.18 多个数据的排序

问题

希望对多个数组或多位数组排序

实现

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
$colors = ['Red', 'White', 'Blue'];
$cities = ['Boston', 'New York', 'Chicago'];
$stuff = [
'colors' => $colors,
'cities' => $cities,
];
array_multisort($colors, $cities);
print_r($colors);
print_r($cities);
array_multisort($stuff['colors'], $stuff['cities']);
print_r($stuff);
$numbers = [0, 1, 2, 3];
$letters = ['a', 'b', 'c', 'd'];
array_multisort($numbers, SORT_NUMERIC, SORT_DESC, $letters, SORT_STRING, SORT_DESC);
print_r($numbers);
print_r($letters);

4.20 随机调整数组

问题

希望按一种随机的顺序重排数组中的元素

实现

1
2
3
4
5
6
7
<?php
$name = ['Ann', 'Eason', 'Luis', 'Edware'];
print_r($name);
shuffle($name);
print_r($name);

4.21 删除数组中重复的元素

问题

希望删除数组中重复的元素

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$array = ['Ann', 'Eason', 'Luis', 'Edware', 'Ann', 'Eason', 'Luis', 'Edware'];
print_r($array);
$unique = array_unique($array);
print_r($unique);
$unique2 = [];
foreach ($array as $item) {
if (!in_array($item, $unique2)) {
$unique2[] = $item;
}
}
print_r($unique2);

4.22 对数组中的各个元素应用一个函数

问题

希望对数组中的各个元素应用一个函数或方法,这就允许一次转换所有输入数据

实现

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
$names = [
'firstName' => 'Ann‘Eason',
'lastName' => 'Luis’Edware',
];
array_walk($names, function(&$value, $key) {
$value = htmlentities($value, ENT_QUOTES);
});
foreach ($names as $name) {
echo $name . "\n";
}
echo '****************************************' . "\n";
// 对于嵌套数据,可以使用 array_walk_recursive()
$names = [
'firstName' => ['Ann"', 'Luis"'],
'lastName' => ['"Eason', '"Edware'],
];
array_walk_recursive($names, function(&$value, $key) {
$value = htmlentities($value, ENT_QUOTES);
});
foreach ($names as $name) {
foreach ($name as $item) {
echo $item . "\n";
}
}

4.23 查找两个数组的并集、交集或差集

问题

有两个数组,希望找出它们的并集、交集、差集和对称差集

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$a = range('a', 'n');
$b = range('h', 't');
// 要计算并集
$union = array_unique(array_merge($a, $b));
print_r($union);
// 要计算交集
$intersection = array_intersect($a, $b);
print_r($intersection);
// 简单差集
$difference1 = array_diff($a, $b);
$difference2 = array_diff($b, $a);
print_r($difference1);
print_r($difference2);
// 对称差集
$difference = array_merge(array_diff($a, $b), array_diff($b, $a));
print_r($difference);

4.24 高效迭代处理大型数据集

问题

希望迭代处理一个元素列表,不过整个列表会占用大量内存,或者生成整个列表的速度非常慢。

实现

4.25 使用数组语法访问对象

问题

有一个对象,不过希望它作为一个数组来读写数据。这样不仅可以得到面向对象设计的好处,还可以利用我们熟悉的数组接口

实现

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
<?php
class FakeArray implements ArrayAccess
{
private $elements;
public function __construct()
{
$this->elements = [];
}
public function offsetExists($offset)
{
return isset($this->elements[$offset]);
}
public function offsetGet($offset)
{
return $this->elements[$offset];
}
public function offsetSet($offset, $value)
{
return $this->elements[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->elements[$offset]);
}
}
$array = new FakeArray;
$array['animal'] = "wabbit";
if (isset($array['animal']) && $array['animal'] == 'wabbit') {
unset($array['animal']);
}
if (!isset($array['animal'])) {
print "Well,What did you expect in an";
}

PHP Codebook(二)

2.1 检查变量是否包含一个合法数字

问题

希望确保变量包含一个数(即使变量的类型是字符串),或者希望检查变量不仅是一个数,而且要特别指定其类型是一个数字类型。

实现

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
foreach ([5,'5','05',12.3,'16.7','five',oxDECAFBAD,'10e2000'] as $maybeNumber) {
$isItNumeric = is_numeric($maybeNumber);
$actualType = gettype($maybeNumber);
$string = "Is the $actualType $maybeNumber numeric?";
if(is_numeric($maybeNumber)){
$answer = "yes";
}else{
$answer = "no";
}
echo pack("A40A3",$string,$answer)."\n";
}
?>
/*
Is the integer 5 numeric? yes
Is the string 5 numeric? yes
Is the string 05 numeric? yes
Is the double 12.3 numeric? yes
Is the string 16.7 numeric? yes
Is the string five numeric? no
Is the string oxDECAFBAD numeric? no
Is the string 10e2000 numeric? yes
*/

2.2 比较浮点数

问题

希望检查两个浮点数是否相等

实现

1
2
3
4
5
6
7
8
<?php
$delta = 0.00001;
$a = 1.00000001;
$b = 1.00000000;
if(abs($a - $b) < $delta){
print "$a and $b are equal enough";
}

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
24
25
26
27
<?php
// 四舍五入 round()
echo round(2.4) . "\n";
echo round(2.5) . "\n";
// 向上取整
echo ceil(2.4) . "\n";
// 向下取整
echo floor(2.9) . "\n";
// 一个数正好落在两个整数中间的位置,PHP 会向远离 0 的方向取整
echo round(2.5) . "\n";
echo round(-2.5) . "\n";
echo round(2.4) . "\n";
echo round(-2.4) . "\n";
echo ceil(2.5) . "\n";
echo ceil(-2.5) . "\n";
echo ceil(2.4) . "\n";
echo ceil(-2.4) . "\n";
echo floor(2.5) . "\n";
echo floor(-2.5) . "\n";
echo floor(2.4) . "\n";
echo floor(-2.4) . "\n";

2.4 处理一系列整数

问题

希望对一个整数范围应用一段代码

实现

1
2
3
4
5
6
7
// 使用 range() 函数实现
<?php
foreach (range(0, 100, 5) as $number) {
echo $number . "\n";
printf("%d squared is %d\n", $number, $number * $number);
printf("%d cubed is %d\n", $number, $number * $number * $number);
}

2.5 在指定范围内生成随机数

问题

希望在指定的数字范围内生成一个随机数

实现

1
2
3
4
5
6
<?php
$lower = 0;
$upper = 1024;
$randNumber = mt_rand($lower, $upper);
echo $randNumber;

2.6 生成可预测的随机数

问题

希望生成可预测的随机数,从而可以保证可重复的行为

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
function pick_color()
{
$colors = ['red', 'orange', 'yellow', 'blue', 'green', 'indigo', 'violet'];
$i = mt_rand(0, count($colors) - 1);
return $colors[$i];
}
mt_srand(34534);
$first = pick_color();
$second = pick_color();
print "$first is red and $second is yellow";

2.10 格式化数字

问题

希望输出一个数,要包括千分位分隔符和小数点。

实现

1
2
3
4
5
6
7
8
9
10
<?php
$number = 1234.56;
// 千位分隔符
echo number_format($number) . "\n";
// 第二个参数指定使用了小数位数
echo number_format($number, 2) . "\n";
// 第三个参数指定小数点字符,第四个参数指定千位分隔符
echo number_format($number, 2, "@", '#') . "\n";

2.16 转换进制

问题

需要将一个数从一个进制转换为另一个进制

实现

1
2
3
4
5
6
7
8
9
10
11
12
<?php
// 十六进制
$hex = 'a1';
// 从十六进制转换为十进制
$decimal = base_convert($hex, 16, 10) . "\n";
echo $decimal;
$decimal = 1024;
echo base_convert($decimal, 10, 2) . "\n";
echo base_convert($decimal, 10, 8) . "\n";
echo base_convert($decimal, 10, 16);

PHP Codebook(一)

1.1 访问子串

问题

想知道一个字符串是否包含一个特定的子串。例如,想查看一个 email 地址是否包含一个 @

实现

1
2
3
4
5
6
7
8
<?php
$string = '506510463@qq.com';
if (($length = strpos($string, '@')) === false) {
echo 'There was no @ in the e-mail address';
} else {
echo 'This is a right e-mail address-' . $length;
}

strpos 找到子串时,会返回子串的位置。找不到子串时,会返回 false,为了区分 0 和 false,必须使用 恒等操作符或非恒等操作符来进行判断。

1.2 抽取子串

问题

希望从字符串中的某个特定位置开始抽取这个字符串的一部分。例如,对于输入到一个表单的用户名,想要得到这个用户名的前八位字符。

实现

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
$start = 0;
$length = 8;
$string = "LuisEdware";
echo substr($string, $start, $length) . "\n";
// LuisEdwa
// 如果忽略 $length,substr() 会返回从 $start 到原字符串末尾的子串
$start = 5;
echo substr($string, $start) . "\n";
// dware
// 如果 $start 大于字符串的长度,substr() 将返回 false
$start = 20;
var_dump(substr($string, $start));
// false
// 如果 $start 加 $length 超过了字符串末尾,substr() 将返回从 $start 开始至字符串末尾的所有字符
echo substr($string, 8, 5) . "\n";
// re
// 如果 $start 为负数,substr() 会从字符串末尾倒数来确定子串从哪里开始
echo substr($string, -6) . "\n";
// Edware
echo substr($string, -6, 2) . "\n";
// Ed
// 如果 $length 为负数,substr() 会从字符串末尾倒数来确定子串到哪里结束
echo substr($string, 2, -2) . "\n";
// isEdwa
echo substr($string, -4, -2) . "\n";
// wa

1.3 替换子串

问题

希望用另外一个不同的字符串来替换一个子串。例如,打印一个信用卡号之前,想要对除了后四位以外的部分模糊处理。或者当需要显示的文档过大,无法一次全部显示,可能希望显示一部分文本,另外还有一个链接指向其余的文本。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$creditCard = "6217 0032 4000 0600 075";
echo substr_replace($creditCard, '****', 0, strlen($creditCard) - 4) . "\n";
// **** 075
echo substr_replace($creditCard, '**** **** **** ****', 0, strlen($creditCard) - 4) . "\n";
// **** **** **** **** 075
$text = "Chief Justice Roberts, President Carter, President Clinton, President Bush, President Obama, fellow Americans and people of the world, thank you.
We, the citizens of America, are now joined in a great national effort to rebuild our country and restore its promise for all of our people.";
echo substr_replace($text, "...", 25);

1.4 逐字节处理字符串

问题

需要分别处理字符串中的各个字节

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$string = "This weekend,I am going shopping for pet chicken.";
$vowels = 0;
for ($i = 0, $j = strlen($string); $i < $j; $i++) {
if (strstr('aeiouAEIOU', $string[$i])) {
$vowels++;
$string[$i] = ucwords($string[$i]);
}
}
echo $vowels . "\n";
echo $string;

1.5 按单词或字节反转字符串

问题

希望反转一个字符串中的单词或字节

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$string = "This is not a palindrome";
// 按字节反转
echo strrev($string) . "\n";
// emordnilap a ton si sihT
// 按单词反转
$string = "Hello World,My Name is Luis Edware.";
// 将字符串分解为单词
$words = explode(' ', $string);
// 反转单词数组
$words = array_reverse($words);
// 重新构建字符串
$string = implode(' ', $words);
echo $string;
// Edware. Luis is Name World,My Hello

1.6 生成随机字符串

问题

希望生成一个随机字符串

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
function str_rand($length, $characters = null)
{
if ($characters === null) {
$characters = '0123456789qwertyuiopasdfghjklzxcvbnm!@#$%^&*()-=_+[]{}:";,.<>/?';
}
if (!is_int($length) || $length < 0) {
return false;
}
$charactersLength = strlen($characters);
$string = '';
for ($i = $length; $i > 0; $i--) {
$string .= $characters[mt_rand(0, $charactersLength)];
}
return $string;
}
echo str_rand(32);

1.8 控制大小写

问题

需要将字符串中的字母全大写或全小写,或者改变字母的大小写。例如,希望名字中的首字母大写,而其余字母都为小写。

实现

1
2
3
4
5
6
7
8
9
<?php
// 使用 ucfirst() 或 ucwords() 将一个单词或多个单词中的首字母大写
$string = "my name is Luis edware";
echo ucfirst($string) . "\n";
echo ucwords($string) . "\n";
// strtoupper()、strtolower() 将作用整个字符串
echo strtoupper($string) . "\n";
echo strtolower($string);

1.10 去除字符串首尾的空格

问题

希望从字符串开头和末尾删除空白符。例如,在验证用户输入之前,可能希望先完成清理。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
/**
* ltrim() 函数从字符串开头删除空白符
* rtrim() 函数从字符串末尾删除空白符
* trim() 函数则删除字符串开头和末尾的空白符
*/
$lString = " LuisEdware";
$rString = "LuisEdware ";
$string = " LuisEdware ";
echo ltrim($lString) . "\n";
echo rtrim($rString) . "\n";
echo trim($string) . "\n";
// trim() 函数还可以从字符串中删除用户指定的字符。
echo ltrim('506510463 2794408425 Luis Edware', '0..9') . "\n";
echo rtrim('LuisEdware 5065104632794408425', '0..9') . "\n";

1.13 生成固定宽度字段数据记录

问题

需要格式化数据记录,使得每个字段占据指定数目的字符。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$books = [
['trueName', 'nickName', 'birthday'],
['Elmer Gantry', 'Sinclair Lewis', 1927],
['The Scarlatti Inheritance', 'Robert Ludlum', 1971],
['The Parsifal Mosaic', 'William Styron', 1979],
];
foreach ($books as $book) {
print pack('A30A20A10', $book[0], $book[1], $book[2]) . "\n";
}
/*
trueName nickName birthday
Elmer Gantry Sinclair Lewis 1927
The Scarlatti Inheritance Robert Ludlum 1971
The Parsifal Mosaic William Styron 1979
*/

1.15 分解字符串

问题

需要将一个字符串分解为片段。例如希望访问用户在一个 表单域中输入的各行文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$string = 'My sentence is not very complicated';
$words = explode(' ', $string);
/*
Array
(
[0] => My
[1] => sentence
[2] => is
[3] => not
[4] => very
[5] => complicated
)
*/

1.16 使文本在指定行长度自动换行

问题

需要实现字符串自动换行

实现

1
2
3
4
5
<?php
$text = "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum.";
echo '<pre>' . $text . '</pre>' . "\n";
echo wordwrap($text, 50);

使用 Laravel 与 Vue.js 进行微信开发(三)

目标

本文的目标是实现微信公众号菜单的创建与删除

创建路由

1
2
3
4
5
6
7
8
9
10
11
/* 微信菜单创建 */
Route::get('/create-menus', [
'as' => 'wechat.create.menus',
'uses' => 'WeChatServerController@createMenus',
]);
/* 微信菜单创建 */
Route::get('/destroy-menus', [
'as' => 'wechat.destroy.menus',
'uses' => 'WeChatServerController@destroyMenus',
]);

修改控制器

修改控制器 app/Http/Controllers/WeChat/WeChatServerController.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
/**
* 创建新的微信菜单
* @param WeChat $weChat
* @return string
*/
public function createMenus(WeChat $weChat)
{
$menu = $weChat->menu;
$buttons = [
[
"type" => "view",
"name" => "进入好答",
"url" => route('wechat.index'),
],
[
"type" => "click",
"name" => "听我唱歌",
"key" => "K0001_LET_ME_SING_MY_SONG",
],
];
$json = $menu->add($buttons);
$result = json_decode($json, true);
if ($result['errcode'] == 0 && $result['errmsg'] == 'ok') {
echo "创建菜单成功";
} else {
echo "创建菜单失败";
}
}
/**
* 删除所有微信菜单
* @param WeChat $weChat
* @return string
*/
public function destroyMenus(WeChat $weChat)
{
$result = $weChat->menu->destroy();
if ($result['errcode'] == 0 && $result['errmsg'] == 'ok') {
echo "删除菜单成功";
} else {
echo "删除菜单失败";
}
}

使用 Laravel 与 Vue.js 进行微信开发(二)

目标

本文的目标是实现微信用户授权,当用户进入 Web App 的时候,能够获取用户的信息。

配置回调地址

微信公众平台 -> 开发者工具 -> 公众平台测试帐号 -> 测试号管理 -> 体验接口权限表 -> 功能服务 -> 网页帐号 -> 修改 -> 填写回调域名,格式如:www.baidu.com

修改配置文件

可以修改文件 config/wechat.php ,也可以在 .env 文件进行配置

wechat.php 文件配置

1
2
3
4
5
6
7
8
9
10
11
12
/*
* OAuth 配置
*
* only_wechat_browser: 只在微信浏览器跳转
* scopes:公众平台(snsapi_userinfo / snsapi_base),开放平台:snsapi_login
* callback:OAuth授权完成后的回调页地址(如果使用中间件,则随便填写。。。)
*/
'oauth' => [
'only_wechat_browser' => false,
'scopes' => array_map('trim', explode(',', env('WECHAT_OAUTH_SCOPES', 'snsapi_userinfo'))),
'callback' => env('WECHAT_OAUTH_CALLBACK', '/examples/oauth_callback.php'),
],

.env 文件配置

1
2
3
4
5
6
7
WECHAT_APPID=****
WECHAT_SECRET=***
WECHAT_TOKEN=****
WECHAT_AES_KEY=
WECHAT_OAUTH_SCOPES=snsapi_userinfo
WECHAT_OAUTH_CALLBACK=/wechat/oauth-callback

新增路由

修改文件 app/Http/Routes/wechat.php,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
/* 处理微信发送消息 */
Route::any('/request-handler', 'WeChatServerController@requestHandler');
/* 处理微信授权回调 */
Route::any('/oauth-callback', 'WeChatServerController@oauthCallback');
/* 微信应用首页 */
Route::get('/', [
'as' => 'wechat.index',
'uses' => 'WeChatApplicationController@index',
'middleware' => [],
]);

关闭路由 CSRF

修改文件 app/Http/Middleware/VerifyCSsrfToken.php,代码如下:

1
2
3
4
protected $except = [
'/wechat/request-handler',
'/wechat/oauth-callback'
];

获取授权

1.授权页面

新增文件 app/Http/Controllers/WeChat/WeChatApplicationController.php,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
namespace App\Http\Controllers\WeChat;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use EasyWeChat\Foundation\Application as WeChat;
class WeChatApplicationController extends Controller
{
public function index(Request $request, WeChat $wechat)
{
// 如果 Session 中没有用户信息,则跳转至微信授权页面
if (empty($request->session()->has('weChatUserInfo'))) {
$oauth = $wechat->oauth;
return $oauth->redirect();
}
$user = $request->session()->get('weChatUserInfo');
dump($user);
}
}

2.回调操作

修改文件 app/Http/Controllers/WeChat/WeChatServerController.php,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
/**
* 微信用户授权回调
* @param Request $request
* @param WeChat $weChat
* @return \Illuminate\Http\RedirectResponse
*/
public function oauthCallback(Request $request, WeChat $weChat)
{
$oauth = $weChat->oauth;
$user = $oauth->user();
$request->session()->put('weChatUserInfo', $user->toArray());
return redirect()->route('wechat.index');
}

3.访问首页

下载微信的 Web 开发者工具,在工具中访问授权首页,比如访问:你的域名/wechat,可以跳转至授权页,如下图:

使用 Laravel 与 Vue.js 进行微信开发(一)

目标

本文的目标是实现与微信服务器的通信。

环境

  • MacOS
  • Laravel 5.1
  • Vue.js 2.1
  • EasyWeChat

安装 EasyWechat

1
composer require "overtrue/laravel-wechat:~3.0"

配置

  • 注册 ServiceProvider:
    • Overtrue\LaravelWechat\ServiceProvider::class,
  • 创建配置文件
    • php artisan vendor:publish
  • 修改 config/wechat.php
    • app_id:微信测试号 appID
    • secret:微信测试号 appsecret
    • token:自定义,要与微信测试号填写的 Token 保持一致
    • aes_key:加密信息用的,本文采用明文的方式,暂不填写

创建微信分组路由

修改文件 app/Http/routes.php,代码如下:

1
2
3
4
5
6
7
Route::group([
'prefix' => 'wechat',
'namespace' => 'WeChat',
'middleware' => [],
], function() {
require_once __DIR__ . '/Routes/wechat.php';
});

新增文件 app/Http/Routes/wechat.php,代码如下:

1
Route::any('/request-handler', 'WeChatServerController@requestHandler');

关闭路由 CSRF

修改文件 app/Http/Middleware/VerifyCSsrfToken.php,代码如下:

1
2
3
protected $except = [
'/wechat/request-handler'
];

创建控制器

新增文件 app/Http/Controllers/WeChat/WeChatServerController.php,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
namespace App\Http\Controllers\WeChat;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use EasyWeChat\Foundation\Application as WeChat;
class WeChatServerController extends Controller
{
/**
* 处理微信的请求消息
* @return string
*/
public function requestHandler(WeChat $weChat)
{
$weChat->server->setMessageHandler(function($message) {
return "欢迎关注 {$message}!";
});
return $weChat->server->serve();
}
}

验证

  • 配置微信公众平台测试号
    • 接口配置信息
      • URL:域名/wechat
      • Token:自定义,要与 wechat.php 文件的 token 一致
    • JS 接口安全域名
      • 填写域名,例如 baidu.com ,不需要写 www 和 http[s]://
    • 测试号二维码
      • 添加测试用户

URL 和 Token 通过验证之后,关注微信公众平台测试号,并发送测试消息,成功的话,如下图: