Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Pôle IS
Bundles Symfony 2
plantuml-bundle
Commits
93e12c5d
Commit
93e12c5d
authored
Mar 10, 2016
by
Guillaume Perréal
Browse files
Ajout d'un système de configuration.
parent
9dd7820d
Changes
10
Hide whitespace changes
Inline
Side-by-side
Command/GenerateCommand.php
View file @
93e12c5d
...
...
@@ -7,24 +7,36 @@
namespace
Irstea\PlantUmlBundle\Command
;
use
Doctrine\ORM\EntityManagerInterface
;
use
Doctrine\ORM\Mapping\ClassMetadata
;
use
Irstea\PlantUmlBundle\Doctrine\AssociationDecorator
;
use
Irstea\PlantUmlBundle\Doctrine\DoctrineNamespace
;
use
Irstea\PlantUmlBundle\Doctrine\EntityDecorator
;
use
Irstea\PlantUmlBundle\Model\ClassFilterInterface
;
use
Irstea\PlantUmlBundle\Model\ClassVisitor
;
use
Irstea\PlantUmlBundle\Model\Decorator\CompositeDecorator
;
use
Irstea\PlantUmlBundle\Model\Decorator\FilteringDecorator
;
use
Irstea\PlantUmlBundle\Model\Decorator\InheritanceDecorator
;
use
Irstea\PlantUmlBundle\Model\Decorator\NullDecorator
;
use
Irstea\PlantUmlBundle\Model\DecoratorInterface
;
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\Filter\Whitelist
;
use
Irstea\PlantUmlBundle\Model\Namespace_\BundleNamespace
;
use
Irstea\PlantUmlBundle\Model\Namespace_\FlatNamespace
;
use
Irstea\PlantUmlBundle\Model\Namespace_\MappedNamespace
;
use
Irstea\PlantUmlBundle\Model\Namespace_\Php\RootNamespace
;
use
Irstea\PlantUmlBundle\Model\NamespaceInterface
;
use
Irstea\PlantUmlBundle\Writer\OutputWriter
;
use
ReflectionClass
;
use
Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand
;
use
Symfony\Component\Config\Definition\Exception\Exception
;
use
Symfony\Component\Console\Input\InputArgument
;
use
Symfony\Component\Console\Input\InputInterface
;
use
Symfony\Component\Console\Output\OutputInterface
;
use
Symfony\Component\Finder\Finder
;
use
Symfony\Component\Finder\SplFileInfo
;
use
Symfony\Component\HttpKernel\KernelInterface
;
use
Symfony\Component\Security\Core\Exception\InvalidArgumentException
;
use
Symfony\Component\Security\Core\Exception\RuntimeException
;
/**
...
...
@@ -32,13 +44,39 @@ use Symfony\Component\Console\Output\OutputInterface;
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class
EntitySchema
Command
extends
ContainerAwareCommand
class
Generate
Command
extends
ContainerAwareCommand
{
/**
* @var string[]
*/
private
$bundles
;
/**
* @var KernelInterface
*/
private
$kernel
;
/**
* @var EntityManagerInterface
*/
private
$entityManager
;
protected
function
configure
()
{
$this
->
setName
(
'irstea:plantuml:entities'
)
->
setDescription
(
"Génère un schéma PlantUML à partir des métadonnées de Doctrine."
);
->
setName
(
'irstea:plantuml:generate'
)
->
setDescription
(
"Génère un graphe en PlantUML."
)
->
addArgument
(
'graph'
,
InputArgument
::
REQUIRED
,
'Nom du graphe à générer'
);
}
protected
function
initialize
(
InputInterface
$input
,
OutputInterface
$output
)
{
parent
::
initialize
(
$input
,
$output
);
// @todo: DI
$this
->
bundles
=
$this
->
getContainer
()
->
getParameter
(
'kernel.bundles'
);
$this
->
kernel
=
$this
->
getContainer
()
->
get
(
'kernel'
);
$this
->
entityManager
=
$this
->
getContainer
()
->
get
(
'doctrine.orm.entity_manager'
);
}
/**
...
...
@@ -50,41 +88,29 @@ class EntitySchemaCommand extends ContainerAwareCommand
*/
protected
function
execute
(
InputInterface
$input
,
OutputInterface
$output
)
{
/* @var $manager EntityManagerInterface */
$
manager
=
$this
->
getContainer
()
->
get
(
'doctrine.orm.entity_manager
'
);
$factory
=
$manager
->
getMetadataFactory
(
);
$allMetadata
=
$factory
->
getAllMetadata
();
$name
=
$input
->
getArgument
(
'graph'
);
$
graphs
=
$this
->
getContainer
()
->
get
Parameter
(
'irstea_plant_uml.graphs
'
);
if
(
!
isset
(
$graphs
[
$name
]))
{
throw
new
InvalidArgumentException
(
"Le graphe '
$name
' n'est pas défini."
);
}
$decorationFilter
=
new
DirectoryFilter
(
[
realpath
(
$this
->
getContainer
()
->
getParameter
(
'kernel.root_dir'
)
.
'/../src'
)
]
);
$config
=
$graphs
[
$name
];
$bundleNamespace
=
'Irstea\\SygadeBundle\\Entity\\'
;
$classes
=
$this
->
findClasses
(
$config
[
'sources'
]);
if
(
empty
(
$classes
))
{
$output
->
writeln
(
"Nothing to analyze."
);
return
0
;
}
$entityFilter
=
new
NamespaceFilter
([
$bundleNamespace
]);
$namespace
=
$this
->
buildNamespace
(
$config
[
'layout'
][
'namespaces'
]);
$displayFilter
=
$this
->
buildFilter
(
$config
[
'layout'
]);
//$namespace = new BundleNamespace($this->getContainer()->getParameter('kernel.bundles'));
$namespace
=
new
MappedNamespace
([
$bundleNamespace
=>
''
]);
$decorator
=
$this
->
buildDecorator
(
$config
[
'decoration'
]);
$decorator
=
new
FilteringDecorator
(
new
CompositeDecorator
([
new
InheritanceDecorator
(),
new
EntityDecorator
(
$factory
),
new
AssociationDecorator
(
$factory
),
]),
$decorationFilter
);
$visitor
=
new
ClassVisitor
(
$decorator
,
$displayFilter
,
$namespace
);
$visitor
=
new
ClassVisitor
(
$decorator
,
null
,
$namespace
);
foreach
(
$allMetadata
as
$metadata
)
{
/* @var $metadata ClassMetadata */
if
(
$entityFilter
->
accept
(
$metadata
->
getReflectionClass
()))
{
$visitor
->
visitClass
(
$metadata
->
getName
());
}
foreach
(
$classes
as
$class
)
{
$visitor
->
visitClass
(
$class
);
}
$writer
=
new
OutputWriter
(
$output
);
...
...
@@ -92,4 +118,223 @@ class EntitySchemaCommand extends ContainerAwareCommand
$visitor
->
outputTo
(
$writer
);
$writer
->
write
(
"@enduml
\n
"
);
}
/**
* @param ReflectionClass[]
*/
protected
function
findClasses
(
array
$config
)
{
switch
(
$config
[
'type'
])
{
case
'entities'
:
$classes
=
$this
->
findEntities
(
$config
[
'entity_manager'
]);
break
;
case
'classes'
:
$classes
=
$this
->
findClassesInDirectories
(
$config
[
'directories'
]);
break
;
}
$filter
=
$this
->
buildFilter
(
$config
);
if
(
$filter
)
{
return
array_filter
(
$classes
,
[
$filter
,
'accept'
]);
}
return
$classes
;
}
/**
* @return ReflectionClass[]
*/
protected
function
findEntities
(
$managerName
)
{
$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 ReflectionClass[]
*/
protected
function
findClassesInDirectories
(
$directories
)
{
$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
;
}
/**
* @param array $config
* @return DecoratorInterface
* @throws RuntimeException
*/
protected
function
buildDecorator
(
array
$config
)
{
$decorators
=
[];
foreach
(
$config
[
'decorators'
]
as
$decorator
)
{
switch
(
$decorator
)
{
case
'inheritance'
:
$decorators
[]
=
new
InheritanceDecorator
();
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
(
$decorators
))
{
return
NullDecorator
::
instance
();
}
if
(
count
(
$decorators
)
===
1
)
{
$decorator
=
$decorators
[
0
];
}
else
{
$decorator
=
new
CompositeDecorator
(
$decorators
);
}
$filter
=
$this
->
buildFilter
(
$config
);
if
(
$filter
)
{
$decorator
=
new
FilteringDecorator
(
$decorator
,
$filter
);
}
return
$decorator
;
}
/**
* @param string $config
* @return NamespaceInterface
*/
protected
function
buildNamespace
(
$config
)
{
switch
(
$config
)
{
case
'php'
:
return
new
RootNamespace
();
case
'flat'
:
return
new
FlatNamespace
();
case
'entities'
:
return
new
DoctrineNamespace
(
$this
->
entityManager
->
getConfiguration
()
->
getEntityNamespaces
());
case
'bundles'
:
return
new
BundleNamespace
(
$this
->
bundles
);
}
}
/**
* @param array $config
* @return ClassFilterInterface|null
*/
protected
function
buildFilter
(
array
$config
)
{
$filters
=
array_merge
(
isset
(
$config
[
'include'
])
?
$this
->
buildSubFilters
(
$config
[
'include'
],
false
)
:
[],
isset
(
$config
[
'exclude'
])
?
$this
->
buildSubFilters
(
$config
[
'exclude'
],
true
)
:
[]
);
switch
(
count
(
$filters
))
{
case
0
:
return
null
;
case
1
:
return
$filters
[
0
];
default
:
return
new
AllFilter
(
$filters
);
}
}
/**
* @param array $config
* @param boolean $notFound
* @return ClassFilterInterface|null
*/
protected
function
buildSubFilters
(
array
$config
,
$notFound
)
{
$filters
=
[];
if
(
!
empty
(
$config
[
'directories'
]))
{
$paths
=
$this
->
parseDirectories
(
$config
[
'directories'
]);
$filters
[]
=
new
DirectoryFilter
(
$paths
,
$notFound
);
}
if
(
!
empty
(
$config
[
'namespaces'
]))
{
$namespaces
=
$this
->
parseNamespaces
(
$config
[
'namespaces'
]);
$filters
[]
=
new
NamespaceFilter
(
$namespaces
,
$notFound
);
}
return
$filters
;
}
/**
* @param array $paths
* @return array
*/
protected
function
parseDirectories
(
array
$paths
)
{
$actualPaths
=
[];
foreach
(
$paths
as
$path
)
{
if
(
preg_match
(
'/^@(\w+)(.*)$/'
,
$path
,
$groups
))
{
$bundle
=
$this
->
kernel
->
getBundle
(
$groups
[
1
]);
$path
=
$bundle
->
getPath
()
.
$groups
[
2
];
}
$actualPaths
[]
=
realpath
(
$path
);
}
return
$actualPaths
;
}
/**
* @param array $paths
* @return array
*/
protected
function
parseNamespaces
(
array
$namespaces
)
{
$actualNamespaces
=
[];
foreach
(
$namespaces
as
$namespace
)
{
if
(
preg_match
(
'/^@(\w+)(.*)$/'
,
$namespace
,
$groups
))
{
$bundle
=
$this
->
kernel
->
getBundle
(
$groups
[
1
]);
$namespace
=
$bundle
->
getNamespace
()
.
$groups
[
2
];
}
$actualNamespaces
[]
=
$namespace
;
}
return
$actualNamespaces
;
}
}
function
irstea_plantmul_include
(
$filepath
)
{
@
include_once
(
$filepath
);
}
\ No newline at end of file
DependencyInjection/Configuration.php
0 → 100644
View file @
93e12c5d
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace
Irstea\PlantUmlBundle\DependencyInjection
;
use
Symfony\Component\Config\Definition\Builder\TreeBuilder
;
use
Symfony\Component\Config\Definition\ConfigurationInterface
;
/**
* Description of Configuration
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class
Configuration
implements
ConfigurationInterface
{
public
function
getConfigTreeBuilder
()
{
$treeBuilder
=
new
TreeBuilder
();
$treeBuilder
->
root
(
'irstea_plant_uml'
)
->
children
()
->
arrayNode
(
'binaries'
)
->
addDefaultsIfNotSet
()
->
children
()
->
scalarNode
(
'java'
)
->
defaultValue
(
"java"
)
->
end
()
->
scalarNode
(
'plamtuml_jar'
)
->
defaultValue
(
"plantuml.jar"
)
->
end
()
->
scalarNode
(
'dot'
)
->
defaultValue
(
"dot"
)
->
end
()
->
end
()
->
end
()
->
append
(
$this
->
buildGraphNode
())
->
end
();
return
$treeBuilder
;
}
protected
function
buildGraphNode
()
{
$node
=
(
new
TreeBuilder
())
->
root
(
'graphs'
);
$node
->
useAttributeAsKey
(
'name'
)
->
prototype
(
'array'
)
->
children
()
->
arrayNode
(
'sources'
)
->
addDefaultsIfNotSet
()
->
children
()
->
enumNode
(
'type'
)
->
defaultValue
(
'classes'
)
->
values
([
'classes'
,
'entities'
])
->
end
()
->
scalarNode
(
'entity_manager'
)
->
defaultValue
(
'default'
)
->
end
()
->
arrayNode
(
'directories'
)
->
defaultValue
([
'%kernel.root_dir%/../src'
])
->
prototype
(
'scalar'
)
->
end
()
->
end
()
->
append
(
$this
->
buildFilterNode
(
'include'
))
->
append
(
$this
->
buildFilterNode
(
'exclude'
))
->
end
()
->
end
()
->
arrayNode
(
'layout'
)
->
addDefaultsIfNotSet
()
->
children
()
->
enumNode
(
'namespaces'
)
->
defaultValue
(
'php'
)
->
values
([
'bundles'
,
'php'
,
'flat'
,
'entities'
])
->
end
()
->
append
(
$this
->
buildFilterNode
(
'include'
))
->
append
(
$this
->
buildFilterNode
(
'exclude'
))
->
end
()
->
end
()
->
arrayNode
(
'decoration'
)
->
addDefaultsIfNotSet
()
->
children
()
->
arrayNode
(
'decorators'
)
->
defaultValue
([
'inheritance'
,
'entity'
,
'associations'
,
'properties'
,
'methods'
])
->
prototype
(
'enum'
)
->
values
([
'inheritance'
,
'entity'
,
'associations'
,
'properties'
,
'methods'
])
->
end
()
->
end
()
->
append
(
$this
->
buildFilterNode
(
'include'
))
->
append
(
$this
->
buildFilterNode
(
'exclude'
))
->
end
()
->
end
()
->
end
()
->
end
();
return
$node
;
}
protected
function
buildFilterNode
(
$nodeName
)
{
$node
=
(
new
TreeBuilder
())
->
root
(
$nodeName
);
$node
->
children
()
->
arrayNode
(
'directories'
)
->
prototype
(
'scalar'
)
->
end
()
->
end
()
->
arrayNode
(
'namespaces'
)
->
prototype
(
'scalar'
)
->
end
()
->
end
()
->
end
();
return
$node
;
}
}
DependencyInjection/IrsteaPlantUmlExtension.php
0 → 100644
View file @
93e12c5d
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace
Irstea\PlantUmlBundle\DependencyInjection
;
use
Symfony\Component\DependencyInjection\ContainerBuilder
;
use
Symfony\Component\HttpKernel\DependencyInjection\Extension
;
/**
* Description of IrsteaPlantUmlExtension
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class
IrsteaPlantUmlExtension
extends
Extension
{
public
function
load
(
array
$configs
,
ContainerBuilder
$container
)
{
$configuration
=
new
Configuration
();
$config
=
$this
->
processConfiguration
(
$configuration
,
$configs
);
$container
->
setParameter
(
'irstea_plant_uml.binaries'
,
$config
[
'binaries'
]);
$container
->
setParameter
(
'irstea_plant_uml.graphs'
,
$config
[
'graphs'
]);
}
}
Model/ClassVisitor.php
View file @
93e12c5d
...
...
@@ -70,15 +70,21 @@ class ClassVisitor implements ClassVisitorInterface, WritableInterface
return
$this
;
}
public
function
visitClass
(
$className
)
public
function
visitClass
(
$class
Or
Name
)
{
assert
(
'is_string($className)'
,
$className
);
if
(
$classOrName
instanceof
\
ReflectionClass
)
{
$class
=
$classOrName
;
}
elseif
(
is_string
(
$classOrName
))
{
$class
=
new
\
ReflectionClass
(
$classOrName
);
}
else
{
throw
new
\
InvalidArgumentException
(
"Invalid argument, expected ReflectionClass or string"
);
}
$className
=
$class
->
getName
();
if
(
isset
(
$this
->
nodes
[
$className
]))
{
return
$this
->
nodes
[
$className
];
}
$class
=
new
ReflectionClass
(
$className
);
if
(
!
$this
->
filter
->
accept
(
$class
))
{
return
$this
->
nodes
[
$className
]
=
false
;
}
...
...
Model/ClassVisitorInterface.php
View file @
93e12c5d
...
...
@@ -8,6 +8,8 @@
namespace
Irstea\PlantUmlBundle\Model
;
use
ReflectionClass
;
/**
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
...
...
@@ -15,7 +17,8 @@ namespace Irstea\PlantUmlBundle\Model;
interface
ClassVisitorInterface
{
/**
* @param ReflectionClass|string
* @return NodeInterface
*/
public
function
visitClass
(
$className
);
public
function
visitClass
(
$class
Or
Name
);
}
Model/Filter/Composite/AbstractCompositeFilter.php
0 → 100644
View file @
93e12c5d
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace
Irstea\PlantUmlBundle\Model\Filter\Composite
;
use
Irstea\PlantUmlBundle\Model\ClassFilterInterface
;
use
ReflectionClass
;
/**
* Description of CompositeFilter
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
abstract
class
AbstractCompositeFilter
implements
ClassFilterInterface
{
/**
* @var ClassFilterInterface
*/
protected
$filters
;
public
function
__construct
(
array
$filters
)
{
$this
->
filters
=
$filters
;
}
}
Model/Filter/Composite/AllFilter.php
0 → 100644
View file @
93e12c5d
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace
Irstea\PlantUmlBundle\Model\Filter\Composite
;
/**
* Description of AnyFilter
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class
AllFilter
extends
AbstractCompositeFilter
{
public
function
accept
(
\
ReflectionClass
$class
)
{
foreach
(
$this
->
filters
as
$filter
)
{
if
(
!
$filter
->
accept
(
$class
))
{
return
false
;
}
}
<