Tip
output.phpVersion in tyhp.json chooses the emitted PHP and the 80N prefix on compiled tyhp/* helpers. Each tyhp/* package’s X.Y is independent. tyhpdef/* packages are not prefixed.
Tier 1 · Story 04Complete
Compiled Tyhp programs depend on small Composer packages (tyhpdef/php, tyhp/core, and others). Those packages do not share the compiler version. You can ship a new tyhp/core without bumping the Tyhp compiler.
The compiler version 805.1.0-beta.1 only means this toolchain can emit PHP up through 8.5 (and still emit 8.2–8.4 when output.phpVersion says so).
Each package has its own version (in that package’s composer.json).
Compiled tyhp/* helpers (core, async, decimal, lambda) publish Packagist artifacts as 80N.X.Y where 80N is the PHP that artifact is for (802 = 8.2, 803 = 8.3, 804 = 8.4, 805 = 8.5).
tyhpdef/* packages (tyhpdef/php, tyhpdef/php-ext-*, Composer-lib wrappers) publish that composer.json version as the Packagist tag. They do not use 80N.X.Y.
This alpha’s compiled helpers start at source 0.0 / 0.1 / 0.2, so the Packagist pins look like 804.0.2 for PHP 8.4 tyhp/core. If tyhp/core later becomes 1.4, PHP 8.4 apps require 804.1.4 (or 804.1.*). tyhp/lambda can be 2.0 at the same time. tyhpdef/php currently publishes as 0.0.1; put it in require-dev as @dev rather than pinning that tag.
tyhp/* helpers)output.phpVersion |
Package MAJOR | Pin when the package is 0.0 |
Composer php on that artifact |
|---|---|---|---|
"8.2" |
802 |
802.0.0 |
~8.2.0 |
"8.3" |
803 |
803.0.0 |
~8.3.0 |
"8.4" |
804 |
804.0.0 |
~8.4.0 |
"8.5" |
805 |
805.0.0 |
~8.5.0 |
tyhp init writes @dev for tyhp/core in require and for tyhpdef/php in require-dev (and --vendor adds the same constraint for missing runtime tyhp/* packages, and @dev in require-dev for missing tyhpdef/php). tyhpdef/* packages are compile-time stubs — prefer require-dev with @dev. Compiled tyhp/* helpers belong in require because emitted PHP loads them at runtime. tyhp build (when not using a compiler-checkout path repository, and when build.updateComposer writes pins) uses the pin for each compiled tyhp/* package from that package’s own X.Y plus your output.phpVersion. They do not all have to be the same version.
An app targets one PHP version. Pin compiled tyhp/* helpers in require at that MAJOR and that package’s X.Y. Put tyhpdef/* in require-dev as @dev:
{
"require": {
"php": "~8.4.0",
"tyhp/core": "804.0.0"
},
"require-dev": {
"tyhpdef/php": "@dev"
}
}
If tyhp/decimal is on 1.2 and you use decimals, require tyhp/decimal: 804.1.2 (or 804.1.*). tyhp build adds those packages when the compiled code needs them.
804.0.* is fine for an app if you want patch updates on that package line without changing PHP MAJOR.
Ambient tyhpdefs for the require tree (extra.tyhp.require) land on root require-dev. Allow tyhp/core under config.allow-plugins. The first composer require tyhp/core often misses that transaction — run composer update or tyhp composer sync. See Ambient tyhpdefs and CLI: Composer.
A library that supports several PHP versions should OR together the PHP majors it supports and keep that package’s X (not the compiler MINOR, and not some other package’s X):
{
"require": {
"php": ">=8.3",
"tyhp/core": "803.0.* || 804.0.* || 805.0.*"
},
"require-dev": {
"tyhpdef/php": "@dev"
}
}
If tyhp/core is on 1.y and tyhp/lambda is on 2.y:
"tyhp/core": "803.1.* || 804.1.* || 805.1.*",
"tyhp/lambda": "803.2.* || 804.2.* || 805.2.*"
Composer then installs the artifact that matches the consumer's PHP (~8.3.0 vs ~8.4.0 vs ~8.5.0).
Do not mix X values for the same package (803.0.* || 804.1.* for core). 803.1 and 804.1 are the same core line on different PHP; 804.0 is an older core line.
Published Tyhp libraries also declare ambient tyhpdefs in extra.tyhp.require (see below). Do not put author-only tyhpdefs there.
| Package | Role |
|---|---|
tyhpdef/php |
Always-present PHP 8.2+ builtins (Core, date, filter, hash, json, libxml, pcre, random, Reflection, SPL, standard) |
tyhpdef/php-ext-* |
Optional / disableable / PECL extensions (see below) |
tyhp/core |
Generics, typed variables, property accessors, scalar extension methods |
tyhp/async |
Promise, event loop, cancellation |
tyhp/decimal |
Arbitrary-precision decimal (\Tyhp\Decimal, bcmath-backed) |
tyhp/lambda |
Expression-tree / PropertyPath runtime |
Runtime helpers (tyhp/core, tyhp/decimal, tyhp/async, tyhp/lambda) are published with four PHP-target tags (802.…805. plus that package’s X.Y).
tyhpdef/php and tyhpdef/php-ext-* are one Composer package each for PHP 8.2–8.5. Their composer.json requires "php": ">=8.2", and Packagist uses that file’s version as the tag. There are no tyhpdef/php-8.2 / tyhpdef/php-8.x-ext-* forks. APIs that exist only on some minors are marked with declare(php=…) or #[\Tyhp\Php]; the compiler keeps only the declarations that match your output.phpVersion. See PHP Version Gating.
tyhpdef/php (always present)Install tyhpdef/php in require-dev as @dev so \strlen, DateTime, SPL, json, hash, and libxml type-check. json, hash, and libxml stay in this package — there is no tyhpdef/php-ext-json, tyhpdef/php-ext-hash, or tyhpdef/php-ext-libxml.
tyhp/core (runtime helpers + scalar methods)tyhp/core ships the scalar method catalog ($s->length(), $arr->mapped(...)) via _tyhpdef/extensions/ and global use extension \Tyhp\StringExtensions (and the matching Array / Int / Float / Bool / Closure files). See Scalar Pseudo-Objects.
tyhpdef/php-ext-* (optional extensions)Add a tyhpdef/php-ext-* package in require-dev (also @dev) when your project uses that PHP extension. Each package’s composer.json requires ext-<name> in require, and lists tyhpdef/php in both require-dev and extra.tyhp.require. Driver packages list parent wrappers the same way — for example tyhpdef/php-ext-pdo_mysql lists tyhpdef/php-ext-pdo and tyhpdef/php-ext-mysqlnd.
tyhpdef/php-ext-decimal is the PECL Decimal extension (php-decimal / mpdecimal). It is unrelated to tyhp/decimal (\Tyhp\Decimal\, bcmath-backed).
Shipped packages (Composer name tyhpdef/php-ext-<id>):
apcu, bcmath, bz2, calendar, csv, ctype, curl, dba, decimal, dom, exif, ffi, fileinfo, ftp, gd, gettext, gmp, iconv, imagick, intl, ldap, lexbor, mbstring, memcached, mysqli, mysqlnd, odbc, opcache, openssl, pcntl, pdo, pdo_dblib, pdo_mysql, pdo_odbc, pdo_pgsql, pdo_sqlite, pgsql, phar, posix, readline, redis, session, shmop, simplexml, snmp, soap, sockets, sodium, sqlite3, sysvmsg, sysvsem, sysvshm, tidy, tokenizer, uri, xml, xmlreader, xmlwriter, xsl, zip, zlib.
You do not install a PHP version matrix to type-check these packages. Set output.phpVersion to "8.2", "8.3", "8.4", or "8.5" and lint/build; gated symbols appear or disappear for that target.
extra.tyhp.require)A Tyhp library can type-check against tyhpdef packages that its consumers must not install. List everything this package needs to compile in require-dev. Copy the subset that consumers need into extra.tyhp.require. Duplicate those ambient pins in require-dev so authors lock and install them.
Any Composer package name is allowed — not only tyhpdef/*. Values are ordinary Composer constraints (including @dev for path repositories).
{
"require": {
"php": ">=8.2",
"tyhp/core": "@dev"
},
"require-dev": {
"tyhpdef/php": "@dev",
"tyhpdef/php-ext-bcmath": "@dev",
"tyhpdef/php-ext-gmp": "@dev",
"tyhpdef/php-ext-decimal": "@dev"
},
"extra": {
"tyhp": {
"interopContractVersion": 1,
"require": {
"tyhpdef/php": "@dev"
}
}
}
}
That shape is tyhp/decimal: authors compile optional backends against the bcmath / gmp / php-decimal wrappers, but apps that only call \Tyhp\Decimal install tyhpdef/php. They do not pick up those three wrappers. tyhp/core keeps tyhpdef/php-ext-mbstring in require-dev only so it can compile \Tyhp\StringHelper; consumers do not need that package to type-check $s->length().
| Bucket | Where | Published package.tyhpdef |
|---|---|---|
| Runtime PHP | require |
Spell the FQN. Do not copy. Do not emit extern. |
| Ambient for consumers | extra.tyhp.require (also list in require-dev) |
Spell the FQN. Consumers install that tyhpdef. |
| Author-only | require-dev only |
Name-only extern plus // @provided-by: <owning package>. |
Omitted / {} extras |
— | No ambient tyhpdefs from this package. |
Never put author-only tyhpdefs in extras. A public type must not extends / implements an author-only name (TYHP3026 / TYHP3027) — keep those owners ambient (decimal lists tyhpdef/php so \JsonSerializable stays a real type). Mark author-only classes internal so they drop out of the public tyhpdef; see The internal Visibility Modifier.
tyhp/compiler is not listed in extras on tyhp/* packages. Application roots still pin it on require-dev (tyhp init does; tyhp composer sync adds it when tyhp/core is in the graph). The tyhp/compiler package itself does not require itself.
tyhp/coretyhp/core is a Composer plugin (type: composer-plugin) that walks extra.tyhp.require and runtime require edges, merges constraints onto the root require-dev, and adds tyhp/compiler unless the root package is already tyhp/compiler. Allow it:
"config": {
"allow-plugins": {
"tyhp/core": true
}
}
tyhp init writes that key. tyhp composer, tyhp composer sync, and tyhp build --fix also write it when missing and do not overwrite an explicit false. A raw composer command still needs the key already on disk. A --no-dev install does not rewrite composer.json.
The first composer require tyhp/core often misses ambient tyhpdefs in that same transaction. Run composer update or tyhp composer sync. If extras are missing, tyhp build explains (TYHP7701) and tyhp composer sync / tyhp build --fix recovers. Ordinary tyhp build never auto-updates Composer. Details: CLI: Composer.
If tyhp build can see runtime/packages/ next to the compiler, it pins each package’s source version (for example 0.2 or 0.0.1) and adds Composer path repositories. That is only for developing against a compiler tree. Packagist consumers of compiled tyhp/* helpers use the 80N.X.Y form. Packagist consumers of tyhpdef/* typically use @dev in require-dev; the published tag is the source version (currently 0.0.1 for tyhpdef/php).
output.phpVersion in tyhp.json chooses the emitted PHP and the 80N prefix on compiled tyhp/* helpers. Each tyhp/* package’s X.Y is independent. tyhpdef/* packages are not prefixed.