Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test_php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: 8.2 Optimized
version: "8.2.2"
version-short: "8.2"
command: composer test && composer test_c
command: composer test && composer test_c && composer test_pie
- name: 8.2 Debug
version: 8.2.2-dbg
version-short: "8.2"
Expand All @@ -51,7 +51,7 @@ jobs:
- name: 8.5 Optimized
version: "8.5.2"
version-short: "8.5"
command: composer test && composer test_c
command: composer test && composer test_c && composer test_pie

name: ${{ matrix.continuous-only && inputs.continuous-prefix || '' }} Linux ${{ matrix.name}}
runs-on: ubuntu-22-4core
Expand Down
43 changes: 40 additions & 3 deletions .github/workflows/update_php_repo.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Update protobuf-php Repo
name: Update protobuf-php and protobuf-php-ext Repos

on:
push:
Expand All @@ -10,8 +10,8 @@ permissions:
contents: read # to fetch code in 'Clone protobuf' (actions/checkout)

jobs:
update-repo:
name: Update PHP Repo
update-php-repo:
name: Update protobuf-php Repo
runs-on: ubuntu-latest
steps:
- name: Checkout protobuf-php
Expand Down Expand Up @@ -47,3 +47,40 @@ jobs:
git push --force origin master
git tag -a ${{ env.VERSION_TAG }} -m "Tag release ${{ env.VERSION_TAG }}"
git push origin ${{ env.VERSION_TAG }}

