/**
  * @param Project $project
  */
 public function analyze(Project $project)
 {
     foreach ($this->graph->getObjects() as $object) {
         if ($object instanceof ParsedInterface) {
             foreach ($object->getMethods() as $method) {
                 if ($method->getMethod()->isAbstract()) {
                     $report = new StringReport('Access type for interface method ' . $object->getName() . '::' . $method->getNameAndParamsSignature() . ' must be ommmited in ' . $object->getFile()->getSplFile()->getRealPath() . ':' . $object->getInterface()->getLine());
                     $report->setSourceFragment(new SourceFragment($object->getFile(), new Lines($method->getMethod()->getAttribute('startLine') - 1 - $this->sourceContext, $method->getMethod()->getAttribute('endLine') - 1 + $this->sourceContext, $method->getMethod()->getAttribute('startLine') - 1)));
                     $project->addReport($report);
                 }
             }
         }
     }
 }
Example #2
0
 /**
  * @param Project $project
  */
 public function analyze(Project $project)
 {
     foreach ($this->graph->getObjects() as $object) {
         if ($object instanceof ParsedInterface) {
             foreach ($object->getMethods() as $method) {
                 $methodCompare = new MethodSignatureCompare($method);
                 foreach ($this->checkInterfaceMethods($methodCompare, $this->helper->findInterfaceImplements($object)) as $brokenInterfaceAndMethod) {
                     $report = new Report($brokenInterfaceAndMethod, $method);
                     $report->setSourceFragment(new SourceFragment($object->getFile(), new Lines($method->getMethod()->getAttribute('startLine') - 1 - $this->sourceContext, $method->getMethod()->getAttribute('endLine') - 1 + $this->sourceContext, $method->getMethod()->getAttribute('startLine') - 1)));
                     $project->addReport($report);
                 }
             }
         }
     }
 }
Example #3
0
 /**
  * Finds all interfaces implementing the provided one
  *
  * @param ParsedInterface $interface
  * @return ParsedInterface[]
  */
 public function findInterfaceImplements(ParsedInterface $interface)
 {
     $found = [];
     foreach ($this->graph->getInterfaces() as $object) {
         foreach ($object->getInterfaces() as $extends) {
             if ($interface === $extends) {
                 $found[] = $object;
                 break;
             }
         }
     }
     return $found;
 }
