Skip to content

Commit 6f55f14

Browse files
committed
Initial import
1 parent c677199 commit 6f55f14

21 files changed

+2224
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
app/bootstrap*
33

44
# Symfony directories
5+
.idea
56
vendor/*
67
*/logs/*
78
*/cache/*

Command/ConvertCommand.php

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?php
2+
namespace Tuck\ConverterBundle\Command;
3+
4+
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
5+
use Symfony\Component\Console\Input\InputArgument;
6+
use Symfony\Component\Console\Input\InputInterface;
7+
use Symfony\Component\Console\Input\InputOption;
8+
use Symfony\Component\Console\Output\OutputInterface;
9+
use \SplFileInfo;
10+
use Symfony\Component\HttpKernel\Bundle\Bundle;
11+
use Tuck\ConverterBundle\Exception\UnknownBundleException;
12+
use Tuck\ConverterBundle\Exception\EmptyDirectoryException;
13+
14+
/**
15+
* Converts a service config file to another format.
16+
*
17+
* @author Ross Tuck <[email protected]>
18+
*/
19+
class ConvertCommand extends ContainerAwareCommand
20+
{
21+
protected function configure()
22+
{
23+
$this
24+
->setName('container:convert')
25+
->setDescription('Convert a service container config from one format to another')
26+
->addArgument('format', InputArgument::REQUIRED, 'The format to convert to (yml, xml, graphviz, php)')
27+
->addArgument('file', InputArgument::OPTIONAL, 'Source file to convert')
28+
->addOption('output', 'o', InputOption::VALUE_NONE, 'Echo the new config instead of writing it to a file');
29+
}
30+
31+
/**
32+
* Interactively determine the file and convert it
33+
*
34+
* @param InputInterface $input
35+
* @param OutputInterface $output
36+
* @return void
37+
*/
38+
protected function execute(InputInterface $input, OutputInterface $output)
39+
{
40+
$fileToConvert = $this->determineFile($output, $input->getArgument('file'));
41+
$newFormat = $input->getArgument('format');
42+
43+
$newConfig = $this
44+
->getContainer()
45+
->get('tuck_converter.config_format_converter')
46+
->convertFile($fileToConvert, $newFormat);
47+
48+
if ($input->getOption('output')) {
49+
$output->write($newConfig);
50+
} else {
51+
$this->writeToFile($output, $fileToConvert, $newConfig, $newFormat);
52+
}
53+
}
54+
55+
/**
56+
* Choose file from cli input or interactively
57+
*
58+
* @param OutputInterface $output
59+
* @param string $givenFileName
60+
* @return SplFileInfo
61+
*/
62+
protected function determineFile(OutputInterface $output, $givenFileName)
63+
{
64+
if (is_file($givenFileName)) {
65+
return new \SplFileInfo($givenFileName);
66+
}
67+
68+
return $this->chooseFileInDirectory(
69+
$output,
70+
$this->chooseBundle($output)->getPath().'/Resources/config/'
71+
);
72+
}
73+
74+
/**
75+
* Prompt the user to choose the bundle whose config they want to convert
76+
*
77+
* @param OutputInterface $output
78+
* @return Bundle
79+
* @throws UnknownBundleException
80+
*/
81+
protected function chooseBundle(OutputInterface $output)
82+
{
83+
$bundles = $this->getContainer()->get('kernel')->getBundles();
84+
85+
$selectedBundle = $this->getHelperSet()->get('dialog')->ask(
86+
$output,
87+
'<info>Which bundle\'s config would you like to convert? </info>',
88+
null,
89+
array_keys($bundles)
90+
);
91+
92+
if (!isset($bundles[$selectedBundle])) {
93+
throw UnknownBundleException::create($selectedBundle);
94+
}
95+
96+
return $bundles[$selectedBundle];
97+
}
98+
99+
/**
100+
*
101+
* @param OutputInterface $output
102+
* @param string $path
103+
* @return SplFileInfo
104+
* @throws EmptyDirectoryException
105+
*/
106+
protected function chooseFileInDirectory(OutputInterface $output, $path)
107+
{
108+
// Gather files in directory
109+
$files = array();
110+
foreach (new \FilesystemIterator($path) as $file) {
111+
$files[] = $file;
112+
}
113+
114+
// Handle edge cases
115+
if (count($files) === 0) {
116+
throw EmptyDirectoryException::create($path);
117+
} elseif (count($files) === 1) {
118+
return current($files);
119+
}
120+
121+
// Prompt user for which file
122+
$fileIndex = $this->getHelperSet()->get('dialog')->select(
123+
$output,
124+
'<info>Which file should be converted? </info>',
125+
array_map(
126+
function ($fileInfo) {
127+
return $fileInfo->getFilename();
128+
},
129+
$files
130+
)
131+
);
132+
133+
return $files[$fileIndex];
134+
}
135+
136+
/**
137+
* Write the new config to the same directory as the original file
138+
*
139+
* @param OutputInterface $output
140+
* @param SplFileInfo $originalFile
141+
* @param string $newConfig
142+
* @param string $newFormat The short file name given, like
143+
*/
144+
protected function writeToFile(OutputInterface $output, SplFileInfo $originalFile, $newConfig, $newFormat)
145+
{
146+
$proposedLocation = $originalFile->getPath().'/services.'.$newFormat;
147+
$location = $this->getHelperSet()->get('dialog')->ask(
148+
$output,
149+
"<info>Where should the new config be written?</info> [{$proposedLocation}] ",
150+
$proposedLocation
151+
);
152+
153+
if (file_exists($location)
154+
&& !$this->getHelperSet()->get('dialog')->askConfirmation($output, 'File exists. Overwrite? ')
155+
) {
156+
$output->writeln('Aborting...');
157+
158+
return;
159+
}
160+
161+
file_put_contents($location, $newConfig);
162+
$output->writeln(
163+
"Written! Don't forget to update the DependencyInjection/*Extension class in your bundle to use the new ".
164+
"loader class and delete the old config file."
165+
);
166+
}
167+
}

