4 * This file is part of the Behat.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
11 namespace Behat\Behat\Gherkin\ServiceContainer;
13 use Behat\Testwork\Cli\ServiceContainer\CliExtension;
14 use Behat\Testwork\Filesystem\ServiceContainer\FilesystemExtension;
15 use Behat\Testwork\ServiceContainer\Exception\ExtensionException;
16 use Behat\Testwork\ServiceContainer\Extension;
17 use Behat\Testwork\ServiceContainer\ExtensionManager;
18 use Behat\Testwork\ServiceContainer\ServiceProcessor;
19 use Behat\Testwork\Specification\ServiceContainer\SpecificationExtension;
20 use Behat\Testwork\Suite\ServiceContainer\SuiteExtension;
21 use Behat\Testwork\Translator\ServiceContainer\TranslatorExtension;
23 use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
24 use Symfony\Component\DependencyInjection\ContainerBuilder;
25 use Symfony\Component\DependencyInjection\Definition;
26 use Symfony\Component\DependencyInjection\Reference;
29 * Extends Behat with gherkin suites and features.
31 * @author Konstantin Kudryashov <ever.zet@gmail.com>
33 final class GherkinExtension implements Extension
38 const MANAGER_ID = 'gherkin';
39 const KEYWORDS_DUMPER_ID = 'gherkin.keywords_dumper';
40 const KEYWORDS_ID = 'gherkin.keywords';
43 * Available extension points
45 const LOADER_TAG = 'gherkin.loader';
48 * @var ServiceProcessor
53 * Initializes extension.
55 * @param null|ServiceProcessor $processor
57 public function __construct(ServiceProcessor $processor = null)
59 $this->processor = $processor ? : new ServiceProcessor();
65 public function getConfigKey()
73 public function initialize(ExtensionManager $extensionManager)
80 public function configure(ArrayNodeDefinition $builder)
83 ->addDefaultsIfNotSet()
86 ->info('Sets the gherkin parser cache folder')
88 is_writable(sys_get_temp_dir())
89 ? sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'behat_gherkin_cache'
93 ->arrayNode('filters')
94 ->info('Sets the gherkin filters (overridable by CLI options)')
95 ->performNoDeepMerging()
96 ->defaultValue(array())
97 ->useAttributeAsKey('name')
98 ->prototype('scalar')->end()
107 public function load(ContainerBuilder $container, array $config)
109 $this->loadParameters($container);
110 $this->loadGherkin($container);
111 $this->loadKeywords($container);
112 $this->loadParser($container);
113 $this->loadDefaultLoaders($container, $config['cache']);
114 $this->loadProfileFilters($container, $config['filters']);
115 $this->loadSyntaxController($container);
116 $this->loadFilterController($container);
117 $this->loadSuiteWithPathsSetup($container);
118 $this->loadFilesystemFeatureLocator($container);
119 $this->loadFilesystemScenariosListLocator($container);
120 $this->loadFilesystemRerunScenariosListLocator($container);
126 public function process(ContainerBuilder $container)
128 $this->processLoaders($container);
132 * Loads default container parameters.
134 * @param ContainerBuilder $container
136 private function loadParameters(ContainerBuilder $container)
138 $container->setParameter('gherkin.paths.lib', $this->getLibPath());
139 $container->setParameter('gherkin.paths.i18n', '%gherkin.paths.lib%/i18n.php');
140 $container->setParameter(
141 'suite.generic.default_settings',
143 'paths' => array('%paths.base%/features'),
144 'contexts' => array('FeatureContext')
150 * Returns gherkin library path.
154 private function getLibPath()
156 $reflection = new ReflectionClass('Behat\Gherkin\Gherkin');
157 $libPath = rtrim(dirname($reflection->getFilename()) . '/../../../', DIRECTORY_SEPARATOR);
163 * Loads gherkin service.
165 * @param ContainerBuilder $container
167 private function loadGherkin(ContainerBuilder $container)
169 $definition = new Definition('Behat\Gherkin\Gherkin');
170 $container->setDefinition(self::MANAGER_ID, $definition);
174 * Loads keyword services.
176 * @param ContainerBuilder $container
178 private function loadKeywords(ContainerBuilder $container)
180 $definition = new Definition('Behat\Gherkin\Keywords\CachedArrayKeywords', array(
181 '%gherkin.paths.i18n%'
183 $container->setDefinition(self::KEYWORDS_ID, $definition);
185 $definition = new Definition('Behat\Gherkin\Keywords\KeywordsDumper', array(
186 new Reference(self::KEYWORDS_ID)
188 $container->setDefinition(self::KEYWORDS_DUMPER_ID, $definition);
192 * Loads gherkin parser.
194 * @param ContainerBuilder $container
196 private function loadParser(ContainerBuilder $container)
198 $definition = new Definition('Behat\Gherkin\Parser', array(
199 new Reference('gherkin.lexer')
201 $container->setDefinition('gherkin.parser', $definition);
203 $definition = new Definition('Behat\Gherkin\Lexer', array(
204 new Reference('gherkin.keywords')
206 $container->setDefinition('gherkin.lexer', $definition);
210 * Loads gherkin loaders.
212 * @param ContainerBuilder $container
213 * @param string $cachePath
215 private function loadDefaultLoaders(ContainerBuilder $container, $cachePath)
217 $definition = new Definition('Behat\Gherkin\Loader\GherkinFileLoader', array(
218 new Reference('gherkin.parser')
222 $cacheDefinition = new Definition('Behat\Gherkin\Cache\FileCache', array($cachePath));
224 $cacheDefinition = new Definition('Behat\Gherkin\Cache\MemoryCache');
227 $definition->addMethodCall('setCache', array($cacheDefinition));
228 $definition->addMethodCall('setBasePath', array('%paths.base%'));
229 $definition->addTag(self::LOADER_TAG, array('priority' => 50));
230 $container->setDefinition('gherkin.loader.gherkin_file', $definition);
234 * Loads profile-level gherkin filters.
236 * @param ContainerBuilder $container
237 * @param array $filters
239 private function loadProfileFilters(ContainerBuilder $container, array $filters)
241 $gherkin = $container->getDefinition(self::MANAGER_ID);
242 foreach ($filters as $type => $filterString) {
243 $filter = $this->createFilterDefinition($type, $filterString);
244 $gherkin->addMethodCall('addFilter', array($filter));
249 * Loads syntax controller.
251 * @param ContainerBuilder $container
253 private function loadSyntaxController(ContainerBuilder $container)
255 $definition = new Definition('Behat\Behat\Gherkin\Cli\SyntaxController', array(
256 new Reference(self::KEYWORDS_DUMPER_ID),
257 new Reference(TranslatorExtension::TRANSLATOR_ID)
259 $definition->addTag(CliExtension::CONTROLLER_TAG, array('priority' => 600));
260 $container->setDefinition(CliExtension::CONTROLLER_TAG . '.gherkin_syntax', $definition);
264 * Loads filter controller.
266 * @param ContainerBuilder $container
268 private function loadFilterController(ContainerBuilder $container)
270 $definition = new Definition('Behat\Behat\Gherkin\Cli\FilterController', array(
271 new Reference(self::MANAGER_ID)
273 $definition->addTag(CliExtension::CONTROLLER_TAG, array('priority' => 700));
274 $container->setDefinition(CliExtension::CONTROLLER_TAG . '.gherkin_filters', $definition);
278 * Loads suite with paths setup.
280 * @param ContainerBuilder $container
282 private function loadSuiteWithPathsSetup(ContainerBuilder $container)
284 $definition = new Definition('Behat\Behat\Gherkin\Suite\Setup\SuiteWithPathsSetup', array(
286 new Reference(FilesystemExtension::LOGGER_ID)
288 $definition->addTag(SuiteExtension::SETUP_TAG, array('priority' => 50));
289 $container->setDefinition(SuiteExtension::SETUP_TAG . '.suite_with_paths', $definition);
293 * Loads filesystem feature locator.
295 * @param ContainerBuilder $container
297 private function loadFilesystemFeatureLocator(ContainerBuilder $container)
299 $definition = new Definition('Behat\Behat\Gherkin\Specification\Locator\FilesystemFeatureLocator', array(
300 new Reference(self::MANAGER_ID),
303 $definition->addTag(SpecificationExtension::LOCATOR_TAG, array('priority' => 60));
304 $container->setDefinition(SpecificationExtension::LOCATOR_TAG . '.filesystem_feature', $definition);
308 * Loads filesystem scenarios list locator.
310 * @param ContainerBuilder $container
312 private function loadFilesystemScenariosListLocator(ContainerBuilder $container)
314 $definition = new Definition('Behat\Behat\Gherkin\Specification\Locator\FilesystemScenariosListLocator', array(
315 new Reference(self::MANAGER_ID)
317 $definition->addTag(SpecificationExtension::LOCATOR_TAG, array('priority' => 50));
318 $container->setDefinition(SpecificationExtension::LOCATOR_TAG . '.filesystem_scenarios_list', $definition);
322 * Loads filesystem rerun scenarios list locator.
324 * @param ContainerBuilder $container
326 private function loadFilesystemRerunScenariosListLocator(ContainerBuilder $container)
328 $definition = new Definition('Behat\Behat\Gherkin\Specification\Locator\FilesystemRerunScenariosListLocator', array(
329 new Reference(self::MANAGER_ID)
331 $definition->addTag(SpecificationExtension::LOCATOR_TAG, array('priority' => 50));
332 $container->setDefinition(SpecificationExtension::LOCATOR_TAG . '.filesystem_rerun_scenarios_list', $definition);
336 * Processes all available gherkin loaders.
338 * @param ContainerBuilder $container
340 private function processLoaders(ContainerBuilder $container)
342 $references = $this->processor->findAndSortTaggedServices($container, self::LOADER_TAG);
343 $definition = $container->getDefinition(self::MANAGER_ID);
345 foreach ($references as $reference) {
346 $definition->addMethodCall('addLoader', array($reference));
351 * Creates filter definition of provided type.
353 * @param string $type
354 * @param string $filterString
358 * @throws ExtensionException If filter type is not recognised
360 private function createFilterDefinition($type, $filterString)
362 if ('role' === $type) {
363 return new Definition('Behat\Gherkin\Filter\RoleFilter', array($filterString));
366 if ('name' === $type) {
367 return new Definition('Behat\Gherkin\Filter\NameFilter', array($filterString));
370 if ('tags' === $type) {
371 return new Definition('Behat\Gherkin\Filter\TagFilter', array($filterString));
374 if ('narrative' === $type) {
375 return new Definition('Behat\Gherkin\Filter\NarrativeFilter', array($filterString));
378 throw new ExtensionException(sprintf(
379 '`%s` filter is not supported by the `filters` option of gherkin extension. Supported types are `%s`.',
381 implode('`, `', array('narrative', 'role', 'name', 'tags'))