Skip to content
This repository has been archived by the owner on Dec 11, 2020. It is now read-only.

Commit

Permalink
Merge pull request #836 from fzaninotto/valid_modifier
Browse files Browse the repository at this point in the history
[RFR] Introducting the valid() modifier
  • Loading branch information
fzaninotto committed Feb 23, 2016
2 parents 3179e10 + 9a06c09 commit f03cdfd
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 5 deletions.
24 changes: 21 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Faker requires PHP >= 5.3.3.
- [Barcode](#fakerproviderbarcode)
- [Miscellaneous](#fakerprovidermiscellaneous)
- [Biased](#fakerproviderbiased)
- [Unique and Optional modifiers](#unique-and-optional-modifiers)
- [Modifiers](#unique-and-optional-modifiers)
- [Localization](#localization)
- [Populating Entities Using an ORM or an ODM](#populating-entities-using-an-orm-or-an-odm)
- [Seeding the Generator](#seeding-the-generator)
Expand Down Expand Up @@ -292,9 +292,9 @@ Each of the generator properties (like `name`, `address`, and `lorem`) are calle
// with more chances to be close to 20
biasedNumberBetween($min = 10, $max = 20, $function = 'sqrt')

## Unique and Optional modifiers
## Modifiers

Faker provides two special providers, `unique()` and `optional()`, to be called before any provider. `optional()` can be useful for seeding non-required fields, like a mobile telephone number; `unique()` is required to populate fields that cannot accept twice the same value, like primary identifiers.
Faker provides three special providers, `unique()`, `optional()`, and `valid()`, to be called before any provider.

```php
// unique() forces providers to return unique values
Expand Down Expand Up @@ -336,6 +336,24 @@ $faker->optional($weight = 0.9)->randomDigit; // 10% chance of NULL
// Defaults to NULL.
$faker->optional($weight = 0.5, $default = false)->randomDigit; // 50% chance of FALSE
$faker->optional($weight = 0.9, $default = 'abc')->word; // 10% chance of 'abc'

// valid() only accepts valid values according to the passed validator functions
$values = array();
$evenValidator = function($digit) {
return $digit % 2 === 0;
};
for ($i=0; $i < 10; $i++) {
$values []= $faker->valid($evenValidator)->randomDigit;
}
print_r($values); // [0, 4, 8, 4, 2, 6, 0, 8, 8, 6]

// just like unique(), valid() throws an overflow exception when it can't generate a valid value
$values = array();
try {
$faker->valid($evenValidator)->randomElement(1, 3, 5, 7, 9);
} catch (\OverflowException $e) {
echo "Can't pick an even number in that set!";
}
```

## Localization
Expand Down
31 changes: 30 additions & 1 deletion src/Faker/Provider/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Faker\Generator;
use Faker\DefaultGenerator;
use Faker\UniqueGenerator;
use Faker\ValidGenerator;

class Base
{
Expand Down Expand Up @@ -519,7 +520,7 @@ public function optional($weight = 0.5, $default = null)
if ($weight > 0 && $weight < 1 && mt_rand() / mt_getrandmax() <= $weight) {
return $this->generator;
}

// new system with percentage
if (is_int($weight) && mt_rand(1, 100) <= $weight) {
return $this->generator;
Expand Down Expand Up @@ -551,4 +552,32 @@ public function unique($reset = false, $maxRetries = 10000)

return $this->unique;
}

/**
* Chainable method for forcing any formatter to return only valid values.
*
* The value validity is determined by a function passed as first argument.
*
* <code>
* $values = array();
* $evenValidator = function ($digit) {
* return $digit % 2 === 0;
* };
* for ($i=0; $i < 10; $i++) {
* $values []= $faker->valid($evenValidator)->randomDigit;
* }
* print_r($values); // [0, 4, 8, 4, 2, 6, 0, 8, 8, 6]
* </code>
*
* @param Closure $validator A function returning true for valid values
* @param integer $maxRetries Maximum number of retries to find a unique value,
* After which an OverflowException is thrown.
* @throws \OverflowException When no valid value can be found by iterating $maxRetries times
*
* @return ValidGenerator A proxy class returning only valid values
*/
public function valid($validator = null, $maxRetries = 10000)
{
return new ValidGenerator($this->generator, $validator, $maxRetries);
}
}
59 changes: 59 additions & 0 deletions src/Faker/ValidGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Faker;

/**
* Proxy for other generators, to return only valid values. Works with
* Faker\Generator\Base->valid()
*/
class ValidGenerator
{
protected $generator;
protected $validator;
protected $maxRetries;

/**
* @param Generator $generator
*/
public function __construct(Generator $generator, $validator = null, $maxRetries = 10000)
{
if (is_null($validator)) {
$validator = function () {
return true;
};
} elseif (!is_callable($validator)) {
throw new \InvalidArgumentException('valid() only accepts callables as first argument');
}
$this->generator = $generator;
$this->validator = $validator;
$this->maxRetries = $maxRetries;
}

/**
* Catch and proxy all generator calls but return only valid values
* @param string $attribute
*/
public function __get($attribute)
{
return $this->__call($attribute, array());
}

/**
* Catch and proxy all generator calls with arguments but return only valid values
* @param string $name
* @param array $arguments
*/
public function __call($name, $arguments)
{
$i = 0;
do {
$res = call_user_func_array(array($this->generator, $name), $arguments);
$i++;
if ($i > $this->maxRetries) {
throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries));
}
} while (!call_user_func($this->validator, $res));

return $res;
}
}
55 changes: 55 additions & 0 deletions test/Faker/Provider/BaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,61 @@ public function testUniqueCanResetUniquesWhenPassedTrueAsArgument()
$this->assertEquals(array(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9), $values);
}

public function testValidAllowsChainingPropertyAccess()
{
$faker = new \Faker\Generator();
$faker->addProvider(new \Faker\Provider\Base($faker));
$this->assertLessThan(10, $faker->valid()->randomDigit);
}

public function testValidAllowsChainingMethodCall()
{
$faker = new \Faker\Generator();
$faker->addProvider(new \Faker\Provider\Base($faker));
$this->assertLessThan(10, $faker->valid()->numberBetween(5, 9));
}

public function testValidReturnsOnlyValidValues()
{
$faker = new \Faker\Generator();
$faker->addProvider(new \Faker\Provider\Base($faker));
$values = array();
$evenValidator = function($digit) {
return $digit % 2 === 0;
};
for ($i=0; $i < 50; $i++) {
$values[$faker->valid($evenValidator)->randomDigit] = true;
}
$uniqueValues = array_keys($values);
sort($uniqueValues);
$this->assertEquals(array(0, 2, 4, 6, 8), $uniqueValues);
}

/**
* @expectedException OverflowException
*/
public function testValidThrowsExceptionWhenNoValidValueCanBeGenerated()
{
$faker = new \Faker\Generator();
$faker->addProvider(new \Faker\Provider\Base($faker));
$evenValidator = function($digit) {
return $digit % 2 === 0;
};
for ($i=0; $i < 11; $i++) {
$faker->valid($evenValidator)->randomElement(array(1, 3, 5, 7, 9));
}
}

/**
* @expectedException InvalidArgumentException
*/
public function testValidThrowsExceptionWhenParameterIsNotCollable()
{
$faker = new \Faker\Generator();
$faker->addProvider(new \Faker\Provider\Base($faker));
$faker->valid(12)->randomElement(array(1, 3, 5, 7, 9));
}

/**
* @expectedException LengthException
* @expectedExceptionMessage Cannot get 2 elements, only 1 in array
Expand Down
2 changes: 1 addition & 1 deletion test/Faker/Provider/ColorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function testRgbCssColor()
public function testRgbaCssColor()
{
$regexp = '([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])';
$regexpAlpha = '([01]?\.[0-9])';
$regexpAlpha = '([01]?(\.\d+)?)';
$this->assertRegExp('/^rgba\(' . $regexp . ',' . $regexp . ',' . $regexp . ',' . $regexpAlpha . '\)$/i', Color::rgbaCssColor());
}

Expand Down

0 comments on commit f03cdfd

Please sign in to comment.