diff --git a/src/Faker/Provider/Base.php b/src/Faker/Provider/Base.php index d16f8b5ca6..9c7aefe2c6 100644 --- a/src/Faker/Provider/Base.php +++ b/src/Faker/Provider/Base.php @@ -317,6 +317,19 @@ public static function shuffleString($string = '', $encoding = 'UTF-8') return join('', static::shuffleArray($array)); } + private static function replaceWildcard($string, $wildcard = '#', $callback = 'static::randomDigit') + { + if (($pos = strpos($string, $wildcard)) === false) { + return $string; + } + for ($i = $pos, $last = strrpos($string, $wildcard, $pos) + 1; $i < $last; $i++) { + if ($string[$i] === $wildcard) { + $string[$i] = call_user_func($callback); + } + } + return $string; + } + /** * Replaces all hash sign ('#') occurrences with a random number * Replaces all percentage sign ('%') occurrences with a not null number @@ -329,9 +342,11 @@ public static function numerify($string = '###') // instead of using randomDigit() several times, which is slow, // count the number of hashes and generate once a large number $toReplace = array(); - for ($i = 0, $count = strlen($string); $i < $count; $i++) { - if ($string[$i] === '#') { - $toReplace []= $i; + if (($pos = strpos($string, '#')) !== false) { + for ($i = $pos, $last = strrpos($string, '#', $pos) + 1; $i < $last; $i++) { + if ($string[$i] === '#') { + $toReplace[] = $i; + } } } if ($nbReplacements = count($toReplace)) { @@ -347,7 +362,7 @@ public static function numerify($string = '###') $string[$toReplace[$i]] = $numbers[$i]; } } - $string = preg_replace_callback('/\%/u', 'static::randomDigitNotNull', $string); + $string = self::replaceWildcard($string, '%', 'static::randomDigitNotNull'); return $string; } @@ -360,17 +375,21 @@ public static function numerify($string = '###') */ public static function lexify($string = '????') { - return preg_replace_callback('/\?/u', 'static::randomLetter', $string); + return self::replaceWildcard($string, '?', 'static::randomLetter'); } /** - * Replaces hash signs and question marks with random numbers and letters + * Replaces hash signs ('#') and question marks ('?') with random numbers and letters + * An asterisk ('*') is replaced with either a random number or a random letter * * @param string $string String that needs to bet parsed * @return string */ public static function bothify($string = '## ??') { + $string = self::replaceWildcard($string, '*', function () { + return mt_rand(0, 1) ? '#' : '?'; + }); return static::lexify(static::numerify($string)); } diff --git a/test/Faker/Provider/BaseTest.php b/test/Faker/Provider/BaseTest.php index a426dc62d7..4af392965c 100644 --- a/test/Faker/Provider/BaseTest.php +++ b/test/Faker/Provider/BaseTest.php @@ -276,6 +276,17 @@ public function testBothifyCombinesNumerifyAndLexify() $this->assertRegExp('/foo[a-z]Ba\dr/', BaseProvider::bothify('foo?Ba#r')); } + public function testBothifyAsterisk() + { + $this->assertRegExp('/foo([a-z]|\d)Ba([a-z]|\d)r/', BaseProvider::bothify('foo*Ba*r')); + } + + public function testBothifyUtf() + { + $utf = 'œ∑´®†¥¨ˆøπ“‘和製╯°□°╯︵ ┻━┻🐵 🙈 ﺚﻣ ﻦﻔﺳ ﺲﻘﻄﺗ ﻮﺑﺎﻠﺘﺣﺪﻳﺩ،, ﺝﺰﻳﺮﺘﻳ ﺏﺎﺴﺘﺧﺩﺎﻣ ﺄﻧ ﺪﻧﻭ. ﺇﺫ ﻪﻧﺍ؟ ﺎﻠﺴﺗﺍﺭ ﻮﺘ'; + $this->assertRegExp('/'.$utf.'foo\dB[a-z]a([a-z]|\d)r/u', BaseProvider::bothify($utf.'foo#B?a*r')); + } + public function testAsciifyReturnsSameStringWhenItContainsNoStarSign() { $this->assertEquals('fooBar?', BaseProvider::asciify('fooBar?'));