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\Context\Suite\Setup;
13 use Behat\Behat\Context\ContextClass\ClassGenerator;
14 use Behat\Behat\Context\Exception\ContextNotFoundException;
15 use Behat\Testwork\Filesystem\FilesystemLogger;
16 use Behat\Testwork\Suite\Exception\SuiteConfigurationException;
17 use Behat\Testwork\Suite\Setup\SuiteSetup;
18 use Behat\Testwork\Suite\Suite;
19 use Symfony\Component\ClassLoader\ClassLoader;
22 * Generates classes for all contexts in the suite using autoloader.
24 * @author Konstantin Kudryashov <ever.zet@gmail.com>
26 final class SuiteWithContextsSetup implements SuiteSetup
33 * @var null|FilesystemLogger
37 * @var ClassGenerator[]
39 private $classGenerators = array();
44 * @param ClassLoader $autoloader
45 * @param null|FilesystemLogger $logger
47 public function __construct(ClassLoader $autoloader, FilesystemLogger $logger = null)
49 $this->autoloader = $autoloader;
50 $this->logger = $logger;
54 * Registers class generator.
56 * @param ClassGenerator $generator
58 public function registerClassGenerator(ClassGenerator $generator)
60 $this->classGenerators[] = $generator;
66 public function supportsSuite(Suite $suite)
68 return $suite->hasSetting('contexts');
74 public function setupSuite(Suite $suite)
76 foreach ($this->getNormalizedContextClasses($suite) as $class) {
77 if (class_exists($class)) {
81 $this->ensureContextDirectory($path = $this->findClassFile($class));
83 if ($content = $this->generateClass($suite, $class)) {
84 $this->createContextFile($path, $content);
90 * Returns normalized context classes.
96 private function getNormalizedContextClasses(Suite $suite)
100 return is_array($context) ? current(array_keys($context)) : $context;
102 $this->getSuiteContexts($suite)
107 * Returns array of context classes configured for the provided suite.
109 * @param Suite $suite
113 * @throws SuiteConfigurationException If `contexts` setting is not an array
115 private function getSuiteContexts(Suite $suite)
117 $contexts = $suite->getSetting('contexts');
119 if (!is_array($contexts)) {
120 throw new SuiteConfigurationException(
121 sprintf('`contexts` setting of the "%s" suite is expected to be an array, `%s` given.',
133 * Creates context directory in the filesystem.
135 * @param string $path
137 private function createContextDirectory($path)
139 mkdir($path, 0777, true);
142 $this->logger->directoryCreated($path, 'place your context classes here');
147 * Creates context class file in the filesystem.
149 * @param string $path
150 * @param string $content
152 private function createContextFile($path, $content)
154 file_put_contents($path, $content);
157 $this->logger->fileCreated($path, 'place your definitions, transformations and hooks here');
162 * Finds file to store a class.
164 * @param string $class
168 * @throws ContextNotFoundException If class file could not be determined
170 private function findClassFile($class)
172 list($classpath, $classname) = $this->findClasspathAndClass($class);
173 $classpath .= str_replace('_', DIRECTORY_SEPARATOR, $classname) . '.php';
175 foreach ($this->autoloader->getPrefixes() as $prefix => $dirs) {
176 if (0 === strpos($class, $prefix)) {
177 return current($dirs) . DIRECTORY_SEPARATOR . $classpath;
181 if ($dirs = $this->autoloader->getFallbackDirs()) {
182 return current($dirs) . DIRECTORY_SEPARATOR . $classpath;
185 throw new ContextNotFoundException(sprintf(
186 'Could not find where to put "%s" class. Have you configured autoloader properly?',
192 * Generates class using registered class generators.
194 * @param Suite $suite
195 * @param string $class
197 * @return null|string
199 private function generateClass(Suite $suite, $class)
202 foreach ($this->classGenerators as $generator) {
203 if ($generator->supportsSuiteAndClass($suite, $class)) {
204 $content = $generator->generateClass($suite, $class);
212 * Ensures that directory for a classpath exists.
214 * @param string $classpath
216 private function ensureContextDirectory($classpath)
218 if (!is_dir(dirname($classpath))) {
219 $this->createContextDirectory(dirname($classpath));
224 * Finds classpath and classname from class.
226 * @param string $class
230 private function findClasspathAndClass($class)
232 if (false !== $pos = strrpos($class, '\\')) {
233 // namespaced class name
234 $classpath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR;
235 $classname = substr($class, $pos + 1);
237 return array($classpath, $classname);
240 // PEAR-like class name
244 return array($classpath, $classname);