franken_zstd

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 15, 2025 License: MIT Imports: 11 Imported by: 0

README

FrankenPHP Zstd Extension

A high-performance zstd compression extension for FrankenPHP, implemented in pure Go without CGO dependencies for the compression backend.

Overview

This extension provides zstd (Zstandard) compression and decompression capabilities for PHP applications running on FrankenPHP. Unlike traditional C-based zstd extensions, this implementation leverages the pure Go compression library github.com/klauspost/compress, eliminating memory leaks and CGO-related issues commonly found in C implementations.

Key Advantages

Memory Safety
  • No Memory Leaks: Pure Go implementation eliminates memory management issues present in C-based zstd extensions
  • Garbage Collection: Automatic memory management through Go's garbage collector
  • Thread Safety: Built-in concurrency safety without manual lock management
Performance Benefits
  • Pure Go Backend: Uses github.com/klauspost/compress - a high-performance, pure Go zstd implementation
  • No CGO Overhead: Eliminates the performance penalty of C-Go interop for compression operations
  • Object Pooling: Efficient encoder/decoder reuse reduces allocation overhead
  • Optimized for FrankenPHP: Native integration with FrankenPHP's Go runtime
Reliability
  • Crash Resistance: Pure Go code is less prone to segmentation faults and memory corruption
  • Better Error Handling: Comprehensive error propagation from Go to PHP
  • Predictable Behavior: Consistent performance across different platforms

Installation

Prerequisites
  • FrankenPHP
  • Go 1.25 or later
  • PHP 8.2 or later with ZTS (Zend Thread Safety) enabled
Building FrankenPHP with the Extension

This extension is built as a FrankenPHP module and requires building a custom FrankenPHP binary that includes the zstd extension.

Method 1: Manual Build with xcaddy

If you prefer to build manually or integrate into your own build process:

  1. Install xcaddy (if not already installed):

    go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
    
  2. Build FrankenPHP with the zstd extension:

    # Set up environment variables for cross-platform compatibility
    export CGO_ENABLED=1
    export CGO_CFLAGS="$(php-config --includes) -I/usr/local/include"
    
    # Detect Homebrew/Linuxbrew installation
    if command -v brew >/dev/null 2>&1; then
        BREW_PREFIX=$(brew --prefix)
        export CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs) -L/usr/local/lib -L${BREW_PREFIX}/lib -lphp -lsqlite3"
    else
        # Fallback for systems without Homebrew/Linuxbrew
        export CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs) -L/usr/local/lib -lphp -lsqlite3"
    fi
    
    # Build with xcaddy
    xcaddy build \
      --output frankenphp \
      --with github.com/dunglas/frankenphp/caddy@v1.9.1 \
      --with github.com/phillarmonic/franken-zstd-go@v1.0.3
    
  3. The resulting frankenphp binary will include the zstd extension.

  1. Clone the repository:

    git clone https://github.com/phillarmonic/franken-zstd-go.git
    cd franken-zstd-go
    
  2. Build FrankenPHP with the extension using the automated build:

    just build
    

This command will:

  • Generate PHP stubs and argument info

  • Use xcaddy to build FrankenPHP with the zstd extension included

  • Create a frankenphp binary in the project directory

  1. Verify the installation:

    just check-extension
    
Method 3: Integration into Existing FrankenPHP Build

To integrate this extension into an existing FrankenPHP project:

  1. Add the extension to your go.mod:

    require github.com/phillarmonic/franken-zstd-go v1.0.3
    
  2. Import the extension in your main Go file:

    import _ "github.com/phillarmonic/franken-zstd-go"
    
  3. Build your FrankenPHP application as usual.

Running the Built Binary

Once built, you can use the FrankenPHP binary with the zstd extension:

# Run PHP CLI with the extension
./frankenphp php-cli script.php

# Start a web server using Caddyfile (recommended)
./frankenphp run --config Caddyfile

# Alternative: Direct PHP server (may have module limitations)
./frankenphp php-server --listen :8080 --root /path/to/webroot
Web Server Testing

The project includes a complete web testing setup:

# Start the web server for testing
just web-server

# This will start the server on http://127.0.0.1:10000 with test pages:
# - http://127.0.0.1:10000/web_test.php (Web-specific test)
# - http://127.0.0.1:10000/index.php (Full test suite)

Note: The php-server command may encounter module registration issues. Use the Caddyfile approach for reliable web serving.

API Reference

Global Functions
compress(string $data, int $level = 3): string

