Golang

2709 readers
2 users here now

This is a community dedicated to the go programming language.

Useful Links:

Rules:

founded 3 years ago
MODERATORS
1
2
3
4
5
 
 

This project is an extensive experiment that got out of hand. It aims to provide a restricted PHP runtime to interface with Go code, as well as run more extensive PHP code like a full blown PHP template engine with eval and what not. It provides implicit context and error return handling, triggering an exception if an error occurs in the request. The exception can be caught with try/catch, or be passed into the error handler inside the go runner.

Main benefits are:

  • request lived allocations / lifecycle of php apps
  • good integration with go-native objects, methods, returns
  • some level of compatibility with stdlib-only PHP code
  • no inheritance, traits, interfaces
  • can provide an embed.FS for the php contents

The PHP runtime in this case is written in Go, pure Go, and doesn't rely on CGO to provide functionality like FrankenPHP does. The changes to .php files are immediately visible without a server restart.

It's all experimental so if you're interested in this, there may be quite a few feature gaps to maturity. That said, you could use what's implemented and extend it with your own APIs where the odd standard library function is missing. You may want to consider this a portable interpreter as well, as you could delivery it as a static binary, further reducing PHP runtime environment requirements.

I worked with the plugin standard library package extensively in the past, and I was always missing an option where the runtime would be severely restricted, and you could provide APIs in Go directly. Maybe it's the power of Go, it's way easier to build a PHP runtime than it is to get a decent SSR CMS website under type safety and similar restrictions (no globals, no shared state).

Current goals:

Optimization of the pre-execution steps, exposing a database client to the PHP code to directly write SQL queries, figuring out a routing system that takes nice URLs into account, more test coverage with the template engine. Maybe write a nice /admin dashboard without drama

6
 
 

Bit of a newbie question here, but I've set up a local Forgejo server in my homelab that I'm using for personal projects. I've created some modules that I want to be able to reference in other local projects.

Trying to run

go get code.mydomainname.com/tapdattl/test

returns

go: downloading code.mydomainname.com/tapdattl/test v1.0.0
go: code.mydomainname.com/tapdattl/test@v1.0.0: verifying module: code.mydomainname.com/tapdattl/test@v1.0.0: reading https://sum.golang.org/lookup/code.mydomainname.com/tapdattl/test@v1.0.0: 404 Not Found
        server response: not found: code.mydomainname.com/tapdattl/test@v1.0.0: unrecognized import path "code.mydomainname.com/tapdattl/test": https fetch: Get "https://code.mydomainname.com/tapdattl/test?go-get=1": dial tcp <my netbird ip address>:443: connect: connection refused

However I know my Forgejo server is up, and I can push to it and clone from it and do all the normal Git workflows. But Go apparently can't talk to it.

Can anyone explain what's going on?

Thanks!!

7
8
9
10
 
 

When switching from other languages the urge to reach for recover/defer as a try/catch substitute is real

11
 
 

I’ve spent years reaching for Makefile by default, but I recently started using Taskfile for my projects instead. While it’s still the industry standard, it often feels like a mismatch for the specific needs of a modern web stack… Since moving a few of my workflows over, I’ve found it much better suited for the way I work today.

Here are three features that convinced me:

=> Self-documenting by design

With Makefile, just getting a readable help output requires a cryptic grep | awk one-liner that’s been copy-pasted between projects for 40 years. Taskfile simply has a built-in desc field for each task, and running task --list instantly shows everything available with a clean description. It’s a small thing, but it makes onboarding new developers (or just returning to a project after a few weeks :) ) so much smoother.

=> Truly cross-platform without hacks

Make was designed for Unix. The moment someone on your team opens a PR from Windows, you’re suddenly wrestling with OS detection conditionals, WSL edge cases, and PowerShell compatibility. Taskfile was built cross-platform from day one. It uses sh as a universal shell by default, and if you do need platform-specific commands, there’s a native platforms field that handles it cleanly at the command level. No more fragile branching logic.

=> Built-in validation and interactive prompts

