Skip to content
This repository was archived by the owner on Mar 15, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions Command/GenerateBundleCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace Profideo\GeneratorBundle\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Profideo\GeneratorBundle\Generator\BundleGenerator;
use Profideo\GeneratorBundle\Manipulator\KernelManipulator;
use Sensio\Bundle\GeneratorBundle\Command\GeneratorCommand;
use Symfony\Component\HttpKernel\KernelInterface;

/**
* Generates bundle defined in configuration and enables it in AppKernel.
*/
class GenerateBundleCommand extends GeneratorCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setName('profideo:generate-bundle');
}

/**
* @throws \RuntimeException When bundle can't be executed or not bundle is registered
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$container = $this->getContainer();

$bundle = $container->getParameter('profideo.generator_bundle');

if (empty($bundle)) {
throw new \RuntimeException('There is not bundle registered.');
}

$bundleName = $bundle['class_prefix'].ucfirst($bundle['name']).'Bundle';
$namespace = $bundle['base_namespace'].'\\'.ucfirst($bundle['name']).'Bundle';

$generator = $this->getGenerator();
$generator->setSkeletonDirs([__DIR__.'/../Resources/skeleton']);
$generator->generate($namespace, $bundleName, 'src', null, null, ['parent' => $bundle['parent']]);

$output->writeln("Generating the bundle '$namespace\\$bundleName'".
(!empty($bundle['parent']) ? " as child of '{$bundle['parent']}'" : '').
' : <info>OK</info>');

$this->updateKernel($container->get('kernel'), $bundle['name'], "$namespace\\$bundleName");

$output->writeln("Enabling bundle '$namespace\\$bundleName' in AppKernel and disabling others that are".
" included in '{$bundle['base_namespace']}' : <info>OK</info>");
}

/**
* Add bundle in kernel class.
*
* @param KernelInterface $kernel
* @param string $bundle
*/
protected function updateKernel(KernelInterface $kernel, $name, $bundle)
{
$kernelManipulator = new KernelManipulator($kernel);
$kernelManipulator->addBundle($name, $bundle);
}

/**
* Returns an instance of BundleGenerator.
*
* @return BundleGenerator
*/
protected function createGenerator()
{
return new BundleGenerator($this->getContainer()->get('filesystem'));
}
}
31 changes: 31 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Profideo\GeneratorBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

