Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

$finder = PhpCsFixer\Finder::create()
->exclude([
'tests/data'
'tests/data',
'playground',
])
->in(__DIR__);

Expand Down
36 changes: 29 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,38 @@ You can customize in your `phpstan.neon`:
```neon
parameters:
friendly:
# default is 3
lineBefore: 3
lineAfter: 3
# default is null
lineBefore: 3 # Number of lines to display before error line (default: 2)
lineAfter: 3 # Number of lines to display after error line (default: 2)
editorUrl: 'phpstorm://open?file=%%file%%&line=%%line%%' # Editor URL (default: null)
```

### Editor URL Configuration

The `editorUrl` option allows you to create clickable links in terminal output that open files directly in your editor.

**Available placeholders:**
- `%%file%%` - Absolute file path
- `%%relFile%%` - Relative file path (useful for Docker/container environments)
- `%%line%%` - Line number

**Editor examples:**
```neon
parameters:
friendly:
# PhpStorm / IntelliJ IDEA
editorUrl: 'phpstorm://open?file=%%file%%&line=%%line%%'

# VSCode (with absolute path)
editorUrl: 'vscode://file/%%file%%:%%line%%'

# VSCode (with relative path - for Docker environments, requires base path)
editorUrl: 'vscode://file//your/local/project/path/%%relFile%%:%%line%%'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The double slash // after vscode://file: can be confusing. While it might be technically valid in some URI parsers, a single slash is more conventional for absolute file paths and less likely to cause confusion. The format vscode://file/path/to/file is what's typically expected and is easier to read.

Suggested change
editorUrl: 'vscode://file//your/local/project/path/%%relFile%%:%%line%%'
editorUrl: 'vscode://file/your/local/project/path/%%relFile%%:%%line%%'


# Sublime Text
editorUrl: 'subl://open?url=file://%%file%%&line=%%line%%'
```

- `lineBefore` ... Number of lines to display before error line
- `lineAfter` ... Number of lines to display after error line
- `editorUrl` ... URL with placeholders like [table formatter config](URL for editor like table formatter)
> **Note:** When running PHPStan in Docker or other virtualized environments, use `%%relFile%%` instead of `%%file%%` to get the relative path. For VSCode, you may need to prepend your local project path since VSCode requires absolute paths.


## 🖼️ Example
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"analyze": "phpstan analyze -c phpstan.neon.dist --error-format friendly",
"analyze-raw": "phpstan analyze -c phpstan.neon.dist --error-format raw",
"analyze-table": "phpstan analyze -c phpstan.neon.dist --error-format table",
"playground": "phpstan analyze playground -c playground/phpstan.neon --error-format friendly",
"cs-fix": "php-cs-fixer fix",
"cs-fix-dry": "php-cs-fixer fix --dry-run"
}
Expand Down
4 changes: 4 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ parameters:
friendly:
lineBefore: 2
lineAfter: 2
# editorUrl examples:
# - PhpStorm: 'phpstorm://open?file=%%relFile%%&line=%%line%%'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This example for PhpStorm uses %%relFile%%, which is typically for containerized environments. However, the main README.md example for PhpStorm uses %%file%% for the standard local setup. To avoid confusion, it would be better to use %%file%% here to represent the most common local development scenario.

        # - PhpStorm: 'phpstorm://open?file=%%file%%&line=%%line%%'

# - VSCode (local): 'vscode://file//path/to/project/%%relFile%%:%%line%%'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment (local) is misleading for this VSCode example. This configuration, which uses %%relFile%% and requires prepending a local project path, is characteristic of running PHPStan in a container (like Docker) while editing files locally. A true 'local' setup would typically use %%file%%. I suggest changing (local) to something like (Docker/Remote) to more accurately describe the use case. Also, the double slash // can be simplified to a single / for clarity.

        # - VSCode (Docker/Remote): 'vscode://file/path/to/project/%%relFile%%:%%line%%'

editorUrl: null
52 changes: 52 additions & 0 deletions playground/dead_code.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

/**
* Dead code and unreachable code errors for PHPStan testing
*/

// 1. Unreachable code after return
function unreachableAfterReturn(): string
{
return 'hello';
echo 'This is unreachable'; // Error: unreachable
}

// 2. Unreachable code after throw
function unreachableAfterThrow(): void
{
throw new RuntimeException('Error');
echo 'Never executed'; // Error: unreachable
}

// 3. Always true/false condition
function alwaysTrueCondition(): void
{
$value = 'string';
if (is_string($value)) { // Error: always true
echo 'Always executes';
}
}

// 4. Impossible type check
function impossibleCheck(int $value): void
{
if (is_string($value)) { // Error: impossible, $value is int
echo 'Never happens';
}
}

