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;
 }
示例#2
0
 /**
  * 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;
 }
示例#3
0
 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;
 }