File: //usr/local/wp/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/TurnipPatternPolicy.php
<?php
/*
* This file is part of the Behat.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Behat\Behat\Definition\Pattern\Policy;
use Behat\Behat\Definition\Pattern\Pattern;
use Behat\Behat\Definition\Exception\InvalidPatternException;
use Behat\Transliterator\Transliterator;
/**
* Defines a way to handle turnip patterns.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
final class TurnipPatternPolicy implements PatternPolicy
{
const TOKEN_REGEX = "[\"']?(?P<%s>(?<=\")[^\"]*(?=\")|(?<=')[^']*(?=')|\-?[\w\.\,]+)['\"]?";
const PLACEHOLDER_REGEXP = "/\\\:(\w+)/";
const OPTIONAL_WORD_REGEXP = '/(\s)?\\\\\(([^\\\]+)\\\\\)(\s)?/';
const ALTERNATIVE_WORD_REGEXP = '/(\w+)\\\\\/(\w+)/';
/**
* @var string[]
*/
private $regexCache = array();
/**
* @var string[]
*/
private static $placeholderPatterns = array(
"/(?<!\w)\"[^\"]+\"(?!\w)/",
"/(?<!\w)'[^']+'(?!\w)/",
"/(?<!\w|\.|\,)\-?\d+(?:[\.\,]\d+)?(?!\w|\.|\,)/"
);
/**
* {@inheritdoc}
*/
public function supportsPatternType($type)
{
return null === $type || 'turnip' === $type;
}
/**
* {@inheritdoc}
*/
public function generatePattern($stepText)
{
$count = 0;
$pattern = $stepText;
foreach (self::$placeholderPatterns as $replacePattern) {
$pattern = preg_replace_callback(
$replacePattern,
function () use (&$count) { return ':arg' . ++$count; },
$pattern
);
}
$pattern = $this->escapeAlternationSyntax($pattern);
$canonicalText = $this->generateCanonicalText($stepText);
return new Pattern($canonicalText, $pattern, $count);
}
/**
* {@inheritdoc}
*/
public function supportsPattern($pattern)
{
return true;
}
/**
* {@inheritdoc}
*/
public function transformPatternToRegex($pattern)
{
if (!isset($this->regexCache[$pattern])) {
$this->regexCache[$pattern] = $this->createTransformedRegex($pattern);
}
return $this->regexCache[$pattern];
}
/**
* @param string $pattern
* @return string
*/
private function createTransformedRegex($pattern)
{
$regex = preg_quote($pattern, '/');
$regex = $this->replaceTokensWithRegexCaptureGroups($regex);
$regex = $this->replaceTurnipOptionalEndingWithRegex($regex);
$regex = $this->replaceTurnipAlternativeWordsWithRegex($regex);
return '/^' . $regex . '$/iu';
}
/**
* Generates canonical text for step text.
*
* @param string $stepText
*
* @return string
*/
private function generateCanonicalText($stepText)
{
$canonicalText = preg_replace(self::$placeholderPatterns, '', $stepText);
$canonicalText = Transliterator::transliterate($canonicalText, ' ');
$canonicalText = preg_replace('/[^a-zA-Z\_\ ]/', '', $canonicalText);
$canonicalText = str_replace(' ', '', ucwords($canonicalText));
return $canonicalText;
}
/**
* Replaces turnip tokens with regex capture groups.
*
* @param string $regex
*
* @return string
*/
private function replaceTokensWithRegexCaptureGroups($regex)
{
$tokenRegex = self::TOKEN_REGEX;
return preg_replace_callback(
self::PLACEHOLDER_REGEXP,
array($this, 'replaceTokenWithRegexCaptureGroup'),
$regex
);
}
private function replaceTokenWithRegexCaptureGroup($tokenMatch)
{
if (strlen($tokenMatch[1]) >= 32) {
throw new InvalidPatternException(
"Token name should not exceed 32 characters, but `{$tokenMatch[1]}` was used."
);
}
return sprintf(self::TOKEN_REGEX, $tokenMatch[1]);
}
/**
* Replaces turnip optional ending with regex non-capturing optional group.
*
* @param string $regex
*
* @return string
*/
private function replaceTurnipOptionalEndingWithRegex($regex)
{
return preg_replace(self::OPTIONAL_WORD_REGEXP, '(?:\1)?(?:\2)?(?:\3)?', $regex);
}
/**
* Replaces turnip alternative words with regex non-capturing alternating group.
*
* @param string $regex
*
* @return string
*/
private function replaceTurnipAlternativeWordsWithRegex($regex)
{
$regex = preg_replace(self::ALTERNATIVE_WORD_REGEXP, '(?:\1|\2)', $regex);
$regex = $this->removeEscapingOfAlternationSyntax($regex);
return $regex;
}
/**
* Adds escaping to alternation syntax in pattern.
*
* By default, Turnip treats `/` as alternation syntax. Meaning `one/two` for Turnip
* means either `one` or `two`. Sometimes though you'll want to use slash character
* with different purpose (URL, UNIX paths). In this case, you would escape slashes
* with backslash.
*
* This method adds escaping to all slashes in generated snippets.
*
* @param string $pattern
*
* @return string
*/
private function escapeAlternationSyntax($pattern)
{
return str_replace('/', '\/', $pattern);
}
/**
* Removes escaping of alternation syntax from regex.
*
* This method removes those escaping backslashes from your slashes, so your steps
* could be matched against your escaped definitions.
*
* @param string $regex
*
* @return string
*/
private function removeEscapingOfAlternationSyntax($regex)
{
return str_replace('\\\/', '/', $regex);
}
}