2016-01-08 00:28:27 +01:00
|
|
|
<?php
|
|
|
|
|
2016-01-08 00:36:55 +01:00
|
|
|
namespace CodeInspector;
|
2016-01-08 00:28:27 +01:00
|
|
|
|
2016-04-04 20:06:29 +02:00
|
|
|
ini_set('xdebug.max_nesting_level', 512);
|
2016-01-08 00:28:27 +01:00
|
|
|
|
2016-02-04 15:22:46 +01:00
|
|
|
use PhpParser;
|
2016-01-08 00:28:27 +01:00
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
class FunctionChecker implements StatementsSource
|
2016-01-08 00:28:27 +01:00
|
|
|
{
|
|
|
|
protected $_function;
|
|
|
|
protected $_aliased_classes = [];
|
|
|
|
protected $_namespace;
|
|
|
|
protected $_file_name;
|
2016-01-11 23:18:19 +01:00
|
|
|
protected $_is_static = false;
|
2016-01-20 00:27:06 +01:00
|
|
|
protected $_absolute_class;
|
|
|
|
protected $_statements_checker;
|
2016-01-30 00:48:09 +01:00
|
|
|
protected $_source;
|
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
protected $_function_params = [];
|
|
|
|
protected $_function_return_types = [];
|
2016-01-08 00:28:27 +01:00
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
public function __construct(PhpParser\Node\FunctionLike $function, StatementsSource $source)
|
2016-01-08 00:28:27 +01:00
|
|
|
{
|
|
|
|
$this->_function = $function;
|
2016-01-20 00:27:06 +01:00
|
|
|
$this->_aliased_classes = $source->getAliasedClasses();
|
|
|
|
$this->_namespace = $source->getNamespace();
|
|
|
|
$this->_class_name = $source->getClassName();
|
2016-04-17 17:22:18 +02:00
|
|
|
$this->_class_extends = $source->getParentClass();
|
2016-01-20 00:27:06 +01:00
|
|
|
$this->_file_name = $source->getFileName();
|
|
|
|
$this->_absolute_class = $source->getAbsoluteClass();
|
2016-01-30 00:48:09 +01:00
|
|
|
$this->_source = $source;
|
2016-01-08 00:28:27 +01:00
|
|
|
}
|
|
|
|
|
2016-05-16 05:06:03 +02:00
|
|
|
public function check(&$extra_scope_vars = [])
|
2016-01-08 00:28:27 +01:00
|
|
|
{
|
|
|
|
if ($this->_function->stmts) {
|
2016-01-20 00:27:06 +01:00
|
|
|
$vars_in_scope = $extra_scope_vars;
|
|
|
|
$vars_possibly_in_scope = $extra_scope_vars;
|
|
|
|
|
2016-05-10 20:00:44 +02:00
|
|
|
$statements_checker = new StatementsChecker($this, !empty($this->_function->params));
|
2016-04-20 19:35:59 +02:00
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
foreach ($this->_function->params as $param) {
|
|
|
|
if ($param->type) {
|
|
|
|
if (is_object($param->type)) {
|
|
|
|
if (!in_array($param->type->parts[0], ['self', 'parent'])) {
|
|
|
|
ClassChecker::checkClassName($param->type, $this->_namespace, $this->_aliased_classes, $this->_file_name);
|
2016-01-09 00:30:51 +01:00
|
|
|
}
|
|
|
|
}
|
2016-01-11 17:38:02 +01:00
|
|
|
}
|
2016-01-11 17:05:24 +01:00
|
|
|
|
2016-04-03 20:25:41 +02:00
|
|
|
$is_nullable = $param->default !== null &&
|
|
|
|
$param->default instanceof \PhpParser\Node\Expr\ConstFetch &&
|
|
|
|
$param->default->name instanceof PhpParser\Node\Name &&
|
|
|
|
$param->default->name->parts = ['null'];
|
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
if ($param->type && is_object($param->type)) {
|
|
|
|
$vars_in_scope[$param->name] =
|
|
|
|
$param->type->parts === ['self'] ?
|
|
|
|
$this->_absolute_class :
|
|
|
|
ClassChecker::getAbsoluteClassFromName($param->type, $this->_namespace, $this->_aliased_classes);
|
2016-04-03 20:25:41 +02:00
|
|
|
|
|
|
|
if ($is_nullable) {
|
|
|
|
$vars_in_scope[$param->name] .= '|null';
|
|
|
|
}
|
2016-01-11 15:27:34 +01:00
|
|
|
}
|
2016-04-30 20:14:22 +02:00
|
|
|
else {
|
|
|
|
$vars_in_scope[$param->name] = 'mixed';
|
|
|
|
}
|
|
|
|
|
|
|
|
$vars_possibly_in_scope[$param->name] = true;
|
|
|
|
$statements_checker->registerVariable($param->name, $param->getLine());
|
2016-01-11 15:27:34 +01:00
|
|
|
}
|
2016-01-08 05:40:29 +01:00
|
|
|
|
2016-04-20 19:35:59 +02:00
|
|
|
$statements_checker->check($this->_function->stmts, $vars_in_scope, $vars_possibly_in_scope);
|
2016-05-16 05:06:03 +02:00
|
|
|
|
|
|
|
$extra_scope_vars = $vars_in_scope;
|
2016-01-08 05:40:29 +01:00
|
|
|
}
|
2016-01-08 00:28:27 +01:00
|
|
|
}
|
|
|
|
|
2016-04-27 00:42:48 +02:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
2016-02-27 01:11:11 +01:00
|
|
|
public function getMethodId()
|
|
|
|
{
|
2016-05-09 14:56:07 +02:00
|
|
|
if ($this->_function instanceof PhpParser\Node\Expr\Closure) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2016-02-27 01:11:11 +01:00
|
|
|
return $this->getAbsoluteClass() . '::' . $this->_function->name;
|
|
|
|
}
|
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
public function getNamespace()
|
2016-01-08 00:28:27 +01:00
|
|
|
{
|
2016-01-20 00:27:06 +01:00
|
|
|
return $this->_namespace;
|
2016-01-08 00:28:27 +01:00
|
|
|
}
|
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
public function getAliasedClasses()
|
2016-01-08 00:28:27 +01:00
|
|
|
{
|
2016-01-20 00:27:06 +01:00
|
|
|
return $this->_aliased_classes;
|
2016-01-08 00:28:27 +01:00
|
|
|
}
|
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
public function getAbsoluteClass()
|
2016-01-08 00:28:27 +01:00
|
|
|
{
|
2016-01-20 00:27:06 +01:00
|
|
|
return $this->_absolute_class;
|
2016-01-08 00:28:27 +01:00
|
|
|
}
|
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
public function getClassName()
|
2016-01-08 00:28:27 +01:00
|
|
|
{
|
2016-01-20 00:27:06 +01:00
|
|
|
return $this->_class_name;
|
2016-01-08 00:28:27 +01:00
|
|
|
}
|
|
|
|
|
2016-01-30 00:48:09 +01:00
|
|
|
public function getClassChecker()
|
|
|
|
{
|
|
|
|
return $this->_source->getClassChecker();
|
|
|
|
}
|
|
|
|
|
2016-04-17 17:22:18 +02:00
|
|
|
public function getParentClass()
|
2016-01-08 00:28:27 +01:00
|
|
|
{
|
2016-01-20 00:27:06 +01:00
|
|
|
return $this->_class_extends;
|
2016-01-08 00:28:27 +01:00
|
|
|
}
|
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
public function getFileName()
|
2016-01-08 00:28:27 +01:00
|
|
|
{
|
2016-01-20 00:27:06 +01:00
|
|
|
return $this->_file_name;
|
2016-01-08 00:28:27 +01:00
|
|
|
}
|
|
|
|
|
2016-01-20 00:27:06 +01:00
|
|
|
public function isStatic()
|
2016-01-08 00:28:27 +01:00
|
|
|
{
|
2016-01-20 00:27:06 +01:00
|
|
|
return $this->_is_static;
|
2016-01-08 00:28:27 +01:00
|
|
|
}
|
2016-05-16 05:06:03 +02:00
|
|
|
|
|
|
|
public function getSource()
|
|
|
|
{
|
|
|
|
return $this->_source;
|
|
|
|
}
|
2016-01-08 00:28:27 +01:00
|
|
|
}
|