first commit -> move some files from one-api
This commit is contained in:
3
vendor/symfony/options-resolver/.gitignore
vendored
Normal file
3
vendor/symfony/options-resolver/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
46
vendor/symfony/options-resolver/CHANGELOG.md
vendored
Normal file
46
vendor/symfony/options-resolver/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
2.6.0
|
||||
-----
|
||||
|
||||
* deprecated OptionsResolverInterface
|
||||
* [BC BREAK] removed "array" type hint from OptionsResolverInterface methods
|
||||
setRequired(), setAllowedValues(), addAllowedValues(), setAllowedTypes() and
|
||||
addAllowedTypes()
|
||||
* added OptionsResolver::setDefault()
|
||||
* added OptionsResolver::hasDefault()
|
||||
* added OptionsResolver::setNormalizer()
|
||||
* added OptionsResolver::isRequired()
|
||||
* added OptionsResolver::getRequiredOptions()
|
||||
* added OptionsResolver::isMissing()
|
||||
* added OptionsResolver::getMissingOptions()
|
||||
* added OptionsResolver::setDefined()
|
||||
* added OptionsResolver::isDefined()
|
||||
* added OptionsResolver::getDefinedOptions()
|
||||
* added OptionsResolver::remove()
|
||||
* added OptionsResolver::clear()
|
||||
* deprecated OptionsResolver::replaceDefaults()
|
||||
* deprecated OptionsResolver::setOptional() in favor of setDefined()
|
||||
* deprecated OptionsResolver::isKnown() in favor of isDefined()
|
||||
* [BC BREAK] OptionsResolver::isRequired() returns true now if a required
|
||||
option has a default value set
|
||||
* [BC BREAK] merged Options into OptionsResolver and turned Options into an
|
||||
interface
|
||||
* deprecated Options::overload() (now in OptionsResolver)
|
||||
* deprecated Options::set() (now in OptionsResolver)
|
||||
* deprecated Options::get() (now in OptionsResolver)
|
||||
* deprecated Options::has() (now in OptionsResolver)
|
||||
* deprecated Options::replace() (now in OptionsResolver)
|
||||
* [BC BREAK] Options::get() (now in OptionsResolver) can only be used within
|
||||
lazy option/normalizer closures now
|
||||
* [BC BREAK] removed Traversable interface from Options since using within
|
||||
lazy option/normalizer closures resulted in exceptions
|
||||
* [BC BREAK] removed Options::all() since using within lazy option/normalizer
|
||||
closures resulted in exceptions
|
||||
* [BC BREAK] OptionDefinitionException now extends LogicException instead of
|
||||
RuntimeException
|
||||
* [BC BREAK] normalizers are not executed anymore for unset options
|
||||
* normalizers are executed after validating the options now
|
||||
* [BC BREAK] an UndefinedOptionsException is now thrown instead of an
|
||||
InvalidOptionsException when non-existing options are passed
|
||||
22
vendor/symfony/options-resolver/Exception/AccessException.php
vendored
Normal file
22
vendor/symfony/options-resolver/Exception/AccessException.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Thrown when trying to read an option outside of or write it inside of
|
||||
* {@link \Symfony\Component\OptionsResolver\Options::resolve()}.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class AccessException extends \LogicException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/options-resolver/Exception/ExceptionInterface.php
vendored
Normal file
21
vendor/symfony/options-resolver/Exception/ExceptionInterface.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Marker interface for all exceptions thrown by the OptionsResolver component.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface ExceptionInterface
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/options-resolver/Exception/InvalidArgumentException.php
vendored
Normal file
21
vendor/symfony/options-resolver/Exception/InvalidArgumentException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Thrown when an argument is invalid.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
23
vendor/symfony/options-resolver/Exception/InvalidOptionsException.php
vendored
Normal file
23
vendor/symfony/options-resolver/Exception/InvalidOptionsException.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Thrown when the value of an option does not match its validation rules.
|
||||
*
|
||||
* You should make sure a valid value is passed to the option.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class InvalidOptionsException extends InvalidArgumentException
|
||||
{
|
||||
}
|
||||
23
vendor/symfony/options-resolver/Exception/MissingOptionsException.php
vendored
Normal file
23
vendor/symfony/options-resolver/Exception/MissingOptionsException.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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 required option is missing.
|
||||
*
|
||||
* Add the option to the passed options array.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class MissingOptionsException extends InvalidArgumentException
|
||||
{
|
||||
}
|
||||
26
vendor/symfony/options-resolver/Exception/NoSuchOptionException.php
vendored
Normal file
26
vendor/symfony/options-resolver/Exception/NoSuchOptionException.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Thrown when trying to read an option that has no value set.
|
||||
*
|
||||
* When accessing optional options from within a lazy option or normalizer you should first
|
||||
* check whether the optional option is set. You can do this with `isset($options['optional'])`.
|
||||
* In contrast to the {@link UndefinedOptionsException}, this is a runtime exception that can
|
||||
* occur when evaluating lazy options.
|
||||
*
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
class NoSuchOptionException extends \OutOfBoundsException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/options-resolver/Exception/OptionDefinitionException.php
vendored
Normal file
21
vendor/symfony/options-resolver/Exception/OptionDefinitionException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Thrown when two lazy options have a cyclic dependency.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class OptionDefinitionException extends \LogicException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
24
vendor/symfony/options-resolver/Exception/UndefinedOptionsException.php
vendored
Normal file
24
vendor/symfony/options-resolver/Exception/UndefinedOptionsException.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?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 an undefined option is passed.
|
||||
*
|
||||
* You should remove the options in question from your code or define them
|
||||
* beforehand.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class UndefinedOptionsException extends InvalidArgumentException
|
||||
{
|
||||
}
|
||||
19
vendor/symfony/options-resolver/LICENSE
vendored
Normal file
19
vendor/symfony/options-resolver/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2004-2018 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
22
vendor/symfony/options-resolver/Options.php
vendored
Normal file
22
vendor/symfony/options-resolver/Options.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Contains resolved option values.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
interface Options extends \ArrayAccess, \Countable
|
||||
{
|
||||
}
|
||||
1157
vendor/symfony/options-resolver/OptionsResolver.php
vendored
Normal file
1157
vendor/symfony/options-resolver/OptionsResolver.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
208
vendor/symfony/options-resolver/OptionsResolverInterface.php
vendored
Normal file
208
vendor/symfony/options-resolver/OptionsResolverInterface.php
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
|
||||
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
|
||||
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @deprecated since version 2.6, to be removed in 3.0. Use {@link OptionsResolver} instead.
|
||||
*/
|
||||
interface OptionsResolverInterface
|
||||
{
|
||||
/**
|
||||
* Sets default option values.
|
||||
*
|
||||
* The options can either be values of any types or closures that
|
||||
* evaluate the option value lazily. These closures must have one
|
||||
* of the following signatures:
|
||||
*
|
||||
* function (Options $options)
|
||||
* function (Options $options, $value)
|
||||
*
|
||||
* The second parameter passed to the closure is the previously
|
||||
* set default value, in case you are overwriting an existing
|
||||
* default value.
|
||||
*
|
||||
* The closures should return the lazily created option value.
|
||||
*
|
||||
* @param array $defaultValues A list of option names as keys and default
|
||||
* values or closures as values
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDefaults(array $defaultValues);
|
||||
|
||||
/**
|
||||
* Replaces default option values.
|
||||
*
|
||||
* Old defaults are erased, which means that closures passed here cannot
|
||||
* access the previous default value. This may be useful to improve
|
||||
* performance if the previous default value is calculated by an expensive
|
||||
* closure.
|
||||
*
|
||||
* @param array $defaultValues A list of option names as keys and default
|
||||
* values or closures as values
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function replaceDefaults(array $defaultValues);
|
||||
|
||||
/**
|
||||
* Sets optional options.
|
||||
*
|
||||
* This method declares valid option names without setting default values for them.
|
||||
* If these options are not passed to {@link resolve()} and no default has been set
|
||||
* for them, they will be missing in the final options array. This can be helpful
|
||||
* if you want to determine whether an option has been set or not because otherwise
|
||||
* {@link resolve()} would trigger an exception for unknown options.
|
||||
*
|
||||
* @param array $optionNames A list of option names
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOptional(array $optionNames);
|
||||
|
||||
/**
|
||||
* Sets required options.
|
||||
*
|
||||
* If these options are not passed to {@link resolve()} and no default has been set for
|
||||
* them, an exception will be thrown.
|
||||
*
|
||||
* @param array $optionNames A list of option names
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRequired($optionNames);
|
||||
|
||||
/**
|
||||
* Sets allowed values for a list of options.
|
||||
*
|
||||
* @param array $allowedValues A list of option names as keys and arrays
|
||||
* with values acceptable for that option as
|
||||
* values
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidOptionsException if an option has not been defined
|
||||
* (see {@link isKnown()}) for which
|
||||
* an allowed value is set
|
||||
*/
|
||||
public function setAllowedValues($allowedValues);
|
||||
|
||||
/**
|
||||
* Adds allowed values for a list of options.
|
||||
*
|
||||
* The values are merged with the allowed values defined previously.
|
||||
*
|
||||
* @param array $allowedValues A list of option names as keys and arrays
|
||||
* with values acceptable for that option as
|
||||
* values
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidOptionsException if an option has not been defined
|
||||
* (see {@link isKnown()}) for which
|
||||
* an allowed value is set
|
||||
*/
|
||||
public function addAllowedValues($allowedValues);
|
||||
|
||||
/**
|
||||
* Sets allowed types for a list of options.
|
||||
*
|
||||
* @param array $allowedTypes A list of option names as keys and type
|
||||
* names passed as string or array as values
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidOptionsException if an option has not been defined for
|
||||
* which an allowed type is set
|
||||
*/
|
||||
public function setAllowedTypes($allowedTypes);
|
||||
|
||||
/**
|
||||
* Adds allowed types for a list of options.
|
||||
*
|
||||
* The types are merged with the allowed types defined previously.
|
||||
*
|
||||
* @param array $allowedTypes A list of option names as keys and type
|
||||
* names passed as string or array as values
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidOptionsException if an option has not been defined for
|
||||
* which an allowed type is set
|
||||
*/
|
||||
public function addAllowedTypes($allowedTypes);
|
||||
|
||||
/**
|
||||
* Sets normalizers that are applied on resolved options.
|
||||
*
|
||||
* The normalizers should be closures with the following signature:
|
||||
*
|
||||
* function (Options $options, $value)
|
||||
*
|
||||
* The second parameter passed to the closure is the value of
|
||||
* the option.
|
||||
*
|
||||
* The closure should return the normalized value.
|
||||
*
|
||||
* @param array $normalizers An array of closures
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNormalizers(array $normalizers);
|
||||
|
||||
/**
|
||||
* Returns whether an option is known.
|
||||
*
|
||||
* An option is known if it has been passed to either {@link setDefaults()},
|
||||
* {@link setRequired()} or {@link setOptional()} before.
|
||||
*
|
||||
* @param string $option The name of the option
|
||||
*
|
||||
* @return bool Whether the option is known
|
||||
*/
|
||||
public function isKnown($option);
|
||||
|
||||
/**
|
||||
* Returns whether an option is required.
|
||||
*
|
||||
* An option is required if it has been passed to {@link setRequired()},
|
||||
* but not to {@link setDefaults()}. That is, the option has been declared
|
||||
* as required and no default value has been set.
|
||||
*
|
||||
* @param string $option The name of the option
|
||||
*
|
||||
* @return bool Whether the option is required
|
||||
*/
|
||||
public function isRequired($option);
|
||||
|
||||
/**
|
||||
* Returns the combination of the default and the passed options.
|
||||
*
|
||||
* @param array $options The custom option values
|
||||
*
|
||||
* @return array A list of options and their values
|
||||
*
|
||||
* @throws InvalidOptionsException if any of the passed options has not
|
||||
* been defined or does not contain an
|
||||
* allowed value
|
||||
* @throws MissingOptionsException if a required option is missing
|
||||
* @throws OptionDefinitionException if a cyclic dependency is detected
|
||||
* between two lazy options
|
||||
*/
|
||||
public function resolve(array $options = array());
|
||||
}
|
||||
15
vendor/symfony/options-resolver/README.md
vendored
Normal file
15
vendor/symfony/options-resolver/README.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
OptionsResolver Component
|
||||
=========================
|
||||
|
||||
The OptionsResolver component is `array_replace` on steroids. It allows you to
|
||||
create an options system with required options, defaults, validation (type,
|
||||
value), normalization and more.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/options_resolver.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
734
vendor/symfony/options-resolver/Tests/LegacyOptionsResolverTest.php
vendored
Normal file
734
vendor/symfony/options-resolver/Tests/LegacyOptionsResolverTest.php
vendored
Normal file
@@ -0,0 +1,734 @@
|
||||
<?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\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class LegacyOptionsResolverTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var OptionsResolver
|
||||
*/
|
||||
private $resolver;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->resolver = new OptionsResolver();
|
||||
}
|
||||
|
||||
public function testResolve()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'two' => '20',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'two' => '20',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveNumericOptions()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'1' => '1',
|
||||
'2' => '2',
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'2' => '20',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'1' => '1',
|
||||
'2' => '20',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveLazy()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => function (Options $options) {
|
||||
return '20';
|
||||
},
|
||||
));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'two' => '20',
|
||||
), $this->resolver->resolve(array()));
|
||||
}
|
||||
|
||||
public function testTypeAliasesForAllowedTypes()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'force' => false,
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'force' => 'boolean',
|
||||
));
|
||||
|
||||
$this->assertSame(array('force' => true), $this->resolver->resolve(array(
|
||||
'force' => true,
|
||||
)));
|
||||
}
|
||||
|
||||
public function testResolveLazyDependencyOnOptional()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => function (Options $options) {
|
||||
return $options['one'].'2';
|
||||
},
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => '10',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '10',
|
||||
'two' => '102',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveLazyDependencyOnMissingOptionalWithoutDefault()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
$this->resolver->setOptional(array(
|
||||
'one',
|
||||
));
|
||||
|
||||
$this->resolver->setDefaults(array(
|
||||
'two' => function (Options $options) use ($test) {
|
||||
/* @var TestCase $test */
|
||||
$test->assertArrayNotHasKey('one', $options);
|
||||
|
||||
return '2';
|
||||
},
|
||||
));
|
||||
|
||||
$options = array();
|
||||
|
||||
$this->assertEquals(array(
|
||||
'two' => '2',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveLazyDependencyOnOptionalWithoutDefault()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
$this->resolver->setOptional(array(
|
||||
'one',
|
||||
));
|
||||
|
||||
$this->resolver->setDefaults(array(
|
||||
'two' => function (Options $options) use ($test) {
|
||||
/* @var TestCase $test */
|
||||
$test->assertArrayHasKey('one', $options);
|
||||
|
||||
return $options['one'].'2';
|
||||
},
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => '10',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '10',
|
||||
'two' => '102',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveLazyDependencyOnRequired()
|
||||
{
|
||||
$this->resolver->setRequired(array(
|
||||
'one',
|
||||
));
|
||||
$this->resolver->setDefaults(array(
|
||||
'two' => function (Options $options) {
|
||||
return $options['one'].'2';
|
||||
},
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => '10',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '10',
|
||||
'two' => '102',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveLazyReplaceDefaults()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => function (Options $options) use ($test) {
|
||||
/* @var TestCase $test */
|
||||
$test->fail('Previous closure should not be executed');
|
||||
},
|
||||
));
|
||||
|
||||
$this->resolver->replaceDefaults(array(
|
||||
'one' => function (Options $options, $previousValue) {
|
||||
return '1';
|
||||
},
|
||||
));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
), $this->resolver->resolve(array()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
|
||||
* @expectedExceptionMessage The option "foo" does not exist. Defined options are: "one", "three", "two".
|
||||
*/
|
||||
public function testResolveFailsIfNonExistingOption()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->resolver->setRequired(array(
|
||||
'two',
|
||||
));
|
||||
|
||||
$this->resolver->setOptional(array(
|
||||
'three',
|
||||
));
|
||||
|
||||
$this->resolver->resolve(array(
|
||||
'foo' => 'bar',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
|
||||
*/
|
||||
public function testResolveFailsIfMissingRequiredOption()
|
||||
{
|
||||
$this->resolver->setRequired(array(
|
||||
'one',
|
||||
));
|
||||
|
||||
$this->resolver->setDefaults(array(
|
||||
'two' => '2',
|
||||
));
|
||||
|
||||
$this->resolver->resolve(array(
|
||||
'two' => '20',
|
||||
));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfOptionValueAllowed()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedValues(array(
|
||||
'one' => array('1', 'one'),
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => 'one',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => 'one',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfOptionValueAllowed2()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedValues(array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
));
|
||||
$this->resolver->addAllowedValues(array(
|
||||
'one' => 'one',
|
||||
'two' => 'two',
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
'two' => 'two',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'two' => 'two',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfOptionalWithAllowedValuesNotSet()
|
||||
{
|
||||
$this->resolver->setRequired(array(
|
||||
'one',
|
||||
));
|
||||
|
||||
$this->resolver->setOptional(array(
|
||||
'two',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedValues(array(
|
||||
'one' => array('1', 'one'),
|
||||
'two' => array('2', 'two'),
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testResolveFailsIfOptionValueNotAllowed()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedValues(array(
|
||||
'one' => array('1', 'one'),
|
||||
));
|
||||
|
||||
$this->resolver->resolve(array(
|
||||
'one' => '2',
|
||||
));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfOptionTypeAllowed()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'one' => 'string',
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => 'one',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => 'one',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfOptionTypeAllowedPassArray()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'one' => array('string', 'bool'),
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => true,
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => true,
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfOptionTypeAllowedPassObject()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'one' => 'object',
|
||||
));
|
||||
|
||||
$object = new \stdClass();
|
||||
$options = array(
|
||||
'one' => $object,
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => $object,
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfOptionTypeAllowedPassClass()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'one' => '\stdClass',
|
||||
));
|
||||
|
||||
$object = new \stdClass();
|
||||
$options = array(
|
||||
'one' => $object,
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => $object,
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfOptionTypeAllowedAddTypes()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'one' => 'string',
|
||||
'two' => 'bool',
|
||||
));
|
||||
$this->resolver->addAllowedTypes(array(
|
||||
'one' => 'float',
|
||||
'two' => 'integer',
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => 1.23,
|
||||
'two' => false,
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => 1.23,
|
||||
'two' => false,
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfOptionalWithTypeAndWithoutValue()
|
||||
{
|
||||
$this->resolver->setOptional(array(
|
||||
'one',
|
||||
'two',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'one' => 'string',
|
||||
'two' => 'int',
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'two' => 1,
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'two' => 1,
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testResolveFailsIfOptionTypeNotAllowed()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'one' => array('string', 'bool'),
|
||||
));
|
||||
|
||||
$this->resolver->resolve(array(
|
||||
'one' => 1.23,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testResolveFailsIfOptionTypeNotAllowedMultipleOptions()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'one' => 'string',
|
||||
'two' => 'bool',
|
||||
));
|
||||
|
||||
$this->resolver->resolve(array(
|
||||
'one' => 'foo',
|
||||
'two' => 1.23,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testResolveFailsIfOptionTypeNotAllowedAddTypes()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->resolver->setAllowedTypes(array(
|
||||
'one' => 'string',
|
||||
));
|
||||
$this->resolver->addAllowedTypes(array(
|
||||
'one' => 'bool',
|
||||
));
|
||||
|
||||
$this->resolver->resolve(array(
|
||||
'one' => 1.23,
|
||||
));
|
||||
}
|
||||
|
||||
public function testFluidInterface()
|
||||
{
|
||||
$this->resolver->setDefaults(array('one' => '1'))
|
||||
->replaceDefaults(array('one' => '2'))
|
||||
->setAllowedValues(array('one' => array('1', '2')))
|
||||
->addAllowedValues(array('one' => array('3')))
|
||||
->setRequired(array('two'))
|
||||
->setOptional(array('three'));
|
||||
|
||||
$options = array(
|
||||
'two' => '2',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '2',
|
||||
'two' => '2',
|
||||
), $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testKnownIfDefaultWasSet()
|
||||
{
|
||||
$this->assertFalse($this->resolver->isKnown('foo'));
|
||||
|
||||
$this->resolver->setDefaults(array(
|
||||
'foo' => 'bar',
|
||||
));
|
||||
|
||||
$this->assertTrue($this->resolver->isKnown('foo'));
|
||||
}
|
||||
|
||||
public function testKnownIfRequired()
|
||||
{
|
||||
$this->assertFalse($this->resolver->isKnown('foo'));
|
||||
|
||||
$this->resolver->setRequired(array(
|
||||
'foo',
|
||||
));
|
||||
|
||||
$this->assertTrue($this->resolver->isKnown('foo'));
|
||||
}
|
||||
|
||||
public function testKnownIfOptional()
|
||||
{
|
||||
$this->assertFalse($this->resolver->isKnown('foo'));
|
||||
|
||||
$this->resolver->setOptional(array(
|
||||
'foo',
|
||||
));
|
||||
|
||||
$this->assertTrue($this->resolver->isKnown('foo'));
|
||||
}
|
||||
|
||||
public function testRequiredIfRequired()
|
||||
{
|
||||
$this->assertFalse($this->resolver->isRequired('foo'));
|
||||
|
||||
$this->resolver->setRequired(array(
|
||||
'foo',
|
||||
));
|
||||
|
||||
$this->assertTrue($this->resolver->isRequired('foo'));
|
||||
}
|
||||
|
||||
public function testNormalizersTransformFinalOptions()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'foo' => 'bar',
|
||||
'bam' => 'baz',
|
||||
));
|
||||
$this->resolver->setNormalizers(array(
|
||||
'foo' => function (Options $options, $value) {
|
||||
return $options['bam'].'['.$value.']';
|
||||
},
|
||||
));
|
||||
|
||||
$expected = array(
|
||||
'foo' => 'baz[bar]',
|
||||
'bam' => 'baz',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $this->resolver->resolve(array()));
|
||||
|
||||
$expected = array(
|
||||
'foo' => 'boo[custom]',
|
||||
'bam' => 'boo',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $this->resolver->resolve(array(
|
||||
'foo' => 'custom',
|
||||
'bam' => 'boo',
|
||||
)));
|
||||
}
|
||||
|
||||
public function testResolveWithoutOptionSucceedsIfRequiredAndDefaultValue()
|
||||
{
|
||||
$this->resolver->setRequired(array(
|
||||
'foo',
|
||||
));
|
||||
$this->resolver->setDefaults(array(
|
||||
'foo' => 'bar',
|
||||
));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'foo' => 'bar',
|
||||
), $this->resolver->resolve(array()));
|
||||
}
|
||||
|
||||
public function testResolveWithoutOptionSucceedsIfDefaultValueAndRequired()
|
||||
{
|
||||
$this->resolver->setDefaults(array(
|
||||
'foo' => 'bar',
|
||||
));
|
||||
$this->resolver->setRequired(array(
|
||||
'foo',
|
||||
));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'foo' => 'bar',
|
||||
), $this->resolver->resolve(array()));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfOptionRequiredAndValueAllowed()
|
||||
{
|
||||
$this->resolver->setRequired(array(
|
||||
'one', 'two',
|
||||
));
|
||||
$this->resolver->setAllowedValues(array(
|
||||
'two' => array('2'),
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
);
|
||||
|
||||
$this->assertEquals($options, $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testResolveSucceedsIfValueAllowedCallbackReturnsTrue()
|
||||
{
|
||||
$this->resolver->setRequired(array(
|
||||
'test',
|
||||
));
|
||||
$this->resolver->setAllowedValues(array(
|
||||
'test' => function ($value) {
|
||||
return true;
|
||||
},
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'test' => true,
|
||||
);
|
||||
|
||||
$this->assertEquals($options, $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testResolveFailsIfValueAllowedCallbackReturnsFalse()
|
||||
{
|
||||
$this->resolver->setRequired(array(
|
||||
'test',
|
||||
));
|
||||
$this->resolver->setAllowedValues(array(
|
||||
'test' => function ($value) {
|
||||
return false;
|
||||
},
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'test' => true,
|
||||
);
|
||||
|
||||
$this->assertEquals($options, $this->resolver->resolve($options));
|
||||
}
|
||||
|
||||
public function testClone()
|
||||
{
|
||||
$this->resolver->setDefaults(array('one' => '1'));
|
||||
|
||||
$clone = clone $this->resolver;
|
||||
|
||||
// Changes after cloning don't affect each other
|
||||
$this->resolver->setDefaults(array('two' => '2'));
|
||||
$clone->setDefaults(array('three' => '3'));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
), $this->resolver->resolve());
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'three' => '3',
|
||||
), $clone->resolve());
|
||||
}
|
||||
|
||||
public function testOverloadReturnsThis()
|
||||
{
|
||||
$this->assertSame($this->resolver, $this->resolver->overload('foo', 'bar'));
|
||||
}
|
||||
|
||||
public function testOverloadCallsSet()
|
||||
{
|
||||
$this->resolver->overload('foo', 'bar');
|
||||
|
||||
$this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
|
||||
}
|
||||
}
|
||||
336
vendor/symfony/options-resolver/Tests/LegacyOptionsTest.php
vendored
Normal file
336
vendor/symfony/options-resolver/Tests/LegacyOptionsTest.php
vendored
Normal file
@@ -0,0 +1,336 @@
|
||||
<?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\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class LegacyOptionsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var OptionsResolver
|
||||
*/
|
||||
private $options;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->options = new OptionsResolver();
|
||||
}
|
||||
|
||||
public function testSetLazyOption()
|
||||
{
|
||||
$this->options->set('foo', function (Options $options) {
|
||||
return 'dynamic';
|
||||
});
|
||||
|
||||
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testOverloadKeepsPreviousValue()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
// defined by superclass
|
||||
$this->options->set('foo', 'bar');
|
||||
|
||||
// defined by subclass
|
||||
$this->options->overload('foo', function (Options $options, $previousValue) use ($test) {
|
||||
/* @var TestCase $test */
|
||||
$test->assertEquals('bar', $previousValue);
|
||||
|
||||
return 'dynamic';
|
||||
});
|
||||
|
||||
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testPreviousValueIsEvaluatedIfLazy()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
// defined by superclass
|
||||
$this->options->set('foo', function (Options $options) {
|
||||
return 'bar';
|
||||
});
|
||||
|
||||
// defined by subclass
|
||||
$this->options->overload('foo', function (Options $options, $previousValue) use ($test) {
|
||||
/* @var TestCase $test */
|
||||
$test->assertEquals('bar', $previousValue);
|
||||
|
||||
return 'dynamic';
|
||||
});
|
||||
|
||||
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testPreviousValueIsNotEvaluatedIfNoSecondArgument()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
// defined by superclass
|
||||
$this->options->set('foo', function (Options $options) use ($test) {
|
||||
$test->fail('Should not be called');
|
||||
});
|
||||
|
||||
// defined by subclass, no $previousValue argument defined!
|
||||
$this->options->overload('foo', function (Options $options) {
|
||||
return 'dynamic';
|
||||
});
|
||||
|
||||
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testLazyOptionCanAccessOtherOptions()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
$this->options->set('foo', 'bar');
|
||||
|
||||
$this->options->set('bam', function (Options $options) use ($test) {
|
||||
/* @var TestCase $test */
|
||||
$test->assertEquals('bar', $options->get('foo'));
|
||||
|
||||
return 'dynamic';
|
||||
});
|
||||
|
||||
$this->assertEquals(array('foo' => 'bar', 'bam' => 'dynamic'), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testLazyOptionCanAccessOtherLazyOptions()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
$this->options->set('foo', function (Options $options) {
|
||||
return 'bar';
|
||||
});
|
||||
|
||||
$this->options->set('bam', function (Options $options) use ($test) {
|
||||
/* @var TestCase $test */
|
||||
$test->assertEquals('bar', $options->get('foo'));
|
||||
|
||||
return 'dynamic';
|
||||
});
|
||||
|
||||
$this->assertEquals(array('foo' => 'bar', 'bam' => 'dynamic'), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testNormalizer()
|
||||
{
|
||||
$this->options->set('foo', 'bar');
|
||||
|
||||
$this->options->setNormalizer('foo', function () {
|
||||
return 'normalized';
|
||||
});
|
||||
|
||||
$this->assertEquals(array('foo' => 'normalized'), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testNormalizerReceivesUnnormalizedValue()
|
||||
{
|
||||
$this->options->set('foo', 'bar');
|
||||
|
||||
$this->options->setNormalizer('foo', function (Options $options, $value) {
|
||||
return 'normalized['.$value.']';
|
||||
});
|
||||
|
||||
$this->assertEquals(array('foo' => 'normalized[bar]'), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testNormalizerCanAccessOtherOptions()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
$this->options->set('foo', 'bar');
|
||||
$this->options->set('bam', 'baz');
|
||||
|
||||
$this->options->setNormalizer('bam', function (Options $options) use ($test) {
|
||||
/* @var TestCase $test */
|
||||
$test->assertEquals('bar', $options->get('foo'));
|
||||
|
||||
return 'normalized';
|
||||
});
|
||||
|
||||
$this->assertEquals(array('foo' => 'bar', 'bam' => 'normalized'), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testNormalizerCanAccessOtherLazyOptions()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
$this->options->set('foo', function (Options $options) {
|
||||
return 'bar';
|
||||
});
|
||||
$this->options->set('bam', 'baz');
|
||||
|
||||
$this->options->setNormalizer('bam', function (Options $options) use ($test) {
|
||||
/* @var TestCase $test */
|
||||
$test->assertEquals('bar', $options->get('foo'));
|
||||
|
||||
return 'normalized';
|
||||
});
|
||||
|
||||
$this->assertEquals(array('foo' => 'bar', 'bam' => 'normalized'), $this->options->resolve());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
|
||||
*/
|
||||
public function testFailForCyclicDependencies()
|
||||
{
|
||||
$this->options->set('foo', function (Options $options) {
|
||||
$options->get('bam');
|
||||
});
|
||||
|
||||
$this->options->set('bam', function (Options $options) {
|
||||
$options->get('foo');
|
||||
});
|
||||
|
||||
$this->options->resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
|
||||
*/
|
||||
public function testFailForCyclicDependenciesBetweenNormalizers()
|
||||
{
|
||||
$this->options->set('foo', 'bar');
|
||||
$this->options->set('bam', 'baz');
|
||||
|
||||
$this->options->setNormalizer('foo', function (Options $options) {
|
||||
$options->get('bam');
|
||||
});
|
||||
|
||||
$this->options->setNormalizer('bam', function (Options $options) {
|
||||
$options->get('foo');
|
||||
});
|
||||
|
||||
$this->options->resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
|
||||
*/
|
||||
public function testFailForCyclicDependenciesBetweenNormalizerAndLazyOption()
|
||||
{
|
||||
$this->options->set('foo', function (Options $options) {
|
||||
$options->get('bam');
|
||||
});
|
||||
$this->options->set('bam', 'baz');
|
||||
|
||||
$this->options->setNormalizer('bam', function (Options $options) {
|
||||
$options->get('foo');
|
||||
});
|
||||
|
||||
$this->options->resolve();
|
||||
}
|
||||
|
||||
public function testReplaceClearsAndSets()
|
||||
{
|
||||
$this->options->set('one', '1');
|
||||
|
||||
$this->options->replace(array(
|
||||
'two' => '2',
|
||||
'three' => function (Options $options) {
|
||||
return '2' === $options['two'] ? '3' : 'foo';
|
||||
},
|
||||
));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'two' => '2',
|
||||
'three' => '3',
|
||||
), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testClearRemovesAllOptions()
|
||||
{
|
||||
$this->options->set('one', 1);
|
||||
$this->options->set('two', 2);
|
||||
|
||||
$this->options->clear();
|
||||
|
||||
$this->assertEmpty($this->options->resolve());
|
||||
}
|
||||
|
||||
public function testOverloadCannotBeEvaluatedLazilyWithoutExpectedClosureParams()
|
||||
{
|
||||
$this->options->set('foo', 'bar');
|
||||
|
||||
$this->options->overload('foo', function () {
|
||||
return 'test';
|
||||
});
|
||||
|
||||
$resolved = $this->options->resolve();
|
||||
$this->assertInternalType('callable', $resolved['foo']);
|
||||
}
|
||||
|
||||
public function testOverloadCannotBeEvaluatedLazilyWithoutFirstParamTypeHint()
|
||||
{
|
||||
$this->options->set('foo', 'bar');
|
||||
|
||||
$this->options->overload('foo', function ($object) {
|
||||
return 'test';
|
||||
});
|
||||
|
||||
$resolved = $this->options->resolve();
|
||||
$this->assertInternalType('callable', $resolved['foo']);
|
||||
}
|
||||
|
||||
public function testRemoveOptionAndNormalizer()
|
||||
{
|
||||
$this->options->set('foo1', 'bar');
|
||||
$this->options->setNormalizer('foo1', function (Options $options) {
|
||||
return '';
|
||||
});
|
||||
$this->options->set('foo2', 'bar');
|
||||
$this->options->setNormalizer('foo2', function (Options $options) {
|
||||
return '';
|
||||
});
|
||||
|
||||
$this->options->remove('foo2');
|
||||
$this->assertEquals(array('foo1' => ''), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testReplaceOptionAndNormalizer()
|
||||
{
|
||||
$this->options->set('foo1', 'bar');
|
||||
$this->options->setNormalizer('foo1', function (Options $options) {
|
||||
return '';
|
||||
});
|
||||
$this->options->set('foo2', 'bar');
|
||||
$this->options->setNormalizer('foo2', function (Options $options) {
|
||||
return '';
|
||||
});
|
||||
|
||||
$this->options->replace(array('foo1' => 'new'));
|
||||
$this->assertEquals(array('foo1' => 'new'), $this->options->resolve());
|
||||
}
|
||||
|
||||
public function testClearOptionAndNormalizer()
|
||||
{
|
||||
$this->options->set('foo1', 'bar');
|
||||
$this->options->setNormalizer('foo1', function (Options $options) {
|
||||
return '';
|
||||
});
|
||||
$this->options->set('foo2', 'bar');
|
||||
$this->options->setNormalizer('foo2', function (Options $options) {
|
||||
return '';
|
||||
});
|
||||
|
||||
$this->options->clear();
|
||||
$this->assertEmpty($this->options->resolve());
|
||||
}
|
||||
}
|
||||
1495
vendor/symfony/options-resolver/Tests/OptionsResolver2Dot6Test.php
vendored
Normal file
1495
vendor/symfony/options-resolver/Tests/OptionsResolver2Dot6Test.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
33
vendor/symfony/options-resolver/composer.json
vendored
Normal file
33
vendor/symfony/options-resolver/composer.json
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"type": "library",
|
||||
"description": "Symfony OptionsResolver Component",
|
||||
"keywords": ["options", "config", "configuration"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.9"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\OptionsResolver\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.8-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
31
vendor/symfony/options-resolver/phpunit.xml.dist
vendored
Normal file
31
vendor/symfony/options-resolver/phpunit.xml.dist
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Symfony OptionsResolver Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Resources</directory>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
227
vendor/symfony/polyfill-ctype/Ctype.php
vendored
Normal file
227
vendor/symfony/polyfill-ctype/Ctype.php
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
<?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\Polyfill\Ctype;
|
||||
|
||||
/**
|
||||
* Ctype implementation through regex.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @author Gert de Pagter <BackEndTea@gmail.com>
|
||||
*/
|
||||
final class Ctype
|
||||
{
|
||||
/**
|
||||
* Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise.
|
||||
*
|
||||
* @see https://php.net/ctype-alnum
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_alnum($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if every character in text is a letter, FALSE otherwise.
|
||||
*
|
||||
* @see https://php.net/ctype-alpha
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_alpha($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise.
|
||||
*
|
||||
* @see https://php.net/ctype-cntrl
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_cntrl($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise.
|
||||
*
|
||||
* @see https://php.net/ctype-digit
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_digit($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise.
|
||||
*
|
||||
* @see https://php.net/ctype-graph
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_graph($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if every character in text is a lowercase letter.
|
||||
*
|
||||
* @see https://php.net/ctype-lower
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_lower($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all.
|
||||
*
|
||||
* @see https://php.net/ctype-print
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_print($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise.
|
||||
*
|
||||
* @see https://php.net/ctype-punct
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_punct($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters.
|
||||
*
|
||||
* @see https://php.net/ctype-space
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_space($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if every character in text is an uppercase letter.
|
||||
*
|
||||
* @see https://php.net/ctype-upper
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_upper($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise.
|
||||
*
|
||||
* @see https://php.net/ctype-xdigit
|
||||
*
|
||||
* @param string|int $text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ctype_xdigit($text)
|
||||
{
|
||||
$text = self::convert_int_to_char_for_ctype($text);
|
||||
|
||||
return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts integers to their char versions according to normal ctype behaviour, if needed.
|
||||
*
|
||||
* If an integer between -128 and 255 inclusive is provided,
|
||||
* it is interpreted as the ASCII value of a single character
|
||||
* (negative values have 256 added in order to allow characters in the Extended ASCII range).
|
||||
* Any other integer is interpreted as a string containing the decimal digits of the integer.
|
||||
*
|
||||
* @param string|int $int
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private static function convert_int_to_char_for_ctype($int)
|
||||
{
|
||||
if (!\is_int($int)) {
|
||||
return $int;
|
||||
}
|
||||
|
||||
if ($int < -128 || $int > 255) {
|
||||
return (string) $int;
|
||||
}
|
||||
|
||||
if ($int < 0) {
|
||||
$int += 256;
|
||||
}
|
||||
|
||||
return \chr($int);
|
||||
}
|
||||
}
|
||||
19
vendor/symfony/polyfill-ctype/LICENSE
vendored
Normal file
19
vendor/symfony/polyfill-ctype/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2018-2019 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
12
vendor/symfony/polyfill-ctype/README.md
vendored
Normal file
12
vendor/symfony/polyfill-ctype/README.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
Symfony Polyfill / Ctype
|
||||
========================
|
||||
|
||||
This component provides `ctype_*` functions to users who run php versions without the ctype extension.
|
||||
|
||||
More information can be found in the
|
||||
[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
This library is released under the [MIT license](LICENSE).
|
||||
26
vendor/symfony/polyfill-ctype/bootstrap.php
vendored
Normal file
26
vendor/symfony/polyfill-ctype/bootstrap.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
use Symfony\Polyfill\Ctype as p;
|
||||
|
||||
if (!function_exists('ctype_alnum')) {
|
||||
function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); }
|
||||
function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); }
|
||||
function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); }
|
||||
function ctype_digit($text) { return p\Ctype::ctype_digit($text); }
|
||||
function ctype_graph($text) { return p\Ctype::ctype_graph($text); }
|
||||
function ctype_lower($text) { return p\Ctype::ctype_lower($text); }
|
||||
function ctype_print($text) { return p\Ctype::ctype_print($text); }
|
||||
function ctype_punct($text) { return p\Ctype::ctype_punct($text); }
|
||||
function ctype_space($text) { return p\Ctype::ctype_space($text); }
|
||||
function ctype_upper($text) { return p\Ctype::ctype_upper($text); }
|
||||
function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); }
|
||||
}
|
||||
34
vendor/symfony/polyfill-ctype/composer.json
vendored
Normal file
34
vendor/symfony/polyfill-ctype/composer.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"type": "library",
|
||||
"description": "Symfony polyfill for ctype functions",
|
||||
"keywords": ["polyfill", "compatibility", "portable", "ctype"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gert de Pagter",
|
||||
"email": "BackEndTea@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Polyfill\\Ctype\\": "" },
|
||||
"files": [ "bootstrap.php" ]
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.11-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
vendor/symfony/property-access/.gitignore
vendored
Normal file
3
vendor/symfony/property-access/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
29
vendor/symfony/property-access/CHANGELOG.md
vendored
Normal file
29
vendor/symfony/property-access/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
2.7.0
|
||||
------
|
||||
|
||||
* `UnexpectedTypeException` now expects three constructor arguments: The invalid property value,
|
||||
the `PropertyPathInterface` object and the current index of the property path.
|
||||
|
||||
2.5.0
|
||||
------
|
||||
|
||||
* allowed non alpha numeric characters in second level and deeper object properties names
|
||||
* [BC BREAK] when accessing an index on an object that does not implement
|
||||
ArrayAccess, a NoSuchIndexException is now thrown instead of the
|
||||
semantically wrong NoSuchPropertyException
|
||||
* [BC BREAK] added isReadable() and isWritable() to PropertyAccessorInterface
|
||||
|
||||
2.3.0
|
||||
------
|
||||
|
||||
* added PropertyAccessorBuilder, to enable or disable the support of "__call"
|
||||
* added support for "__call" in the PropertyAccessor (disabled by default)
|
||||
* [BC BREAK] changed PropertyAccessor to continue its search for a property or
|
||||
method even if a non-public match was found. Before, a PropertyAccessDeniedException
|
||||
was thrown in this case. Class PropertyAccessDeniedException was removed
|
||||
now.
|
||||
* deprecated PropertyAccess::getPropertyAccessor
|
||||
* added PropertyAccess::createPropertyAccessor and PropertyAccess::createPropertyAccessorBuilder
|
||||
21
vendor/symfony/property-access/Exception/AccessException.php
vendored
Normal file
21
vendor/symfony/property-access/Exception/AccessException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\PropertyAccess\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a property path is not available.
|
||||
*
|
||||
* @author Stéphane Escandell <stephane.escandell@gmail.com>
|
||||
*/
|
||||
class AccessException extends RuntimeException
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/property-access/Exception/ExceptionInterface.php
vendored
Normal file
21
vendor/symfony/property-access/Exception/ExceptionInterface.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\PropertyAccess\Exception;
|
||||
|
||||
/**
|
||||
* Marker interface for the PropertyAccess component.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface ExceptionInterface
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/property-access/Exception/InvalidArgumentException.php
vendored
Normal file
21
vendor/symfony/property-access/Exception/InvalidArgumentException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\PropertyAccess\Exception;
|
||||
|
||||
/**
|
||||
* Base InvalidArgumentException for the PropertyAccess component.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/property-access/Exception/InvalidPropertyPathException.php
vendored
Normal file
21
vendor/symfony/property-access/Exception/InvalidPropertyPathException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\PropertyAccess\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a property path is malformed.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class InvalidPropertyPathException extends RuntimeException
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/property-access/Exception/NoSuchIndexException.php
vendored
Normal file
21
vendor/symfony/property-access/Exception/NoSuchIndexException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\PropertyAccess\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when an index cannot be found.
|
||||
*
|
||||
* @author Stéphane Escandell <stephane.escandell@gmail.com>
|
||||
*/
|
||||
class NoSuchIndexException extends AccessException
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/property-access/Exception/NoSuchPropertyException.php
vendored
Normal file
21
vendor/symfony/property-access/Exception/NoSuchPropertyException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\PropertyAccess\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a property cannot be found.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class NoSuchPropertyException extends AccessException
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/property-access/Exception/OutOfBoundsException.php
vendored
Normal file
21
vendor/symfony/property-access/Exception/OutOfBoundsException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\PropertyAccess\Exception;
|
||||
|
||||
/**
|
||||
* Base OutOfBoundsException for the PropertyAccess component.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/property-access/Exception/RuntimeException.php
vendored
Normal file
21
vendor/symfony/property-access/Exception/RuntimeException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\PropertyAccess\Exception;
|
||||
|
||||
/**
|
||||
* Base RuntimeException for the PropertyAccess component.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class RuntimeException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
50
vendor/symfony/property-access/Exception/UnexpectedTypeException.php
vendored
Normal file
50
vendor/symfony/property-access/Exception/UnexpectedTypeException.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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\PropertyAccess\Exception;
|
||||
|
||||
use Symfony\Component\PropertyAccess\PropertyPathInterface;
|
||||
|
||||
/**
|
||||
* Thrown when a value does not match an expected type.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class UnexpectedTypeException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* @param mixed $value The unexpected value found while traversing property path
|
||||
* @param PropertyPathInterface $path The property path
|
||||
* @param int $pathIndex The property path index when the unexpected value was found
|
||||
*/
|
||||
public function __construct($value, $path, $pathIndex = null)
|
||||
{
|
||||
if (3 === \func_num_args() && $path instanceof PropertyPathInterface) {
|
||||
$message = sprintf(
|
||||
'PropertyAccessor requires a graph of objects or arrays to operate on, '.
|
||||
'but it found type "%s" while trying to traverse path "%s" at property "%s".',
|
||||
\gettype($value),
|
||||
(string) $path,
|
||||
$path->getElement($pathIndex)
|
||||
);
|
||||
} else {
|
||||
@trigger_error('The '.__CLASS__.' constructor now expects 3 arguments: the invalid property value, the '.__NAMESPACE__.'\PropertyPathInterface object and the current index of the property path.', E_USER_DEPRECATED);
|
||||
|
||||
$message = sprintf(
|
||||
'Expected argument of type "%s", "%s" given',
|
||||
$path,
|
||||
\is_object($value) ? \get_class($value) : \gettype($value)
|
||||
);
|
||||
}
|
||||
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
19
vendor/symfony/property-access/LICENSE
vendored
Normal file
19
vendor/symfony/property-access/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2004-2018 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
62
vendor/symfony/property-access/PropertyAccess.php
vendored
Normal file
62
vendor/symfony/property-access/PropertyAccess.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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\PropertyAccess;
|
||||
|
||||
/**
|
||||
* Entry point of the PropertyAccess component.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
final class PropertyAccess
|
||||
{
|
||||
/**
|
||||
* Creates a property accessor with the default configuration.
|
||||
*
|
||||
* @return PropertyAccessor
|
||||
*/
|
||||
public static function createPropertyAccessor()
|
||||
{
|
||||
return self::createPropertyAccessorBuilder()->getPropertyAccessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a property accessor builder.
|
||||
*
|
||||
* @return PropertyAccessorBuilder
|
||||
*/
|
||||
public static function createPropertyAccessorBuilder()
|
||||
{
|
||||
return new PropertyAccessorBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of {@link createPropertyAccessor}.
|
||||
*
|
||||
* @return PropertyAccessor
|
||||
*
|
||||
* @deprecated since version 2.3, to be removed in 3.0.
|
||||
* Use {@link createPropertyAccessor()} instead.
|
||||
*/
|
||||
public static function getPropertyAccessor()
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.3 and will be removed in 3.0. Use the createPropertyAccessor() method instead.', E_USER_DEPRECATED);
|
||||
|
||||
return self::createPropertyAccessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* This class cannot be instantiated.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
812
vendor/symfony/property-access/PropertyAccessor.php
vendored
Normal file
812
vendor/symfony/property-access/PropertyAccessor.php
vendored
Normal file
@@ -0,0 +1,812 @@
|
||||
<?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\PropertyAccess;
|
||||
|
||||
use Symfony\Component\PropertyAccess\Exception\AccessException;
|
||||
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
|
||||
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
|
||||
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link PropertyAccessorInterface}.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class PropertyAccessor implements PropertyAccessorInterface
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const VALUE = 0;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const REF = 1;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const IS_REF_CHAINED = 2;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_HAS_PROPERTY = 0;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_TYPE = 1;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_NAME = 2;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_REF = 3;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_ADDER = 4;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_REMOVER = 5;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_TYPE_METHOD = 0;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_TYPE_PROPERTY = 1;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_TYPE_MAGIC = 2;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_TYPE_ADDER_AND_REMOVER = 3;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const ACCESS_TYPE_NOT_FOUND = 4;
|
||||
|
||||
private $magicCall;
|
||||
private $ignoreInvalidIndices;
|
||||
private $readPropertyCache = array();
|
||||
private $writePropertyCache = array();
|
||||
|
||||
private static $previousErrorHandler = false;
|
||||
private static $errorHandler = array(__CLASS__, 'handleError');
|
||||
private static $resultProto = array(self::VALUE => null);
|
||||
|
||||
/**
|
||||
* Should not be used by application code. Use
|
||||
* {@link PropertyAccess::createPropertyAccessor()} instead.
|
||||
*
|
||||
* @param bool $magicCall
|
||||
* @param bool $throwExceptionOnInvalidIndex
|
||||
*/
|
||||
public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false)
|
||||
{
|
||||
$this->magicCall = $magicCall;
|
||||
$this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue($objectOrArray, $propertyPath)
|
||||
{
|
||||
if (!$propertyPath instanceof PropertyPathInterface) {
|
||||
$propertyPath = new PropertyPath($propertyPath);
|
||||
}
|
||||
|
||||
$zval = array(
|
||||
self::VALUE => $objectOrArray,
|
||||
);
|
||||
$propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices);
|
||||
|
||||
return $propertyValues[\count($propertyValues) - 1][self::VALUE];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValue(&$objectOrArray, $propertyPath, $value)
|
||||
{
|
||||
if (!$propertyPath instanceof PropertyPathInterface) {
|
||||
$propertyPath = new PropertyPath($propertyPath);
|
||||
}
|
||||
|
||||
$zval = array(
|
||||
self::VALUE => $objectOrArray,
|
||||
self::REF => &$objectOrArray,
|
||||
);
|
||||
$propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1);
|
||||
$overwrite = true;
|
||||
|
||||
try {
|
||||
if (\PHP_VERSION_ID < 70000 && false === self::$previousErrorHandler) {
|
||||
self::$previousErrorHandler = set_error_handler(self::$errorHandler);
|
||||
}
|
||||
|
||||
for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) {
|
||||
$zval = $propertyValues[$i];
|
||||
unset($propertyValues[$i]);
|
||||
|
||||
// You only need set value for current element if:
|
||||
// 1. it's the parent of the last index element
|
||||
// OR
|
||||
// 2. its child is not passed by reference
|
||||
//
|
||||
// This may avoid uncessary value setting process for array elements.
|
||||
// For example:
|
||||
// '[a][b][c]' => 'old-value'
|
||||
// If you want to change its value to 'new-value',
|
||||
// you only need set value for '[a][b][c]' and it's safe to ignore '[a][b]' and '[a]'
|
||||
if ($overwrite) {
|
||||
$property = $propertyPath->getElement($i);
|
||||
|
||||
if ($propertyPath->isIndex($i)) {
|
||||
if ($overwrite = !isset($zval[self::REF])) {
|
||||
$ref = &$zval[self::REF];
|
||||
$ref = $zval[self::VALUE];
|
||||
}
|
||||
$this->writeIndex($zval, $property, $value);
|
||||
if ($overwrite) {
|
||||
$zval[self::VALUE] = $zval[self::REF];
|
||||
}
|
||||
} else {
|
||||
$this->writeProperty($zval, $property, $value);
|
||||
}
|
||||
|
||||
// if current element is an object
|
||||
// OR
|
||||
// if current element's reference chain is not broken - current element
|
||||
// as well as all its ancients in the property path are all passed by reference,
|
||||
// then there is no need to continue the value setting process
|
||||
if (\is_object($zval[self::VALUE]) || isset($zval[self::IS_REF_CHAINED])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$value = $zval[self::VALUE];
|
||||
}
|
||||
} catch (\TypeError $e) {
|
||||
try {
|
||||
self::throwInvalidArgumentException($e->getMessage(), $e->getTrace(), 0);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID < 70000 && false !== self::$previousErrorHandler) {
|
||||
restore_error_handler();
|
||||
self::$previousErrorHandler = false;
|
||||
}
|
||||
if (isset($e)) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function handleError($type, $message, $file, $line, $context)
|
||||
{
|
||||
if (E_RECOVERABLE_ERROR === $type) {
|
||||
self::throwInvalidArgumentException($message, debug_backtrace(false), 1);
|
||||
}
|
||||
|
||||
return null !== self::$previousErrorHandler && false !== \call_user_func(self::$previousErrorHandler, $type, $message, $file, $line, $context);
|
||||
}
|
||||
|
||||
private static function throwInvalidArgumentException($message, $trace, $i)
|
||||
{
|
||||
// the type mismatch is not caused by invalid arguments (but e.g. by an incompatible return type hint of the writer method)
|
||||
if (0 !== strpos($message, 'Argument ')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file'] && array_key_exists(0, $trace[$i]['args'])) {
|
||||
$pos = strpos($message, $delim = 'must be of the type ') ?: (strpos($message, $delim = 'must be an instance of ') ?: strpos($message, $delim = 'must implement interface '));
|
||||
$pos += \strlen($delim);
|
||||
$type = $trace[$i]['args'][0];
|
||||
$type = \is_object($type) ? \get_class($type) : \gettype($type);
|
||||
|
||||
throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given', substr($message, $pos, strpos($message, ',', $pos) - $pos), $type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable($objectOrArray, $propertyPath)
|
||||
{
|
||||
if (!$propertyPath instanceof PropertyPathInterface) {
|
||||
$propertyPath = new PropertyPath($propertyPath);
|
||||
}
|
||||
|
||||
try {
|
||||
$zval = array(
|
||||
self::VALUE => $objectOrArray,
|
||||
);
|
||||
$this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices);
|
||||
|
||||
return true;
|
||||
} catch (AccessException $e) {
|
||||
return false;
|
||||
} catch (UnexpectedTypeException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable($objectOrArray, $propertyPath)
|
||||
{
|
||||
if (!$propertyPath instanceof PropertyPathInterface) {
|
||||
$propertyPath = new PropertyPath($propertyPath);
|
||||
}
|
||||
|
||||
try {
|
||||
$zval = array(
|
||||
self::VALUE => $objectOrArray,
|
||||
);
|
||||
$propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1);
|
||||
|
||||
for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) {
|
||||
$zval = $propertyValues[$i];
|
||||
unset($propertyValues[$i]);
|
||||
|
||||
if ($propertyPath->isIndex($i)) {
|
||||
if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!$this->isPropertyWritable($zval[self::VALUE], $propertyPath->getElement($i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_object($zval[self::VALUE])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (AccessException $e) {
|
||||
return false;
|
||||
} catch (UnexpectedTypeException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the path from an object up to a given path index.
|
||||
*
|
||||
* @param array $zval The array containing the object or array to read from
|
||||
* @param PropertyPathInterface $propertyPath The property path to read
|
||||
* @param int $lastIndex The index up to which should be read
|
||||
* @param bool $ignoreInvalidIndices Whether to ignore invalid indices or throw an exception
|
||||
*
|
||||
* @return array The values read in the path
|
||||
*
|
||||
* @throws UnexpectedTypeException if a value within the path is neither object nor array
|
||||
* @throws NoSuchIndexException If a non-existing index is accessed
|
||||
*/
|
||||
private function readPropertiesUntil($zval, PropertyPathInterface $propertyPath, $lastIndex, $ignoreInvalidIndices = true)
|
||||
{
|
||||
if (!\is_object($zval[self::VALUE]) && !\is_array($zval[self::VALUE])) {
|
||||
throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, 0);
|
||||
}
|
||||
|
||||
// Add the root object to the list
|
||||
$propertyValues = array($zval);
|
||||
|
||||
for ($i = 0; $i < $lastIndex; ++$i) {
|
||||
$property = $propertyPath->getElement($i);
|
||||
$isIndex = $propertyPath->isIndex($i);
|
||||
|
||||
if ($isIndex) {
|
||||
// Create missing nested arrays on demand
|
||||
if (($zval[self::VALUE] instanceof \ArrayAccess && !$zval[self::VALUE]->offsetExists($property)) ||
|
||||
(\is_array($zval[self::VALUE]) && !isset($zval[self::VALUE][$property]) && !array_key_exists($property, $zval[self::VALUE]))
|
||||
) {
|
||||
if (!$ignoreInvalidIndices) {
|
||||
if (!\is_array($zval[self::VALUE])) {
|
||||
if (!$zval[self::VALUE] instanceof \Traversable) {
|
||||
throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s".', $property, (string) $propertyPath));
|
||||
}
|
||||
|
||||
$zval[self::VALUE] = iterator_to_array($zval[self::VALUE]);
|
||||
}
|
||||
|
||||
throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s". Available indices are "%s".', $property, (string) $propertyPath, print_r(array_keys($zval[self::VALUE]), true)));
|
||||
}
|
||||
|
||||
if ($i + 1 < $propertyPath->getLength()) {
|
||||
if (isset($zval[self::REF])) {
|
||||
$zval[self::VALUE][$property] = array();
|
||||
$zval[self::REF] = $zval[self::VALUE];
|
||||
} else {
|
||||
$zval[self::VALUE] = array($property => array());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$zval = $this->readIndex($zval, $property);
|
||||
} else {
|
||||
$zval = $this->readProperty($zval, $property);
|
||||
}
|
||||
|
||||
// the final value of the path must not be validated
|
||||
if ($i + 1 < $propertyPath->getLength() && !\is_object($zval[self::VALUE]) && !\is_array($zval[self::VALUE])) {
|
||||
throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, $i + 1);
|
||||
}
|
||||
|
||||
if (isset($zval[self::REF]) && (0 === $i || isset($propertyValues[$i - 1][self::IS_REF_CHAINED]))) {
|
||||
// Set the IS_REF_CHAINED flag to true if:
|
||||
// current property is passed by reference and
|
||||
// it is the first element in the property path or
|
||||
// the IS_REF_CHAINED flag of its parent element is true
|
||||
// Basically, this flag is true only when the reference chain from the top element to current element is not broken
|
||||
$zval[self::IS_REF_CHAINED] = true;
|
||||
}
|
||||
|
||||
$propertyValues[] = $zval;
|
||||
}
|
||||
|
||||
return $propertyValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a key from an array-like structure.
|
||||
*
|
||||
* @param array $zval The array containing the array or \ArrayAccess object to read from
|
||||
* @param string|int $index The key to read
|
||||
*
|
||||
* @return array The array containing the value of the key
|
||||
*
|
||||
* @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array
|
||||
*/
|
||||
private function readIndex($zval, $index)
|
||||
{
|
||||
if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
|
||||
throw new NoSuchIndexException(sprintf('Cannot read index "%s" from object of type "%s" because it doesn\'t implement \ArrayAccess.', $index, \get_class($zval[self::VALUE])));
|
||||
}
|
||||
|
||||
$result = self::$resultProto;
|
||||
|
||||
if (isset($zval[self::VALUE][$index])) {
|
||||
$result[self::VALUE] = $zval[self::VALUE][$index];
|
||||
|
||||
if (!isset($zval[self::REF])) {
|
||||
// Save creating references when doing read-only lookups
|
||||
} elseif (\is_array($zval[self::VALUE])) {
|
||||
$result[self::REF] = &$zval[self::REF][$index];
|
||||
} elseif (\is_object($result[self::VALUE])) {
|
||||
$result[self::REF] = $result[self::VALUE];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the a property from an object.
|
||||
*
|
||||
* @param array $zval The array containing the object to read from
|
||||
* @param string $property The property to read
|
||||
*
|
||||
* @return array The array containing the value of the property
|
||||
*
|
||||
* @throws NoSuchPropertyException if the property does not exist or is not public
|
||||
*/
|
||||
private function readProperty($zval, $property)
|
||||
{
|
||||
if (!\is_object($zval[self::VALUE])) {
|
||||
throw new NoSuchPropertyException(sprintf('Cannot read property "%s" from an array. Maybe you intended to write the property path as "[%1$s]" instead.', $property));
|
||||
}
|
||||
|
||||
$result = self::$resultProto;
|
||||
$object = $zval[self::VALUE];
|
||||
$access = $this->getReadAccessInfo(\get_class($object), $property);
|
||||
|
||||
if (self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]) {
|
||||
$result[self::VALUE] = $object->{$access[self::ACCESS_NAME]}();
|
||||
} elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) {
|
||||
$result[self::VALUE] = $object->{$access[self::ACCESS_NAME]};
|
||||
|
||||
if ($access[self::ACCESS_REF] && isset($zval[self::REF])) {
|
||||
$result[self::REF] = &$object->{$access[self::ACCESS_NAME]};
|
||||
}
|
||||
} elseif (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) {
|
||||
// Needed to support \stdClass instances. We need to explicitly
|
||||
// exclude $access[self::ACCESS_HAS_PROPERTY], otherwise if
|
||||
// a *protected* property was found on the class, property_exists()
|
||||
// returns true, consequently the following line will result in a
|
||||
// fatal error.
|
||||
|
||||
$result[self::VALUE] = $object->$property;
|
||||
if (isset($zval[self::REF])) {
|
||||
$result[self::REF] = &$object->$property;
|
||||
}
|
||||
} elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) {
|
||||
// we call the getter and hope the __call do the job
|
||||
$result[self::VALUE] = $object->{$access[self::ACCESS_NAME]}();
|
||||
} else {
|
||||
throw new NoSuchPropertyException($access[self::ACCESS_NAME]);
|
||||
}
|
||||
|
||||
// Objects are always passed around by reference
|
||||
if (isset($zval[self::REF]) && \is_object($result[self::VALUE])) {
|
||||
$result[self::REF] = $result[self::VALUE];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses how to read the property value.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getReadAccessInfo($class, $property)
|
||||
{
|
||||
$key = $class.'::'.$property;
|
||||
|
||||
if (isset($this->readPropertyCache[$key])) {
|
||||
$access = $this->readPropertyCache[$key];
|
||||
} else {
|
||||
$access = array();
|
||||
|
||||
$reflClass = new \ReflectionClass($class);
|
||||
$access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property);
|
||||
$camelProp = $this->camelize($property);
|
||||
$getter = 'get'.$camelProp;
|
||||
$getsetter = lcfirst($camelProp); // jQuery style, e.g. read: last(), write: last($item)
|
||||
$isser = 'is'.$camelProp;
|
||||
$hasser = 'has'.$camelProp;
|
||||
|
||||
if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
|
||||
$access[self::ACCESS_NAME] = $getter;
|
||||
} elseif ($reflClass->hasMethod($getsetter) && $reflClass->getMethod($getsetter)->isPublic()) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
|
||||
$access[self::ACCESS_NAME] = $getsetter;
|
||||
} elseif ($reflClass->hasMethod($isser) && $reflClass->getMethod($isser)->isPublic()) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
|
||||
$access[self::ACCESS_NAME] = $isser;
|
||||
} elseif ($reflClass->hasMethod($hasser) && $reflClass->getMethod($hasser)->isPublic()) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
|
||||
$access[self::ACCESS_NAME] = $hasser;
|
||||
} elseif ($reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->isPublic()) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
|
||||
$access[self::ACCESS_NAME] = $property;
|
||||
$access[self::ACCESS_REF] = false;
|
||||
} elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
|
||||
$access[self::ACCESS_NAME] = $property;
|
||||
$access[self::ACCESS_REF] = true;
|
||||
} elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) {
|
||||
// we call the getter and hope the __call do the job
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC;
|
||||
$access[self::ACCESS_NAME] = $getter;
|
||||
} else {
|
||||
$methods = array($getter, $getsetter, $isser, $hasser, '__get');
|
||||
if ($this->magicCall) {
|
||||
$methods[] = '__call';
|
||||
}
|
||||
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND;
|
||||
$access[self::ACCESS_NAME] = sprintf(
|
||||
'Neither the property "%s" nor one of the methods "%s()" '.
|
||||
'exist and have public access in class "%s".',
|
||||
$property,
|
||||
implode('()", "', $methods),
|
||||
$reflClass->name
|
||||
);
|
||||
}
|
||||
|
||||
$this->readPropertyCache[$key] = $access;
|
||||
}
|
||||
|
||||
return $access;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of an index in a given array-accessible value.
|
||||
*
|
||||
* @param array $zval The array containing the array or \ArrayAccess object to write to
|
||||
* @param string|int $index The index to write at
|
||||
* @param mixed $value The value to write
|
||||
*
|
||||
* @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array
|
||||
*/
|
||||
private function writeIndex($zval, $index, $value)
|
||||
{
|
||||
if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) {
|
||||
throw new NoSuchIndexException(sprintf('Cannot modify index "%s" in object of type "%s" because it doesn\'t implement \ArrayAccess', $index, \get_class($zval[self::VALUE])));
|
||||
}
|
||||
|
||||
$zval[self::REF][$index] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a property in the given object.
|
||||
*
|
||||
* @param array $zval The array containing the object to write to
|
||||
* @param string $property The property to write
|
||||
* @param mixed $value The value to write
|
||||
*
|
||||
* @throws NoSuchPropertyException if the property does not exist or is not public
|
||||
*/
|
||||
private function writeProperty($zval, $property, $value)
|
||||
{
|
||||
if (!\is_object($zval[self::VALUE])) {
|
||||
throw new NoSuchPropertyException(sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%1$s]" instead?', $property));
|
||||
}
|
||||
|
||||
$object = $zval[self::VALUE];
|
||||
$access = $this->getWriteAccessInfo(\get_class($object), $property, $value);
|
||||
|
||||
if (self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]) {
|
||||
$object->{$access[self::ACCESS_NAME]}($value);
|
||||
} elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) {
|
||||
$object->{$access[self::ACCESS_NAME]} = $value;
|
||||
} elseif (self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE]) {
|
||||
$this->writeCollection($zval, $property, $value, $access[self::ACCESS_ADDER], $access[self::ACCESS_REMOVER]);
|
||||
} elseif (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) {
|
||||
// Needed to support \stdClass instances. We need to explicitly
|
||||
// exclude $access[self::ACCESS_HAS_PROPERTY], otherwise if
|
||||
// a *protected* property was found on the class, property_exists()
|
||||
// returns true, consequently the following line will result in a
|
||||
// fatal error.
|
||||
|
||||
$object->$property = $value;
|
||||
} elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) {
|
||||
$object->{$access[self::ACCESS_NAME]}($value);
|
||||
} else {
|
||||
throw new NoSuchPropertyException($access[self::ACCESS_NAME]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts a collection-valued property by calling add*() and remove*() methods.
|
||||
*
|
||||
* @param array $zval The array containing the object to write to
|
||||
* @param string $property The property to write
|
||||
* @param iterable $collection The collection to write
|
||||
* @param string $addMethod The add*() method
|
||||
* @param string $removeMethod The remove*() method
|
||||
*/
|
||||
private function writeCollection($zval, $property, $collection, $addMethod, $removeMethod)
|
||||
{
|
||||
// At this point the add and remove methods have been found
|
||||
$previousValue = $this->readProperty($zval, $property);
|
||||
$previousValue = $previousValue[self::VALUE];
|
||||
|
||||
if ($previousValue instanceof \Traversable) {
|
||||
$previousValue = iterator_to_array($previousValue);
|
||||
}
|
||||
if ($previousValue && \is_array($previousValue)) {
|
||||
if (\is_object($collection)) {
|
||||
$collection = iterator_to_array($collection);
|
||||
}
|
||||
foreach ($previousValue as $key => $item) {
|
||||
if (!\in_array($item, $collection, true)) {
|
||||
unset($previousValue[$key]);
|
||||
$zval[self::VALUE]->{$removeMethod}($item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$previousValue = false;
|
||||
}
|
||||
|
||||
foreach ($collection as $item) {
|
||||
if (!$previousValue || !\in_array($item, $previousValue, true)) {
|
||||
$zval[self::VALUE]->{$addMethod}($item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses how to write the property value.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $property
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getWriteAccessInfo($class, $property, $value)
|
||||
{
|
||||
$key = $class.'::'.$property;
|
||||
|
||||
if (isset($this->writePropertyCache[$key])) {
|
||||
$access = $this->writePropertyCache[$key];
|
||||
} else {
|
||||
$access = array();
|
||||
|
||||
$reflClass = new \ReflectionClass($class);
|
||||
$access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property);
|
||||
$camelized = $this->camelize($property);
|
||||
$singulars = (array) StringUtil::singularify($camelized);
|
||||
|
||||
if (\is_array($value) || $value instanceof \Traversable) {
|
||||
$methods = $this->findAdderAndRemover($reflClass, $singulars);
|
||||
|
||||
if (null !== $methods) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER;
|
||||
$access[self::ACCESS_ADDER] = $methods[0];
|
||||
$access[self::ACCESS_REMOVER] = $methods[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($access[self::ACCESS_TYPE])) {
|
||||
$setter = 'set'.$camelized;
|
||||
$getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item)
|
||||
|
||||
if ($this->isMethodAccessible($reflClass, $setter, 1)) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
|
||||
$access[self::ACCESS_NAME] = $setter;
|
||||
} elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
|
||||
$access[self::ACCESS_NAME] = $getsetter;
|
||||
} elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
|
||||
$access[self::ACCESS_NAME] = $property;
|
||||
} elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
|
||||
$access[self::ACCESS_NAME] = $property;
|
||||
} elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) {
|
||||
// we call the getter and hope the __call do the job
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC;
|
||||
$access[self::ACCESS_NAME] = $setter;
|
||||
} elseif (null !== $methods = $this->findAdderAndRemover($reflClass, $singulars)) {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND;
|
||||
$access[self::ACCESS_NAME] = sprintf(
|
||||
'The property "%s" in class "%s" can be defined with the methods "%s()" but '.
|
||||
'the new value must be an array or an instance of \Traversable, '.
|
||||
'"%s" given.',
|
||||
$property,
|
||||
$reflClass->name,
|
||||
implode('()", "', $methods),
|
||||
\is_object($value) ? \get_class($value) : \gettype($value)
|
||||
);
|
||||
} else {
|
||||
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND;
|
||||
$access[self::ACCESS_NAME] = sprintf(
|
||||
'Neither the property "%s" nor one of the methods %s"%s()", "%s()", '.
|
||||
'"__set()" or "__call()" exist and have public access in class "%s".',
|
||||
$property,
|
||||
implode('', array_map(function ($singular) {
|
||||
return '"add'.$singular.'()"/"remove'.$singular.'()", ';
|
||||
}, $singulars)),
|
||||
$setter,
|
||||
$getsetter,
|
||||
$reflClass->name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->writePropertyCache[$key] = $access;
|
||||
}
|
||||
|
||||
return $access;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a property is writable in the given object.
|
||||
*
|
||||
* @param object $object The object to write to
|
||||
* @param string $property The property to write
|
||||
*
|
||||
* @return bool Whether the property is writable
|
||||
*/
|
||||
private function isPropertyWritable($object, $property)
|
||||
{
|
||||
if (!\is_object($object)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$access = $this->getWriteAccessInfo(\get_class($object), $property, array());
|
||||
|
||||
return self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]
|
||||
|| self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]
|
||||
|| self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE]
|
||||
|| (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property))
|
||||
|| self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Camelizes a given string.
|
||||
*
|
||||
* @param string $string Some string
|
||||
*
|
||||
* @return string The camelized version of the string
|
||||
*/
|
||||
private function camelize($string)
|
||||
{
|
||||
return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for add and remove methods.
|
||||
*
|
||||
* @param \ReflectionClass $reflClass The reflection class for the given object
|
||||
* @param array $singulars The singular form of the property name or null
|
||||
*
|
||||
* @return array|null An array containing the adder and remover when found, null otherwise
|
||||
*/
|
||||
private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars)
|
||||
{
|
||||
foreach ($singulars as $singular) {
|
||||
$addMethod = 'add'.$singular;
|
||||
$removeMethod = 'remove'.$singular;
|
||||
|
||||
$addMethodFound = $this->isMethodAccessible($reflClass, $addMethod, 1);
|
||||
$removeMethodFound = $this->isMethodAccessible($reflClass, $removeMethod, 1);
|
||||
|
||||
if ($addMethodFound && $removeMethodFound) {
|
||||
return array($addMethod, $removeMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a method is public and has the number of required parameters.
|
||||
*
|
||||
* @param \ReflectionClass $class The class of the method
|
||||
* @param string $methodName The method name
|
||||
* @param int $parameters The number of parameters
|
||||
*
|
||||
* @return bool Whether the method is public and has $parameters required parameters
|
||||
*/
|
||||
private function isMethodAccessible(\ReflectionClass $class, $methodName, $parameters)
|
||||
{
|
||||
if ($class->hasMethod($methodName)) {
|
||||
$method = $class->getMethod($methodName);
|
||||
|
||||
if ($method->isPublic()
|
||||
&& $method->getNumberOfRequiredParameters() <= $parameters
|
||||
&& $method->getNumberOfParameters() >= $parameters) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
102
vendor/symfony/property-access/PropertyAccessorBuilder.php
vendored
Normal file
102
vendor/symfony/property-access/PropertyAccessorBuilder.php
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
<?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\PropertyAccess;
|
||||
|
||||
/**
|
||||
* A configurable builder to create a PropertyAccessor.
|
||||
*
|
||||
* @author Jérémie Augustin <jeremie.augustin@pixel-cookers.com>
|
||||
*/
|
||||
class PropertyAccessorBuilder
|
||||
{
|
||||
private $magicCall = false;
|
||||
private $throwExceptionOnInvalidIndex = false;
|
||||
|
||||
/**
|
||||
* Enables the use of "__call" by the PropertyAccessor.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function enableMagicCall()
|
||||
{
|
||||
$this->magicCall = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the use of "__call" by the PropertyAccessor.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function disableMagicCall()
|
||||
{
|
||||
$this->magicCall = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool whether the use of "__call" by the PropertyAccessor is enabled
|
||||
*/
|
||||
public function isMagicCallEnabled()
|
||||
{
|
||||
return $this->magicCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables exceptions when reading a non-existing index.
|
||||
*
|
||||
* This has no influence on writing non-existing indices with PropertyAccessorInterface::setValue()
|
||||
* which are always created on-the-fly.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function enableExceptionOnInvalidIndex()
|
||||
{
|
||||
$this->throwExceptionOnInvalidIndex = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables exceptions when reading a non-existing index.
|
||||
*
|
||||
* Instead, null is returned when calling PropertyAccessorInterface::getValue() on a non-existing index.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function disableExceptionOnInvalidIndex()
|
||||
{
|
||||
$this->throwExceptionOnInvalidIndex = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool whether an exception is thrown or null is returned when reading a non-existing index
|
||||
*/
|
||||
public function isExceptionOnInvalidIndexEnabled()
|
||||
{
|
||||
return $this->throwExceptionOnInvalidIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a new PropertyAccessor object.
|
||||
*
|
||||
* @return PropertyAccessorInterface The built PropertyAccessor
|
||||
*/
|
||||
public function getPropertyAccessor()
|
||||
{
|
||||
return new PropertyAccessor($this->magicCall, $this->throwExceptionOnInvalidIndex);
|
||||
}
|
||||
}
|
||||
114
vendor/symfony/property-access/PropertyAccessorInterface.php
vendored
Normal file
114
vendor/symfony/property-access/PropertyAccessorInterface.php
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
<?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\PropertyAccess;
|
||||
|
||||
/**
|
||||
* Writes and reads values to/from an object/array graph.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface PropertyAccessorInterface
|
||||
{
|
||||
/**
|
||||
* Sets the value at the end of the property path of the object graph.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
*
|
||||
* $propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
*
|
||||
* echo $propertyAccessor->setValue($object, 'child.name', 'Fabien');
|
||||
* // equals echo $object->getChild()->setName('Fabien');
|
||||
*
|
||||
* This method first tries to find a public setter for each property in the
|
||||
* path. The name of the setter must be the camel-cased property name
|
||||
* prefixed with "set".
|
||||
*
|
||||
* If the setter does not exist, this method tries to find a public
|
||||
* property. The value of the property is then changed.
|
||||
*
|
||||
* If neither is found, an exception is thrown.
|
||||
*
|
||||
* @param object|array $objectOrArray The object or array to modify
|
||||
* @param string|PropertyPathInterface $propertyPath The property path to modify
|
||||
* @param mixed $value The value to set at the end of the property path
|
||||
*
|
||||
* @throws Exception\InvalidArgumentException If the property path is invalid
|
||||
* @throws Exception\AccessException If a property/index does not exist or is not public
|
||||
* @throws Exception\UnexpectedTypeException If a value within the path is neither object nor array
|
||||
*/
|
||||
public function setValue(&$objectOrArray, $propertyPath, $value);
|
||||
|
||||
/**
|
||||
* Returns the value at the end of the property path of the object graph.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
*
|
||||
* $propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
*
|
||||
* echo $propertyAccessor->getValue($object, 'child.name);
|
||||
* // equals echo $object->getChild()->getName();
|
||||
*
|
||||
* This method first tries to find a public getter for each property in the
|
||||
* path. The name of the getter must be the camel-cased property name
|
||||
* prefixed with "get", "is", or "has".
|
||||
*
|
||||
* If the getter does not exist, this method tries to find a public
|
||||
* property. The value of the property is then returned.
|
||||
*
|
||||
* If none of them are found, an exception is thrown.
|
||||
*
|
||||
* @param object|array $objectOrArray The object or array to traverse
|
||||
* @param string|PropertyPathInterface $propertyPath The property path to read
|
||||
*
|
||||
* @return mixed The value at the end of the property path
|
||||
*
|
||||
* @throws Exception\InvalidArgumentException If the property path is invalid
|
||||
* @throws Exception\AccessException If a property/index does not exist or is not public
|
||||
* @throws Exception\UnexpectedTypeException If a value within the path is neither object
|
||||
* nor array
|
||||
*/
|
||||
public function getValue($objectOrArray, $propertyPath);
|
||||
|
||||
/**
|
||||
* Returns whether a value can be written at a given property path.
|
||||
*
|
||||
* Whenever this method returns true, {@link setValue()} is guaranteed not
|
||||
* to throw an exception when called with the same arguments.
|
||||
*
|
||||
* @param object|array $objectOrArray The object or array to check
|
||||
* @param string|PropertyPathInterface $propertyPath The property path to check
|
||||
*
|
||||
* @return bool Whether the value can be set
|
||||
*
|
||||
* @throws Exception\InvalidArgumentException If the property path is invalid
|
||||
*/
|
||||
public function isWritable($objectOrArray, $propertyPath);
|
||||
|
||||
/**
|
||||
* Returns whether a property path can be read from an object graph.
|
||||
*
|
||||
* Whenever this method returns true, {@link getValue()} is guaranteed not
|
||||
* to throw an exception when called with the same arguments.
|
||||
*
|
||||
* @param object|array $objectOrArray The object or array to check
|
||||
* @param string|PropertyPathInterface $propertyPath The property path to check
|
||||
*
|
||||
* @return bool Whether the property path can be read
|
||||
*
|
||||
* @throws Exception\InvalidArgumentException If the property path is invalid
|
||||
*/
|
||||
public function isReadable($objectOrArray, $propertyPath);
|
||||
}
|
||||
205
vendor/symfony/property-access/PropertyPath.php
vendored
Normal file
205
vendor/symfony/property-access/PropertyPath.php
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
<?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\PropertyAccess;
|
||||
|
||||
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException;
|
||||
use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link PropertyPathInterface}.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class PropertyPath implements \IteratorAggregate, PropertyPathInterface
|
||||
{
|
||||
/**
|
||||
* Character used for separating between plural and singular of an element.
|
||||
*/
|
||||
const SINGULAR_SEPARATOR = '|';
|
||||
|
||||
/**
|
||||
* The elements of the property path.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $elements = array();
|
||||
|
||||
/**
|
||||
* The number of elements in the property path.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $length;
|
||||
|
||||
/**
|
||||
* Contains a Boolean for each property in $elements denoting whether this
|
||||
* element is an index. It is a property otherwise.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $isIndex = array();
|
||||
|
||||
/**
|
||||
* String representation of the path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $pathAsString;
|
||||
|
||||
/**
|
||||
* Constructs a property path from a string.
|
||||
*
|
||||
* @param PropertyPath|string $propertyPath The property path as string or instance
|
||||
*
|
||||
* @throws InvalidArgumentException If the given path is not a string
|
||||
* @throws InvalidPropertyPathException If the syntax of the property path is not valid
|
||||
*/
|
||||
public function __construct($propertyPath)
|
||||
{
|
||||
// Can be used as copy constructor
|
||||
if ($propertyPath instanceof self) {
|
||||
/* @var PropertyPath $propertyPath */
|
||||
$this->elements = $propertyPath->elements;
|
||||
$this->length = $propertyPath->length;
|
||||
$this->isIndex = $propertyPath->isIndex;
|
||||
$this->pathAsString = $propertyPath->pathAsString;
|
||||
|
||||
return;
|
||||
}
|
||||
if (!\is_string($propertyPath)) {
|
||||
throw new InvalidArgumentException(sprintf('The property path constructor needs a string or an instance of "Symfony\Component\PropertyAccess\PropertyPath". Got: "%s"', \is_object($propertyPath) ? \get_class($propertyPath) : \gettype($propertyPath)));
|
||||
}
|
||||
|
||||
if ('' === $propertyPath) {
|
||||
throw new InvalidPropertyPathException('The property path should not be empty.');
|
||||
}
|
||||
|
||||
$this->pathAsString = $propertyPath;
|
||||
$position = 0;
|
||||
$remaining = $propertyPath;
|
||||
|
||||
// first element is evaluated differently - no leading dot for properties
|
||||
$pattern = '/^(([^\.\[]++)|\[([^\]]++)\])(.*)/';
|
||||
|
||||
while (preg_match($pattern, $remaining, $matches)) {
|
||||
if ('' !== $matches[2]) {
|
||||
$element = $matches[2];
|
||||
$this->isIndex[] = false;
|
||||
} else {
|
||||
$element = $matches[3];
|
||||
$this->isIndex[] = true;
|
||||
}
|
||||
|
||||
$this->elements[] = $element;
|
||||
|
||||
$position += \strlen($matches[1]);
|
||||
$remaining = $matches[4];
|
||||
$pattern = '/^(\.([^\.|\[]++)|\[([^\]]++)\])(.*)/';
|
||||
}
|
||||
|
||||
if ('' !== $remaining) {
|
||||
throw new InvalidPropertyPathException(sprintf('Could not parse property path "%s". Unexpected token "%s" at position %d', $propertyPath, $remaining[0], $position));
|
||||
}
|
||||
|
||||
$this->length = \count($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->pathAsString;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLength()
|
||||
{
|
||||
return $this->length;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
if ($this->length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent = clone $this;
|
||||
|
||||
--$parent->length;
|
||||
$parent->pathAsString = substr($parent->pathAsString, 0, max(strrpos($parent->pathAsString, '.'), strrpos($parent->pathAsString, '[')));
|
||||
array_pop($parent->elements);
|
||||
array_pop($parent->isIndex);
|
||||
|
||||
return $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new iterator for this path.
|
||||
*
|
||||
* @return PropertyPathIteratorInterface
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new PropertyPathIterator($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getElements()
|
||||
{
|
||||
return $this->elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getElement($index)
|
||||
{
|
||||
if (!isset($this->elements[$index])) {
|
||||
throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index));
|
||||
}
|
||||
|
||||
return $this->elements[$index];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isProperty($index)
|
||||
{
|
||||
if (!isset($this->isIndex[$index])) {
|
||||
throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index));
|
||||
}
|
||||
|
||||
return !$this->isIndex[$index];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isIndex($index)
|
||||
{
|
||||
if (!isset($this->isIndex[$index])) {
|
||||
throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index));
|
||||
}
|
||||
|
||||
return $this->isIndex[$index];
|
||||
}
|
||||
}
|
||||
299
vendor/symfony/property-access/PropertyPathBuilder.php
vendored
Normal file
299
vendor/symfony/property-access/PropertyPathBuilder.php
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
<?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\PropertyAccess;
|
||||
|
||||
use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class PropertyPathBuilder
|
||||
{
|
||||
private $elements = array();
|
||||
private $isIndex = array();
|
||||
|
||||
/**
|
||||
* Creates a new property path builder.
|
||||
*
|
||||
* @param PropertyPathInterface|string|null $path The path to initially store
|
||||
* in the builder. Optional.
|
||||
*/
|
||||
public function __construct($path = null)
|
||||
{
|
||||
if (null !== $path) {
|
||||
$this->append($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a (sub-) path to the current path.
|
||||
*
|
||||
* @param PropertyPathInterface|string $path The path to append
|
||||
* @param int $offset The offset where the appended
|
||||
* piece starts in $path
|
||||
* @param int $length The length of the appended piece
|
||||
* If 0, the full path is appended
|
||||
*/
|
||||
public function append($path, $offset = 0, $length = 0)
|
||||
{
|
||||
if (\is_string($path)) {
|
||||
$path = new PropertyPath($path);
|
||||
}
|
||||
|
||||
if (0 === $length) {
|
||||
$end = $path->getLength();
|
||||
} else {
|
||||
$end = $offset + $length;
|
||||
}
|
||||
|
||||
for (; $offset < $end; ++$offset) {
|
||||
$this->elements[] = $path->getElement($offset);
|
||||
$this->isIndex[] = $path->isIndex($offset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an index element to the current path.
|
||||
*
|
||||
* @param string $name The name of the appended index
|
||||
*/
|
||||
public function appendIndex($name)
|
||||
{
|
||||
$this->elements[] = $name;
|
||||
$this->isIndex[] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a property element to the current path.
|
||||
*
|
||||
* @param string $name The name of the appended property
|
||||
*/
|
||||
public function appendProperty($name)
|
||||
{
|
||||
$this->elements[] = $name;
|
||||
$this->isIndex[] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes elements from the current path.
|
||||
*
|
||||
* @param int $offset The offset at which to remove
|
||||
* @param int $length The length of the removed piece
|
||||
*
|
||||
* @throws OutOfBoundsException if offset is invalid
|
||||
*/
|
||||
public function remove($offset, $length = 1)
|
||||
{
|
||||
if (!isset($this->elements[$offset])) {
|
||||
throw new OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset));
|
||||
}
|
||||
|
||||
$this->resize($offset, $length, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a sub-path by a different (sub-) path.
|
||||
*
|
||||
* @param int $offset The offset at which to replace
|
||||
* @param int $length The length of the piece to replace
|
||||
* @param PropertyPathInterface|string $path The path to insert
|
||||
* @param int $pathOffset The offset where the inserted piece
|
||||
* starts in $path
|
||||
* @param int $pathLength The length of the inserted piece
|
||||
* If 0, the full path is inserted
|
||||
*
|
||||
* @throws OutOfBoundsException If the offset is invalid
|
||||
*/
|
||||
public function replace($offset, $length, $path, $pathOffset = 0, $pathLength = 0)
|
||||
{
|
||||
if (\is_string($path)) {
|
||||
$path = new PropertyPath($path);
|
||||
}
|
||||
|
||||
if ($offset < 0 && abs($offset) <= $this->getLength()) {
|
||||
$offset = $this->getLength() + $offset;
|
||||
} elseif (!isset($this->elements[$offset])) {
|
||||
throw new OutOfBoundsException('The offset '.$offset.' is not within the property path');
|
||||
}
|
||||
|
||||
if (0 === $pathLength) {
|
||||
$pathLength = $path->getLength() - $pathOffset;
|
||||
}
|
||||
|
||||
$this->resize($offset, $length, $pathLength);
|
||||
|
||||
for ($i = 0; $i < $pathLength; ++$i) {
|
||||
$this->elements[$offset + $i] = $path->getElement($pathOffset + $i);
|
||||
$this->isIndex[$offset + $i] = $path->isIndex($pathOffset + $i);
|
||||
}
|
||||
ksort($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a property element by an index element.
|
||||
*
|
||||
* @param int $offset The offset at which to replace
|
||||
* @param string $name The new name of the element. Optional
|
||||
*
|
||||
* @throws OutOfBoundsException If the offset is invalid
|
||||
*/
|
||||
public function replaceByIndex($offset, $name = null)
|
||||
{
|
||||
if (!isset($this->elements[$offset])) {
|
||||
throw new OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset));
|
||||
}
|
||||
|
||||
if (null !== $name) {
|
||||
$this->elements[$offset] = $name;
|
||||
}
|
||||
|
||||
$this->isIndex[$offset] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces an index element by a property element.
|
||||
*
|
||||
* @param int $offset The offset at which to replace
|
||||
* @param string $name The new name of the element. Optional
|
||||
*
|
||||
* @throws OutOfBoundsException If the offset is invalid
|
||||
*/
|
||||
public function replaceByProperty($offset, $name = null)
|
||||
{
|
||||
if (!isset($this->elements[$offset])) {
|
||||
throw new OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset));
|
||||
}
|
||||
|
||||
if (null !== $name) {
|
||||
$this->elements[$offset] = $name;
|
||||
}
|
||||
|
||||
$this->isIndex[$offset] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the current path.
|
||||
*
|
||||
* @return int The path length
|
||||
*/
|
||||
public function getLength()
|
||||
{
|
||||
return \count($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current property path.
|
||||
*
|
||||
* @return PropertyPathInterface The constructed property path
|
||||
*/
|
||||
public function getPropertyPath()
|
||||
{
|
||||
$pathAsString = $this->__toString();
|
||||
|
||||
return '' !== $pathAsString ? new PropertyPath($pathAsString) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current property path as string.
|
||||
*
|
||||
* @return string The property path as string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$string = '';
|
||||
|
||||
foreach ($this->elements as $offset => $element) {
|
||||
if ($this->isIndex[$offset]) {
|
||||
$element = '['.$element.']';
|
||||
} elseif ('' !== $string) {
|
||||
$string .= '.';
|
||||
}
|
||||
|
||||
$string .= $element;
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the path so that a chunk of length $cutLength is
|
||||
* removed at $offset and another chunk of length $insertionLength
|
||||
* can be inserted.
|
||||
*
|
||||
* @param int $offset The offset where the removed chunk starts
|
||||
* @param int $cutLength The length of the removed chunk
|
||||
* @param int $insertionLength The length of the inserted chunk
|
||||
*/
|
||||
private function resize($offset, $cutLength, $insertionLength)
|
||||
{
|
||||
// Nothing else to do in this case
|
||||
if ($insertionLength === $cutLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
$length = \count($this->elements);
|
||||
|
||||
if ($cutLength > $insertionLength) {
|
||||
// More elements should be removed than inserted
|
||||
$diff = $cutLength - $insertionLength;
|
||||
$newLength = $length - $diff;
|
||||
|
||||
// Shift elements to the left (left-to-right until the new end)
|
||||
// Max allowed offset to be shifted is such that
|
||||
// $offset + $diff < $length (otherwise invalid index access)
|
||||
// i.e. $offset < $length - $diff = $newLength
|
||||
for ($i = $offset; $i < $newLength; ++$i) {
|
||||
$this->elements[$i] = $this->elements[$i + $diff];
|
||||
$this->isIndex[$i] = $this->isIndex[$i + $diff];
|
||||
}
|
||||
|
||||
// All remaining elements should be removed
|
||||
for (; $i < $length; ++$i) {
|
||||
unset($this->elements[$i], $this->isIndex[$i]);
|
||||
}
|
||||
} else {
|
||||
$diff = $insertionLength - $cutLength;
|
||||
|
||||
$newLength = $length + $diff;
|
||||
$indexAfterInsertion = $offset + $insertionLength;
|
||||
|
||||
// $diff <= $insertionLength
|
||||
// $indexAfterInsertion >= $insertionLength
|
||||
// => $diff <= $indexAfterInsertion
|
||||
|
||||
// In each of the following loops, $i >= $diff must hold,
|
||||
// otherwise ($i - $diff) becomes negative.
|
||||
|
||||
// Shift old elements to the right to make up space for the
|
||||
// inserted elements. This needs to be done left-to-right in
|
||||
// order to preserve an ascending array index order
|
||||
// Since $i = max($length, $indexAfterInsertion) and $indexAfterInsertion >= $diff,
|
||||
// $i >= $diff is guaranteed.
|
||||
for ($i = max($length, $indexAfterInsertion); $i < $newLength; ++$i) {
|
||||
$this->elements[$i] = $this->elements[$i - $diff];
|
||||
$this->isIndex[$i] = $this->isIndex[$i - $diff];
|
||||
}
|
||||
|
||||
// Shift remaining elements to the right. Do this right-to-left
|
||||
// so we don't overwrite elements before copying them
|
||||
// The last written index is the immediate index after the inserted
|
||||
// string, because the indices before that will be overwritten
|
||||
// anyway.
|
||||
// Since $i >= $indexAfterInsertion and $indexAfterInsertion >= $diff,
|
||||
// $i >= $diff is guaranteed.
|
||||
for ($i = $length - 1; $i >= $indexAfterInsertion; --$i) {
|
||||
$this->elements[$i] = $this->elements[$i - $diff];
|
||||
$this->isIndex[$i] = $this->isIndex[$i - $diff];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
vendor/symfony/property-access/PropertyPathInterface.php
vendored
Normal file
86
vendor/symfony/property-access/PropertyPathInterface.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?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\PropertyAccess;
|
||||
|
||||
/**
|
||||
* A sequence of property names or array indices.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface PropertyPathInterface extends \Traversable
|
||||
{
|
||||
/**
|
||||
* Returns the string representation of the property path.
|
||||
*
|
||||
* @return string The path as string
|
||||
*/
|
||||
public function __toString();
|
||||
|
||||
/**
|
||||
* Returns the length of the property path, i.e. the number of elements.
|
||||
*
|
||||
* @return int The path length
|
||||
*/
|
||||
public function getLength();
|
||||
|
||||
/**
|
||||
* Returns the parent property path.
|
||||
*
|
||||
* The parent property path is the one that contains the same items as
|
||||
* this one except for the last one.
|
||||
*
|
||||
* If this property path only contains one item, null is returned.
|
||||
*
|
||||
* @return PropertyPath The parent path or null
|
||||
*/
|
||||
public function getParent();
|
||||
|
||||
/**
|
||||
* Returns the elements of the property path as array.
|
||||
*
|
||||
* @return array An array of property/index names
|
||||
*/
|
||||
public function getElements();
|
||||
|
||||
/**
|
||||
* Returns the element at the given index in the property path.
|
||||
*
|
||||
* @param int $index The index key
|
||||
*
|
||||
* @return string A property or index name
|
||||
*
|
||||
* @throws Exception\OutOfBoundsException If the offset is invalid
|
||||
*/
|
||||
public function getElement($index);
|
||||
|
||||
/**
|
||||
* Returns whether the element at the given index is a property.
|
||||
*
|
||||
* @param int $index The index in the property path
|
||||
*
|
||||
* @return bool Whether the element at this index is a property
|
||||
*
|
||||
* @throws Exception\OutOfBoundsException If the offset is invalid
|
||||
*/
|
||||
public function isProperty($index);
|
||||
|
||||
/**
|
||||
* Returns whether the element at the given index is an array index.
|
||||
*
|
||||
* @param int $index The index in the property path
|
||||
*
|
||||
* @return bool Whether the element at this index is an array index
|
||||
*
|
||||
* @throws Exception\OutOfBoundsException If the offset is invalid
|
||||
*/
|
||||
public function isIndex($index);
|
||||
}
|
||||
49
vendor/symfony/property-access/PropertyPathIterator.php
vendored
Normal file
49
vendor/symfony/property-access/PropertyPathIterator.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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\PropertyAccess;
|
||||
|
||||
/**
|
||||
* Traverses a property path and provides additional methods to find out
|
||||
* information about the current element.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class PropertyPathIterator extends \ArrayIterator implements PropertyPathIteratorInterface
|
||||
{
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @param PropertyPathInterface $path The property path to traverse
|
||||
*/
|
||||
public function __construct(PropertyPathInterface $path)
|
||||
{
|
||||
parent::__construct($path->getElements());
|
||||
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isIndex()
|
||||
{
|
||||
return $this->path->isIndex($this->key());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isProperty()
|
||||
{
|
||||
return $this->path->isProperty($this->key());
|
||||
}
|
||||
}
|
||||
34
vendor/symfony/property-access/PropertyPathIteratorInterface.php
vendored
Normal file
34
vendor/symfony/property-access/PropertyPathIteratorInterface.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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\PropertyAccess;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface PropertyPathIteratorInterface extends \Iterator, \SeekableIterator
|
||||
{
|
||||
/**
|
||||
* Returns whether the current element in the property path is an array
|
||||
* index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isIndex();
|
||||
|
||||
/**
|
||||
* Returns whether the current element in the property path is a property
|
||||
* name.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isProperty();
|
||||
}
|
||||
14
vendor/symfony/property-access/README.md
vendored
Normal file
14
vendor/symfony/property-access/README.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
PropertyAccess Component
|
||||
========================
|
||||
|
||||
The PropertyAccess component provides function to read and write from/to an
|
||||
object or array using a simple string notation.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/property_access/index.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
226
vendor/symfony/property-access/StringUtil.php
vendored
Normal file
226
vendor/symfony/property-access/StringUtil.php
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
<?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\PropertyAccess;
|
||||
|
||||
/**
|
||||
* Creates singulars from plurals.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class StringUtil
|
||||
{
|
||||
/**
|
||||
* Map english plural to singular suffixes.
|
||||
*
|
||||
* @see http://english-zone.com/spelling/plurals.html
|
||||
*/
|
||||
private static $pluralMap = array(
|
||||
// First entry: plural suffix, reversed
|
||||
// Second entry: length of plural suffix
|
||||
// Third entry: Whether the suffix may succeed a vocal
|
||||
// Fourth entry: Whether the suffix may succeed a consonant
|
||||
// Fifth entry: singular suffix, normal
|
||||
|
||||
// bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
|
||||
array('a', 1, true, true, array('on', 'um')),
|
||||
|
||||
// nebulae (nebula)
|
||||
array('ea', 2, true, true, 'a'),
|
||||
|
||||
// services (service)
|
||||
array('secivres', 8, true, true, 'service'),
|
||||
|
||||
// mice (mouse), lice (louse)
|
||||
array('eci', 3, false, true, 'ouse'),
|
||||
|
||||
// geese (goose)
|
||||
array('esee', 4, false, true, 'oose'),
|
||||
|
||||
// fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
|
||||
array('i', 1, true, true, 'us'),
|
||||
|
||||
// men (man), women (woman)
|
||||
array('nem', 3, true, true, 'man'),
|
||||
|
||||
// children (child)
|
||||
array('nerdlihc', 8, true, true, 'child'),
|
||||
|
||||
// oxen (ox)
|
||||
array('nexo', 4, false, false, 'ox'),
|
||||
|
||||
// indices (index), appendices (appendix), prices (price)
|
||||
array('seci', 4, false, true, array('ex', 'ix', 'ice')),
|
||||
|
||||
// selfies (selfie)
|
||||
array('seifles', 7, true, true, 'selfie'),
|
||||
|
||||
// movies (movie)
|
||||
array('seivom', 6, true, true, 'movie'),
|
||||
|
||||
// feet (foot)
|
||||
array('teef', 4, true, true, 'foot'),
|
||||
|
||||
// geese (goose)
|
||||
array('eseeg', 5, true, true, 'goose'),
|
||||
|
||||
// teeth (tooth)
|
||||
array('hteet', 5, true, true, 'tooth'),
|
||||
|
||||
// news (news)
|
||||
array('swen', 4, true, true, 'news'),
|
||||
|
||||
// series (series)
|
||||
array('seires', 6, true, true, 'series'),
|
||||
|
||||
// babies (baby)
|
||||
array('sei', 3, false, true, 'y'),
|
||||
|
||||
// accesses (access), addresses (address), kisses (kiss)
|
||||
array('sess', 4, true, false, 'ss'),
|
||||
|
||||
// analyses (analysis), ellipses (ellipsis), funguses (fungus),
|
||||
// neuroses (neurosis), theses (thesis), emphases (emphasis),
|
||||
// oases (oasis), crises (crisis), houses (house), bases (base),
|
||||
// atlases (atlas)
|
||||
array('ses', 3, true, true, array('s', 'se', 'sis')),
|
||||
|
||||
// objectives (objective), alternative (alternatives)
|
||||
array('sevit', 5, true, true, 'tive'),
|
||||
|
||||
// drives (drive)
|
||||
array('sevird', 6, false, true, 'drive'),
|
||||
|
||||
// lives (life), wives (wife)
|
||||
array('sevi', 4, false, true, 'ife'),
|
||||
|
||||
// moves (move)
|
||||
array('sevom', 5, true, true, 'move'),
|
||||
|
||||
// hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff)
|
||||
array('sev', 3, true, true, array('f', 've', 'ff')),
|
||||
|
||||
// axes (axis), axes (ax), axes (axe)
|
||||
array('sexa', 4, false, false, array('ax', 'axe', 'axis')),
|
||||
|
||||
// indexes (index), matrixes (matrix)
|
||||
array('sex', 3, true, false, 'x'),
|
||||
|
||||
// quizzes (quiz)
|
||||
array('sezz', 4, true, false, 'z'),
|
||||
|
||||
// bureaus (bureau)
|
||||
array('suae', 4, false, true, 'eau'),
|
||||
|
||||
// roses (rose), garages (garage), cassettes (cassette),
|
||||
// waltzes (waltz), heroes (hero), bushes (bush), arches (arch),
|
||||
// shoes (shoe)
|
||||
array('se', 2, true, true, array('', 'e')),
|
||||
|
||||
// tags (tag)
|
||||
array('s', 1, true, true, ''),
|
||||
|
||||
// chateaux (chateau)
|
||||
array('xuae', 4, false, true, 'eau'),
|
||||
|
||||
// people (person)
|
||||
array('elpoep', 6, true, true, 'person'),
|
||||
);
|
||||
|
||||
/**
|
||||
* This class should not be instantiated.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singular form of a word.
|
||||
*
|
||||
* If the method can't determine the form with certainty, an array of the
|
||||
* possible singulars is returned.
|
||||
*
|
||||
* @param string $plural A word in plural form
|
||||
*
|
||||
* @return string|array The singular form or an array of possible singular
|
||||
* forms
|
||||
*/
|
||||
public static function singularify($plural)
|
||||
{
|
||||
$pluralRev = strrev($plural);
|
||||
$lowerPluralRev = strtolower($pluralRev);
|
||||
$pluralLength = \strlen($lowerPluralRev);
|
||||
|
||||
// The outer loop iterates over the entries of the plural table
|
||||
// The inner loop $j iterates over the characters of the plural suffix
|
||||
// in the plural table to compare them with the characters of the actual
|
||||
// given plural suffix
|
||||
foreach (self::$pluralMap as $map) {
|
||||
$suffix = $map[0];
|
||||
$suffixLength = $map[1];
|
||||
$j = 0;
|
||||
|
||||
// Compare characters in the plural table and of the suffix of the
|
||||
// given plural one by one
|
||||
while ($suffix[$j] === $lowerPluralRev[$j]) {
|
||||
// Let $j point to the next character
|
||||
++$j;
|
||||
|
||||
// Successfully compared the last character
|
||||
// Add an entry with the singular suffix to the singular array
|
||||
if ($j === $suffixLength) {
|
||||
// Is there any character preceding the suffix in the plural string?
|
||||
if ($j < $pluralLength) {
|
||||
$nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]);
|
||||
|
||||
if (!$map[2] && $nextIsVocal) {
|
||||
// suffix may not succeed a vocal but next char is one
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$map[3] && !$nextIsVocal) {
|
||||
// suffix may not succeed a consonant but next char is one
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$newBase = substr($plural, 0, $pluralLength - $suffixLength);
|
||||
$newSuffix = $map[4];
|
||||
|
||||
// Check whether the first character in the plural suffix
|
||||
// is uppercased. If yes, uppercase the first character in
|
||||
// the singular suffix too
|
||||
$firstUpper = ctype_upper($pluralRev[$j - 1]);
|
||||
|
||||
if (\is_array($newSuffix)) {
|
||||
$singulars = array();
|
||||
|
||||
foreach ($newSuffix as $newSuffixEntry) {
|
||||
$singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
|
||||
}
|
||||
|
||||
return $singulars;
|
||||
}
|
||||
|
||||
return $newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix);
|
||||
}
|
||||
|
||||
// Suffix is longer than word
|
||||
if ($j === $pluralLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assume that plural and singular is identical
|
||||
return $plural;
|
||||
}
|
||||
}
|
||||
65
vendor/symfony/property-access/Tests/Fixtures/NonTraversableArrayObject.php
vendored
Normal file
65
vendor/symfony/property-access/Tests/Fixtures/NonTraversableArrayObject.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?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\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* This class is a hand written simplified version of PHP native `ArrayObject`
|
||||
* class, to show that it behaves differently than the PHP native implementation.
|
||||
*/
|
||||
class NonTraversableArrayObject implements \ArrayAccess, \Countable, \Serializable
|
||||
{
|
||||
private $array;
|
||||
|
||||
public function __construct(array $array = null)
|
||||
{
|
||||
$this->array = $array ?: array();
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->array);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->array[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (null === $offset) {
|
||||
$this->array[] = $value;
|
||||
} else {
|
||||
$this->array[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->array[$offset]);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return \count($this->array);
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->array);
|
||||
}
|
||||
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$this->array = (array) unserialize((string) $serialized);
|
||||
}
|
||||
}
|
||||
36
vendor/symfony/property-access/Tests/Fixtures/ReturnTyped.php
vendored
Normal file
36
vendor/symfony/property-access/Tests/Fixtures/ReturnTyped.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ReturnTyped
|
||||
{
|
||||
public function getFoos(): array
|
||||
{
|
||||
return 'It doesn\'t respect the return type on purpose';
|
||||
}
|
||||
|
||||
public function addFoo(\DateTime $dateTime)
|
||||
{
|
||||
}
|
||||
|
||||
public function removeFoo(\DateTime $dateTime)
|
||||
{
|
||||
}
|
||||
|
||||
public function setName($name): self
|
||||
{
|
||||
return 'This does not respect the return type on purpose.';
|
||||
}
|
||||
}
|
||||
176
vendor/symfony/property-access/Tests/Fixtures/TestClass.php
vendored
Normal file
176
vendor/symfony/property-access/Tests/Fixtures/TestClass.php
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
<?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\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
class TestClass
|
||||
{
|
||||
public $publicProperty;
|
||||
protected $protectedProperty;
|
||||
private $privateProperty;
|
||||
|
||||
private $publicAccessor;
|
||||
private $publicMethodAccessor;
|
||||
private $publicGetSetter;
|
||||
private $publicAccessorWithDefaultValue;
|
||||
private $publicAccessorWithRequiredAndDefaultValue;
|
||||
private $publicAccessorWithMoreRequiredParameters;
|
||||
private $publicIsAccessor;
|
||||
private $publicHasAccessor;
|
||||
private $publicGetter;
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->publicProperty = $value;
|
||||
$this->publicAccessor = $value;
|
||||
$this->publicMethodAccessor = $value;
|
||||
$this->publicGetSetter = $value;
|
||||
$this->publicAccessorWithDefaultValue = $value;
|
||||
$this->publicAccessorWithRequiredAndDefaultValue = $value;
|
||||
$this->publicAccessorWithMoreRequiredParameters = $value;
|
||||
$this->publicIsAccessor = $value;
|
||||
$this->publicHasAccessor = $value;
|
||||
$this->publicGetter = $value;
|
||||
}
|
||||
|
||||
public function setPublicAccessor($value)
|
||||
{
|
||||
$this->publicAccessor = $value;
|
||||
}
|
||||
|
||||
public function setPublicAccessorWithDefaultValue($value = null)
|
||||
{
|
||||
$this->publicAccessorWithDefaultValue = $value;
|
||||
}
|
||||
|
||||
public function setPublicAccessorWithRequiredAndDefaultValue($value, $optional = null)
|
||||
{
|
||||
$this->publicAccessorWithRequiredAndDefaultValue = $value;
|
||||
}
|
||||
|
||||
public function setPublicAccessorWithMoreRequiredParameters($value, $needed)
|
||||
{
|
||||
$this->publicAccessorWithMoreRequiredParameters = $value;
|
||||
}
|
||||
|
||||
public function getPublicAccessor()
|
||||
{
|
||||
return $this->publicAccessor;
|
||||
}
|
||||
|
||||
public function getPublicAccessorWithDefaultValue()
|
||||
{
|
||||
return $this->publicAccessorWithDefaultValue;
|
||||
}
|
||||
|
||||
public function getPublicAccessorWithRequiredAndDefaultValue()
|
||||
{
|
||||
return $this->publicAccessorWithRequiredAndDefaultValue;
|
||||
}
|
||||
|
||||
public function getPublicAccessorWithMoreRequiredParameters()
|
||||
{
|
||||
return $this->publicAccessorWithMoreRequiredParameters;
|
||||
}
|
||||
|
||||
public function setPublicIsAccessor($value)
|
||||
{
|
||||
$this->publicIsAccessor = $value;
|
||||
}
|
||||
|
||||
public function isPublicIsAccessor()
|
||||
{
|
||||
return $this->publicIsAccessor;
|
||||
}
|
||||
|
||||
public function setPublicHasAccessor($value)
|
||||
{
|
||||
$this->publicHasAccessor = $value;
|
||||
}
|
||||
|
||||
public function hasPublicHasAccessor()
|
||||
{
|
||||
return $this->publicHasAccessor;
|
||||
}
|
||||
|
||||
public function publicGetSetter($value = null)
|
||||
{
|
||||
if (null !== $value) {
|
||||
$this->publicGetSetter = $value;
|
||||
}
|
||||
|
||||
return $this->publicGetSetter;
|
||||
}
|
||||
|
||||
public function getPublicMethodMutator()
|
||||
{
|
||||
return $this->publicGetSetter;
|
||||
}
|
||||
|
||||
protected function setProtectedAccessor($value)
|
||||
{
|
||||
}
|
||||
|
||||
protected function getProtectedAccessor()
|
||||
{
|
||||
return 'foobar';
|
||||
}
|
||||
|
||||
protected function setProtectedIsAccessor($value)
|
||||
{
|
||||
}
|
||||
|
||||
protected function isProtectedIsAccessor()
|
||||
{
|
||||
return 'foobar';
|
||||
}
|
||||
|
||||
protected function setProtectedHasAccessor($value)
|
||||
{
|
||||
}
|
||||
|
||||
protected function hasProtectedHasAccessor()
|
||||
{
|
||||
return 'foobar';
|
||||
}
|
||||
|
||||
private function setPrivateAccessor($value)
|
||||
{
|
||||
}
|
||||
|
||||
private function getPrivateAccessor()
|
||||
{
|
||||
return 'foobar';
|
||||
}
|
||||
|
||||
private function setPrivateIsAccessor($value)
|
||||
{
|
||||
}
|
||||
|
||||
private function isPrivateIsAccessor()
|
||||
{
|
||||
return 'foobar';
|
||||
}
|
||||
|
||||
private function setPrivateHasAccessor($value)
|
||||
{
|
||||
}
|
||||
|
||||
private function hasPrivateHasAccessor()
|
||||
{
|
||||
return 'foobar';
|
||||
}
|
||||
|
||||
public function getPublicGetter()
|
||||
{
|
||||
return $this->publicGetter;
|
||||
}
|
||||
}
|
||||
27
vendor/symfony/property-access/Tests/Fixtures/TestClassIsWritable.php
vendored
Normal file
27
vendor/symfony/property-access/Tests/Fixtures/TestClassIsWritable.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
class TestClassIsWritable
|
||||
{
|
||||
protected $value;
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
37
vendor/symfony/property-access/Tests/Fixtures/TestClassMagicCall.php
vendored
Normal file
37
vendor/symfony/property-access/Tests/Fixtures/TestClassMagicCall.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
class TestClassMagicCall
|
||||
{
|
||||
private $magicCallProperty;
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->magicCallProperty = $value;
|
||||
}
|
||||
|
||||
public function __call($method, array $args)
|
||||
{
|
||||
if ('getMagicCallProperty' === $method) {
|
||||
return $this->magicCallProperty;
|
||||
}
|
||||
|
||||
if ('getConstantMagicCallProperty' === $method) {
|
||||
return 'constant value';
|
||||
}
|
||||
|
||||
if ('setMagicCallProperty' === $method) {
|
||||
$this->magicCallProperty = reset($args);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
vendor/symfony/property-access/Tests/Fixtures/TestClassMagicGet.php
vendored
Normal file
42
vendor/symfony/property-access/Tests/Fixtures/TestClassMagicGet.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?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\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
class TestClassMagicGet
|
||||
{
|
||||
private $magicProperty;
|
||||
|
||||
public $publicProperty;
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->magicProperty = $value;
|
||||
}
|
||||
|
||||
public function __set($property, $value)
|
||||
{
|
||||
if ('magicProperty' === $property) {
|
||||
$this->magicProperty = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function __get($property)
|
||||
{
|
||||
if ('magicProperty' === $property) {
|
||||
return $this->magicProperty;
|
||||
}
|
||||
|
||||
if ('constantMagicProperty' === $property) {
|
||||
return 'constant value';
|
||||
}
|
||||
}
|
||||
}
|
||||
32
vendor/symfony/property-access/Tests/Fixtures/TestClassSetValue.php
vendored
Normal file
32
vendor/symfony/property-access/Tests/Fixtures/TestClassSetValue.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
class TestClassSetValue
|
||||
{
|
||||
protected $value;
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
31
vendor/symfony/property-access/Tests/Fixtures/Ticket5775Object.php
vendored
Normal file
31
vendor/symfony/property-access/Tests/Fixtures/Ticket5775Object.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
class Ticket5775Object
|
||||
{
|
||||
private $property;
|
||||
|
||||
public function getProperty()
|
||||
{
|
||||
return $this->property;
|
||||
}
|
||||
|
||||
private function setProperty()
|
||||
{
|
||||
}
|
||||
|
||||
public function __set($property, $value)
|
||||
{
|
||||
$this->$property = $value;
|
||||
}
|
||||
}
|
||||
70
vendor/symfony/property-access/Tests/Fixtures/TraversableArrayObject.php
vendored
Normal file
70
vendor/symfony/property-access/Tests/Fixtures/TraversableArrayObject.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?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\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* This class is a hand written simplified version of PHP native `ArrayObject`
|
||||
* class, to show that it behaves differently than the PHP native implementation.
|
||||
*/
|
||||
class TraversableArrayObject implements \ArrayAccess, \IteratorAggregate, \Countable, \Serializable
|
||||
{
|
||||
private $array;
|
||||
|
||||
public function __construct(array $array = null)
|
||||
{
|
||||
$this->array = $array ?: array();
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->array);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->array[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (null === $offset) {
|
||||
$this->array[] = $value;
|
||||
} else {
|
||||
$this->array[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->array[$offset]);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->array);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return \count($this->array);
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->array);
|
||||
}
|
||||
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$this->array = (array) unserialize((string) $serialized);
|
||||
}
|
||||
}
|
||||
51
vendor/symfony/property-access/Tests/Fixtures/TypeHinted.php
vendored
Normal file
51
vendor/symfony/property-access/Tests/Fixtures/TypeHinted.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?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\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class TypeHinted
|
||||
{
|
||||
private $date;
|
||||
|
||||
/**
|
||||
* @var \Countable
|
||||
*/
|
||||
private $countable;
|
||||
|
||||
public function setDate(\DateTime $date)
|
||||
{
|
||||
$this->date = $date;
|
||||
}
|
||||
|
||||
public function getDate()
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Countable
|
||||
*/
|
||||
public function getCountable()
|
||||
{
|
||||
return $this->countable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Countable $countable
|
||||
*/
|
||||
public function setCountable(\Countable $countable)
|
||||
{
|
||||
$this->countable = $countable;
|
||||
}
|
||||
}
|
||||
87
vendor/symfony/property-access/Tests/PropertyAccessorArrayAccessTest.php
vendored
Normal file
87
vendor/symfony/property-access/Tests/PropertyAccessorArrayAccessTest.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||
|
||||
abstract class PropertyAccessorArrayAccessTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var PropertyAccessor
|
||||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor();
|
||||
}
|
||||
|
||||
abstract protected function getContainer(array $array);
|
||||
|
||||
public function getValidPropertyPaths()
|
||||
{
|
||||
return array(
|
||||
array($this->getContainer(array('firstName' => 'Bernhard')), '[firstName]', 'Bernhard'),
|
||||
array($this->getContainer(array('person' => $this->getContainer(array('firstName' => 'Bernhard')))), '[person][firstName]', 'Bernhard'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
*/
|
||||
public function testGetValue($collection, $path, $value)
|
||||
{
|
||||
$this->assertSame($value, $this->propertyAccessor->getValue($collection, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
|
||||
*/
|
||||
public function testGetValueFailsIfNoSuchIndex()
|
||||
{
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
|
||||
->enableExceptionOnInvalidIndex()
|
||||
->getPropertyAccessor();
|
||||
|
||||
$object = $this->getContainer(array('firstName' => 'Bernhard'));
|
||||
|
||||
$this->propertyAccessor->getValue($object, '[lastName]');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
*/
|
||||
public function testSetValue($collection, $path)
|
||||
{
|
||||
$this->propertyAccessor->setValue($collection, $path, 'Updated');
|
||||
|
||||
$this->assertSame('Updated', $this->propertyAccessor->getValue($collection, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
*/
|
||||
public function testIsReadable($collection, $path)
|
||||
{
|
||||
$this->assertTrue($this->propertyAccessor->isReadable($collection, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
*/
|
||||
public function testIsWritable($collection, $path)
|
||||
{
|
||||
$this->assertTrue($this->propertyAccessor->isWritable($collection, $path));
|
||||
}
|
||||
}
|
||||
20
vendor/symfony/property-access/Tests/PropertyAccessorArrayObjectTest.php
vendored
Normal file
20
vendor/symfony/property-access/Tests/PropertyAccessorArrayObjectTest.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
class PropertyAccessorArrayObjectTest extends PropertyAccessorCollectionTest
|
||||
{
|
||||
protected function getContainer(array $array)
|
||||
{
|
||||
return new \ArrayObject($array);
|
||||
}
|
||||
}
|
||||
20
vendor/symfony/property-access/Tests/PropertyAccessorArrayTest.php
vendored
Normal file
20
vendor/symfony/property-access/Tests/PropertyAccessorArrayTest.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
class PropertyAccessorArrayTest extends PropertyAccessorCollectionTest
|
||||
{
|
||||
protected function getContainer(array $array)
|
||||
{
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
56
vendor/symfony/property-access/Tests/PropertyAccessorBuilderTest.php
vendored
Normal file
56
vendor/symfony/property-access/Tests/PropertyAccessorBuilderTest.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorBuilder;
|
||||
|
||||
class PropertyAccessorBuilderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var PropertyAccessorBuilder
|
||||
*/
|
||||
protected $builder;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->builder = new PropertyAccessorBuilder();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->builder = null;
|
||||
}
|
||||
|
||||
public function testEnableMagicCall()
|
||||
{
|
||||
$this->assertSame($this->builder, $this->builder->enableMagicCall());
|
||||
}
|
||||
|
||||
public function testDisableMagicCall()
|
||||
{
|
||||
$this->assertSame($this->builder, $this->builder->disableMagicCall());
|
||||
}
|
||||
|
||||
public function testIsMagicCallEnable()
|
||||
{
|
||||
$this->assertFalse($this->builder->isMagicCallEnabled());
|
||||
$this->assertTrue($this->builder->enableMagicCall()->isMagicCallEnabled());
|
||||
$this->assertFalse($this->builder->disableMagicCall()->isMagicCallEnabled());
|
||||
}
|
||||
|
||||
public function testGetPropertyAccessor()
|
||||
{
|
||||
$this->assertInstanceOf('Symfony\Component\PropertyAccess\PropertyAccessor', $this->builder->getPropertyAccessor());
|
||||
$this->assertInstanceOf('Symfony\Component\PropertyAccess\PropertyAccessor', $this->builder->enableMagicCall()->getPropertyAccessor());
|
||||
}
|
||||
}
|
||||
200
vendor/symfony/property-access/Tests/PropertyAccessorCollectionTest.php
vendored
Normal file
200
vendor/symfony/property-access/Tests/PropertyAccessorCollectionTest.php
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
class PropertyAccessorCollectionTest_Car
|
||||
{
|
||||
private $axes;
|
||||
|
||||
public function __construct($axes = null)
|
||||
{
|
||||
$this->axes = $axes;
|
||||
}
|
||||
|
||||
// In the test, use a name that StringUtil can't uniquely singularify
|
||||
public function addAxis($axis)
|
||||
{
|
||||
$this->axes[] = $axis;
|
||||
}
|
||||
|
||||
public function removeAxis($axis)
|
||||
{
|
||||
foreach ($this->axes as $key => $value) {
|
||||
if ($value === $axis) {
|
||||
unset($this->axes[$key]);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAxes()
|
||||
{
|
||||
return $this->axes;
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyAccessorCollectionTest_CarOnlyAdder
|
||||
{
|
||||
public function addAxis($axis)
|
||||
{
|
||||
}
|
||||
|
||||
public function getAxes()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyAccessorCollectionTest_CarOnlyRemover
|
||||
{
|
||||
public function removeAxis($axis)
|
||||
{
|
||||
}
|
||||
|
||||
public function getAxes()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyAccessorCollectionTest_CarNoAdderAndRemover
|
||||
{
|
||||
public function getAxes()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyAccessorCollectionTest_CompositeCar
|
||||
{
|
||||
public function getStructure()
|
||||
{
|
||||
}
|
||||
|
||||
public function setStructure($structure)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyAccessorCollectionTest_CarStructure
|
||||
{
|
||||
public function addAxis($axis)
|
||||
{
|
||||
}
|
||||
|
||||
public function removeAxis($axis)
|
||||
{
|
||||
}
|
||||
|
||||
public function getAxes()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
abstract class PropertyAccessorCollectionTest extends PropertyAccessorArrayAccessTest
|
||||
{
|
||||
public function testSetValueCallsAdderAndRemoverForCollections()
|
||||
{
|
||||
$axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth', 4 => 'fifth'));
|
||||
$axesMerged = $this->getContainer(array(1 => 'first', 2 => 'second', 3 => 'third'));
|
||||
$axesAfter = $this->getContainer(array(1 => 'second', 5 => 'first', 6 => 'third'));
|
||||
$axesMergedCopy = \is_object($axesMerged) ? clone $axesMerged : $axesMerged;
|
||||
|
||||
// Don't use a mock in order to test whether the collections are
|
||||
// modified while iterating them
|
||||
$car = new PropertyAccessorCollectionTest_Car($axesBefore);
|
||||
|
||||
$this->propertyAccessor->setValue($car, 'axes', $axesMerged);
|
||||
|
||||
$this->assertEquals($axesAfter, $car->getAxes());
|
||||
|
||||
// The passed collection was not modified
|
||||
$this->assertEquals($axesMergedCopy, $axesMerged);
|
||||
}
|
||||
|
||||
public function testSetValueCallsAdderAndRemoverForNestedCollections()
|
||||
{
|
||||
$car = $this->getMockBuilder(__CLASS__.'_CompositeCar')->getMock();
|
||||
$structure = $this->getMockBuilder(__CLASS__.'_CarStructure')->getMock();
|
||||
$axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth'));
|
||||
$axesAfter = $this->getContainer(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
|
||||
$car->expects($this->any())
|
||||
->method('getStructure')
|
||||
->will($this->returnValue($structure));
|
||||
|
||||
$structure->expects($this->at(0))
|
||||
->method('getAxes')
|
||||
->will($this->returnValue($axesBefore));
|
||||
$structure->expects($this->at(1))
|
||||
->method('removeAxis')
|
||||
->with('fourth');
|
||||
$structure->expects($this->at(2))
|
||||
->method('addAxis')
|
||||
->with('first');
|
||||
$structure->expects($this->at(3))
|
||||
->method('addAxis')
|
||||
->with('third');
|
||||
|
||||
$this->propertyAccessor->setValue($car, 'structure.axes', $axesAfter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
|
||||
* @expectedExceptionMessage Neither the property "axes" nor one of the methods "addAx()"/"removeAx()", "addAxe()"/"removeAxe()", "addAxis()"/"removeAxis()", "setAxes()", "axes()", "__set()" or "__call()" exist and have public access in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover
|
||||
*/
|
||||
public function testSetValueFailsIfNoAdderNorRemoverFound()
|
||||
{
|
||||
$car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock();
|
||||
$axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth'));
|
||||
$axesAfter = $this->getContainer(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
|
||||
$car->expects($this->any())
|
||||
->method('getAxes')
|
||||
->will($this->returnValue($axesBefore));
|
||||
|
||||
$this->propertyAccessor->setValue($car, 'axes', $axesAfter);
|
||||
}
|
||||
|
||||
public function testIsWritableReturnsTrueIfAdderAndRemoverExists()
|
||||
{
|
||||
$car = $this->getMockBuilder(__CLASS__.'_Car')->getMock();
|
||||
$this->assertTrue($this->propertyAccessor->isWritable($car, 'axes'));
|
||||
}
|
||||
|
||||
public function testIsWritableReturnsFalseIfOnlyAdderExists()
|
||||
{
|
||||
$car = $this->getMockBuilder(__CLASS__.'_CarOnlyAdder')->getMock();
|
||||
$this->assertFalse($this->propertyAccessor->isWritable($car, 'axes'));
|
||||
}
|
||||
|
||||
public function testIsWritableReturnsFalseIfOnlyRemoverExists()
|
||||
{
|
||||
$car = $this->getMockBuilder(__CLASS__.'_CarOnlyRemover')->getMock();
|
||||
$this->assertFalse($this->propertyAccessor->isWritable($car, 'axes'));
|
||||
}
|
||||
|
||||
public function testIsWritableReturnsFalseIfNoAdderNorRemoverExists()
|
||||
{
|
||||
$car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock();
|
||||
$this->assertFalse($this->propertyAccessor->isWritable($car, 'axes'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
|
||||
* expectedExceptionMessageRegExp /The property "axes" in class "Mock_PropertyAccessorCollectionTest_Car[^"]*" can be defined with the methods "addAxis()", "removeAxis()" but the new value must be an array or an instance of \Traversable, "string" given./
|
||||
*/
|
||||
public function testSetValueFailsIfAdderAndRemoverExistButValueIsNotTraversable()
|
||||
{
|
||||
$car = $this->getMockBuilder(__CLASS__.'_Car')->getMock();
|
||||
|
||||
$this->propertyAccessor->setValue($car, 'axes', 'Not an array or Traversable');
|
||||
}
|
||||
}
|
||||
22
vendor/symfony/property-access/Tests/PropertyAccessorNonTraversableArrayObjectTest.php
vendored
Normal file
22
vendor/symfony/property-access/Tests/PropertyAccessorNonTraversableArrayObjectTest.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\NonTraversableArrayObject;
|
||||
|
||||
class PropertyAccessorNonTraversableArrayObjectTest extends PropertyAccessorArrayAccessTest
|
||||
{
|
||||
protected function getContainer(array $array)
|
||||
{
|
||||
return new NonTraversableArrayObject($array);
|
||||
}
|
||||
}
|
||||
605
vendor/symfony/property-access/Tests/PropertyAccessorTest.php
vendored
Normal file
605
vendor/symfony/property-access/Tests/PropertyAccessorTest.php
vendored
Normal file
@@ -0,0 +1,605 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClass;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassIsWritable;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicCall;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicGet;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassSetValue;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
|
||||
|
||||
class PropertyAccessorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var PropertyAccessor
|
||||
*/
|
||||
private $propertyAccessor;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor();
|
||||
}
|
||||
|
||||
public function getPathsWithUnexpectedType()
|
||||
{
|
||||
return array(
|
||||
array('', 'foobar'),
|
||||
array('foo', 'foobar'),
|
||||
array(null, 'foobar'),
|
||||
array(123, 'foobar'),
|
||||
array((object) array('prop' => null), 'prop.foobar'),
|
||||
array((object) array('prop' => (object) array('subProp' => null)), 'prop.subProp.foobar'),
|
||||
array(array('index' => null), '[index][foobar]'),
|
||||
array(array('index' => array('subIndex' => null)), '[index][subIndex][foobar]'),
|
||||
);
|
||||
}
|
||||
|
||||
public function getPathsWithMissingProperty()
|
||||
{
|
||||
return array(
|
||||
array((object) array('firstName' => 'Bernhard'), 'lastName'),
|
||||
array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.lastName'),
|
||||
array(array('index' => (object) array('firstName' => 'Bernhard')), '[index].lastName'),
|
||||
array(new TestClass('Bernhard'), 'protectedProperty'),
|
||||
array(new TestClass('Bernhard'), 'privateProperty'),
|
||||
array(new TestClass('Bernhard'), 'protectedAccessor'),
|
||||
array(new TestClass('Bernhard'), 'protectedIsAccessor'),
|
||||
array(new TestClass('Bernhard'), 'protectedHasAccessor'),
|
||||
array(new TestClass('Bernhard'), 'privateAccessor'),
|
||||
array(new TestClass('Bernhard'), 'privateIsAccessor'),
|
||||
array(new TestClass('Bernhard'), 'privateHasAccessor'),
|
||||
|
||||
// Properties are not camelized
|
||||
array(new TestClass('Bernhard'), 'public_property'),
|
||||
);
|
||||
}
|
||||
|
||||
public function getPathsWithMissingIndex()
|
||||
{
|
||||
return array(
|
||||
array(array('firstName' => 'Bernhard'), '[lastName]'),
|
||||
array(array(), '[index][lastName]'),
|
||||
array(array('index' => array()), '[index][lastName]'),
|
||||
array(array('index' => array('firstName' => 'Bernhard')), '[index][lastName]'),
|
||||
array((object) array('property' => array('firstName' => 'Bernhard')), 'property[lastName]'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
*/
|
||||
public function testGetValue($objectOrArray, $path, $value)
|
||||
{
|
||||
$this->assertSame($value, $this->propertyAccessor->getValue($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingProperty
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
|
||||
*/
|
||||
public function testGetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $path)
|
||||
{
|
||||
$this->propertyAccessor->getValue($objectOrArray, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingIndex
|
||||
*/
|
||||
public function testGetValueThrowsNoExceptionIfIndexNotFound($objectOrArray, $path)
|
||||
{
|
||||
$this->assertNull($this->propertyAccessor->getValue($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingIndex
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
|
||||
*/
|
||||
public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor(false, true);
|
||||
$this->propertyAccessor->getValue($objectOrArray, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
|
||||
*/
|
||||
public function testGetValueThrowsExceptionIfNotArrayAccess()
|
||||
{
|
||||
$this->propertyAccessor->getValue(new \stdClass(), '[index]');
|
||||
}
|
||||
|
||||
public function testGetValueReadsMagicGet()
|
||||
{
|
||||
$this->assertSame('Bernhard', $this->propertyAccessor->getValue(new TestClassMagicGet('Bernhard'), 'magicProperty'));
|
||||
}
|
||||
|
||||
public function testGetValueReadsArrayWithMissingIndexForCustomPropertyPath()
|
||||
{
|
||||
$object = new \ArrayObject();
|
||||
$array = array('child' => array('index' => $object));
|
||||
|
||||
$this->assertNull($this->propertyAccessor->getValue($array, '[child][index][foo][bar]'));
|
||||
$this->assertSame(array(), $object->getArrayCopy());
|
||||
}
|
||||
|
||||
// https://github.com/symfony/symfony/pull/4450
|
||||
public function testGetValueReadsMagicGetThatReturnsConstant()
|
||||
{
|
||||
$this->assertSame('constant value', $this->propertyAccessor->getValue(new TestClassMagicGet('Bernhard'), 'constantMagicProperty'));
|
||||
}
|
||||
|
||||
public function testGetValueNotModifyObject()
|
||||
{
|
||||
$object = new \stdClass();
|
||||
$object->firstName = array('Bernhard');
|
||||
|
||||
$this->assertNull($this->propertyAccessor->getValue($object, 'firstName[1]'));
|
||||
$this->assertSame(array('Bernhard'), $object->firstName);
|
||||
}
|
||||
|
||||
public function testGetValueNotModifyObjectException()
|
||||
{
|
||||
$propertyAccessor = new PropertyAccessor(false, true);
|
||||
$object = new \stdClass();
|
||||
$object->firstName = array('Bernhard');
|
||||
|
||||
try {
|
||||
$propertyAccessor->getValue($object, 'firstName[1]');
|
||||
} catch (NoSuchIndexException $e) {
|
||||
}
|
||||
|
||||
$this->assertSame(array('Bernhard'), $object->firstName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
|
||||
*/
|
||||
public function testGetValueDoesNotReadMagicCallByDefault()
|
||||
{
|
||||
$this->propertyAccessor->getValue(new TestClassMagicCall('Bernhard'), 'magicCallProperty');
|
||||
}
|
||||
|
||||
public function testGetValueReadsMagicCallIfEnabled()
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor(true);
|
||||
|
||||
$this->assertSame('Bernhard', $this->propertyAccessor->getValue(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
|
||||
}
|
||||
|
||||
// https://github.com/symfony/symfony/pull/4450
|
||||
public function testGetValueReadsMagicCallThatReturnsConstant()
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor(true);
|
||||
|
||||
$this->assertSame('constant value', $this->propertyAccessor->getValue(new TestClassMagicCall('Bernhard'), 'constantMagicCallProperty'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithUnexpectedType
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
|
||||
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on
|
||||
*/
|
||||
public function testGetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $path)
|
||||
{
|
||||
$this->propertyAccessor->getValue($objectOrArray, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
*/
|
||||
public function testSetValue($objectOrArray, $path)
|
||||
{
|
||||
$this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
|
||||
|
||||
$this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingProperty
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
|
||||
*/
|
||||
public function testSetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $path)
|
||||
{
|
||||
$this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingIndex
|
||||
*/
|
||||
public function testSetValueThrowsNoExceptionIfIndexNotFound($objectOrArray, $path)
|
||||
{
|
||||
$this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
|
||||
|
||||
$this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingIndex
|
||||
*/
|
||||
public function testSetValueThrowsNoExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor(false, true);
|
||||
$this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
|
||||
|
||||
$this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
|
||||
*/
|
||||
public function testSetValueThrowsExceptionIfNotArrayAccess()
|
||||
{
|
||||
$object = new \stdClass();
|
||||
|
||||
$this->propertyAccessor->setValue($object, '[index]', 'Updated');
|
||||
}
|
||||
|
||||
public function testSetValueUpdatesMagicSet()
|
||||
{
|
||||
$author = new TestClassMagicGet('Bernhard');
|
||||
|
||||
$this->propertyAccessor->setValue($author, 'magicProperty', 'Updated');
|
||||
|
||||
$this->assertEquals('Updated', $author->__get('magicProperty'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
|
||||
*/
|
||||
public function testSetValueThrowsExceptionIfThereAreMissingParameters()
|
||||
{
|
||||
$object = new TestClass('Bernhard');
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'publicAccessorWithMoreRequiredParameters', 'Updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
|
||||
*/
|
||||
public function testSetValueDoesNotUpdateMagicCallByDefault()
|
||||
{
|
||||
$author = new TestClassMagicCall('Bernhard');
|
||||
|
||||
$this->propertyAccessor->setValue($author, 'magicCallProperty', 'Updated');
|
||||
}
|
||||
|
||||
public function testSetValueUpdatesMagicCallIfEnabled()
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor(true);
|
||||
|
||||
$author = new TestClassMagicCall('Bernhard');
|
||||
|
||||
$this->propertyAccessor->setValue($author, 'magicCallProperty', 'Updated');
|
||||
|
||||
$this->assertEquals('Updated', $author->__call('getMagicCallProperty', array()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithUnexpectedType
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
|
||||
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on
|
||||
*/
|
||||
public function testSetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $path)
|
||||
{
|
||||
$this->propertyAccessor->setValue($objectOrArray, $path, 'value');
|
||||
}
|
||||
|
||||
public function testGetValueWhenArrayValueIsNull()
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor(false, true);
|
||||
$this->assertNull($this->propertyAccessor->getValue(array('index' => array('nullable' => null)), '[index][nullable]'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
*/
|
||||
public function testIsReadable($objectOrArray, $path)
|
||||
{
|
||||
$this->assertTrue($this->propertyAccessor->isReadable($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingProperty
|
||||
*/
|
||||
public function testIsReadableReturnsFalseIfPropertyNotFound($objectOrArray, $path)
|
||||
{
|
||||
$this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingIndex
|
||||
*/
|
||||
public function testIsReadableReturnsTrueIfIndexNotFound($objectOrArray, $path)
|
||||
{
|
||||
// Non-existing indices can be read. In this case, null is returned
|
||||
$this->assertTrue($this->propertyAccessor->isReadable($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingIndex
|
||||
*/
|
||||
public function testIsReadableReturnsFalseIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor(false, true);
|
||||
|
||||
// When exceptions are enabled, non-existing indices cannot be read
|
||||
$this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path));
|
||||
}
|
||||
|
||||
public function testIsReadableRecognizesMagicGet()
|
||||
{
|
||||
$this->assertTrue($this->propertyAccessor->isReadable(new TestClassMagicGet('Bernhard'), 'magicProperty'));
|
||||
}
|
||||
|
||||
public function testIsReadableDoesNotRecognizeMagicCallByDefault()
|
||||
{
|
||||
$this->assertFalse($this->propertyAccessor->isReadable(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
|
||||
}
|
||||
|
||||
public function testIsReadableRecognizesMagicCallIfEnabled()
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor(true);
|
||||
|
||||
$this->assertTrue($this->propertyAccessor->isReadable(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithUnexpectedType
|
||||
*/
|
||||
public function testIsReadableReturnsFalseIfNotObjectOrArray($objectOrArray, $path)
|
||||
{
|
||||
$this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
*/
|
||||
public function testIsWritable($objectOrArray, $path)
|
||||
{
|
||||
$this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingProperty
|
||||
*/
|
||||
public function testIsWritableReturnsFalseIfPropertyNotFound($objectOrArray, $path)
|
||||
{
|
||||
$this->assertFalse($this->propertyAccessor->isWritable($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingIndex
|
||||
*/
|
||||
public function testIsWritableReturnsTrueIfIndexNotFound($objectOrArray, $path)
|
||||
{
|
||||
// Non-existing indices can be written. Arrays are created on-demand.
|
||||
$this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithMissingIndex
|
||||
*/
|
||||
public function testIsWritableReturnsTrueIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor(false, true);
|
||||
|
||||
// Non-existing indices can be written even if exceptions are enabled
|
||||
$this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path));
|
||||
}
|
||||
|
||||
public function testIsWritableRecognizesMagicSet()
|
||||
{
|
||||
$this->assertTrue($this->propertyAccessor->isWritable(new TestClassMagicGet('Bernhard'), 'magicProperty'));
|
||||
}
|
||||
|
||||
public function testIsWritableDoesNotRecognizeMagicCallByDefault()
|
||||
{
|
||||
$this->assertFalse($this->propertyAccessor->isWritable(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
|
||||
}
|
||||
|
||||
public function testIsWritableRecognizesMagicCallIfEnabled()
|
||||
{
|
||||
$this->propertyAccessor = new PropertyAccessor(true);
|
||||
|
||||
$this->assertTrue($this->propertyAccessor->isWritable(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPathsWithUnexpectedType
|
||||
*/
|
||||
public function testIsWritableReturnsFalseIfNotObjectOrArray($objectOrArray, $path)
|
||||
{
|
||||
$this->assertFalse($this->propertyAccessor->isWritable($objectOrArray, $path));
|
||||
}
|
||||
|
||||
public function getValidPropertyPaths()
|
||||
{
|
||||
return array(
|
||||
array(array('Bernhard', 'Schussek'), '[0]', 'Bernhard'),
|
||||
array(array('Bernhard', 'Schussek'), '[1]', 'Schussek'),
|
||||
array(array('firstName' => 'Bernhard'), '[firstName]', 'Bernhard'),
|
||||
array(array('index' => array('firstName' => 'Bernhard')), '[index][firstName]', 'Bernhard'),
|
||||
array((object) array('firstName' => 'Bernhard'), 'firstName', 'Bernhard'),
|
||||
array((object) array('property' => array('firstName' => 'Bernhard')), 'property[firstName]', 'Bernhard'),
|
||||
array(array('index' => (object) array('firstName' => 'Bernhard')), '[index].firstName', 'Bernhard'),
|
||||
array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.firstName', 'Bernhard'),
|
||||
|
||||
// Accessor methods
|
||||
array(new TestClass('Bernhard'), 'publicProperty', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicAccessor', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicAccessorWithDefaultValue', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicAccessorWithRequiredAndDefaultValue', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicIsAccessor', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicHasAccessor', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicGetSetter', 'Bernhard'),
|
||||
|
||||
// Methods are camelized
|
||||
array(new TestClass('Bernhard'), 'public_accessor', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), '_public_accessor', 'Bernhard'),
|
||||
|
||||
// Missing indices
|
||||
array(array('index' => array()), '[index][firstName]', null),
|
||||
array(array('root' => array('index' => array())), '[root][index][firstName]', null),
|
||||
|
||||
// Special chars
|
||||
array(array('%!@$§.' => 'Bernhard'), '[%!@$§.]', 'Bernhard'),
|
||||
array(array('index' => array('%!@$§.' => 'Bernhard')), '[index][%!@$§.]', 'Bernhard'),
|
||||
array((object) array('%!@$§' => 'Bernhard'), '%!@$§', 'Bernhard'),
|
||||
array((object) array('property' => (object) array('%!@$§' => 'Bernhard')), 'property.%!@$§', 'Bernhard'),
|
||||
|
||||
// nested objects and arrays
|
||||
array(array('foo' => new TestClass('bar')), '[foo].publicGetSetter', 'bar'),
|
||||
array(new TestClass(array('foo' => 'bar')), 'publicGetSetter[foo]', 'bar'),
|
||||
array(new TestClass(new TestClass('bar')), 'publicGetter.publicGetSetter', 'bar'),
|
||||
array(new TestClass(array('foo' => new TestClass('bar'))), 'publicGetter[foo].publicGetSetter', 'bar'),
|
||||
array(new TestClass(new TestClass(new TestClass('bar'))), 'publicGetter.publicGetter.publicGetSetter', 'bar'),
|
||||
array(new TestClass(array('foo' => array('baz' => new TestClass('bar')))), 'publicGetter[foo][baz].publicGetSetter', 'bar'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testTicket5755()
|
||||
{
|
||||
$object = new Ticket5775Object();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'property', 'foobar');
|
||||
|
||||
$this->assertEquals('foobar', $object->getProperty());
|
||||
}
|
||||
|
||||
public function testSetValueDeepWithMagicGetter()
|
||||
{
|
||||
$obj = new TestClassMagicGet('foo');
|
||||
$obj->publicProperty = array('foo' => array('bar' => 'some_value'));
|
||||
$this->propertyAccessor->setValue($obj, 'publicProperty[foo][bar]', 'Updated');
|
||||
$this->assertSame('Updated', $obj->publicProperty['foo']['bar']);
|
||||
}
|
||||
|
||||
public function getReferenceChainObjectsForSetValue()
|
||||
{
|
||||
return array(
|
||||
array(array('a' => array('b' => array('c' => 'old-value'))), '[a][b][c]', 'new-value'),
|
||||
array(new TestClassSetValue(new TestClassSetValue('old-value')), 'value.value', 'new-value'),
|
||||
array(new TestClassSetValue(array('a' => array('b' => array('c' => new TestClassSetValue('old-value'))))), 'value[a][b][c].value', 'new-value'),
|
||||
array(new TestClassSetValue(array('a' => array('b' => 'old-value'))), 'value[a][b]', 'new-value'),
|
||||
array(new \ArrayIterator(array('a' => array('b' => array('c' => 'old-value')))), '[a][b][c]', 'new-value'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getReferenceChainObjectsForSetValue
|
||||
*/
|
||||
public function testSetValueForReferenceChainIssue($object, $path, $value)
|
||||
{
|
||||
$this->propertyAccessor->setValue($object, $path, $value);
|
||||
|
||||
$this->assertEquals($value, $this->propertyAccessor->getValue($object, $path));
|
||||
}
|
||||
|
||||
public function getReferenceChainObjectsForIsWritable()
|
||||
{
|
||||
return array(
|
||||
array(new TestClassIsWritable(array('a' => array('b' => 'old-value'))), 'value[a][b]', false),
|
||||
array(new TestClassIsWritable(new \ArrayIterator(array('a' => array('b' => 'old-value')))), 'value[a][b]', true),
|
||||
array(new TestClassIsWritable(array('a' => array('b' => array('c' => new TestClassSetValue('old-value'))))), 'value[a][b][c].value', true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getReferenceChainObjectsForIsWritable
|
||||
*/
|
||||
public function testIsWritableForReferenceChainIssue($object, $path, $value)
|
||||
{
|
||||
$this->assertEquals($value, $this->propertyAccessor->isWritable($object, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Expected argument of type "DateTime", "string" given
|
||||
*/
|
||||
public function testThrowTypeError()
|
||||
{
|
||||
$object = new TypeHinted();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'date', 'This is a string, \DateTime expected.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Expected argument of type "DateTime", "NULL" given
|
||||
*/
|
||||
public function testThrowTypeErrorWithNullArgument()
|
||||
{
|
||||
$object = new TypeHinted();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'date', null);
|
||||
}
|
||||
|
||||
public function testSetTypeHint()
|
||||
{
|
||||
$date = new \DateTime();
|
||||
$object = new TypeHinted();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'date', $date);
|
||||
$this->assertSame($date, $object->getDate());
|
||||
}
|
||||
|
||||
public function testArrayNotBeeingOverwritten()
|
||||
{
|
||||
$value = array('value1' => 'foo', 'value2' => 'bar');
|
||||
$object = new TestClass($value);
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'publicAccessor[value2]', 'baz');
|
||||
$this->assertSame('baz', $this->propertyAccessor->getValue($object, 'publicAccessor[value2]'));
|
||||
$this->assertSame(array('value1' => 'foo', 'value2' => 'baz'), $object->getPublicAccessor());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Expected argument of type "Countable", "string" given
|
||||
*/
|
||||
public function testThrowTypeErrorWithInterface()
|
||||
{
|
||||
$object = new TypeHinted();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'countable', 'This is a string, \Countable expected.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 7
|
||||
*
|
||||
* @expectedException \TypeError
|
||||
*/
|
||||
public function testDoNotDiscardReturnTypeError()
|
||||
{
|
||||
$object = new ReturnTyped();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'foos', array(new \DateTime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 7
|
||||
*
|
||||
* @expectedException \TypeError
|
||||
*/
|
||||
public function testDoNotDiscardReturnTypeErrorWhenWriterMethodIsMisconfigured()
|
||||
{
|
||||
$object = new ReturnTyped();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'name', 'foo');
|
||||
}
|
||||
}
|
||||
22
vendor/symfony/property-access/Tests/PropertyAccessorTraversableArrayObjectTest.php
vendored
Normal file
22
vendor/symfony/property-access/Tests/PropertyAccessorTraversableArrayObjectTest.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TraversableArrayObject;
|
||||
|
||||
class PropertyAccessorTraversableArrayObjectTest extends PropertyAccessorCollectionTest
|
||||
{
|
||||
protected function getContainer(array $array)
|
||||
{
|
||||
return new TraversableArrayObject($array);
|
||||
}
|
||||
}
|
||||
288
vendor/symfony/property-access/Tests/PropertyPathBuilderTest.php
vendored
Normal file
288
vendor/symfony/property-access/Tests/PropertyPathBuilderTest.php
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyAccess\PropertyPath;
|
||||
use Symfony\Component\PropertyAccess\PropertyPathBuilder;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class PropertyPathBuilderTest extends TestCase
|
||||
{
|
||||
const PREFIX = 'old1[old2].old3[old4][old5].old6';
|
||||
|
||||
/**
|
||||
* @var PropertyPathBuilder
|
||||
*/
|
||||
private $builder;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->builder = new PropertyPathBuilder(new PropertyPath(self::PREFIX));
|
||||
}
|
||||
|
||||
public function testCreateEmpty()
|
||||
{
|
||||
$builder = new PropertyPathBuilder();
|
||||
|
||||
$this->assertNull($builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testCreateCopyPath()
|
||||
{
|
||||
$this->assertEquals(new PropertyPath(self::PREFIX), $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testAppendIndex()
|
||||
{
|
||||
$this->builder->appendIndex('new1');
|
||||
|
||||
$path = new PropertyPath(self::PREFIX.'[new1]');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testAppendProperty()
|
||||
{
|
||||
$this->builder->appendProperty('new1');
|
||||
|
||||
$path = new PropertyPath(self::PREFIX.'.new1');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testAppend()
|
||||
{
|
||||
$this->builder->append(new PropertyPath('new1[new2]'));
|
||||
|
||||
$path = new PropertyPath(self::PREFIX.'.new1[new2]');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testAppendUsingString()
|
||||
{
|
||||
$this->builder->append('new1[new2]');
|
||||
|
||||
$path = new PropertyPath(self::PREFIX.'.new1[new2]');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testAppendWithOffset()
|
||||
{
|
||||
$this->builder->append(new PropertyPath('new1[new2].new3'), 1);
|
||||
|
||||
$path = new PropertyPath(self::PREFIX.'[new2].new3');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testAppendWithOffsetAndLength()
|
||||
{
|
||||
$this->builder->append(new PropertyPath('new1[new2].new3'), 1, 1);
|
||||
|
||||
$path = new PropertyPath(self::PREFIX.'[new2]');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testReplaceByIndex()
|
||||
{
|
||||
$this->builder->replaceByIndex(1, 'new1');
|
||||
|
||||
$path = new PropertyPath('old1[new1].old3[old4][old5].old6');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testReplaceByIndexWithoutName()
|
||||
{
|
||||
$this->builder->replaceByIndex(0);
|
||||
|
||||
$path = new PropertyPath('[old1][old2].old3[old4][old5].old6');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testReplaceByIndexDoesNotAllowInvalidOffsets()
|
||||
{
|
||||
$this->builder->replaceByIndex(6, 'new1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testReplaceByIndexDoesNotAllowNegativeOffsets()
|
||||
{
|
||||
$this->builder->replaceByIndex(-1, 'new1');
|
||||
}
|
||||
|
||||
public function testReplaceByProperty()
|
||||
{
|
||||
$this->builder->replaceByProperty(1, 'new1');
|
||||
|
||||
$path = new PropertyPath('old1.new1.old3[old4][old5].old6');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testReplaceByPropertyWithoutName()
|
||||
{
|
||||
$this->builder->replaceByProperty(1);
|
||||
|
||||
$path = new PropertyPath('old1.old2.old3[old4][old5].old6');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testReplaceByPropertyDoesNotAllowInvalidOffsets()
|
||||
{
|
||||
$this->builder->replaceByProperty(6, 'new1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testReplaceByPropertyDoesNotAllowNegativeOffsets()
|
||||
{
|
||||
$this->builder->replaceByProperty(-1, 'new1');
|
||||
}
|
||||
|
||||
public function testReplace()
|
||||
{
|
||||
$this->builder->replace(1, 1, new PropertyPath('new1[new2].new3'));
|
||||
|
||||
$path = new PropertyPath('old1.new1[new2].new3.old3[old4][old5].old6');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testReplaceUsingString()
|
||||
{
|
||||
$this->builder->replace(1, 1, 'new1[new2].new3');
|
||||
|
||||
$path = new PropertyPath('old1.new1[new2].new3.old3[old4][old5].old6');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testReplaceNegative()
|
||||
{
|
||||
$this->builder->replace(-1, 1, new PropertyPath('new1[new2].new3'));
|
||||
|
||||
$path = new PropertyPath('old1[old2].old3[old4][old5].new1[new2].new3');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidOffsets
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testReplaceDoesNotAllowInvalidOffsets($offset)
|
||||
{
|
||||
$this->builder->replace($offset, 1, new PropertyPath('new1[new2].new3'));
|
||||
}
|
||||
|
||||
public function provideInvalidOffsets()
|
||||
{
|
||||
return array(
|
||||
array(6),
|
||||
array(-7),
|
||||
);
|
||||
}
|
||||
|
||||
public function testReplaceWithLengthGreaterOne()
|
||||
{
|
||||
$this->builder->replace(0, 2, new PropertyPath('new1[new2].new3'));
|
||||
|
||||
$path = new PropertyPath('new1[new2].new3.old3[old4][old5].old6');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testReplaceSubstring()
|
||||
{
|
||||
$this->builder->replace(1, 1, new PropertyPath('new1[new2].new3.new4[new5]'), 1, 3);
|
||||
|
||||
$path = new PropertyPath('old1[new2].new3.new4.old3[old4][old5].old6');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testReplaceSubstringWithLengthGreaterOne()
|
||||
{
|
||||
$this->builder->replace(1, 2, new PropertyPath('new1[new2].new3.new4[new5]'), 1, 3);
|
||||
|
||||
$path = new PropertyPath('old1[new2].new3.new4[old4][old5].old6');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
// https://github.com/symfony/symfony/issues/5605
|
||||
public function testReplaceWithLongerPath()
|
||||
{
|
||||
// error occurs when path contains at least two more elements
|
||||
// than the builder
|
||||
$path = new PropertyPath('new1.new2.new3');
|
||||
|
||||
$builder = new PropertyPathBuilder(new PropertyPath('old1'));
|
||||
$builder->replace(0, 1, $path);
|
||||
|
||||
$this->assertEquals($path, $builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testReplaceWithLongerPathKeepsOrder()
|
||||
{
|
||||
$path = new PropertyPath('new1.new2.new3');
|
||||
$expected = new PropertyPath('new1.new2.new3.old2');
|
||||
|
||||
$builder = new PropertyPathBuilder(new PropertyPath('old1.old2'));
|
||||
$builder->replace(0, 1, $path);
|
||||
|
||||
$this->assertEquals($expected, $builder->getPropertyPath());
|
||||
}
|
||||
|
||||
public function testRemove()
|
||||
{
|
||||
$this->builder->remove(3);
|
||||
|
||||
$path = new PropertyPath('old1[old2].old3[old5].old6');
|
||||
|
||||
$this->assertEquals($path, $this->builder->getPropertyPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testRemoveDoesNotAllowInvalidOffsets()
|
||||
{
|
||||
$this->builder->remove(6);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testRemoveDoesNotAllowNegativeOffsets()
|
||||
{
|
||||
$this->builder->remove(-1);
|
||||
}
|
||||
}
|
||||
206
vendor/symfony/property-access/Tests/PropertyPathTest.php
vendored
Normal file
206
vendor/symfony/property-access/Tests/PropertyPathTest.php
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyAccess\PropertyPath;
|
||||
|
||||
class PropertyPathTest extends TestCase
|
||||
{
|
||||
public function testToString()
|
||||
{
|
||||
$path = new PropertyPath('reference.traversable[index].property');
|
||||
|
||||
$this->assertEquals('reference.traversable[index].property', $path->__toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
||||
*/
|
||||
public function testDotIsRequiredBeforeProperty()
|
||||
{
|
||||
new PropertyPath('[index]property');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
||||
*/
|
||||
public function testDotCannotBePresentAtTheBeginning()
|
||||
{
|
||||
new PropertyPath('.property');
|
||||
}
|
||||
|
||||
public function providePathsContainingUnexpectedCharacters()
|
||||
{
|
||||
return array(
|
||||
array('property.'),
|
||||
array('property.['),
|
||||
array('property..'),
|
||||
array('property['),
|
||||
array('property[['),
|
||||
array('property[.'),
|
||||
array('property[]'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providePathsContainingUnexpectedCharacters
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
||||
*/
|
||||
public function testUnexpectedCharacters($path)
|
||||
{
|
||||
new PropertyPath($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
||||
*/
|
||||
public function testPathCannotBeEmpty()
|
||||
{
|
||||
new PropertyPath('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function testPathCannotBeNull()
|
||||
{
|
||||
new PropertyPath(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function testPathCannotBeFalse()
|
||||
{
|
||||
new PropertyPath(false);
|
||||
}
|
||||
|
||||
public function testZeroIsValidPropertyPath()
|
||||
{
|
||||
$propertyPath = new PropertyPath('0');
|
||||
|
||||
$this->assertSame('0', (string) $propertyPath);
|
||||
}
|
||||
|
||||
public function testGetParentWithDot()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent.child');
|
||||
|
||||
$this->assertEquals(new PropertyPath('grandpa.parent'), $propertyPath->getParent());
|
||||
}
|
||||
|
||||
public function testGetParentWithIndex()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
|
||||
$this->assertEquals(new PropertyPath('grandpa.parent'), $propertyPath->getParent());
|
||||
}
|
||||
|
||||
public function testGetParentWhenThereIsNoParent()
|
||||
{
|
||||
$propertyPath = new PropertyPath('path');
|
||||
|
||||
$this->assertNull($propertyPath->getParent());
|
||||
}
|
||||
|
||||
public function testCopyConstructor()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
$copy = new PropertyPath($propertyPath);
|
||||
|
||||
$this->assertEquals($propertyPath, $copy);
|
||||
}
|
||||
|
||||
public function testGetElement()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
|
||||
$this->assertEquals('child', $propertyPath->getElement(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testGetElementDoesNotAcceptInvalidIndices()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
|
||||
$propertyPath->getElement(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testGetElementDoesNotAcceptNegativeIndices()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
|
||||
$propertyPath->getElement(-1);
|
||||
}
|
||||
|
||||
public function testIsProperty()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
|
||||
$this->assertTrue($propertyPath->isProperty(1));
|
||||
$this->assertFalse($propertyPath->isProperty(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testIsPropertyDoesNotAcceptInvalidIndices()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
|
||||
$propertyPath->isProperty(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testIsPropertyDoesNotAcceptNegativeIndices()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
|
||||
$propertyPath->isProperty(-1);
|
||||
}
|
||||
|
||||
public function testIsIndex()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
|
||||
$this->assertFalse($propertyPath->isIndex(1));
|
||||
$this->assertTrue($propertyPath->isIndex(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testIsIndexDoesNotAcceptInvalidIndices()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
|
||||
$propertyPath->isIndex(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testIsIndexDoesNotAcceptNegativeIndices()
|
||||
{
|
||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||
|
||||
$propertyPath->isIndex(-1);
|
||||
}
|
||||
}
|
||||
172
vendor/symfony/property-access/Tests/StringUtilTest.php
vendored
Normal file
172
vendor/symfony/property-access/Tests/StringUtilTest.php
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
<?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\PropertyAccess\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyAccess\StringUtil;
|
||||
|
||||
class StringUtilTest extends TestCase
|
||||
{
|
||||
public function singularifyProvider()
|
||||
{
|
||||
// see http://english-zone.com/spelling/plurals.html
|
||||
// see http://www.scribd.com/doc/3271143/List-of-100-Irregular-Plural-Nouns-in-English
|
||||
return array(
|
||||
array('accesses', 'access'),
|
||||
array('addresses', 'address'),
|
||||
array('agendas', 'agenda'),
|
||||
array('alumnae', 'alumna'),
|
||||
array('alumni', 'alumnus'),
|
||||
array('analyses', array('analys', 'analyse', 'analysis')),
|
||||
array('antennae', 'antenna'),
|
||||
array('antennas', 'antenna'),
|
||||
array('appendices', array('appendex', 'appendix', 'appendice')),
|
||||
array('arches', array('arch', 'arche')),
|
||||
array('atlases', array('atlas', 'atlase', 'atlasis')),
|
||||
array('axes', array('ax', 'axe', 'axis')),
|
||||
array('babies', 'baby'),
|
||||
array('bacteria', array('bacterion', 'bacterium')),
|
||||
array('bases', array('bas', 'base', 'basis')),
|
||||
array('batches', array('batch', 'batche')),
|
||||
array('beaux', 'beau'),
|
||||
array('bees', array('be', 'bee')),
|
||||
array('boxes', 'box'),
|
||||
array('boys', 'boy'),
|
||||
array('bureaus', 'bureau'),
|
||||
array('bureaux', 'bureau'),
|
||||
array('buses', array('bus', 'buse', 'busis')),
|
||||
array('bushes', array('bush', 'bushe')),
|
||||
array('calves', array('calf', 'calve', 'calff')),
|
||||
array('cars', 'car'),
|
||||
array('cassettes', array('cassett', 'cassette')),
|
||||
array('caves', array('caf', 'cave', 'caff')),
|
||||
array('chateaux', 'chateau'),
|
||||
array('cheeses', array('chees', 'cheese', 'cheesis')),
|
||||
array('children', 'child'),
|
||||
array('circuses', array('circus', 'circuse', 'circusis')),
|
||||
array('cliffs', 'cliff'),
|
||||
array('committee', 'committee'),
|
||||
array('crises', array('cris', 'crise', 'crisis')),
|
||||
array('criteria', array('criterion', 'criterium')),
|
||||
array('cups', 'cup'),
|
||||
array('data', array('daton', 'datum')),
|
||||
array('days', 'day'),
|
||||
array('discos', 'disco'),
|
||||
array('devices', array('devex', 'devix', 'device')),
|
||||
array('drives', 'drive'),
|
||||
array('drivers', 'driver'),
|
||||
array('dwarves', array('dwarf', 'dwarve', 'dwarff')),
|
||||
array('echoes', array('echo', 'echoe')),
|
||||
array('elves', array('elf', 'elve', 'elff')),
|
||||
array('emphases', array('emphas', 'emphase', 'emphasis')),
|
||||
array('faxes', 'fax'),
|
||||
array('feet', 'foot'),
|
||||
array('feedback', 'feedback'),
|
||||
array('foci', 'focus'),
|
||||
array('focuses', array('focus', 'focuse', 'focusis')),
|
||||
array('formulae', 'formula'),
|
||||
array('formulas', 'formula'),
|
||||
array('fungi', 'fungus'),
|
||||
array('funguses', array('fungus', 'funguse', 'fungusis')),
|
||||
array('garages', array('garag', 'garage')),
|
||||
array('geese', 'goose'),
|
||||
array('halves', array('half', 'halve', 'halff')),
|
||||
array('hats', 'hat'),
|
||||
array('heroes', array('hero', 'heroe')),
|
||||
array('hippopotamuses', array('hippopotamus', 'hippopotamuse', 'hippopotamusis')), //hippopotami
|
||||
array('hoaxes', 'hoax'),
|
||||
array('hooves', array('hoof', 'hoove', 'hooff')),
|
||||
array('houses', array('hous', 'house', 'housis')),
|
||||
array('indexes', 'index'),
|
||||
array('indices', array('index', 'indix', 'indice')),
|
||||
array('ions', 'ion'),
|
||||
array('irises', array('iris', 'irise', 'irisis')),
|
||||
array('kisses', 'kiss'),
|
||||
array('knives', 'knife'),
|
||||
array('lamps', 'lamp'),
|
||||
array('leaves', array('leaf', 'leave', 'leaff')),
|
||||
array('lice', 'louse'),
|
||||
array('lives', 'life'),
|
||||
array('matrices', array('matrex', 'matrix', 'matrice')),
|
||||
array('matrixes', 'matrix'),
|
||||
array('men', 'man'),
|
||||
array('mice', 'mouse'),
|
||||
array('moves', 'move'),
|
||||
array('movies', 'movie'),
|
||||
array('nebulae', 'nebula'),
|
||||
array('neuroses', array('neuros', 'neurose', 'neurosis')),
|
||||
array('news', 'news'),
|
||||
array('oases', array('oas', 'oase', 'oasis')),
|
||||
array('objectives', 'objective'),
|
||||
array('oxen', 'ox'),
|
||||
array('parties', 'party'),
|
||||
array('people', 'person'),
|
||||
array('persons', 'person'),
|
||||
array('phenomena', array('phenomenon', 'phenomenum')),
|
||||
array('photos', 'photo'),
|
||||
array('pianos', 'piano'),
|
||||
array('plateaux', 'plateau'),
|
||||
array('poppies', 'poppy'),
|
||||
array('prices', array('prex', 'prix', 'price')),
|
||||
array('quizzes', 'quiz'),
|
||||
array('radii', 'radius'),
|
||||
array('roofs', 'roof'),
|
||||
array('roses', array('ros', 'rose', 'rosis')),
|
||||
array('sandwiches', array('sandwich', 'sandwiche')),
|
||||
array('scarves', array('scarf', 'scarve', 'scarff')),
|
||||
array('schemas', 'schema'), //schemata
|
||||
array('selfies', 'selfie'),
|
||||
array('series', 'series'),
|
||||
array('services', 'service'),
|
||||
array('sheriffs', 'sheriff'),
|
||||
array('shoes', array('sho', 'shoe')),
|
||||
array('spies', 'spy'),
|
||||
array('staves', array('staf', 'stave', 'staff')),
|
||||
array('stories', 'story'),
|
||||
array('strata', array('straton', 'stratum')),
|
||||
array('suitcases', array('suitcas', 'suitcase', 'suitcasis')),
|
||||
array('syllabi', 'syllabus'),
|
||||
array('tags', 'tag'),
|
||||
array('teeth', 'tooth'),
|
||||
array('theses', array('thes', 'these', 'thesis')),
|
||||
array('thieves', array('thief', 'thieve', 'thieff')),
|
||||
array('trees', array('tre', 'tree')),
|
||||
array('waltzes', array('waltz', 'waltze')),
|
||||
array('wives', 'wife'),
|
||||
|
||||
// test casing: if the first letter was uppercase, it should remain so
|
||||
array('Men', 'Man'),
|
||||
array('GrandChildren', 'GrandChild'),
|
||||
array('SubTrees', array('SubTre', 'SubTree')),
|
||||
|
||||
// Known issues
|
||||
//array('insignia', 'insigne'),
|
||||
//array('insignias', 'insigne'),
|
||||
//array('rattles', 'rattle'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider singularifyProvider
|
||||
*/
|
||||
public function testSingularify($plural, $singular)
|
||||
{
|
||||
$single = StringUtil::singularify($plural);
|
||||
if (\is_string($singular) && \is_array($single)) {
|
||||
$this->fail("--- Expected\n`string`: ".$singular."\n+++ Actual\n`array`: ".implode(', ', $single));
|
||||
} elseif (\is_array($singular) && \is_string($single)) {
|
||||
$this->fail("--- Expected\n`array`: ".implode(', ', $singular)."\n+++ Actual\n`string`: ".$single);
|
||||
}
|
||||
|
||||
$this->assertEquals($singular, $single);
|
||||
}
|
||||
}
|
||||
34
vendor/symfony/property-access/composer.json
vendored
Normal file
34
vendor/symfony/property-access/composer.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "symfony/property-access",
|
||||
"type": "library",
|
||||
"description": "Symfony PropertyAccess Component",
|
||||
"keywords": ["property", "index", "access", "object", "array", "extraction", "injection", "reflection", "property path"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.9",
|
||||
"symfony/polyfill-ctype": "~1.8"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\PropertyAccess\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.8-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
31
vendor/symfony/property-access/phpunit.xml.dist
vendored
Normal file
31
vendor/symfony/property-access/phpunit.xml.dist
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Symfony PropertyAccess Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Resources</directory>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
Reference in New Issue
Block a user