behat/gherkin
behat/gherkin is a PHP library for parsing the Gherkin language used in BDD. Read and tokenize feature files, build an AST, and integrate with Behat or other test runners to execute human-readable scenarios in your test suite.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require behat/gherkin
Add to composer.json if using Laravel's autoloader:
"autoload": {
"psr-4": {
"App\\": "app/",
"Behat\\Gherkin\\": "vendor/behat/gherkin/src/"
}
}
First Use Case:
Parse a .feature file in a Laravel command or service:
use Behat\Gherkin\Parser;
use Behat\Gherkin\Lexer;
use Behat\Gherkin\Keywords\ArrayKeywords;
$keywords = new ArrayKeywords(['en' => [...]]); // Default English keywords
$lexer = new Lexer($keywords);
$parser = new Parser($lexer);
$featureContent = file_get_contents('features/user_login.feature');
$featureNode = $parser->parse($featureContent);
// Access parsed data (e.g., scenarios, steps)
foreach ($featureNode->getChildren() as $child) {
if ($child instanceof \Behat\Gherkin\Node\ScenarioNode) {
echo "Scenario: " . $child->getTitle() . "\n";
}
}
Where to Look First:
Given, When, Then).FeatureNode object.\Behat\Gherkin\Node (e.g., FeatureNode, ScenarioNode, StepNode) for parsed data structure.ArrayKeywords or DialectProviderInterface (v4.15+).Workflow:
storage/app/features/ or resources/features/).Lexer with language keywords (default: English).FeatureNode:
$parser = new Parser(new Lexer($keywords));
$featureNode = $parser->parse(file_get_contents($path));
$scenarios = $featureNode->getChildrenOfType(\Behat\Gherkin\Node\ScenarioNode::class);
foreach ($scenarios as $scenario) {
$steps = $scenario->getSteps();
foreach ($steps as $step) {
$text = $step->getText(); // e.g., "Click the login button"
$keyword = $step->getKeyword(); // e.g., "Given"
}
}
Integration with Laravel:
$cacheKey = 'gherkin.feature.' . md5($path);
$featureNode = Cache::remember($cacheKey, now()->addHours(1), function () use ($parser, $path) {
return $parser->parse(file_get_contents($path));
});
Default Keywords:
$keywords = new ArrayKeywords([
'en' => [
'feature' => 'Feature',
'given' => 'Given',
// ... other keywords
],
'es' => [
'feature' => 'CaracterÃstica',
'given' => 'Dado',
]
]);
Dynamic Language Selection:
$language = request()->header('Accept-Language') ?? 'en';
$keywords = new ArrayKeywords([$language => [...]]);
DialectProviderInterface (v4.15+):
For advanced use cases, implement DialectProviderInterface to load keywords dynamically:
use Behat\Gherkin\Dialect\DialectProviderInterface;
class CustomDialectProvider implements DialectProviderInterface {
public function getDialect(string $language): array {
return [
'Feature' => 'CustomFeature',
'Given' => 'CustomGiven',
];
}
}
Extract Step Text:
$stepText = $stepNode->getFullText(); // Includes keyword (e.g., "Given I am on the homepage")
$stepText = $stepNode->getText(); // Only the step text (e.g., "I am on the homepage")
Table/Argument Parsing:
if ($stepNode->hasArgument()) {
$argument = $stepNode->getArgument();
if ($argument instanceof \Behat\Gherkin\Node\TableNode) {
$rows = $argument->getRows();
foreach ($rows as $row) {
$cells = $row->getCells();
}
}
}
Example Scenarios:
$examples = $scenarioOutline->getExamples();
foreach ($examples as $example) {
$table = $example->getTable();
$rows = $table->getRows();
}
try {
$featureNode = $parser->parse($content);
} catch (\Behat\Gherkin\Exception\ParserException $e) {
Log::error("Gherkin parse error: " . $e->getMessage());
throw new \RuntimeException("Invalid feature file syntax", 0, $e);
}
GherkinCompatibilityMode (v4.15+) to enforce strict parsing:
$parser = new Parser($lexer, new \Behat\Gherkin\GherkinCompatibilityMode('gherkin-32'));
$cache = new \Behat\Gherkin\Loader\FileLoader(
new \Behat\Gherkin\Loader\CachingLoader(
new \Behat\Gherkin\Loader\FileLoader(new \Behat\Gherkin\Lexer($keywords)),
new \Symfony\Component\Cache\Adapter\FilesystemAdapter()
)
);
$featureNode = $cache->load($path);
$this->app->singleton('feature.parser', function () {
return new Parser(new Lexer(new ArrayKeywords(['en' => [...]))));
});
Language Mismatches:
# language: xyz), the parser will fall back to English (v4.15+). To enforce strict parsing:
$parser = new Parser($lexer, null, true); // $strictLanguage = true
gherkin-lint.Deprecated Syntax:
@ (e.g., wip&&~slow) are deprecated (v4.16.1). Use @wip&&~@slow instead.GherkinCompatibilityMode::LEGACY temporarily.Background Placement:
Background section after the first Scenario will throw a ParserException (v4.14+).Empty Feature Files:
Feature: keyword will trigger a ParserException (v4.14+)..feature files start with Feature:.Table Parsing Quirks:
| in tables is ignored (v4.14+), but malformed tables may still cause issues.gherkin-lint to validate syntax.Inspect Tokens:
Use the Lexer directly to debug tokenization:
$lexer = new Lexer($keywords);
$tokens = $lexer->tokenize(file_get_contents('feature.feature'));
print_r($tokens);
Node Structure:
Dump the parsed FeatureNode to understand its structure:
use Symfony\Component\VarDumper\Cloner\VarCloner;
(new VarCloner())->dump($featureNode);
**
How can I help you explore Laravel packages today?