Example #1
0
 /**
  * Analyze the parameters and arguments for a call
  * to the given method or function
  *
  * @param CodeBase $code_base
  * @param Method $method
  * @param Node $node
  *
  * @return null
  */
 private function analyzeCallToMethod(CodeBase $code_base, Method $method, Node $node)
 {
     if (Database::isEnabled()) {
         // Store the call to the method so we can track
         // dependencies later
         (new CalledBy((string) $method->getFQSEN(), $this->context))->write(Database::get());
     }
     // Create variables for any pass-by-reference
     // parameters
     $argument_list = $node->children['args'];
     foreach ($argument_list->children as $i => $argument) {
         $parameter = $method->getParameterList()[$i] ?? null;
         if (!$parameter) {
             continue;
         }
         // If pass-by-reference, make sure the variable exists
         // or create it if it doesn't.
         if ($parameter->isPassByReference()) {
             if ($argument->kind == \ast\AST_VAR) {
                 // We don't do anything with it; just create it
                 // if it doesn't exist
                 $variable = AST::getOrCreateVariableFromNodeInContext($argument, $this->context, $this->code_base);
             } else {
                 if ($argument->kind == \ast\AST_STATIC_PROP || $argument->kind == \ast\AST_PROP) {
                     $property_name = $argument->children['prop'];
                     if (is_string($property_name)) {
                         // We don't do anything with it; just create it
                         // if it doesn't exist
                         try {
                             $property = AST::getOrCreatePropertyFromNodeInContext($argument->children['prop'], $argument, $this->context, $this->code_base);
                         } catch (CodeBaseException $exception) {
                             Log::err(Log::EUNDEF, $exception->getMessage(), $this->context->getFile(), $node->lineno);
                         } catch (NodeException $exception) {
                             // If we can't figure out what kind of a call
                             // this is, don't worry about it
                         }
                     } else {
                         // This is stuff like `Class->$foo`. I'm ignoring
                         // it.
                     }
                 }
             }
         }
     }
     // Confirm the argument types are clean
     ArgumentType::analyze($method, $node, $this->context, $this->code_base);
     // Take another pass over pass-by-reference parameters
     // and assign types to passed in variables
     foreach ($argument_list->children as $i => $argument) {
         $parameter = $method->getParameterList()[$i] ?? null;
         if (!$parameter) {
             continue;
         }
         // If the parameter is pass-by-reference and we're
         // passing a variable in, see if we should pass
         // the parameter and variable types to eachother
         $variable = null;
         if ($parameter->isPassByReference()) {
             if ($argument->kind == \ast\AST_VAR) {
                 $variable = AST::getOrCreateVariableFromNodeInContext($argument, $this->context, $this->code_base);
             } else {
                 if ($argument->kind == \ast\AST_STATIC_PROP || $argument->kind == \ast\AST_PROP) {
                     $property_name = $argument->children['prop'];
                     if (is_string($property_name)) {
                         // We don't do anything with it; just create it
                         // if it doesn't exist
                         try {
                             $variable = AST::getOrCreatePropertyFromNodeInContext($argument->children['prop'], $argument, $this->context, $this->code_base);
                         } catch (CodeBaseException $exception) {
                             Log::err(Log::EUNDEF, $exception->getMessage(), $this->context->getFile(), $node->lineno);
                         } catch (NodeException $exception) {
                             // If we can't figure out what kind of a call
                             // this is, don't worry about it
                         }
                     } else {
                         // This is stuff like `Class->$foo`. I'm ignoring
                         // it.
                     }
                 }
             }
             if ($variable) {
                 $variable->getUnionType()->addUnionType($parameter->getUnionType());
             }
         }
     }
     // If we're in quick mode, don't retest methods based on
     // parameter types passed in
     if (Config::get()->quick_mode) {
         return;
     }
     // We're going to hunt to see if any of the arguments
     // have a mismatch with the parameters. If so, we'll
     // re-check the method to see how the parameters impact
     // its return type
     $has_argument_parameter_mismatch = false;
     // Now that we've made sure the arguments are sufficient
     // for definitions on the method, we iterate over the
     // arguments again and add their types to the parameter
     // types so we can test the method again
     $argument_list = $node->children['args'];
     // We create a copy of the parameter list so we can switch
     // back to it after
     $original_parameter_list = $method->getParameterList();
     foreach ($argument_list->children as $i => $argument) {
         $parameter = $method->getParameterList()[$i] ?? null;
         if (!$parameter) {
             continue;
         }
         // If the parameter has no type, pass the
         // argument's type to it
         if ($parameter->getUnionType()->isEmpty()) {
             $has_argument_parameter_mismatch = true;
             $argument_type = UnionType::fromNode($this->context, $this->code_base, $argument);
             // If this isn't an internal function or method
             // and it has no type, add the argument's type
             // to it so we can compare it to subsequent
             // calls
             if (!$parameter->getContext()->isInternal()) {
                 // Clone the parameter in the original
                 // parameter list so we can reset it
                 // later
                 $original_parameter_list[$i] = clone $parameter;
                 // Then set the new type on that parameter based
                 // on the argument's type. We'll use this to
                 // retest the method with the passed in types
                 $parameter->getUnionType()->addUnionType($argument_type);
             }
         }
     }
     // Now that we know something about the parameters used
     // to call the method, we can reanalyze the method with
     // the types of the parameter, making sure we don't get
     // into an infinite loop of checking calls to the current
     // method in scope
     if ($has_argument_parameter_mismatch && !$method->getContext()->isInternal() && (!$this->context->isMethodScope() || $method->getFQSEN() !== $this->context->getMethodFQSEN())) {
         $method->analyze($method->getContext(), $code_base);
     }
     // Reset to the original parameter list after having
     // tested the parameters with the types passed in
     $method->setParameterList($original_parameter_list);
 }
