Skip to content

[OptionsResolver] added frozen options #17161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\OptionsResolver\Exception;

/**
* Exception thrown when a unassigned option trying to be freeze.
*/
class MissingDefaultValueException extends InvalidArgumentException
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\OptionsResolver\Exception;

/**
* Exception thrown when a frozen option is set.
*/
class OptionFrozenException extends InvalidArgumentException
{
}
78 changes: 77 additions & 1 deletion src/Symfony/Component/OptionsResolver/OptionsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@

use Symfony\Component\OptionsResolver\Exception\AccessException;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Exception\MissingDefaultValueException;
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
use Symfony\Component\OptionsResolver\Exception\NoSuchOptionException;
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
use Symfony\Component\OptionsResolver\Exception\OptionFrozenException;
use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;

/**
Expand Down Expand Up @@ -54,6 +56,13 @@ class OptionsResolver implements Options
*/
private $resolved = array();

/**
* The frozen options.
*
* @var array
*/
private $frozen = array();

/**
* A list of normalizer closures.
*
Expand Down Expand Up @@ -158,6 +167,13 @@ public function setDefault($option, $value)
throw new AccessException('Default values cannot be set from a lazy option or normalizer.');
}

if ($this->isFrozen($option)) {
throw new OptionFrozenException(sprintf(
'The option "%s" is frozen. You cannot change its default value.',
$option
));
}

// If an option is a closure that should be evaluated lazily, store it
// in the "lazy" property.
if ($value instanceof \Closure) {
Expand Down Expand Up @@ -366,6 +382,57 @@ public function getDefinedOptions()
return array_keys($this->defined);
}

/**
* Freeze the option with the given name.
*
* @param string|string[] $optionNames One or more option names
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this really be a string?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, thanks to type casting

*
* @return OptionsResolver This instance
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function setFrozen($optionNames)
{
if ($this->locked) {
throw new AccessException('Options cannot be frozen from a lazy option or normalizer.');
}

foreach ((array) $optionNames as $option) {
if (!$this->hasDefault($option)) {
throw new MissingDefaultValueException(sprintf(
'The option "%s" has no default value. You cannot freeze it.',
$option
));
}

$this->frozen[$option] = true;
}

return $this;
}

/**
* Returns whether an option is frozen.
*
* @param string $option The option name
*
* @return bool Whether the option is frozen
*/
public function isFrozen($option)
{
return isset($this->frozen[$option]);
}

/**
* Returns the names of all frozen options.
*
* @return string[] The names of the frozen options
*/
public function getFrozenOptions()
{
return array_keys($this->frozen);
}

/**
* Sets the normalizer for an option.
*
Expand Down Expand Up @@ -610,7 +677,7 @@ public function remove($optionNames)
}

foreach ((array) $optionNames as $option) {
unset($this->defined[$option], $this->defaults[$option], $this->required[$option], $this->resolved[$option]);
unset($this->defined[$option], $this->defaults[$option], $this->required[$option], $this->resolved[$option], $this->frozen[$option]);
unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option]);
}

Expand All @@ -633,6 +700,7 @@ public function clear()
$this->defined = array();
$this->defaults = array();
$this->required = array();
$this->frozen = array();
$this->resolved = array();
$this->lazy = array();
$this->normalizers = array();
Expand All @@ -658,6 +726,7 @@ public function clear()
* @return array The merged and validated options
*
* @throws UndefinedOptionsException If an option name is undefined
* @throws OptionFrozenException If a frozen option is set
* @throws InvalidOptionsException If an option doesn't fulfill the
* specified validation rules
* @throws MissingOptionsException If a required option is missing
Expand Down Expand Up @@ -691,6 +760,13 @@ public function resolve(array $options = array())

// Override options set by the user
foreach ($options as $option => $value) {
if (isset($this->frozen[$option])) {
throw new OptionFrozenException(sprintf(
'The option "%s" is frozen. You cannot change its value.',
$option
));
}

$clone->defaults[$option] = $value;
unset($clone->resolved[$option], $clone->lazy[$option]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,61 @@ public function testGetMissingOptions()
$this->assertSame(array('bar'), $this->resolver->getMissingOptions());
}

////////////////////////////////////////////////////////////////////////////
// setFrozen()/isFrozen()/getFrozenOptions()
////////////////////////////////////////////////////////////////////////////

/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionFrozenException
* @expectedExceptionMessage The option "foo" is frozen. You cannot change its value.
*/
public function testFailIfFrozenOptionSetted()
{
$this->resolver->setDefault('foo', 'bar');
$this->resolver->setFrozen('foo');

$this->resolver->resolve(array('foo' => 'baz'));
}

public function testIsFrozen()
{
$this->resolver->setDefault('foo', 'bar');
$this->resolver->setFrozen('foo');

$this->assertTrue($this->resolver->isFrozen('foo'));
$this->assertFalse($this->resolver->isFrozen('bar'));
}

public function testGetFrozenOptions()
{
$this->assertEquals(array(), $this->resolver->getFrozenOptions());

$this->resolver->setDefaults(array('foo' => 1, 'bar' => 2, 'baz' => 3));
$this->resolver->setFrozen(array('foo', 'bar', 'baz'));

$this->assertEquals(array('foo', 'bar', 'baz'), $this->resolver->getFrozenOptions());
}

/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingDefaultValueException
* @expectedExceptionMessage The option "foo" has no default value. You cannot freeze it.
*/
public function testFailIfUnassignedOptionTryingToFrozen()
{
$this->resolver->setFrozen('foo');
}

/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionFrozenException
* @expectedExceptionMessage The option "foo" is frozen. You cannot change its default value.
*/
public function testFailIfTryingToSetDefaultForFrozenOption()
{
$this->resolver->setDefault('foo', 'bar');
$this->resolver->setFrozen('foo');
$this->resolver->setDefault('foo', 'baz');
}

////////////////////////////////////////////////////////////////////////////
// setDefined()/isDefined()/getDefinedOptions()
////////////////////////////////////////////////////////////////////////////
Expand Down