phit

Contributing

Development

This page is for working on phit itself: building from a checkout, running the tests, and changing the implementation. To use phit in an application, see Installation. Run the commands below from the repository root, which holds Cargo.toml, crates/ and examples/.

Environment

Following the official ext-php-rs installation guide and Hello World build workflow:

# macOS: Xcode command-line tools, Rust, PHP (includes headers), libclang
xcode-select --install
brew install rust php llvm
export PHP="$(brew --prefix php)/bin/php"
export PHP_CONFIG="$(brew --prefix php)/bin/php-config"
export PATH="$(brew --prefix php)/bin:$PATH"
# If bindgen cannot find libclang:
export LIBCLANG_PATH="$(brew --prefix llvm)/lib"

Linux (Debian or Ubuntu with PHP 8.3, 8.4 or 8.5 in the configured repositories):

sudo apt-get update
sudo apt-get install build-essential clang libclang-dev php-cli php-dev curl
# Install stable Rust using https://rustup.rs, then source ~/.cargo/env.
export PHP="$(command -v php)"
export PHP_CONFIG="$(command -v php-config)"

Check php -v, php-config --version, and rustc --version. PHP and php-config must belong to the same installation (including architecture, ZTS/NTS and debug mode). scripts/check and scripts/check-fpm take PHP from php-config --php-binary unless PHP is set, so a different php earlier on PATH, such as Herd’s, does not matter to them. Commands you run by hand still need the matching php. The macOS linker configuration permits symbols resolved by PHP when it loads the extension; Linux needs no macOS linker flags.

When using Homebrew LLVM’s libclang on macOS, select the matching compiler and builtin header directory as well. Mixing Xcode’s older arm_neon.h with a newer libclang can fail before any statistical tests run:

export LIBCLANG_PATH="$(brew --prefix llvm)/lib"
export CC="$(brew --prefix llvm)/bin/clang"
export CXX="$(brew --prefix llvm)/bin/clang++"
export BINDGEN_EXTRA_CLANG_ARGS="-resource-dir=$($CC -print-resource-dir) -isysroot=$(xcrun --show-sdk-path)"

The CI matrix disables fail-fast so a macOS failure does not cancel Linux evidence.

Build and run

Run from the repository root with the environment above:

cargo test --workspace --locked
cargo build -p phit --locked
# macOS:
export STATS_EXTENSION="$PWD/target/debug/libphit.dylib"
# Linux instead:
# export STATS_EXTENSION="$PWD/target/debug/libphit.so"
php -n -d "extension=$STATS_EXTENSION" -m
php -n -d "extension=$STATS_EXTENSION" -r 'echo phpversion("phit"), PHP_EOL;'
php -n -d "extension=$STATS_EXTENSION" tests/ols.php
php -n -d "extension=$STATS_EXTENSION" tests/logistic.php
php -n -d "extension=$STATS_EXTENSION" tests/probit.php
php -n -d "extension=$STATS_EXTENSION" tests/poisson.php
php -n -d "extension=$STATS_EXTENSION" tests/lmm.php
php -n -d "extension=$STATS_EXTENSION" tests/r-compatibility.php
php -n -d "extension=$STATS_EXTENSION" tests/lmm-expanded.php
php -n -d "extension=$STATS_EXTENSION" tests/logistic-mixed.php
php -n -d "extension=$STATS_EXTENSION" examples/ols.php
php -n -d "extension=$STATS_EXTENSION" examples/logistic.php
php -n -d "extension=$STATS_EXTENSION" examples/probit.php
php -n -d "extension=$STATS_EXTENSION" examples/poisson.php
php -n -d "extension=$STATS_EXTENSION" examples/lmm.php
php -n -d "extension=$STATS_EXTENSION" examples/lmm-slopes.php
php -n -d "extension=$STATS_EXTENSION" examples/logistic-mixed.php

For persistent installation, the official optional cargo-php workflow is cargo install cargo-php --locked, then from crates/php-extension, cargo php install --release. Back up php.ini first. Alternatively build with cargo build -p phit --release --locked and add extension=/absolute/path/to/target/release/libphit.dylib (Linux: .so) to the CLI php.ini shown by php --ini. Then php examples/ols.php runs directly. Rebuild for each PHP version; -n skips ini files for isolated development tests.

Installation covers PIE and the prebuilt binaries. Deploying to servers covers containers and PHP-FPM, and Releasing covers release artifacts.

Numerical dependency policy

The engine uses nalgebra’s Rust-native SVD and QR. There is one maintained numerical path, with no BLAS/LAPACK dependency or backend selection. We defer external numerical backends until a concrete requirement justifies the additional build and deployment work. The earlier Accelerate trial is retained as historical benchmark evidence; its Cargo feature and foreign-call wrapper have been removed.

The posterior-draw kernels also use nalgebra Cholesky, triangular solves and pivoted LU. Their numerical notes document covariance/rank tolerances and independent fixtures; tests/draws.php is included in scripts/check.

Changing the implementation

Keep statistical calculations in crates/stats-core and PHP conversion in crates/php-extension. Work in small increments with established numerical crates. Check APIs against the dependency versions in Cargo.lock. Explain important mathematical steps near the code, and update contracts and reference tests when behavior changes. Do not add abstractions for hypothetical models.

Follow verification.qmd for the checks appropriate to a change. The tested baseline is Apple Silicon, Rust 1.99 and Homebrew PHP 8.5.10. CI runs the checks on macOS and Ubuntu, and the packaging workflow tests Linux x86_64 and aarch64 at PHP 8.3, 8.4 and 8.5.

The general mixed fitter also uses pinned Rust-native bobyqa and argmin (Nelder–Mead). These add no C/Fortran, BLAS or LAPACK runtime requirements. See R compatibility for reference regeneration and the remaining native optimizer backend work.

./scripts/check runs the complete offline gate: Rust tests and clippy, every PHP suite and example, the stub and signature checks, the reference manifest, and the site export. Run it before pushing.

From phpstats/phit@76b22f0.