Are you a PHP developer contemplating a switch to Go? You're not alone. As technology evolves, many developers find themselves reevaluating their choice of programming languages to keep up with industry trends, improve performance, and enhance their skill sets. PHP has been a reliable workhorse for web development for decades, powering a vast number of websites and applications. However, with the rise of Go, known for its simplicity, efficiency, and powerful concurrency features, it’s no wonder you might be curious about making the transition.
In this post, we'll dive deep into what it means to move from PHP to Go. We'll explore the key differences between these two languages, and provide practical steps to help you navigate this journey. So, before you take the plunge, read on to discover everything you need to know about transitioning from PHP to Go.
History of Go
Go, also known as Golang, is a statically typed, compiled programming language designed by Google for simplicity and efficiency.
Origins:
Developed by Google: Created by Robert Griesemer, Rob Pike, and Ken Thompson.
First Released: Publicly announced in 2009, with version 1.0 released in March 2012.
Design Philosophy:
Simplicity and Clarity: Designed to be easy to understand, avoiding unnecessary complexity.
Efficiency: Emphasizes fast compilation and execution.
Concurrency: Features goroutines and channels for efficient concurrent task management.
Key Milestones:
2015: Go 1.5 introduced a major runtime overhaul and self-hosted compiler toolchain.
2017: Go 1.9 added type aliases and improved concurrent map access.
2021: Go 1.17 brought performance enhancements and new module features.
Go continues to evolve, supported by an active community and driven by the need for efficient, scalable, and maintainable software.
Key differences between Go and PHP
When to Use Go
Go is ideal for high-performance, concurrent, and scalable applications such as microservices, real-time systems, and distributed systems. Its efficient handling of concurrent processes, faster execution times, and maintainable code make it suitable for backend services, network servers, and command-line tools.
When to Use PHP
PHP is perfect for server-side scripting and building dynamic websites and CMS-driven projects. Its ease of use, extensive documentation, and large community support make it ideal for beginners and small to medium-sized projects.
Side-by-side comparison
PHP | Go | |
Execution | interpreted, runtime is needed | compiled, self-contained |
Concurrency | shared nothing architecture | concurrency is the default |
Error handling | exceptions, mixed returns, errors | error struct returned, very verbose |
Memory management | garbage collected | garbage collected |
Testing | 3rd party libraries | part of the standard library |
Benchmarking | 3rd party libraries | part of the standard library |
Frameworks | large ecosystem | fewer frameworks but steadily growing |
Return values | one or no value returned | no void type, multiple return values are normal, often accompanied by an “error” type |
Code formatting | freestyle, automation via 3rd party libraries, or IDE, no synchronization across application and vendor code | hard-baked into the language, the base format is mandatory for the code to compile |
Package management | via 3rd party tools: composer/packagist | built in the language core |
Go basics
Variable declaration for primitives
// standard declaration
var sum int
// type inferred
var a = "a string"
// multiple variables at once
var b, c int64 = 1, 2
var (
d = 42
e = "another string"
)
// shorthand
f := "apple"
Arrays, Slices and Maps
Arrays
// template
var arrayName [size]Type
// example
var num [3]int
// declaration and initialization
numbers := [5]int{1, 2, 3}
fmt.Println(numbers) // [1 2 3 0 0]
fixed capacity and order
keys are 0 indexed integers without gaps
passed by reference
Slices
// template
var sliceName []Type
// example
var ages []int
// explicit declaration
threeValues := make([]int, 3, 10) // 3 is the length, 10 is the capacity
// shorthand declaration
numbers := []int{1, 2, 3}
fmt.Println(numbers) // [1 2 3]
size is dynamic
order is fixed
slice is a reference to an underlying array
more flexibility and convenience in comparison to arrays
Maps
// template
var mapName map[KeyType]ValueType
// explicit declaration
ages := make(map[string]int)
// shorthand declaration
ages := map[string]int{
"alice": 27,
"bob": 28,
}
grows and shrinks dynamically
backed by hash-map
passed by reference
random order when iterating over
cannot be compared with the equality operator
Structs
type BankAccount struct {
IBAN int
password string
}
a := BankAccount{}
b := BankAccount{}
fmt.Println(a == b) // true
fmt.Println(&a == &b) // false
structs are not objects you know in php
just a collection of properties to represent the state
there is no parent/child relationship
structs are composed (composition over inheritance)
structs are compared by comparing each property
Visibility
type BankAccount struct {
IBAN int // exported (public)
password string // unexported (private)
}
No extra keywords for public and private
Visibility is defined by the lower and upper casing of the property name
The same applies to variables, constants, methods, and types
Struct methods
type BankAccount struct {
IBAN int
password string
}
func (b BankAccount) HasLongPassword() bool {
return len(b.password) > 12
}
b := BankAccount{
password: "Lorem ipsum dolor sit amet",
}
fmt.Println(b.HasLongPassword()) // true
Interfaces
type error interface {
Error() string
}
type Reader interface {
Read(p []byte) (n int, err error) // read into a buffer
}
type Writer interface {
Write(p []byte) (n int, err error) // write into a buffer
}
interfaces are implemented implicitly
no "implements" keyword
this decouples the definition of an interface from its implementation
typically very small (1-2 methods)
Iterating
data := []int{3, 2, 1}
var i int
// initialization ; end condition ; post loop action
for i = 0; i < len(data); i++ {
fmt.Printf("loop 1: key %d has value %d\n", i, data[i])
}
i = 0
// similar to while(condition === true)
for i < len(data) {
fmt.Printf("loop 2: key %d has value %d\n", i, data[i])
i++
}
//via range keyword
data = []int{3, 2, 1}
for k, v := range data {
fmt.Printf("key %d has value %d\n", k, v)
}
Error handling
func Copy(dst Writer, src Reader) (written int64, err error) {
...
}
func PipeStdinToStdout() (int, error) {
bytesWritten, err := io.Copy(os.Stdout, os.Stdin)
if err != nil {
return 0, fmt.Errorf("streaming stdin to stdout failed: %v", err)
}
return int(bytesWritten), nil
}
doesn’t have exceptions
doesn’t “throw” errors/warnings
doesn’t have mixed returns
but has multiple returns instead
errors are explicitly handled and
passed to the caller
Go routines/concurrency
prefix any function call with the go keyword to
run it in a non-blocking fashion
no promises, no async-await, no callbacks
→ code looks and feels synchronous
concurrency needs to be controlled
Wait groups
wg := &sync.WaitGroup{}
for range data {
wg.Add(1) // add a "unit of work"
go func() {
dowork()
wg.Done() // signal one unit of work is done
}()
}
wg.Wait() // block until all goroutines are finished
allows you to wait for a collection of goroutines (concurrent functions) to finish
internally implemented as a counter
when synchronization needs to happen, Wait() must be invoked
Channels
radio := make(chan string)
send := func(ch chan string, msg string) {
ch <- msg
}
go send(radio, "hello world") // non blocking
fmt.Println(<-radio) // blocking
channels enable communication between go routines
communication happens when a sender and a receiver are present at the same time
sending and receiving is a blocking operation
Write & execute your first "Hello World"
Install Go
Follow the required steps for your OS here: https://go.dev/doc/install.
Programm code "Hello World"
Create a file called main.go
in a folder of your choice and past the following content.
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello World")
}
Running and compiling your program
go run main.go
> Hello World
go build -o godemo main.go
./godemo
> Hello World
file godemo
> godemo: Mach-O 64-bit executable arm64
GOOS=windows GOARCH=amd64 go build -o godemo.exe main.go
file godemo.exe
> godemo.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
GOOS
and GOARCH
you can compile your Go program into any OS.Learning resources
Interactive Go Playground: https://go.dev/play or https://goplay.tools/
Case Studies: https://go.dev/solutions/case-studies
Effective Go: https://go.dev/doc/effective_go
Go by example: https://gobyexample.com
Book - Learning Go: https://www.oreilly.com/library/view/learning-go-2nd/9781098139285/
Book - Concurrency in Go: https://www.oreilly.com/library/view/concurrency-in-go/9781491941294/
Summary
In summary, Go and PHP each have their strengths and ideal use cases. Go excels in performance, concurrency, and scalability, making it perfect for complex, high-demand applications. PHP, with its ease of use and strong community support, is well-suited for web development and content management systems. If you’re considering a switch from PHP to Go, carefully evaluate your project requirements and team capabilities. Ready to dive deeper into Go? Explore our recommended resources and start experimenting with Go today to experience its benefits firsthand. Share your journey or ask questions in the comments below!