The toolchain
PHP's tooling is a collection: php runs things, php -l checks syntax, Composer manages packages, PHPUnit tests, PHPStan or Psalm catch bugs, PHP-CS-Fixer argues about whitespace. Each comes from a different project, with its own config file and its own opinions.
Go ships one command, go, and it does nearly all of that. It's a Swiss Army knife, if the knife also came with a test runner and a formatter, and politely refused to cut anything unidiomatic.
The commands you'll use every day
| You'd type in PHP | In Go | What it does |
|---|---|---|
php hello.php |
go run . |
Compile to a temp binary and run it |
| (deploy the source) | go build |
Compile to a real binary you keep |
php -l file.php |
go build / go vet |
Syntax and type errors / suspicious code |
vendor/bin/phpunit |
go test ./... |
Run tests (Unit 5) |
composer require |
go get |
Add a dependency (lesson 0.4) |
php-cs-fixer fix |
gofmt -w . |
Format code (lesson 0.5) |
| php.net | go doc fmt.Println |
Docs, right in your terminal |
The ./... you'll see everywhere means "this directory and everything below it". It's Go's version of a glob, and yes, it really is three dots.
go run vs go build
go run . feels like php hello.php: type it and see the output. Under the hood, though, it compiles your whole package to a temporary binary, runs it, then deletes it. go build does the same compile but leaves the binary on disk, named after your project, ready to copy to a server.
flowchart TB
accTitle: go run compared with go build
accDescr: Both commands compile the package. go run puts the binary in a temporary directory, runs it and deletes it. go build writes the binary to the current directory and stops, so you can run or deploy it later.
src[Your .go files] --> c{{Compile the package}}
c -->|go run .| tmp[Temp binary] --> run([Run it, then delete it])
c -->|go build| bin[Binary on disk] --> ship([Run it, or copy it to a server])
Mind the gap
Run go run ., not go run main.go. Passing a file name compiles only that file, so anything defined in your other files vanishes and you get a baffling undefined: helper. The dot means "the whole package in this directory", which is almost always what you want.
The compiler is your php -l, times ten
php -l only checks syntax. The Go compiler checks syntax and types and that every name exists and that you used every import and variable. A lot of what PHPStan does at level 5 or so, Go does on every build, before you can even run the thing.
go vet: it compiles, but it's probably wrong
Some mistakes are perfectly legal Go. go vet looks for code that compiles but is almost certainly a bug. Its classic catch involves fmt.Printf and fmt.Sprintf, which work just like PHP's printf and sprintf: same %s and %d verbs, same idea. Here's a bug you've probably shipped at least once, arguments in the wrong order:
// mins is 3, dest is "Leeds" fmt.Printf("%s departs in %d min\n", mins, dest) // go vet: fmt.Printf format %s has arg mins of wrong type int // go run: %!s(int=3) departs in %!d(string=Leeds) min
PHP would cheerfully print 3 departs in 0 min and let a passenger find out. Go's compiler lets it through too, but vet won't.
Here's the bit worth remembering: go test runs vet's most important checks before every test run, and refuses to run the tests if they fail. That's why, in this course, Run might "work" and print nonsense while Submit stops and shows you vet's complaints. The exercise is built around exactly that.
Recap
- One
gocommand:run,build,vet,test,get,fmt,doc. go run .compiles and runs the whole package. Don't pass it a single file.- The compiler catches far more than
php -l, andgo vetcatches code that compiles but is wrong.go testruns vet first.