Hello,
With the method below:
private function getDocumentRoot(): string
{
return realpath(__DIR__ . "/web/");
}
Phan returns the following error:
PhanPossiblyFalseTypeReturn Returning realpath((__DIR__ . '/web/')) of type false|string but getDocumentRoot() is declared to return string (false is incompatible)
It is an entirely valid error: realpath()
can return string or false, while getDocumentRoot()
is declared to return a string exclusively.
My question is: how to address this issue in the state of the art?
The options I thought about are the following:
1) Specific Exception:
private function getDocumentRoot(): string
{
$result = realpath(__DIR__ . "/web/");
if (!is_string($result)) {
throw new \RuntimeException("Cannot compute document root");
}
return $result;
}
While this makes the Phan error dissappear, it just changes the way to catch the exception from \TypeError
to \RuntimeException
. Somehow it feels non natural to remap the \TypeError
to something else, this leads to the next option:
2) Throwing \TypeError manually:
private function getDocumentRoot(): string
{
$result = realpath(__DIR__ . "/web/");
if (!is_string($result)) {
throw new \TypeError("Cannot compute document root");
}
return $result;
}
As option 1), it "solves" the reported issue, however it re-implements the natural behaviour of PHP itself: it really feels like boilerplate code.
3) Acknowledging that a TypeError
can occur:
/**
* @throws \TypeError
*/
private function getDocumentRoot(): string
{
return realpath(__DIR__ . "/web/");
}
While potentially no additional code involved, it could "mark" that a \TypeError
is possible and should not be reported as something not handled. However, using @throws
does not make the issue dissappear.
4) Suppressing the issue locally:
/**
* @suppress PhanPossiblyFalseTypeReturn
*/
private function getDocumentRoot(): string
{
return realpath(__DIR__ . "/web/");
}
This is supressing the issue, however, I feel that @suppress
is more like a workaround to tackle edge cases while this seems pretty common. It also feels more like silencing it over declaring an intention.
I'd like to know how you usually deal with that situation and what the best practices are.
While potentially no additional code involved, it could "mark" that a \TypeError is possible and should not be reported as something not handled. However, using @throws does not make the issue dissappear.
I don't plan to implement any support for making @throws
hide phan errors/notices - In general, in code being analyzed, it's also possible to throw a TypeError explicitly. It's also unclear what @throws Error
and @throws Throwable
would/should do since they're parent types.
Again, depending on the specifics of the situation, I've implemented 1/2/4 in the past to work around notices. To add, I'd personally change 4. to add a description such as @suppress PhanPossiblyFalseTypeReturn in abnormal situtations where PHP can't find the directory or the directory was removed, this will throw a TypeError, and there's no great way to handle this
(or bad entries in the realpath cache, or nfs wierdness)
To add, I'd personally change 4. to add a description such as
@suppress PhanPossiblyFalseTypeReturn in abnormal situtations where PHP can't find the directory or the directory was removed, this will throw a TypeError, and there's no great way to handle this
(or bad entries in the realpath cache, or nfs wierdness)
I also opted for 4), the extra comment is indeed probably worth it!
Hi!
FYI, I filled an issue on PHPStorm's tracker: https://youtrack.jetbrains.com/issue/WI-54573
It is about the lack of understanding:
/**
* @return iterable<Foo>
*/
The IDE doesn't understand that syntax: looping over the results, it doesn't understand that the inner variable's type is Foo
, same with: Generics<Foo>
.
Does anyone have a workaround in the mean time? For now it means commenting with /** @var Foo $item */
every loops, but that is duplication and I hate those markers :-(
Generator|Foo[]
syntax, which phan doesn't because it uses Generator<Foo>
- so you can use @return Generator|Foo[]
and @phan-return Generator<Foo>
, but that's another type of repetitionGenerator|Foo[]
because that syntax implies that a native array of Foo
could possibly be returned. At least I see all alternatives and didn't think about combining @return
and @phan-return
at the same time! Thanks again!
also saw an error i don't understand:
PhanTypeInvalidRightOperandOfBitwiseOp Invalid operator: right operand of & is bool (expected int|string)
the line in question is like $x & $key
, where $key
is an array key (so int|string) and is also assert
ed to be an int. any idea why phan thinks it's bool?
type[][]
if all keys are unknown, there's also more elaborate types such as list<array<string, type>>
assert(is_int($b))
before the op and it still errors.
phan ±0198a2479⚡ » php internal/dump_fallback_ast.php --php-ast-native '$x = ($a & $b === $a);'
AST_STMT_LIST [] #1
0 => AST_ASSIGN [] #1
var => AST_VAR [] #1
name => x
expr => AST_BINARY_OP [BINARY_BITWISE_AND] #1
left => AST_VAR [] #1
name => a
right => AST_BINARY_OP [BINARY_IS_IDENTICAL] #1
left => AST_VAR [] #1
name => b
right => AST_VAR [] #1
name => a
$x = ($a & $b) === $a;
phan 2.7.3 is emittingPhanEmptyFQSENInClasslike
with code like
class C {
/** @var string Fully Qualified Classname. */
const FQCN = '';
public function f() {
if (! empty(static::FQCN)) {
$fqcn = static::FQCN;
new $fqcn();
}
}
}
shouldn't it be obvious to phan that $fqcn is a non-empty string?
Currently, no - in abstract classes declaring a class constant with /** @phan-abstract */ public const X = '';
would help detect classes that didn't override the constant, but that doesn't help in your use case because traits can't have class constants.
@mixin
for a stub interface (e.g. .phan/stubs) with a abstract class constant but I don't know if that even works now and wouldn't rely on it even if it did work.There were discussions of allowing traits to have class constants or require implementing interfaces in php itself in some future php version
IteratorAggregate
as being a kind of Iterator<T>
for a specific T
? For example, in the following code, I’d like Phan to emit a deprecation error on lines 19 and 26, but it only does so on line 26.<?php
class X {
/** @deprecated */
public $x;
}
class Y implements IteratorAggregate {
/** @var list<X> */
public $xs;
/** @return Iterator<X> */
function getIterator() {
return new ArrayIterator($this->xs);
}
}
/** @param Y $y */
function f1($y) {
foreach ($y as $x) {
echo $x->x;
}
}
/** @param Y|Iterator<X> $y */
function f2($y) {
foreach ($y as $x) {
echo $x->x;
}
}
Hello! I have a question about
input:22: PhanTypeNoPropertiesForeach Class \ActiveRecord was passed to foreach, but it does not extend Traversable and doesn't have any declared properties. (This check excludes dynamic properties)
In this snippet
If I remove ActiveRecord
from union type on line 5, why Phan doesn't warn about passing null|int|string|bool
to foreach
? Why only non-traversable object instance bothers it?
As you already guessed, in real code return type of that find()
method depends on its arguments. Can I help Phan to define type in each case more precisely except of add exclusive @phan-var
for each call of find()
?
PhanTypeMismatchForeach detects the case where there are no valid types, but if some of the types are valid, it doesn't warn. I don't believe that --strict-type-checking
or similar options currently cover array|null
- there's probably hundreds of issues for which a warning about being partially invalid could start being emitted
https://github.com/phan/phan#readme
Phan is a static analyzer for PHP that prefers to minimize false-positives.
target_php_version
? Faced with overlooked one like this for PHP 5.6
. It worth to mention that PHPStorm also doesn't show it while Netbeans IDE does. And this code become valid in PHP 7+
https://github.com/phan/phan/wiki/Incrementally-Strengthening-Analysis#just-backward-compatibility
If you are still using versions of php older than 5.6, PHP53CompatibilityPlugin may be worth looking into if you are not running syntax checks for php 5.3 through another method such as InvokePHPNativeSyntaxCheckPlugin or phan --native-syntax-check php53 (see .phan/plugins/README.md).
For issues that php5.6 --syntax-check
would check, I'd recommend using php5.6 --syntax-check
in addition to Phan,, whether internally or externally - it's redundant to detect the hundreds of issue types that the syntax checks would already check for
phan --native-syntax-check /path/to/php53
assuming php5.6 is a binary in your PATH
Is Phan intended to detect PHP Parse errors corresponding its target_php_version?
It doesn't - it catches some of the parse/compile errors, in addition to incompatibilities (e.g. usingstring
would mean a class named string in php 5.6 and not be detected by php56 --syntax-check)
Also,
If you are migrating from PHP 5 to PHP 7, you should also look into using php7cc (no longer maintained) and php7mar, which have different backwards compatibility checks.