Compresses data using zstd compression.

Parameters:

  • $data - The data to compress
  • $level - Compression level (1-22, default: 3)

Returns: Compressed data as a binary string

Example:

$compressed = \Phillarmonic\FrankenZstd\compress("Hello, World!");
decompress(string $compressedData): string

Decompresses zstd-compressed data.

Parameters:

  • $compressedData - The compressed data to decompress

Returns: Original uncompressed data

Example:

$original = \Phillarmonic\FrankenZstd\decompress($compressed);
getInfo(): array

Returns information about the zstd implementation.

Returns: Array containing version and level information

Example:

$info = \Phillarmonic\FrankenZstd\getInfo();
// Returns: ['version' => '1.5.6', 'minLevel' => 1, 'maxLevel' => 22, 'defaultLevel' => 3]
Compressor Class

The Compressor class provides stateful compression with statistics tracking.

Compressor::create(int $level = 3, int $windowSize = 22): Compressor

Creates a new compressor instance.

Parameters:

  • $level - Compression level (1-22, default: 3)
  • $windowSize - Window size (10-31, default: 22)

Returns: New Compressor instance

compress(string $data): string

Compresses data using the configured compressor.

decompress(string $compressedData): string

Decompresses data using the configured compressor.

getStats(): array

Returns compression statistics.

Returns: Array with compression metrics:

  • originalSize - Total bytes processed
  • compressedSize - Total compressed bytes
  • compressionRatio - Compression ratio
  • compressionTime - Time spent compressing (seconds)
close(): void

Closes the compressor and releases resources.

Example Usage
<?php

use Phillarmonic\FrankenZstd\Compressor;

// Quick compression
$data = "Large amount of data to compress...";
$compressed = \Phillarmonic\FrankenZstd\compress($data, 6);
$decompressed = \Phillarmonic\FrankenZstd\decompress($compressed);

// Stateful compression with statistics
$compressor = Compressor::create(Compressor::LEVEL_DEFAULT, Compressor::WINDOW_SIZE_DEFAULT);

$compressed1 = $compressor->compress($data1);
$compressed2 = $compressor->compress($data2);

$stats = $compressor->getStats();
echo "Compression ratio: " . $stats['compressionRatio'] . ":1\n";
echo "Total time: " . $stats['compressionTime'] . " seconds\n";

$compressor->close();

Performance Benchmarks

Typical performance characteristics on modern hardware:

Operation Data Size Speed
Compression 1MB 4,000+ MB/s
Decompression 1MB 6,000+ MB/s
Compression 100KB 3,500+ MB/s
Decompression 100KB 2,000+ MB/s

Configuration

Compression Levels
  • 1-3: Fast compression, lower ratio
  • 4-6: Balanced compression (recommended)
  • 7-15: Higher compression, slower speed
  • 16-22: Maximum compression, slowest speed
Window Sizes
  • 10-15: Low memory usage, faster
  • 16-22: Higher memory usage, better compression
  • 22: Default, good balance

Development

Building for Development
# Install dependencies
just deps

# Generate PHP stubs
just generate-stubs

# Build the extension
just build

# Run CLI tests
just test

# Check extension registration
just check-extension

# Start web server for testing
just web-server

# Run benchmarks
just benchmark
Testing

The extension includes comprehensive tests:

  • Extension Registration: Verifies proper loading and API availability
  • CLI Functionality Tests: Tests compression/decompression with various data types
  • Web Interface Tests: Browser-accessible test pages for web environments
  • Performance Benchmarks: Measures compression speed and ratios
  • Memory Safety: Validates proper resource cleanup

Run all CLI tests:

just test

Test web functionality:

just web-server
# Then visit http://127.0.0.1:10000/web_test.php
Project Structure
franken-zstd-go/
├── README.md              # This file
├── LICENSE               # MIT license
├── go.mod               # Go module definition
├── justfile             # Build automation
├── zstd.stub.php        # PHP API definitions
├── zstd_arginfo.h       # Generated PHP argument info
├── franken_zstd.go      # Core Go implementation
├── convert.go           # Type conversion utilities
├── caddy.go            # FrankenPHP integration
├── zstd.h              # C header declarations
├── zstd.c              # C bridge implementation
└── testdata/           # Test files and examples
    ├── extension_check.php
    ├── index.php
    ├── benchmark.php
    └── Caddyfile

Comparison with C Extensions

