freema/simdjson-polyfill

🚀 Automatic simdjson integration for PHP with multiple override strategies. Drop-in replacement for json_decode() with 3x performance boost. Symfony, Nette & Laravel support included.

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/freema/simdjson-polyfill

v1.1.0 2025-11-19 09:14 UTC

This package is auto-updated.

Last update: 2025-11-19 09:15:28 UTC


README

Tests Latest Stable Version License

Automatic simdjson integration for PHP with multiple override strategies.

Get 3x faster JSON parsing with minimal code changes. Drop-in replacement for json_decode() with Symfony, Nette & Laravel support included.

📋 Table of Contents

🎯 Why SimdJson?

simdjson is the world's fastest JSON parser, using SIMD instructions to parse JSON at speeds approaching 3 GB/s. This library makes it trivial to use simdjson in your PHP applications.

Performance gains:

  • 🚀 3x faster JSON parsing compared to native json_decode()
  • ⚡ Perfect for APIs processing large JSON payloads
  • 💾 Lower CPU usage and better resource utilization
  • 🔥 Zero-copy parsing when possible

📋 Requirements

  • PHP 8.0 or higher (tested on PHP 8.0, 8.1, 8.2, 8.3, and 8.4)
  • Composer

PHP 8.4 Note: All strategies work on PHP 8.4 except UopzStrategy, which requires PHP ≤ 8.3 due to uopz extension incompatibility.

📦 Installation

composer require freema/simdjson-polyfill

Optional (for best performance):

# Install simdjson PHP extension
pecl install simdjson

Optional (for advanced strategies):

# Install UOPZ extension for global override
pecl install uopz

🚀 Quick Start

Option 1: Safe Polyfill (Recommended)

Use JsonDecoder::decode() or fast_json_decode() instead of json_decode():

use SimdJsonPolyfill\JsonDecoder;

// Using static method
$data = JsonDecoder::decode($json, true);

// Or using helper function
$data = \SimdJsonPolyfill\fast_json_decode($json, true);

Option 2: Auto-Enable (Magic Mode)

Automatically use the best available strategy:

use SimdJsonPolyfill\SimdJsonPolyfill;

// Enable once at application bootstrap
SimdJsonPolyfill::enable();

// Now json_decode() uses simdjson (if using aggressive strategies)
// Or use JsonDecoder::decode() for safe polyfill

Option 3: Explicit Strategy

Choose a specific strategy:

use SimdJsonPolyfill\SimdJsonPolyfill;

// Use UOPZ to override json_decode() globally
SimdJsonPolyfill::enable([
    'strategy' => 'uopz',
    'allow_in_production' => true, // Required for production
]);

// Now ALL json_decode() calls use simdjson!
$data = json_decode($json, true);

🎛️ Strategies

SimdJson Polyfill offers multiple strategies with different trade-offs:

Strategy Risk Level Performance Global Override Requires Extension
PolyfillStrategy ✅ Safe High No ext-simdjson
UopzStrategy ⚠️ Risky Highest Yes ext-uopz, ext-simdjson
NamespaceStrategy ⚠️ Medium High Per-namespace ext-simdjson
AutoPrependStrategy ⚠️ Medium High Yes (manual) ext-simdjson
ComposerPluginStrategy 🔥 Very Risky High Yes (build-time) ext-simdjson, nikic/php-parser

1. PolyfillStrategy (Safe, Default)

Recommended for most use cases.

Provides JsonDecoder::decode() and fast_json_decode() functions. No magic, no risks.

use SimdJsonPolyfill\JsonDecoder;

$data = JsonDecoder::decode($jsonString, true);

Pros:

  • ✅ Zero risk - no global side effects
  • ✅ Works everywhere
  • ✅ Easy to test and debug

Cons:

  • ❌ Requires code changes to use new functions
  • ❌ Won't affect third-party code

2. UopzStrategy (Risky, Powerful)

⚠️ WARNING: Modifies runtime behavior globally!

Uses the UOPZ extension to override json_decode() at runtime.

SimdJsonPolyfill::enable([
    'strategy' => 'uopz',
    'allow_in_production' => true, // Required!
]);

// Now ALL json_decode() calls use simdjson
$data = json_decode($json, true);