/**
* @codeCoverageIgnore
*/
class Configuration implements ConfigurationInterface
{
/**
* {@inheritdoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('profideo_generator');

$rootNode
->children()
->scalarNode('name')->isRequired()->end()
->scalarNode('base_namespace')->isRequired()->end()
->scalarNode('parent')->defaultNull()->end()
->scalarNode('class_prefix')->defaultNull()->end()
->end();

return $treeBuilder;
}
}
24 changes: 24 additions & 0 deletions DependencyInjection/ProfideoGeneratorExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Profideo\GeneratorBundle\DependencyInjection;

use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* @codeCoverageIgnore
*/
class ProfideoGeneratorExtension extends Extension
{
/**
* {@inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();

$config = $this->processConfiguration($configuration, $configs);

$container->setParameter('profideo.generator_bundle', $config);
}
}
34 changes: 34 additions & 0 deletions Generator/BundleGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Profideo\GeneratorBundle\Generator;

use Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator as SensioBundleGenerator;

class BundleGenerator extends SensioBundleGenerator
{
/**
* Overloads to generate only bundle class.
* Moreover, parameters can be transmitted to the twig template.
*/
public function generate($namespace, $bundle, $dir, $format, $structure, $parameters = [])
{
$dir .= '/'.strtr($namespace, '\\', '/');

if (file_exists($dir)) {
if (!is_dir($dir)) {
throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" exists but is a file.', realpath($dir)));
}
$files = scandir($dir);
if ($files != ['.', '..']) {
throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" is not empty.', realpath($dir)));
}
if (!is_writable($dir)) {
throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" is not writable.', realpath($dir)));
}
}

$parameters += ['namespace' => $namespace, 'bundle' => $bundle, 'parent' => null];

$this->renderFile('Bundle.php.twig', $dir.'/'.$bundle.'.php', $parameters);
}
}
115 changes: 115 additions & 0 deletions Manipulator/KernelManipulator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

namespace Profideo\GeneratorBundle\Manipulator;

use Symfony\Component\HttpKernel\KernelInterface;
use Sensio\Bundle\GeneratorBundle\Manipulator\Manipulator;

/**
* @codeCoverageIgnore
*/
class KernelManipulator extends Manipulator
{
protected $kernel;
protected $reflected;

/**
* Constructor.
*
* @param KernelInterface $kernel A KernelInterface instance
*/
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
$this->reflected = new \ReflectionObject($kernel);
}

/**
* Add bundle in method getGeneratedBundle of kernel. If method getGeneratedBundle does not exist, it is created.
*/
public function addBundle($name, $class)
{
if (!$this->reflected->getFilename()) {
return false;
}

try {
$method = $this->reflected->getMethod('getGeneratedBundle');
} catch (\ReflectionException $e) {
// getGeneratedBundle method does not exist, creates it and adds new bundle in it
file_put_contents(
$this->reflected->getFilename(),
str_replace(
' public function registerBundles()',
' private function getGeneratedBundle($name)'.PHP_EOL.
' {'.PHP_EOL.
' $bundles = array('.PHP_EOL.
" '$name' => new $class(),".PHP_EOL.
' );'.PHP_EOL.
PHP_EOL.
' return isset($bundles[$name]) ? $bundles[$name] : null;'.PHP_EOL.
' }'.PHP_EOL.
PHP_EOL.
' public function registerBundles()',
file_get_contents($this->reflected->getFilename())
)
);

return true;
}

// Adds new bundle in getGeneratedBundle method

$src = file($this->reflected->getFilename());
$lines = array_slice($src, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1);

// Don't add same bundle twice
if (false !== strpos(implode('', $lines), $class)) {
throw new \RuntimeException(sprintf('Bundle "%s" is already defined in "AppKernel::getGeneratedBundle()".', $class));
}

$this->setCode(token_get_all('<?php '.implode('', $lines)), $method->getStartLine());
while ($token = $this->next()) {
// $bundles
if (T_VARIABLE !== $token[0] || '$bundles' !== $token[1]) {
continue;
}

// =
$this->next();

// array start with traditional or short syntax
$token = $this->next();
if (T_ARRAY !== $token[0] && '[' !== $this->value($token)) {
return false;
}

// add the bundle at the end of the array
while ($token = $this->next()) {
// look for ); or ];
if (')' !== $this->value($token) && ']' !== $this->value($token)) {
continue;
}

if (';' !== $this->value($this->peek())) {
continue;
}

// ;
$this->next();

$lines = array_merge(
array_slice($src, 0, $this->line - 2),
// Appends a separator comma to the current last position of the array
array(rtrim(rtrim($src[$this->line - 2]), ',').','.PHP_EOL),
array(sprintf(" '$name' => new %s(),".PHP_EOL, $class)),
array_slice($src, $this->line - 1)
);

file_put_contents($this->reflected->getFilename(), implode('', $lines));

return true;
}
}
}
}
13 changes: 13 additions & 0 deletions ProfideoGeneratorBundle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Profideo\GeneratorBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class ProfideoGeneratorBundle extends Bundle
{
public function getParent()
{
return 'SensioGeneratorBundle';
}
}
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# generator-bundle

Il permet de générer un bundle à l'aide d'une commande (`./app/console profideo:generate-bundle`) et de les déclarer
dans le noyau de l'application (`./app/AppKernel.php`).

Le bundle doivent être définis dans la configuration de l'application symfony.

D'autres part, un bundle parent peut être défini pour le bundle ainsi qu'un préfixe qui sera ajouté au nom de
la classe du bundle.

La configuration doit être renseignée dans un des fichiers de configuration de l'application. Comme c'est une commande
destinée aux développeurs, le plus judicieux est de le définir dans le fichier `app/config_dev.yml`.

Un exemple de configuration serait :

```
profideo_generator:
name: bar
base_namespace: AcmeBis\Bundles
parent: ParentBisBundle #optionnel
class_prefix: AcmeBis #optionnel
```

La configuration précédente génère l'architecture de bundle suivant :

```
src/
Acme/
Bundles/
FooBundle/
AcmeFooBundle.php
```

avec le contenu suivant dans le fichier `src/FooBundle/AcmeFooBundle.php` :

```
namespace Acme\Bundles\FooBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class AcmeFooBundle extends Bundle
{
public function getParent()
{
return 'ParentBundle';
}
}
```

Après l'exécution de la commande, le bundle est déclaré dans la méthode privée `AppKernel::getGeneratedBundle`. Pour que le bundle généré soit
fonctionnel, il faut le récupérer en appelant cette méthode dans la méthode `AppKernel::registerBundles` et l'ajouter à la variable `$bundles`.
21 changes: 21 additions & 0 deletions Resources/skeleton/Bundle.php.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace {{ namespace }};

{% block use_statements %}
use Symfony\Component\HttpKernel\Bundle\Bundle;
{% endblock use_statements %}

{% block class_definition %}
class {{ bundle }} extends Bundle
{% endblock class_definition %}
{
{% block class_body %}
{% if (parent is not empty) %}
public function getParent()
{
return '{{ parent }}';
}
{% endif %}
{% endblock class_body %}
}
Loading