How to Use Protocol Buffers in PHP

A practical guide to working with Protocol Buffers in PHP web applications

Published: January 2025 • 9 min read

Protocol Buffers works great with PHP for building efficient web APIs and microservices. While JSON is common in PHP, Protobuf offers faster serialization and smaller payloads, especially useful for high-traffic applications or mobile API backends.

This guide walks you through everything you need to get started with Protocol Buffers in PHP, from installation to building real working examples. We'll keep it simple and practical.

What You'll Need

  • PHP: Version 7.4 or higher (check with php -v)
  • Composer: PHP package manager (get it here)
  • Protocol Buffer Compiler: We'll install this in the next step

Step 1: Install Protocol Buffers

First, install the PHP protobuf extension using Composer:

composer require google/protobuf

Next, install the Protocol Buffer compiler (protoc). On Ubuntu/Debian:

sudo apt update
sudo apt install protobuf-compiler

# Verify installation
protoc --version

On macOS with Homebrew:

brew install protobuf

On Windows, download from the official releases page and add to your PATH.

Step 2: Set Up Your Project

Create a new PHP project:

mkdir php-protobuf-demo
cd php-protobuf-demo
composer init --name="your-name/protobuf-demo"
composer require google/protobuf

mkdir protos
mkdir generated

This creates a basic PHP project with folders for your proto files and generated code.

Step 3: Create Your .proto File

Create a file called protos/subscriber.proto. We'll use a telecom subscriber example:

syntax = "proto3";

package telecom;

// Mobile subscriber information
message Subscriber {
  string msisdn = 1;           // Mobile number
  string name = 2;             // Subscriber name
  string email = 3;            // Email address
  SubscriptionType type = 4;   // Plan type
  bool active = 5;             // Account status
  repeated string services = 6; // Active services
}

enum SubscriptionType {
  PREPAID = 0;
  POSTPAID = 1;
  CORPORATE = 2;
}

Note: In PHP, we use the package declaration which will become the namespace in generated PHP code.

Step 4: Compile the .proto File

Generate PHP classes from your proto file:

protoc --php_out=generated --proto_path=protos protos/subscriber.proto

This creates PHP files in the generated/ folder. The structure will be:

generated/
  └── Telecom/
      ├── Subscriber.php
      └── SubscriptionType.php

Step 5: Use Protocol Buffers in PHP

Create a file called example.php:

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Telecom\Subscriber;
use Telecom\SubscriptionType;

// Create a new subscriber
$subscriber = new Subscriber();
$subscriber->setMsisdn('+91-9876543210');
$subscriber->setName('Telecom Customer');
$subscriber->setEmail('customer@telecom.com');
$subscriber->setType(SubscriptionType::POSTPAID);
$subscriber->setActive(true);
$subscriber->setServices(['Voice', 'Data', '4G LTE']);

echo "Created subscriber: " . $subscriber->getName() . "\n";
echo "MSISDN: " . $subscriber->getMsisdn() . "\n";
echo "Type: " . SubscriptionType::name($subscriber->getType()) . "\n";

// Serialize to binary
$binaryData = $subscriber->serializeToString();
echo "Serialized to " . strlen($binaryData) . " bytes\n\n";

// Deserialize from binary
$newSubscriber = new Subscriber();
$newSubscriber->mergeFromString($binaryData);
echo "Deserialized subscriber: " . $newSubscriber->getName() . "\n";
echo "Email: " . $newSubscriber->getEmail() . "\n";
echo "Active: " . ($newSubscriber->getActive() ? 'Yes' : 'No') . "\n";

// Access repeated fields (services)
echo "\nActive services:\n";
foreach ($newSubscriber->getServices() as $service) {
    echo "  - $service\n";
}

Step 6: Run Your Application

Run the PHP script:

php example.php

You should see output like:

Created subscriber: Telecom Customer
MSISDN: +91-9876543210
Type: POSTPAID
Serialized to 78 bytes

Deserialized subscriber: Telecom Customer
Email: customer@telecom.com
Active: Yes

Active services:
  - Voice
  - Data
  - 4G LTE

Saving and Loading from Files

Save and load protobuf data to/from files:

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Telecom\Subscriber;
use Telecom\SubscriptionType;

// Create subscriber
$subscriber = new Subscriber();
$subscriber->setMsisdn('+91-9123456789');
$subscriber->setName('Network Admin');
$subscriber->setEmail('admin@telecom.com');
$subscriber->setType(SubscriptionType::CORPORATE);
$subscriber->setActive(true);

