/** * Analyzes the given method call * * @param PC_Obj_Call $call the call */ private function analyze_method_call($call) { $name = $call->get_function(); $classname = $call->get_class(); $c = $this->env->get_types()->get_class($classname); if ($c !== null) { if (!$c->contains_method($name)) { // no obj-creation here because the constructor can be named '__construct' or // '<classname>'. the call uses always '__construct'. if (!$call->is_object_creation() && !$this->is_method_of_sub($c, $name)) { $this->report($call, 'The method "' . $name . '" does not exist in the class "#' . $classname . '#"!', PC_Obj_Error::E_S_METHOD_MISSING); } } else { if ($call->is_object_creation() && $c->is_abstract()) { $this->report($call, 'You can\'t instantiate the abstract class "#' . $c->get_name() . '#"!', PC_Obj_Error::E_S_ABSTRACT_CLASS_INSTANTIATION); } else { // check for static / not static $m = $c->get_method($name); if ($call->is_static() && !$m->is_static()) { $this->report($call, 'Your call "' . $this->get_call_link($call) . '" calls "' . $m->get_name() . '" statically, but the method is not static!', PC_Obj_Error::E_S_STATIC_CALL); } else { if (!$call->is_static() && $m->is_static()) { $this->report($call, 'Your call "' . $this->get_call_link($call) . '" calls "' . $m->get_name() . '" not statically, but the method is static!', PC_Obj_Error::E_S_NONSTATIC_CALL); } } $this->check_params($call, $m); } } } else { $this->report($call, 'The class "#' . $classname . '#" does not exist!', PC_Obj_Error::E_S_CLASS_MISSING); } }
/** * Adds a function-call * * @param PC_Obj_MultiType $class the class-name (or null) * @param PC_Obj_MultiType $func the function-name * @param array $args the function-arguments * @param bool $static whether its a static call * @return PC_Obj_MultiType the result */ public function add_call($class, $func, $args, $static = false) { if ($class !== null && !$class instanceof PC_Obj_MultiType) { return $this->handle_error('$class is invalid'); } if (!$func instanceof PC_Obj_MultiType) { return $this->handle_error('$func is invalid'); } if (!is_array($args)) { return $this->handle_error('$args is invalid'); } // if we don't know the function- or class-name, we can't do anything here $cname = $class !== null ? $class->get_string() : null; $fname = $func->get_string(); if ($fname === null || $class !== null && $cname === null) { return $this->create_unknown(); } // create call $call = new PC_Obj_Call($this->get_file(), $this->get_line()); // determine class- and function-name $call->set_function($fname); // do it manually here because we might not know the method $call->set_object_creation(strcasecmp($fname, '__construct') == 0 || strcasecmp($fname, $cname) == 0); if ($class !== null) { // support for php4 constructors if (strcasecmp($cname, $fname) == 0) { $call->set_object_creation(true); } else { if (strcasecmp($cname, 'parent') == 0) { // in this case its no object-creation for us because it would lead to reports like // instantiation of abstract classes when calling the constructor of an abstract // parent-class $call->set_object_creation(false); $cname = $this->scope->get_name_of(T_CLASS_C); $classobj = $this->env->get_types()->get_class($cname); if ($classobj === null || $classobj->get_super_class() == '') { return $this->create_unknown(); } $cname = $classobj->get_super_class(); // if we're in a static method, the call is always static $curfname = $this->scope->get_name_of(T_FUNC_C); $curfunc = $classobj->get_method($curfname); if ($curfunc === null || $curfunc->is_static()) { $static = true; } else { // if we're in a non-static method, its a static-call if the method we're calling is // static, and not if its not. i.e. we basically don't detect errors here $func = null; $super = $this->env->get_types()->get_class($cname); if ($super !== null) { $func = $super->get_method($fname); } $static = $func !== null && $func->is_static(); } } else { if (strcasecmp($cname, 'self') == 0) { $cname = $this->scope->get_name_of(T_CLASS_C); // self is static if its not a constructor-call $static = !$call->is_object_creation(); } } } $call->set_class($cname); } // clone because it might be a variable foreach ($args as $arg) { if (!$arg instanceof PC_Obj_MultiType) { $this->handle_error('$arg is invalid'); continue; } $call->add_argument(clone $arg); } $call->set_static($static); $this->env->get_types()->add_call($call); $funcobj = $this->env->get_types()->get_method_or_func($cname, $fname); $this->calls_analyzer->analyze($call); $this->modifiers_analyzer->analyze($this->scope, $call, $funcobj); if ($funcobj === null) { // if it's a constructor, we still know the type if ($call->is_object_creation()) { return PC_Obj_MultiType::create_object($cname); } return $this->create_unknown(); } // reference parameters implicitly create variables $i = 0; foreach ($funcobj->get_params() as $param) { if ($i >= count($args)) { break; } // this is a hack: we store the variable name for missing variable names when the variable // appears. when detecting calls, we check whether there are reference parameters and then // use the name to implicitly create the variable if ($param->is_reference() && $args[$i]->get_missing_varname() !== null) { $var = new PC_Obj_Variable($this->get_file(), $this->get_line(), $args[$i]->get_missing_varname(), clone $param->get_mtype()); $this->vars->set($this->scope->get_name(), $var); $args[$i]->set_missing_varname(null); } $i++; } $this->req_analyzer->analyze($call, $funcobj->get_version()->get_min(), $funcobj->get_version()->get_max()); // add the throws of the method to our throws foreach (array_keys($funcobj->get_throws()) as $tclass) { $this->throws_analyzer->add(PC_Obj_Method::THROW_FUNC, PC_Obj_MultiType::create_object($tclass)); } // if its a constructor we know the type directly if ($call->is_object_creation()) { return PC_Obj_MultiType::create_object($cname); } return clone $funcobj->get_return_type(); }
/** * Builds the fields to insert for the given call and project * * @param PC_Obj_Call $call the call * @param PC_Project $project the project * @return array an associative array with the fields */ private function build_fields($call, $project) { $args = serialize($call->get_arguments()); if (strlen($args) > self::MAX_ARGS_LEN) { $arglist = array(); foreach ($call->get_arguments() as $arg) { $arg->clear_values(); $arglist[] = $arg; } $args = serialize($arglist); } return array('project_id' => $project !== null ? $project->get_id() : 0, 'file' => $call->get_file(), 'line' => $call->get_line(), 'function' => $call->get_function(), 'class' => $call->get_class() === null ? null : $call->get_class(), 'static' => $call->is_static() ? 1 : 0, 'objcreation' => $call->is_object_creation() ? 1 : 0, 'arguments' => $args); }