protected function checkMethod(Class_ $class, ClassMethod $method, ClassInterface $parentClass, MethodInterface $parentMethod) { $visibility = $method->getAccessLevel(); $oldVisibility = $parentMethod->getAccessLevel(); $fileName = $this->symbolTable->getClassFile(strval($class->namespacedName)); $this->incTests(); // "public" and "protected" can be redefined," private can not. if ($oldVisibility != $visibility && $oldVisibility == "private") { $this->emitError($fileName, $class, self::TYPE_SIGNATURE_TYPE, "Access level mismatch in " . $method->getName() . "() " . $visibility . " vs " . $oldVisibility); } $params = $method->getParameters(); $parentMethodParams = $parentMethod->getParameters(); $count1 = count($params); $count2 = count($parentMethodParams); if ($count1 < $count2) { $this->emitError($fileName, $class, self::TYPE_SIGNATURE_COUNT, "Parameter count mismatch {$count1} vs {$count2} in method " . $class->namespacedName . "->" . $method->getName()); } else { foreach ($params as $index => $param) { /** @var FunctionLikeParameter $param */ // Only parameters specified by the parent need to match. (Child can add more as long as they have a default.) if ($index < $count2) { $parentParam = $parentMethodParams[$index]; $name1 = strval($param->getType()); $name2 = strval($parentParam->getType()); if (strcasecmp($name1, $name2) !== 0) { $this->emitErrorOnLine($fileName, $method->getStartingLine(), self::TYPE_SIGNATURE_TYPE, "Parameter mismatch type mismatch " . $class->namespacedName . "::" . $method->getName() . " : {$name1} vs {$name2}"); break; } if ($param->isReference() != $parentParam->isReference()) { $this->emitErrorOnLine($fileName, $method->getStartingLine(), self::TYPE_SIGNATURE_TYPE, "Child Method " . $class->name . "::" . $method->getName() . " add or removes & in \$" . $param->getName()); break; } if (!$param->isOptional() && $parentParam->isOptional()) { $this->emitErrorOnLine($fileName, $method->getStartingLine(), self::TYPE_SIGNATURE_TYPE, "Child method " . $class->name . "::" . $method->getName() . " changes parameter \$" . $param->getName() . " to be required."); break; } } else { if (!$param->isOptional()) { $this->emitErrorOnLine($fileName, $method->getStartingLine(), self::TYPE_SIGNATURE_TYPE, "Child method " . $method->getName() . " adds parameter \$" . $param->getName() . " that doesn't have a default value"); break; } } } } }
/** * @param $fileName * @param $node * @param string $inside * @param Scope $scope * @param $method */ protected function checkMethod($fileName, $node, $inside, Scope $scope, MethodInterface $method) { if ($method->isStatic()) { $this->emitError($fileName, $node, self::TYPE_INCORRECT_DYNAMIC_CALL, "Call to static method of {$inside}::" . $method->getName() . " non-statically"); return; } $params = $method->getParameters(); $minimumArgs = $method->getMinimumRequiredParameters(); if (count($node->args) < $minimumArgs) { $this->emitError($fileName, $node, self::TYPE_SIGNATURE_COUNT, "Function call parameter count mismatch to method " . $method->getName() . " (passed " . count($node->args) . " requires {$minimumArgs})"); } if (count($node->args) > count($params) && !$method->isVariadic()) { $this->emitError($fileName, $node, self::TYPE_SIGNATURE_COUNT_EXCESS, "Too many parameters to non-variadic method " . $method->getName() . " (passed " . count($node->args) . " only takes " . count($params) . ")"); } foreach ($node->args as $index => $arg) { if ($scope && $arg->value instanceof \PhpParser\Node\Expr\Variable && $index < count($params) && $params[$index]->getType() != "") { $variableName = $arg->value->name; $type = $scope->getVarType($variableName); $expectedType = $params[$index]->getType(); if (!in_array($type, [Scope::SCALAR_TYPE, Scope::MIXED_TYPE, Scope::UNDEFINED]) && $type != "" && !$this->symbolTable->isParentClassOrInterface($expectedType, $type)) { $this->emitError($fileName, $node, self::TYPE_SIGNATURE_TYPE, "Variable passed to method " . $inside . "->" . $node->name . "() parameter \${$variableName} must be a {$expectedType}, passing {$type}"); } } } }