Getting into Go for PHP developers

#

How many programming languages do you regularly work with? But you probably have your favorites. Myself, I’ve built my career on PHP and JavaScript. I’m always looking to learn something new and I find the best way to learn a new skill is to force myself to use it the way I would a familiar tool.

So that’s what I set out to do with Go! These last few months I decided to hunker down and properly learn Go and see what all the fuss was about.

In the series I’m going to go over some of the things I’ve learned (and continue to learn) about Go – from the perspective of a more traditional PHP or JavaScript programming background.

Go is a big topic to cover, so let’s get into it!

What is Go and why is it cool?

If you’re not familiar with Go (also called Golang), it is a programming language created by some folks at Google in 2009. A lot of large, household name companies are switching parts of their software over to Go. Shopify has a database migration tool they’ve built using Go, Basecamp has incrementally switched from Ruby to Go and even Twitter has gotten on the Go train in 2015 with it’s “Answers” analytics platform. Chances are if you look under the hood of most larger companies these days you’ll find some Go.

Go is statically typed, compiled and honestly, pretty straightforward to learn. It’s not as verbose as PHP in some areas, but not quite as minimal as Ruby or Python.

In the example below you’ll see that Go uses packages, imports and has a main() function where code runs:

package main

import "fmt"

func main() {
    fmt.Println("Hey y'all")
}

Go code starts from the main package and, like C, starts in the main() function. Packages that aren’t main are called shared libraries and can be imported once built and installed.

What makes Go cool is that it includes a built in web server and router, makes deployment super simple as it compiles to a single executable, and includes concurrency out of the box.

Danny van Kooten has a great write up on his blog about switching from Laravel to Go which highlights Go’s efficiency.

Plus, it’s always good to learn different things to enhance our skills as programmers. So let’s see how to get _go_ing with Go…

Dad joke

Getting started

Before we do anything we need the Go runtime and all its included goodies. You can read about how to do this on the “Getting Started” page in the Go documentation.

Once that’s done, run go version or go help in the terminal of your choice. If that works the go binary has been added to your $PATH and you’re off to the races.

$GOPATH

One thing that I didn’t do correctly when I starting working with Go is set up my Go workspace correctly. You’ll want to make sure the $GOPATH environment variable is set correctly as this is the base workspace for working with Go files.

Adding something like the following to your $HOME/.profile file works for *nix systems:

export GOPATH=${HOME}/go

Next you’ll want to make sure you have the src, pkg and bin folders created in your $GOPATH folder. More information on the how and why these folders need to exist can be found here. You’ll typically create new packages (self-contained applications) in the $GOPATH/src/github.com/user/package_name folder.

Documentation

One of the things that has struck me while learning Go is that the documentation is all over the place. The official documentation is a bit hard to follow and it doesn’t really explain the key aspects to actually programming in Go. I’ve found other resources to be better at explaining how you’d actually want to write Go code.

Google will also have pretty much any answer you’re looking for, you know, because SkyNet Google created Go.

I’d recommend at least taking a quick look at the A Tour of Go, just to see what you’re getting yourself into.

It includes the Go Playground which is a way to interactively edit and run Go code. It’s useful for testing out things like Slices and Pointers, which we’ll talk about soon!

Since Go is a such a huge topic we’ll keep this post focused on learning some of the key differences from PHP and/or JavaScript, with future posts covering Go more in depth.

In this post we’ll go over:

IDE or Editor

Writing Go code can be done in pretty much any text editor. VS Code is pretty decent. I’ve actually found that using GoLand from Jetbrains is really awesome as it will automatically highlight syntax errors and allows you to ‘click into’ core functions. GoLand will also auto-import packages if you use a function that’s not already imported.

GoLand IDE screenshot

Variables

Coming straight from PHP or JavaScript to Go, one of the big things you’ll notice is Go’s type system. Go is statically typed, so variables must be assigned a type when they’re declared. This also means that compared to PHP you can’t reassign variables an incorrectly typed value.

For example, this is perfectly valid PHP code:

// PHP
$old = true;
$age = null;

if ( $old ) {
    $age = 36; //null cast as an int
}

In Go, not so much:

// Go
old := true
var age bool