ConfigFormatConverter.php

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
namespace Tuck\ConverterBundle;
3+
4+
use SplFileInfo;
5+
use Symfony\Component\DependencyInjection\ContainerBuilder;
6+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
7+
use Tuck\ConverterBundle\Dumper\DumperFactory;
8+
use Tuck\ConverterBundle\Exception\UnknownFileException;
9+
use Tuck\ConverterBundle\Loader\LoaderFactory;
10+
11+
/**
12+
* Converts a services config file to another format.
13+
*
14+
* Future changes:
15+
* This class exposes the file part rather heavily because that's how it is
16+
* in practice but also because that's how the internal loader and dumpers
17+
* work. Still, this could be used to build a site based converter, perhaps
18+
* with an addition to the interface or the temp stream.
19+
*
20+
* This would probably be improved by refactoring it to return an actual
21+
* ConfigFile object, then we could break the coupling with file extensions
22+
* in the UI.
23+
*
24+
* @author Ross Tuck <[email protected]>
25+
*/
26+
class ConfigFormatConverter
27+
{
28+
/**
29+
* @var LoaderFactory
30+
*/
31+
protected $loaderFactory;
32+
33+
/**
34+
* @var DumperFactory
35+
*/
36+
protected $dumperFactory;
37+
38+
public function __construct(LoaderFactory $loaderFactory, DumperFactory $dumperFactory)
39+
{
40+
$this->loaderFactory = $loaderFactory;
41+
$this->dumperFactory = $dumperFactory;
42+
}
43+
44+
public function convertFile(SplFileInfo $file, $newFormat)
45+
{
46+
if (!$file->isFile()) {
47+
throw UnknownFileException::create($file->getRealPath());
48+
}
49+
50+
$container = new ContainerBuilder(new ParameterBag());
51+
52+
$loader = $this->loaderFactory->createFileLoader($file->getExtension(), $container, $file->getPath());
53+
$loader->load($file->getFilename());
54+
55+
return $this->dumperFactory->createDumper($newFormat, $container)->dump();
56+
}
57+
58+
//TODO: convertString - could be convertFile using the temp stream?
59+
}

