2016-04-27 00:18:49 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace CodeInspector;
|
|
|
|
|
|
|
|
use PhpParser;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A class for analysing a given method call's effects in relation to
|
|
|
|
* $this/self and also looking at return types
|
|
|
|
*/
|
|
|
|
class EffectsAnalyser
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Gets the return types from a list of statements
|
|
|
|
*
|
|
|
|
* @param array<PhpParser\Node\Stmt> $stmts
|
2016-06-16 02:16:40 +02:00
|
|
|
* @return array<AtomicType> a list of return types
|
2016-04-27 00:18:49 +02:00
|
|
|
*/
|
2016-05-09 14:56:07 +02:00
|
|
|
public static function getReturnTypes(array $stmts, $collapse_types = false)
|
2016-04-27 00:18:49 +02:00
|
|
|
{
|
|
|
|
$return_types = [];
|
|
|
|
|
2016-06-20 07:05:44 +02:00
|
|
|
$last_stmt = null;
|
|
|
|
|
2016-05-09 14:56:07 +02:00
|
|
|
foreach ($stmts as $stmt) {
|
2016-06-20 07:05:44 +02:00
|
|
|
if (!$stmt instanceof PhpParser\Node\Stmt\Nop) {
|
|
|
|
$last_stmt = $stmt;
|
|
|
|
}
|
|
|
|
|
2016-04-27 00:18:49 +02:00
|
|
|
if ($stmt instanceof PhpParser\Node\Stmt\Return_) {
|
2016-06-20 18:38:31 +02:00
|
|
|
if (isset($stmt->inferredType)) {
|
2016-06-17 22:05:28 +02:00
|
|
|
$return_types = array_merge(array_values($stmt->inferredType->types), $return_types);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$return_types[] = Type::getMixed(false);
|
|
|
|
}
|
2016-04-27 00:18:49 +02:00
|
|
|
|
|
|
|
} elseif ($stmt instanceof PhpParser\Node\Stmt\If_) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($stmt->stmts));
|
|
|
|
|
|
|
|
foreach ($stmt->elseifs as $elseif) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($elseif->stmts));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($stmt->else) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($stmt->else->stmts));
|
|
|
|
}
|
|
|
|
|
|
|
|
} elseif ($stmt instanceof PhpParser\Node\Stmt\TryCatch) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($stmt->stmts));
|
|
|
|
|
|
|
|
foreach ($stmt->catches as $catch) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($catch->stmts));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($stmt->finallyStmts) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($stmt->finallyStmts));
|
|
|
|
}
|
|
|
|
|
|
|
|
} elseif ($stmt instanceof PhpParser\Node\Stmt\For_) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($stmt->stmts));
|
|
|
|
|
|
|
|
} elseif ($stmt instanceof PhpParser\Node\Stmt\Foreach_) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($stmt->stmts));
|
|
|
|
|
|
|
|
} elseif ($stmt instanceof PhpParser\Node\Stmt\While_) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($stmt->stmts));
|
|
|
|
|
|
|
|
} elseif ($stmt instanceof PhpParser\Node\Stmt\Do_) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($stmt->stmts));
|
|
|
|
|
|
|
|
} elseif ($stmt instanceof PhpParser\Node\Stmt\Switch_) {
|
|
|
|
foreach ($stmt->cases as $case) {
|
|
|
|
$return_types = array_merge($return_types, self::getReturnTypes($case->stmts));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-20 07:05:44 +02:00
|
|
|
// if we're at the top level and we're not ending in a return, make sure to add possible null
|
2016-06-20 22:18:31 +02:00
|
|
|
if ($collapse_types && !$last_stmt instanceof PhpParser\Node\Stmt\Return_ && !ScopeChecker::doesReturnOrThrow($stmts)) {
|
2016-06-20 07:05:44 +02:00
|
|
|
$return_types[] = Type::getNull(false);
|
|
|
|
}
|
|
|
|
|
2016-06-16 02:16:40 +02:00
|
|
|
return $return_types;
|
2016-04-27 00:18:49 +02:00
|
|
|
}
|
|
|
|
}
|