Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Gherkin Laravel Package

behat/gherkin

Behat's PHP Gherkin parser for reading .feature files, with support for 40+ native languages and a clean, extensible architecture. Parse features, scenarios, backgrounds, outlines, and examples into structured objects for your test suite.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and first look

composer require behat/gherkin

Read these files in order — the package is small and un-frameworked, so the source is the documentation:

  1. src/Parser.php and src/Lexer.php — the two classes you wire together.
  2. src/Node/* — FeatureNode, ScenarioNode, OutlineNode, StepNode, TableNode, PyStringNode (and RuleNode in modern modes). This is your output API.
  3. src/Loader/* — GherkinFileLoader / DirectoryLoader, which wrap a parser and save you writing file discovery.
  4. src/Filter/* — tag/name/line selection, the feature you'll actually use day-to-day.
  5. i18n.php in the package — the bundled 40+ language keyword table. Use it; don't hand-write keywords like the README's toy example.

Minimal working setup

use Behat\Gherkin\Keywords\ArrayKeywords;
use Behat\Gherkin\Lexer;
use Behat\Gherkin\Parser;
use Behat\Gherkin\GherkinCompatibilityMode;

$keywords = new ArrayKeywords(require '/path/to/behat/gherkin/i18n.php');
$lexer    = new Lexer($keywords);
$parser   = new Parser($lexer, GherkinCompatibilityMode::GHERKIN_42); // see Gotchas on modes

$feature = $parser->parse(file_get_contents(base_path('features/login.feature')));

foreach ($feature->getScenarios() as $scenario) {
    echo $scenario->getTitle().' (line '.$scenario->getLine().")\n";
    foreach ($scenario->getSteps() as $step) {
        echo '  '.$step->getKeyword().' '.$step->getText()."\n";
    }
}

First use case: a CI "feature lint" — parse every .feature file in the repo, fail the build on ParserException, and fail on duplicate scenario titles or steps without a matching tag. That gives immediate value without writing a test runner.

Implementation Patterns

Bind one parser/loader in the container. Parsing is pure; construct the Parser once (a singleton binding in a service provider) and reuse it. Use GherkinFileLoader for single files, DirectoryLoader for a features directory (it handles .feature discovery and delegates per file).

Cache parsed ASTs. GherkinFileLoader accepts a cache implementation; Behat\Gherkin\Cache\FileCache with a path under storage/framework/cache (or storage/app/gherkin) avoids re-parsing a large suite on every request/command. Parsing is the slow part, traversal is trivial.

Walk the tree, don't string-match. Everything you need for reporting lives on the nodes: getTitle(), getDescription(), getTags(), getLine(), getKeyword(), getSteps(), getBackground(), getLanguage(). StepNode::getArguments() returns a mixed array of TableNode and PyStringNode (use getRows() / getRaw()), which is exactly what you need to feed data-driven Pest/PHPUnit skeletons.

Source mapping for tooling. getLine() on features/scenarios/steps lets you build IDE jump-to-line links, annotate CI output, or map a failing JUnit test back to a .feature line. Since 4.17, StepNode::getFullText() gives the keyword + text exactly as written, which is what you want when re-rendering a feature for docs.

Expand outlines yourself. OutlineNode::getExampleTables() yields ExampleTableNodes whose getRows() are header-keyed associative arrays. Substitute <placeholder> tokens in step text and docstrings per row — the parser does not pre-expand scenarios for you.

Drive test selection with filters. TagFilter('@wip&&~@slow'), NameFilter('/login/'), and LineFilter/LineRangeFilter let you implement "re-run only the scenarios that failed last time" by storing failing line numbers in a JSON report and feeding them back. Loaders expose filter configuration so filtering happens during load, not after.

Custom dialects. Implement DialectProviderInterface (or an ArrayKeywords array) if your team writes a domain-specific Gherkin dialect. Also useful for pinning specific synonyms (Scenario Template, Scenarios) rather than relying on defaults.

Laravel integration points: an Artisan command (gherkin:lint, gherkin:list), a queued job that generates a Markdown spec document from getTitle()/getDescription()/steps, and a Pest dataset generator that maps each outline example row to a test case.

Gotchas and Tips

  • LEGACY is still the default. Parsing differs meaningfully from cucumber/gherkin in legacy mode: tags are returned without the @ prefix, keyword whitespace and description whitespace are trimmed differently, docstring escapes and table \n literals are not interpreted. If you compare tags against @wip strings, either strip the @ or opt into GHERKIN_32/GHERKIN_42. GHERKIN_42 is the mode to pick for new tooling.
  • Rule nodes silently disappear. In legacy mode (or with callers not updated for Rule support) the tree is rewritten so scenarios appear as direct Feature children, with Rule backgrounds inlined into each scenario. Don't assume your tree shape matches the file.
  • Backgrounds are not prepended to scenarios. ScenarioNode::getSteps() is only the scenario's own steps. Walk up and merge FeatureNode::getBackground() yourself, or you'll execute tests missing their setup.
  • Tag inheritance is your job. Feature tags and scenario tags are separate; merge them explicitly when deciding what to run.
  • Filter expressions changed and are mid-deprecation. Tag expressions without the @ prefix (wip&&~slow) still work but are deprecated since 4.17 and will break in the next major. Whitespace around &&, ||, ~ is ignored now. Multiple filters on one loader are combined (AND semantics) — verify against the docblocks, because this was only documented precisely in 4.18.
  • FilterInterface and SimpleFilter are deprecated as of 4.18. Don't type-hint FilterInterface in new code; use the concrete filters and the loader API, and expect a replacement interface in the next major.
  • Don't extend Lexer, Parser, or the Node classes — deprecated since 4.15. Implement/wrap ParserInterface instead; the BC check in their CI will catch you otherwise.
  • Exceptions moved around. NoSuchLanguageException is wrapped in a ParserException (4.17); invalid multiple table/docstring arguments throw ParserException instead of NodeException (4.18). Catch Behat\Gherkin\Exception\ParserException broadly and inspect line info there, not in node construction.
  • Invalid #language: headers fall back silently in legacy mode (throw in GHERKIN_32). FeatureNode::getLanguage() now returns the language actually used for parsing, not the raw header value — good for diagnostics, confusing if you expected the literal text.
  • Hard parse failures to expect: a file ending with dangling tags, and a Background appearing after the first Scenario. Both raise ParserException; surface line numbers in your CI output.
  • Cache directory concurrency was fixed in 4.15, but only for the built-in FileCache. If you write your own cache, make directory creation atomic and key on file mtime plus the gherkin version.
  • Version pinning matters. PHP 8.5 support and the compatibility modes are recent; if you're on an older Laravel/PHP matrix, pin ^4.14 or lower and don't rely on gherkin-42, getFullText(), or the compat-mode constructor argument.
  • Cheap debugging: dump $feature->getNodeType() for each node during a recursive walk — it's the fastest way to see how your mode actually shaped the tree (especially around Rules, outlines, and examples).
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib