Troubleshooting & FAQ
This page addresses common questions, debugging techniques, and edge cases you might encounter when using TypePHP in local development, test suites, or production environments.
Configuration & Execution
Why is my file or method not being type-checked?
If TypePHP is not enforcing contracts on a specific file or method, check the following common causes:
- Path Exclusion Specificity: Check your
typephp.phpconfiguration. If your file matches anexcludepattern (such asvendor/**orstorage/**), TypePHP skips AST transformation. Remember that equal length patterns favorexclude. - Ignore Annotations: Check if the file header contains
@typephp-ignore-fileor if the method docblock contains@typephp-ignore. - Disabled Inline Variable Toggles: If an inline variable (
/** @var positive-int $x */) is not throwing an error, verify that the corresponding toggle insideinline_varsintypephp.phpis set totrue. - Stale Cache: If you recently edited docblocks or configuration settings, your pre-transformed file might be cached on disk. Run
vendor/bin/typephp cache:clear.
How do I know if TypePHP is actively transforming a file?
You can verify that a file is being intercepted and transformed in two ways:
- Intentionally Trigger an Error: Pass an invalid argument (such as a negative integer to a
positive-intparameter). If aTypePHP\Exception\TypeErroris thrown, TypePHP is active. - Inspect the Cache Directory: Look inside your configured
cache_dir(if undefined, this defaults to your system temporary directory:sys_get_temp_dir() . '/typephp-cache/'). You will see transformed PHP files containing injectedRuntimeTypeCheckercalls.
Why are files inside my custom cache_dir not being intercepted?
If you configured a custom cache_dir inside your project directory (e.g., __DIR__ . '/storage/typephp') and set your include paths to ['**'], you might wonder why the cache files aren't being transformed.
This is a built-in safety mechanism. TypePHP automatically detects your cache_dir and unconditionally excludes it from its internal StreamWrapper and FileFilter. This prevents catastrophic infinite loops and double-parsing overhead that would occur if TypePHP tried to intercept and transform its own cached files.
How do I clear the AST cache?
You can wipe the cache using the CLI runner:
vendor/bin/typephp cache:clearIf you are changing configuration settings frequently during local development, you can temporarily disable disk caching in typephp.php:
'cache' => false, // Transforms files purely in RAM (php://memory)Type Enforcement & Edge Cases
Why didn't TypePHP catch a bad property assignment from an external file?
TypePHP injects guard rails at the call site where assignments happen.
- Whitelisted Caller File: If
Controller.php(whitelisted) sets$user->id = -5, TypePHP intercepts the assignment and throws aTypeError. - Excluded Caller File: If
LegacyVendor.php(excluded) sets$user->id = -5, TypePHP does not modifyLegacyVendor.php, so the assignment runs natively.
Solution: In PHP 8.4, use Property Hooks (set => $this->_id = $value). Property hooks run inside the class itself, guaranteeing that assignments are validated regardless of where the call originated.
Why is my @method annotation with quoted literals like 'active'|'pending' not being enforced?
phpdoc-parser's grammar engine for @method parameter lists can encounter ambiguity when parsing unparenthesized single or double quotes directly inside parameter type signatures (such as @method bool updateStatus('active'|'pending' $status)). When phpdoc-parser encounters this grammar ambiguity, it drops that specific @method tag during DocBlock parsing.
Solution: Use a local @phpstan-type alias to define the union string literal or shape, and reference the alias in your @method annotation:
/**
* Best Practice: Clean & Grammar-Safe via @phpstan-type
*
* @phpstan-type StatusUnion 'active'|'pending'
*
* @method bool updateStatus(StatusUnion $status)
*/
class OrderService
{
public function __call(string $name, array $arguments) { ... }
}Why is my Pest or PHPUnit test suite running slower with JIT enabled?
During CLI test execution, a single short-lived PHP process runs your tests.
If you pass -d opcache.enable_cli=1 with PHP 8 JIT enabled, PHP spends extra CPU cycles compiling JIT tracing buffers that are discarded the moment the test suite finishes a second later.
Solution: Run CLI test runs with standard PHP execution (without opcache.enable_cli=1 or JIT enabled). TypePHP executes 380+ complex type checks in sub-second time without JIT. Save JIT optimization for long-running production web servers (PHP-FPM, FrankenPHP, Swoole).
Why does a generic container allow any item if no annotation is provided?
If you instantiate a generic class without an inline @var annotation:
$collection = new Collection(); // Unannotated generic instanceTypePHP uses First-Use Type Inference. It allows the first method call (such as $collection->add(new User())) to establish the template type T = User. Once established, all subsequent calls on that instance enforce T = User.
If you want strict enforcement before any items are added, prebind the instance using an inline @var annotation:
/** @var Collection<User> $collection */
$collection = new Collection();Frameworks & Tooling
Does TypePHP work with Laravel, Symfony, or WordPress?
Yes. TypePHP boots automatically as soon as Composer's autoloader (vendor/autoload.php) is required.
It works seamlessly with standard framework entry points like public/index.php, Laravel's artisan, or Symfony's bin/console. No special framework bundles or service providers are required.
How do I temporarily disable TypePHP in an emergency?
You have two options for turning off TypePHP instantly:
- Environment Level (Full Prevention): Set
TYPEPHP_DISABLE=truein your.envor server environment. This prevents TypePHP from registering its stream wrapper during Composer autoloading. - Config Level (Pass-Through Mode): Set
'enabled' => falseintypephp.phpor callTypePHP::setConfig(['enabled' => false]). TypePHP will run, but all checks turn into instant no-ops.
Can I run TypePHP alongside static analysis tools?
Yes, it is highly recommended.
- Static Analyzers (PHPStan, Psalm, Mago): Analyze your source code at compile-time, linting docblock syntax and checking static logic in your IDE.
- TypePHP: Enforces those same PHPDoc contracts at runtime during dynamic execution, protecting your application against invalid database records, un-sanitized API payloads, and unexpected runtime state.