if old {
    age = 36
}

 //Compiler error: cannot use 36 (type int) as type bool in assignment

Go’s type system is a lot stricter than PHP and you’re not able to convert a bool to an int, even with the built-in conversion functions. It really forces you to think about your variable declarations and what you’re trying to do with your code.

If you want to try the above Go sample out you can either copy the above code into a .go file and run go run myfile.go or paste it into a new Go Playground and hit ‘run’.

Did you notice the :=? That’s what’s called a ‘short assignment’ operator. Inside a function, these are equivalent:

var old bool
old = true

// Or..
var old = true //inferred type

// Or...
old := true

Go’s compiler is smart enough to figure out which type the variable should be!

Go supports all the usual types like int, string, floats (float32, float64) etc. but it also supports composite types like array, slice, map and channel. Go’s type system is extremely complex and similar to C++ includes pointers, structs (which act as the core to Go’s object model and also provide the ability for developers to create their own types), as well as interfaces.

Arrays

If you’re like me, you may forget that variables actually use computer memory to store their values and just go about your business creating variables willy-nilly.

Welp, Go is here to slap you in the face 😏.

You see, in Go you need to be aware of how memory is used. This becomes obvious when you start working with arrays in Go.

For example in PHP, if you had a list of dogs you’d probably create an array like so:

$mah_dogs = [
    'Snoopy',
    'Skinny',
    'Scooby',
    'Santos L. Halper'
];

Cool, so we’ll just do the same thing in Go, right?

mah_dogs := [4]string{
    "Snoopy",
    "Skinny",
    "Scooby",
    "Santos L. Halper",
}

Looks pretty similar, but what’s that [4]string part about? That, friend, is the fixed size of the array. In Go, much like C, arrays have a fixed size. Want to add another item to the array? Better copy it and create a new array that’s slightly larger!

😬

You can read more about why and how this works on the Go blog, but basically arrays use sequential chunks of memory so they can be accessed by index. This makes arrays super quick to access, but slow to update.

The more commonly used data type in Go is the slice, which is kind of like a dynamic PHP array.

Slices

Based on our earlier example, let’s make a slice instead of an array:

mah_dogs := []string{
    "Snoopy",
    "Skinny",
    "Scooby",
    "Santos L. Halper",
}

Note the difference? It’s essentially identical but there’s no fixed count in the square brackets. Huh?

Slices in Go are a layer on top of arrays, they hold a reference to an underlying array, but they don’t actually contain any data. They just reference data in the underlying array. Slices have a length (how many items they contain) and a capacity (how many items they could contain). Slices also automatically resize if you add more elements to them!

You can access items in a slice by index, just like you would in PHP arrays:

fmt.Println(mah_dogs[0]) // Snoopy
mah_dogs[0] = "Frank"
fmt.Println(mah_dogs[0]) // Frank

Just like array_slice() with PHP arrays, you can break up slices to create a new slice! Slices use the slice[high:low] syntax to get a subset of items.

new_dogs := mah_dogs[2:]
fmt.Println(new_dogs) // Scooby, Santos L. Halper

You can exclude the high or low bound if you want all items to the end of slice, for example.

new_dogs := mah_dogs[2:]

// Is the same as

new_dogs := mah_dogs[2:4]

Lastly, you can’t add items to a slice like you can in PHP. In PHP you’d just use the [] syntax to add another item to an array. In Go you have to use the append() function.

//PHP
$mah_dogs[] = 'Dewey';
$mah_dogs[] = 'Steve';

//Go
mah_dogs = append(mah_dogs, "Dewey", "Steve")

Associative Arrays = Maps

Ok so slices are cool and all, but you may have noticed that the keys (indexes) are just integers. Can you use a string as a key like with PHP associative arrays?

No, no you can’t.

Go uses a structure called a map. They’re pretty much associative arrays, but Go flavored.

//PHP

$groceries = [
    'bread' => 1.99,
    'milk' => 2.99,
    'eggs' => 4.99,
];

//Go

groceries := map[string]float32{
    "bread" : 1.99,
    "milk" : 2.99,
    "eggs" : 4.99,
}

The syntax for creating a map literal, like above, is map[key type]value type.

