Skip to content

Commit 3128131

Browse files
committed
Merge pull request #17 from robcaw/feature/ConfigurableGenerator
Feature/configurable generator
2 parents e3e1b53 + 6e9c6c5 commit 3128131

12 files changed

Lines changed: 407 additions & 45 deletions

File tree

README.md

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,35 @@ vivait_string_generator:
3131
generators:
3232
string: vivait_generator.generator.string
3333
secure_bytes: vivait_generator.generator.secure_bytes
34+
secure_string: vivait_generator.generator.secure_bytes
35+
```
36+
37+
## Bundled generators
38+
39+
### `StringGenerator`
40+
Generates a random string based on a pool or characters. Defaults:
41+
42+
```php
43+
@Generate(generator="string", options={"length"=8, "chars"="abcdefjhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345567890", "prefix"=""})
3444
```
35-
### Bundled generators
36-
* `StringGenerator` generates a random string based on a pool or characters
37-
* `SecureBytesGenerator` generates a secure random string using the `Symfony\Component\Security\Core\Util\SecureRandom` class
3845

39-
### Custom generators
40-
You can use your own generators by implementing `GeneratorInterface` and defining the generator in the configuration,
41-
using either its service or classname.
4246

43-
## Basic usage
47+
### `SecureBytesGenerator`
48+
Generates a secure random byte string using the `Symfony\Component\Security\Core\Util\SecureRandom` class. Defaults:
49+
50+
```php
51+
@Generate(generator="secure_bytes", options={"length"=8})
52+
```
53+
54+
### `SecureStringGenerator`
55+
Generates a secure random string using [ircmaxell's RandomLib](https://github.com/ircmaxell/RandomLib). The library provides three different strengths of
56+
strings(currently `high` is unavailable), `low` and `medium`. Defaults:
57+
58+
```php
59+
@Generate(generator="secure_string", options={"length"=32, "chars"="", "strength"="medium"})
60+
```
4461

62+
## Usage
4563
Add the `@Generate(generator="generator_name")` annotation to an entity property
4664
(where `generator_name` is the name of a generator defined in the configuration).
4765

@@ -75,24 +93,26 @@ class Api
7593
*/
7694
private $api_key;
7795
```
78-
## Options
7996

80-
### Length
97+
### Options
98+
99+
Generators that implement `ConfigurableGeneratorInterface`, such as the bundled generators have options which can be configured.
100+
101+
To do this, set the options parameter on the annotation:
81102

