The <?tyhp and <?tyhpdef Open Tags

Tier 0 · Story 06Complete

Two of the first differences you will notice in Tyhp are a new open tag and a new file extension. These allow the Tyhp compiler to distinguish Tyhp code from PHP code and apply its type-checking and compilation rules only to the appropriate sections. The new open tags work the same way as the existing PHP open tag <?php. In a .tyhp file, <?= is a Tyhp echo: the expression is parsed and checked as Tyhp and emitted as echo. In a .php file, <?= stays a PHP echo. A bare <? is not a PHP short open tag — Tyhp uses <? only as the start of <?tyhp, <?tyhpdef, or <?php. See Templates.

The <?tyhp Open Tag

The <?tyhp open tag marks the start of a Tyhp code block. It is the primary open tag for all Tyhp source files. Like <?php, it must be followed by at least one whitespace character. Tyhp code blocks can be closed with ?> for inline output (same as PHP), and the file can contain multiple Tyhp code blocks interleaved with inline HTML.

<?tyhp

$greeting = "Hello, Tyhp!";
echo $greeting;

When the compiler encounters <?tyhp, it enters Tyhp language mode. All code within this block is subject to Tyhp's type checking, new keywords (struct, extension, async, await, with, operator, is, typeof, nameof, variable_exists, etc.), and compile-time checks. The block ends at ?> (for inline output) or at the end of the file.

The <?tyhpdef Open Tag

The <?tyhpdef open tag marks the start of a Tyhp type definition file. Tyhpdef files describe the type signatures of external PHP code -- extensions, Composer packages, or any PHP code that your Tyhp project depends on. They provide the compiler with type information without containing any runtime logic.

<?tyhpdef

namespace App\Services;

class PaymentGateway {
    public function charge(decimal $amount, string $currency): bool;
    public function refund(string $transactionId): bool;
}

Tyhpdef files are not compiled into PHP output. They exist purely for the compiler's type-checking phase. The compiler parses them and registers their symbols (classes, functions, constants, interfaces, enums, traits, type aliases) into the global scope so that your Tyhp code can reference and type-check against them. Tyhpdef also supports the deprecated and obsolete keywords to mark declarations that should trigger compiler warnings or errors when used.

File Extensions

Tyhp uses two file extensions that correspond to the two open tags:

  • .tyhp -- Tyhp source files containing <?tyhp code blocks. These are compiled into .php output files.
  • .tyhpdef -- Tyhp type definition files containing <?tyhpdef declarations. These are consumed by the compiler for type checking but produce no output.

The compiler uses the file extension to determine how to parse the file. A .tyhp file is parsed starting with the tyhpSrcFile grammar rule, which expects <?tyhp blocks (with optional inline HTML output between blocks). A .tyhpdef file is parsed starting with the tyhpdefSrcFile grammar rule, which expects a single <?tyhpdef block.

Tagless source (source.tagless)