Accessing map items is as you would expect, priceOfBread := groceries["bread"], just like PHP’s implementation.

Like PHP associative arrays and unlike Go slices, maps can be appended to by just adding a new key value pair:

groceries["fruit"] = 5.99

Loops

We’ve now looked at some of Go’s collection types, but how do you iterate over these structures? Do we use the old PHP and JavaScript stalwart, the foreach loop?

Not exactly.

Go only has one loop type, the for loop.

Fortunately it’s really flexible and can be used just like a foreach or while loop.

The basic for loop looks almost identical to PHP’s version:

sum := 0
for i := 0; i < 10; i++ {
    sum += i
}

A while loop:

for sum < 1000 {
    sum += sum
}

To get the for loop to act more like a foreach loop you just need to include the range clause.

For example, based on our earlier groceries map:

for key, value := range groceries {
    fmt.Printf("%s costs $%.2f\n", key, value)
}

//Output
bread costs $1.99
milk costs $2.99
eggs costs $4.99

Note the conversion of the float to string with %.2f. This just tells Go that we only want 2 decimals of precision since we don’t need any more levels of precision.

Functions and multiple return values

Functions in Go are similar to those in PHP, with some twists. The one thing that threw me when I first started reading about Go is that you can return multiple values from a function. No more unwrapping an array when you want to return multiple values!

//PHP

function getDogs() {
    $dogs = [
        'count' => 5,
        'dogs' => [
            'Snoopy',
            'Skinny',
            'Scooby',
            'Santos L. Halper'
        ]
    ];

    return $dogs;
}

extract(getDogs());

//Go

func getDogs() (count int, dogs []string) {
    count = 5
    dogs = []string{
        "Snoopy",
        "Skinny",
        "Scooby",
        "Santos L. Halper",
    }

    return
}

count, dogs = getDogs()

You’ll note that after the argument list you can define the return types. This is sort of similar to PHP 7’s return types, but Go has named returns on top of returning multiple values. Named results mean that you don’t have to manually return both values.

In the above example Go uses the named results (count int, dogs []string) when the return statement is reached. Bit of a syntactical change, but I find it more readable.

Go also includes the blank identifier (_), which allows you to discard return values if you don’t need them.

In our dogs example, if we don’t care about the count, we would use it to discard the value:

_, dogs = getDogs()

Structs

That last thing we’ll cover very briefly is structs. Structs are a complex type and form the foundation of how Go deals with objects and composition. We’ll cover them more in depth next time, but for now it’s useful to know what they are.

In PHP we have classes and a traditional OOP model – we create classes that contain a bunch of methods and properties. A kind of similar structure in Go is the struct. Structs are basically just groupings of other types.

package main

import (
    "fmt"
    "strings"
)

type dog struct {
    name  string
    age   int
    likes []string
}

func main() {
    goodBoy := dog{"Skinny", 4, []string{"Food", "Sleeping"}}
    fmt.Printf("This is %s. It's age is %d and it's favorite things are %s", goodBoy.name, goodBoy.age, strings.Join(goodBoy.likes, ", "))
}

// Output: This is Skinny. It's age is 4 and it's favorite things are Food, Sleeping

In the above example we have a new struct defined – dog. It has three fields: name, age and likes. Likes is a slice because dogs like more than one thing, right?

When we want to create a new dog we can reference the struct we created, just filling in the required fields.

Feel free to edit this on the Go Playground.

Structs are pretty cool and they can do a lot more. We’ll go into using structs and Go’s composition-over-inheritance next time.

You’ve Been Learned

With this post, I hope you’ve been able to see how similar to PHP Go is. If you take a few hours to read through some of the resources I’ve outlined at the beginning and gone through some exercises, you’ll be well on your way to learning Go.

In the next post will go over some of the more complex topics like composition over traditional OOP, pointers/references and concurrency among others!

Have you dug into Go? What are you thoughts on how it matches up with PHP and JavaScript? Let us know in the comments!

About the Author

Peter Tasker

Peter is a PHP and JavaScript developer from Ottawa, Ontario, Canada. In a previous life he worked for marketing and public relations agencies. Love's WordPress, dislikes FTP.