Featured image of post Differences Between Go and PHP and Parameter Passing in Context

Differences Between Go and PHP and Parameter Passing in Context

Let's explore the role of Context

  • For developers transitioning from PHP, some common questions arise:
  • How can sequential requests affect each other?
  • In many programming languages, this could be easily achieved using global variables
  • However, in PHP, each request is completely independent
  • This design has both advantages and disadvantages:
    • Downside: Requires reloading code and configurations on each request
    • Advantage: Automatic memory cleanup after request completion eliminates leakage concerns
  • While PHP can achieve cross-request effects using tools like Redis, the approaches differ fundamentally

Sample Go Code Analysis

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
    "time"
)

func main()  {
    router := gin.Default()
    router.GET("/ping", func(c *gin.Context) {
        go func() {
            var i int
            for {
                i++
                fmt.Println(i)
                time.Sleep(time.Second)
            }
        }()
        time.Sleep(time.Second * 5)
        c.String(http.StatusOK, "pong")
    })
    router.Run(":8888")
}
  • Accessing http://127.0.0.1:8888/ping
  • PHP expectation: Console prints 1-5 then returns response
  • Go reality: Numbers keep incrementing indefinitely until server shutdown
  • Solution using Context-aware goroutine:
package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
    "time"
)

func main()  {
    router := gin.Default()
    router.GET("/ping", func(c *gin.Context) {
        go func() {
            var i int
            for {
                select {
                case <-c.Request.Context().Done():
                    return
                default:
                    i++
                    fmt.Println(i)
                    time.Sleep(time.Second)
                }
            }
        }()
        time.Sleep(time.Second * 5)
        c.String(http.StatusOK, "pong")
    })
    router.Run(":8888")
}
  • Key mechanism: Monitoring c.Request.Context().Done() status
  • Goroutine terminates automatically when request completes

  • PHP executes within the request handler’s lifecycle
  • Asynchronous operations in PHP require external tools (e.g., Redis queues)
  • Go natively supports controlled concurrency via goroutines and Context

Context Parameter Reference Patterns

package main

import (
    "context"
    "fmt"
    "github.com/gin-gonic/gin"
    "github.com/go-redis/redis/v8"
    "net/http"
)

var ctx = context.Background()

func main()  {
    // Redis client setup omitted
    
    g := gin.Default()
    g.GET("/ping", func(c *gin.Context) {
        var msg string
        
        // Option 1: Global context
        msg = client.Get(ctx, "ping").String()
        
        // Option 2: Empty context
        msg = client.Get(context.Background(), "ping").String()
        
        // Option 3: Gin context
        msg = client.Get(c, "ping").String()
        
        // Option 4: Request context (Correct)
        msg = client.Get(c.Request.Context(), "ping").String()
        
        c.String(http.StatusOK, msg)
    })
}

Expert Analysis:

> 4 is the right choice.

1 is problematic due to using a global context
2 creates a pointless empty context  
3 incorrectly uses Gin context instead of context.Context

Reference: https://github.com/go-redis/redis/issues/1594

https://blog.golang.org/context