// Save to file
file_put_contents('subscriber.bin', $subscriber->serializeToString());
echo "Saved to file\n";

// Load from file
$loadedSubscriber = new Subscriber();
$loadedSubscriber->mergeFromString(file_get_contents('subscriber.bin'));
echo "Loaded: " . $loadedSubscriber->getName() . "\n";
echo "MSISDN: " . $loadedSubscriber->getMsisdn() . "\n";

Convert Between JSON and Protobuf

PHP protobuf library supports JSON conversion:

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Telecom\Subscriber;
use Telecom\SubscriptionType;
use Google\Protobuf\Internal\Message;

// Create subscriber
$subscriber = new Subscriber();
$subscriber->setMsisdn('+91-9876543210');
$subscriber->setName('Mobile User');
$subscriber->setType(SubscriptionType::PREPAID);

// Convert to JSON
$jsonString = $subscriber->serializeToJsonString();
echo "JSON output:\n";
echo $jsonString . "\n\n";

// Parse from JSON
$jsonData = '{
  "msisdn": "+91-9111111111",
  "name": "New Subscriber",
  "type": "POSTPAID",
  "active": true
}';

$newSubscriber = new Subscriber();
$newSubscriber->mergeFromJsonString($jsonData);
echo "Parsed from JSON: " . $newSubscriber->getName() . "\n";
echo "Type: " . SubscriptionType::name($newSubscriber->getType()) . "\n";

Using Protobuf with Laravel

Here's how to use Protobuf in a Laravel API controller:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Telecom\Subscriber;
use Telecom\SubscriptionType;

class SubscriberController extends Controller
{
    public function create(Request $request)
    {
        // Accept JSON, convert to Protobuf
        $subscriber = new Subscriber();
        $subscriber->setMsisdn($request->input('msisdn'));
        $subscriber->setName($request->input('name'));
        $subscriber->setEmail($request->input('email'));
        $subscriber->setType(SubscriptionType::POSTPAID);
        $subscriber->setActive(true);
        
        // Save to database or process...
        
        // Return protobuf binary
        return response($subscriber->serializeToString())
            ->header('Content-Type', 'application/x-protobuf');
    }
    
    public function get($msisdn)
    {
        // Fetch subscriber from database
        // For demo, create one
        $subscriber = new Subscriber();
        $subscriber->setMsisdn($msisdn);
        $subscriber->setName('Retrieved Subscriber');
        $subscriber->setType(SubscriptionType::PREPAID);
        
        // Return as protobuf
        return response($subscriber->serializeToString())
            ->header('Content-Type', 'application/x-protobuf');
    }
    
    public function parse(Request $request)
    {
        // Receive protobuf binary in request
        $subscriber = new Subscriber();
        $subscriber->mergeFromString($request->getContent());
        
        // Process the subscriber
        return response()->json([
            'name' => $subscriber->getName(),
            'msisdn' => $subscriber->getMsisdn(),
            'type' => SubscriptionType::name($subscriber->getType())
        ]);
    }
}

Best Practices for PHP

Use Composer Autoloading

Always use composer autoload instead of manually requiring files. This ensures proper namespace loading.

Handle Binary Data Carefully

When sending protobuf over HTTP, set the correct content type:application/x-protobuf

Keep Generated Code Separate

Store generated PHP files in a separate directory and don't edit them manually. They'll be overwritten on recompilation.

Check Field Existence

Use has methods to check if optional fields are set before accessing them.

Common Issues

Issue: Class not found after compilation

Solution: Make sure your generated files are in a directory that Composer can autoload. Update composer.json to include the generated directory in the autoload section.

Issue: protoc command not found

Solution: Install the Protocol Buffer compiler using your system package manager or download from GitHub releases.

Issue: Namespace errors

Solution: PHP uses the package name from your .proto file as the namespace. Make sure it follows PHP naming conventions.

Related Tools

Additional Resources

Official Documentation & References

Conclusion

Protocol Buffers integrates smoothly with PHP, especially for building efficient APIs and microservices. While JSON is more common in the PHP world, Protobuf shines when you need speed, smaller payloads, or are building mobile backends.

Start with the simple examples here, then integrate into your Laravel, Symfony, or custom PHP applications. The performance benefits become clear when handling high traffic or mobile clients with limited bandwidth.