public function analyze(Stmt $contextBefore, Stmt $contextAfter) { $report = new Report(); $propertiesBefore = $this->getProperties($contextBefore); $propertiesAfter = $this->getProperties($contextAfter); $propertiesBeforeKeyed = []; foreach ($propertiesBefore as $property) { $propertiesBeforeKeyed[$this->getName($property)] = $property; } $propertiesAfterKeyed = []; foreach ($propertiesAfter as $property) { $propertiesAfterKeyed[$this->getName($property)] = $property; } $propertyNamesBefore = array_keys($propertiesBeforeKeyed); $propertyNamesAfter = array_keys($propertiesAfterKeyed); $propertiesAdded = array_diff($propertyNamesAfter, $propertyNamesBefore); $propertiesRemoved = array_diff($propertyNamesBefore, $propertyNamesAfter); $propertiesToVerify = array_intersect($propertyNamesBefore, $propertyNamesAfter); foreach ($propertiesRemoved as $property) { $propertyBefore = $propertiesBeforeKeyed[$property]; $data = new PropertyRemoved($this->context, $this->fileBefore, $contextBefore, $propertyBefore); $report->add($this->context, $data); } foreach ($propertiesAdded as $property) { $propertyAfter = $propertiesAfterKeyed[$property]; $data = new PropertyAdded($this->context, $this->fileAfter, $contextAfter, $propertyAfter); $report->add($this->context, $data); } return $report; }
/** * Compare with a destination registry (what the new source code is like). * * @param \PHPSemVerChecker\Registry\Registry $registryBefore * @param \PHPSemVerChecker\Registry\Registry $registryAfter * @return \PHPSemVerChecker\Report\Report */ public function analyze(Registry $registryBefore, Registry $registryAfter) { $finalReport = new Report(); $analyzers = [new FunctionAnalyzer(), new ClassAnalyzer(), new InterfaceAnalyzer(), new TraitAnalyzer()]; foreach ($analyzers as $analyzer) { $report = $analyzer->analyze($registryBefore, $registryAfter); $finalReport->merge($report); } return $finalReport; }
protected function outputReport(OutputInterface $output, Report $report, $context) { if (!$report->hasDifferences($context)) { return; } $output->writeln(''); // line clear $output->writeln(ucfirst($context) . ' (' . Level::toString($report->getLevelForContext($context)) . ')'); $this->outputTable($output, $report, $context); }
public function getOutput() { $output = []; $output['level'] = Level::toString($this->report->getSuggestedLevel()); $output['changes'] = []; $contexts = ['class', 'function', 'interface', 'trait']; $differences = $this->report->getDifferences(); foreach ($contexts as $context) { foreach (Level::asList('desc') as $level) { $reportForLevel = $differences[$context][$level]; /** @var \PHPSemVerChecker\Operation\Operation $operation */ foreach ($reportForLevel as $operation) { $output['changes'][$context][] = ['level' => Level::toString($level), 'location' => $operation->getLocation(), 'line' => $operation->getLine(), 'target' => $operation->getTarget(), 'reason' => $operation->getReason(), 'code' => $operation->getCode()]; } } } return $output; }
public function analyze(Registry $registryBefore, Registry $registryAfter) { $report = new Report(); $keysBefore = array_keys($registryBefore->data['trait']); $keysAfter = array_keys($registryAfter->data['trait']); $added = array_diff($keysAfter, $keysBefore); $removed = array_diff($keysBefore, $keysAfter); $toVerify = array_intersect($keysBefore, $keysAfter); foreach ($removed as $key) { $fileBefore = $registryBefore->mapping['trait'][$key]; $traitBefore = $registryBefore->data['trait'][$key]; $data = new TraitRemoved($fileBefore, $traitBefore); $report->addTrait($data); } foreach ($toVerify as $key) { $fileBefore = $registryBefore->mapping['trait'][$key]; /** @var \PhpParser\Node\Stmt\Class_ $traitBefore */ $traitBefore = $registryBefore->data['trait'][$key]; $fileAfter = $registryAfter->mapping['trait'][$key]; /** @var \PhpParser\Node\Stmt\Class_ $traitBefore */ $traitAfter = $registryAfter->data['trait'][$key]; // Leave non-strict comparison here if ($traitBefore != $traitAfter) { $analyzers = [new ClassMethodAnalyzer('trait', $fileBefore, $fileAfter), new PropertyAnalyzer('trait', $fileBefore, $fileAfter)]; foreach ($analyzers as $analyzer) { $internalReport = $analyzer->analyze($traitBefore, $traitAfter); $report->merge($internalReport); } } } foreach ($added as $key) { $fileAfter = $registryAfter->mapping['trait'][$key]; $traitAfter = $registryAfter->data['trait'][$key]; $data = new TraitAdded($fileAfter, $traitAfter); $report->addTrait($data); } return $report; }
public function analyze(Registry $registryBefore, Registry $registryAfter) { $report = new Report(); $keysBefore = array_keys($registryBefore->data['interface']); $keysAfter = array_keys($registryAfter->data['interface']); $added = array_diff($keysAfter, $keysBefore); $removed = array_diff($keysBefore, $keysAfter); $toVerify = array_intersect($keysBefore, $keysAfter); foreach ($removed as $key) { $fileBefore = $registryBefore->mapping['interface'][$key]; $interfaceBefore = $registryBefore->data['interface'][$key]; $data = new InterfaceRemoved($fileBefore, $interfaceBefore); $report->addInterface($data); } foreach ($toVerify as $key) { $fileBefore = $registryBefore->mapping['interface'][$key]; /** @var \PhpParser\Node\Stmt\Interface_ $interfaceBefore */ $interfaceBefore = $registryBefore->data['interface'][$key]; $fileAfter = $registryAfter->mapping['interface'][$key]; /** @var \PhpParser\Node\Stmt\Interface_ $interfaceBefore */ $interfaceAfter = $registryAfter->data['interface'][$key]; // Leave non-strict comparison here if ($interfaceBefore != $interfaceAfter) { $analyzer = new ClassMethodAnalyzer('interface', $fileBefore, $fileAfter); $interfaceMethodReport = $analyzer->analyze($interfaceBefore, $interfaceAfter); $report->merge($interfaceMethodReport); } } foreach ($added as $key) { $fileAfter = $registryAfter->mapping['interface'][$key]; $interfaceAfter = $registryAfter->data['interface'][$key]; $data = new InterfaceAdded($fileAfter, $interfaceAfter); $report->addInterface($data); } return $report; }
/** * @param \PHPSemVerChecker\Registry\Registry $registryBefore * @param \PHPSemVerChecker\Registry\Registry $registryAfter * @return \PHPSemVerChecker\Report\Report */ public function analyze(Registry $registryBefore, Registry $registryAfter) { $report = new Report(); $keysBefore = array_keys($registryBefore->data['function']); $keysAfter = array_keys($registryAfter->data['function']); $added = array_diff($keysAfter, $keysBefore); $removed = array_diff($keysBefore, $keysAfter); $toVerify = array_intersect($keysBefore, $keysAfter); foreach ($removed as $key) { $fileBefore = $registryBefore->mapping['function'][$key]; $functionBefore = $registryBefore->data['function'][$key]; $data = new FunctionRemoved($fileBefore, $functionBefore); $report->addFunction($data); } foreach ($toVerify as $key) { $fileBefore = $registryBefore->mapping['function'][$key]; $functionBefore = $registryBefore->data['function'][$key]; $fileAfter = $registryAfter->mapping['function'][$key]; $functionAfter = $registryAfter->data['function'][$key]; // Leave non-strict comparison here if ($functionBefore != $functionAfter) { $paramsBefore = $functionBefore->params; $paramsAfter = $functionAfter->params; // Signature if (!Signature::isSameTypehints($paramsBefore, $paramsAfter)) { $data = new FunctionParameterChanged($fileBefore, $functionBefore, $fileAfter, $functionAfter); $report->addFunction($data); continue; } if (!Signature::isSameVariables($paramsBefore, $paramsAfter)) { $data = new FunctionParameterNameChanged($fileBefore, $functionBefore, $fileAfter, $functionAfter); $report->addFunction($data); continue; } // Different length (considering params with defaults) // Difference in source code if ($functionBefore->stmts != $functionAfter->stmts) { $data = new FunctionImplementationChanged($fileBefore, $functionBefore, $fileAfter, $functionAfter); $report->addFunction($data); continue; } } } foreach ($added as $key) { $fileAfter = $registryAfter->mapping['function'][$key]; $functionAfter = $registryAfter->data['function'][$key]; $data = new FunctionAdded($fileAfter, $functionAfter); $report->addFunction($data); } return $report; }
/** * @param \PHPSemVerChecker\Registry\Registry $registryBefore * @param \PHPSemVerChecker\Registry\Registry $registryAfter * @return \PHPSemVerChecker\Report\Report */ public function analyze(Registry $registryBefore, Registry $registryAfter) { $report = new Report(); $keysBefore = array_keys($registryBefore->data['function']); $keysAfter = array_keys($registryAfter->data['function']); $added = array_diff($keysAfter, $keysBefore); $removed = array_diff($keysBefore, $keysAfter); $toVerify = array_intersect($keysBefore, $keysAfter); foreach ($removed as $key) { $fileBefore = $registryBefore->mapping['function'][$key]; $functionBefore = $registryBefore->data['function'][$key]; $data = new FunctionRemoved($fileBefore, $functionBefore); $report->addFunction($data); } foreach ($toVerify as $key) { $fileBefore = $registryBefore->mapping['function'][$key]; $functionBefore = $registryBefore->data['function'][$key]; $fileAfter = $registryAfter->mapping['function'][$key]; $functionAfter = $registryAfter->data['function'][$key]; // Leave non-strict comparison here if ($functionBefore != $functionAfter) { $paramsBefore = $functionBefore->params; $paramsAfter = $functionAfter->params; $signatureResult = Signature::analyze($paramsBefore, $paramsAfter); $changes = ['parameter_added' => FunctionParameterAdded::class, 'parameter_removed' => FunctionParameterRemoved::class, 'parameter_renamed' => FunctionParameterNameChanged::class, 'parameter_typing_added' => FunctionParameterTypingAdded::class, 'parameter_typing_removed' => FunctionParameterTypingRemoved::class, 'parameter_default_added' => FunctionParameterDefaultAdded::class, 'parameter_default_removed' => FunctionParameterDefaultRemoved::class, 'parameter_default_value_changed' => FunctionParameterDefaultValueChanged::class]; foreach ($changes as $changeType => $class) { if (!$signatureResult[$changeType]) { continue; } if (is_a($class, FunctionOperationUnary::class, true)) { $data = new $class($fileAfter, $functionAfter); } else { $data = new $class($fileBefore, $functionBefore, $fileAfter, $functionAfter); } $report->addFunction($data); } // Difference in source code if (!Implementation::isSame($functionBefore->stmts, $functionAfter->stmts)) { $data = new FunctionImplementationChanged($fileBefore, $functionBefore, $fileAfter, $functionAfter); $report->addFunction($data); } } } foreach ($added as $key) { $fileAfter = $registryAfter->mapping['function'][$key]; $functionAfter = $registryAfter->data['function'][$key]; $data = new FunctionAdded($fileAfter, $functionAfter); $report->addFunction($data); } return $report; }
public function analyze(Stmt $contextBefore, Stmt $contextAfter) { $report = new Report(); $methodsBefore = $contextBefore->getMethods(); $methodsAfter = $contextAfter->getMethods(); $methodsBeforeKeyed = []; foreach ($methodsBefore as $method) { $methodsBeforeKeyed[$method->name] = $method; } $methodsAfterKeyed = []; foreach ($methodsAfter as $method) { $methodsAfterKeyed[$method->name] = $method; } $methodNamesBefore = array_keys($methodsBeforeKeyed); $methodNamesAfter = array_keys($methodsAfterKeyed); $methodsAdded = array_diff($methodNamesAfter, $methodNamesBefore); $methodsRemoved = array_diff($methodNamesBefore, $methodNamesAfter); $methodsToVerify = array_intersect($methodNamesBefore, $methodNamesAfter); // Here we only care about public methods as they are the only part of the API we care about // Removed methods can either be implemented in parent classes or not exist anymore foreach ($methodsRemoved as $method) { $methodBefore = $methodsBeforeKeyed[$method]; $data = new ClassMethodRemoved($this->context, $this->fileBefore, $contextBefore, $methodBefore); $report->add($this->context, $data); } foreach ($methodsToVerify as $method) { /** @var \PhpParser\Node\Stmt\ClassMethod $methodBefore */ $methodBefore = $methodsBeforeKeyed[$method]; /** @var \PhpParser\Node\Stmt\ClassMethod $methodAfter */ $methodAfter = $methodsAfterKeyed[$method]; // Leave non-strict comparison here if ($methodBefore != $methodAfter) { $paramsBefore = $methodBefore->params; $paramsAfter = $methodAfter->params; // Signature $signatureChanged = false; if (!Signature::isSameTypehints($paramsBefore, $paramsAfter)) { $data = new ClassMethodParameterChanged($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter); $report->add($this->context, $data); $signatureChanged = true; } if (!$signatureChanged && !Signature::isSameVariables($paramsBefore, $paramsAfter)) { $data = new ClassMethodParameterNameChanged($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter); $report->add($this->context, $data); } // Different length (considering params with defaults) // Difference in source code if ($methodBefore->stmts != $methodAfter->stmts) { $data = new ClassMethodImplementationChanged($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter); $report->add($this->context, $data); } } } // Added methods implies MINOR BUMP foreach ($methodsAdded as $method) { $methodAfter = $methodsAfterKeyed[$method]; $data = new ClassMethodAdded($this->context, $this->fileAfter, $contextAfter, $methodAfter); $report->add($this->context, $data); } return $report; }
public function analyze(Stmt $contextBefore, Stmt $contextAfter) { $report = new Report(); $methodsBefore = $contextBefore->getMethods(); $methodsAfter = $contextAfter->getMethods(); $methodsBeforeKeyed = []; foreach ($methodsBefore as $method) { $methodsBeforeKeyed[$method->name] = $method; } $methodsAfterKeyed = []; foreach ($methodsAfter as $method) { $methodsAfterKeyed[$method->name] = $method; } $methodNamesBefore = array_keys($methodsBeforeKeyed); $methodNamesAfter = array_keys($methodsAfterKeyed); $methodsAdded = array_diff($methodNamesAfter, $methodNamesBefore); $methodsRemoved = array_diff($methodNamesBefore, $methodNamesAfter); $methodsToVerify = array_intersect($methodNamesBefore, $methodNamesAfter); // Here we only care about public methods as they are the only part of the API we care about // Removed methods can either be implemented in parent classes or not exist anymore foreach ($methodsRemoved as $method) { $methodBefore = $methodsBeforeKeyed[$method]; $data = new ClassMethodRemoved($this->context, $this->fileBefore, $contextBefore, $methodBefore); $report->add($this->context, $data); } foreach ($methodsToVerify as $method) { /** @var \PhpParser\Node\Stmt\ClassMethod $methodBefore */ $methodBefore = $methodsBeforeKeyed[$method]; /** @var \PhpParser\Node\Stmt\ClassMethod $methodAfter */ $methodAfter = $methodsAfterKeyed[$method]; // Leave non-strict comparison here if ($methodBefore != $methodAfter) { $paramsBefore = $methodBefore->params; $paramsAfter = $methodAfter->params; $signatureResult = Signature::analyze($paramsBefore, $paramsAfter); $changes = ['parameter_added' => ClassMethodParameterAdded::class, 'parameter_removed' => ClassMethodParameterRemoved::class, 'parameter_renamed' => ClassMethodParameterNameChanged::class, 'parameter_typing_added' => ClassMethodParameterTypingAdded::class, 'parameter_typing_removed' => ClassMethodParameterTypingRemoved::class, 'parameter_default_added' => ClassMethodParameterDefaultAdded::class, 'parameter_default_removed' => ClassMethodParameterDefaultRemoved::class, 'parameter_default_value_changed' => ClassMethodParameterDefaultValueChanged::class]; foreach ($changes as $changeType => $class) { if (!$signatureResult[$changeType]) { continue; } if (is_a($class, ClassMethodOperationUnary::class, true)) { $data = new $class($this->context, $this->fileAfter, $contextAfter, $methodAfter); } else { $data = new $class($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter); } $report->add($this->context, $data); } // Difference in source code // Cast to array because interfaces do not have stmts (= null) if (!Implementation::isSame((array) $methodBefore->stmts, (array) $methodAfter->stmts)) { $data = new ClassMethodImplementationChanged($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter); $report->add($this->context, $data); } } } // Added methods implies MINOR BUMP foreach ($methodsAdded as $method) { $methodAfter = $methodsAfterKeyed[$method]; $data = new ClassMethodAdded($this->context, $this->fileAfter, $contextAfter, $methodAfter); $report->add($this->context, $data); } return $report; }