Adding a confirmation prompt or a precondition check in Make means writing verbose, bash-specific shell code that breaks outside of bash. Taskfile has prompt and preconditions as first-class features: one line to ask for confirmation before a deploy, another to verify an env variable is set. It works on every platform, out of the box, no shell scripting required.

In my opinion, Taskfile feels like a much more predictable and modern successor!

What do you think about it?

12
13
 
 

Given the following short function

func example(foo string) error {
    if bar, err := doSomething(foo); err != nil {
        return err
    } else {
        doSomethingElse(bar)
    }
    return nil
}

Why does the linter recommend I change the if block to

    var bar whateverType
    if bar, err = doSomething(foo); err != nil {
        return err
    }
    doSomethingElse(bar)
    return nil
}

In my mind the former example restricts the bar variable to the smallest scope that is needed, and more clearly identifies doSomethingElse as something that should only happen if err != nil.

I know it's redundant, but now if I want to change it to an else if ... chain I don't have to worry about accidentally including or excluding code from that block, I already know exactly what's supposed to be in it. I just feel like it's a safer programming practice.

But looking forward to other opinions and discussion. Thanks!

14
15
16
17
18
19
20
21
22
 
 

Marshaling the field is fine, so using the tag json:"-" won't work. I could use the UnmarshalJSON method on the struct, but I have no idea how to implement it.

I'd rather not make a duplicate of the struct with that field removed, as it causes quite a bit of code duplication, and it's pretty chaotic.

23
24
25
 
 

Hi, what's the best way for testing a gin route?

I currently have one function where the routes are defined. Here it is:

package router

import (
	"github.com/gin-gonic/gin"
	"github.com/gragorther/epigo/handlers"
	"github.com/gragorther/epigo/middlewares"
)

func Setup(userUserStore handlers.UserUserStore, groupGroupStore handlers.GroupGroupStore, groupAuthStore handlers.GroupAuthStore, messageAuthStore handlers.MessageAuthStore, messageMessageStore handlers.MessageMessageStore, middlewareUserStore middlewares.UserStore) *gin.Engine {
	r := gin.Default()
	r.Use(middlewares.ErrorHandler())

	userHandler := handlers.NewUserHandler(userUserStore)
	authHandler := middlewares.NewAuthMiddleware(middlewareUserStore)
	groupHandler := handlers.NewGroupHandler(groupGroupStore, groupAuthStore)
	messageHandler := handlers.NewMessageHandler(messageMessageStore, messageAuthStore)

	// user stuff
	r.POST("/user/register", userHandler.RegisterUser)
	r.POST("/user/login", userHandler.LoginUser)
	r.GET("/user/profile", authHandler.CheckAuth, userHandler.GetUserProfile)
	r.PUT("/user/setEmailInterval", authHandler.CheckAuth, userHandler.SetEmailInterval)

	// groups
	r.DELETE("/user/groups/delete/:id", authHandler.CheckAuth, groupHandler.DeleteGroup)
	r.POST("/user/groups/add", authHandler.CheckAuth, groupHandler.AddGroup)
	r.GET("/user/groups", authHandler.CheckAuth, groupHandler.ListGroups) // list groups
	r.PATCH("/user/groups/edit/:id", authHandler.CheckAuth, groupHandler.EditGroup)

	// lastMessages
	r.POST("/user/lastMessages/add", authHandler.CheckAuth, messageHandler.AddLastMessage)
	r.GET("/user/lastMessages", authHandler.CheckAuth, messageHandler.ListLastMessages)
	r.PATCH("/user/lastMessages/edit/:id", authHandler.CheckAuth, messageHandler.EditLastMessage)
	r.DELETE("/user/lastMessages/delete/:id", authHandler.CheckAuth, messageHandler.DeleteLastMessage)
	return r
}

so, my question is, how can I test just one route? should I run this function in every test and send a request to a route with httptest? Or should I set up my handlers like it's described at https://gin-gonic.com/en/docs/testing/

(like this)

func postUser(router *gin.Engine) *gin.Engine {
  router.POST("/user/add", func(c *gin.Context) {
    var user User
    c.BindJSON(&user)
    c.JSON(200, user)
  })
  return router
}

let me know if you have any other questions about my code.

It's available on github: https://github.com/gragorther/epigo

view more: next ›