PHPackages                             spintax/core - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. spintax/core

ActiveLibrary

spintax/core
============

Framework-agnostic spintax engine: enumerations, permutations, variables, conditionals, plural agreement, includes and a post-processing pipeline. Zero dependencies.

v0.6.0(1w ago)028MITPHPPHP ^8.0CI passing

Since Jul 13Pushed 1w agoCompare

[ Source](https://github.com/investblog/spintax-php)[ Packagist](https://packagist.org/packages/spintax/core)[ Docs](https://spintax.net)[ RSS](/packages/spintax-core/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (3)Versions (11)Used By (0)

spintax/core
============

[](#spintaxcore)

[![CI](https://github.com/investblog/spintax-php/actions/workflows/ci.yml/badge.svg)](https://github.com/investblog/spintax-php/actions/workflows/ci.yml)[![Packagist](https://camo.githubusercontent.com/ee7f1a6ca282d450e546da1fb1c6e36af5abff4ccf2134cc4c34cd437e127495/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7370696e7461782f636f7265)](https://packagist.org/packages/spintax/core)[![License: MIT](https://camo.githubusercontent.com/8bb50fd2278f18fc326bf71f6e88ca8f884f72f179d3e555e20ed30157190d0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e2e737667)](LICENSE)

A framework-agnostic **spintax engine** for PHP. Zero dependencies, PHP 8.0+.

This is the engine behind the [Spintax WordPress plugin](https://wordpress.org/plugins/spintax/) and [`@spintax/core`](https://www.npmjs.com/package/@spintax/core) on npm — extracted, so it can be used from any PHP application.

```
use Spintax\Core\Render\Pipeline;

echo (new Pipeline())->render(
    'Hola %name%, {tenemos|traemos} [ ofertas|novedades|precios] para ti. ' .
    '{plural 3: producto|productos}.',
    ['name' => 'Ana'],
    locale: 'es_ES',
);

// Every render is a different variant of the same message:
//   Hola Ana, tenemos ofertas, novedades, precios para ti. Productos.
//   Hola Ana, traemos novedades, precios, ofertas para ti. Productos.
```

Why another one
---------------

[](#why-another-one)

Most PHP spintax libraries parse `{a|b|c}` and stop there. That is a *replacement* primitive, and it is enough only until your copy needs to agree with itself:

`{a|b|c}``[…]` permutations`%vars%`conditionalsplural agreement`#include`prose clean-uptypical spintax libs✅——————**`spintax/core`**✅✅✅✅✅✅✅Plural agreement is the one people discover late. `%n% {товар|товара|товаров}` picks a form at random — which is wrong for 1, wrong for 3, and wrong for 5. Russian, Ukrainian and Serbian content cannot be generated correctly without a number-gated form; this engine has one.

Three-form locales are `ru`, `uk`, `be`, `sr`, `hr`, `bs`. Everything else takes the EN-style 2-form rule — including `pl`, `cs`, `sk`, `sl` and `bg`, whose real rules differ and which are therefore **not** yet supported: they are accepted silently and bucketed wrongly, not rejected.

Install
-------

[](#install)

```
composer require spintax/core
```

Syntax
------

[](#syntax)

constructmeaning`{a|b|c}`**enumeration** — pick one. Nests: `{a|{b|c}}``[a|b|c]`**permutation** — pick N, shuffle, join`[ a|b|c]`configured permutation`%var%`variable reference (case-insensitive)`#set %var% = value`local variable, **macro**: the value is substituted at every reference and its brackets re-roll each time`#def %var% = value`local variable, **roll-once**: the value is rendered a single time and every reference sees that same text`{?VAR?then|else}`conditional — `{?!VAR?…}` inverts it`{plural : one|few|many}`plural agreement by grammatical bucket (RU/UK/BE + SR/HR/BS 3-form, EN-style 2-form)`#include "name"`embed another template`/# … #/`comment, stripped from the outputFull reference with a live playground: **[spintax.net/docs/syntax](https://spintax.net/docs/syntax)**.

Includes
--------

[](#includes)

Fetching a template is I/O, so it belongs to your application. Everything *around* the fetch — recursion, cycle detection, depth and fan-out budgets, and scope isolation — stays in the engine, so a naive host cannot hang itself:

```
$pipeline = new Pipeline(
    globals: ['brand' => 'Acme'],
    source: fn (string $name): ?string => $repository->rawTemplate($name),
);

echo $pipeline->render("Intro.\n#include \"footer\"");
```

A nested template inherits globals and runtime variables, but never the parent's `#set` locals — it defines its own. A circular include resolves to nothing rather than looping.

Validation
----------

[](#validation)

```
use Spintax\Core\Engine\Validator;

$result = (new Validator())->validate($template);
// $result['errors'] — line/column diagnostics: unbalanced brackets, bad plural arity, unknown includes…
```

Deterministic output
--------------------

[](#deterministic-output)

Inject the RNG and a render becomes reproducible — which is what makes the test suite below possible:

```
use Spintax\Core\Engine\Parser;

$always_first = new Parser(fn (int $min, int $max): int => $min);
$pipeline     = new Pipeline($always_first);
```

The parity contract
-------------------

[](#the-parity-contract)

The same spintax syntax is implemented three times: here, in [`@spintax/core`](https://github.com/investblog/spintax-js) (TypeScript), and in the [WordPress plugin](https://github.com/investblog/spintax). They are held together by a **shared golden corpus** — one set of JSON fixtures that every engine must reproduce.

This package's test suite *is* that corpus, run against the shipped `Pipeline`. Not a replica of it, not a subset: the same file the TypeScript engine is tested against. If a fixture goes red here, the engines have diverged, and that is the point.

```
git clone https://github.com/investblog/spintax-js ../spintax-js
SPINTAX_FIXTURES=../spintax-js/packages/conformance/fixtures vendor/bin/phpunit
```

Deliberately not here
---------------------

[](#deliberately-not-here)

Caching, template storage, settings, and **output sanitisation**. `render()` returns pre-sanitise text; a host emitting HTML must run its own sanitiser over it (the WordPress plugin applies `wp_kses_post()`). Those are host concerns, and an engine that guesses at them is an engine you have to fight.

Also available
--------------

[](#also-available)

- **JavaScript / TypeScript:** [`@spintax/core`](https://www.npmjs.com/package/@spintax/core).
- **Python:** [`spintax-core`](https://pypi.org/project/spintax-core/).
- **Object Pascal:** [spintax-win](https://github.com/investblog/spintax-win) — Free Pascal, MIT, zero deps.
- **WordPress:** [Spintax](https://wordpress.org/plugins/spintax/) — templates, ACF / post-meta bindings, WooCommerce product context, WP-CLI.
- **Docs &amp; playground:** [spintax.net](https://spintax.net).

License
-------

[](#license)

MIT © [301st](https://301.st)

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance98

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity35

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

9

Last Release

13d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/28910481?v=4)[Adam Magee](/maintainers/Spintax)[@spintax](https://github.com/spintax)

---

Top Contributors

[![Divisor2](https://avatars.githubusercontent.com/u/95831114?v=4)](https://github.com/Divisor2 "Divisor2 (33 commits)")

---

Tags

phpspintaxpluralseotemplate engineContent Generationspintaxtext-spinning

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/spintax-core/health.svg)

```
[![Health](https://phpackages.com/badges/spintax-core/health.svg)](https://phpackages.com/packages/spintax-core)
```

###  Alternatives

[doctrine/inflector

PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.

11.4k933.5M969](/packages/doctrine-inflector)[jbroadway/urlify

A fast PHP slug generator and transliteration library that converts non-ascii characters for use in URLs.

6948.7M84](/packages/jbroadway-urlify)[nette/application

🏆 Nette Application: a full-stack component-based MVC kernel for PHP that helps you write powerful and modern web applications. Write less, have cleaner code and your work will bring you joy.

46016.2M1.2k](/packages/nette-application)[gettext/languages

gettext languages with plural rules

7934.3M12](/packages/gettext-languages)[bjoernffm/spintax

Spintax {parsing|processing} library.

1441.5k](/packages/bjoernffm-spintax)[chilldev/spintax

Spintax {parsing|processing} library.

162.4k](/packages/chilldev-spintax)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