82-
To change the length of the generated string, add `length` to the annotation.
83103
```php
84-
@Generate(length=4)
85-
```
104+
@Generate(generator="string", options={"length"=32, "chars"="ABCDEFG"})
105+
```
86106

87107
### Callbacks
88108

89109
It's possible to define callbacks on the `Generator` class that you are using.
90-
For example, with the bundled StringGenerator, you may wish to set the character pool.
110+
For example, with the bundled StringGenerator, you may wish to set the a prefix on the generated string
91111

92112
This can be achieved by setting the `callbacks` option. For example:
93113

94114
```php
95-
@Generate(generator="string", callbacks={"setChars"="ABCDEFG"})
115+
@Generate(generator="my_generator", callbacks={"setPrefix"="VIVA_"})
96116
```
97117

98118
Here, `setChars()` is called in the `StringGenerator` class, passing `ABCDEFG` as a parameter.
@@ -104,7 +124,7 @@ It's even possible to set a callback value dynamically:
104124
* @var string
105125
*
106126
* @ORM\Column(name="short_id", type="string", length=255, nullable=false)
107-
* @Generate(generator="string", length=5, callbacks={"setPrefix"="getPrefix"})
127+
* @Generate(generator="string", options={"length"=32}, callbacks={"setPrefix"="getPrefix"})
108128
*/
109129
private $short_id;
110130
@@ -133,3 +153,43 @@ However, by setting `override` to false, only null properties will have a string
133153
```php
134154
@Generate(generator="string", override=false)
135155
```
156+
157+
158+
## Custom generators
159+
You can use your own generators by implementing `GeneratorInterface` and defining the generator in the configuration,
160+
using either its service or classname.
161+
162+
To create configurable generators, implement `ConfigurableGeneratorInterface`. This interface uses
163+
[`Symfony\Component\OptionsResolver\OptionsResolver`](http://symfony.com/doc/current/components/options_resolver.html) to set the generator configuration.
164+
165+
Set default options:
166+
167+
```php
168+
/**
169+
* @param OptionsResolver $resolver
170+
* @return mixed
171+
*/
172+
public function getDefaultOptions(OptionsResolver $resolver)
173+
{
174+
$resolver->setDefaults([
175+
'chars' => $this->chars,
176+
'length' => $this->length,
177+
'prefix' => $this->prefix,
178+
]);
179+
}
180+
```
181+
182+
Do something with options:
183+
184+
```php
185+
/**
186+
* @param array $options
187+
* @return mixed|void
188+
*/
189+
public function setOptions(array $options)
190+
{
191+
$this->chars = $options['chars'];
192+
$this->length = $options['length'];
193+
$this->prefix = $options['prefix'];
194+
}
195+
```

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
"php": ">=5.3.3",
1515
"doctrine/common": "~2.2",
1616
"doctrine/orm": "~2.2",
17-
"symfony/security": "~2.1"
17+
"symfony/security": "~2.1",
18+
"symfony/options-resolver": "~2.1",
19+
"ircmaxell/random-lib": "~1.0"
1820
},
1921
"require-dev": {
2022
"phpspec/phpspec": "~2.0"

spec/Vivait/StringGeneratorBundle/EventListener/GeneratorListenerSpec.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
use Doctrine\ORM\Mapping\ClassMetadata;
1212
use PhpSpec\ObjectBehavior;
1313
use Prophecy\Argument;
14+
use Symfony\Component\OptionsResolver\OptionsResolver;
1415
use Vivait\StringGeneratorBundle\Annotation\GeneratorAnnotation;
1516
use Vivait\StringGeneratorBundle\Generator\StringGenerator;
17+
use Vivait\StringGeneratorBundle\Model\ConfigurableGeneratorInterface;
18+
use Vivait\StringGeneratorBundle\Model\GeneratorInterface;
1619
use Vivait\StringGeneratorBundle\Registry\Registry;
1720

1821
class GeneratorListenerSpec extends ObjectBehavior
@@ -58,7 +61,7 @@ function it_can_get_callback_values_from_annotated_object(StringGenerator $gener
5861
$this->performCallbacks($generator, $annotation, $this->mockEntity);
5962
}
6063

61-
function it_sets_null_properties_if_override_set_to_true(Registry $registry, Reader $reader, LifecycleEventArgs $args, StringGenerator $generator)
64+
function it_sets_null_properties_if_override_set_to_true(Registry $registry, Reader $reader, LifecycleEventArgs $args, GeneratorInterface $generator)
6265
{
6366
$annotation = new GeneratorAnnotation([]);
6467
$annotation->override = false;
@@ -72,7 +75,7 @@ function it_sets_null_properties_if_override_set_to_true(Registry $registry, Rea
7275

7376
$this->prePersist($args);
7477
}
75-
function it_wont_set_non_null_properties_if_override_set_to_true(Reader $reader, LifecycleEventArgs $args, StringGenerator $generator)
78+
function it_wont_set_non_null_properties_if_override_set_to_true(Reader $reader, LifecycleEventArgs $args, GeneratorInterface $generator)
7679
{
7780
$this->mockEntity->setName('Robin');
7881
$annotation = new GeneratorAnnotation([]);
@@ -84,7 +87,7 @@ function it_wont_set_non_null_properties_if_override_set_to_true(Reader $reader,
8487
$this->prePersist($args);
8588
}
8689

87-
function it_generates_a_string_on_a_property(Registry $registry, Reader $reader, LifecycleEventArgs $args, StringGenerator $generator)
90+
function it_generates_a_string_on_a_property(Registry $registry, Reader $reader, LifecycleEventArgs $args, GeneratorInterface $generator)
8891
{
8992
$annotation = new GeneratorAnnotation([]);
9093

@@ -96,6 +99,24 @@ function it_generates_a_string_on_a_property(Registry $registry, Reader $reader,
9699

97100
$this->prePersist($args);
98101
}
102+
103+
function it_can_configure_a_generator(Registry $registry, Reader $reader, LifecycleEventArgs $args, ConfigurableGeneratorInterface $generator)
104+
{
105+
$annotation = new GeneratorAnnotation([]);
106+
107+
$reader->getPropertyAnnotations(Argument::any())->willReturn([$annotation]);
108+
109+
$registry->get(Argument::any())->willReturn($generator);
110+
$generator->generate()->shouldBeCalled();
111+
$generator->setLength(Argument::any())->shouldBeCalled();
112+
113+
$resolver = new OptionsResolver();
114+
115+
$generator->getDefaultOptions($resolver)->shouldBeCalled();
116+
$generator->setOptions(Argument::any())->shouldBeCalled();
117+
118+
$this->prePersist($args);
119+
}
99120
}
100121

101122
class Entity
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
namespace spec\Vivait\StringGeneratorBundle\Generator;
4+
5+
use PhpSpec\ObjectBehavior;
6+
use Prophecy\Argument;
7+
use RandomLib\Factory;
8+
use RandomLib\Generator;
9+
use RandomLib\Mixer;
10+
use SecurityLib\Strength;
11+
12+
class SecureStringGeneratorSpec extends ObjectBehavior
13+
{
14+
15+
/**
16+
* @var Factory
17+
*/
18+
private $factory;
19+
20+
function it_is_initializable()
21+
{
22+
$this->shouldHaveType('Vivait\StringGeneratorBundle\Generator\SecureStringGenerator');
23+
}
24+
25+
function let(Factory $factory, Generator $low, Generator $medium)
26+
{
27+
$factory->getMediumStrengthGenerator()->willReturn($medium);
28+
$factory->getLowStrengthGenerator()->willReturn($low);
29+
$this->beConstructedWith($factory);
30+
31+
$defaults = [
32+
'length' => 32,
33+
'chars' => '',
34+
'strength' => 'medium',
35+
];
36+
37+
$this->setOptions($defaults);
38+
}
39+
40+
function it_chooses_medium_strength_gen_by_default(Generator $medium)
41+
{
42+
$this->getGenerator()->shouldReturn($medium);
43+
}
44+
45+
function it_chooses_a_specified_strength_gen(Generator $medium, Generator $low)
46+
{
47+
$this->getGenerator('medium')->shouldReturn($medium);
48+
$this->getGenerator('low')->shouldReturn($low);
49+
}
50+
51+
function it_errors_on_invalid_strength_gen(Generator $medium, Generator $low)
52+
{
53+
$this->getGenerator('medium')->shouldReturn($medium);
54+
$this->getGenerator('low')->shouldReturn($low);
55+
56+
$this->shouldThrow('\InvalidArgumentException')->duringGetGenerator('high');
57+
$this->shouldThrow('\InvalidArgumentException')->duringGetGenerator('invalid');
58+
}
59+
60+
function it_generates_a_random_string(Generator $medium)
61+
{
62+
$medium->generateString(32, '')->shouldBeCalled();
63+
$this->generate();
64+
}
65+
66+
function it_generates_a_random_string_of_set_length(Generator $medium, Generator $low)
67+
{
68+
$options = [
69+
'length' => 8,
70+
'chars' => '',
71+
'strength' => 'low',
72+
];
73+
$this->setOptions($options);
74+
75+
$medium->generateString(8, '')->shouldNotBeCalled();
76+
$low->generateString(8, '')->shouldBeCalled();
77+
78+
$this->generate();
79+
}
80+
81+
function getMatchers()
82+
{
83+
return [
84+
'haveStrlen' => function($string, $length){
85+
return strlen($string) == $length;
86+
}
87+
];
88+
}
89+
}

spec/Vivait/StringGeneratorBundle/Generator/StringGeneratorSpec.php

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,6 @@ function it_generates_a_string_based_on_length()
1818
$this->generate()->shouldHaveStrlen(10);
1919
}
2020

21-
function it_should_throw_an_exception_if_it_has_no_chars()
22-
{
23-
$this->shouldThrow('\OutOfBoundsException')->duringSetChars('');
24-
$this->shouldThrow('\OutOfBoundsException')->duringSetChars(null);
25-
$this->shouldThrow('\OutOfBoundsException')->duringSetChars(true);
26-
}
27-
28-
function it_should_error_if_its_length_is_set_to_less_than_1()
29-
{
30-
$this->shouldThrow('\OutOfBoundsException')->duringSetLength(0);
31-
$this->shouldThrow('\OutOfBoundsException')->duringSetLength(-1);
32-
$this->shouldThrow('\OutOfBoundsException')->duringSetLength("ten");
33-
}
34-
3521
function getMatchers()
3622
{
3723
return [

src/Vivait/StringGeneratorBundle/Annotation/GeneratorAnnotation.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,16 @@
1212
*/
1313
class GeneratorAnnotation extends Annotation
1414
{
15+
/**
16+
* @var array
17+
*/
1518
public $callbacks = [];
19+
20+
/**
21+
* @var array
22+
*/
23+
public $options = [];
24+
1625
/**
1726
* @var
1827
*/
@@ -28,5 +37,8 @@ class GeneratorAnnotation extends Annotation
2837
*/
2938
public $unique = true;
3039

40+
/**
41+
* @var bool
42+
*/
3143
public $override = true;
3244
}

src/Vivait/StringGeneratorBundle/EventListener/GeneratorListener.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
use Doctrine\ORM\EntityRepository;
77
use Doctrine\ORM\Event\LifecycleEventArgs;
88
use Symfony\Component\DependencyInjection\ContainerInterface;
9+
use Symfony\Component\OptionsResolver\OptionsResolver;
910
use Vivait\StringGeneratorBundle\Annotation\GeneratorAnnotation;
11+
use Vivait\StringGeneratorBundle\Model\ConfigurableGeneratorInterface;
1012
use Vivait\StringGeneratorBundle\Model\GeneratorInterface;
1113
use Vivait\StringGeneratorBundle\Registry\Registry;
1214

@@ -88,7 +90,7 @@ public function prePersist(LifecycleEventArgs $args)
8890
*/
8991
private function generateString($property, GeneratorAnnotation $annotation, $object)
9092
{
91-
/** @var GeneratorInterface $generator */
93+
/** @var GeneratorInterface|ConfigurableGeneratorInterface $generator */
9294
$generator = $this->getRegistry()->get($annotation->generator);
9395

9496
$generator->setLength($annotation->length);
@@ -97,6 +99,10 @@ private function generateString($property, GeneratorAnnotation $annotation, $obj
9799
$this->performCallbacks($generator, $annotation, $object);
98100
}
99101

102+
if($generator instanceof ConfigurableGeneratorInterface){
103+
$generator = $this->configureGenerator($generator, $annotation->options);
104+
}
105+
100106
$str = $generator->generate();
101107

102108
if (!$annotation->unique) {
@@ -110,6 +116,22 @@ private function generateString($property, GeneratorAnnotation $annotation, $obj
110116
}
111117
}
112118

119+
/**
120+
* @param ConfigurableGeneratorInterface $generator
121+
* @param $options
122+
* @return \Vivait\StringGeneratorBundle\Model\ConfigurableGeneratorInterface
123+
*/
124+
public function configureGenerator(ConfigurableGeneratorInterface $generator, $options)
125+
{
126+
$resolver = new OptionsResolver();
127+
$generator->getDefaultOptions($resolver);
128+
129+
$options = $resolver->resolve($options);
130+
$generator->setOptions($options);
131+
132+
return $generator;
133+
}
134+
113135
/**
114136
* @param GeneratorInterface $generator
115137
* @param GeneratorAnnotation $annotation

0 commit comments

Comments
 (0)