At the time of writing the most recent PHP version is 7.4 and Go 1.13
Up until November 2018 my commercial development experience consisted of predominantly PHP with a splatter of Vue JS thrown in. I’d spent my early development career honing my skills using PHP but the time came to broaden my horizons and as advised by a colleague I took on the challenge of learning another programming language.
I felt comfortable with server side development so should I focus my attention on front end development or should I learn another server side language? I was advised to learn another server side language. Doing so would not only add another string to my bow it would also improve me as a software developer and improve the way I write PHP.
With this advice on board I decided to learn Go. Having read numerous Medium articles and popular development forums Go was gaining a lot of traction and popularity so naturally felt the right direction to Go (no pun intended).
I delved into blog posts, YouTube videos, Udemy courses and various other literature but I found the barrier to entry quite steep coming from my PHP background. I often found myself looking for PHP to Go comparison resources to help reduce the barrier so I’ve put this post together for anyone who finds themselves in a similar position I was in.
The Go community and ecosystem is extremely good at providing documentation, examples and tutorials especially to get you off the ground. The aim of this post is to dive into common programming concepts and provide a high level view with comparable examples to help PHP developers making the leap into Go development that little bit smoother.
PHP is a dynamic programming language although since PHP 7.0 I’ve seen it described as a gradually typed language. Meaning on the whole PHP is a dynamic language with some static type features. Developers using PHP have the flexibility to adhere to strict conventions such as function argument type hinting and the declaration of return types however on the flip side users can opt not to conform to any strict typing convention and still have a fully functional application.
PHP allows developers to dynamically declare variables of a certain type and then override the same variable with a different type.
<?php
$error = true;
$message = null;
if ($error) {
$message = 'You have encountered an error';
// Original variable type of null now cast to a string
}
echo $message; // 'You have encountered an error'
Even with strict typing declared PHP still allows variables to be assigned with a new type inside a function. Below provides an example of a strictly typed function declared although strict typing is not fully adhered to within the function:
<?php
declare(strict_types=1);
/**
* @param bool $error
* @param null $message
* @return string
*/
function printError(bool $error, $message = null): ?string
{
if ($error) {
$message = 'You have encountered an error';
// Initial null variable now contains a string value
// even with strict type enforcement
}
return $message;
}
$message = printError(true, null);
echo $message; // 'You have encountered an error'
Go on the other hand is a statically typed language with strict typing enforced. Variables are assigned a type at the time of declaration and once assigned cannot be converted or cast to another type. Attempting to do so will result in a compiler error.
package errors
// Short variable assignement
err := false
// Inferred type assignment
var message string
if err == false {
message = nil
//Compiler error: cannot use nil as type string in assignment
}
Return types are also strict and function arguments MUST HAVE a type declaration. The PHP example above allowed a null or string return type whereas Go is strict in the sense that the return type can ONLY be one defined type. Go’s approach to the above PHP code above is defined as:
package errors
import (
"fmt"
)
func printError(err bool, m string) string {
if err {
message = "You have encountered an error"
// Attempting to set 'message' to anything other than a string will result
// in a compiler error
}
return message
}
m := printError(true, "")
fmt.Println(m) // "You have encountered an error"
Go’s strict typing convention provides the over riding feeling of what you see is what you get. It reduces the need for additional type checking inside functions and provides an additional level of robustness and confidence sometimes lost in weaker typed systems.
My personal approach to PHP has always been to adopt a strict approach using explicitly declared types. I often utilise static analysis libraries such as PHPStan to enforce this approach on my projects so for Go to enforce strict typing out of the box was not a complete culture shock to me.
On the other hand if you feel more comfortable using PHP to it’s more flexible capabilities the adoption of a stricter approach is something you will need to come to terms with. The inclusion of typed properties in PHP 7.4 brings PHP a step closer to the conventions Go promotes so should PHP developers choose to utilise this convention in their projects the barrier to entry from PHP to Go could somewhat be reduced.
Classes in PHP are blueprints of objects that contain properties in the form of internally scoped variables with class behaviour defined in methods. The Go alternative is called a struct (short for structure). A struct is a user defined type that contains a collection of data fields with declared data types. Comparative examples in PHP and Go are below:
<?php
class Person
{
/**
* @var string
*/
public $firstName;
/**
* @var string
*/
public $lastName;
/**
* @var int
*/
public $age;
public function __construct(string $firstName, string $lastName, int $age)
{
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->age = $age;
}
}
package people
type Person struct {
FirstName string
FastName string
Age int
}
PHP enforces classes to be instantiated with their required properties whereas struct types in Go can be instantiated in a variety of ways.
package people
// Declares a variable of type 'Person'. The internal fields of the p variable
// are set to their zero value i.e. FirstName is an empty string and Age is 0
var p Person
// Instantiate a struct by supplying the value of all the struct fields.
var p = Person{"Joe", "Sweeny", 36}
// Initialize a struct by supplying name: value pairs of all the struct fields.
var p = Person{FirstName: "Joe", LastName: "Sweeny", Age: 36}
Functions are first class citizens in Go, the same as PHP. Functions are central to the Go ecosystem with an extremely familiar feel to PHP functions with one key difference, multiple return types.
By all means PHP developers have the option of being creative and bundling multiple return values into a single returned array to semi-replicate the functionality Go provides or alternatively returning a value object containing multiple values. However the out of the box capabilities Go provides is a clearer more elegant and readable solution.
package main
func SumProductDiff(i, j int) (int, int, int) {
return i+j, i*j, i-j
}
A function declared INSIDE a class in PHP is called a method. In Go a method is a function with a special receiver argument. Go supports methods defined ON types.
package people
import "fmt"
type Person struct {
FirstName string
LastName string
Age int
}
func (p Person) FullName() string {
return fmt.Sprintf("%s %s", p.FirstName, p.LastName)
}
In Go terminology the above example reads as the FullName method has a receiver type of Person.
A common use case in Go applications is to return the expected type(s) from a function accompanied by a nullable error type (more on this in the Error vs Exception Handling section below).
PHP uses namespaces as a way of encapsulating items that also assists in avoiding naming collisions. Namespaces also provide a level of organisation to a PHP codebase with name spacing conventions generally adopting a directory structure. To utilise functionality residing in a different class the use keyword is used to import a required class, interface or function.
<?php
namespace App\Domain\Entity\User;
use App\Domain\Persistence\Repository;
Go utilises a modular system that organises logical boundaries in your code into a packages. Functionality from external packages are imported via an import statement.
package user
import (
"github.com/joesweeny/app/persistence/repository"
)
If you’ve got experience with Python (or Javascript to a certain extent) the modular package based system will be familiar. Coming directly from a PHP background will require a mentality shift in the way you approach your project structure and organise your code.
Dealing with packages over namespaces was a particular hurdle I had to overcome. I approach my PHP generally from a layered architecture mindset creating bounded contexts and clear logical boundaries between layers using the namespace initiative to my advantage. I found the mindset shift a particular barrier especially when it came to package naming although I found this fantastic talk by Brian Ketelsen as my barrier breaker.
The visibility of a property in PHP be it a method, constant or variable can be defined by prefixing the declaration with the keyword public, protected or private. Class members declared public are accessible from anywhere, private members may only be accessed by the class that defines them and protected members utilise PHPs notion of inheritance as they can be accessed by the declaring class itself and by inheriting parent classes.
<?php
class Visibility
{
protected string $protected = 'Protected';
private string $private = 'Private';
public function public(): string
{
return 'Access from me anywhere';
}
}
The Go approach to visibility adopts a somewhat more simplistic approach. Go does not support inheritance instead opting for composition, embedding and interfaces to support code reuse and polymorphism. Visibility is reduced to either public or private. The visibility of a type, variable, function or method in Go is declared public with a leading capital letter and deemed private with a leading lowercase letter.
package greeter
var hello bool
func SayHello(h bool) {
hello = h
}
func PrintHello() string {
if !hello {
return 'Goodbye'
}
return 'Hello'
}
In the above example the SayHello and PrintHello functions are declared within the greeter package. External code consuming the greeter package will receive the SayHello and PrintHello as exported functions and have the authority to use them as they are declared public with a capital letter.
The hello variable however is a private variable and unexported as it is declared with a lowercase letter. External code importing withe greeter package will not have access to the hello variable. However one key aspect to remember is the hello variable IS accessible by other files within the greeter package. This here typifies Go’s modular approach.
PHP provides encapsulation on a class basis whereas Go achieves encapsulation via its package system.
Interfaces are at the very core of good software design providing a defined contract of what a specific class, type or component can do leaving the how to the specific implementations. PHP provides an interface policy where interfaces are implemented explicitly.
<?php
namespace App\Domain\Storage\File
interface Storage
{
public function save(File $file): void;
}
An interface is declared and a concrete implementation in the form of a class implements the methods declared on the interface.
<?php
namespace App\Domain\Storage;
class LocalStorage implements Storage
{
private array $files = [];
public function save(File $file): void
{
$this->files[] = $file;
}
}
The implements keyword provides an explicit binding between the concrete LocalStorage class and Storage interface. Go also provides interface support albeit implicitly.
package file
type Storage interface {
Save(file File)
}
An interface in Go is two things, firstly it is a set of defined methods and secondly it is a type. Using the above as an example, the implicit binding occurs when any type defines a Save method with an argument of File therefore satisfying the interface declaration. The LocalStorage and AwsStorage types below do exactly this therefore satisfying the Storage interface albeit implicitly without the need for a formal implements declaration.
package file
type LocalStorage struct {
Files []File
}
func (l LocalStorage) Save(file File) {
l.Files = append(l.Files, File)
}
type AwsStorage struct {
Client aws.Client
}
func (a AwsStorage) Save(file File) {
// Persist to external AWS storage
}
The following Go proverb originally attributed to one of the creators of Go, Rob Pike, is always good to remember before you write your next interface in Go:
The bigger the interface, the weaker the abstraction
Application errors occur and the application consumer needs to be made aware in a constructive way. PHP handles error by utilising an exception model that is fairly similar to other programming languages.
Code is surrounded by a try/ catch block and handled accordingly. PHP affords the developer the ability to throw an exception leaving the responsibility of the consuming class to either catch it or in turn throw again. Generally common exceptions are caught and handled by a global exception handler.
<?php
/**
* @param array|int[] $numbers
* @return int
* @throws \InvalidArgumentException
*/
function sum(array $numbers): int
{
if (count($numbers) === 0) {
throw new \InvalidArgumentExceptin('Array must contain one or more numbers')
}
return array_sum($numbers);
}
Go does not provide the conventional try/catch paradigm to handling errors. Errors in Go are a type and are returned from a function as a standard return value. By Go convention errors are the last returned value from a function with a nil value returned if no such error occurred.
package count
import "errors"
func Sum(numbers []int) (int, error) {
if len(numbers) == 0 {
return 0, errors.New("numbers slice must contain one or more integer values")
}
sum := 0
for _, num := range numbers {
sum += num
}
return sum, nil
}
Error handling in Go is extremely verbose as each error returned from a function NEEDS to be checked for a nil value before proceeding. Stepping into Go you will see the following pattern A LOT :
if err != nil {
// Do something
}
Go in general provides a mind shift towards more forward up front thinking and the error handling system is a good example of this. The language’s design and conventions encourage you to check for errors where they occur as opposed to throwing and sometimes catching exceptions.
Coming into Go from PHP error handling feels extremely verbose and often repetitive. This may feel uncomfortable to begin with however I’d argue that proper error handling is paramount to building robust and dependable software and Go enforces this by design.
Managing dependencies manually in any programming language can be extremely cumbersome and an outright headache to deal with. PHP is spoiled with the Composer dependency management tool. By its own definition…
Composer is not a package manager in the same sense as Yum or Apt are. Yes, it deals with “packages” or libraries, but it manages them on a per-project basis, installing them in a directory (e.g. vendor) inside your project.
Composer is baked into the PHP ecosystem. It is mature and a vital tool in any PHP developers toolkit. Dependency management in Go is another story.
Until recently dependency management in Go has been a bone of contention until the introduction of Go Modules, however the debate continues to rumble on. Initially Go had no dependency management tool and executing go get along with the fully qualified import path of the required dependency was the only way to download a dependency for your project pulling the latest code from the master branch of a repository.
Third party tools such as dep were introduced along with concepts such as vendoring. To date they have not gained the same popularity and support that Composer has gained in PHP which prompted the Go team into developing their own solution.
Step forward Go Modules. Go Modules is a built in dependency management system that makes dependency version information explicit and easier to manage. Dependencies are declared within a go.mod file similarly to how dependencies are declared within a composer.json file in PHP.
Go versions 1.11 and 1.12 included preliminary support for modules with version 1.13 using module mode by default. Go modules are here for the foreseeable future and is considered the go to dependency management solution for your Go projects.
Testing is at the backbone of any well built and robust application. Testing our code can spare precious debugging time and hours of headache and PHP developers are spoilt for choice with an abundance of third party libraries. PHPUnit is the best known testing framework with Codeception, Behat and PHPSpec notable mentions.
Testing in PHP is generally performed by extending a base class, creating dependant classes via concrete implementations or mocks and performing assertions to determine our code is behaving well and executing exactly as we expect it to.
<?php
class PersonTest extends TestCase
{
public function test_full_name_returns_a_string()
{
$person = new Person('Joe', 'Sweeny');
$this->assertEquals('Joe Sweeny', $person->fullName());
}
}
PHP provides testing capabilities courtesy of third party libraries. Go provides testing out of the box. Go has an inbuilt testing framework with preferred testing conventions.
package people
import "testing"
func TestFullName(t *testing.T) {
p := Person{"Joe", "Sweeny", 36}
name := p.FullName()
if name != 'Joe Sweeny' {
t.Errorf("Test failed, expected 'Joe Sweeny', got %s", name)
}
}
Tests are executed using the go test command. Go’s approach to testing is simplistic however with the language being strongly typed and statically compiled it makes mocking harder to achieve and far less flexible.
Test assertions by convention are not the norm. The style of the above test along with table driven tests are considered more popular conventions. If you feel more at home with explicit test assertions the stretchr/testify third party library is a fantastic tool that provides powerful support for assertions as well as a mocking library.
I found Learn Go with Tests an extremely powerful resource that immensely helped me in solidify Go’s testing approach and conventions.
I hope the above has provided a better insight into Go for those with a PHP hat on. This post was designed to provide a high level overview with comparable examples to help those PHP developers looking to add Go as another string to their bow. This post has barely scratched the surface of Go’s capabilities such as its in built concurrency model, powerful memory management by design and much more. I’d strongly advise exploring the following resources to help setting you on your way into the world of Go:
Go is an incredibly simple yet powerful language that I enjoy using immensely. Go by design promotes simplicity over complexity and readable code over potentially hard to follow abstractions. Compared to PHP, Go is a completely different language and environment however I’ve personally found developing in Go has improved my overall approach to software development and improved the way I write PHP.
I hope you enjoyed reading this. If you did or even if you didn’t I’d appreciate any feedback @iamjoesweeny
Originally published at https://joesweeny.co.uk.
Special thanks to our guest blogger Joe Sweeny, a Senior Software Developer at Intelligence Fusion for his contribution to the Ronald James Blog this week.
Visit the original link for this blog here.
We are a leading niche digital & tech recruitment specialist for the North East of England. We Specialise in the acquisition of high-performing technology talent across a variety of IT sectors including Digital & Technology Software Development.
Our ultimate goal is to make a positive impact on every client and candidate we serve - from the initial call and introduction, right up to the final delivery, we want our clients and candidates to feel they have had a beneficial and productive experience.
If you’re looking to start your journey in sourcing talent or find your dream job, you’ll need a passionate, motivated team of experts to guide you. Check out our Jobs page for open vacancies. If interested, contact us or call 0191 620 0123 for a quick chat with our team.
Follow us on our blog, Facebook, LinkedIn, Twitter or Instagram to follow industry news, events, success stories and new blogs releases.
Back to Blog