// 5. Unused private method
class ClassWithDeadCode
{
public function publicMethod(): void
{
echo 'public';
}

private function unusedPrivateMethod(): void // Error: unused
{
echo 'never called';
}
}
58 changes: 58 additions & 0 deletions playground/logic_errors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

/**
* Logic and comparison errors for PHPStan testing
*/

// 1. Strict comparison with different types
function strictComparison(): bool
{
return 'string' === 123; // Error: comparing different types
}

// 2. Division by zero
function divisionByZero(): float
{
$divisor = 0;
return 100 / $divisor; // Error: division by zero
}

// 3. Array access on non-array
function arrayAccessOnString(): string
{
$value = 'hello';
return $value['key']; // Error: invalid array access
}

// 4. Instanceof with final class that doesn't match
final class FinalClass
{
}

class OtherClass
{
}

function impossibleInstanceof(OtherClass $obj): bool
{
return $obj instanceof FinalClass; // Error: impossible
}

// 5. Wrong number of arguments
function expectsThreeArgs(int $a, int $b, int $c): int
{
return $a + $b + $c;
}

$result = expectsThreeArgs(1, 2); // Error: missing argument

// 6. Duplicate array key
function duplicateKeys(): array
{
return [
'key' => 'first',
'key' => 'second', // Error: duplicate key
];
}
39 changes: 39 additions & 0 deletions playground/missing_types.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

/**
* Missing type declarations for PHPStan testing
*/

// 1. Missing return type
function noReturnType() // Error at higher levels: missing return type
{
return 'hello';
}

// 2. Missing parameter type
function noParamType($value): string // Error at higher levels: missing param type
{
return (string) $value;
}

// 3. Missing property type
class MissingTypes
{
public $untyped; // Error at higher levels: missing property type

private $alsoUntyped = 'default'; // Error at higher levels

public function process($input) // Error: missing types
{
$this->untyped = $input;
return $input;
}
}

// 4. Mixed type usage
function usesMixed(mixed $value): mixed
{
return $value->someMethod(); // Error: calling method on mixed
}
16 changes: 16 additions & 0 deletions playground/phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# PHPStan configuration for playground
# Usage: cd playground && ../vendor/bin/phpstan analyze

includes:
- ../extension.neon

parameters:
level: 9
paths:
- .

# Friendly formatter settings
friendly:
lineBefore: 3
lineAfter: 3
editorUrl: 'vscode://file/%%relFile%%:%%line%%'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The editorUrl for VSCode is configured with %%relFile%%. The vscode://file/ protocol handler generally requires an absolute file path to work correctly. When running composer playground, PHPStan is executed from the project root, and %%relFile%% will resolve to a path like playground/type_errors.php. The resulting URL vscode://file/playground/type_errors.php is a relative path and will likely fail to open the file in VSCode. To ensure the links work during testing, you should use %%file%% to get the absolute path, as documented in the README for local setups.

        editorUrl: 'vscode://file/%%file%%:%%line%%'

56 changes: 56 additions & 0 deletions playground/type_errors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

/**
* Type-related errors for PHPStan testing
*/

// 1. Wrong return type
function getString(): string
{
return 123; // Error: should return string
}

// 2. Wrong argument type
function expectsInt(int $value): void
{
echo $value;
}

expectsInt('not an int'); // Error: wrong argument type

// 3. Nullable type not handled
function processString(string $value): int
{
return strlen($value);
}

function mayReturnNull(): ?string
{
return rand(0, 1) ? 'hello' : null;
}

processString(mayReturnNull()); // Error: might be null

// 4. Array type mismatch
/** @param array<int, string> $items */
function processStringArray(array $items): void
{
foreach ($items as $item) {
echo $item;
}
}

processStringArray([1, 2, 3]); // Error: array of int, not string

// 5. Property type mismatch
class TypedClass
{
public string $name;

public function __construct()
{
$this->name = 42; // Error: wrong type
}
}
42 changes: 42 additions & 0 deletions playground/undefined_errors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

/**
* Undefined variable/method/class errors for PHPStan testing
*/

// 1. Undefined variable
function useUndefinedVar(): void
{
echo $undefinedVariable; // Error: undefined variable
}

// 2. Undefined method
class SomeClass
{
public function existingMethod(): void
{
}
}

$obj = new SomeClass();
$obj->nonExistentMethod(); // Error: undefined method

// 3. Undefined class
$instance = new NonExistentClass(); // Error: undefined class

// 4. Undefined constant
echo UNDEFINED_CONSTANT; // Error: undefined constant

// 5. Undefined property
class AnotherClass
{
public string $definedProperty = 'hello';
}

$another = new AnotherClass();
echo $another->undefinedProperty; // Error: undefined property

// 6. Undefined function
undefinedFunction(); // Error: undefined function