/** * @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); } } } } }
/** * @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); } } } } }
/** * Finds all classes or interfaces implementing the provided one * * @param ParsedInterface $interface * @return ParsedInterface[]|ParsedClass[] */ public function findImplements(ParsedInterface $interface) { $found = []; foreach ($this->graph->getObjects() as $object) { if ($object instanceof ParsedClass) { foreach ($object->getInterfaces() as $implements) { if ($interface === $implements) { $found[] = $object; break; } } } else { if ($object instanceof ParsedInterface) { foreach ($object->getInterfaces() as $extends) { if ($interface === $extends) { $found[] = $object; break; } } } } } return $found; }
/** * @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"; }