Install xphp into a Composer project, set up the PSR-4 autoload so the generated classes resolve, write your first generic, and run it. Should take under ten minutes.
- PHP 8.4+.
- Composer.
- A project with a working
composer.json(or runcomposer initfirst).
composer require --dev xphp-lang/xphpThis puts the compiler at vendor/bin/xphp and pulls in the runtime
dependencies (nikic/php-parser, symfony/console).
xphp emits two kinds of files: rewritten user code (your .xphp with
the generic call sites resolved, now valid .php) and specialized
classes (each unique Box<int> / Box<string> / ... becomes its
own real class under the XPHP\Generated\ namespace).
Both are picked up by Composer's standard PSR-4 autoloader once you
register them. Add to your project's composer.json:
{
"autoload": {
"psr-4": {
// Specialized classes live in their own namespace
// mirroring your template's namespace.
"XPHP\\Generated\\": ".xphp-cache/Generated/",
"App\\": [
// Path where your normal php code lives.
"src",
// Path where xphp drops the rewritten .php files.
// Same App\ namespace; PSR-4 finds the class in either.
"dist"
]
}
}
}Then rebuild the autoloader:
composer dump-autoloadReplace
App\\with your project's actual top-level namespace.
// src/Container.xphp
<?php
declare(strict_types=1);
namespace App;
class Container<T> {
public function __construct(public T $item) {}
public function get(): T {
return $this->item;
}
}// src/Use.xphp
<?php
declare(strict_types=1);
namespace App;
$intContainer = new Container::<int>(42);
$strContainer = new Container::<string>('hello');
echo $intContainer->get(), PHP_EOL;
echo $strContainer->get(), PHP_EOL;vendor/bin/xphp compile src dist .xphp-cache| Argument | Required | Default | Purpose |
|---|---|---|---|
<source> |
yes | -- | Directory of .xphp files (PSR-4 layout). |
<target> |
no | dist |
Where rewritten .php files land — your user code with generic args resolved. |
<cache> |
no | .xphp-cache |
Where specialized classes live. |
After the compile completes you'll have:
dist/Container.php— the template stripped down to a marker interface.dist/Use.php— your script withnew Container::<int>(42)rewritten to point at the specialized class..xphp-cache/Generated/App/Container/T_<hash>.php— one specialized class file per unique instantiation.
Both dist/ and .xphp-cache/ can be gitignored — they're
generated artifacts your CI/CD pipeline rebuilds on every deploy.
Any normal PHP runtime that loads Composer's autoload will pick up the generated classes via PSR-4. For example:
// public/index.php (or wherever your entry point lives)
<?php
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../dist/Use.php';Expected output:
42
hello
The compiler:
- Read the
.xphpfiles and parsed the generic syntax. - Found two distinct instantiations:
Container<int>andContainer<string>. - Emitted two specialized classes under
.xphp-cache/Generated/App/Container/T_<hash>.php, each withintorstringbaked into every signature. - Rewrote
src/Use.xphpintodist/Use.php, replacingnew Container::<int>(...)with a reference to the right specialized class.
PSR-4 routes the autoload, so neither you nor any downstream library ever has to know the generated class names.
- Syntax tour — every shipped feature, one page each.
- Caveats — what's NOT supported and what to do instead.
- Errors — verbatim catalog of every compile-time error message.
- How it works — the compile pipeline, end to end.
- Roadmap — what's coming next.