Set "source": { "tagless": true } in tyhp.json (or source.tagless on a package's extra.tyhp.package) to parse .tyhp / .tyhpdef files as Tyhp/tyhpdef from the first token, without an open tag. A .tyhp code file in a tagless project omits <?tyhp. A .tyhpdef file cannot contain ?>. The default remains tagged <?tyhp / <?tyhpdef files.

Templates in a tagless project

In a tagless project, a .tyhp file whose first six characters are exactly <?tyhp is parsed as a normal tagged .tyhp file. Any other .tyhp file is code and is parsed with no open tag. A blank line or any text before <?tyhp keeps that entry, so <?tyhp is not accepted there. A leading <div> or <?= is a syntax error.

A template can therefore have an empty first section (<?tyhp ?> or <?tyhp?>). After ?> the file can hold inline HTML and <?= … ?>, and <?tyhp opens another section later. Inline HTML and echoes are emitted in source order, including inside functions and control-flow blocks. Each <?= … ?> is a Tyhp echo: parsed and checked as Tyhp, emitted as echo, with no escaping. A <?php block in a .tyhp file is an error (TYHP2005). See Templates.

<?tyhp
string $title = 'Hello';
int $count = 3;
?>
<h1><?= $title ?></h1>
<p><?= $count ?></p>
<?tyhp
echo $title;
<?tyhp ?>
<h1>Hello</h1>
<?tyhp
int $count = 3;
?>
<p><?= $count ?></p>

A file that starts with anything else, including a blank line, is parsed as code with no open tag. <?tyhp in that file is not an open tag. A file that starts with <div> or <?= and has no leading <?tyhp is a syntax error. A file that starts with <?tyhp and never closes it is code. <?php in a .tyhp file is an error in every mode (TYHP2005).

Strict Types Are Always Enabled

In Tyhp, compiled PHP output always includes declare(strict_types=1);. Every .tyhp file is compiled with strict types enforced. This means PHP's type coercion rules do not apply in Tyhp code -- passing an int where a string is expected always produces a type error, never a silent conversion.

<?tyhp

// No need to write declare(strict_types=1) -- compiled output always includes it

function greet(string $name): string {
    return "Hello, " . $name;
}

// greet(42);  // ERROR: int is not assignable to string

The compiled PHP output always includes declare(strict_types=1); automatically:

<?php
declare(strict_types=1);

function greet(string $name): string {
    return "Hello, " . $name;
}

The declare(output_file='path') Directive

Tyhp extends PHP's declare() statement with a custom output_file directive. This tells the compiler to emit the code that follows the declaration into a specific output file path, overriding the default PSR-4 path computation. This is useful when you need precise control over where a file ends up in the build output.

<?tyhp

declare(output_file='public/index.php');
declare(autoload='composer');

// Everything after output_file is emitted to public/index.php
App\Application $app = new App\Application();
$app->run();

Note

output_file and autoload are Tyhp-specific declare() keys. They are stripped from the compiled PHP output — only their effects (output path and whether the emitter injects an autoloader require_once) apply during the build. Tyhp source cannot write require / include itself (that is always TYHP4801). See the Including Files page.

The declare(autoload=…) Directive

declare(autoload=…) is a per-file override for build.entryPointAutoloader. Values:

  • 'composer' — inject the configured Composer autoloader (default vendor/autoload.php under output.publishPath)
  • 'none' (or empty) — do not inject an autoloader for this file
  • a config map key or a path — look up build.entryPointAutoloader first, otherwise treat the value as a path relative to output.publishPath

The emitter writes the require_once into the PHP output. Do not put require_once 'vendor/autoload.php' in Tyhp source.

How the Compiler Distinguishes Tyhp from PHP

The lexer recognizes the tag that follows <? and sets an internal language mode accordingly. When it sees <?tyhp followed by whitespace, it emits a T_TYHP_OPEN_TAG token and enters Tyhp mode. When it sees <?tyhpdef followed by whitespace, it emits a T_TYHPDEF_OPEN_TAG token and enters Tyhpdef mode. When it sees <?php, it emits the standard T_OPEN_TAG token and enters PHP mode. In a .tyhp file, <?= stays in Tyhp mode, so is, typeof, with, and nameof work inside the echo. In a .php file, <?= is a PHP echo. A bare <? that is not followed by tyhp, tyhpdef, or php is not a PHP short open tag. This language mode determines which keywords are active -- for example, struct, async, await, is, typeof, and nameof are only recognized as keywords in Tyhp mode.

PHP Code Belongs in .php Files

A .tyhp file contains Tyhp code, plus inline HTML between <?tyhp blocks. It does not contain <?php blocks. Put PHP code in a .php file in the same project. The compiler passes .php files through unchanged, and they can call your compiled Tyhp code like any other PHP.

Compiled PHP Output

Every <?tyhp code block compiles to a <?php block with declare(strict_types=1) automatically inserted. Tyhp-specific syntax (variable type declarations, generics, structs, etc.) is transformed into valid PHP. The <?tyhp tag itself does not appear in the output.

<?tyhp

namespace App\Controllers;

class HomeController {
    public function index(): string {
        return "Welcome!";
    }
}

Compiles to:

<?php
declare(strict_types=1);

namespace App\Controllers;

class HomeController {
    public function index(): string {
        return "Welcome!";
    }
}

Best Practices

Tip

Always start Tyhp source files with <?tyhp as the open tag. This activates the full Tyhp type system and all Tyhp-specific keywords.

Tip

Use the .tyhp extension for all Tyhp source files and .tyhpdef for type definition files. The compiler relies on the extension to determine the parsing mode.

Tip

Use <?tyhpdef for describing the types of external PHP libraries. This gives the compiler the type information it needs without requiring you to modify the library code.

Tip

Use declare(output_file='path') when you need a specific output file path, such as for entry point scripts or CLI tools that must live at exact locations.

Common Mistakes

Danger

Don't put <?php blocks in a .tyhp file. Write PHP code in a .php file instead.

Danger

Don't write a bare <? as a PHP short open tag; it is not treated as PHP. In a .tyhp file, <?= is a Tyhp echo, and Tyhp statements still use <?tyhp (or tagless source). Use <?php only in .php files. In a .php file, <?= stays a PHP echo.

Danger

Don't use <?tyhp inside .tyhpdef files. Tyhpdef files must start with <?tyhpdef and contain only type declarations, not executable code.

Compiler Error Examples

If you accidentally use <?php in a .tyhp file and try to use Tyhp-specific syntax, the compiler reports parse errors because those keywords are not active in PHP mode:

<?php
// ERROR: 'struct' is not recognized as a keyword in PHP mode
// type Point = struct { int $x; int $y; };

// ERROR: Variable type declarations are not valid PHP syntax
// string $name = "hello";

Correct usage with <?tyhp:

<?tyhp

// All Tyhp keywords are active in <?tyhp blocks
type Point = struct {
    int $x;
    int $y;
};

// Variable type declarations work
string $name = "hello";