PHP 开发者学习 Go 指南
如果你熟悉 PHP 和 Web 开发,这份指南将帮助你快速上手 Go 语言。
快速对比表
| 特性 | PHP | Go |
|---|---|---|
| 类型系统 | 动态类型 | 静态类型 |
| 执行方式 | 解释执行 | 编译执行 |
| 包管理 | Composer | Go Modules |
| Web 框架 | Laravel/Symfony/ThinkPHP | Zoox |
| 数组 | 关联数组/索引数组 | 数组/切片/Map |
| 错误处理 | Exception | 显式错误返回 |
| 并发 | 多进程/多线程 | Goroutine |
| 部署 | 需要 PHP-FPM/Web 服务器 | 单一可执行文件 |
概念映射
变量和类型
PHP:
php
<?php
// 动态类型
$name = "Alice";
$age = 30;
$price = 19.99;
// 类型提示(PHP 7+)
function greet(string $name): string {
return "Hello, " . $name;
}Go:
go
// 静态类型
var name string = "Alice"
age := 30
price := 19.99
// 函数签名
func greet(name string) string {
return "Hello, " + name
}关键差异:
- Go 是静态类型,编译时检查
- Go 使用
:=进行短变量声明 - Go 的类型系统更严格
数组和关联数组
PHP:
php
// 索引数组
$numbers = [1, 2, 3, 4, 5];
// 关联数组
$person = [
'name' => 'Alice',
'age' => 30
];
// 数组操作
$doubled = array_map(function($n) {
return $n * 2;
}, $numbers);Go:
go
// 数组(固定长度)
var numbers [5]int = [5]int{1, 2, 3, 4, 5}
// 切片(动态数组)
slice := []int{1, 2, 3, 4, 5}
// Map(关联数组)
person := map[string]interface{}{
"name": "Alice",
"age": 30,
}
// 数组操作(需要手动实现)
var doubled []int
for _, n := range slice {
doubled = append(doubled, n*2)
}关键差异:
- Go 区分数组、切片和 Map
- PHP 的数组是混合类型,Go 需要明确类型
- Go 没有内置的 array_map/filter,需要手动实现
类和结构体
PHP:
php
class Person {
private $name;
private $age;
public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
public function greet() {
return "Hello, I'm " . $this->name;
}
}
$person = new Person("Alice", 30);Go:
go
type Person struct {
name string
age int
}
func NewPerson(name string, age int) *Person {
return &Person{name: name, age: age}
}
func (p *Person) Greet() string {
return "Hello, I'm " + p.name
}
person := NewPerson("Alice", 30)关键差异:
- Go 使用结构体而不是类
- Go 没有构造函数,使用工厂函数
- Go 没有继承,使用组合
错误处理
PHP:
php
try {
$data = file_get_contents('file.txt');
if ($data === false) {
throw new Exception('文件读取失败');
}
// 处理数据
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}Go:
go
data, err := os.ReadFile("file.txt")
if err != nil {
return fmt.Errorf("文件读取失败: %w", err)
}
// 处理数据关键差异:
- Go 不使用异常,使用显式错误返回
- Go 的错误处理是函数签名的一部分
- Go 要求显式检查每个错误
Web 开发
PHP (Laravel):
php
// routes/web.php
Route::get('/users/{id}', function ($id) {
$user = User::find($id);
if (!$user) {
return response()->json(['error' => '用户不存在'], 404);
}
return response()->json($user);
});PHP (ThinkPHP):
php
// application/index/controller/User.php
namespace app\index\controller;
class User
{
public function index($id)
{
$user = \app\index\model\User::get($id);
if (!$user) {
return json(['error' => '用户不存在'], 404);
}
return json($user);
}
}Go (Zoox):
go
// main.go
app := zoox.New()
app.Get("/users/:id", func(c *zoox.Context) {
id := c.Param("id")
user, err := getUser(id)
if err != nil {
c.JSON(404, zoox.H{"error": "用户不存在"})
return
}
c.JSON(200, user)
})关键差异:
- Go 的框架更轻量
- Go 的错误处理是显式的
- Go 的性能通常更好
快速上手路径
第 1 步:理解类型系统(2-3 天)
从动态类型到静态类型是最大的挑战:
- 类型声明 - 所有变量都需要类型
- 类型转换 - 需要显式转换
- 类型推断 - 使用
:=简化声明
重点章节:
第 2 步:学习编译和部署(1-2 天)
PHP 是解释型,Go 是编译型:
- 编译 -
go build生成可执行文件 - 部署 - 单一可执行文件,不需要运行时
- 交叉编译 - 可以编译到不同平台
重点章节:
- Go 语言简介 - 安装和环境配置
第 3 步:掌握 Web 框架(2-3 天)
使用 Zoox 框架,类似 Laravel/Symfony/ThinkPHP,但更简单:
- 路由 - 类似 Laravel/ThinkPHP 路由
- 中间件 - 类似 Laravel/ThinkPHP 中间件
- 错误处理 - 显式错误返回
重点章节:
- 实战项目 - RESTful API 部分
第 4 步:学习并发编程(3-5 天)
PHP 主要使用多进程,Go 使用 Goroutine:
- Goroutine - 轻量级线程
- Channel - 用于通信
- 并发模式 - Worker Pool 等
重点章节:
代码对比示例
数组操作
PHP:
php
$numbers = [1, 2, 3, 4, 5];
// Map
$doubled = array_map(function($n) {
return $n * 2;
}, $numbers);
// Filter
$evens = array_filter($numbers, function($n) {
return $n % 2 === 0;
});
// Reduce
$sum = array_reduce($numbers, function($carry, $n) {
return $carry + $n;
}, 0);Go:
go
numbers := []int{1, 2, 3, 4, 5}
// Map
doubled := make([]int, len(numbers))
for i, n := range numbers {
doubled[i] = n * 2
}
// Filter
var evens []int
for _, n := range numbers {
if n%2 == 0 {
evens = append(evens, n)
}
}
// Reduce
sum := 0
for _, n := range numbers {
sum += n
}数据库操作
PHP (PDO):
php
try {
$pdo = new PDO("mysql:host=localhost;dbname=test", $user, $pass);
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
return $user;
} catch (PDOException $e) {
throw new Exception("数据库错误: " . $e->getMessage());
}Go (database/sql):
go
db, err := sql.Open("mysql", "user:pass@/test")
if err != nil {
return nil, fmt.Errorf("连接数据库失败: %w", err)
}
defer db.Close()
var user User
err = db.QueryRow("SELECT * FROM users WHERE id = ?", id).Scan(&user.ID, &user.Name)
if err != nil {
return nil, fmt.Errorf("查询失败: %w", err)
}
return &user, nilJSON 处理
PHP:
php
// 编码
$person = ['name' => 'Alice', 'age' => 30];
$json = json_encode($person);
// 解码
$data = json_decode($json, true);
$name = $data['name'];Go:
go
// 编码
person := map[string]interface{}{
"name": "Alice",
"age": 30,
}
jsonData, err := json.Marshal(person)
// 解码
var data map[string]interface{}
err := json.Unmarshal(jsonData, &data)
name := data["name"].(string)常见陷阱
1. 类型转换习惯
PHP 自动转换:
php
$result = "5" + 3; // 8Go 需要显式转换:
go
// 错误
result := "5" + 3
// 正确
result := "5" + strconv.Itoa(3)
// 或者
num, _ := strconv.Atoi("5")
result := num + 32. 数组和切片混淆
PHP 数组是动态的:
php
$arr = [];
$arr[] = 1;
$arr[] = 2;Go 区分数组和切片:
go
// 数组(固定长度)
var arr [2]int
arr[0] = 1
arr[1] = 2
// 切片(动态)
var slice []int
slice = append(slice, 1)
slice = append(slice, 2)3. 错误处理方式
PHP 使用异常:
php
try {
$data = fetchData();
} catch (Exception $e) {
// 处理错误
}Go 使用显式错误返回:
go
data, err := fetchData()
if err != nil {
// 处理错误
return err
}4. 并发编程概念
PHP 主要使用多进程:
php
$pid = pcntl_fork();
if ($pid == 0) {
// 子进程
} else {
// 父进程
}Go 使用 Goroutine:
go
go func() {
// 并发执行
}()学习建议
- 适应静态类型:这是最大的挑战,需要时间适应
- 理解编译过程:Go 是编译型语言,部署方式不同
- 学习并发编程:PHP 的并发模型与 Go 完全不同
- 利用 Web 开发经验:你的 PHP Web 开发经验很有用
推荐学习顺序
- ✅ Go 语言简介 - 理解编译型语言
- ✅ 基础语法 - 重点学习类型系统
- ✅ 函数和方法 - 注意多返回值
- ✅ 数据结构 - 理解数组、切片、Map
- ✅ 接口和类型 - 类似 PHP 的接口
- ✅ 错误处理 - 适应新的错误处理方式
- ✅ 并发编程 - 重点学习
- ✅ 包和模块 - 类似 Composer
- ✅ 实战项目 - Web API 开发
下一步
现在你已经了解了从 PHP 到 Go 的转换要点,建议:
- 重点学习类型系统 - 这是最大的差异
- 适应错误处理方式 - 从异常到显式错误返回
- 学习并发编程 - PHP 的并发模型与 Go 完全不同
- 完成 Web 项目 - 利用你的 Web 开发经验
祝你学习愉快!