Feature FrankenPHP Zstd (Go) Traditional C Extension
Memory Safety Garbage collected, no leaks Manual management, leak-prone
Performance High (pure Go) High (but CGO overhead)
Crash Resistance Excellent Moderate (segfaults possible)
Thread Safety Built-in Manual synchronization
Maintenance Easier (Go ecosystem) Complex (C dependencies)
Platform Support Consistent Platform-specific issues

Technical Details

Architecture
  • Frontend: PHP extension API implemented in C
  • Bridge: Minimal CGO layer for PHP-Go communication
  • Backend: Pure Go compression using github.com/klauspost/compress
  • Integration: Native FrankenPHP module registration
Memory Management
  • Encoder/decoder object pooling for efficiency
  • Automatic cleanup of Go resources
  • Binary-safe data transfer between PHP and Go
  • No manual memory management required
Error Handling
  • Comprehensive error propagation from Go to PHP
  • Custom exception types for different error conditions
  • Graceful handling of invalid data and parameters

License

This project is licensed under the MIT License. See the LICENSE file for details.

Troubleshooting

Web Server Issues

Problem: Error: module not registered: http.encoders.br Solution: Use the Caddyfile approach instead of direct php-server:

# Instead of: ./frankenphp php-server --listen :8080
# Use: ./frankenphp run --config Caddyfile
just web-server

Problem: 404 errors when accessing web pages Solution: Ensure you're starting the server from the correct directory:

cd testdata
../frankenphp run --config Caddyfile
Build Issues

Problem: xcaddy not found Solution: Install xcaddy:

go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

Problem: PHP headers not found during build Solution: Ensure PHP ZTS development headers are installed:

# macOS with Homebrew or Linux with Linuxbrew (use your desired version >=8.2)
brew install shivammathur/php/php@8.4-zts

# Ubuntu/Debian
sudo apt-get install php-dev

# CentOS/RHEL
sudo yum install php-devel

# Windows (using WSL2 with Ubuntu)
sudo apt-get install php-dev

Problem: libphp not found during linking Solution: The build script automatically detects Homebrew/Linuxbrew installations. For manual builds:

# Find your PHP library location
php-config --prefix
find /usr -name "libphp*" 2>/dev/null
find $(brew --prefix 2>/dev/null || echo /usr/local) -name "libphp*" 2>/dev/null

# Add the correct path to CGO_LDFLAGS
export CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs) -L/path/to/php/lib -lphp -lsqlite3"
Extension Loading Issues

Problem: Extension not loaded (extension_loaded('franken-zstd-go') returns false) Solution: Verify the extension is built into the FrankenPHP binary:

just check-extension

Contributing

Contributions are welcome! Please ensure that:

  1. All tests pass (just test)
  2. Code follows Go and PHP best practices
  3. New features include appropriate tests
  4. Documentation is updated for API changes

Support

For issues, questions, or contributions, please visit the project repository or contact the maintainers.

Acknowledgments

  • FrankenPHP: For providing the Go-PHP integration framework
  • klauspost/compress: For the excellent pure Go zstd implementation
  • Zstandard: For the compression algorithm specification

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type App

type App struct {
	// contains filtered or unexported fields
}

App is the Caddy app for the zstd extension

func (App) CaddyModule

func (App) CaddyModule() caddy.ModuleInfo

CaddyModule returns the Caddy module information

func (*App) Provision

func (a *App) Provision(ctx caddy.Context) error

Provision sets up the app

func (*App) Start

func (a *App) Start() error

Start starts the app

func (*App) Stop

func (a *App) Stop() error

Stop stops the app

func (*App) UnmarshalCaddyfile

func (a *App) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

UnmarshalCaddyfile implements caddyfile.Unmarshaler

type CompressionStats

type CompressionStats struct {
	OriginalSize     int64
	CompressedSize   int64
	CompressionRatio float64
	CompressionTime  time.Duration
}

Compression statistics

type Compressor

type Compressor struct {
	// contains filtered or unexported fields
}

Compressor instance

func NewCompressor

func NewCompressor(level, windowSize int) (*Compressor, error)

NewCompressor creates a new compressor instance

func (*Compressor) Close

func (c *Compressor) Close()

Close releases resources

func (*Compressor) Compress

func (c *Compressor) Compress(data []byte) ([]byte, error)

Compress compresses data using zstd

func (*Compressor) Decompress

func (c *Compressor) Decompress(compressedData []byte) ([]byte, error)

Decompress decompresses zstd-compressed data

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL