Skip to content
Merged
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
35 changes: 33 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,35 @@ jobs:
run: |
docker run --rm liblpm-go:ci

# PHP bindings test
test-php-bindings:
name: Test PHP Bindings
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: recursive

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build PHP container
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile.php
push: false
load: true
tags: liblpm-php:ci
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Run PHP tests
run: |
docker run --rm liblpm-php:ci

# Code quality checks
code-quality:
name: Code Quality
Expand Down Expand Up @@ -167,7 +196,7 @@ jobs:
ci-summary:
name: CI Summary
runs-on: ubuntu-latest
needs: [build-and-test, test-cpp-bindings, test-go-bindings, code-quality]
needs: [build-and-test, test-cpp-bindings, test-go-bindings, test-php-bindings, code-quality]
if: always()

steps:
Expand All @@ -177,12 +206,14 @@ jobs:
echo "Build and test: ${{ needs.build-and-test.result }}"
echo "C++ bindings: ${{ needs.test-cpp-bindings.result }}"
echo "Go bindings: ${{ needs.test-go-bindings.result }}"
echo "PHP bindings: ${{ needs.test-php-bindings.result }}"
echo "Code quality: ${{ needs.code-quality.result }}"

# Fail if any required job failed
if [[ "${{ needs.build-and-test.result }}" == "failure" ]] || \
[[ "${{ needs.test-cpp-bindings.result }}" == "failure" ]] || \
[[ "${{ needs.test-go-bindings.result }}" == "failure" ]]; then
[[ "${{ needs.test-go-bindings.result }}" == "failure" ]] || \
[[ "${{ needs.test-php-bindings.result }}" == "failure" ]]; then
echo "One or more required jobs failed"
exit 1
fi
88 changes: 88 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ option(WITH_DPDK_BENCHMARK "Build DPDK comparison benchmark (requires DPDK)" OFF
option(WITH_EXTERNAL_LPM_BENCHMARK "Build benchmarks with external LPM libraries (Docker only)" OFF)
option(BUILD_GO_WRAPPER "Build Go wrapper and bindings" OFF)
option(BUILD_CPP_WRAPPER "Build C++ wrapper and bindings" OFF)
option(BUILD_PHP_WRAPPER "Build PHP extension and bindings" OFF)

# External LPM libraries directory (set by Docker builds)
set(EXTERNAL_LPM_DIR "" CACHE PATH "Directory containing external LPM libraries")
Expand Down Expand Up @@ -390,6 +391,88 @@ if(BUILD_GO_WRAPPER)
endif()
endif()

# PHP extension
if(BUILD_PHP_WRAPPER)
find_program(PHP_CONFIG_EXECUTABLE NAMES php-config php-config8.3 php-config8.2 php-config8.1)
if(PHP_CONFIG_EXECUTABLE)
# Get PHP version
execute_process(
COMMAND ${PHP_CONFIG_EXECUTABLE} --version
OUTPUT_VARIABLE PHP_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(STATUS "Found PHP: ${PHP_VERSION}")

# Get PHP include path
execute_process(
COMMAND ${PHP_CONFIG_EXECUTABLE} --includes
OUTPUT_VARIABLE PHP_INCLUDES
OUTPUT_STRIP_TRAILING_WHITESPACE
)

# Get PHP extension directory
execute_process(
COMMAND ${PHP_CONFIG_EXECUTABLE} --extension-dir
OUTPUT_VARIABLE PHP_EXTENSION_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)

# Get PHP binary
execute_process(
COMMAND ${PHP_CONFIG_EXECUTABLE} --php-binary
OUTPUT_VARIABLE PHP_BINARY
OUTPUT_STRIP_TRAILING_WHITESPACE
)

message(STATUS " PHP binary: ${PHP_BINARY}")
message(STATUS " PHP extension dir: ${PHP_EXTENSION_DIR}")

# Custom target to configure PHP extension
add_custom_target(php_configure
COMMAND phpize
COMMAND ${CMAKE_COMMAND} -E env "LDFLAGS=-L${CMAKE_BINARY_DIR}"
./configure --with-liblpm=${CMAKE_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bindings/php
DEPENDS lpm
COMMENT "Configuring PHP extension"
)

# Custom target to build PHP extension
add_custom_target(php_wrapper ALL
COMMAND make
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bindings/php
DEPENDS php_configure
COMMENT "Building PHP extension"
)

# Custom target to test PHP extension
add_custom_target(php_test
COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{LD_LIBRARY_PATH}"
${PHP_BINARY} run-tests.php -q tests/
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bindings/php
DEPENDS php_wrapper
COMMENT "Testing PHP extension"
)

# Custom target to install PHP extension
add_custom_target(php_install
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/bindings/php/modules/liblpm.so
${PHP_EXTENSION_DIR}/liblpm.so
DEPENDS php_wrapper
COMMENT "Installing PHP extension to ${PHP_EXTENSION_DIR}"
)

message(STATUS "PHP extension targets added:")
message(STATUS " make php_wrapper - Build PHP extension")
message(STATUS " make php_test - Run PHP tests")
message(STATUS " make php_install - Install PHP extension")
else()
message(WARNING "php-config not found. PHP extension will not be built.")
message(WARNING "Install PHP development files: sudo apt install php-dev")
endif()
endif()

# Print configuration summary
message(STATUS "liblpm configuration:")
message(STATUS " Version: ${PROJECT_VERSION}")
Expand Down Expand Up @@ -435,6 +518,11 @@ if(BUILD_CPP_WRAPPER)
else()
message(STATUS " Build C++ wrapper: OFF")
endif()
if(BUILD_PHP_WRAPPER AND PHP_CONFIG_EXECUTABLE)
message(STATUS " Build PHP extension: ON")
else()
message(STATUS " Build PHP extension: OFF")
endif()

# ============================================================================
# CPack Configuration for .deb and .rpm packages
Expand Down
52 changes: 52 additions & 0 deletions bindings/php/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Build artifacts
*.lo
*.la
*.o
*.so
.libs/
.deps/
modules/

# Autotools generated files
autom4te.cache/
Makefile
Makefile.fragments
Makefile.objects
acinclude.m4
aclocal.m4
build/
config.guess
config.h
config.h.in~
config.log
config.nice
config.status
config.sub
configure
configure.ac~
install-sh
libtool
ltmain.sh
missing
mkinstalldirs
run-tests.php

# Dependency files
*.dep

# PHP extension build files
*.gcda
*.gcno

# Test results
tests/*.diff
tests/*.exp
tests/*.log
tests/*.out
tests/*.php
tests/*.sh

# Backup files
*~
*.bak
*.swp
121 changes: 121 additions & 0 deletions bindings/php/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# CMakeLists.txt for liblpm PHP extension
#
# This file enables building the PHP extension using CMake directly.
# For production use, prefer the phpize/configure/make workflow.
#
# Usage:
# mkdir build && cd build
# cmake -DBUILD_PHP_WRAPPER=ON ..
# make php_wrapper
# make php_test
# make php_install
#
# Or standalone (requires liblpm to be installed):
# cd bindings/php
# phpize
# ./configure --with-liblpm=/usr/local
# make
# make test
# sudo make install

cmake_minimum_required(VERSION 3.16)
project(liblpm_php LANGUAGES C)

# Find php-config
find_program(PHP_CONFIG_EXECUTABLE NAMES php-config php-config8.3 php-config8.2 php-config8.1)

if(NOT PHP_CONFIG_EXECUTABLE)
message(FATAL_ERROR "php-config not found. Install PHP development files.")
endif()

# Get PHP configuration
execute_process(
COMMAND ${PHP_CONFIG_EXECUTABLE} --version
OUTPUT_VARIABLE PHP_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)

execute_process(
COMMAND ${PHP_CONFIG_EXECUTABLE} --includes
OUTPUT_VARIABLE PHP_INCLUDES
OUTPUT_STRIP_TRAILING_WHITESPACE
)

execute_process(
COMMAND ${PHP_CONFIG_EXECUTABLE} --extension-dir
OUTPUT_VARIABLE PHP_EXTENSION_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)

execute_process(
COMMAND ${PHP_CONFIG_EXECUTABLE} --php-binary
OUTPUT_VARIABLE PHP_BINARY
OUTPUT_STRIP_TRAILING_WHITESPACE
)

message(STATUS "PHP version: ${PHP_VERSION}")
message(STATUS "PHP extension dir: ${PHP_EXTENSION_DIR}")

# Parse include flags
separate_arguments(PHP_INCLUDE_FLAGS UNIX_COMMAND "${PHP_INCLUDES}")

# Find liblpm
find_library(LPM_LIBRARY NAMES lpm PATHS /usr/local/lib /usr/lib)
find_path(LPM_INCLUDE_DIR lpm/lpm.h PATHS /usr/local/include /usr/include)

if(NOT LPM_LIBRARY OR NOT LPM_INCLUDE_DIR)
# Try parent project
if(TARGET lpm)
set(LPM_LIBRARY lpm)
set(LPM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include)
else()
message(FATAL_ERROR "liblpm not found. Install liblpm or build from parent project.")
endif()
endif()

message(STATUS "liblpm library: ${LPM_LIBRARY}")
message(STATUS "liblpm include: ${LPM_INCLUDE_DIR}")

# Build the PHP extension as a shared library
add_library(liblpm_php MODULE
liblpm.c
)

# Set output name to liblpm (produces liblpm.so)
set_target_properties(liblpm_php PROPERTIES
OUTPUT_NAME liblpm
PREFIX ""
SUFFIX ".so"
)

# Add PHP include paths
target_include_directories(liblpm_php PRIVATE
${LPM_INCLUDE_DIR}
)

# Add PHP include flags
target_compile_options(liblpm_php PRIVATE
${PHP_INCLUDE_FLAGS}
-DCOMPILE_DL_LIBLPM
-DHAVE_CONFIG_H
)

# Link against liblpm
target_link_libraries(liblpm_php PRIVATE
${LPM_LIBRARY}
)

# Installation
install(TARGETS liblpm_php
LIBRARY DESTINATION ${PHP_EXTENSION_DIR}
)

# Test target
add_custom_target(test_php
COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{LD_LIBRARY_PATH}"
${PHP_BINARY} -d "extension=${CMAKE_CURRENT_BINARY_DIR}/liblpm.so"
${CMAKE_CURRENT_SOURCE_DIR}/tests/run_tests.php
DEPENDS liblpm_php
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Running PHP extension tests"
)
25 changes: 25 additions & 0 deletions bindings/php/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Boost Software License - Version 1.0 - August 17th, 2003

Copyright (c) 2025 MuriloChianfa

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
Loading
Loading