Guidance for AI coding agents contributing to Tensor — a scientific-computing extension for PHP. The repository houses a single PHP extension: an object-oriented API written in Zephir and backed by hand-written C (OpenBLAS / LAPACKE), built from tensor/ (Zephir source), ext/, and config.json.
| Path | Purpose |
|---|---|
tensor/ |
Zephir source. Tensor interface plus Vector, Matrix, ColumnVector; the sub-interfaces ArrayLike, Arithmetic, Comparable, Unary, Trigonometric, Reductions, Special; Decompositions/ (Cholesky, Eigen, LU, SVD), Reductions/ (REF, RREF), Exceptions/, and settings.zep. Note Reductions is both an interface in Tensor and a namespace holding REF/RREF. Compiled into C by composer compile. |
docs/ |
Project documentation. |
tests/ |
PHPUnit test suite. One *Test.php per class. |
benchmarks/ |
phpbench suites, organized per functional area. |
optimizers/ |
Zephir function-call optimizers, one Tensor*Optimizer.php per operation. |
ext/ |
Generated Zephir C code + hand-written C under ext/include/*.c. Do not hand-edit generated files. |
config.json |
Zephir build config (namespace, version, extra-libs, optimization & warning flags). |
build-ext |
PHP script that patches ext/config.m4 before compile (Alpine/musl + backtrace_symbols execinfo handling). |
- PHP 8.1+ (CI matrix is 8.1 → 8.5).
composer.jsondeclares>=8.1. - Dev tooling is installed as Composer dev dependencies (php-cs-fixer, phpunit, phpbench, Zephir).
- Compiling the extension additionally needs a C compiler, GFortran,
phpize, OpenBLAS dev headers, LAPACKE, and re2c (see README for per-OS install commands).
Two things block composer compile with a clean shell — set both before compiling:
# 1. OpenBLAS/LAPACKE/gfortran are keg-only (not symlinked into /opt/homebrew),
# so export the brew prefixes — otherwise: 'cblas.h' file not found.
export LDFLAGS="-L$(brew --prefix openblas)/lib -L$(brew --prefix lapack)/lib -L$(brew --prefix pcre2)/lib -L$(brew --prefix gcc)/lib/gcc/current"
export CPPFLAGS="-I$(brew --prefix openblas)/include -I$(brew --prefix lapack)/include -I$(brew --prefix pcre2)/include -I$(brew --prefix gcc)/include"
export PKG_CONFIG_PATH="$(brew --prefix openblas)/lib/pkgconfig:$(brew --prefix lapack)/lib/pkgconfig:$(brew --prefix pcre2)/lib/pkgconfig:$(brew --prefix gcc)/lib/pkgconfig"
export PATH="$(brew --prefix gcc)/bin:$PATH"; FC="$(brew --prefix gcc)/bin/gfortran"
# 2. Zephir's PCH fails on the macOS `gcc` shim ("C23 was disabled in precompiled
# file") — aborts every .lo. Disable it (only loses a speed-up).
export ZEPHIR_NO_PCH=1
composer compileNote: Zephir's re-configure fingerprint ignores CPPFLAGS/LDFLAGS — if you change them on an existing tree, delete .zephir/1.5.0-$Id$/build-fingerprint first to force a fresh configure.
To verify, raise the memory limit (some tests alloc large buffers) and load the built .so:
php -n -d extension=$PWD/ext/modules/tensor.so -d memory_limit=-1 -d extension=iconv vendor/bin/phpunitAll are Composer scripts (see composer.json):
| Task | Command |
|---|---|
| Install deps | composer install |
| Validate manifest | composer validate |
| Run tests | composer test (PHPUnit, test suite Base; requires the extension to be loaded) |
| Check style | composer check (php-cs-fixer, dry-run; sets PHP_CS_FIXER_IGNORE_ENV=1) |
| Fix style | composer fix |
| Full build | composer build = validate → install → analyze → test → check |
| Benchmarks | composer benchmark (requires the extension to be loaded) |
| Compile extension | composer compile = zephir generate → zephir compile |
| Clean generated extension | composer clean (zephir fullclean) |
Recommended loop before submitting a change:
composer install
composer test
composer fix(composer build runs all of the above in one shot, plus composer validate.)
- Coding style is governed by
.php-cs-fixer.dist.php(extends@PSR2). Highlights: single quotes, short array syntax, compact nullable type hints, pre-increment, ordered class elements, trimmed/multi-line phpdoc,echooverprint. Rather than memorize the rule set, runcomposer fix. - Testing guidance (from
CONTRIBUTING.md):- New functionality ships with a matching unit test in
tests/. - Bug fixes ship with a passing test that would have reproduced the bug beforehand.
- Tests target public methods and cover edge cases / invalid input.
- New functionality ships with a matching unit test in
- Documentation: update docs if behavior changes.
- PHPDoc: classes use
@category/@package/@authorblocks; methods carry param and return annotations. Use@var list<float>for element arrays. - Exceptions are typed under
Tensor\Exceptions(e.g.InvalidArgumentException,DimensionalityMismatch,RuntimeException). Use the existing ones rather thanException. - Math is float-only. Values stored/computed as
float; don't introduce integer-only branches. When adding a new operation, mirror it across theTensorsub-interfaces (Arithmetic,Comparable,Unary,Trigonometric,Reductions,Special). - Optimizations should be accompanied by a before and after benchmark to measure and prove the performance gain.
- No inline comments or excessive commenting in general. Use expressive syntax and naming.
Every public method a new tensor/ class adds typically routes into the C backing it. When you add or change an operation at the API level:
- Update the Zephir class in
tensor/. - Add/adjust the matching
optimizers/Tensor<Op>Optimizer.phpif it is a callable that the extension should route into C. - Ensure the underlying C implementation exists under
ext/include/*.cand is linked (already wired inconfig.jsonextra-sources). - Bump the version in both
config.jsonif this is a released change, and record it inCHANGELOG.md.
Do not hand-edit the generated C in ext/ (files like *.dep, *.lo, *.o, Makefile*, config.h). They are produced by composer compile. Hand-written logic belongs in ext/include/*.c.
An installed Tensor extension will override any new changes. To test the locally compiled extension, load the built shared object (see the macOS compile section above):
php -n -d extension=$PWD/ext/modules/tensor.so -d memory_limit=-1 -d extension=iconv vendor/bin/phpunitIf a system-installed tensor extension is already enabled, you can rely on it instead of building locally.
- Run
composer fixrather than trying to normalize formatting by hand — the rule set is broad and idiosyncratic. phpunit.xmlruns a singleBasetest suite fromtests/; add new files there.- The
build-extscript is part ofcomposer compileand patchesext/config.m4idempotently; it is safe to re-run. - This is a numerical library: when in doubt about precision, match the existing
MAX_DELTA-style tolerance approach used intests. - The
optimizers/namespace isZephir\Optimizers\FunctionCall(PSR-4 incomposer.json), notTensor.