Example #2
0
 /**
  * Analyze the parameters and arguments for a call
  * to the given method or function
  *
  * @param CodeBase $code_base
  * @param Method $method
  * @param Node $node
  *
  * @return null
  */
 private function analyzeCallToMethod(CodeBase $code_base, Method $method, Node $node)
 {
     $method->addReference($this->context);
     // Create variables for any pass-by-reference
     // parameters
     $argument_list = $node->children['args'];
     foreach ($argument_list->children as $i => $argument) {
         $parameter = $method->getParameterList()[$i] ?? null;
         if (!$parameter || !is_object($argument)) {
             continue;
         }
         // If pass-by-reference, make sure the variable exists
         // or create it if it doesn't.
         if ($parameter->isPassByReference()) {
             if ($argument->kind == \ast\AST_VAR) {
                 // We don't do anything with it; just create it
                 // if it doesn't exist
                 $variable = (new ContextNode($this->code_base, $this->context, $argument))->getOrCreateVariable();
             } else {
                 if ($argument->kind == \ast\AST_STATIC_PROP || $argument->kind == \ast\AST_PROP) {
                     $property_name = $argument->children['prop'];
                     if (is_string($property_name)) {
                         // We don't do anything with it; just create it
                         // if it doesn't exist
                         try {
                             $property = (new ContextNode($this->code_base, $this->context, $argument))->getOrCreateProperty($argument->children['prop']);
                         } catch (IssueException $exception) {
                             $exception->getIssueInstance()();
                         } catch (\Exception $exception) {
                             // If we can't figure out what kind of a call
                             // this is, don't worry about it
                         }
                     } else {
                         // This is stuff like `Class->$foo`. I'm ignoring
                         // it.
                     }
                 }
             }
         }
     }
     // Confirm the argument types are clean
     ArgumentType::analyze($method, $node, $this->context, $this->code_base);
     // Take another pass over pass-by-reference parameters
     // and assign types to passed in variables
     foreach ($argument_list->children as $i => $argument) {
         $parameter = $method->getParameterList()[$i] ?? null;
         if (!$parameter || !is_object($argument)) {
             continue;
         }
         if (Config::get()->dead_code_detection) {
             (new ArgumentVisitor($this->code_base, $this->context))($argument);
         }
         // If the parameter is pass-by-reference and we're
         // passing a variable in, see if we should pass
         // the parameter and variable types to eachother
         $variable = null;
         if ($parameter->isPassByReference()) {
             if ($argument->kind == \ast\AST_VAR) {
                 $variable = (new ContextNode($this->code_base, $this->context, $argument))->getOrCreateVariable();
             } else {
                 if ($argument->kind == \ast\AST_STATIC_PROP || $argument->kind == \ast\AST_PROP) {
                     $property_name = $argument->children['prop'];
                     if (is_string($property_name)) {
                         // We don't do anything with it; just create it
                         // if it doesn't exist
                         try {
                             $variable = (new ContextNode($this->code_base, $this->context, $argument))->getOrCreateProperty($argument->children['prop']);
                         } catch (IssueException $exception) {
                             $exception->getIssueInstance()();
                         } catch (\Exception $exception) {
                             // If we can't figure out what kind of a call
                             // this is, don't worry about it
                         }
                     } else {
                         // This is stuff like `Class->$foo`. I'm ignoring
                         // it.
                     }
                 }
             }
             if ($variable) {
                 $variable->getUnionType()->addUnionType($parameter->getUnionType());
             }
         }
     }
     // If we're in quick mode, don't retest methods based on
     // parameter types passed in
     if (Config::get()->quick_mode) {
         return;
     }
     // We're going to hunt to see if any of the arguments
     // have a mismatch with the parameters. If so, we'll
     // re-check the method to see how the parameters impact
     // its return type
     $has_argument_parameter_mismatch = false;
     // Now that we've made sure the arguments are sufficient
     // for definitions on the method, we iterate over the
     // arguments again and add their types to the parameter
     // types so we can test the method again
     $argument_list = $node->children['args'];
     // We create a copy of the parameter list so we can switch
     // back to it after
     $original_parameter_list = $method->getParameterList();
     // Create a backup of the method's scope so that we can
     // reset it after f*****g with it below
     $original_method_scope = $method->getContext()->getScope();
     foreach ($argument_list->children as $i => $argument) {
         $parameter = $method->getParameterList()[$i] ?? null;
         if (!$parameter) {
             continue;
         }
         // If the parameter has no type, pass the
         // argument's type to it
         if ($parameter->getUnionType()->isEmpty()) {
             $has_argument_parameter_mismatch = true;
             $argument_type = UnionType::fromNode($this->context, $this->code_base, $argument);
             // If this isn't an internal function or method
             // and it has no type, add the argument's type
             // to it so we can compare it to subsequent
             // calls
             if (!$parameter->getContext()->isInternal()) {
                 // Clone the parameter in the original
                 // parameter list so we can reset it
                 // later
                 $original_parameter_list[$i] = clone $parameter;
                 // Then set the new type on that parameter based
                 // on the argument's type. We'll use this to
                 // retest the method with the passed in types
                 $parameter->getUnionType()->addUnionType($argument_type);
                 if (!is_object($argument)) {
                     continue;
                 }
                 // If we're passing by reference, get the variable
                 // we're dealing with wrapped up and shoved into
                 // the scope of the method
                 if ($parameter->isPassByReference()) {
                     if ($argument->kind == \ast\AST_VAR) {
                         // Get the variable
                         $variable = (new ContextNode($this->code_base, $this->context, $argument))->getOrCreateVariable();
                         // Add it to the scope of the function wrapped
                         // in a way that makes it addressable as the
                         // parameter its mimicking
                         $method->getContext()->addScopeVariable(new PassByReferenceVariable($parameter, $variable));
                     }
                 } else {
                     // Overwrite the method's variable representation
                     // of the parameter with the parameter with the
                     // new type
                     $method->getContext()->addScopeVariable($parameter);
                 }
             }
         }
     }
     // Now that we know something about the parameters used
     // to call the method, we can reanalyze the method with
     // the types of the parameter, making sure we don't get
     // into an infinite loop of checking calls to the current
     // method in scope
     if ($has_argument_parameter_mismatch && !$method->getContext()->isInternal() && (!$this->context->isMethodScope() || $method->getFQSEN() !== $this->context->getMethodFQSEN())) {
         $method->analyze($method->getContext(), $code_base);
     }
     // Reset to the original parameter list after having
     // tested the parameters with the types passed in
     $method->setParameterList($original_parameter_list);
     // Reset the scope to its original version before we
     // put new parameters in it
     $method->getContext()->setScope($original_method_scope);
 }