diff --git a/Command/GenerateCommand.php b/Command/GenerateCommand.php index dbc497a21c8e177682876c2fc48630643af1b2a5..d2748355f0ba6d29b44314eb458def70765cdd2b 100644 --- a/Command/GenerateCommand.php +++ b/Command/GenerateCommand.php @@ -10,6 +10,10 @@ use Doctrine\ORM\EntityManagerInterface; use Irstea\PlantUmlBundle\Doctrine\AssociationDecorator; use Irstea\PlantUmlBundle\Doctrine\DoctrineNamespace; use Irstea\PlantUmlBundle\Doctrine\EntityDecorator; +use Irstea\PlantUmlBundle\Doctrine\EntityFinder; +use Irstea\PlantUmlBundle\Finder\ClassFinder; +use Irstea\PlantUmlBundle\Finder\FilteringFinder; +use Irstea\PlantUmlBundle\Finder\FinderInterface; use Irstea\PlantUmlBundle\Model\ClassFilterInterface; use Irstea\PlantUmlBundle\Model\ClassVisitor; use Irstea\PlantUmlBundle\Model\Decorator\CompositeDecorator; @@ -23,6 +27,7 @@ use Irstea\PlantUmlBundle\Model\Filter\AcceptAllFilter; use Irstea\PlantUmlBundle\Model\Filter\Composite\AllFilter; use Irstea\PlantUmlBundle\Model\Filter\DirectoryFilter; use Irstea\PlantUmlBundle\Model\Filter\NamespaceFilter; +use Irstea\PlantUmlBundle\Model\Graph; use Irstea\PlantUmlBundle\Model\Namespace_\BundleNamespace; use Irstea\PlantUmlBundle\Model\Namespace_\FlatNamespace; use Irstea\PlantUmlBundle\Model\Namespace_\Php\RootNamespace; @@ -98,102 +103,62 @@ class GenerateCommand extends ContainerAwareCommand $config = $graphs[$name]; - $classes = $this->findClasses($config['sources']); - if (empty($classes)) { - $output->writeln("Nothing to analyze."); - return 0; - } - - $namespace = $this->buildNamespace($config['layout']['namespaces']); - $displayFilter = $this->buildFilter($config['layout']); - - $decorator = $this->buildDecorator($config['decoration']); + $graph = new Graph( + new ClassVisitor( + $this->buildDecorator($config['decoration']), + $this->buildFilter($config['layout']), + $this->buildNamespace($config['layout']['namespaces']) + ), + $this->buildFinder($config['sources']) + ); - $visitor = new ClassVisitor($decorator, $displayFilter, $namespace); - - foreach($classes as $class) { - $visitor->visitClass($class); - } + $graph->visitAll(); $writer = new OutputWriter($output); - $writer->write("@startuml\n"); - $visitor->outputTo($writer); - $writer->write("@enduml\n"); + $graph->outputTo($writer); } /** - * @param ReflectionClass[] + * @param array $config + * @return FinderInterface */ - protected function findClasses(array $config) + protected function buildFinder(array $config) { switch($config['type']) { case 'entities': - $classes = $this->findEntities($config['entity_manager']); + $finder = $this->buildEntityFinder($config['entity_manager']); break; case 'classes': - $classes = $this->findClassesInDirectories($config['directories']); + $finder = $this->buildClassFinder($config['directories']); break; } $filter = $this->buildFilter($config); - if ($filter) { - return array_filter($classes, [$filter, 'accept']); + if (!$filter) { + return $finder; } - return $classes; + return new FilteringFinder($finder, $filter); } /** - * @return ReflectionClass[] + * @param array $directories + * @return FinderInterface */ - protected function findEntities($managerName) + protected function buildClassFinder(array $directories) { - $doctrine = $this->getContainer()->get('doctrine'); - $this->entityManager = $doctrine->getManager($managerName); - - $classes = []; - foreach($this->entityManager->getMetadataFactory()->getAllMetadata() as $metadata) { - $classes[$metadata->getName()] = $metadata->getReflectionClass(); - } - - return $classes; + return new ClassFinder($this->parseDirectories($directories)); } /** - * - * @return ReflectionClass[] + * @param string $managerName + * @return FinderInterface */ - protected function findClassesInDirectories($directories) + protected function buildEntityFinder($managerName) { - $paths = $this->parseDirectories($directories); - - $files = Finder::create() - ->in($paths) - ->files() - ->name('*.php') - ->getIterator(); - - foreach($files as $file) - { - /* @var $file SplFileInfo */ - $path = $file->getPathname(); - try { - irstea_plantmul_include($path); - } catch(Exception $ex) { - printf("%s: %s (%s)\n", $path, get_class($ex), $ex->getMessage()); - } - } - - $filter = new DirectoryFilter($paths); - $classes = []; - foreach(get_declared_classes() as $className) { - $class = new ReflectionClass($className); - if ($filter->accept($class)) { - $classes[$className] = $class; - } - } - - return $classes; + return new EntityFinder( + $this->getContainer()->get('doctrine')->getManager($managerName) + ); } /** @@ -203,32 +168,13 @@ class GenerateCommand extends ContainerAwareCommand */ protected function buildDecorator(array $config) { - $decorators = []; - - foreach($config['decorators'] as $decorator) { - switch($decorator) { - case 'inheritance': - $decorators[] = new InheritanceDecorator(); - break; - case 'interfaces': - $decorators[] = new InterfaceDecorator(); - break; - case 'traits': - $decorators[] = new TraitDecorator(); - break; - case 'entity': - $decorators[] = new EntityDecorator($this->entityManager->getMetadataFactory()); - break; - case 'associations': - $decorators[] = new AssociationDecorator($this->entityManager->getMetadataFactory()); - break; - /*default: - throw new RuntimeException("Decorator '$decorator' not yet implemented");*/ - } + if (empty($config['decorators'])) { + return NullDecorator::instance(); } - if (empty($decorators)) { - return NullDecorator::instance(); + $decorators = []; + foreach($config['decorators'] as $type) { + $decorators[] = $this->buildTypedDecorator($type); } if (count($decorators) === 1) { @@ -245,6 +191,26 @@ class GenerateCommand extends ContainerAwareCommand return $decorator; } + /** + * @param type $type + * @return DecoratorInterface + */ + protected function buildTypedDecorator($type) + { + switch($type) { + case 'inheritance': + return new InheritanceDecorator(); + case 'interfaces': + return new InterfaceDecorator(); + case 'traits': + return new TraitDecorator(); + case 'entity': + return new EntityDecorator($this->entityManager->getMetadataFactory()); + case 'associations': + return new AssociationDecorator($this->entityManager->getMetadataFactory()); + } + } + /** * @param string $config * @return NamespaceInterface @@ -340,9 +306,4 @@ class GenerateCommand extends ContainerAwareCommand return $actualNamespaces; } -} - -function irstea_plantmul_include($filepath) -{ - @include_once($filepath); } \ No newline at end of file diff --git a/Doctrine/EntityFinder.php b/Doctrine/EntityFinder.php new file mode 100644 index 0000000000000000000000000000000000000000..3b870cf6f39f3627ad64a8d4cfffd9f806f2839e --- /dev/null +++ b/Doctrine/EntityFinder.php @@ -0,0 +1,39 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Doctrine; + +use ArrayIterator; +use Doctrine\ORM\EntityManager; +use Irstea\PlantUmlBundle\Finder\FinderInterface; + +/** + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +class EntityFinder implements FinderInterface +{ + /** + * @var EntityManager + */ + private $entityManager; + + public function __construct(EntityManager $entityManager) + { + $this->entityManager = $entityManager; + } + + public function getIterator() + { + $classes = []; + foreach($this->entityManager->getMetadataFactory()->getAllMetadata() as $metadata) { + $classes[$metadata->getName()] = $metadata->getReflectionClass(); + } + return new ArrayIterator($classes); + } +} diff --git a/Finder/ClassFinder.php b/Finder/ClassFinder.php new file mode 100644 index 0000000000000000000000000000000000000000..e89403466cd3f81737ae2be546d0e59502da76d8 --- /dev/null +++ b/Finder/ClassFinder.php @@ -0,0 +1,82 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Finder; + +use ArrayIterator; +use Irstea\PlantUmlBundle\Model\Filter\DirectoryFilter; +use ReflectionClass; +use Symfony\Component\Config\Definition\Exception\Exception; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; + +/** + * Description of ClassFinder + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +class ClassFinder implements FinderInterface +{ + /** + * @var string[] + */ + private $directories; + + /** + * + * @var \ReflectionClass + */ + private $classes = null; + + public function __construct($directories) + { + $this->directories = $directories; + } + + private function initialize() + { + $files = Finder::create() + ->in($this->directories) + ->files() + ->name('*.php') + ->getIterator(); + + foreach($files as $file) + { + /* @var $file SplFileInfo */ + $path = $file->getPathname(); + try { + irstea_plantmul_include($path); + } catch(Exception $ex) { + printf("%s: %s (%s)\n", $path, get_class($ex), $ex->getMessage()); + } + } + + $filter = new DirectoryFilter($this->directories); + $this->classes = []; + foreach(get_declared_classes() as $className) { + $class = new ReflectionClass($className); + if ($filter->accept($class)) { + $this->classes[$className] = $class; + } + } + } + + public function getIterator() + { + if ($this->classes === null) { + $this->initialize(); + } + return new ArrayIterator($this->classes); + } +} + +function irstea_plantmul_include($filepath) +{ + @include_once($filepath); +} diff --git a/Finder/FilteringFinder.php b/Finder/FilteringFinder.php new file mode 100644 index 0000000000000000000000000000000000000000..e78fd868b83966ac526f2777fa5749b4065b4f6f --- /dev/null +++ b/Finder/FilteringFinder.php @@ -0,0 +1,41 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Finder; + +use CallbackFilterIterator; +use Irstea\PlantUmlBundle\Model\ClassFilterInterface; + +/** + * Description of FilteringFinder + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +class FilteringFinder implements FinderInterface +{ + /** + * @var FinderInterface + */ + private $inner; + + /** + * @var ClassFilterInterface + */ + private $filter; + + public function __construct(FinderInterface $inner, ClassFilterInterface $filter) + { + $this->inner = $inner; + $this->filter = $filter; + } + + public function getIterator() + { + return new CallbackFilterIterator($this->inner->getIterator(), [$this->filter, 'accept']); + } +} diff --git a/Finder/FinderInterface.php b/Finder/FinderInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..56657cd8fcde888557f355274dc2b3e2f55ff097 --- /dev/null +++ b/Finder/FinderInterface.php @@ -0,0 +1,17 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Finder; + +/** + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +interface FinderInterface extends \Traversable, \IteratorAggregate +{ +} diff --git a/Model/ClassVisitor.php b/Model/ClassVisitor.php index 1f557bc08da5c95421faa459e3cd8be9f0b48216..c793cf1311c1be347b6eedfd604031113cf4d7d9 100644 --- a/Model/ClassVisitor.php +++ b/Model/ClassVisitor.php @@ -23,7 +23,7 @@ use ReflectionClass; * * @author Guillaume Perréal <guillaume.perreal@irstea.fr> */ -class ClassVisitor implements ClassVisitorInterface, WritableInterface +class ClassVisitor implements ClassVisitorInterface { /** * @var NamespaceInterface diff --git a/Model/ClassVisitorInterface.php b/Model/ClassVisitorInterface.php index dbc6608022934facf36f3e89e092f958d59746ac..9ad017898ed85d72dfe850d07df1f357fdb477c5 100644 --- a/Model/ClassVisitorInterface.php +++ b/Model/ClassVisitorInterface.php @@ -8,13 +8,13 @@ namespace Irstea\PlantUmlBundle\Model; -use ReflectionClass; +use Irstea\PlantUmlBundle\Writer\WritableInterface; /** * * @author Guillaume Perréal <guillaume.perreal@irstea.fr> */ -interface ClassVisitorInterface +interface ClassVisitorInterface extends WritableInterface { /** * @param ReflectionClass|string diff --git a/Model/Graph.php b/Model/Graph.php new file mode 100644 index 0000000000000000000000000000000000000000..9442c1fffe2233aaa25cae118b563117abbea3ec --- /dev/null +++ b/Model/Graph.php @@ -0,0 +1,51 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Model; + +use Irstea\PlantUmlBundle\Finder\FinderInterface; +use Irstea\PlantUmlBundle\Writer\WriterInterface; + +/** + * Description of Graph + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +class Graph implements GraphInterface +{ + /** + * @var ClassVisitorInterface + */ + private $visitor; + + /** + * @var FinderInterface + */ + private $finder; + + public function __construct(ClassVisitorInterface $visitor, FinderInterface $finder) + { + $this->visitor = $visitor; + $this->finder = $finder; + } + + public function visitAll() + { + foreach($this->finder->getIterator() as $class) { + $this->visitor->visitClass($class); + } + } + + public function outputTo(WriterInterface $writer) + { + $writer->write("@startuml@\n"); + $this->visitor->outputTo($writer); + $writer->write("@enduml@\n"); + return $this; + } +} diff --git a/Model/GraphInterface.php b/Model/GraphInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..e45bf8ae4bea800ac11fefcd1e83c6b5b42610f5 --- /dev/null +++ b/Model/GraphInterface.php @@ -0,0 +1,23 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Model; + +use Irstea\PlantUmlBundle\Writer\WritableInterface; + +/** + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +interface GraphInterface extends WritableInterface +{ + /** + * @return void + */ + public function visitAll(); +}