DependencyInjection/Configuration.php

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Tuck\ConverterBundle\DependencyInjection;
4+
5+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
6+
use Symfony\Component\Config\Definition\ConfigurationInterface;
7+
8+
/**
9+
* This is the class that validates and merges configuration from your app/config files
10+
*
11+
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
12+
*/
13+
class Configuration implements ConfigurationInterface
14+
{
15+
/**
16+
* {@inheritDoc}
17+
*/
18+
public function getConfigTreeBuilder()
19+
{
20+
$treeBuilder = new TreeBuilder();
21+
$rootNode = $treeBuilder->root('tuck_converter');
22+
23+
// Here you should define the parameters that are allowed to
24+
// configure your bundle. See the documentation linked above for
25+
// more information on that topic.
26+
return $treeBuilder;
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Tuck\ConverterBundle\DependencyInjection;
4+
5+
use Symfony\Component\DependencyInjection\ContainerBuilder;
6+
use Symfony\Component\Config\FileLocator;
7+
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
8+
use Symfony\Component\DependencyInjection\Loader;
9+
10+
/**
11+
* This is the class that loads and manages your bundle configuration
12+
*
13+
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
14+
*/
15+
class TuckConverterExtension extends Extension
16+
{
17+
/**<?php
18+
* {@inheritDoc}
19+
*/
20+
public function load(array $configs, ContainerBuilder $container)
21+
{
22+
$configuration = new Configuration();
23+
$config = $this->processConfiguration($configuration, $configs);
24+
25+
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
26+
$loader->load('services.xml');
27+
}
28+
}

Dumper/DumperFactory.php

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
namespace Tuck\ConverterBundle\Dumper;
3+
4+
use Symfony\Component\DependencyInjection\ContainerBuilder;
5+
use Symfony\Component\DependencyInjection\Dumper\DumperInterface;
6+
use Tuck\ConverterBundle\Exception\UnknownFormatException;
7+
8+
/**
9+
* Create a config dumper for different formats
10+
*
11+
* @author Ross Tuck <[email protected]>
12+
*/
13+
class DumperFactory
14+
{
15+
/**
16+
* @var array
17+
*/
18+
protected $shortTypeToClassMapping = array(
19+
'xml' => 'Symfony\Component\DependencyInjection\Dumper\XmlDumper',
20+
'yaml' => 'Symfony\Component\DependencyInjection\Dumper\YamlDumper',
21+
'yml' => 'Symfony\Component\DependencyInjection\Dumper\YamlDumper',
22+
'php' => 'Symfony\Component\DependencyInjection\Dumper\PhpDumper',
23+
'gv' => 'Symfony\Component\DependencyInjection\Dumper\GraphvizDumper',
24+
);
25+
26+
/**
27+
* Creates a dumper for use only by the given container
28+
*
29+
* @param string $type
30+
* @param ContainerBuilder $container
31+
* @return DumperInterface
32+
*/
33+
public function createDumper($type, ContainerBuilder $container)
34+
{
35+
$className = $this->getClassNameByShortType($type);
36+
37+
return new $className($container);
38+
}
39+
40+
/**
41+
* Get class name based on format's short name
42+
* @param $type
43+
* @return mixed
44+
* @throws UnknownFormatException
45+
*/
46+
protected function getClassNameByShortType($type)
47+
{
48+
if (!isset($this->shortTypeToClassMapping[$type])) {
49+
throw UnknownFormatException::create($type);
50+
}
51+
52+
return $this->shortTypeToClassMapping[$type];
53+
}
54+
}

Exception/EmptyDirectoryException.php

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
namespace Tuck\ConverterBundle\Exception;
3+
4+
/**
5+
* Thrown when no files were found in a specified directory
6+
* @author Ross Tuck <[email protected]>
7+
*/
8+
class EmptyDirectoryException extends \Exception
9+
{
10+
/**
11+
* Factory function to format message
12+
* @param string $path
13+
* @return self
14+
*/
15+
public static function create($path)
16+
{
17+
return new static("No files were found in directory '{$path}'");
18+
}
19+
}

Exception/UnknownBundleException.php

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
namespace Tuck\ConverterBundle\Exception;
3+
4+
/**
5+
* Thrown when a user specifies a missing bundle
6+
* @author Ross Tuck <[email protected]>
7+
*/
8+
class UnknownBundleException extends \Exception
9+
{
10+
/**
11+
* Factory that formats the message
12+
* @param $bundleName
13+
* @return self
14+
*/
15+
public static function create($bundleName)
16+
{
17+
return new static("Bundle '{$bundleName}' does not exist.");
18+
}
19+
}

0 commit comments

Comments
 (0)