use Mfn\PHP\Analyzer\Analyzers\NameResolver;
use Mfn\PHP\Analyzer\Analyzers\ObjectGraph\Helper;
use Mfn\PHP\Analyzer\Analyzers\ObjectGraph\ObjectGraph;
use Mfn\PHP\Analyzer\Analyzers\Parser;
use Mfn\PHP\Analyzer\Logger\Stdout;
use Mfn\PHP\Analyzer\Project;
use Mfn\PHP\Analyzer\Util\Util;
use PhpParser\Lexer;
require_once __DIR__ . '/../vendor/autoload.php';
error_reporting(E_ALL);
Util::installMinimalError2ExceptionHandler();
$projectRealPath = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..');
# Use analyzer to gather the object graph
$project = new Project(new Stdout());
$project->addSplFileInfos(Util::scanDir(__DIR__ . '/../lib/'));
$project->addAnalyzers([new Parser(new \PhpParser\Parser(new Lexer())), new NameResolver(), $objectGraph = new ObjectGraph()]);
$project->analyze();
$helper = new Helper($objectGraph);
$className = 'Mfn\\PHP\\Analyzer\\Analyzers\\Analyzer';
$class = $objectGraph->getClassByFqn($className);
if (NULL === $class) {
    throw new \RuntimeException("Unable to find class {$className}");
}
unset($className);
$descendants = $helper->findExtends($class, true);
sort($descendants);
/** @var string[] $index */
$index = [];
$index[] = '# Built-in / available Analyzers';
$index[] = '';
foreach ($descendants as $class) {
Example #5
0
 /**
  * @return string
  */
 public function generate()
 {
     $nodes = [];
     $edges = [];
     $mapping = $this->graph->getObjects();
     $fqns = array_keys($mapping);
     $objects = array_values($mapping);
     $markNodeReferenced = [];
     $addEdge = function ($from, $to) use(&$edges, &$markNodeReferenced) {
         $edges[] = sprintf('%d -> %d', $from, $to);
         $markNodeReferenced[$from] = true;
         $markNodeReferenced[$to] = true;
     };
     $clusters = [];
     # Array of namespaces containing a list of nodes
     foreach ($objects as $index => $object) {
         # In case object from Reflections are available, filter them out
         if (!$object instanceof Parsed) {
             continue;
         }
         if (!empty($this->namespaceWhitelist)) {
             $fqn = strtolower($object->getFqn());
             $match = false;
             foreach ($this->namespaceWhitelist as $namespace) {
                 if (0 === strpos($fqn, $namespace)) {
                     $match = true;
                     break;
                 }
             }
             if (!$match) {
                 continue;
             }
         }
         if ($this->clusterByNamespace) {
             $namespaceName = $object->getNamespaceName();
             if (!empty($namespaceName)) {
                 if (!isset($clusters[$namespaceName])) {
                     $clusters[$namespaceName] = ['nodes' => [], 'subgraphs' => [], 'alreadyDrawn' => false];
                 }
                 $clusters[$namespaceName]['nodes'][] = $index;
             }
         }
         $shape = $object instanceof Interface_ ? 'shape=diamond' : '';
         # we add them by their index to be able to remove them if showOnlyConnected
         $nodes[$index] = sprintf('%d [ label = "%s"; %s ]', $index, $this->showNamespace ? str_replace('\\', '\\\\', $object->getName()) : $object->getNode()->name, $shape);
         if ($object instanceof ParsedClass) {
             $fqExtends = $object->getFqExtends();
             if (NULL !== $fqExtends) {
                 if (false !== ($pos = array_search($fqExtends, $fqns, true))) {
                     $addEdge($index, $pos);
                 }
             }
             $fqImplements = $object->getInterfaceNames();
             foreach ($fqImplements as $fqImplement) {
                 if (false !== ($pos = array_search($fqImplement, $fqns, true))) {
                     $addEdge($index, $pos);
                 }
             }
         } else {
             if ($object instanceof ParsedInterface) {
                 $fqExtends = $object->getInterfaceNames();
                 foreach ($fqExtends as $fqExtend) {
                     if (false !== ($pos = array_search($fqExtend, $fqns, true))) {
                         $addEdge($index, $pos);
                     }
                 }
             } else {
                 throw new \RuntimeException('Unsupported object ' . get_class($object));
             }
         }
     }
     if ($this->showOnlyConnected) {
         $markNodeReferenced = array_keys($markNodeReferenced);
         foreach ($nodes as $index => $label) {
             if (!in_array($index, $markNodeReferenced)) {
                 unset($nodes[$index]);
             }
         }
     }
     $out = ['digraph {'];
     $out[] = 'rankdir = "RL"';
     $out[] = 'node[shape=record]';
     if ($this->clusterByNamespace) {
         # Remove clusters without nodes; this can happen due showOnlyConnected
         foreach ($clusters as $clusterName => &$clusterData) {
             $clusterNodes = array_filter($clusterData['nodes'], function ($node) use($nodes) {
                 return isset($nodes[$node]);
             });
             if (empty($clusterNodes)) {
                 unset($clusters[$clusterName]);
             } else {
                 $clusterData['nodes'] = $clusterNodes;
             }
         }
         if ($this->nestClusters) {
             # Handling nested graphs is tricky because they need to be literally
             # nested in the dot file too:
             # - reverse sort namespaces
             # - for each namespace, see if you a matching parent namespace and add it
             ksort($clusters);
             $clusterNames = array_keys($clusters);
             rsort($clusterNames);
             foreach ($clusterNames as $index => $clusterName) {
                 foreach (array_slice($clusterNames, $index + 1) as $tryingMatchName) {
                     $nameToMatch = $tryingMatchName . '\\';
                     if (0 === strpos($clusterName, $nameToMatch)) {
                         $clusters[$tryingMatchName]['subgraphs'][] = $clusterName;
                         break;
                     }
                 }
             }
         }
         $clusterIndex = 0;
         # counter .dot file
         $drawCluster = function ($name) use(&$clusters, &$out, &$drawCluster, &$clusterIndex) {
             $cluster = $clusters[$name];
             if ($cluster['alreadyDrawn']) {
                 return;
             }
             $clusters[$name]['alreadyDrawn'] = true;
             $out[] = sprintf('subgraph cluster_%d {', $clusterIndex);
             $out[] = sprintf('label = "%s";', str_replace('\\', '\\\\', $name));
             # subgraphs are actually only filled if nestClusters is true
             foreach ($cluster['subgraphs'] as $subgraphName) {
                 $drawCluster($subgraphName);
             }
             foreach ($cluster['nodes'] as $node) {
                 $out[] = $node . ';';
             }
             $out[] = '}';
             $clusterIndex++;
         };
         foreach (array_keys($clusters) as $clusterName) {
             $drawCluster($clusterName);
         }
     }
     $out = array_merge($out, $nodes);
     $out = array_merge($out, $edges);
     $out[] = '}';
     return join("\n", $out) . "\n";
 }