A hands-on walk through every feature xphp adds on top of PHP. Each page leads with runnable source, shows the slice of generated PHP that matters, lists the rules, and links to a test fixture you can run yourself.
If you're brand new, start with getting started first.
| Page | What it covers |
|---|---|
| Classes and interfaces | class Box<T> {}, generic interfaces and traits, marker-interface runtime behavior |
| Methods and functions | Generic methods (static + instance), generic free functions, bare top-level |
| Closures and arrows | function<T>(...), fn<T>(...) => ..., captures incl. by-ref |
| Type bounds | T : Stringable, T : A & B, T : (A & B) | C, F-bounded T : Box<T> |
| Variance | +T, -T, position rules, subtype edges between specializations |
| Defaults | T = int, forward refs Pair<A, B = A>, empty turbofish $f::<>() |
| Pseudo-types | self<T> / static<T> / parent<T> and the new self::<T>(...) form |
| Turbofish | All four call-site shapes plus variable and empty turbofish |
| Array sugar | T[] shorthand |
// Generic class
class Box<T> {
public function __construct(public T $item) {}
}
$intBox = new Box::<int>(42);
// Generic interface / trait
interface Container<T> { public function get(): T; }
trait HasItem<T> { public T $item; }
// Generic method (static or instance)
class Util {
public static function id<T>(T $x): T { return $x; }
}
$x = Util::id::<int>(7);
// Generic free function
function pair<A, B>(A $a, B $b): array { return [$a, $b]; }
$p = pair::<int, string>(1, 'a');
// Generic closure
$pick = function<T>(T $x, T $y, bool $first): T { return $first ? $x : $y; };
$pick::<int>(1, 2, true);
// Generic arrow function
$id = fn<T>(T $x): T => $x;
$id::<string>('hi');
// Type bounds
class Sortable<T : Comparable<T>> {} // F-bounded
class Pair<K : Stringable & Countable, V> {}
// Variance
class Producer<+T> { public function get(): T; } // covariant
class Consumer<-T> { public function set(T $x): void; } // contravariant
// Default type params
class Cache<K = string, V = mixed> {}
new Cache; // pads to <string, mixed>
new Cache::<>; // same
new Cache::<int, User>; // explicit
// Pseudo-types
class Container<T> {
public function with(T $n): self<T> { return new self::<T>($n); }
}
return new self::<T>($x); // constructor turbofishFor the runtime side -- how marker interfaces work, how specialized classes are named, why reflection sees the real types -- see runtime semantics.