interfaces define a set of methods. Any type that implements these methods satisfies the interface.
Example of an Interface Implementation:
Output:
interfaces define a set of methods. Any type that implements these methods satisfies the interface.
Example of an Interface Implementation:
package main
==========================
import "fmt"
// Interface definition
type Printable interface {
PrintInfo() string
}
==========================
// Struct that implements the interface
type User struct {
Name string
Email string
}
==========================
// Implement the interface
func (u User) PrintInfo() string {
return fmt.Sprintf("Name: %s, Email: %s", u.Name, u.Email)
}
==========================
func main() {
user := User{Name: "Le Giang", Email: "le.giang@example.com"}
var p Printable = user // User satisfies the Printable interface
fmt.Println(p.PrintInfo())
}
Output:
Name: Le Giang, Email: le.giang@example.com
Composition Instead of Inheritance
In Go, you can "extend" functionality by embedding structs. When a struct embeds another struct, it inherits its methods and fields.
Example of Composition (Struct Embedding):
package main
===========================
import "fmt"
// Base struct
type Base struct {
ID int
}
// Method of Base struct
func (b *Base) PrintID() {
fmt.Printf("ID: %d\n", b.ID)
}
===========================
// Extended struct using composition
type Extended struct {
Base // Embedding Base struct
Name string
}
===========================
func main() {
// Initialize Extended
ext := Extended{
Base: Base{ID: 42},
Name: "Le Giang",
}
// Access methods from Base
ext.PrintID()
// Access field from Extended
fmt.Println("Name:", ext.Name)
}
Output:
ID: 42 Name: Le GiangThank
Below is an example of a folder structure often used with Gin in large-scale projects. Gin itself does not enforce a strict layout, because it follows a minimalistic philosophy. However, the community usually follows certain best practices to keep the code organized.
my-gin-app/
├── cmd/
│ └── server/
│ └── main.go
├── config/
│ └── config.go
├── controllers/ (or handlers/)
│ └── user_controller.go
├── middlewares/
│ └── auth.go
├── models/
│ └── user.go
├── routes/
│ └── router.go
├── services/ (business logic)
│ └── user_service.go
├── repository/ (DB access, optional)
│ └── user_repository.go
├── go.mod
└── go.sum
cmd/server/main.go
main.go inside cmd/server/ is a common Go convention to keep the root clean.config/
.env, using Viper, etc.).config.go might have func InitConfig() to load environment variables or database connection info.controllers/ (or handlers/)
user_controller.go has func GetUsers(c *gin.Context) and func CreateUser(c *gin.Context).middlewares/
auth.go with func AuthMiddleware() gin.HandlerFunc.models/
User with ID, Name, Email...).gorm:"...").routes/
Contains a SetupRouter() function to group and register routes, apply middleware, etc.
Example:
package routes
import (
"github.com/gin-gonic/gin"
"my-gin-app/controllers"
"my-gin-app/middlewares"
)
func SetupRouter() *gin.Engine {
r := gin.Default()
r.Use(middlewares.LoggerMiddleware())
user := r.Group("/users")
{
user.GET("/", controllers.GetUsers)
user.POST("/", controllers.CreateUser)
}
return r
}
services/
user_service.go for email sending, domain rules, etc.).repository/ (optional)
go.mod / go.sum
(Additional directories)
cmd/server/main.gopackage main
import (
"log"
"my-gin-app/config"
"my-gin-app/routes"
)
func main() {
// 1. Initialize config, DB, etc.
config.InitConfig()
// 2. Set up Gin router
r := routes.SetupRouter()
// 3. Run
if err := r.Run(":8080"); err != nil {
log.Fatal(err)
}
}
config/config.gopackage config
import (
"fmt"
"os"
// import "github.com/joho/godotenv" if you need to load .env
// import GORM libraries if you plan to connect to a DB
)
func InitConfig() {
// For example: load environment variables
// godotenv.Load()
fmt.Println("Config loaded, database connected... (placeholder)")
}
models/user.gopackage models
import "time"
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:255"`
Email string `gorm:"size:255;unique"`
CreatedAt time.Time
UpdatedAt time.Time
}
controllers/user_controller.gopackage controllers
import (
"net/http"
"github.com/gin-gonic/gin"
"my-gin-app/services"
)
func GetUsers(c *gin.Context) {
users, err := services.GetAllUsers()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, users)
}
func CreateUser(c *gin.Context) {
var input struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user, err := services.CreateUser(input.Name, input.Email)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, user)
}
services/user_service.gopackage services
import (
"fmt"
"my-gin-app/models"
)
// Mocked data for illustration
func GetAllUsers() ([]models.User, error) {
return []models.User{
{ID: 1, Name: "Alice", Email: "[email protected]"},
{ID: 2, Name: "Bob", Email: "[email protected]"},
}, nil
}
func CreateUser(name, email string) (models.User, error) {
if email == "" {
return models.User{}, fmt.Errorf("email is required")
}
// Suppose we insert into DB and return the created record
user := models.User{
ID: 3,
Name: name,
Email: email,
}
return user, nil
}
routes/router.gopackage routes
import (
"github.com/gin-gonic/gin"
"my-gin-app/controllers"
)
func SetupRouter() *gin.Engine {
r := gin.Default()
userGroup := r.Group("/users")
{
userGroup.GET("/", controllers.GetUsers)
userGroup.POST("/", controllers.CreateUser)
}
return r
}
Feel free to modify this structure according to your team’s needs and project size.
Containerizing PHP and Nginx involves creating Docker containers for each service and configuring them to work together seamlessly. This approach ensures consistency across different environments, simplifies deployment, and enhances scalability. Below are the detailed steps to create a basic Docker configuration for PHP and Nginx, based on a simple PHP application.
Start by setting up your project with the following structure:
project
├── index.php
├── nginx
│ └── nginx.conf
├── Dockerfile
├── Dockerfile-nginx
└── docker-compose.yml
This structure organizes your PHP application, Nginx configuration, Dockerfiles, and Docker Compose file in a clear and maintainable manner.
Create a simple PHP application to serve as the basis for containerization. In the root of your project (./index.php), add the following code:
<?php
echo "Hello!";
?>
This basic script will help you verify that your container setup is working correctly.
Create an Nginx configuration file in nginx/nginx.conf with the following content:
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
}
}
Explanation:
php container on port 9000.Create a Dockerfile in the root of your project to set up the PHP environment:
# Use the official PHP image with PHP-FPM
FROM php:8.1-fpm
# Set working directory
WORKDIR /var/www/html
# Install necessary PHP extensions
RUN docker-php-ext-install mysqli pdo pdo_mysql
# Copy application files
COPY . /var/www/html
# Set permissions
RUN chown -R www-data:www-data /var/www/html
# Expose port 9000
EXPOSE 9000
# Start PHP-FPM server
CMD ["php-fpm"]
Explanation:
9000 for communication with Nginx.Create a Dockerfile-nginx in the nginx directory to set up the Nginx environment:
# Use the official Nginx image
FROM nginx:latest
# Remove the default Nginx configuration
RUN rm /etc/nginx/conf.d/default.conf
# Copy the custom Nginx configuration
COPY nginx.conf /etc/nginx/conf.d
# Copy application files to serve static content if needed
COPY ../ /var/www/html
# Expose port 80
EXPOSE 80
# Start Nginx
CMD ["nginx", "-g", "daemon off;"]
Explanation:
80 for HTTP traffic.Create a docker-compose.yml file in the root of your project to orchestrate the PHP and Nginx containers:
version: '3.8'
services:
php:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/var/www/html
networks:
- app-network
nginx:
build:
context: ./nginx
dockerfile: Dockerfile-nginx
ports:
- "80:80"
depends_on:
- php
volumes:
- .:/var/www/html
networks:
- app-network
networks:
app-network:
driver: bridge
Explanation:
php and nginx).
/var/www/html inside the container.app-network.80 of the host to port 80 of the container./var/www/html inside the container.app-network.Navigate to the root of your project in the terminal and execute the following command to build and start the containers:
docker-compose up -d --build
Expected Output:
Creating network "project_app-network" with the default driver
Building php
Step 1/7 : FROM php:8.1-fpm
...
Successfully built abc123def456
Successfully tagged project_php:latest
Building nginx
Step 1/5 : FROM nginx:latest
...
Successfully built ghi789jkl012
Successfully tagged project_nginx:latest
Creating project_php_1 ... done
Creating project_nginx_1 ... done
After the containers are up and running, open your web browser and navigate to http://localhost. You should see the message:
Hello!
This confirms that Nginx is correctly serving the PHP application through the Docker containers.
Port Conflicts: Ensure that port 80 is not being used by another service on your host machine.
Container Logs: If you encounter issues, check the logs using:
docker-compose logs
Container Status: Verify that all containers are running using:
docker-compose ps
While the above setup provides a basic containerization of PHP and Nginx, consider the following enhancements for more complex applications:
Manage configuration settings using environment variables. You can define them in the docker-compose.yml file under each service or use a .env file for better security and flexibility.
services:
php:
environment:
- APP_ENV=production
- DB_HOST=db
nginx:
environment:
- NGINX_HOST=localhost
If your PHP application requires a database, add another service (e.g., MySQL or PostgreSQL) to the docker-compose.yml and configure PHP to connect to it.
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: app_db
MYSQL_USER: app_user
MYSQL_PASSWORD: app_password
volumes:
- db_data:/var/lib/mysql
networks:
- app-network
volumes:
db_data:
Update the php service to include the database connection details.
Ensure that any data you want to persist (like database files) is stored in Docker volumes to prevent data loss when containers are recreated.
Docker Compose allows you to scale services. For example, you can run multiple instances of the PHP service to handle increased load:
docker-compose up --scale php=3 -d
Update Docker Images: Always keep your Docker images updated to include the latest security patches.
Multi-Stage Builds: Use multi-stage builds to minimize the image size and reduce the attack surface.
FROM node:14 AS build
WORKDIR /app
COPY . .
RUN npm install && npm run build
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
User Permissions: Run containers with non-root users where possible to enhance security.
Implement logging and monitoring solutions to track the performance and health of your containers. Tools like Prometheus, Grafana, and ELK Stack can be integrated for comprehensive monitoring.
Containerizing PHP and Nginx using Docker simplifies the deployment process, ensures consistency across different environments, and enhances scalability. By following the steps outlined above, you can set up a robust environment for your PHP applications, making development and deployment more efficient and manageable.
Embracing containerization not only streamlines your workflow but also positions your applications for better performance and reliability in production environments. As you grow your applications, consider integrating more advanced Docker features and orchestration tools like Kubernetes to further optimize your infrastructure.
Happy Coding!
Feel free to follow me for more insights on containerization, Docker, and web development best practices.
Adopt a modular structure that respects SRP (Single Responsibility Principle) and facilitates the use of design patterns:
app/
├── Actions/ # For Single Action Classes
├── Contracts/ # For Interfaces
├── DTOs/ # For Data Transfer Objects
├── Events/ # For Events
├── Exceptions/ # For Custom Exceptions
├── Http/
│ ├── Controllers/ # REST/GraphQL Controllers
│ ├── Middleware/ # Middleware Classes
│ ├── Requests/ # Form Request Validation
├── Jobs/ # For Jobs and Commands
├── Listeners/ # For Event Listeners
├── Models/ # Eloquent Models
├── Observers/ # For Model Observers
├── Policies/ # For Authorization Logic
├── Providers/ # Service Providers
├── Repositories/ # Repository Pattern
├── Rules/ # Custom Validation Rules
├── Services/ # Service Layer for Business Logic
└── ViewModels/ # For View Model Logic2. Open/Closed Principle (OCP)
- Use Strategy Pattern to extend functionality without modifying existing code.
3. Liskov Substitution Principle (LSP)
- Rely on abstractions (
Contracts), and ensure derived classes are substitutable.
4. Interface Segregation Principle (ISP)
- Use smaller, specific interfaces (e.g.,
UserRepositoryInterface, PostRepositoryInterface).
5. Dependency Inversion Principle (DIP)
- Inject dependencies using Laravel's Service Container.
II. Essential Design Patterns in Laravel
1. Repository Pattern
- Create repositories for data access:
- Example:
UserRepository for interacting with user data.
namespace App\Repositories;
use App\Models\User;
class UserRepository implements UserRepositoryInterface
{
public function findById($id)
{
return User::find($id);
}
}
2. Service Pattern
- Move business logic to services:
namespace App\Services;
use App\Repositories\UserRepositoryInterface;
class UserService
{
protected $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
public function getUserProfile($id)
{
return $this->userRepository->findById($id);
}
}
3 Factory Pattern
- Use Laravel's built-in factories for generating test data:
User::factory()->create();
4. Strategy Pattern
- Implement strategies for interchangeable algorithms:
namespace App\Services\Payment;
interface PaymentStrategy
{
public function pay($amount);
}
class PaypalPayment implements PaymentStrategy
{
public function pay($amount)
{
// Pay with PayPal
}
}
class CreditCardPayment implements PaymentStrategy
{
public function pay($amount)
{
// Pay with Credit Card
}
}
5 Observer Pattern
- Use model observers for handling model events:
namespace App\Observers;
use App\Models\User;
class UserObserver
{
public function created(User $user)
{
// Send welcome email
}
}
6 Decorator Pattern
- Extend existing functionalities dynamically:
namespace App\Services;
class LoggerDecorator
{
protected $service;
public function __construct($service)
{
$this->service = $service;
}
public function execute()
{
\Log::info('Executing service');
return $this->service->execute();
}
}
7 Builder Pattern
- Chain complex object creation:
namespace App\Builders;
class UserQueryBuilder
{
protected $query;
public function __construct()
{
$this->query = User::query();
}
public function whereActive()
{
$this->query->where('active', true);
return $this;
}
public function whereRole($role)
{
$this->query->where('role', $role);
return $this;
}
public function get()
{
return $this->query->get();
}
}
Thank you
When deciding whether to use Puppeteer with Chromium or the full Chrome browser, it depends on your specific requirements. Here's a breakdown to help you decide:
Advantages:
Disadvantages:
Advantages:
Disadvantages:
| Use Case | Recommendation |
|---|---|
| General web scraping or testing | Puppeteer with Chromium |
| CI/CD environments or Docker | Puppeteer with Chromium |
| Mimicking real user environments | Puppeteer with full Chrome |
| DRM-protected content or media apps | Puppeteer with full Chrome |
| Lightweight environment (Alpine) | Puppeteer with Chromium |
javascriptconst puppeteer = require('puppeteer');
puppeteer.launch({
executablePath: '/path/to/chrome',
});
If you’re running in production or CI/CD environments, Puppeteer with Chromium is usually sufficient. However, for scenarios that require real-world testing or proprietary features, consider switching to full Chrome.
Nginx configuration with explanations for various keywords and directives you will encounter in a typical Nginx setup:
The main configuration file for Nginx is usually located at /etc/nginx/nginx.conf.
user nginx; worker_processes auto; pid /run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; server { listen 80; server_name example.com www.example.com; location / { root /usr/share/nginx/html; index index.html index.htm; } } }
user: Specifies the user and group under which Nginx workers will run.worker_processes: The number of worker processes that Nginx will spawn. auto will set this to the number of CPU cores.pid: The file location where Nginx stores its process ID.The events block defines settings that affect the operation of worker processes.
worker_connections: Specifies the maximum number of simultaneous connections each worker process can handle.events { worker_connections 1024; }
The http block contains directives that configure the HTTP server functionality. This is where you define most of your server configurations.
include: Includes other configuration files (e.g., MIME types).default_type: Specifies the default MIME type if it cannot be determined.http { include /etc/nginx/mime.types; default_type application/octet-stream; }
Each server block defines a virtual server.
listen: Specifies the port and/or IP address to listen on (e.g., listen 80;).server_name: Defines the domain names or IP addresses the server will respond to.location: Defines how to handle requests for specific URI patterns.server { listen 80; server_name example.com www.example.com; location / { root /usr/share/nginx/html; index index.html index.htm; } }
The location block is used to define how to handle specific URI patterns or locations.
root: Specifies the directory from which files will be served.index: Specifies the index file to serve when a directory is requested.try_files: Tries to serve the file and, if not found, can redirect to another location.Example:
location / { root /var/www/html; index index.php index.html index.htm; }
rewrite: This directive allows you to rewrite URLs based on regular expressions.return: This is used to send an HTTP response directly, often for redirects.Example:
rewrite ^/old-page$ /new-page permanent; return 301 https://example.com$request_uri;
To serve HTTPS traffic, you’ll need to include SSL certificates and enable SSL in your server block.
server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/example.crt; ssl_certificate_key /etc/nginx/ssl/example.key; location / { root /usr/share/nginx/html; index index.html; } }
You can use Nginx as a reverse proxy to forward traffic to an upstream server.
server { listen 80; server_name example.com; location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
You can define custom error pages for certain HTTP status codes.
server { listen 80; server_name example.com; error_page 404 /404.html; location = /404.html { root /usr/share/nginx/html; } }
Enabling Gzip can improve your website's performance by compressing responses.
http { gzip on; gzip_types text/plain text/css application/javascript application/json application/xml text/javascript; gzip_min_length 1000; }
You can configure caching rules for certain resources.
location ~* \.(jpg|jpeg|png|gif|css|js)$ { expires 30d; add_header Cache-Control "public, no-transform"; }
allow: Grants access to a specific IP address or range.deny: Denies access to a specific IP address or range.Example:
location /admin { allow 192.168.1.1; deny all; }
You can configure logging for access and errors.
http { access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; }
To load balance requests across multiple servers, use the upstream directive.
upstream backend { server backend1.example.com; server backend2.example.com; } server { location / { proxy_pass http://backend; } }
Rate limiting is used to control the number of requests a client can make in a given period.
http { limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s; server { location / { limit_req zone=mylimit burst=5; } } }
This is a general overview of the essential Nginx configuration keywords and directives. You can use these directives to set up a basic to advanced Nginx configuration for serving static files, reverse proxying, SSL setup, caching, logging, error handling, and more. Be sure to consult the official Nginx documentation for more detailed information on any specific directive.
| File/Directory | Purpose |
|---|---|
/etc/nginx/nginx.conf | Main Nginx configuration file. |
/etc/nginx/sites-available/ | Virtual host configuration files. |
/etc/nginx/sites-enabled/ | Symlinks to enabled virtual host configurations. |
/etc/nginx/mime.types | Maps file extensions to MIME types. |
/etc/nginx/conf.d/ | Additional configuration files (e.g., SSL). |
/etc/nginx/snippets/ | Reusable configuration snippets. |
/var/log/nginx/ | Stores access and error logs. |
/etc/nginx/ssl/ | Stores SSL certificates and keys. |
Thank you.
Để publish pav-kit lên NPM, bạn hãy làm theo các bước dưới đây. Tôi đã tạo thêm file index.js để đảm bảo gói tin hợp lệ. Bước 1: Tạo ...