update-php-ext-repo:
name: Update protobuf-php-ext Repo
runs-on: ubuntu-latest
steps:
- name: Checkout protobuf-php-ext
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
repository: protocolbuffers/protobuf-php-ext
token: ${{ secrets.BOT_ACCESS_TOKEN }}
- name: Clone protobuf
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
path: protobuf
- name: Configure Git Bot
run: |
git config user.name "Protobuf Team Bot"
git config user.email "protobuf-team-bot@google.com"
- name: Get PHP Version
run: |
unformatted_version=$( cat protobuf/version.json | jq -r '.[].languages.php' )
version=${unformatted_version/-rc/RC}
version_tag=v$version
echo "VERSION=$version" >> $GITHUB_ENV
echo "VERSION_TAG=$version_tag" >> $GITHUB_ENV
- name: Copy files
run: |
find . -maxdepth 1 ! -name '.git' ! -name '.gitignore' ! -name 'protobuf' ! -name '.' -exec rm -rf {} +
python3 protobuf/php/ext/google/protobuf/package_mirror.py protobuf .
rm -rf protobuf
- name: Push Changes
run: |
git add --all
git commit --allow-empty -m "${{ env.VERSION }} sync"
git push --force origin master
git tag -a ${{ env.VERSION_TAG }} -m "Tag release ${{ env.VERSION_TAG }}"
git push origin ${{ env.VERSION_TAG }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ php/ext/google/protobuf/Makefile.fragments
php/ext/google/protobuf/Makefile.global
php/ext/google/protobuf/Makefile.objects
php/ext/google/protobuf/acinclude.m4
php/ext/google/protobuf/autom4te.cache/
php/ext/google/protobuf/build/
php/ext/google/protobuf/bundled_php.c
php/ext/google/protobuf/config.h
Expand Down
3 changes: 2 additions & 1 deletion php/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"test_c": "./generate_test_protos.sh && ./tests/compile_extension.sh && php -dextension=ext/google/protobuf/modules/protobuf.so vendor/bin/phpunit --bootstrap tests/force_c_ext.php tests",
"test_valgrind": "./generate_test_protos.sh && ./tests/compile_extension.sh && ZEND_DONT_UNLOAD_MODULES=1 USE_VALGRIND=1 USE_ZEND_ALLOC=0 valgrind --leak-check=full --error-exitcode=1 php -dextension=ext/google/protobuf/modules/protobuf.so vendor/bin/phpunit --bootstrap tests/force_c_ext.php tests",
"test": "./generate_test_protos.sh && vendor/bin/phpunit tests",
"aggregate_metadata_test": "./generate_test_protos.sh --aggregate_metadata && vendor/bin/phpunit tests"
"aggregate_metadata_test": "./generate_test_protos.sh --aggregate_metadata && vendor/bin/phpunit tests",
"test_pie": "./ext/google/protobuf/test_pie_package.sh"
},
"config": {
"process-timeout": 1200
Expand Down
15 changes: 15 additions & 0 deletions php/ext/google/protobuf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Protocol Buffers PHP Extension Mirror (PIE)

This repository is a **read-only mirror** of the Protocol Buffers PHP extension C source code, optimized for installation using **PIE (PHP Installer for Extensions)**.

Development of the Protocol Buffers PHP extension takes place inside the main repository at [protocolbuffers/protobuf](https://github.com/protocolbuffers/protobuf). Please submit all issues, feature requests, and pull requests to the main repository.

## Installation via PIE

You can install this extension using PIE:

```bash
pie install google/protobuf-ext
```

Ensure your system has the required build tools (such as `make`, `gcc`, `autoconf`, and `phpize`) installed prior to running the installation command.
14 changes: 14 additions & 0 deletions php/ext/google/protobuf/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "google/protobuf-ext",
"type": "php-ext",
"description": "Protocol Buffers PHP Extension",
"keywords": ["proto", "protobuf"],
"homepage": "https://developers.google.com/protocol-buffers/",
"license": "BSD-3-Clause",
"require": {
"php": ">=8.2.0"
},
"php-ext": {
"extension-name": "protobuf"
}
}
66 changes: 66 additions & 0 deletions php/ext/google/protobuf/package_mirror.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
# Protocol Buffers - Google's data interchange format
# Copyright 2026 Google Inc. All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd

import glob
import os
import shutil
import sys

def copy_files(src_dir, dst_dir, patterns):
os.makedirs(dst_dir, exist_ok=True)
for pattern in patterns:
for src_path in glob.glob(os.path.join(src_dir, pattern)):
if os.path.isfile(src_path):
filename = os.path.basename(src_path)
dst_path = os.path.join(dst_dir, filename)
shutil.copy2(src_path, dst_path)
print(f"Copied: {src_path} -> {dst_path}")

def main():
src_root = sys.argv[1] if len(sys.argv) > 1 else 'protobuf'
dst_root = sys.argv[2] if len(sys.argv) > 2 else '.'

ext_src_dir = os.path.join(src_root, 'php', 'ext', 'google', 'protobuf')
utf8_range_src_dir = os.path.join(src_root, 'third_party', 'utf8_range')

if not os.path.exists(ext_src_dir):
print(f"Error: Extension source directory not found: {ext_src_dir}")
sys.exit(1)

if not os.path.exists(utf8_range_src_dir):
fallback_dir = os.path.join(ext_src_dir, 'third_party', 'utf8_range')
if os.path.exists(fallback_dir):
utf8_range_src_dir = fallback_dir
else:
print(f"Warning: utf8_range source directory not found at {utf8_range_src_dir}")

print(f"Syncing PHP extension files from {src_root} to {dst_root}...")

# Copy root extension files (.c, .h, .inc, config files, composer.json, README.md)
copy_files(ext_src_dir, dst_root, [
'*.c', '*.h', '*.inc', '*.m4', '*.w32',
'composer.json', 'README.md', 'LICENSE'
])

# Copy third_party/utf8_range dependencies
if os.path.exists(utf8_range_src_dir):
utf8_range_dst_dir = os.path.join(dst_root, 'third_party', 'utf8_range')
copy_files(utf8_range_src_dir, utf8_range_dst_dir, [
'*.c', '*.h', '*.inc', 'LICENSE', 'README.md'
])

# Copy root LICENSE if not already copied from ext_src_dir
root_license = os.path.join(src_root, 'LICENSE')
if os.path.exists(root_license) and not os.path.exists(os.path.join(dst_root, 'LICENSE')):
shutil.copy2(root_license, os.path.join(dst_root, 'LICENSE'))
print(f"Copied: {root_license} -> {os.path.join(dst_root, 'LICENSE')}")

print("Sync completed successfully.")

if __name__ == '__main__':
main()
43 changes: 43 additions & 0 deletions php/ext/google/protobuf/test_pie_package.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash
# Protocol Buffers - Google's data interchange format
# Copyright 2026 Google Inc. All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd

set -ex

# Ensure we are running from inside the php/ directory
if [ ! -f "ext/google/protobuf/package_mirror.py" ]; then
echo "This script must be run from inside the php/ directory."
exit 1
fi

PIE_PHAR_URL="https://github.com/php/pie/releases/latest/download/pie.phar"
TEST_DIR=$(mktemp -d -t pie_test_XXXXXX)
PIE_BIN="$TEST_DIR/pie.phar"

export XDG_CONFIG_HOME="$TEST_DIR/config"
export COMPOSER_HOME="$TEST_DIR/composer"
export PIE_HOME="$TEST_DIR/pie"
mkdir -p "$XDG_CONFIG_HOME" "$COMPOSER_HOME" "$PIE_HOME"

cleanup() {
rm -rf "$TEST_DIR"
}
trap cleanup EXIT

echo "Downloading latest PIE installer..."
curl -sSL -o "$PIE_BIN" "$PIE_PHAR_URL"

echo "Syncing package mirror structure into temporary test directory..."
python3 ext/google/protobuf/package_mirror.py .. "$TEST_DIR/mirror"

echo "Adding local path repository to PIE..."
php "$PIE_BIN" repository:add path "$TEST_DIR/mirror"

echo "Testing PIE build for google/protobuf-ext..."
php "$PIE_BIN" build google/protobuf-ext:@dev

echo "PIE package build test completed successfully!"
37 changes: 37 additions & 0 deletions src/google/protobuf/compiler/php/generator_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,43 @@ TEST_F(PhpGeneratorTest, Basic) {
ExpectNoErrors();
}

TEST_F(PhpGeneratorTest, Formatting) {
CreateTempFile("google/protobuf/wrappers.proto",
R"schema(
syntax = "proto3";
package google.protobuf;
message Int32Value {
int32 value = 1;
})schema");

CreateTempFile("foo.proto",
R"schema(
syntax = "proto3";
import "google/protobuf/wrappers.proto";
message Foo {
optional int32 bar = 1;
google.protobuf.Int32Value wrapped_val = 2;
})schema");

RunProtoc(
"protocol_compiler --proto_path=$tmpdir --php_out=$tmpdir foo.proto");

ExpectNoErrors();

ExpectFileContentContainsSubstring(
"Foo.php",
" public function __construct($data = null)\n"
" {\n");

ExpectFileContentContainsSubstring(
"Foo.php",
" public function setWrappedValUnwrapped($var)\n"
" {\n"
" $this->writeWrapperValue(\"wrapped_val\", $var);\n"
" return $this;\n"
" }\n");
}

TEST_F(PhpGeneratorTest, Proto2File) {
CreateTempFile("foo.proto",
R"schema(
Expand Down
Loading