Skip to content

Commit 8597cb7

Browse files
committed
Add the ability to pass in configuration to a given filter
This enables us to do interesting things, such as modifying a filter at runtime to whitelist keys from modification etc.
1 parent d938f7b commit 8597cb7

File tree

4 files changed

+107
-18
lines changed

4 files changed

+107
-18
lines changed

README.markdown

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,14 @@ It is possible to optionally filter the environment data produced by php-dotenv
213213
<?php
214214
class LollipopFilter
215215
{
216-
public function __invoke(array $environment)
216+
/**
217+
* Sets every key's value to the string `lollipop`
218+
*
219+
* @param array $environment Array of environment data
220+
* @param array $config Array of configuration data that includes the callable
221+
* @return array
222+
*/
223+
public function __invoke(array $environment, array $config)
217224
{
218225
$newEnvironment = [];
219226
foreach ($environment as $key => $value) {
@@ -234,7 +241,18 @@ $Loader = (new josegonzalez\Dotenv\Loader('path/to/.env'))
234241
?>
235242
```
236243

237-
Filters can also be callables functions, which is useful in one-off situations:
244+
Note that you can optionally set configuration for your filters. These are passed to the `__invoke` method as the second argument.:
245+
246+
```php
247+
<?php
248+
$Loader = (new josegonzalez\Dotenv\Loader('path/to/.env'))
249+
->setFilters([
250+
'LollipopFilter' => ['paintshop'],
251+
]); // Takes an array of namespaced class names
252+
?>
253+
```
254+
255+
Filters can also be callables functions, which is useful in one-off situations. They are wrapped by the special `CallableFilter`.
238256

239257
```php
240258
<?php
@@ -245,6 +263,22 @@ $Loader = (new josegonzalez\Dotenv\Loader('path/to/.env'))
245263
?>
246264
```
247265

266+
If you need special configuration for your callable filters, you can prefix your callable with `__callable__N`, where `N` is the integer index the callable is in your array. The callable itself should be contained in a `callable` config key, as follows:
267+
268+
```php
269+
<?php
270+
$Loader = (new josegonzalez\Dotenv\Loader('path/to/.env'))
271+
->setFilters([
272+
'__callable__0' => [
273+
'callable' => function ($data, $config) {
274+
return $data;
275+
},
276+
'someKey' => 'value',
277+
]
278+
]);
279+
?>
280+
```
281+
248282
Finally, to invoke a filter, you must call `filter()` after calling `parse()`.
249283

250284
```php
@@ -260,6 +294,7 @@ $Loader = (new josegonzalez\Dotenv\Loader('path/to/.env'))
260294

261295
The following filters are built into php-dotenv.
262296

297+
- `josegonzalez\Dotenv\Filter\CallableFilter`: Wraps a callable and invokes it upon the environment.
263298
- `josegonzalez\Dotenv\Filter\LowercaseKeyFilter`: Lowercases all the keys for an environment to a single-depth.
264299
- `josegonzalez\Dotenv\Filter\NullFilter`: Returns the environment data without any changes.
265300
- `josegonzalez\Dotenv\Filter\UnderscoreArrayFilter`: Expands a flat array to a nested array. For example, `['0_Foo_Bar' => 'Far']` becomes `[['Foo' => ['Bar' => 'Far']]]`.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace josegonzalez\Dotenv\Filter;
4+
5+
class CallableFilter
6+
{
7+
/**
8+
* Wraps a callable and invokes it upon the environment.
9+
*
10+
* @param array $environment Array of environment data
11+
* @param array $config Array of configuration data that includes the callable
12+
* @return array
13+
*/
14+
public function __invoke(array $environment, array $config)
15+
{
16+
$callable = $config['callable'];
17+
return $callable($environment, $config);
18+
}
19+
}

src/josegonzalez/Dotenv/Loader.php

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use InvalidArgumentException;
66
use josegonzalez\Dotenv\Expect;
7+
use josegonzalez\Dotenv\Filter\CallableFilter;
78
use josegonzalez\Dotenv\Parser;
89
use LogicException;
910

@@ -114,26 +115,46 @@ public function filters()
114115

115116
public function setFilters(array $filters)
116117
{
117-
$this->filters = $filters;
118-
foreach ($this->filters as $filterClass) {
119-
if (is_string($filterClass)) {
120-
if (is_callable($filterClass)) {
121-
continue;
122-
}
123-
if (!class_exists($filterClass)) {
124-
return $this->raise(
125-
'LogicException',
126-
sprintf('Invalid filter class %s', $filterClass)
118+
$newList = array();
119+
$keys = array_keys($filters);
120+
$count = count($keys);
121+
for ($i = 0; $i < $count; $i++) {
122+
if (is_int($keys[$i])) {
123+
$filter = $filters[$keys[$i]];
124+
if (is_string($filter)) {
125+
$newList[$filter] = null;
126+
} else {
127+
$newList['__callable__' . $i] = array(
128+
'callable' => $filter
127129
);
128130
}
129-
continue;
131+
} else {
132+
$newList[$keys[$i]] = $filters[$keys[$i]];
130133
}
131-
if (!is_callable($filterClass)) {
134+
}
135+
136+
$this->filters = $newList;
137+
138+
foreach ($this->filters as $filterClass => $config) {
139+
if (substr($filterClass, 0, 12) === '__callable__') {
140+
if (is_callable($config['callable'])) {
141+
continue;
142+
}
132143
return $this->raise(
133144
'LogicException',
134145
sprintf('Invalid filter class')
135146
);
136147
}
148+
if (is_callable($filterClass)) {
149+
continue;
150+
}
151+
if (!class_exists($filterClass)) {
152+
return $this->raise(
153+
'LogicException',
154+
sprintf('Invalid filter class %s', $filterClass)
155+
);
156+
}
157+
continue;
137158
}
138159
return $this;
139160
}
@@ -143,14 +164,17 @@ public function filter()
143164
$this->requireParse('filter');
144165

145166
$environment = $this->environment;
146-
foreach ($this->filters as $filterClass) {
167+
foreach ($this->filters as $filterClass => $config) {
147168
$filter = $filterClass;
148169
if (is_string($filterClass)) {
170+
if (substr($filterClass, 0, 12) === '__callable__') {
171+
$filter = new CallableFilter;
172+
}
149173
if (class_exists($filterClass)) {
150174
$filter = new $filterClass;
151175
}
152176
}
153-
$environment = $filter($environment);
177+
$environment = $filter($environment, $config);
154178
}
155179

156180
$this->environment = $environment;

tests/josegonzalez/Dotenv/LoaderTest.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,16 @@ public function testSetFilters()
221221
$this->assertEquals($this->Loader, $this->Loader->setFilters(array(
222222
'josegonzalez\Dotenv\doNothing',
223223
)));
224+
225+
$this->assertEquals($this->Loader, $this->Loader->setFilters(array(
226+
'josegonzalez\Dotenv\doNothing' => array('key' => 'value'),
227+
)));
228+
229+
$this->assertEquals($this->Loader, $this->Loader->setFilters(array(
230+
function () {
231+
return array();
232+
}
233+
)));
224234
}
225235

226236
/**
@@ -269,17 +279,18 @@ public function testFilter()
269279

270280
/**
271281
* @covers \josegonzalez\Dotenv\Loader::filter
282+
* @covers \josegonzalez\Dotenv\Filter\CallableFilter::__invoke
272283
*/
273284
public function testFilterCallable()
274285
{
275286
$this->assertEquals($this->Loader, $this->Loader->setFilters(array(
276287
function () {
277-
return array();
288+
return array('FOO' => 'BAR');
278289
}
279290
)));
280291
$this->Loader->parse();
281292
$this->Loader->filter();
282-
$this->assertEquals(array(), $this->Loader->toArray());
293+
$this->assertEquals(array('FOO' => 'BAR'), $this->Loader->toArray());
283294
}
284295

285296
/**

0 commit comments

Comments
 (0)