Complete reference of all major Phan issue types, organized by category, with examples and how to fix each one.
Total Issue Types: 600+
- Syntax Errors
- Undefined Elements
- Type Mismatches
- Type Inference
- Access Control
- Object/Class Issues
- Deprecated
- Generic/Template Issues
- Plugin-Specific
Code has invalid PHP syntax.
Example:
function foo( { // Missing parameter name
echo "test";
}Fix: Correct the syntax error. Phan shows the exact location and nature of the error.
Function declared as returning void but contains a return with a value.
Example:
/**
* @return void
*/
function logMessage($msg) {
return "logged"; // Error: void functions can't return values
}Fix: Either remove the return value or change the return type.
/**
* @return void
*/
function logMessage($msg) {
echo $msg; // OK: no return value
}Function declared with never return type but has a return statement.
Example:
/**
* @return never
*/
function throwError($msg): never {
return; // Error: never functions must not return
}Fix: Remove the return or throw an exception.
/**
* @return never
*/
function throwError($msg): never {
throw new Exception($msg); // OK: never functions throw or exit
}break or continue used outside a loop.
Example:
if ($x > 5) {
break; // Error: not in a loop
}Fix: Only use break/continue inside for, while, foreach, or switch.
break or continue targets more levels than available.
Example:
for ($i = 0; $i < 3; $i++) {
break 3; // Error: only 1 level of loop exists
}Fix: Reduce the level number.
for ($i = 0; $i < 3; $i++) {
break; // OK: default is 1
}A constant expression contains invalid syntax.
Example:
const MAX = $someVariable; // Error: constants can't reference variablesFix: Use only literal values in constant expressions.
const MAX = 100; // OK: literal valueA property with hooks is declared with a default value.
Example (PHP 8.4):
public string $name = "default" { // Error: hooked properties can't have defaults
get { return $this->name; }
}Fix: Remove the default value from hooked properties.
public string $name {
get { return $this->name; }
}A class reference doesn't correspond to any declared class.
Example:
$user = new UnknownClass(); // Error: class doesn't existFix: Declare the class or import it.
use App\Models\User;
$user = new User(); // OK: class is declaredA function call references an undefined function.
Example:
echo my_special_function(); // Error: function not definedFix: Define the function or import it from a library.
function my_special_function() {
return "result";
}
echo my_special_function(); // OKA method call references a method that doesn't exist.
Example:
class User {
public function getName() { return "John"; }
}
$user = new User();
echo $user->getEmail(); // Error: getEmail() doesn't existFix: Implement the method or call an existing one.
class User {
public function getName() { return "John"; }
public function getEmail() { return "john@example.com"; }
}
$user = new User();
echo $user->getEmail(); // OKA property is accessed that isn't declared in the class.
Example:
class User {
public $name;
}
$user = new User();
echo $user->email; // Error: $email not declaredFix: Declare the property or use @property annotation.
class User {
public $name;
public $email;
}
$user = new User();
echo $user->email; // OKOr for dynamic properties:
/**
* @property string $email
*/
class User {
public $name;
}
$user = new User();
echo $user->email; // OK: documented via @propertyA variable is used that was never assigned.
Example:
if (some_condition()) {
$result = fetchData();
}
echo $result; // Error: $result might not be definedFix: Initialize the variable before use.
$result = null; // Initialize
if (some_condition()) {
$result = fetchData();
}
echo $result; // OKA variable might not be defined in some code paths.
Example:
if ($x > 5) {
$message = "large";
}
echo $message; // Warning: might not be defined if $x <= 5Fix: Ensure the variable is initialized in all paths.
$message = "small"; // Default value
if ($x > 5) {
$message = "large";
}
echo $message; // OK: always definedA static method is called on a class that doesn't have that method.
Example:
class Config {
public static function get($key) {
return $_ENV[$key] ?? null;
}
}
echo Config::getAll(); // Error: no getAll() methodFix: Implement the method or call an existing one.
class Config {
public static function get($key) {
return $_ENV[$key] ?? null;
}
public static function getAll() {
return $_ENV;
}
}
echo count(Config::getAll()); // OKA static property is accessed that doesn't exist.
Example:
class Settings {
public static $debug = false;
}
echo Settings::$timeout; // Error: $timeout not declaredFix: Declare the property.
class Settings {
public static $debug = false;
public static $timeout = 30;
}
echo Settings::$timeout; // OKA global constant is used that doesn't exist.
Example:
echo MY_CONSTANT; // Error: constant not definedFix: Define the constant or import it.
define('MY_CONSTANT', 'value');
echo MY_CONSTANT; // OKOr via class constant:
class Config {
const MY_CONSTANT = 'value';
}
echo Config::MY_CONSTANT; // OKA class has abstract methods but isn't declared abstract.
Example:
class Handler {
abstract public function process($data); // Error: class must be abstract
}Fix: Either declare the class as abstract or implement the method.
abstract class Handler {
abstract public function process($data); // OK: class is abstract
}
class ConcreteHandler extends Handler {
public function process($data) {
// Implementation
}
}A function argument has the wrong type.
Example:
/**
* @param int $count
*/
function setLimit($count) {
// ...
}
setLimit("50"); // Error: string passed, int expectedFix: Pass the correct type.
setLimit(50); // OK: int passed
// Or convert the string
setLimit((int) "50"); // OK: explicit castA function returns the wrong type.
Example:
/**
* @return int
*/
function getCount() {
return "0"; // Error: returns string, int expected
}Fix: Return the correct type.
/**
* @return int
*/
function getCount() {
return 0; // OK: returns int
}A property is assigned a value of the wrong type.
Example:
class User {
/** @var string */
public $name;
}
$user = new User();
$user->name = 123; // Error: int assigned to string propertyFix: Assign the correct type.
$user->name = "John"; // OK: string assignedAn array element is assigned the wrong type.
Example:
/** @var array<int, string> */
$names = [];
$names[0] = 123; // Error: int assigned to string arrayFix: Assign the correct type.
$names[0] = "John"; // OK: string assignedAn argument might be null, but the parameter doesn't accept null.
Example:
/**
* @param string $name
*/
function greet($name) {
echo "Hello, $name";
}
$user_name = getUserName(); // Returns string|null
greet($user_name); // Error: might be nullFix: Check for null or change the parameter type.
// Option 1: Check for null
if ($user_name !== null) {
greet($user_name);
}
// Option 2: Accept null
/**
* @param string|null $name
*/
function greet($name) {
echo "Hello, " . ($name ?? "Guest");
}
greet($user_name); // OKAn argument might be false, but the parameter doesn't accept it.
Example:
/**
* @param string $data
*/
function process($data) {
echo strlen($data);
}
$result = file_get_contents("file.txt"); // Returns string|false
process($result); // Error: might be falseFix: Check for false.
$result = file_get_contents("file.txt");
if ($result !== false) {
process($result); // OK
}A property has a default value of the wrong type.
Example:
class Config {
/** @var int */
public $timeout = "30"; // Error: string default for int property
}Fix: Use the correct type for the default.
class Config {
/** @var int */
public $timeout = 30; // OK: int default
}A property might be null, but code treats it as non-null.
Example:
class User {
/** @var string|null */
public $email = null;
}
function sendEmail(User $user) {
mail($user->email, "Subject", "Body"); // Error: email might be null
}Fix: Check for null.
function sendEmail(User $user) {
if ($user->email !== null) {
mail($user->email, "Subject", "Body"); // OK
}
}Array offset is of wrong type.
Example:
/** @var array<int, string> */
$items = ["a", "b"];
$value = $items["key"]; // Error: string key on int-keyed arrayFix: Use the correct key type.
$value = $items[0]; // OK: int keyIteration over non-traversable type.
Example:
/**
* @param string $value
*/
function process($value) {
foreach ($value as $char) { // Error: can't iterate string
echo $char;
}
}Fix: Use a traversable type or convert.
/**
* @param array $items
*/
function process($items) {
foreach ($items as $item) { // OK: iterating array
echo $item;
}
}Array operator applied to non-array.
Example:
function getData() {
return "not an array";
}
$data = getData();
echo $data[0]; // Error: can't index string (usually)Fix: Ensure the type is an array.
function getData() {
return ["item1", "item2"];
}
$data = getData();
echo $data[0]; // OK: string is indexable, warning still possibleAttempting to instantiate an abstract class.
Example:
abstract class Vehicle {
abstract public function start();
}
$vehicle = new Vehicle(); // Error: can't instantiate abstract classFix: Use a concrete subclass.
class Car extends Vehicle {
public function start() {
echo "Engine started";
}
}
$vehicle = new Car(); // OK: concrete classAttempting to instantiate an interface.
Example:
interface Logger {
public function log($message);
}
$logger = new Logger(); // Error: can't instantiate interfaceFix: Implement the interface and instantiate the concrete class.
class ConsoleLogger implements Logger {
public function log($message) {
echo $message;
}
}
$logger = new ConsoleLogger(); // OKCalling __construct() directly rather than using parent::__construct() or new.
Example:
class Base {
public function __construct($value) {
$this->value = $value;
}
}
class Child extends Base {
public function __construct($value) {
$this->__construct($value); // Error: recursive call
}
}Fix: Call parent's constructor properly.
class Child extends Base {
public function __construct($value) {
parent::__construct($value); // OK: calls parent
}
}A private method is called from outside the class.
Example:
class User {
private function hashPassword($pwd) {
return md5($pwd);
}
}
$user = new User();
$user->hashPassword("secret"); // Error: private methodFix: Make it public/protected or call from within the class.
class User {
public function setPassword($pwd) {
$this->password = $this->hashPassword($pwd);
}
private function hashPassword($pwd) {
return md5($pwd);
}
}
$user = new User();
$user->setPassword("secret"); // OKA private property is accessed from outside the class.
Example:
class User {
private $ssn;
}
$user = new User();
echo $user->ssn; // Error: private propertyFix: Use a public accessor method.
class User {
private $ssn;
public function getSsn() {
return $this->ssn;
}
}
$user = new User();
echo $user->getSsn(); // OKA protected method is called outside a related class.
Example:
class Parent {
protected function internalMethod() { }
}
class Unrelated {
public function test() {
$p = new Parent();
$p->internalMethod(); // Error: not a subclass
}
}Fix: Only call protected methods from the class or subclasses.
class Parent {
protected function internalMethod() { }
}
class Child extends Parent {
public function test() {
$this->internalMethod(); // OK: subclass
}
}Using instanceof with non-class type.
Example:
if ($value instanceof string) { // Error: string is not a class
echo $value;
}Fix: Check with a class or use a type check function.
if (is_string($value)) { // OK: type check function
echo $value;
}Calling a method on something that isn't a class instance.
Example:
$value = "string";
$value->method(); // Error: strings don't have methodsFix: Ensure you're calling methods on objects.
$user = new User();
$user->getName(); // OK: calling method on objectThrowing something that doesn't extend Throwable.
Example:
class NotAnException {
// ...
}
throw new NotAnException(); // Error: doesn't extend ThrowableFix: Extend Exception or Throwable.
class CustomException extends Exception {
// ...
}
throw new CustomException(); // OKA readonly property has a set hook.
Example:
class User {
public readonly string $id {
set { $this->id = $value; } // Error: readonly can't have set hook
}
}Fix: Remove the readonly keyword if you want a set hook.
class User {
public string $id {
get { return $this->id; }
set { $this->id = $value; } // OK: not readonly
}
}A deprecated function is called.
Example:
/**
* @deprecated Use newFunction() instead
*/
function oldFunction() {
// ...
}
oldFunction(); // Warning: function is deprecatedFix: Use the recommended replacement.
newFunction(); // OK: use current functionA deprecated method is called.
Example:
class User {
/**
* @deprecated Use getFullName() instead
*/
public function getName() {
return $this->name;
}
public function getFullName() {
return $this->firstName . " " . $this->lastName;
}
}
$user = new User();
echo $user->getName(); // Warning: method is deprecatedFix: Call the replacement method.
echo $user->getFullName(); // OKA deprecated class constant is referenced.
Example:
class Config {
/**
* @deprecated Use PHP_VERSION_ID instead
*/
const PHP_VERSION = "8.0";
}
echo Config::PHP_VERSION; // Warning: constant is deprecatedFix: Use the recommended constant.
echo PHP_VERSION_ID; // OKA template type violates its constraints.
Example:
/**
* @template T of string|int
* @param T $value
*/
function process($value) {
// ...
}
process([]); // Error: array doesn't satisfy T constraintFix: Pass a value that satisfies the constraint.
process("value"); // OK: string satisfies constraint
process(42); // OK: int satisfies constraintA return type violates template type constraints.
Example:
/**
* @template T
* @param T $value
* @return T
*/
function identity($value) {
return 42; // Error: return type doesn't match T
}Fix: Return the correct type.
/**
* @template T
* @param T $value
* @return T
*/
function identity($value) {
return $value; // OK: returns T
}A public method is never called.
Example:
class Helper {
public function unusedMethod() { // Warning: never called
return "result";
}
}Fix: Either remove the method or call it somewhere.
class Helper {
public function usedMethod() {
return "result";
}
}
echo Helper::usedMethod(); // OK: method is usedA variable is assigned but never used.
Example:
$value = getData(); // Warning: value never used
echo "done";Fix: Use the variable or remove the assignment.
$value = getData();
echo $value; // OK: variable is usedSuppress a specific issue in code:
/**
* @suppress PhanTypeMismatchArgument
*/
function legacyFunction() {
myFunction("string"); // This error is suppressed
}Suppress multiple issues in a file:
<?php
/**
* @phan-suppress-all PhanUndeclaredFunction, PhanUndeclaredClass
*/
myFunction(); // Suppressed
new UnknownClass(); // SuppressedConfigure issue handling in config.php:
return [
'suppress_issue_types' => [
'PhanUnreferencedPublicMethod',
'PhanUnusedVariable',
],
];Track acceptable issues over time:
# Create baseline
phan --save-baseline .phan/.baseline.php
# Run against baseline (only NEW issues reported)
phan --load-baseline .phan/.baseline.phpTo get comprehensive details on any issue:
-
In Phan output: The error message includes the issue type in square brackets
file.php:10 [PhanTypeMismatchArgument] Argument 1 -
Search the codebase: Look at
src/Phan/Issue.phpfor all issue type names -
Check the NEWS: See recent issues added in
NEWS.md -
Use help:
phan --help | grep -i issue
- [[Annotating-Your-Source-Code-V6]] - How to add type hints to avoid issues
- [[Generic-Types-V6]] - Using templates and generics
- [[Using-Phan-From-Command-Line]] - CLI options for configuring issue reporting
- [[Home]] - Main documentation index