Pros:

  • ✅ Zero code changes needed
  • ✅ Affects vendor code too
  • ✅ Can be toggled on/off

Cons:

  • ⚠️ Requires ext-uopz
  • ⚠️ May have unexpected side effects
  • ⚠️ Disabled in production by default
  • Not compatible with PHP 8.4+ (uopz doesn't support PHP 8.4 due to ZEND_EXIT opcode removal)

3. NamespaceStrategy (Medium Risk)

Generates namespace-specific json_decode() functions.

SimdJsonPolyfill::enable([
    'strategy' => 'namespace',
    'namespaces' => ['App\\Services', 'App\\Controllers'],
    'output_dir' => '/tmp/simdjson-functions',
]);

Pros:

  • ✅ Scoped to specific namespaces
  • ✅ More controlled than UOPZ

Cons:

  • ❌ Requires namespace configuration
  • ❌ Generated files need to be autoloaded

4. AutoPrependStrategy (Medium Risk)

Generates a file for PHP's auto_prepend_file directive.

SimdJsonPolyfill::enable([
    'strategy' => 'auto-prepend',
    'output_file' => '/var/www/simdjson-prepend.php',
]);

// Then configure php.ini:
// auto_prepend_file = "/var/www/simdjson-prepend.php"

Pros:

  • ✅ Global override without UOPZ
  • ✅ Works for all PHP scripts

Cons:

  • ⚠️ Requires php.ini configuration
  • ⚠️ Affects entire PHP environment

5. ComposerPluginStrategy (Very Risky)

🔥 EXTREMELY RISKY: Modifies vendor code using AST rewriting!

Rewrites json_decode() calls in vendor/ at install time.

SimdJsonPolyfill::enable([
    'strategy' => 'composer-plugin',
    'i_understand_the_risks' => true, // Required!
    'create_backups' => true,
    'exclude_patterns' => ['*/tests/*'],
]);

Pros:

  • ✅ Build-time modification
  • ✅ No runtime overhead

Cons:

  • 🔥 Modifies third-party code
  • 🔥 May break updates
  • 🔥 Creates .bak files everywhere
  • 🔥 Never auto-detected, always explicit

🔌 Framework Integration

Symfony

// config/bundles.php
return [
    // ...
    SimdJsonPolyfill\Bridge\Symfony\SimdJsonBundle::class => ['all' => true],
];
# config/packages/simdjson.yaml
simdjson:
    enabled: true
    strategy: auto  # or: polyfill, uopz, namespace, etc.
    auto_detect: true

    # UOPZ strategy config
    uopz:
        allow_in_production: false

    # Namespace strategy config
    namespace:
        namespaces:
            - 'App\Service'
            - 'App\Controller'
        output_dir: '%kernel.cache_dir%/simdjson'

Laravel

// config/app.php (Laravel 10 and below)
'providers' => [
    // ...
    SimdJsonPolyfill\Bridge\Laravel\SimdJsonServiceProvider::class,
];

Laravel 11+ uses auto-discovery, no manual registration needed!

# Publish configuration (optional)
php artisan vendor:publish --provider="SimdJsonPolyfill\Bridge\Laravel\SimdJsonServiceProvider" --tag="config"
// config/simdjson.php
return [
    'enabled' => env('SIMDJSON_ENABLED', true),
    'strategy' => env('SIMDJSON_STRATEGY', 'auto'),
    'auto_detect' => env('SIMDJSON_AUTO_DETECT', true),

    'uopz' => [
        'allow_in_production' => env('SIMDJSON_UOPZ_ALLOW_PRODUCTION', false),
    ],

    'namespace' => [
        'namespaces' => [
            // 'App\Services',
            // 'App\Http\Controllers',
        ],
        'output_dir' => storage_path('framework/simdjson'),
    ],
];

.env configuration:

SIMDJSON_ENABLED=true
SIMDJSON_STRATEGY=auto
SIMDJSON_AUTO_DETECT=true

Nette

// config/common.neon
extensions:
    simdjson: SimdJsonPolyfill\Bridge\Nette\DI\SimdJsonExtension

simdjson:
    enabled: true
    strategy: auto
    autoDetect: true

    namespace:
        namespaces:
            - App\Services
            - App\Presenters

📊 Benchmarks

Results from benchmarking on PHP 8.3:

========================================
SimdJsonPolyfill Benchmark
========================================
Iterations: 10000
PHP Version: 8.3.27
simdjson extension: YES
========================================

===== File: small.json (3.3 KB) =====
json_decode             98.26 ms (0.010 ms/op) | memΔ:     4 KB | peakΔ:      0 B
simdjson_decode         35.67 ms (0.004 ms/op) | memΔ:     4 KB | peakΔ:      0 B
➡️  simdjson_decode is approximately 2.75x faster (wall-time)
simdjson (polyfill)     35.69 ms (0.004 ms/op) | memΔ:     4 KB | peakΔ:      0 B

===== File: medium.json (50.6 KB) =====
json_decode            926.56 ms (0.093 ms/op) | memΔ:     4 KB | peakΔ:      0 B
simdjson_decode        379.83 ms (0.038 ms/op) | memΔ:     4 KB | peakΔ:      0 B
➡️  simdjson_decode is approximately 2.44x faster (wall-time)
simdjson (polyfill)    378.66 ms (0.038 ms/op) | memΔ:     4 KB | peakΔ:      0 B

===== File: large.json (101.1 KB) =====
json_decode          1,833.82 ms (0.183 ms/op) | memΔ:     4 KB | peakΔ:      0 B
simdjson_decode        792.74 ms (0.079 ms/op) | memΔ:     4 KB | peakΔ:      0 B
➡️  simdjson_decode is approximately 2.31x faster (wall-time)
simdjson (polyfill)    779.16 ms (0.078 ms/op) | memΔ:     4 KB | peakΔ:      0 B

===== File: xlarge.json (500.7 KB) =====
json_decode          9,130.94 ms (0.913 ms/op) | memΔ:     4 KB | peakΔ:   1.8 MB
simdjson_decode      3,752.57 ms (0.375 ms/op) | memΔ:     4 KB | peakΔ:    376 B
➡️  simdjson_decode is approximately 2.43x faster (wall-time)
simdjson (polyfill)  3,751.59 ms (0.375 ms/op) | memΔ:     4 KB | peakΔ:    376 B

Run benchmarks yourself:

task benchmark
# or
docker-compose run --rm php php tests/Benchmark/JsonDecodeBenchmark.php

🐳 Development

This project uses Docker for development and testing, with Task as the build tool.

Prerequisites

Install Task:

# macOS
brew install go-task

# Linux
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin

# Or see https://taskfile.dev/installation/

Setup

# Build containers
task build

# Install dependencies
task install

# Generate benchmark fixtures
task fixtures

Testing

# Run all tests
task test

# Run tests with coverage
task test-coverage

# Test on all PHP versions
task test-all

# Run static analysis
task stan

Benchmarking

# Generate fixtures
task fixtures

# Run benchmarks
task benchmark

Available Commands

task --list          # Show all available commands
task shell           # Open shell in PHP container
task install         # Install Composer dependencies
task test            # Run PHPUnit tests
task benchmark       # Run benchmarks
task clean           # Clean up containers and dependencies

⚠️ Warnings and Best Practices

When to Use Which Strategy

Production Applications:

  • ✅ Use PolyfillStrategy - Safe, predictable, testable
  • ⚠️ Consider UopzStrategy only if you thoroughly test

Development/Staging:

  • ✅ Any strategy is fine for experimentation

Never Use:

  • 🚫 ComposerPluginStrategy in production
  • 🚫 AutoPrependStrategy without extensive testing

Safety Tips

  1. Always test in staging first
  2. Monitor for edge cases - simdjson may parse JSON slightly differently
  3. Have a rollback plan - disable with one config change
  4. Check compatibility - some JSON edge cases may behave differently
  5. Use version control - especially with ComposerPluginStrategy

🧪 Testing

# Unit tests
vendor/bin/phpunit

# With Docker
make test

# Test specific PHP version
make test-83

# Test all versions
make test-all

📄 License

MIT License. See LICENSE file for details.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

🔗 Links

📮 Support

Made with ❤️ by Tomas Grasl