From c81ec4dce53a0c909f17a18c31ba604c83653488 Mon Sep 17 00:00:00 2001 From: datibbaw Date: Thu, 21 Feb 2013 10:35:14 +0800 Subject: [PATCH 001/373] Update src/Symfony/Component/HttpFoundation/Request.php This makes `getContentType()` work when a regular form is submitted. --- src/Symfony/Component/HttpFoundation/Request.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 867a29b6afc64..961a27fc7d8b4 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1649,6 +1649,7 @@ protected static function initializeFormats() 'rdf' => array('application/rdf+xml'), 'atom' => array('application/atom+xml'), 'rss' => array('application/rss+xml'), + 'form' => array('application/x-www-form-urlencoded'), ); } From c6f3dd6a85eb5893df94ab2c9646842b53e47f29 Mon Sep 17 00:00:00 2001 From: Felds Liscia Date: Fri, 24 May 2013 14:02:19 -0300 Subject: [PATCH 002/373] add allow_extra_fields option --- .../Validator/Constraints/FormValidator.php | 2 +- .../Type/FormTypeValidatorExtension.php | 1 + .../Constraints/FormValidatorTest.php | 22 +++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index 1b8b68ed838cd..b14523755f913 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -107,7 +107,7 @@ public function validate($form, Constraint $constraint) } // Mark the form with an error if it contains extra fields - if (count($form->getExtraData()) > 0) { + if (!$config->getOption('allow_extra_fields') && count($form->getExtraData()) > 0) { $this->context->addViolation( $config->getOption('extra_fields_message'), array('{{ extra_fields }}' => implode('", "', array_keys($form->getExtraData()))), diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php index 99ef51f2bfed5..38e1ca5d31d99 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php @@ -67,6 +67,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) 'cascade_validation' => false, 'invalid_message' => 'This value is not valid.', 'invalid_message_parameters' => array(), + 'allow_extra_fields' => false, 'extra_fields_message' => 'This form should not contain extra fields.', 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', )); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php index 79d004cd6f294..2c4a239ae8164 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -615,6 +615,28 @@ public function testViolationIfExtraData() $this->validator->validate($form, new Form()); } + public function testNoViolationIfAllowExtraData() + { + $context = $this->getMockExecutionContext(); + + $form = $this + ->getBuilder('parent', null, array('allow_extra_fields' => true)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add($this->getBuilder('child')) + ->getForm(); + + $form->bind(array('foo' => 'bar')); + + $context->expects($this->never()) + ->method('addViolation'); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + /** * @dataProvider getPostMaxSizeFixtures */ From 4e6b27fe909c5eafbdf563b3c99304fc6ab73005 Mon Sep 17 00:00:00 2001 From: Adrien Brault Date: Fri, 13 Sep 2013 08:21:38 -0700 Subject: [PATCH 003/373] [Form] Choice children can be template customized like collection --- .../Tests/Extension/custom_widgets.html.twig | 13 ++++++-- .../Custom/_name_c_entry_label.html.php | 2 ++ ...l.html.php => _names_entry_label.html.php} | 0 .../Form/Extension/Core/Type/ChoiceType.php | 1 + .../Form/Tests/AbstractDivLayoutTest.php | 32 ++++++++++++++++--- .../Form/Tests/AbstractTableLayoutTest.php | 10 +++--- 6 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php rename src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/{_name_entry_label.html.php => _names_entry_label.html.php} (100%) diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig index 12fd7c66d8245..4eda8d76d3736 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig @@ -6,11 +6,20 @@ {% endspaceless %} {% endblock _text_id_widget %} -{% block _name_entry_label %} +{% block _names_entry_label %} {% spaceless %} {% if label is empty %} {% set label = name|humanize %} {% endif %} {% endspaceless %} -{% endblock _name_entry_label %} +{% endblock _names_entry_label %} + +{% block _name_c_entry_label %} +{% spaceless %} + {% if label is empty %} + {% set label = name|humanize %} + {% endif %} + +{% endspaceless %} +{% endblock _name_c_entry_label %} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php new file mode 100644 index 0000000000000..05240035c023d --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php @@ -0,0 +1,2 @@ +humanize($name); } ?> + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_names_entry_label.html.php similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_names_entry_label.html.php diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index d3d9187c0c0c7..eb7ae3b1e2604 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -256,6 +256,7 @@ private function addSubForms(FormBuilderInterface $builder, array $choiceViews, 'value' => $choiceView->value, 'label' => $choiceView->label, 'translation_domain' => $options['translation_domain'], + 'block_name' => 'entry', ); if ($options['multiple']) { diff --git a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php index ee9ed8f2a6a77..c4a825bd2dd6a 100644 --- a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -283,7 +283,7 @@ public function testRestAndRepeatedWithWidgetPerChild() public function testCollection() { - $form = $this->factory->createNamed('name', 'collection', array('a', 'b'), array( + $form = $this->factory->createNamed('names', 'collection', array('a', 'b'), array( 'type' => 'text', )); @@ -305,7 +305,7 @@ public function testCollectionWithAlternatingRowTypes() array('title' => 'a'), array('title' => 'b'), ); - $form = $this->factory->createNamed('name', 'collection', $data, array( + $form = $this->factory->createNamed('names', 'collection', $data, array( 'type' => new AlternatingRowType(), )); @@ -323,13 +323,13 @@ public function testCollectionWithAlternatingRowTypes() public function testEmptyCollection() { - $form = $this->factory->createNamed('name', 'collection', array(), array( + $form = $this->factory->createNamed('names', 'collection', array(), array( 'type' => 'text', )); $this->assertWidgetMatchesXpath($form->createView(), array(), '/div - [./input[@type="hidden"][@id="name__token"]] + [./input[@type="hidden"][@id="names__token"]] [count(./div)=0] ' ); @@ -670,7 +670,7 @@ public function testThemeInheritance($parentTheme, $childTheme) public function testCollectionRowWithCustomBlock() { $collection = array('one', 'two', 'three'); - $form = $this->factory->createNamedBuilder('name', 'collection', $collection) + $form = $this->factory->createNamedBuilder('names', 'collection', $collection) ->getForm(); $this->assertWidgetMatchesXpath($form->createView(), array(), @@ -684,6 +684,28 @@ public function testCollectionRowWithCustomBlock() ); } + /** + * The block "_name_c_entry_label" should be overridden in the theme of the + * implemented driver. + */ + public function testChoiceRowWithCustomBlock() + { + $form = $this->factory->createNamedBuilder('name_c', 'choice', 'a', array( + 'choices' => array('a' => 'ChoiceA', 'b' => 'ChoiceB'), + 'expanded' => true, + )) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./label[.="Custom name label: [trans]ChoiceA[/trans]"] + /following-sibling::label[.="Custom name label: [trans]ChoiceB[/trans]"] + ] +' + ); + } + public function testFormEndWithRest() { $view = $this->factory->createNamedBuilder('name', 'form') diff --git a/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php index 5c91195169951..7e7c8f33ebbdd 100644 --- a/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php @@ -193,7 +193,7 @@ public function testRest() public function testCollection() { - $form = $this->factory->createNamed('name', 'collection', array('a', 'b'), array( + $form = $this->factory->createNamed('names', 'collection', array('a', 'b'), array( 'type' => 'text', )); @@ -202,7 +202,7 @@ public function testCollection() [ ./tr[./td/input[@type="text"][@value="a"]] /following-sibling::tr[./td/input[@type="text"][@value="b"]] - /following-sibling::tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]] + /following-sibling::tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="names__token"]] ] [count(./tr[./td/input])=3] ' @@ -211,13 +211,13 @@ public function testCollection() public function testEmptyCollection() { - $form = $this->factory->createNamed('name', 'collection', array(), array( + $form = $this->factory->createNamed('names', 'collection', array(), array( 'type' => 'text', )); $this->assertWidgetMatchesXpath($form->createView(), array(), '/table - [./tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]]] + [./tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="names__token"]]] [count(./tr[./td/input])=1] ' ); @@ -439,7 +439,7 @@ public function testRepeatedWithCustomOptions() public function testCollectionRowWithCustomBlock() { $collection = array('one', 'two', 'three'); - $form = $this->factory->createNamedBuilder('name', 'collection', $collection) + $form = $this->factory->createNamedBuilder('names', 'collection', $collection) ->getForm(); $this->assertWidgetMatchesXpath($form->createView(), array(), From 78ceed193f4b2b2283c7283f8a8871e0bb28333f Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Thu, 5 Dec 2013 18:15:45 +0100 Subject: [PATCH 004/373] [Serializer] Added PropertyNormalizer, a new normalizer that maps an object's properties to an array --- src/Symfony/Component/Serializer/CHANGELOG.md | 6 + .../Normalizer/PropertyNormalizer.php | 217 +++++++++++++++ .../Normalizer/PropertyNormalizerTest.php | 247 ++++++++++++++++++ 3 files changed, 470 insertions(+) create mode 100644 src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php create mode 100644 src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 5b859623fef09..49ca9065de1d2 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +2.5.0 +----- + + * added a new serializer: `PropertyNormalizer`. Like `GetSetMethodNormalizer`, + this normalizer will map an object's properties to an array. + 2.4.0 ----- diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php new file mode 100644 index 0000000000000..9eefff3ca81b5 --- /dev/null +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -0,0 +1,217 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Normalizer; + +use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\Exception\RuntimeException; + +/** + * Converts between objects and arrays by mapping properties. + * + * The normalization process looks for all the object's properties (public and private). + * The result is a map from property names to property values. Property values + * are normalized through the serializer. + * + * The denormalization first looks at the constructor of the given class to see + * if any of the parameters have the same name as one of the properties. The + * constructor is then called with all parameters or an exception is thrown if + * any required parameters were not present as properties. Then the denormalizer + * walks through the given map of property names to property values to see if a + * property with the corresponding name exists. If found, the property gets the value. + * + * @author Matthieu Napoli + */ +class PropertyNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface +{ + private $callbacks = array(); + private $ignoredAttributes = array(); + private $camelizedAttributes = array(); + + /** + * Set normalization callbacks + * + * @param array $callbacks help normalize the result + * + * @throws InvalidArgumentException if a non-callable callback is set + */ + public function setCallbacks(array $callbacks) + { + foreach ($callbacks as $attribute => $callback) { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'The given callback for attribute "%s" is not callable.', + $attribute + )); + } + } + $this->callbacks = $callbacks; + } + + /** + * Set ignored attributes for normalization + * + * @param array $ignoredAttributes + */ + public function setIgnoredAttributes(array $ignoredAttributes) + { + $this->ignoredAttributes = $ignoredAttributes; + } + + /** + * Set attributes to be camelized on denormalize + * + * @param array $camelizedAttributes + */ + public function setCamelizedAttributes(array $camelizedAttributes) + { + $this->camelizedAttributes = $camelizedAttributes; + } + + /** + * {@inheritdoc} + */ + public function normalize($object, $format = null, array $context = array()) + { + $reflectionObject = new \ReflectionObject($object); + $attributes = array(); + + foreach ($reflectionObject->getProperties() as $property) { + if (in_array($property->name, $this->ignoredAttributes)) { + continue; + } + + // Override visibility + if (! $property->isPublic()) { + $property->setAccessible(true); + } + + $attributeValue = $property->getValue($object); + + if (array_key_exists($property->name, $this->callbacks)) { + $attributeValue = call_user_func($this->callbacks[$property->name], $attributeValue); + } + if (null !== $attributeValue && !is_scalar($attributeValue)) { + $attributeValue = $this->serializer->normalize($attributeValue, $format); + } + + $attributes[$property->name] = $attributeValue; + } + + return $attributes; + } + + /** + * {@inheritdoc} + */ + public function denormalize($data, $class, $format = null, array $context = array()) + { + $reflectionClass = new \ReflectionClass($class); + $constructor = $reflectionClass->getConstructor(); + + if ($constructor) { + $constructorParameters = $constructor->getParameters(); + + $params = array(); + foreach ($constructorParameters as $constructorParameter) { + $paramName = lcfirst($this->formatAttribute($constructorParameter->name)); + + if (isset($data[$paramName])) { + $params[] = $data[$paramName]; + // don't run set for a parameter passed to the constructor + unset($data[$paramName]); + } elseif (!$constructorParameter->isOptional()) { + throw new RuntimeException(sprintf( + 'Cannot create an instance of %s from serialized data because ' . + 'its constructor requires parameter "%s" to be present.', + $class, + $constructorParameter->name + )); + } + } + + $object = $reflectionClass->newInstanceArgs($params); + } else { + $object = new $class; + } + + foreach ($data as $propertyName => $value) { + $propertyName = lcfirst($this->formatAttribute($propertyName)); + + if ($reflectionClass->hasProperty($propertyName)) { + $property = $reflectionClass->getProperty($propertyName); + + // Override visibility + if (! $property->isPublic()) { + $property->setAccessible(true); + } + + $property->setValue($object, $value); + } + } + + return $object; + } + + /** + * {@inheritDoc} + */ + public function supportsNormalization($data, $format = null) + { + return is_object($data) && $this->supports(get_class($data)); + } + + /** + * {@inheritDoc} + */ + public function supportsDenormalization($data, $type, $format = null) + { + return $this->supports($type); + } + + /** + * Format an attribute name, for example to convert a snake_case name to camelCase. + * + * @param string $attributeName + * @return string + */ + protected function formatAttribute($attributeName) + { + if (in_array($attributeName, $this->camelizedAttributes)) { + return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { + return ('.' === $match[1] ? '_' : '').strtoupper($match[2]); + }, $attributeName); + } + + return $attributeName; + } + + /** + * Checks if the given class has any non-static property. + * + * @param string $class + * + * @return Boolean + */ + private function supports($class) + { + $class = new \ReflectionClass($class); + + // We look for at least one non-static property + foreach ($class->getProperties() as $property) { + if (! $property->isStatic()) { + return true; + } + } + + return false; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php new file mode 100644 index 0000000000000..b398b75efb0a4 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php @@ -0,0 +1,247 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Normalizer; + +use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; + +class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var PropertyNormalizer + */ + private $normalizer; + + protected function setUp() + { + $this->normalizer = new PropertyNormalizer(); + $this->normalizer->setSerializer($this->getMock('Symfony\Component\Serializer\Serializer')); + } + + public function testNormalize() + { + $obj = new PropertyDummy(); + $obj->foo = 'foo'; + $obj->setBar('bar'); + $obj->setCamelCase('camelcase'); + $this->assertEquals( + array('foo' => 'foo', 'bar' => 'bar', 'camelCase' => 'camelcase'), + $this->normalizer->normalize($obj, 'any') + ); + } + + public function testDenormalize() + { + $obj = $this->normalizer->denormalize( + array('foo' => 'foo', 'bar' => 'bar'), + __NAMESPACE__.'\PropertyDummy', + 'any' + ); + $this->assertEquals('foo', $obj->foo); + $this->assertEquals('bar', $obj->getBar()); + } + + public function testDenormalizeOnCamelCaseFormat() + { + $this->normalizer->setCamelizedAttributes(array('camel_case')); + $obj = $this->normalizer->denormalize( + array('camel_case' => 'value'), + __NAMESPACE__.'\PropertyDummy' + ); + $this->assertEquals('value', $obj->getCamelCase()); + } + + /** + * @dataProvider attributeProvider + */ + public function testFormatAttribute($attribute, $camelizedAttributes, $result) + { + $r = new \ReflectionObject($this->normalizer); + $m = $r->getMethod('formatAttribute'); + $m->setAccessible(true); + + $this->normalizer->setCamelizedAttributes($camelizedAttributes); + $this->assertEquals($m->invoke($this->normalizer, $attribute, $camelizedAttributes), $result); + } + + public function attributeProvider() + { + return array( + array('attribute_test', array('attribute_test'),'AttributeTest'), + array('attribute_test', array('any'),'attribute_test'), + array('attribute', array('attribute'),'Attribute'), + array('attribute', array(), 'attribute'), + ); + } + + public function testConstructorDenormalize() + { + $obj = $this->normalizer->denormalize( + array('foo' => 'foo', 'bar' => 'bar'), + __NAMESPACE__.'\PropertyConstructorDummy', + 'any' + ); + $this->assertEquals('foo', $obj->getFoo()); + $this->assertEquals('bar', $obj->getBar()); + } + + /** + * @dataProvider provideCallbacks + */ + public function testCallbacks($callbacks, $value, $result, $message) + { + $this->normalizer->setCallbacks($callbacks); + + $obj = new PropertyConstructorDummy('', $value); + + $this->assertEquals( + $result, + $this->normalizer->normalize($obj, 'any'), + $message + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testUncallableCallbacks() + { + $this->normalizer->setCallbacks(array('bar' => null)); + + $obj = new PropertyConstructorDummy('baz', 'quux'); + + $this->normalizer->normalize($obj, 'any'); + } + + public function testIgnoredAttributes() + { + $this->normalizer->setIgnoredAttributes(array('foo', 'bar', 'camelCase')); + + $obj = new PropertyDummy(); + $obj->foo = 'foo'; + $obj->setBar('bar'); + + $this->assertEquals( + array(), + $this->normalizer->normalize($obj, 'any') + ); + } + + public function provideCallbacks() + { + return array( + array( + array( + 'bar' => function ($bar) { + return 'baz'; + }, + ), + 'baz', + array('foo' => '', 'bar' => 'baz'), + 'Change a string', + ), + array( + array( + 'bar' => function ($bar) { + return null; + }, + ), + 'baz', + array('foo' => '', 'bar' => null), + 'Null an item' + ), + array( + array( + 'bar' => function ($bar) { + return $bar->format('d-m-Y H:i:s'); + }, + ), + new \DateTime('2011-09-10 06:30:00'), + array('foo' => '', 'bar' => '10-09-2011 06:30:00'), + 'Format a date', + ), + array( + array( + 'bar' => function ($bars) { + $foos = ''; + foreach ($bars as $bar) { + $foos .= $bar->getFoo(); + } + + return $foos; + }, + ), + array(new PropertyConstructorDummy('baz', ''), new PropertyConstructorDummy('quux', '')), + array('foo' => '', 'bar' => 'bazquux'), + 'Collect a property', + ), + array( + array( + 'bar' => function ($bars) { + return count($bars); + }, + ), + array(new PropertyConstructorDummy('baz', ''), new PropertyConstructorDummy('quux', '')), + array('foo' => '', 'bar' => 2), + 'Count a property', + ), + ); + } +} + +class PropertyDummy +{ + public $foo; + private $bar; + protected $camelCase; + + public function getBar() + { + return $this->bar; + } + + public function setBar($bar) + { + $this->bar = $bar; + } + + public function getCamelCase() + { + return $this->camelCase; + } + + public function setCamelCase($camelCase) + { + $this->camelCase = $camelCase; + } +} + +class PropertyConstructorDummy +{ + protected $foo; + private $bar; + + public function __construct($foo, $bar) + { + $this->foo = $foo; + $this->bar = $bar; + } + + public function getFoo() + { + return $this->foo; + } + + public function getBar() + { + return $this->bar; + } +} From 78fa5e285bc11508e31a255ee7524342a6ef8515 Mon Sep 17 00:00:00 2001 From: Kacper Gunia Date: Fri, 11 Apr 2014 16:33:31 +0100 Subject: [PATCH 005/373] Call AuthenticationManager in AnonymousAuthenticationListener --- .../Resources/config/security_listeners.xml | 1 + .../AnonymousAuthenticationListener.php | 25 ++++++++++---- .../AnonymousAuthenticationListenerTest.php | 34 +++++++++++++++---- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml index a8d9bf316c8db..44c92d15ece20 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml @@ -54,6 +54,7 @@ + diff --git a/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php index 59f05ff6251a9..986c9a8ee1c4d 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Http\Firewall; +use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\SecurityContextInterface; use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -26,13 +28,15 @@ class AnonymousAuthenticationListener implements ListenerInterface { private $context; private $key; + private $authenticationManager; private $logger; - public function __construct(SecurityContextInterface $context, $key, LoggerInterface $logger = null) + public function __construct(SecurityContextInterface $context, $key, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null) { - $this->context = $context; - $this->key = $key; - $this->logger = $logger; + $this->context = $context; + $this->key = $key; + $this->authenticationManager = $authenticationManager; + $this->logger = $logger; } /** @@ -46,10 +50,17 @@ public function handle(GetResponseEvent $event) return; } - $this->context->setToken(new AnonymousToken($this->key, 'anon.', array())); + try { + $token = $this->authenticationManager->authenticate(new AnonymousToken($this->key, 'anon.', array())); + $this->context->setToken($token); - if (null !== $this->logger) { - $this->logger->info('Populated SecurityContext with an anonymous Token'); + if (null !== $this->logger) { + $this->logger->info('Populated SecurityContext with an anonymous Token'); + } + } catch (AuthenticationException $failed) { + if (null !== $this->logger) { + $this->logger->info(sprintf('Anonymous authentication failed: %s', $failed->getMessage())); + } } } } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php index 1fb7350c6cbae..e6bab4ea255f7 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Http\Tests\Firewall; +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener; class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase @@ -28,7 +29,13 @@ public function testHandleWithContextHavingAToken() ->method('setToken') ; - $listener = new AnonymousAuthenticationListener($context, 'TheKey'); + $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + $authenticationManager + ->expects($this->never()) + ->method('authenticate') + ; + + $listener = new AnonymousAuthenticationListener($context, 'TheKey', $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } @@ -40,16 +47,27 @@ public function testHandleWithContextHavingNoToken() ->method('getToken') ->will($this->returnValue(null)) ; - $context + + $anonymousToken = new AnonymousToken('TheKey', 'anon.', array()); + + $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + $authenticationManager ->expects($this->once()) - ->method('setToken') + ->method('authenticate') ->with(self::logicalAnd( - $this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken'), - $this->attributeEqualTo('key', 'TheKey') + $this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken'), + $this->attributeEqualTo('key', 'TheKey') )) + ->will($this->returnValue($anonymousToken)) ; - $listener = new AnonymousAuthenticationListener($context, 'TheKey'); + $context + ->expects($this->once()) + ->method('setToken') + ->with($anonymousToken) + ; + + $listener = new AnonymousAuthenticationListener($context, 'TheKey', $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } @@ -66,7 +84,9 @@ public function testHandledEventIsLogged() ->with('Populated SecurityContext with an anonymous Token') ; - $listener = new AnonymousAuthenticationListener($context, 'TheKey', $logger); + $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); + + $listener = new AnonymousAuthenticationListener($context, 'TheKey', $authenticationManager, $logger); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } } From a702124efb03a85685f9808bf3c8e4d4b65ef9b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 10 Jan 2014 01:58:15 +0100 Subject: [PATCH 006/373] [SecurityBundle] added acl:set command --- composer.json | 1 + .../SecurityBundle/Command/SetAclCommand.php | 171 ++++++++++++++++++ .../Functional/Bundle/AclBundle/AclBundle.php | 21 +++ .../Bundle/AclBundle/Entity/Car.php | 22 +++ .../Tests/Functional/SetAclCommandTest.php | 168 +++++++++++++++++ .../Tests/Functional/app/Acl/bundles.php | 8 + .../Tests/Functional/app/Acl/config.yml | 24 +++ .../Bundle/SecurityBundle/composer.json | 3 +- 8 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Bundle/SecurityBundle/Command/SetAclCommand.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/AclBundle.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/Entity/Car.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/bundles.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/config.yml diff --git a/composer.json b/composer.json index 3b67c4b3e8ec5..1860fa7b2220a 100644 --- a/composer.json +++ b/composer.json @@ -70,6 +70,7 @@ "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.2", "doctrine/orm": "~2.2,>=2.2.3", + "doctrine/doctrine-bundle": "~1.2", "monolog/monolog": "~1.3", "propel/propel1": "1.6.*", "ircmaxell/password-compat": "1.0.*", diff --git a/src/Symfony/Bundle/SecurityBundle/Command/SetAclCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/SetAclCommand.php new file mode 100644 index 0000000000000..de01dc6b33d4e --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Command/SetAclCommand.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Security\Acl\Domain\ObjectIdentity; +use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; +use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; +use Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException; +use Symfony\Component\Security\Acl\Permission\MaskBuilder; +use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface; + +/** + * Sets ACL for objects + * + * @author Kévin Dunglas + */ +class SetAclCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + public function isEnabled() + { + if (!$this->getContainer()->has('security.acl.provider')) { + return false; + } + + $provider = $this->getContainer()->get('security.acl.provider'); + if (!$provider instanceof MutableAclProviderInterface) { + return false; + } + + return parent::isEnabled(); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('acl:set') + ->setDescription('Sets ACL for objects') + ->setHelp(<<%command.name% command sets ACL. +The ACL system must have been initialized with the init:acl command. + +To set VIEW and EDIT permissions for the user kevin on the instance of Acme\MyClass having the identifier 42: + +php %command.full_name% --user=Symfony/Component/Security/Core/User/User:kevin VIEW EDIT Acme/MyClass:42 + +Note that you can use / instead of \\ for the namespace delimiter to avoid any +problem. + +To set permissions for a role, use the --role option: + +php %command.full_name% --role=ROLE_USER VIEW Acme/MyClass:1936 + +To set permissions at the class scope, use the --class-scope option: + +php %command.full_name% --class-scope --user=Symfony/Component/Security/Core/User/User:anne OWNER Acme/MyClass:42 +EOF + ) + ->addArgument('arguments', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'A list of permissions and object identities (class name and ID separated by a column)') + ->addOption('user', null, InputOption::VALUE_REQUIRED, 'A list of security identities') + ->addOption('role', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A list of roles') + ->addOption('class-scope', null, InputOption::VALUE_NONE, 'Use class-scope entries') + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Parse arguments + $objectIdentities = array(); + $maskBuilder = $this->getMaskBuilder(); + foreach ($input->getArgument('arguments') as $argument) { + $data = explode(':', $argument, 2); + + if (count($data) > 1) { + $objectIdentities[] = new ObjectIdentity($data[1], strtr($data[0], '/', '\\')); + } else { + $maskBuilder->add($data[0]); + } + } + + // Build permissions mask + $mask = $maskBuilder->get(); + + $userOption = $input->getOption('user'); + $roleOption = $input->getOption('role'); + $classScopeOption = $input->getOption('class-scope'); + + if (empty($userOption) && empty($roleOption)) { + throw new \InvalidArgumentException('A Role or a User must be specified.'); + } + + // Create security identities + $securityIdentities = array(); + + if ($userOption) { + foreach ($userOption as $user) { + $data = explode(':', $user, 2); + + if (count($data) === 1) { + throw new \InvalidArgumentException('The user must follow the format "Acme/MyUser:username".'); + } + + $securityIdentities[] = new UserSecurityIdentity($data[1], strtr($data[0], '/', '\\')); + } + } + + if ($roleOption) { + foreach ($roleOption as $role) { + $securityIdentities[] = new RoleSecurityIdentity($role); + } + } + + /** @var $container \Symfony\Component\DependencyInjection\ContainerInterface */ + $container = $this->getContainer(); + /** @var $aclProvider MutableAclProviderInterface */ + $aclProvider = $container->get('security.acl.provider'); + + // Sets ACL + foreach ($objectIdentities as $objectIdentity) { + // Creates a new ACL if it does not already exist + try { + $aclProvider->createAcl($objectIdentity); + } catch (AclAlreadyExistsException $e) { + } + + $acl = $aclProvider->findAcl($objectIdentity, $securityIdentities); + + foreach ($securityIdentities as $securityIdentity) { + if ($classScopeOption) { + $acl->insertClassAce($securityIdentity, $mask); + } else { + $acl->insertObjectAce($securityIdentity, $mask); + } + } + + $aclProvider->updateAcl($acl); + } + } + + /** + * Gets the mask builder + * + * @return MaskBuilder + */ + protected function getMaskBuilder() + { + return new MaskBuilder(); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/AclBundle.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/AclBundle.php new file mode 100644 index 0000000000000..1208003bcc2c4 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/AclBundle.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * @author Kévin Dunglas + */ +class AclBundle extends Bundle +{ +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/Entity/Car.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/Entity/Car.php new file mode 100644 index 0000000000000..dd46db2f6459c --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/Entity/Car.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\Entity; + +/** + * Car + * + * @author Kévin Dunglas + */ +class Car +{ + public $id; +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php new file mode 100644 index 0000000000000..c476714e88f27 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\SecurityBundle\Command\InitAclCommand; +use Symfony\Bundle\SecurityBundle\Command\SetAclCommand; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Security\Acl\Domain\ObjectIdentity; +use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; +use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; +use Symfony\Component\Security\Acl\Exception\NoAceFoundException; +use Symfony\Component\Security\Acl\Permission\BasicPermissionMap; + +/** + * Tests SetAclCommand + * + * @author Kévin Dunglas + */ +class SetAclCommandTest extends WebTestCase +{ + const OBJECT_CLASS = 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\Entity\Car'; + const SECURITY_CLASS = 'Symfony\Component\Security\Core\User\User'; + + public function testSetAclUser() + { + $objectId = 1; + $securityUsername1 = 'kevin'; + $securityUsername2 = 'anne'; + $grantedPermission1 = 'VIEW'; + $grantedPermission2 = 'EDIT'; + + $application = $this->getApplication(); + $application->add(new SetAclCommand()); + + $setAclCommand = $application->find('acl:set'); + $setAclCommandTester = new CommandTester($setAclCommand); + $setAclCommandTester->execute(array( + 'command' => 'acl:set', + 'arguments' => array($grantedPermission1, $grantedPermission2, sprintf('%s:%s', self::OBJECT_CLASS, $objectId)), + '--user' => array(sprintf('%s:%s', self::SECURITY_CLASS, $securityUsername1), sprintf('%s:%s', self::SECURITY_CLASS, $securityUsername2)) + )); + + $objectIdentity = new ObjectIdentity($objectId, self::OBJECT_CLASS); + $securityIdentity1 = new UserSecurityIdentity($securityUsername1, self::SECURITY_CLASS); + $securityIdentity2 = new UserSecurityIdentity($securityUsername2, self::SECURITY_CLASS); + $permissionMap = new BasicPermissionMap(); + + /** @var \Symfony\Component\Security\Acl\Model\AclProviderInterface $aclProvider */ + $aclProvider = $application->getKernel()->getContainer()->get('security.acl.provider'); + $acl = $aclProvider->findAcl($objectIdentity, array($securityIdentity1)); + + $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission1, null), array($securityIdentity1))); + $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission1, null), array($securityIdentity2))); + $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission2, null), array($securityIdentity2))); + + try { + $acl->isGranted($permissionMap->getMasks('OWNER', null), array($securityIdentity1)); + $this->fail('NoAceFoundException not throwed'); + } catch (NoAceFoundException $e) { + } + + try { + $acl->isGranted($permissionMap->getMasks('OPERATOR', null), array($securityIdentity2)); + $this->fail('NoAceFoundException not throwed'); + } catch (NoAceFoundException $e) { + } + } + + public function testSetAclRole() + { + $objectId = 1; + $securityUsername = 'kevin'; + $grantedPermission = 'VIEW'; + $role = 'ROLE_ADMIN'; + + $application = $this->getApplication(); + $application->add(new SetAclCommand()); + + $setAclCommand = $application->find('acl:set'); + $setAclCommandTester = new CommandTester($setAclCommand); + $setAclCommandTester->execute(array( + 'command' => 'acl:set', + 'arguments' => array($grantedPermission, sprintf('%s:%s', strtr(self::OBJECT_CLASS, '\\', '/'), $objectId)), + '--role' => array($role) + )); + + $objectIdentity = new ObjectIdentity($objectId, self::OBJECT_CLASS); + $userSecurityIdentity = new UserSecurityIdentity($securityUsername, self::SECURITY_CLASS); + $roleSecurityIdentity = new RoleSecurityIdentity($role); + $permissionMap = new BasicPermissionMap(); + + /** @var \Symfony\Component\Security\Acl\Model\AclProviderInterface $aclProvider */ + $aclProvider = $application->getKernel()->getContainer()->get('security.acl.provider'); + $acl = $aclProvider->findAcl($objectIdentity, array($roleSecurityIdentity, $userSecurityIdentity)); + + $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); + $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); + + try { + $acl->isGranted($permissionMap->getMasks('VIEW', null), array($userSecurityIdentity)); + $this->fail('NoAceFoundException not throwed'); + } catch (NoAceFoundException $e) { + } + + try { + $acl->isGranted($permissionMap->getMasks('OPERATOR', null), array($userSecurityIdentity)); + $this->fail('NoAceFoundException not throwed'); + } catch (NoAceFoundException $e) { + } + } + + public function testSetAclClassScope() + { + $objectId = 1; + $grantedPermission = 'VIEW'; + $role = 'ROLE_USER'; + + $application = $this->getApplication(); + $application->add(new SetAclCommand()); + + $setAclCommand = $application->find('acl:set'); + $setAclCommandTester = new CommandTester($setAclCommand); + $setAclCommandTester->execute(array( + 'command' => 'acl:set', + 'arguments' => array($grantedPermission, sprintf('%s:%s', self::OBJECT_CLASS, $objectId)), + '--class-scope' => true, + '--role' => array($role) + )); + + $objectIdentity1 = new ObjectIdentity($objectId, self::OBJECT_CLASS); + $objectIdentity2 = new ObjectIdentity(2, self::OBJECT_CLASS); + $roleSecurityIdentity = new RoleSecurityIdentity($role); + $permissionMap = new BasicPermissionMap(); + + /** @var \Symfony\Component\Security\Acl\Model\AclProviderInterface $aclProvider */ + $aclProvider = $application->getKernel()->getContainer()->get('security.acl.provider'); + + $acl1 = $aclProvider->findAcl($objectIdentity1, array($roleSecurityIdentity)); + $this->assertTrue($acl1->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); + + $acl2 = $aclProvider->createAcl($objectIdentity2); + $this->assertTrue($acl2->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); + } + + private function getApplication() + { + $kernel = $this->createKernel(array('test_case' => 'Acl')); + $kernel->boot(); + + $application = new Application($kernel); + $application->add(new InitAclCommand()); + + $initAclCommand = $application->find('init:acl'); + $initAclCommandTester = new CommandTester($initAclCommand); + $initAclCommandTester->execute(array('command' => 'init:acl')); + + return $application; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/bundles.php new file mode 100644 index 0000000000000..0dad9099b493d --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/bundles.php @@ -0,0 +1,8 @@ + Date: Sun, 27 Apr 2014 11:03:19 +0200 Subject: [PATCH 007/373] Allow overloading ContextListener::refreshUser() --- .../Component/Security/Http/Firewall/ContextListener.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 435c44026ead2..e61907eff302b 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -142,7 +142,7 @@ public function onKernelResponse(FilterResponseEvent $event) * * @throws \RuntimeException */ - private function refreshUser(TokenInterface $token) + protected function refreshUser(TokenInterface $token) { $user = $token->getUser(); if (!$user instanceof UserInterface) { From 88274df463fc72a83e8ee5df319e4147607a8e5b Mon Sep 17 00:00:00 2001 From: Dan Harper Date: Tue, 29 Apr 2014 16:01:48 +0100 Subject: [PATCH 008/373] [HttpKernel] Extract method to make callable controller in ControllerResolver correct naming --- .../HttpKernel/Controller/ControllerResolver.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php index e2e550e54267d..d875416717a59 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php @@ -78,7 +78,7 @@ public function getController(Request $request) if (false === strpos($controller, ':')) { if (method_exists($controller, '__invoke')) { - return new $controller(); + return $this->instantiateController($controller); } elseif (function_exists($controller)) { return $controller; } @@ -167,6 +167,18 @@ protected function createController($controller) throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); } - return array(new $class(), $method); + return array($this->instantiateController($class), $method); + } + + /** + * Returns an instantiated controller + * + * @param string $class A class name + * + * @return object + */ + protected function instantiateController($class) + { + return new $class(); } } From a11645c63740c29c10f49af961c9856676778349 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 23 May 2014 16:36:49 +0200 Subject: [PATCH 009/373] updated version to 2.6 --- composer.json | 2 +- src/Symfony/Bridge/Doctrine/composer.json | 2 +- src/Symfony/Bridge/Monolog/composer.json | 2 +- src/Symfony/Bridge/Propel1/composer.json | 2 +- src/Symfony/Bridge/ProxyManager/composer.json | 2 +- src/Symfony/Bridge/Swiftmailer/composer.json | 2 +- src/Symfony/Bridge/Twig/composer.json | 2 +- src/Symfony/Bundle/FrameworkBundle/composer.json | 2 +- src/Symfony/Bundle/SecurityBundle/composer.json | 2 +- src/Symfony/Bundle/TwigBundle/composer.json | 2 +- src/Symfony/Bundle/WebProfilerBundle/composer.json | 2 +- src/Symfony/Component/BrowserKit/composer.json | 2 +- src/Symfony/Component/ClassLoader/composer.json | 2 +- src/Symfony/Component/Config/composer.json | 2 +- src/Symfony/Component/Console/composer.json | 2 +- src/Symfony/Component/CssSelector/composer.json | 2 +- src/Symfony/Component/Debug/composer.json | 2 +- src/Symfony/Component/DependencyInjection/composer.json | 2 +- src/Symfony/Component/DomCrawler/composer.json | 2 +- src/Symfony/Component/EventDispatcher/composer.json | 2 +- src/Symfony/Component/ExpressionLanguage/composer.json | 2 +- src/Symfony/Component/Filesystem/composer.json | 2 +- src/Symfony/Component/Finder/composer.json | 2 +- src/Symfony/Component/Form/README.md | 2 +- src/Symfony/Component/Form/composer.json | 2 +- src/Symfony/Component/HttpFoundation/composer.json | 2 +- src/Symfony/Component/HttpKernel/Kernel.php | 6 +++--- src/Symfony/Component/HttpKernel/composer.json | 2 +- src/Symfony/Component/Intl/README.md | 2 +- src/Symfony/Component/Intl/composer.json | 2 +- src/Symfony/Component/Locale/composer.json | 2 +- src/Symfony/Component/OptionsResolver/composer.json | 2 +- src/Symfony/Component/Process/composer.json | 2 +- src/Symfony/Component/PropertyAccess/composer.json | 2 +- src/Symfony/Component/Routing/composer.json | 2 +- src/Symfony/Component/Security/Acl/README.md | 2 +- src/Symfony/Component/Security/Acl/composer.json | 2 +- src/Symfony/Component/Security/Core/README.md | 2 +- src/Symfony/Component/Security/Core/composer.json | 2 +- src/Symfony/Component/Security/Csrf/README.md | 2 +- src/Symfony/Component/Security/Csrf/composer.json | 2 +- src/Symfony/Component/Security/Http/README.md | 2 +- src/Symfony/Component/Security/Http/composer.json | 2 +- src/Symfony/Component/Security/README.md | 2 +- src/Symfony/Component/Security/composer.json | 2 +- src/Symfony/Component/Serializer/composer.json | 2 +- src/Symfony/Component/Stopwatch/composer.json | 2 +- src/Symfony/Component/Templating/composer.json | 2 +- src/Symfony/Component/Translation/README.md | 2 +- src/Symfony/Component/Translation/composer.json | 2 +- src/Symfony/Component/Validator/README.md | 2 +- src/Symfony/Component/Validator/composer.json | 2 +- src/Symfony/Component/Yaml/composer.json | 2 +- 53 files changed, 55 insertions(+), 55 deletions(-) diff --git a/composer.json b/composer.json index 7efd53bdfff40..aa44ad6a3a198 100644 --- a/composer.json +++ b/composer.json @@ -87,7 +87,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index 383df1ed370b1..16a0cc27c4628 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -45,7 +45,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Bridge/Monolog/composer.json b/src/Symfony/Bridge/Monolog/composer.json index 067f1c70d71d7..2ce523e5fe2b2 100644 --- a/src/Symfony/Bridge/Monolog/composer.json +++ b/src/Symfony/Bridge/Monolog/composer.json @@ -36,7 +36,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Bridge/Propel1/composer.json b/src/Symfony/Bridge/Propel1/composer.json index 6aa820d6b34e4..e1fe6f469a832 100644 --- a/src/Symfony/Bridge/Propel1/composer.json +++ b/src/Symfony/Bridge/Propel1/composer.json @@ -32,7 +32,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index 04cc581ac3817..9d9859b162a05 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -29,7 +29,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Bridge/Swiftmailer/composer.json b/src/Symfony/Bridge/Swiftmailer/composer.json index 8e5277692e706..d077c543b7e5a 100644 --- a/src/Symfony/Bridge/Swiftmailer/composer.json +++ b/src/Symfony/Bridge/Swiftmailer/composer.json @@ -29,7 +29,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 44541136600bc..59db37b44f68d 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -50,7 +50,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 894f1f7869c32..f30def2e076b5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -52,7 +52,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index c54213c9bfb39..a8deb483a311a 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index 49e32a193e098..3e6c430d02698 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index c6e5ee51248a9..15a9beffb85ca 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/BrowserKit/composer.json b/src/Symfony/Component/BrowserKit/composer.json index ed914dde46684..7ff2cab391ec2 100644 --- a/src/Symfony/Component/BrowserKit/composer.json +++ b/src/Symfony/Component/BrowserKit/composer.json @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/ClassLoader/composer.json b/src/Symfony/Component/ClassLoader/composer.json index 84ce6a0a633a7..f323a76c6f5a7 100644 --- a/src/Symfony/Component/ClassLoader/composer.json +++ b/src/Symfony/Component/ClassLoader/composer.json @@ -28,7 +28,7 @@ "target-dir": "Symfony/Component/ClassLoader", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json index dce426b77c0e3..c11d3a603d601 100644 --- a/src/Symfony/Component/Config/composer.json +++ b/src/Symfony/Component/Config/composer.json @@ -26,7 +26,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json index e7a8e2fc70ed8..40131af463357 100644 --- a/src/Symfony/Component/Console/composer.json +++ b/src/Symfony/Component/Console/composer.json @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/CssSelector/composer.json b/src/Symfony/Component/CssSelector/composer.json index 5b4231d794757..d47c77fe6444a 100644 --- a/src/Symfony/Component/CssSelector/composer.json +++ b/src/Symfony/Component/CssSelector/composer.json @@ -29,7 +29,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Debug/composer.json b/src/Symfony/Component/Debug/composer.json index b9cd2d340a363..447ecc8e9b42b 100644 --- a/src/Symfony/Component/Debug/composer.json +++ b/src/Symfony/Component/Debug/composer.json @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index 2190101fa2c9f..c0090495a1bff 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -35,7 +35,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/DomCrawler/composer.json b/src/Symfony/Component/DomCrawler/composer.json index 0246346238c0f..dd305f7d1f112 100644 --- a/src/Symfony/Component/DomCrawler/composer.json +++ b/src/Symfony/Component/DomCrawler/composer.json @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/EventDispatcher/composer.json b/src/Symfony/Component/EventDispatcher/composer.json index 3715ece302fb1..f07e3be4f22b7 100644 --- a/src/Symfony/Component/EventDispatcher/composer.json +++ b/src/Symfony/Component/EventDispatcher/composer.json @@ -35,7 +35,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/ExpressionLanguage/composer.json b/src/Symfony/Component/ExpressionLanguage/composer.json index 3828f0435aa76..ebd1ba1b2e793 100644 --- a/src/Symfony/Component/ExpressionLanguage/composer.json +++ b/src/Symfony/Component/ExpressionLanguage/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Filesystem/composer.json b/src/Symfony/Component/Filesystem/composer.json index dfa633c1d06ce..981423e6ea1ce 100644 --- a/src/Symfony/Component/Filesystem/composer.json +++ b/src/Symfony/Component/Filesystem/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Finder/composer.json b/src/Symfony/Component/Finder/composer.json index b6e6997884e0f..a91b4707f7c02 100644 --- a/src/Symfony/Component/Finder/composer.json +++ b/src/Symfony/Component/Finder/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Form/README.md b/src/Symfony/Component/Form/README.md index 340bc6e63137f..ff353701f22f2 100644 --- a/src/Symfony/Component/Form/README.md +++ b/src/Symfony/Component/Form/README.md @@ -14,7 +14,7 @@ https://github.com/fabpot/Silex/blob/master/src/Silex/Provider/FormServiceProvid Documentation: -http://symfony.com/doc/2.5/book/forms.html +http://symfony.com/doc/2.6/book/forms.html Resources --------- diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 56176c234ffd2..14412e1f61e05 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -42,7 +42,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/HttpFoundation/composer.json b/src/Symfony/Component/HttpFoundation/composer.json index db57eb563ebfc..25294adb79445 100644 --- a/src/Symfony/Component/HttpFoundation/composer.json +++ b/src/Symfony/Component/HttpFoundation/composer.json @@ -29,7 +29,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 99a3491e55bc7..293b474f07b5f 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -60,10 +60,10 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.5.0-DEV'; - const VERSION_ID = '20500'; + const VERSION = '2.6.0-DEV'; + const VERSION_ID = '20600'; const MAJOR_VERSION = '2'; - const MINOR_VERSION = '5'; + const MINOR_VERSION = '6'; const RELEASE_VERSION = '0'; const EXTRA_VERSION = 'DEV'; diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 5ddee871a37af..7105b5cb12817 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -51,7 +51,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Intl/README.md b/src/Symfony/Component/Intl/README.md index f5a4ca0432440..60b2e3e42b290 100644 --- a/src/Symfony/Component/Intl/README.md +++ b/src/Symfony/Component/Intl/README.md @@ -22,4 +22,4 @@ You can run the unit tests with the following command: $ phpunit [0]: http://www.php.net/manual/en/intl.setup.php -[1]: http://symfony.com/doc/2.5/components/intl.html +[1]: http://symfony.com/doc/2.6/components/intl.html diff --git a/src/Symfony/Component/Intl/composer.json b/src/Symfony/Component/Intl/composer.json index 9be866e05fcb3..9b7148166e5bf 100644 --- a/src/Symfony/Component/Intl/composer.json +++ b/src/Symfony/Component/Intl/composer.json @@ -42,7 +42,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Locale/composer.json b/src/Symfony/Component/Locale/composer.json index 26015cea6ef89..a1dcfc0523f10 100644 --- a/src/Symfony/Component/Locale/composer.json +++ b/src/Symfony/Component/Locale/composer.json @@ -26,7 +26,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/OptionsResolver/composer.json b/src/Symfony/Component/OptionsResolver/composer.json index b0e71ea2997a1..f467e09b7fb51 100644 --- a/src/Symfony/Component/OptionsResolver/composer.json +++ b/src/Symfony/Component/OptionsResolver/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Process/composer.json b/src/Symfony/Component/Process/composer.json index b5dbfe1390a2b..39fa97cb59b05 100644 --- a/src/Symfony/Component/Process/composer.json +++ b/src/Symfony/Component/Process/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index a34297fd9ed11..ebf35abc283e2 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Routing/composer.json b/src/Symfony/Component/Routing/composer.json index 9a88cc149fb33..1226298cc3047 100644 --- a/src/Symfony/Component/Routing/composer.json +++ b/src/Symfony/Component/Routing/composer.json @@ -38,7 +38,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Security/Acl/README.md b/src/Symfony/Component/Security/Acl/README.md index 6c009a368eb66..bb2d4d77f328a 100644 --- a/src/Symfony/Component/Security/Acl/README.md +++ b/src/Symfony/Component/Security/Acl/README.md @@ -11,7 +11,7 @@ Resources Documentation: -http://symfony.com/doc/2.5/book/security.html +http://symfony.com/doc/2.6/book/security.html Tests ----- diff --git a/src/Symfony/Component/Security/Acl/composer.json b/src/Symfony/Component/Security/Acl/composer.json index 5f5787fcc69f7..fb25b5d783a36 100644 --- a/src/Symfony/Component/Security/Acl/composer.json +++ b/src/Symfony/Component/Security/Acl/composer.json @@ -36,7 +36,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Security/Core/README.md b/src/Symfony/Component/Security/Core/README.md index 4585a5d675332..66c323e65a75f 100644 --- a/src/Symfony/Component/Security/Core/README.md +++ b/src/Symfony/Component/Security/Core/README.md @@ -11,7 +11,7 @@ Resources Documentation: -http://symfony.com/doc/2.5/book/security.html +http://symfony.com/doc/2.6/book/security.html Tests ----- diff --git a/src/Symfony/Component/Security/Core/composer.json b/src/Symfony/Component/Security/Core/composer.json index 249d4c14f3192..54a76dc551c8b 100644 --- a/src/Symfony/Component/Security/Core/composer.json +++ b/src/Symfony/Component/Security/Core/composer.json @@ -40,7 +40,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Security/Csrf/README.md b/src/Symfony/Component/Security/Csrf/README.md index 95a1062091ed3..89ed66cb5a724 100644 --- a/src/Symfony/Component/Security/Csrf/README.md +++ b/src/Symfony/Component/Security/Csrf/README.md @@ -9,7 +9,7 @@ Resources Documentation: -http://symfony.com/doc/2.5/book/security.html +http://symfony.com/doc/2.6/book/security.html Tests ----- diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index 398a2d3c454f4..4daba5ca38b49 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -32,7 +32,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Security/Http/README.md b/src/Symfony/Component/Security/Http/README.md index c0760d4edae4a..e19af427b84ea 100644 --- a/src/Symfony/Component/Security/Http/README.md +++ b/src/Symfony/Component/Security/Http/README.md @@ -11,7 +11,7 @@ Resources Documentation: -http://symfony.com/doc/2.5/book/security.html +http://symfony.com/doc/2.6/book/security.html Tests ----- diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index c544ad173ab41..812952339211f 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -38,7 +38,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Security/README.md b/src/Symfony/Component/Security/README.md index 5866d129e9fc8..c799a5d843c5c 100644 --- a/src/Symfony/Component/Security/README.md +++ b/src/Symfony/Component/Security/README.md @@ -11,7 +11,7 @@ Resources Documentation: -http://symfony.com/doc/2.5/book/security.html +http://symfony.com/doc/2.6/book/security.html Tests ----- diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json index a8a99f553f48f..6d60781bfdb59 100644 --- a/src/Symfony/Component/Security/composer.json +++ b/src/Symfony/Component/Security/composer.json @@ -52,7 +52,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 9e0fe096b07e7..eab354fa91728 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Stopwatch/composer.json b/src/Symfony/Component/Stopwatch/composer.json index 75ed1f60fc050..b424c4ea1a0bc 100644 --- a/src/Symfony/Component/Stopwatch/composer.json +++ b/src/Symfony/Component/Stopwatch/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Templating/composer.json b/src/Symfony/Component/Templating/composer.json index d1dd60d15a2ed..c6b42ba5921dd 100644 --- a/src/Symfony/Component/Templating/composer.json +++ b/src/Symfony/Component/Templating/composer.json @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Translation/README.md b/src/Symfony/Component/Translation/README.md index 641db8733c3c3..a9e395c54d2ea 100644 --- a/src/Symfony/Component/Translation/README.md +++ b/src/Symfony/Component/Translation/README.md @@ -26,7 +26,7 @@ https://github.com/fabpot/Silex/blob/master/src/Silex/Provider/TranslationServic Documentation: -http://symfony.com/doc/2.5/book/translation.html +http://symfony.com/doc/2.6/book/translation.html You can run the unit tests with the following command: diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json index 37fef30451385..37248c45400ee 100644 --- a/src/Symfony/Component/Translation/composer.json +++ b/src/Symfony/Component/Translation/composer.json @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Validator/README.md b/src/Symfony/Component/Validator/README.md index 6825a2feee198..83eaa1e3ba2cd 100644 --- a/src/Symfony/Component/Validator/README.md +++ b/src/Symfony/Component/Validator/README.md @@ -107,7 +107,7 @@ https://github.com/fabpot/Silex/blob/master/src/Silex/Provider/ValidatorServiceP Documentation: -http://symfony.com/doc/2.5/book/validation.html +http://symfony.com/doc/2.6/book/validation.html JSR-303 Specification: diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index 012697b19b3c2..cfc6fbfffec64 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -48,7 +48,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } diff --git a/src/Symfony/Component/Yaml/composer.json b/src/Symfony/Component/Yaml/composer.json index ceabb0c132ec8..2b2d961b7b357 100644 --- a/src/Symfony/Component/Yaml/composer.json +++ b/src/Symfony/Component/Yaml/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } } } From 76ccb280fbb966d3d1c612a8dff600cc2228bc58 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Thu, 15 May 2014 19:19:48 +0200 Subject: [PATCH 010/373] [HttpFoundation] implement session locking for PDO --- .../stubs/SessionHandlerInterface.php | 12 + .../Storage/Handler/PdoSessionHandler.php | 306 ++++++++++++++---- .../Storage/Handler/PdoSessionHandlerTest.php | 124 ++++--- 3 files changed, 327 insertions(+), 115 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php b/src/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php index 24280e38fca4a..9557135bcfbd1 100644 --- a/src/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php +++ b/src/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php @@ -12,6 +12,14 @@ /** * SessionHandlerInterface for PHP < 5.4 * + * The order in which these methods are invoked by PHP are: + * 1. open [session_start] + * 2. read + * 3. gc [optional depending on probability settings: gc_probability / gc_divisor] + * 4. destroy [optional when session_regenerate_id(true) is used] + * 5. write [session_write_close] or destroy [session_destroy] + * 6. close + * * Extensive documentation can be found at php.net, see links: * * @see http://php.net/sessionhandlerinterface @@ -19,6 +27,7 @@ * @see http://php.net/session-set-save-handler * * @author Drak + * @author Tobias Schultze */ interface SessionHandlerInterface { @@ -57,6 +66,9 @@ public function read($sessionId); /** * Writes the session data to the storage. * + * Care, the session ID passed to write() can be different from the one previously + * received in read() when the session ID changed due to session_regenerate_id(). + * * @see http://php.net/sessionhandlerinterface.write * * @param string $sessionId Session ID , see http://php.net/function.session-id diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index b831383a24d0b..4cdf3a89856e8 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -12,7 +12,19 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; /** - * PdoSessionHandler. + * Session handler using a PDO connection to read and write data. + * + * It works with MySQL, PostgreSQL, Oracle, SQL Server and SQLite and implements + * locking of sessions to prevent loss of data by concurrent access to the same session. + * This means requests for the same session will wait until the other one finished. + * PHPs internal files session handler also works this way. + * + * Attention: Since SQLite does not support row level locks but locks the whole database, + * it means only one session can be accessed at a time. Even different sessions would wait + * for another to finish. So saving session in SQLite should only be considered for + * development or prototypes. + * + * @see http://php.net/sessionhandlerinterface * * @author Fabien Potencier * @author Michael Williams @@ -25,6 +37,11 @@ class PdoSessionHandler implements \SessionHandlerInterface */ private $pdo; + /** + * @var string Database driver + */ + private $driver; + /** * @var string Table name */ @@ -45,39 +62,50 @@ class PdoSessionHandler implements \SessionHandlerInterface */ private $timeCol; + /** + * @var bool Whether a transaction is active + */ + private $inTransaction = false; + + /** + * @var bool Whether gc() has been called + */ + private $gcCalled = false; + /** * Constructor. * * List of available options: - * * db_table: The name of the table [required] + * * db_table: The name of the table [default: sessions] * * db_id_col: The column where to store the session id [default: sess_id] * * db_data_col: The column where to store the session data [default: sess_data] * * db_time_col: The column where to store the timestamp [default: sess_time] * - * @param \PDO $pdo A \PDO instance - * @param array $dbOptions An associative array of DB options + * @param \PDO $pdo A \PDO instance + * @param array $options An associative array of DB options * - * @throws \InvalidArgumentException When "db_table" option is not provided + * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION */ - public function __construct(\PDO $pdo, array $dbOptions = array()) + public function __construct(\PDO $pdo, array $options = array()) { - if (!array_key_exists('db_table', $dbOptions)) { - throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.'); - } if (\PDO::ERRMODE_EXCEPTION !== $pdo->getAttribute(\PDO::ATTR_ERRMODE)) { throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__)); } + $this->pdo = $pdo; - $dbOptions = array_merge(array( + $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); + + $options = array_replace(array( + 'db_table' => 'sessions', 'db_id_col' => 'sess_id', 'db_data_col' => 'sess_data', 'db_time_col' => 'sess_time', - ), $dbOptions); + ), $options); - $this->table = $dbOptions['db_table']; - $this->idCol = $dbOptions['db_id_col']; - $this->dataCol = $dbOptions['db_data_col']; - $this->timeCol = $dbOptions['db_time_col']; + $this->table = $options['db_table']; + $this->idCol = $options['db_id_col']; + $this->dataCol = $options['db_data_col']; + $this->timeCol = $options['db_time_col']; } /** @@ -85,34 +113,45 @@ public function __construct(\PDO $pdo, array $dbOptions = array()) */ public function open($savePath, $sessionName) { - return true; - } + $this->gcCalled = false; - /** - * {@inheritdoc} - */ - public function close() - { return true; } /** * {@inheritdoc} */ - public function destroy($sessionId) + public function read($sessionId) { - // delete the record associated with this id - $sql = "DELETE FROM $this->table WHERE $this->idCol = :id"; + $this->beginTransaction(); try { + $this->lockSession($sessionId); + + // We need to make sure we do not return session data that is already considered garbage according + // to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes. + $maxlifetime = (int) ini_get('session.gc_maxlifetime'); + + $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id AND $this->timeCol > :time"; + $stmt = $this->pdo->prepare($sql); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT); $stmt->execute(); + + // We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed + $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM); + + if ($sessionRows) { + return base64_decode($sessionRows[0][0]); + } + + return ''; } catch (\PDOException $e) { - throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete a session: %s', $e->getMessage()), 0, $e); - } + $this->rollback(); - return true; + throw $e; + } } /** @@ -120,16 +159,9 @@ public function destroy($sessionId) */ public function gc($maxlifetime) { - // delete the session records that have expired - $sql = "DELETE FROM $this->table WHERE $this->timeCol < :time"; - - try { - $stmt = $this->pdo->prepare($sql); - $stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT); - $stmt->execute(); - } catch (\PDOException $e) { - throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete expired sessions: %s', $e->getMessage()), 0, $e); - } + // We delay gc() to close() so that it is executed outside the transactional and blocking read-write process. + // This way, pruning expired sessions does not block them from being started while the current session is used. + $this->gcCalled = true; return true; } @@ -137,26 +169,22 @@ public function gc($maxlifetime) /** * {@inheritdoc} */ - public function read($sessionId) + public function destroy($sessionId) { - $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id"; + // delete the record associated with this id + $sql = "DELETE FROM $this->table WHERE $this->idCol = :id"; try { $stmt = $this->pdo->prepare($sql); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $stmt->execute(); - - // We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed - $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM); - - if ($sessionRows) { - return base64_decode($sessionRows[0][0]); - } - - return ''; } catch (\PDOException $e) { - throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e); + $this->rollback(); + + throw $e; } + + return true; } /** @@ -167,8 +195,10 @@ public function write($sessionId, $data) // Session data can contain non binary safe characters so we need to encode it. $encoded = base64_encode($data); + // The session ID can be different from the one previously received in read() + // when the session ID changed due to session_regenerate_id(). So we have to + // do an insert or update even if we created a row in read() for locking. // We use a MERGE SQL query when supported by the database. - // Otherwise we have to use a transactional DELETE followed by INSERT to prevent duplicate entries under high concurrency. try { $mergeSql = $this->getMergeSql(); @@ -183,15 +213,18 @@ public function write($sessionId, $data) return true; } - $this->pdo->beginTransaction(); - - try { - $deleteStmt = $this->pdo->prepare( - "DELETE FROM $this->table WHERE $this->idCol = :id" - ); - $deleteStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $deleteStmt->execute(); - + $updateStmt = $this->pdo->prepare( + "UPDATE $this->table SET $this->dataCol = :data, $this->timeCol = :time WHERE $this->idCol = :id" + ); + $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $updateStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); + $updateStmt->execute(); + + // Since we have a lock on the session, this is safe to do. Otherwise it would be prone to + // race conditions in high concurrency. And if it's a regenerated session ID it should be + // unique anyway. + if (!$updateStmt->rowCount()) { $insertStmt = $this->pdo->prepare( "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)" ); @@ -199,18 +232,153 @@ public function write($sessionId, $data) $insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT); $insertStmt->execute(); + } + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->commit(); + + if ($this->gcCalled) { + $maxlifetime = (int) ini_get('session.gc_maxlifetime'); + + // delete the session records that have expired + $sql = "DELETE FROM $this->table WHERE $this->timeCol <= :time"; - $this->pdo->commit(); + $stmt = $this->pdo->prepare($sql); + $stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT); + $stmt->execute(); + } + + return true; + } + + /** + * Helper method to begin a transaction. + * + * Since SQLite does not support row level locks, we have to acquire a reserved lock + * on the database immediately. Because of https://bugs.php.net/42766 we have to create + * such a transaction manually which also means we cannot use PDO::commit or + * PDO::rollback or PDO::inTransaction for SQLite. + */ + private function beginTransaction() + { + if ($this->inTransaction) { + $this->rollback(); + + throw new \BadMethodCallException( + 'Session handler methods have been invoked in wrong sequence. ' . + 'Expected sequence: open() -> read() -> destroy() / write() -> close()'); + } + + if ('sqlite' === $this->driver) { + $this->pdo->exec('BEGIN IMMEDIATE TRANSACTION'); + } else { + $this->pdo->beginTransaction(); + } + $this->inTransaction = true; + } + + /** + * Helper method to commit a transaction. + */ + private function commit() + { + if ($this->inTransaction) { + try { + // commit read-write transaction which also releases the lock + if ('sqlite' === $this->driver) { + $this->pdo->exec('COMMIT'); + } else { + $this->pdo->commit(); + } + $this->inTransaction = false; } catch (\PDOException $e) { - $this->pdo->rollback(); + $this->rollback(); throw $e; } - } catch (\PDOException $e) { - throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e); } + } - return true; + /** + * Helper method to rollback a transaction. + */ + private function rollback() + { + // We only need to rollback if we are in a transaction. Otherwise the resulting + // error would hide the real problem why rollback was called. We might not be + // in a transaction when two callbacks (e.g. destroy and write) are invoked that + // both fail. + if ($this->inTransaction) { + if ('sqlite' === $this->driver) { + $this->pdo->exec('ROLLBACK'); + } else { + $this->pdo->rollback(); + } + $this->inTransaction = false; + } + } + + /** + * Exclusively locks the row so other concurrent requests on the same session will block. + * + * This prevents loss of data by keeping the data consistent between read() and write(). + * We do not use SELECT FOR UPDATE because it does not lock non-existent rows. And a following + * INSERT when not found can result in a deadlock for one connection. + * + * @param string $sessionId Session ID + */ + private function lockSession($sessionId) + { + switch ($this->driver) { + case 'mysql': + // will also lock the row when actually nothing got updated (id = id) + $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) " . + "ON DUPLICATE KEY UPDATE $this->idCol = $this->idCol"; + break; + case 'oci': + // DUAL is Oracle specific dummy table + $sql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) " . + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) " . + "WHEN MATCHED THEN UPDATE SET $this->idCol = $this->idCol"; + break; + case 'sqlsrv': + // MS SQL Server requires MERGE be terminated by semicolon + $sql = "MERGE INTO $this->table USING (SELECT 'x' AS dummy) AS src ON ($this->idCol = :id) " . + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) " . + "WHEN MATCHED THEN UPDATE SET $this->idCol = $this->idCol;"; + break; + case 'pgsql': + // obtain an exclusive transaction level advisory lock + $sql = 'SELECT pg_advisory_xact_lock(:lock_id)'; + $stmt = $this->pdo->prepare($sql); + $stmt->bindValue(':lock_id', hexdec(substr($sessionId, 0, 15)), \PDO::PARAM_INT); + $stmt->execute(); + + return; + default: + return; + } + + // We create a DML lock for the session by inserting empty data or updating the row. + // This is safer than an application level advisory lock because it also prevents concurrent modification + // of the session from other parts of the application. + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $stmt->bindValue(':data', '', \PDO::PARAM_STR); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); } /** @@ -220,9 +388,7 @@ public function write($sessionId, $data) */ private function getMergeSql() { - $driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); - - switch ($driver) { + switch ($this->driver) { case 'mysql': return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) " . "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)"; @@ -230,12 +396,12 @@ private function getMergeSql() // DUAL is Oracle specific dummy table return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) " . "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) " . - "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data"; + "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time"; case 'sqlsrv': // MS SQL Server requires MERGE be terminated by semicolon return "MERGE INTO $this->table USING (SELECT 'x' AS dummy) AS src ON ($this->idCol = :id) " . "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) " . - "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data;"; + "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;"; case 'sqlite': return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)"; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php index e465f398984da..109addbcbd93a 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -29,74 +29,108 @@ protected function setUp() $this->pdo->exec($sql); } - public function testIncompleteOptions() - { - $this->setExpectedException('InvalidArgumentException'); - $storage = new PdoSessionHandler($this->pdo, array()); - } - + /** + * @expectedException \InvalidArgumentException + */ public function testWrongPdoErrMode() { - $pdo = new \PDO("sqlite::memory:"); - $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); - $pdo->exec("CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data TEXT, sess_time INTEGER)"); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); - $this->setExpectedException('InvalidArgumentException'); - $storage = new PdoSessionHandler($pdo, array('db_table' => 'sessions')); + $storage = new PdoSessionHandler($this->pdo); } - public function testWrongTableOptionsWrite() + /** + * @expectedException \RuntimeException + */ + public function testInexistentTable() { - $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'bad_name')); - $this->setExpectedException('RuntimeException'); - $storage->write('foo', 'bar'); + $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'inexistent_table')); + $storage->open('', 'sid'); + $storage->read('id'); + $storage->write('id', 'data'); + $storage->close(); } - public function testWrongTableOptionsRead() + public function testReadWriteRead() { - $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'bad_name')); - $this->setExpectedException('RuntimeException'); - $storage->read('foo', 'bar'); + $storage = new PdoSessionHandler($this->pdo); + $storage->open('', 'sid'); + $this->assertSame('', $storage->read('id'), 'New session returns empty string data'); + $storage->write('id', 'data'); + $storage->close(); + + $storage->open('', 'sid'); + $this->assertSame('data', $storage->read('id'), 'Written value can be read back correctly'); + $storage->close(); } - public function testWriteRead() + /** + * Simulates session_regenerate_id(true) which will require an INSERT or UPDATE (replace) + */ + public function testWriteDifferentSessionIdThanRead() { - $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions')); - $storage->write('foo', 'bar'); - $this->assertEquals('bar', $storage->read('foo'), 'written value can be read back correctly'); + $storage = new PdoSessionHandler($this->pdo); + $storage->open('', 'sid'); + $storage->read('id'); + $storage->destroy('id'); + $storage->write('new_id', 'data_of_new_session_id'); + $storage->close(); + + $storage->open('', 'sid'); + $this->assertSame('data_of_new_session_id', $storage->read('new_id'), 'Data of regenerated session id is available'); + $storage->close(); } - public function testMultipleInstances() + /** + * @expectedException \BadMethodCallException + */ + public function testWrongUsage() { - $storage1 = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions')); - $storage1->write('foo', 'bar'); - - $storage2 = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions')); - $this->assertEquals('bar', $storage2->read('foo'), 'values persist between instances'); + $storage = new PdoSessionHandler($this->pdo); + $storage->open('', 'sid'); + $storage->read('id'); + $storage->read('id'); } public function testSessionDestroy() { - $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions')); - $storage->write('foo', 'bar'); - $this->assertCount(1, $this->pdo->query('SELECT * FROM sessions')->fetchAll()); - - $storage->destroy('foo'); - - $this->assertCount(0, $this->pdo->query('SELECT * FROM sessions')->fetchAll()); + $storage = new PdoSessionHandler($this->pdo); + + $storage->open('', 'sid'); + $storage->read('id'); + $storage->write('id', 'data'); + $storage->close(); + $this->assertEquals(1, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); + + $storage->open('', 'sid'); + $storage->read('id'); + $storage->destroy('id'); + $storage->close(); + $this->assertEquals(0, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); + + $storage->open('', 'sid'); + $this->assertSame('', $storage->read('id'), 'Destroyed session returns empty string'); + $storage->close(); } public function testSessionGC() { - $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions')); - - $storage->write('foo', 'bar'); - $storage->write('baz', 'bar'); - - $this->assertCount(2, $this->pdo->query('SELECT * FROM sessions')->fetchAll()); - - $storage->gc(-1); - $this->assertCount(0, $this->pdo->query('SELECT * FROM sessions')->fetchAll()); + $previousLifeTime = ini_set('session.gc_maxlifetime', 0); + $storage = new PdoSessionHandler($this->pdo); + + $storage->open('', 'sid'); + $storage->read('id'); + $storage->write('id', 'data'); + $storage->close(); + $this->assertEquals(1, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); + + $storage->open('', 'sid'); + $this->assertSame('', $storage->read('id'), 'Session already considered garbage, so not returning data even if it is not pruned yet'); + $storage->gc(0); + $storage->close(); + $this->assertEquals(0, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); + + ini_set('session.gc_maxlifetime', $previousLifeTime); } public function testGetConnection() From ce6644280e784ac346d4de33caadcc30914316eb Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 26 May 2014 12:16:49 +0200 Subject: [PATCH 011/373] [Debug] enhanced error messages for uncaught exceptions --- .../Bridge/Twig/Extension/CodeExtension.php | 12 ++-- .../Resources/views/Profiler/admin.html.twig | 4 +- src/Symfony/Component/Debug/CHANGELOG.md | 5 ++ src/Symfony/Component/Debug/ErrorHandler.php | 9 ++- .../Component/Debug/ExceptionHandler.php | 39 +++++++----- .../ClassNotFoundFatalErrorHandler.php | 30 ++++----- .../UndefinedFunctionFatalErrorHandler.php | 25 +++----- .../UndefinedMethodFatalErrorHandler.php | 10 ++- .../Debug/Tests/ErrorHandlerTest.php | 61 ++----------------- .../Tests/Exception/FlattenExceptionTest.php | 4 +- .../ClassNotFoundFatalErrorHandlerTest.php | 10 +-- ...UndefinedFunctionFatalErrorHandlerTest.php | 8 +-- .../UndefinedMethodFatalErrorHandlerTest.php | 6 +- 13 files changed, 90 insertions(+), 133 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php index 763f315f8ab5c..93d99f936c740 100644 --- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -36,7 +36,7 @@ class CodeExtension extends \Twig_Extension public function __construct($fileLinkFormat, $rootDir, $charset) { $this->fileLinkFormat = empty($fileLinkFormat) ? ini_get('xdebug.file_link_format') : $fileLinkFormat; - $this->rootDir = str_replace('\\', '/', $rootDir).'/'; + $this->rootDir = str_replace('\\', '/', dirname($rootDir)).'/'; $this->charset = $charset; } @@ -164,12 +164,14 @@ public function fileExcerpt($file, $line) */ public function formatFile($file, $line, $text = null) { + $file = trim($file); + if (null === $text) { - $file = trim($file); - $text = $file; + $text = str_replace('\\', '/', $file); if (0 === strpos($text, $this->rootDir)) { - $text = str_replace($this->rootDir, '', str_replace('\\', '/', $text)); - $text = sprintf('kernel.root_dir/%s', $this->rootDir, $text); + $text = substr($text, strlen($this->rootDir)); + $text = explode('/', $text, 2); + $text = sprintf('%s%s', $this->rootDir, $text[0], isset($text[1]) ? '/'.$text[1] : ''); } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/admin.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/admin.html.twig index 3ca28ff1629c0..54140171726dc 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/admin.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/admin.html.twig @@ -13,8 +13,8 @@ » Export {% endif %} - » 
-
+ » 
+
{%- endblock button_widget %} @@ -203,7 +210,14 @@ {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %} {%- endif %} {% if label is empty -%} - {% set label = name|humanize %} + {%- if label_format is not empty -%} + {% set label = label_format|replace({ + '%name%': name, + '%id%': id, + }) %} + {%- else -%} + {% set label = name|humanize %} + {%- endif -%} {%- endif -%} {{ label|trans({}, translation_domain) }} {%- endif %} diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php index 64d44666888ba..9dac32fc994c0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php @@ -1,2 +1,4 @@ -humanize($name); } ?> + $name, '%id%' => $id)) + : $view['form']->humanize($name); } ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php index 4ed04cc392f1e..27bba27c2b5cb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php @@ -1,6 +1,8 @@ -humanize($name); } ?> + $name, '%id%' => $id)) + : $view['form']->humanize($name); } ?> diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 4381726126d48..8ca730cd37e04 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * added "html5" option to Date, Time and DateTimeFormType to be able to enable/disable HTML5 input date when widget option is "single_text" + * added "label_format" option with possible placeholders "%name%" and "%id%" 2.5.0 ------ diff --git a/src/Symfony/Component/Form/Extension/Core/Type/BaseType.php b/src/Symfony/Component/Form/Extension/Core/Type/BaseType.php index a6f8c430e1373..c227edd6f289a 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/BaseType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/BaseType.php @@ -44,6 +44,7 @@ public function buildView(FormView $view, FormInterface $form, array $options) $name = $form->getName(); $blockName = $options['block_name'] ?: $form->getName(); $translationDomain = $options['translation_domain']; + $labelFormat = $options['label_format']; if ($view->parent) { if ('' !== ($parentFullName = $view->parent->vars['full_name'])) { @@ -59,6 +60,10 @@ public function buildView(FormView $view, FormInterface $form, array $options) if (!$translationDomain) { $translationDomain = $view->parent->vars['translation_domain']; } + + if (!$labelFormat) { + $labelFormat = $view->parent->vars['label_format']; + } } else { $id = $name; $fullName = $name; @@ -87,6 +92,7 @@ public function buildView(FormView $view, FormInterface $form, array $options) 'full_name' => $fullName, 'disabled' => $form->isDisabled(), 'label' => $options['label'], + 'label_format' => $labelFormat, 'multipart' => false, 'attr' => $options['attr'], 'block_prefixes' => $blockPrefixes, @@ -111,6 +117,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) 'block_name' => null, 'disabled' => false, 'label' => null, + 'label_format' => null, 'attr' => array(), 'translation_domain' => null, 'auto_initialize' => true, diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 4755e208e9a51..93409636fe633 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -283,6 +283,108 @@ public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly ); } + public function testLabelFormatName() + { + $form = $this->factory->createNamedBuilder('myform') + ->add('myfield', 'text') + ->getForm(); + $view = $form->get('myfield')->createView(); + $html = $this->renderLabel($view, null, array('label_format' => 'form.%name%')); + + $this->assertMatchesXpath($html, +'/label + [@for="myform_myfield"] + [.="[trans]form.myfield[/trans]"] +' + ); + } + + public function testLabelFormatId() + { + $form = $this->factory->createNamedBuilder('myform') + ->add('myfield', 'text') + ->getForm(); + $view = $form->get('myfield')->createView(); + $html = $this->renderLabel($view, null, array('label_format' => 'form.%id%')); + + $this->assertMatchesXpath($html, +'/label + [@for="myform_myfield"] + [.="[trans]form.myform_myfield[/trans]"] +' + ); + } + + public function testLabelFormatAsFormOption() + { + $options = array('label_format' => 'form.%name%'); + + $form = $this->factory->createNamedBuilder('myform', 'form', null, $options) + ->add('myfield', 'text') + ->getForm(); + $view = $form->get('myfield')->createView(); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@for="myform_myfield"] + [.="[trans]form.myfield[/trans]"] +' + ); + } + + public function testLabelFormatOverriddenOption() + { + $options = array('label_format' => 'form.%name%'); + + $form = $this->factory->createNamedBuilder('myform', 'form', null, $options) + ->add('myfield', 'text', array('label_format' => 'field.%name%')) + ->getForm(); + $view = $form->get('myfield')->createView(); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@for="myform_myfield"] + [.="[trans]field.myfield[/trans]"] +' + ); + } + + public function testLabelFormatOnButton() + { + $form = $this->factory->createNamedBuilder('myform') + ->add('mybutton', 'button') + ->getForm(); + $view = $form->get('mybutton')->createView(); + $html = $this->renderWidget($view, array('label_format' => 'form.%name%')); + + $this->assertMatchesXpath($html, +'/button + [@type="button"] + [@name="myform[mybutton]"] + [.="[trans]form.mybutton[/trans]"] +' + ); + } + + public function testLabelFormatOnButtonId() + { + $form = $this->factory->createNamedBuilder('myform') + ->add('mybutton', 'button') + ->getForm(); + $view = $form->get('mybutton')->createView(); + $html = $this->renderWidget($view, array('label_format' => 'form.%id%')); + + $this->assertMatchesXpath($html, +'/button + [@type="button"] + [@name="myform[mybutton]"] + [.="[trans]form.myform_mybutton[/trans]"] +' + ); + } + public function testErrors() { $form = $this->factory->createNamed('name', 'text'); From 10943d9c6da1e8daa4d62abe2d57d542b6ef5dc4 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 26 Sep 2014 09:07:47 +0200 Subject: [PATCH 225/373] [VarDumper] edge case fixes --- .../DependencyInjection/Configuration.php | 3 +- .../Resources/views/Profiler/dump.html.twig | 4 +-- .../Debug/Resources/ext/{ext => }/config.m4 | 0 .../Debug/Resources/ext/{ext => }/config.w32 | 0 .../ext/{ext => }/php_symfony_debug.h | 0 .../Resources/ext/{ext => }/symfony_debug.c | 0 .../Resources/ext/{ext => }/tests/001.phpt | 0 .../DataCollector/DumpDataCollector.php | 3 -- .../Component/VarDumper/Caster/CasterStub.php | 12 +++---- .../VarDumper/Cloner/AbstractCloner.php | 8 ++--- .../Component/VarDumper/Cloner/Data.php | 4 +-- .../Component/VarDumper/Cloner/ExtCloner.php | 34 +++++++++++-------- .../Component/VarDumper/Cloner/PhpCloner.php | 34 +++++++++++-------- .../VarDumper/Tests/CliDumperTest.php | 3 +- .../VarDumper/Tests/HtmlDumperTest.php | 3 +- 15 files changed, 58 insertions(+), 50 deletions(-) rename src/Symfony/Component/Debug/Resources/ext/{ext => }/config.m4 (100%) rename src/Symfony/Component/Debug/Resources/ext/{ext => }/config.w32 (100%) rename src/Symfony/Component/Debug/Resources/ext/{ext => }/php_symfony_debug.h (100%) rename src/Symfony/Component/Debug/Resources/ext/{ext => }/symfony_debug.c (100%) rename src/Symfony/Component/Debug/Resources/ext/{ext => }/tests/001.phpt (100%) diff --git a/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php index 176211ad70940..223bc5b2998c2 100644 --- a/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php @@ -41,7 +41,8 @@ public function getConfigTreeBuilder() ->min(-1) ->defaultValue(-1) ->end() - ->end(); + ->end() + ; return $treeBuilder; } diff --git a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig index 4c650b13d15b3..51796195b7620 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig +++ b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig @@ -18,7 +18,7 @@
in {% if dump.file %} - {% set link = dump.file|file_link(dump.line) }} %} + {% set link = dump.file|file_link(dump.line) %} {% if link %} {{ dump.name }} {% else %} @@ -76,7 +76,7 @@
  • in {% if dump.line %} - {% set link = dump.file|file_link(dump.line) }} %} + {% set link = dump.file|file_link(dump.line) %} {% if link %} {{ dump.name }} {% else %} diff --git a/src/Symfony/Component/Debug/Resources/ext/ext/config.m4 b/src/Symfony/Component/Debug/Resources/ext/config.m4 similarity index 100% rename from src/Symfony/Component/Debug/Resources/ext/ext/config.m4 rename to src/Symfony/Component/Debug/Resources/ext/config.m4 diff --git a/src/Symfony/Component/Debug/Resources/ext/ext/config.w32 b/src/Symfony/Component/Debug/Resources/ext/config.w32 similarity index 100% rename from src/Symfony/Component/Debug/Resources/ext/ext/config.w32 rename to src/Symfony/Component/Debug/Resources/ext/config.w32 diff --git a/src/Symfony/Component/Debug/Resources/ext/ext/php_symfony_debug.h b/src/Symfony/Component/Debug/Resources/ext/php_symfony_debug.h similarity index 100% rename from src/Symfony/Component/Debug/Resources/ext/ext/php_symfony_debug.h rename to src/Symfony/Component/Debug/Resources/ext/php_symfony_debug.h diff --git a/src/Symfony/Component/Debug/Resources/ext/ext/symfony_debug.c b/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c similarity index 100% rename from src/Symfony/Component/Debug/Resources/ext/ext/symfony_debug.c rename to src/Symfony/Component/Debug/Resources/ext/symfony_debug.c diff --git a/src/Symfony/Component/Debug/Resources/ext/ext/tests/001.phpt b/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt similarity index 100% rename from src/Symfony/Component/Debug/Resources/ext/ext/tests/001.phpt rename to src/Symfony/Component/Debug/Resources/ext/tests/001.phpt diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index b54a90237aa9e..ba19540585cb5 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -15,7 +15,6 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Stopwatch\Stopwatch; use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\JsonDumper; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Dumper\DataDumperInterface; @@ -158,8 +157,6 @@ public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1) { if ('html' === $format) { $dumper = new HtmlDumper(); - } elseif ('json' === $format) { - $dumper = new JsonDumper(); } else { throw new \InvalidArgumentException(sprintf('Invalid dump format: %s', $format)); } diff --git a/src/Symfony/Component/VarDumper/Caster/CasterStub.php b/src/Symfony/Component/VarDumper/Caster/CasterStub.php index aaf8ed1035af1..56fc2b112eee8 100644 --- a/src/Symfony/Component/VarDumper/Caster/CasterStub.php +++ b/src/Symfony/Component/VarDumper/Caster/CasterStub.php @@ -22,11 +22,13 @@ class CasterStub extends Stub { public function __construct($value, $class = '') { + $this->class = $class; + $this->value = $value; + switch (gettype($value)) { case 'object': $this->type = self::TYPE_OBJECT; $this->class = get_class($value); - $this->value = spl_object_hash($value); $this->cut = -1; break; @@ -40,7 +42,6 @@ public function __construct($value, $class = '') case 'unknown type': $this->type = self::TYPE_RESOURCE; $this->class = @get_resource_type($value); - $this->value = (int) $value; $this->cut = -1; break; @@ -49,13 +50,8 @@ public function __construct($value, $class = '') $this->type = self::TYPE_STRING; $this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY; $this->cut = self::STRING_BINARY === $this->class ? strlen($value) : (function_exists('iconv_strlen') ? iconv_strlen($value, 'UTF-8') : -1); - break; + $this->value = ''; } - // No break; - - default: - $this->class = $class; - $this->value = $value; break; } } diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index ef94af979e94a..f8983ddbb4a04 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -174,14 +174,14 @@ abstract protected function doClone($var); /** * Casts an object to an array representation. * - * @param object $obj The object itself. * @param Stub $stub The Stub for the casted object. * @param bool $isNested True if the object is nested in the dumped structure. * * @return array The object casted as array. */ - protected function castObject($obj, Stub $stub, $isNested) + protected function castObject(Stub $stub, $isNested) { + $obj = $stub->value; $class = $stub->class; if (isset($this->classInfo[$class])) { @@ -225,15 +225,15 @@ protected function castObject($obj, Stub $stub, $isNested) /** * Casts a resource to an array representation. * - * @param resource $res The resource. * @param Stub $stub The Stub for the casted resource. * @param bool $isNested True if the object is nested in the dumped structure. * * @return array The resource casted as array. */ - protected function castResource($res, Stub $stub, $isNested) + protected function castResource(Stub $stub, $isNested) { $a = array(); + $res = $stub->value; $type = $stub->class; if (!empty($this->casters[':'.$type])) { diff --git a/src/Symfony/Component/VarDumper/Cloner/Data.php b/src/Symfony/Component/VarDumper/Cloner/Data.php index 3394cf796fd5c..077e19b4875a9 100644 --- a/src/Symfony/Component/VarDumper/Cloner/Data.php +++ b/src/Symfony/Component/VarDumper/Cloner/Data.php @@ -200,8 +200,8 @@ private function dumpChildren($dumper, $parentCursor, &$refs, $children, $hashCu */ public static function utf8Encode($s) { - if (function_exists('iconv')) { - return iconv('CP1252', 'UTF-8', $s); + if (function_exists('mb_convert_encoding')) { + return mb_convert_encoding($s, 'UTF-8', 'CP1252'); } $s .= $s; diff --git a/src/Symfony/Component/VarDumper/Cloner/ExtCloner.php b/src/Symfony/Component/VarDumper/Cloner/ExtCloner.php index 9aefcc9f4aa3b..cf1a0d19ea1c5 100644 --- a/src/Symfony/Component/VarDumper/Cloner/ExtCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/ExtCloner.php @@ -95,16 +95,18 @@ protected function doClone($var) $stub = new Stub(); $stub->type = Stub::TYPE_OBJECT; $stub->class = $zval['object_class']; - $stub->value = $h; - $a = $this->castObject($v, $stub, 0 < $i); - if (Stub::TYPE_OBJECT !== $stub->type) { - break; + $stub->value = $v; + $a = $this->castObject($stub, 0 < $i); + if ($v !== $stub->value) { + if (Stub::TYPE_OBJECT !== $stub->type) { + break; + } + $h = spl_object_hash($stub->value); } - $h = $stub->value; - $stub->value = ''; + $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos) { $stub->cut = count($a); - $a = array(); + $a = null; } } if (empty($softRefs[$h])) { @@ -112,6 +114,7 @@ protected function doClone($var) } else { $stub = $softRefs[$h]; $stub->ref = ++$refs; + $a = null; } break; @@ -120,16 +123,18 @@ protected function doClone($var) $stub = new Stub(); $stub->type = Stub::TYPE_RESOURCE; $stub->class = $zval['resource_type']; - $stub->value = $h; - $a = $this->castResource($v, $stub, 0 < $i); - if (Stub::TYPE_RESOURCE !== $stub->type) { - break; + $stub->value = $v; + $a = $this->castResource($stub, 0 < $i); + if ($v !== $stub->value) { + if (Stub::TYPE_RESOURCE !== $stub->type) { + break; + } + $h = (int) $stub->value; } - $h = $stub->value; - $stub->value = ''; + $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos) { $stub->cut = count($a); - $a = array(); + $a = null; } } if (empty($softRefs[$h])) { @@ -137,6 +142,7 @@ protected function doClone($var) } else { $stub = $softRefs[$h]; $stub->ref = ++$refs; + $a = null; } break; } diff --git a/src/Symfony/Component/VarDumper/Cloner/PhpCloner.php b/src/Symfony/Component/VarDumper/Cloner/PhpCloner.php index 46fa6b0f0cbc3..3fdafff66746d 100644 --- a/src/Symfony/Component/VarDumper/Cloner/PhpCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/PhpCloner.php @@ -101,16 +101,18 @@ protected function doClone($var) $stub = new Stub(); $stub->type = Stub::TYPE_OBJECT; $stub->class = get_class($v); - $stub->value = $h; - $a = $this->castObject($v, $stub, 0 < $i); - if (Stub::TYPE_OBJECT !== $stub->type) { - break; + $stub->value = $v; + $a = $this->castObject($stub, 0 < $i); + if ($v !== $stub->value) { + if (Stub::TYPE_OBJECT !== $stub->type) { + break; + } + $h = spl_object_hash($stub->value); } - $h = $stub->value; - $stub->value = ''; + $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos) { $stub->cut = count($a); - $a = array(); + $a = null; } } if (empty($softRefs[$h])) { @@ -118,6 +120,7 @@ protected function doClone($var) } else { $stub = $softRefs[$h]; $stub->ref = ++$refs; + $a = null; } break; @@ -127,16 +130,18 @@ protected function doClone($var) $stub = new Stub(); $stub->type = Stub::TYPE_RESOURCE; $stub->class = get_resource_type($v); - $stub->value = $h; - $a = $this->castResource($v, $stub, 0 < $i); - if (Stub::TYPE_RESOURCE !== $stub->type) { - break; + $stub->value = $v; + $a = $this->castResource($stub, 0 < $i); + if ($v !== $stub->value) { + if (Stub::TYPE_RESOURCE !== $stub->type) { + break; + } + $h = (int) $stub->value; } - $h = $stub->value; - $stub->value = ''; + $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos) { $stub->cut = count($a); - $a = array(); + $a = null; } } if (empty($softRefs[$h])) { @@ -144,6 +149,7 @@ protected function doClone($var) } else { $stub = $softRefs[$h]; $stub->ref = ++$refs; + $a = null; } break; } diff --git a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php index 6a4e9c3f882fe..17d4ef84eb680 100644 --- a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php @@ -40,6 +40,7 @@ public function testGet() $out = ob_get_clean(); $closureLabel = PHP_VERSION_ID >= 50400 ? 'public method' : 'function'; $out = preg_replace('/[ \t]+$/m', '', $out); + $intMax = PHP_INT_MAX; $this->assertSame( << NAN 4 => INF 5 => -INF - 6 => 9223372036854775807 + 6 => {$intMax} "str" => "déjà" 7 => b"é" "[]" => [] diff --git a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php index 25b027d62e269..e93ff236c238c 100644 --- a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php @@ -43,6 +43,7 @@ public function testGet() $closureLabel = PHP_VERSION_ID >= 50400 ? 'public method' : 'function'; $out = preg_replace('/[ \t]+$/m', '', $out); $var['file'] = htmlspecialchars($var['file'], ENT_QUOTES, 'UTF-8'); + $intMax = PHP_INT_MAX; $this->assertSame( <<3 => NAN 4 => INF 5 => -INF - 6 => 9223372036854775807 + 6 => {$intMax} "str" => "déjà" 7 => b"é" "[]" => [] From 499eeb4ef762a62a53ebe5ed6d6bce6191d4e2f7 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Fri, 26 Sep 2014 12:44:24 +0200 Subject: [PATCH 226/373] [Validator] Made it possible to store the cause of a constraint violation --- src/Symfony/Component/Validator/CHANGELOG.md | 1 + .../Validator/ConstraintViolation.php | 25 ++++++++++++++++--- .../AbstractConstraintValidatorTest.php | 11 +++++++- .../Violation/ConstraintViolationBuilder.php | 18 ++++++++++++- .../ConstraintViolationBuilderInterface.php | 9 +++++++ .../LegacyConstraintViolationBuilder.php | 15 +++++++---- 6 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index 753b4426986c9..329eaf3eb0aed 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -14,6 +14,7 @@ CHANGELOG * deprecated `ClassMetadata::addMemberMetadata()` * [BC BREAK] added `Mapping\MetadataInterface::getConstraints()` * added generic "payload" option to all constraints for attaching domain-specific data + * [BC BREAK] added `ConstraintViolationBuilderInterface::setCause()` 2.5.0 ----- diff --git a/src/Symfony/Component/Validator/ConstraintViolation.php b/src/Symfony/Component/Validator/ConstraintViolation.php index 5cf57ed4f8dc5..8f4744f576fe6 100644 --- a/src/Symfony/Component/Validator/ConstraintViolation.php +++ b/src/Symfony/Component/Validator/ConstraintViolation.php @@ -63,6 +63,11 @@ class ConstraintViolation implements ConstraintViolationInterface */ private $code; + /** + * @var mixed + */ + private $cause; + /** * Creates a new constraint violation. * @@ -79,10 +84,11 @@ class ConstraintViolation implements ConstraintViolationInterface * @param int|null $plural The number for determining the plural * form when translating the message * @param mixed $code The error code of the violation - * @param Constraint|null $constraint The constraint that caused the - * violation + * @param Constraint|null $constraint The constraint whose validation + * caused the violation + * @param mixed $cause The cause of the violation */ - public function __construct($message, $messageTemplate, array $parameters, $root, $propertyPath, $invalidValue, $plural = null, $code = null, Constraint $constraint = null) + public function __construct($message, $messageTemplate, array $parameters, $root, $propertyPath, $invalidValue, $plural = null, $code = null, Constraint $constraint = null, $cause = null) { $this->message = $message; $this->messageTemplate = $messageTemplate; @@ -93,6 +99,7 @@ public function __construct($message, $messageTemplate, array $parameters, $root $this->invalidValue = $invalidValue; $this->constraint = $constraint; $this->code = $code; + $this->cause = $cause; } /** @@ -197,7 +204,7 @@ public function getInvalidValue() } /** - * Returns the constraint that caused the violation. + * Returns the constraint whose validation caused the violation. * * @return Constraint|null The constraint or null if it is not known */ @@ -206,6 +213,16 @@ public function getConstraint() return $this->constraint; } + /** + * Returns the cause of the violation. + * + * @return mixed + */ + public function getCause() + { + return $this->cause; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php index 533c6cd9527ad..762dac553be0b 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php @@ -428,6 +428,7 @@ class ConstraintViolationAssertion private $plural; private $code; private $constraint; + private $cause; public function __construct(LegacyExecutionContextInterface $context, $message, Constraint $constraint = null, array $assertions = array()) { @@ -486,6 +487,13 @@ public function setCode($code) return $this; } + public function setCause($cause) + { + $this->cause = $cause; + + return $this; + } + public function buildNextViolation($message) { $assertions = $this->assertions; @@ -525,7 +533,8 @@ private function getViolation() $this->invalidValue, $this->plural, $this->code, - $this->constraint + $this->constraint, + $this->cause ); } } diff --git a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php index 6d6bd02774468..b752c397b8805 100644 --- a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php +++ b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php @@ -83,6 +83,11 @@ class ConstraintViolationBuilder implements ConstraintViolationBuilderInterface */ private $code; + /** + * @var mixed + */ + private $cause; + public function __construct(ConstraintViolationList $violations, Constraint $constraint, $message, array $parameters, $root, $propertyPath, $invalidValue, TranslatorInterface $translator, $translationDomain = null) { $this->violations = $violations; @@ -166,6 +171,16 @@ public function setCode($code) return $this; } + /** + * {@inheritdoc} + */ + public function setCause($cause) + { + $this->cause = $cause; + + return $this; + } + /** * {@inheritdoc} */ @@ -203,7 +218,8 @@ public function addViolation() $this->invalidValue, $this->plural, $this->code, - $this->constraint + $this->constraint, + $this->cause )); } } diff --git a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php index 84cd4d32548dd..9cb3fdb7e6efc 100644 --- a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php +++ b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php @@ -101,6 +101,15 @@ public function setPlural($number); */ public function setCode($code); + /** + * Sets the cause of the violation. + * + * @param mixed $cause The cause of the violation + * + * @return ConstraintViolationBuilderInterface This builder + */ + public function setCause($cause); + /** * Adds the violation to the current execution context. */ diff --git a/src/Symfony/Component/Validator/Violation/LegacyConstraintViolationBuilder.php b/src/Symfony/Component/Validator/Violation/LegacyConstraintViolationBuilder.php index 06cfdb60fbd5b..0607552cfc886 100644 --- a/src/Symfony/Component/Validator/Violation/LegacyConstraintViolationBuilder.php +++ b/src/Symfony/Component/Validator/Violation/LegacyConstraintViolationBuilder.php @@ -11,12 +11,7 @@ namespace Symfony\Component\Validator\Violation; -use Symfony\Component\Translation\TranslatorInterface; -use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\ConstraintViolation; -use Symfony\Component\Validator\ConstraintViolationList; use Symfony\Component\Validator\ExecutionContextInterface; -use Symfony\Component\Validator\Util\PropertyPath; /** * Backwards-compatible implementation of {@link ConstraintViolationBuilderInterface}. @@ -149,6 +144,16 @@ public function setCode($code) return $this; } + /** + * {@inheritdoc} + */ + public function setCause($cause) + { + // do nothing - we can't save the cause through the old API + + return $this; + } + /** * {@inheritdoc} */ From c6923afcd0cbddb3e544f38318572cf3abaff39c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 25 Sep 2014 19:46:28 +0200 Subject: [PATCH 227/373] [Debug] add some file link format handling --- .../FrameworkExtension.php | 45 ++++++++++--------- .../Resources/config/debug_prod.xml | 1 + .../Templating/Helper/CodeHelper.php | 2 +- .../Component/Debug/ExceptionHandler.php | 27 ++++++++--- .../EventListener/DebugHandlersListener.php | 5 ++- 5 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 04c2076dc6b07..e02f927e7a188 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -57,27 +57,6 @@ public function load(array $configs, ContainerBuilder $container) // Property access is used by both the Form and the Validator component $loader->load('property_access.xml'); - $loader->load('debug_prod.xml'); - - if ($container->getParameter('kernel.debug')) { - $loader->load('debug.xml'); - - $definition = $container->findDefinition('debug.debug_handlers_listener'); - $definition->replaceArgument(0, array(new Reference('http_kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'terminateWithException')); - - $definition = $container->findDefinition('http_kernel'); - $definition->replaceArgument(2, new Reference('debug.controller_resolver')); - - // replace the regular event_dispatcher service with the debug one - $definition = $container->findDefinition('event_dispatcher'); - $definition->setPublic(false); - $container->setDefinition('debug.event_dispatcher.parent', $definition); - $container->setAlias('event_dispatcher', 'debug.event_dispatcher'); - } else { - $definition = $container->findDefinition('debug.debug_handlers_listener'); - $definition->replaceArgument(2, E_COMPILE_ERROR | E_PARSE | E_ERROR | E_CORE_ERROR); - } - $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); @@ -144,6 +123,30 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('serializer.xml'); } + $loader->load('debug_prod.xml'); + $definition = $container->findDefinition('debug.debug_handlers_listener'); + + if ($container->hasParameter('templating.helper.code.file_link_format')) { + $definition->replaceArgument(4, '%templating.helper.code.file_link_format%'); + } + + if ($container->getParameter('kernel.debug')) { + $loader->load('debug.xml'); + + $definition->replaceArgument(0, array(new Reference('http_kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'terminateWithException')); + + $definition = $container->findDefinition('http_kernel'); + $definition->replaceArgument(2, new Reference('debug.controller_resolver')); + + // replace the regular event_dispatcher service with the debug one + $definition = $container->findDefinition('event_dispatcher'); + $definition->setPublic(false); + $container->setDefinition('debug.event_dispatcher.parent', $definition); + $container->setAlias('event_dispatcher', 'debug.event_dispatcher'); + } else { + $definition->replaceArgument(2, E_COMPILE_ERROR | E_PARSE | E_ERROR | E_CORE_ERROR); + } + $this->addClassesToCompile(array( 'Symfony\\Component\\Config\\FileLocator', diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml index 8f949f68d168e..983e9f4c821e0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml @@ -17,6 +17,7 @@ %kernel.debug% + null diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php index eb4a834fe9aa0..4ec793b104af2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php @@ -37,7 +37,7 @@ class CodeHelper extends Helper */ public function __construct($fileLinkFormat, $rootDir, $charset) { - $this->fileLinkFormat = empty($fileLinkFormat) ? ini_get('xdebug.file_link_format') : $fileLinkFormat; + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); $this->rootDir = str_replace('\\', '/', $rootDir).'/'; $this->charset = $charset; } diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php index 31a1da17515c6..a25b3392e20a1 100644 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ b/src/Symfony/Component/Debug/ExceptionHandler.php @@ -37,22 +37,24 @@ class ExceptionHandler private $handler; private $caughtBuffer; private $caughtLength; + private $fileLinkFormat; - public function __construct($debug = true) + public function __construct($debug = true, $fileLinkFormat = null) { $this->debug = $debug; + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); } /** * Registers the exception handler. * - * @param bool $debug + * @param bool $debug * * @return ExceptionHandler The registered exception handler */ - public static function register($debug = true) + public static function register($debug = true, $fileLinkFormat = null) { - $handler = new static($debug); + $handler = new static($debug, $fileLinkFormat = null); $prev = set_exception_handler(array($handler, 'handle')); if (is_array($prev) && $prev[0] instanceof ErrorHandler) { @@ -81,6 +83,21 @@ public function setHandler($handler) return $old; } + /** + * Sets the format for links to source files. + * + * @param string $format The format for links to source files + * + * @return string The previous file link format. + */ + public function setFileLinkFormat($format) + { + $old = $this->fileLinkFormat; + $this->fileLinkFormat = $format; + + return $old; + } + /** * Sends a response for the given Exception. * @@ -353,7 +370,7 @@ private function formatPath($path, $line) $path = self::utf8Htmlize($path); $file = preg_match('#[^/\\\\]*$#', $path, $file) ? $file[0] : $path; - if ($linkFormat = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format')) { + if ($linkFormat = $this->fileLinkFormat) { $link = str_replace(array('%f', '%l'), array($path, $line), $linkFormat); return sprintf(' in %s line %d', $link, $file, $line); diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php index cbb7ba0d68608..2c18bd143be06 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php @@ -34,13 +34,15 @@ class DebugHandlersListener implements EventSubscriberInterface * @param LoggerInterface|null $logger A PSR-3 logger * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants * @param bool $debug Enables/disables debug mode + * @param string $fileLinkFormat The format for links to source files */ - public function __construct($exceptionHandler, LoggerInterface $logger = null, $levels = null, $debug = true) + public function __construct($exceptionHandler, LoggerInterface $logger = null, $levels = null, $debug = true, $fileLinkFormat = null) { $this->exceptionHandler = $exceptionHandler; $this->logger = $logger; $this->levels = $levels; $this->debug = $debug; + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); } public function configure() @@ -76,6 +78,7 @@ public function configure() } if ($handler instanceof ExceptionHandler) { $handler->setHandler($this->exceptionHandler); + $handler->setFileLinkFormat($this->fileLinkFormat); } $this->exceptionHandler = null; } From 5f6b67608e5051934ac0d1edf6f9a0eca3b3baef Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 26 Sep 2014 10:06:41 +0200 Subject: [PATCH 228/373] [Debug] expose object_handle --- src/Symfony/Component/Debug/Resources/ext/README.rst | 3 ++- src/Symfony/Component/Debug/Resources/ext/symfony_debug.c | 3 ++- src/Symfony/Component/Debug/Resources/ext/tests/001.phpt | 8 +++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Debug/Resources/ext/README.rst b/src/Symfony/Component/Debug/Resources/ext/README.rst index 77be5648aaa2b..b0d1c58959fc9 100644 --- a/src/Symfony/Component/Debug/Resources/ext/README.rst +++ b/src/Symfony/Component/Debug/Resources/ext/README.rst @@ -33,12 +33,13 @@ Its behavior is about the same as: 'object_class' => get_class($array[$key]), 'object_refcount' => /* internal object refcount of $array[$key] */, 'object_hash' => spl_object_hash($array[$key]), + 'object_handle' => /* internal object handle $array[$key] */, ); break; case 'resource': $info += array( - 'resource_id' => (int) substr((string) $array[$key], 13), + 'resource_handle' => (int) $array[$key], 'resource_type' => get_resource_type($array[$key]), 'resource_refcount' => /* internal resource refcount of $array[$key] */, ); diff --git a/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c b/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c index 2bcb922d5c98b..8dc5d4356a819 100644 --- a/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c +++ b/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c @@ -72,10 +72,11 @@ PHP_FUNCTION(symfony_zval_info) add_assoc_stringl(return_value, "object_class", (char *)Z_OBJCE_P(arg)->name, Z_OBJCE_P(arg)->name_length, 1); add_assoc_long(return_value, "object_refcount", EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(arg)].bucket.obj.refcount); add_assoc_string(return_value, "object_hash", hash, 1); + add_assoc_long(return_value, "object_handle", Z_OBJ_HANDLE_P(arg)); } else if (Z_TYPE_P(arg) == IS_ARRAY) { add_assoc_long(return_value, "array_count", zend_hash_num_elements(Z_ARRVAL_P(arg))); } else if(Z_TYPE_P(arg) == IS_RESOURCE) { - add_assoc_long(return_value, "resource_id", Z_LVAL_P(arg)); + add_assoc_long(return_value, "resource_handle", Z_LVAL_P(arg)); add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg)), 1); add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg))); } else if (Z_TYPE_P(arg) == IS_STRING) { diff --git a/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt b/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt index a0015a939445c..30b25a25e2238 100644 --- a/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt +++ b/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt @@ -72,7 +72,7 @@ array(5) { ["strlen"]=> int(6) } -array(7) { +array(8) { ["type"]=> string(6) "object" ["zval_hash"]=> @@ -87,6 +87,8 @@ array(7) { int(1) ["object_hash"]=> string(32) "%s" + ["object_handle"]=> + int(1) } array(5) { ["type"]=> @@ -109,7 +111,7 @@ array(7) { int(2) ["zval_isref"]=> bool(false) - ["resource_id"]=> + ["resource_handle"]=> int(4) ["resource_type"]=> string(6) "stream" @@ -146,4 +148,4 @@ array(4) { ["zval_isref"]=> bool(true) } -NULL \ No newline at end of file +NULL From 2763227a47302aab862219898d74d1d5ca5d14c6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 27 Sep 2014 11:38:21 +0200 Subject: [PATCH 229/373] fixed tests --- .../Tests/Firewall/AnonymousAuthenticationListenerTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php index e6bab4ea255f7..0f43aa0cf51b2 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php @@ -35,7 +35,7 @@ public function testHandleWithContextHavingAToken() ->method('authenticate') ; - $listener = new AnonymousAuthenticationListener($context, 'TheKey', $authenticationManager); + $listener = new AnonymousAuthenticationListener($context, 'TheKey', null, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } @@ -67,7 +67,7 @@ public function testHandleWithContextHavingNoToken() ->with($anonymousToken) ; - $listener = new AnonymousAuthenticationListener($context, 'TheKey', $authenticationManager); + $listener = new AnonymousAuthenticationListener($context, 'TheKey', null, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } @@ -86,7 +86,7 @@ public function testHandledEventIsLogged() $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); - $listener = new AnonymousAuthenticationListener($context, 'TheKey', $authenticationManager, $logger); + $listener = new AnonymousAuthenticationListener($context, 'TheKey', $logger, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } } From 4e0021b53378e591edc5b592f8af661acca0dd74 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 27 Sep 2014 12:02:32 +0200 Subject: [PATCH 230/373] [Security] fixed fatal error --- .../Security/Http/Firewall/AnonymousAuthenticationListener.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php index 72f0071424389..e3c35a070eb22 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php @@ -51,8 +51,9 @@ public function handle(GetResponseEvent $event) } try { + $token = new AnonymousToken($this->key, 'anon.', array()); if (null !== $this->authenticationManager) { - $token = $this->authenticationManager->authenticate(new AnonymousToken($this->key, 'anon.', array())); + $token = $this->authenticationManager->authenticate($token); } $this->context->setToken($token); From 1c240067f47244071e65af332f4c2364211c7b10 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 28 Sep 2014 18:15:31 +0200 Subject: [PATCH 231/373] fixed deps --- src/Symfony/Bridge/Twig/composer.json | 2 +- src/Symfony/Component/EventDispatcher/composer.json | 3 ++- src/Symfony/Component/Security/composer.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index c732a6a70a114..8f3592730da30 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -21,7 +21,7 @@ "twig/twig": "~1.12" }, "require-dev": { - "symfony/form": "~2.5,<2.6.0", + "symfony/form": "~2.6", "symfony/http-kernel": "~2.2", "symfony/routing": "~2.2", "symfony/templating": "~2.1", diff --git a/src/Symfony/Component/EventDispatcher/composer.json b/src/Symfony/Component/EventDispatcher/composer.json index ef370300864af..80a0f93c7b14c 100644 --- a/src/Symfony/Component/EventDispatcher/composer.json +++ b/src/Symfony/Component/EventDispatcher/composer.json @@ -19,7 +19,8 @@ "php": ">=5.3.3" }, "require-dev": { - "symfony/dependency-injection": "~2.0,<2.6.0", + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", "symfony/config": "~2.0", "symfony/stopwatch": "~2.2", "psr/log": "~1.0" diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json index a977158369e8a..f72b7ceb102df 100644 --- a/src/Symfony/Component/Security/composer.json +++ b/src/Symfony/Component/Security/composer.json @@ -29,7 +29,7 @@ }, "require-dev": { "symfony/routing": "~2.2", - "symfony/validator": "~2.2,<2.5.0", + "symfony/validator": "~2.5", "doctrine/common": "~2.2", "doctrine/dbal": "~2.2", "psr/log": "~1.0", From ecedea22626612bf055607ed3692ddc87d3c057d Mon Sep 17 00:00:00 2001 From: Nikita Konstantinov Date: Sun, 28 Sep 2014 22:12:33 +0400 Subject: [PATCH 232/373] [DependencyInjection] Removed unreachable code --- .../Component/DependencyInjection/ContainerBuilder.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 46da7ecd6e895..e244bf1857826 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -470,10 +470,6 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV return $service; } - if (isset($this->loading[$id])) { - throw new LogicException(sprintf('The service "%s" has a circular reference to itself.', $id), 0, $e); - } - if (!$this->hasDefinition($id) && isset($this->aliasDefinitions[$id])) { return $this->get($this->aliasDefinitions[$id]); } From b23084abb9cb2b64cc4bcd5e6defa4b8def785fe Mon Sep 17 00:00:00 2001 From: Iltar van der Berg Date: Mon, 29 Sep 2014 09:09:34 +0200 Subject: [PATCH 233/373] [DX] Moved constants to a final class --- .../Form/UserLoginFormType.php | 10 +++---- ...ssionStorageInterface.php => Security.php} | 4 +-- .../Core/SecurityContextInterface.php | 5 +++- .../Authentication/AuthenticationUtils.php | 16 +++++----- .../DefaultAuthenticationFailureHandler.php | 6 ++-- .../AbstractAuthenticationListener.php | 5 ++-- .../Http/Firewall/ExceptionListener.php | 3 +- .../SimpleFormAuthenticationListener.php | 3 +- ...namePasswordFormAuthenticationListener.php | 3 +- .../Component/Security/Http/HttpUtils.php | 15 +++++----- ...efaultAuthenticationFailureHandlerTest.php | 8 ++--- .../Security/Http/Tests/HttpUtilsTest.php | 8 ++--- .../Core/SecurityContextInterfaceTest.php | 30 +++++++++++++++++++ 13 files changed, 76 insertions(+), 40 deletions(-) rename src/Symfony/Component/Security/Core/{SecuritySessionStorageInterface.php => Security.php} (86%) create mode 100644 src/Symfony/Component/Security/Tests/Core/SecurityContextInterfaceTest.php diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.php index 21389efd07d6e..6928e6868b09c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.php @@ -17,8 +17,8 @@ use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvent; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\Security\Core\Security; /** * Form type for use with the Security component's form-based authentication @@ -58,10 +58,10 @@ public function buildForm(FormBuilderInterface $builder, array $options) * session for an authentication error and last username. */ $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($request) { - if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { - $error = $request->attributes->get(SecurityContextInterface::AUTHENTICATION_ERROR); + if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { + $error = $request->attributes->get(Security::AUTHENTICATION_ERROR); } else { - $error = $request->getSession()->get(SecurityContextInterface::AUTHENTICATION_ERROR); + $error = $request->getSession()->get(Security::AUTHENTICATION_ERROR); } if ($error) { @@ -69,7 +69,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) } $event->setData(array_replace((array) $event->getData(), array( - 'username' => $request->getSession()->get(SecurityContextInterface::LAST_USERNAME), + 'username' => $request->getSession()->get(Security::LAST_USERNAME), ))); }); } diff --git a/src/Symfony/Component/Security/Core/SecuritySessionStorageInterface.php b/src/Symfony/Component/Security/Core/Security.php similarity index 86% rename from src/Symfony/Component/Security/Core/SecuritySessionStorageInterface.php rename to src/Symfony/Component/Security/Core/Security.php index 47c0bbe01a33d..d397fb40777a0 100644 --- a/src/Symfony/Component/Security/Core/SecuritySessionStorageInterface.php +++ b/src/Symfony/Component/Security/Core/Security.php @@ -12,11 +12,11 @@ namespace Symfony\Component\Security\Core; /** - * The SecuritySessionStorageInterface. + * This class holds security information. * * @author Johannes M. Schmitt */ -interface SecuritySessionStorageInterface +final class Security { const ACCESS_DENIED_ERROR = '_security.403_error'; const AUTHENTICATION_ERROR = '_security.last_error'; diff --git a/src/Symfony/Component/Security/Core/SecurityContextInterface.php b/src/Symfony/Component/Security/Core/SecurityContextInterface.php index 844482b57a100..4421622055c40 100644 --- a/src/Symfony/Component/Security/Core/SecurityContextInterface.php +++ b/src/Symfony/Component/Security/Core/SecurityContextInterface.php @@ -20,6 +20,9 @@ * @author Johannes M. Schmitt * @deprecated Deprecated since version 2.6, to be removed in 3.0. */ -interface SecurityContextInterface extends TokenStorageInterface, AuthorizationCheckerInterface, SecuritySessionStorageInterface +interface SecurityContextInterface extends TokenStorageInterface, AuthorizationCheckerInterface { + const ACCESS_DENIED_ERROR = Security::ACCESS_DENIED_ERROR; + const AUTHENTICATION_ERROR = Security::AUTHENTICATION_ERROR; + const LAST_USERNAME = Security::LAST_USERNAME; } diff --git a/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php b/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php index 03f5e44d4725e..38763dcf3515e 100644 --- a/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php +++ b/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php @@ -11,10 +11,10 @@ namespace Symfony\Component\Security\Http\Authentication; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\SecurityContextInterface; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Security; /** * Extracts Security Errors from Request @@ -46,13 +46,13 @@ public function getLastAuthenticationError($clearSession = true) $session = $request->getSession(); $authenticationException = null; - if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { - $authenticationException = $request->attributes->get(SecurityContextInterface::AUTHENTICATION_ERROR); - } elseif ($session !== null && $session->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { - $authenticationException = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR); + if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { + $authenticationException = $request->attributes->get(Security::AUTHENTICATION_ERROR); + } elseif ($session !== null && $session->has(Security::AUTHENTICATION_ERROR)) { + $authenticationException = $session->get(Security::AUTHENTICATION_ERROR); if ($clearSession) { - $session->remove(SecurityContextInterface::AUTHENTICATION_ERROR); + $session->remove(Security::AUTHENTICATION_ERROR); } } @@ -66,7 +66,7 @@ public function getLastUsername() { $session = $this->getRequest()->getSession(); - return null === $session ? '' : $session->get(SecurityContextInterface::LAST_USERNAME); + return null === $session ? '' : $session->get(Security::LAST_USERNAME); } /** diff --git a/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationFailureHandler.php b/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationFailureHandler.php index 658a9993e9ae7..93150c873366f 100644 --- a/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationFailureHandler.php +++ b/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationFailureHandler.php @@ -15,7 +15,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\HttpUtils; /** @@ -96,7 +96,7 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio } $subRequest = $this->httpUtils->createRequest($request, $this->options['failure_path']); - $subRequest->attributes->set(SecurityContextInterface::AUTHENTICATION_ERROR, $exception); + $subRequest->attributes->set(Security::AUTHENTICATION_ERROR, $exception); return $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST); } @@ -105,7 +105,7 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio $this->logger->debug(sprintf('Redirecting to %s', $this->options['failure_path'])); } - $request->getSession()->set(SecurityContextInterface::AUTHENTICATION_ERROR, $exception); + $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception); return $this->httpUtils->createRedirectResponse($request, $this->options['failure_path']); } diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php index cc1c4a10c201f..7cd0746a965ef 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php @@ -15,6 +15,7 @@ use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; @@ -218,8 +219,8 @@ private function onSuccess(Request $request, TokenInterface $token) $this->securityContext->setToken($token); $session = $request->getSession(); - $session->remove(SecurityContextInterface::AUTHENTICATION_ERROR); - $session->remove(SecurityContextInterface::LAST_USERNAME); + $session->remove(Security::AUTHENTICATION_ERROR); + $session->remove(Security::LAST_USERNAME); if (null !== $this->dispatcher) { $loginEvent = new InteractiveLoginEvent($request, $token); diff --git a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php index d0b167e38b4a0..e224ea3e43d9a 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; @@ -146,7 +147,7 @@ private function handleAccessDeniedException(GetResponseForExceptionEvent $event } } elseif (null !== $this->errorPage) { $subRequest = $this->httpUtils->createRequest($event->getRequest(), $this->errorPage); - $subRequest->attributes->set(SecurityContextInterface::ACCESS_DENIED_ERROR, $exception); + $subRequest->attributes->set(Security::ACCESS_DENIED_ERROR, $exception); $event->setResponse($event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true)); } diff --git a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php index 20ce4f265db71..103dc50ad0bb8 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php @@ -23,6 +23,7 @@ use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; @@ -114,7 +115,7 @@ protected function attemptAuthentication(Request $request) $password = $request->get($this->options['password_parameter'], null, true); } - $request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username); + $request->getSession()->set(Security::LAST_USERNAME, $username); $token = $this->simpleAuthenticator->createToken($request, $username, $password, $this->providerKey); diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index f24d2163f1a7f..5562539b008c4 100644 --- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -25,6 +25,7 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\InvalidArgumentException; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -93,7 +94,7 @@ protected function attemptAuthentication(Request $request) $password = $request->get($this->options['password_parameter'], null, true); } - $request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username); + $request->getSession()->set(Security::LAST_USERNAME, $username); return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey)); } diff --git a/src/Symfony/Component/Security/Http/HttpUtils.php b/src/Symfony/Component/Security/Http/HttpUtils.php index 451c12cba0e85..fbcfdb73ec441 100644 --- a/src/Symfony/Component/Security/Http/HttpUtils.php +++ b/src/Symfony/Component/Security/Http/HttpUtils.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Security\Http; -use Symfony\Component\Security\Core\SecurityContextInterface; - use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; @@ -20,6 +18,7 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Security\Core\Security; /** * Encapsulates the logic needed to create sub-requests, redirect the user, and match URLs. @@ -77,14 +76,14 @@ public function createRequest(Request $request, $path) $newRequest->setSession($request->getSession()); } - if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { - $newRequest->attributes->set(SecurityContextInterface::AUTHENTICATION_ERROR, $request->attributes->get(SecurityContextInterface::AUTHENTICATION_ERROR)); + if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { + $newRequest->attributes->set(Security::AUTHENTICATION_ERROR, $request->attributes->get(Security::AUTHENTICATION_ERROR)); } - if ($request->attributes->has(SecurityContextInterface::ACCESS_DENIED_ERROR)) { - $newRequest->attributes->set(SecurityContextInterface::ACCESS_DENIED_ERROR, $request->attributes->get(SecurityContextInterface::ACCESS_DENIED_ERROR)); + if ($request->attributes->has(Security::ACCESS_DENIED_ERROR)) { + $newRequest->attributes->set(Security::ACCESS_DENIED_ERROR, $request->attributes->get(Security::ACCESS_DENIED_ERROR)); } - if ($request->attributes->has(SecurityContextInterface::LAST_USERNAME)) { - $newRequest->attributes->set(SecurityContextInterface::LAST_USERNAME, $request->attributes->get(SecurityContextInterface::LAST_USERNAME)); + if ($request->attributes->has(Security::LAST_USERNAME)) { + $newRequest->attributes->set(Security::LAST_USERNAME, $request->attributes->get(Security::LAST_USERNAME)); } return $newRequest; diff --git a/src/Symfony/Component/Security/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php index 15adcdf357ec5..e06566052c674 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Security\Http\Tests\Authentication; use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler; -use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\Security\Core\Security; use Symfony\Component\HttpKernel\HttpKernelInterface; class DefaultAuthenticationFailureHandlerTest extends \PHPUnit_Framework_TestCase @@ -47,7 +47,7 @@ public function testForward() $subRequest = $this->getRequest(); $subRequest->attributes->expects($this->once()) - ->method('set')->with(SecurityContextInterface::AUTHENTICATION_ERROR, $this->exception); + ->method('set')->with(Security::AUTHENTICATION_ERROR, $this->exception); $this->httpUtils->expects($this->once()) ->method('createRequest')->with($this->request, '/login') ->will($this->returnValue($subRequest)); @@ -79,7 +79,7 @@ public function testRedirect() public function testExceptionIsPersistedInSession() { $this->session->expects($this->once()) - ->method('set')->with(SecurityContextInterface::AUTHENTICATION_ERROR, $this->exception); + ->method('set')->with(Security::AUTHENTICATION_ERROR, $this->exception); $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, array(), $this->logger); $handler->onAuthenticationFailure($this->request, $this->exception); @@ -91,7 +91,7 @@ public function testExceptionIsPassedInRequestOnForward() $subRequest = $this->getRequest(); $subRequest->attributes->expects($this->once()) - ->method('set')->with(SecurityContextInterface::AUTHENTICATION_ERROR, $this->exception); + ->method('set')->with(Security::AUTHENTICATION_ERROR, $this->exception); $this->httpUtils->expects($this->once()) ->method('createRequest')->with($this->request, '/login') diff --git a/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php b/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php index 5cac50484e986..195fc48cc6116 100644 --- a/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php +++ b/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php @@ -14,7 +14,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; -use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\HttpUtils; class HttpUtilsTest extends \PHPUnit_Framework_TestCase @@ -126,9 +126,9 @@ public function testCreateRequestPassesSecurityContextAttributesToTheNewRequest( public function provideSecurityContextAttributes() { return array( - array(SecurityContextInterface::AUTHENTICATION_ERROR), - array(SecurityContextInterface::ACCESS_DENIED_ERROR), - array(SecurityContextInterface::LAST_USERNAME), + array(Security::AUTHENTICATION_ERROR), + array(Security::ACCESS_DENIED_ERROR), + array(Security::LAST_USERNAME), ); } diff --git a/src/Symfony/Component/Security/Tests/Core/SecurityContextInterfaceTest.php b/src/Symfony/Component/Security/Tests/Core/SecurityContextInterfaceTest.php new file mode 100644 index 0000000000000..f65d20288853c --- /dev/null +++ b/src/Symfony/Component/Security/Tests/Core/SecurityContextInterfaceTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Tests\Core; + +use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\Security\Core\Security; + +class SecurityContextInterfaceTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test if the BC Layer is working as intended + * + * @deprecated Deprecated since version 2.6, to be removed in 3.0. + */ + public function testConstantSync() + { + $this->assertSame(Security::ACCESS_DENIED_ERROR, SecurityContextInterface::ACCESS_DENIED_ERROR); + $this->assertSame(Security::AUTHENTICATION_ERROR, SecurityContextInterface::AUTHENTICATION_ERROR); + $this->assertSame(Security::LAST_USERNAME, SecurityContextInterface::LAST_USERNAME); + } +} From 80d52c73dc796e8ee9e913612665f62a435fcfb5 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 29 Sep 2014 15:03:27 +0200 Subject: [PATCH 234/373] remove invalid tests With #12046, the `getLocale()` method isn't overriden anymore by the `Translator` from the FrameworkBundle, but the locale is set by the new `TranslatorListener`. Therefore, the tests ensuring the old behavior are removed. --- .../Tests/Translation/TranslatorTest.php | 73 ------------------- 1 file changed, 73 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php index 3e8dc1a680c21..263d60ac0ad35 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -12,7 +12,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Translation; use Symfony\Bundle\FrameworkBundle\Translation\Translator; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Translation\MessageSelector; @@ -103,78 +102,6 @@ public function testTransWithCachingWithInvalidLocale() $translator->trans('foo'); } - /** - * @dataProvider getGetLocaleData - */ - public function testGetLocale($inRequestScope) - { - $requestStack = new RequestStack(); - if ($inRequestScope) { - $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); - $request - ->expects($this->any()) - ->method('getLocale') - ->will($this->returnValue('en')) - ; - - $requestStack->push($request); - } - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container - ->expects($this->once()) - ->method('get') - ->with('request_stack') - ->will($this->returnValue($requestStack)) - ; - - $translator = new Translator($container, new MessageSelector()); - - if ($inRequestScope) { - $this->assertSame('en', $translator->getLocale()); - } else { - $this->assertNull($translator->getLocale()); - } - } - - public function getGetLocaleData() - { - return array( - array(false), - array(true), - ); - } - - public function testGetLocaleWithInvalidLocale() - { - $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); - - $request - ->expects($this->any()) - ->method('getLocale') - ->will($this->returnValue('foo bar')) - ; - $request - ->expects($this->once()) - ->method('getDefaultLocale') - ->will($this->returnValue('en-US')) - ; - - $requestStack = new RequestStack(); - $requestStack->push($request); - - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container - ->expects($this->once()) - ->method('get') - ->with('request_stack') - ->will($this->returnValue($requestStack)) - ; - - $translator = new Translator($container, new MessageSelector()); - $this->assertSame('en-US', $translator->getLocale()); - } - protected function getCatalogue($locale, $messages) { $catalogue = new MessageCatalogue($locale); From 3454e59ca28f7190ad18c09c1fe5d716fc12ee79 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 29 Sep 2014 15:50:01 +0200 Subject: [PATCH 235/373] [DebugBundle] fix dev dependencies --- src/Symfony/Bundle/DebugBundle/composer.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Bundle/DebugBundle/composer.json b/src/Symfony/Bundle/DebugBundle/composer.json index 6412fdeaf5c44..12b62ff3a5340 100644 --- a/src/Symfony/Bundle/DebugBundle/composer.json +++ b/src/Symfony/Bundle/DebugBundle/composer.json @@ -21,6 +21,10 @@ "symfony/twig-bridge": "~2.6", "symfony/var-dumper": "~2.6" }, + "require-dev": { + "symfony/config": "~2.3", + "symfony/dependency-injection": "~2.3" + }, "suggest": { "symfony/config": "For service container configuration", "symfony/dependency-injection": "For using as a service from the container" From 7dad54ca085e3ca43b85270bacb8449cf9f4b9e1 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Sat, 17 May 2014 20:23:24 +0200 Subject: [PATCH 236/373] [HttpFoundation] remove base64 encoding of session data --- .../Storage/Handler/PdoSessionHandler.php | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index d6f3704e961b8..a0cc434c061ca 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -19,14 +19,15 @@ * This means requests for the same session will wait until the other one finished. * PHPs internal files session handler also works this way. * - * Session data is a binary string that can contain non-printable characters like the null byte. - * For this reason this handler base64 encodes the data to be able to save it in a character column. - * * Attention: Since SQLite does not support row level locks but locks the whole database, * it means only one session can be accessed at a time. Even different sessions would wait * for another to finish. So saving session in SQLite should only be considered for * development or prototypes. * + * Session data is a binary string that can contain non-printable characters like the null byte. + * For this reason it must be saved in a binary column in the database like BLOB in MySQL. + * Saving it in a character column could corrupt the data. + * * @see http://php.net/sessionhandlerinterface * * @author Fabien Potencier @@ -145,11 +146,7 @@ public function read($sessionId) // We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM); - if ($sessionRows) { - return base64_decode($sessionRows[0][0]); - } - - return ''; + return $sessionRows ? $sessionRows[0][0] : ''; } catch (\PDOException $e) { $this->rollback(); @@ -195,8 +192,6 @@ public function destroy($sessionId) */ public function write($sessionId, $data) { - $encoded = base64_encode($data); - // The session ID can be different from the one previously received in read() // when the session ID changed due to session_regenerate_id(). So we have to // do an insert or update even if we created a row in read() for locking. @@ -208,7 +203,7 @@ public function write($sessionId, $data) if (null !== $mergeSql) { $mergeStmt = $this->pdo->prepare($mergeSql); $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB); $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); $mergeStmt->execute(); @@ -219,7 +214,7 @@ public function write($sessionId, $data) "UPDATE $this->table SET $this->dataCol = :data, $this->timeCol = :time WHERE $this->idCol = :id" ); $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $updateStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $updateStmt->bindParam(':data', $data, \PDO::PARAM_LOB); $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); $updateStmt->execute(); @@ -236,7 +231,7 @@ public function write($sessionId, $data) "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)" ); $insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $insertStmt->bindParam(':data', $encoded, \PDO::PARAM_LOB); $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT); $insertStmt->execute(); } catch (\PDOException $e) { From 251238d9a631a974b26855848ec9b5266c7b4626 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Sun, 18 May 2014 03:41:33 +0200 Subject: [PATCH 237/373] [HttpFoundation] implement lazy connect for pdo session handler --- .../Storage/Handler/PdoSessionHandler.php | 72 ++++++++++++++---- .../Storage/Handler/PdoSessionHandlerTest.php | 75 +++++++++++++++++-- 2 files changed, 127 insertions(+), 20 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index a0cc434c061ca..9d5fa8f4850ab 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -37,10 +37,15 @@ class PdoSessionHandler implements \SessionHandlerInterface { /** - * @var \PDO PDO instance + * @var \PDO|null PDO instance or null when not connected yet */ private $pdo; + /** + * @var string|null|false DNS string or null for session.save_path or false when lazy connection disabled + */ + private $dns = false; + /** * @var string Database driver */ @@ -66,6 +71,21 @@ class PdoSessionHandler implements \SessionHandlerInterface */ private $timeCol; + /** + * @var string Username when lazy-connect + */ + private $username; + + /** + * @var string Password when lazy-connect + */ + private $password; + + /** + * @var array Connection options when lazy-connect + */ + private $connectionOptions = array(); + /** * @var bool Whether a transaction is active */ @@ -79,37 +99,54 @@ class PdoSessionHandler implements \SessionHandlerInterface /** * Constructor. * + * You can either pass an existing database connection as PDO instance or + * pass a DNS string that will be used to lazy-connect to the database + * when the session is actually used. Furthermore it's possible to pass null + * which will then use the session.save_path ini setting as PDO DNS parameter. + * * List of available options: * * db_table: The name of the table [default: sessions] * * db_id_col: The column where to store the session id [default: sess_id] * * db_data_col: The column where to store the session data [default: sess_data] * * db_time_col: The column where to store the timestamp [default: sess_time] + * * db_username: The username when lazy-connect [default: ''] + * * db_password: The password when lazy-connect [default: ''] + * * db_connection_options: An array of driver-specific connection options [default: array()] * - * @param \PDO $pdo A \PDO instance - * @param array $options An associative array of DB options + * @param \PDO|string|null $pdoOrDns A \PDO instance or DNS string or null + * @param array $options An associative array of DB options * * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION */ - public function __construct(\PDO $pdo, array $options = array()) + public function __construct($pdoOrDns, array $options = array()) { - if (\PDO::ERRMODE_EXCEPTION !== $pdo->getAttribute(\PDO::ATTR_ERRMODE)) { - throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__)); - } + if ($pdoOrDns instanceof \PDO) { + if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDns->getAttribute(\PDO::ATTR_ERRMODE)) { + throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__)); + } - $this->pdo = $pdo; - $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); + $this->pdo = $pdoOrDns; + } else { + $this->dns = $pdoOrDns; + } $options = array_replace(array( - 'db_table' => 'sessions', - 'db_id_col' => 'sess_id', - 'db_data_col' => 'sess_data', - 'db_time_col' => 'sess_time', + 'db_table' => 'sessions', + 'db_id_col' => 'sess_id', + 'db_data_col' => 'sess_data', + 'db_time_col' => 'sess_time', + 'db_username' => '', + 'db_password' => '', + 'db_connection_options' => array() ), $options); $this->table = $options['db_table']; $this->idCol = $options['db_id_col']; $this->dataCol = $options['db_data_col']; $this->timeCol = $options['db_time_col']; + $this->username = $options['db_username']; + $this->password = $options['db_password']; + $this->connectionOptions = $options['db_connection_options']; } /** @@ -118,6 +155,11 @@ public function __construct(\PDO $pdo, array $options = array()) public function open($savePath, $sessionName) { $this->gcCalled = false; + if (null === $this->pdo) { + $this->pdo = new \PDO($this->dns ?: $savePath, $this->username, $this->password, $this->connectionOptions); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + } + $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); return true; } @@ -270,6 +312,10 @@ public function close() $stmt->execute(); } + if (false !== $this->dns) { + $this->pdo = null; + } + return true; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php index 41b2e753c510b..3356e3dcea34a 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -25,7 +25,7 @@ protected function setUp() $this->pdo = new \PDO('sqlite::memory:'); $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - $sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data TEXT, sess_time INTEGER)'; + $sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data BLOB, sess_time INTEGER)'; $this->pdo->exec($sql); } @@ -51,17 +51,74 @@ public function testInexistentTable() $storage->close(); } + public function testWithLazyDnsConnection() + { + $dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions'); + if (file_exists($dbFile)) { + @unlink($dbFile); + } + + $pdo = new \PDO('sqlite:' . $dbFile); + $sql = 'CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data BLOB, sess_time INTEGER)'; + $pdo->exec($sql); + $pdo = null; + + $storage = new PdoSessionHandler('sqlite:' . $dbFile); + $storage->open('', 'sid'); + $data = $storage->read('id'); + $storage->write('id', 'data'); + $storage->close(); + $this->assertSame('', $data, 'New session returns empty string data'); + + $storage->open('', 'sid'); + $data = $storage->read('id'); + $storage->close(); + $this->assertSame('data', $data, 'Written value can be read back correctly'); + + @unlink($dbFile); + } + + public function testWithLazySavePathConnection() + { + $dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions'); + if (file_exists($dbFile)) { + @unlink($dbFile); + } + + $pdo = new \PDO('sqlite:' . $dbFile); + $sql = 'CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data BLOB, sess_time INTEGER)'; + $pdo->exec($sql); + $pdo = null; + + // Open is called with what ini_set('session.save_path', 'sqlite:' . $dbFile) would mean + $storage = new PdoSessionHandler(null); + $storage->open('sqlite:' . $dbFile, 'sid'); + $data = $storage->read('id'); + $storage->write('id', 'data'); + $storage->close(); + $this->assertSame('', $data, 'New session returns empty string data'); + + $storage->open('sqlite:' . $dbFile, 'sid'); + $data = $storage->read('id'); + $storage->close(); + $this->assertSame('data', $data, 'Written value can be read back correctly'); + + @unlink($dbFile); + } + public function testReadWriteRead() { $storage = new PdoSessionHandler($this->pdo); $storage->open('', 'sid'); - $this->assertSame('', $storage->read('id'), 'New session returns empty string data'); + $data = $storage->read('id'); $storage->write('id', 'data'); $storage->close(); + $this->assertSame('', $data, 'New session returns empty string data'); $storage->open('', 'sid'); - $this->assertSame('data', $storage->read('id'), 'Written value can be read back correctly'); + $data = $storage->read('id'); $storage->close(); + $this->assertSame('data', $data, 'Written value can be read back correctly'); } /** @@ -77,8 +134,9 @@ public function testWriteDifferentSessionIdThanRead() $storage->close(); $storage->open('', 'sid'); - $this->assertSame('data_of_new_session_id', $storage->read('new_id'), 'Data of regenerated session id is available'); + $data = $storage->read('new_id'); $storage->close(); + $this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available'); } /** @@ -109,8 +167,9 @@ public function testSessionDestroy() $this->assertEquals(0, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); $storage->open('', 'sid'); - $this->assertSame('', $storage->read('id'), 'Destroyed session returns empty string'); + $data = $storage->read('id'); $storage->close(); + $this->assertSame('', $data, 'Destroyed session returns empty string'); } public function testSessionGC() @@ -125,12 +184,14 @@ public function testSessionGC() $this->assertEquals(1, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); $storage->open('', 'sid'); - $this->assertSame('', $storage->read('id'), 'Session already considered garbage, so not returning data even if it is not pruned yet'); + $data = $storage->read('id'); $storage->gc(0); $storage->close(); - $this->assertEquals(0, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); ini_set('session.gc_maxlifetime', $previousLifeTime); + + $this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet'); + $this->assertEquals(0, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); } public function testGetConnection() From af1bb1f6e7e66322323eb8fa036d77b1b52d1c06 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Sun, 18 May 2014 04:10:22 +0200 Subject: [PATCH 238/373] add test for null byte in session data --- .../Storage/Handler/PdoSessionHandlerTest.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php index 3356e3dcea34a..9b29309ad4884 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -106,19 +106,21 @@ public function testWithLazySavePathConnection() @unlink($dbFile); } - public function testReadWriteRead() + public function testReadWriteReadWithNullByte() { + $sessionData = 'da' . "\0" . 'ta'; + $storage = new PdoSessionHandler($this->pdo); $storage->open('', 'sid'); - $data = $storage->read('id'); - $storage->write('id', 'data'); + $readData = $storage->read('id'); + $storage->write('id', $sessionData); $storage->close(); - $this->assertSame('', $data, 'New session returns empty string data'); + $this->assertSame('', $readData, 'New session returns empty string data'); $storage->open('', 'sid'); - $data = $storage->read('id'); + $readData = $storage->read('id'); $storage->close(); - $this->assertSame('data', $data, 'Written value can be read back correctly'); + $this->assertSame($sessionData, $readData, 'Written value can be read back correctly'); } /** From e79229d4d3eaf2143e8497d1608ad66fb726d0c9 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Sun, 18 May 2014 23:56:35 +0200 Subject: [PATCH 239/373] [HttpFoundation] allow different lifetime per session --- .../Storage/Handler/PdoSessionHandler.php | 98 ++++++++++--------- .../Storage/Handler/PdoSessionHandlerTest.php | 42 +++++--- 2 files changed, 79 insertions(+), 61 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index 9d5fa8f4850ab..e5365dd31e22f 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -42,9 +42,9 @@ class PdoSessionHandler implements \SessionHandlerInterface private $pdo; /** - * @var string|null|false DNS string or null for session.save_path or false when lazy connection disabled + * @var string|null|false DSN string or null for session.save_path or false when lazy connection disabled */ - private $dns = false; + private $dsn = false; /** * @var string Database driver @@ -66,6 +66,11 @@ class PdoSessionHandler implements \SessionHandlerInterface */ private $dataCol; + /** + * @var string Column for lifetime + */ + private $lifetimeCol; + /** * @var string Column for timestamp */ @@ -100,40 +105,43 @@ class PdoSessionHandler implements \SessionHandlerInterface * Constructor. * * You can either pass an existing database connection as PDO instance or - * pass a DNS string that will be used to lazy-connect to the database + * pass a DSN string that will be used to lazy-connect to the database * when the session is actually used. Furthermore it's possible to pass null - * which will then use the session.save_path ini setting as PDO DNS parameter. + * which will then use the session.save_path ini setting as PDO DSN parameter. * * List of available options: * * db_table: The name of the table [default: sessions] * * db_id_col: The column where to store the session id [default: sess_id] * * db_data_col: The column where to store the session data [default: sess_data] + * * db_lifetime_col: The column where to store the lifetime [default: sess_lifetime] * * db_time_col: The column where to store the timestamp [default: sess_time] * * db_username: The username when lazy-connect [default: ''] * * db_password: The password when lazy-connect [default: ''] * * db_connection_options: An array of driver-specific connection options [default: array()] * - * @param \PDO|string|null $pdoOrDns A \PDO instance or DNS string or null + * @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or null * @param array $options An associative array of DB options * * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION */ - public function __construct($pdoOrDns, array $options = array()) + public function __construct($pdoOrDsn, array $options = array()) { - if ($pdoOrDns instanceof \PDO) { - if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDns->getAttribute(\PDO::ATTR_ERRMODE)) { + if ($pdoOrDsn instanceof \PDO) { + if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) { throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__)); } - $this->pdo = $pdoOrDns; + $this->pdo = $pdoOrDsn; + $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); } else { - $this->dns = $pdoOrDns; + $this->dsn = $pdoOrDsn; } $options = array_replace(array( 'db_table' => 'sessions', 'db_id_col' => 'sess_id', 'db_data_col' => 'sess_data', + 'db_lifetime_col' => 'sess_lifetime', 'db_time_col' => 'sess_time', 'db_username' => '', 'db_password' => '', @@ -143,6 +151,7 @@ public function __construct($pdoOrDns, array $options = array()) $this->table = $options['db_table']; $this->idCol = $options['db_id_col']; $this->dataCol = $options['db_data_col']; + $this->lifetimeCol = $options['db_lifetime_col']; $this->timeCol = $options['db_time_col']; $this->username = $options['db_username']; $this->password = $options['db_password']; @@ -156,10 +165,10 @@ public function open($savePath, $sessionName) { $this->gcCalled = false; if (null === $this->pdo) { - $this->pdo = new \PDO($this->dns ?: $savePath, $this->username, $this->password, $this->connectionOptions); + $this->pdo = new \PDO($this->dsn ?: $savePath, $this->username, $this->password, $this->connectionOptions); $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); } - $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); return true; } @@ -176,13 +185,12 @@ public function read($sessionId) // We need to make sure we do not return session data that is already considered garbage according // to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes. - $maxlifetime = (int) ini_get('session.gc_maxlifetime'); - $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id AND $this->timeCol > :time"; + $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id AND $this->lifetimeCol + $this->timeCol >= :time"; $stmt = $this->pdo->prepare($sql); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); $stmt->execute(); // We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed @@ -239,6 +247,8 @@ public function write($sessionId, $data) // do an insert or update even if we created a row in read() for locking. // We use a single MERGE SQL query when supported by the database. + $maxlifetime = (int) ini_get('session.gc_maxlifetime'); + try { $mergeSql = $this->getMergeSql(); @@ -246,6 +256,7 @@ public function write($sessionId, $data) $mergeStmt = $this->pdo->prepare($mergeSql); $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); $mergeStmt->execute(); @@ -253,10 +264,11 @@ public function write($sessionId, $data) } $updateStmt = $this->pdo->prepare( - "UPDATE $this->table SET $this->dataCol = :data, $this->timeCol = :time WHERE $this->idCol = :id" + "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id" ); $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $updateStmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); $updateStmt->execute(); @@ -270,10 +282,11 @@ public function write($sessionId, $data) if (!$updateStmt->rowCount()) { try { $insertStmt = $this->pdo->prepare( - "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)" + "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)" ); $insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $insertStmt->bindParam(':data', $encoded, \PDO::PARAM_LOB); + $insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $insertStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT); $insertStmt->execute(); } catch (\PDOException $e) { @@ -302,17 +315,15 @@ public function close() $this->commit(); if ($this->gcCalled) { - $maxlifetime = (int) ini_get('session.gc_maxlifetime'); - // delete the session records that have expired - $sql = "DELETE FROM $this->table WHERE $this->timeCol <= :time"; + $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time"; $stmt = $this->pdo->prepare($sql); - $stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); $stmt->execute(); } - if (false !== $this->dns) { + if (false !== $this->dsn) { $this->pdo = null; } @@ -329,20 +340,14 @@ public function close() */ private function beginTransaction() { - if ($this->inTransaction) { - $this->rollback(); - - throw new \BadMethodCallException( - 'Session handler methods have been invoked in wrong sequence. '. - 'Expected sequence: open() -> read() -> destroy() / write() -> close()'); - } - - if ('sqlite' === $this->driver) { - $this->pdo->exec('BEGIN IMMEDIATE TRANSACTION'); - } else { - $this->pdo->beginTransaction(); + if (!$this->inTransaction) { + if ('sqlite' === $this->driver) { + $this->pdo->exec('BEGIN IMMEDIATE TRANSACTION'); + } else { + $this->pdo->beginTransaction(); + } + $this->inTransaction = true; } - $this->inTransaction = true; } /** @@ -400,20 +405,20 @@ private function lockSession($sessionId) switch ($this->driver) { case 'mysql': // will also lock the row when actually nothing got updated (id = id) - $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". + $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". "ON DUPLICATE KEY UPDATE $this->idCol = $this->idCol"; break; case 'oci': // DUAL is Oracle specific dummy table $sql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ". - "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". "WHEN MATCHED THEN UPDATE SET $this->idCol = $this->idCol"; break; // todo: implement locking for SQL Server < 2008 case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='): // MS SQL Server requires MERGE be terminated by semicolon $sql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ". - "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". "WHEN MATCHED THEN UPDATE SET $this->idCol = $this->idCol;"; break; case 'pgsql': @@ -434,6 +439,7 @@ private function lockSession($sessionId) $stmt = $this->pdo->prepare($sql); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $stmt->bindValue(':data', '', \PDO::PARAM_STR); + $stmt->bindValue(':lifetime', 0, \PDO::PARAM_INT); $stmt->bindValue(':time', time(), \PDO::PARAM_INT); $stmt->execute(); } @@ -447,21 +453,21 @@ private function getMergeSql() { switch ($this->driver) { case 'mysql': - return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". - "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)"; + return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". + "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)"; case 'oci': // DUAL is Oracle specific dummy table return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ". - "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". - "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time"; + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". + "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time"; case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='): // MERGE is only available since SQL Server 2008 and must be terminated by semicolon // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ". - "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". - "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;"; + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". + "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time;"; case 'sqlite': - return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)"; + return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"; } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php index 9b29309ad4884..36da3645c5560 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -25,7 +25,7 @@ protected function setUp() $this->pdo = new \PDO('sqlite::memory:'); $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - $sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data BLOB, sess_time INTEGER)'; + $sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data BLOB, sess_lifetime MEDIUMINT, sess_time INTEGER)'; $this->pdo->exec($sql); } @@ -59,7 +59,7 @@ public function testWithLazyDnsConnection() } $pdo = new \PDO('sqlite:' . $dbFile); - $sql = 'CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data BLOB, sess_time INTEGER)'; + $sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data BLOB, sess_lifetime MEDIUMINT, sess_time INTEGER)'; $pdo->exec($sql); $pdo = null; @@ -86,7 +86,7 @@ public function testWithLazySavePathConnection() } $pdo = new \PDO('sqlite:' . $dbFile); - $sql = 'CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data BLOB, sess_time INTEGER)'; + $sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data BLOB, sess_lifetime MEDIUMINT, sess_time INTEGER)'; $pdo->exec($sql); $pdo = null; @@ -138,18 +138,24 @@ public function testWriteDifferentSessionIdThanRead() $storage->open('', 'sid'); $data = $storage->read('new_id'); $storage->close(); + $this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available'); } - /** - * @expectedException \BadMethodCallException - */ - public function testWrongUsage() + public function testWrongUsageStillWorks() { + // wrong method sequence that should no happen, but still works $storage = new PdoSessionHandler($this->pdo); + $storage->write('id', 'data'); + $storage->write('other_id', 'other_data'); + $storage->destroy('inexistent'); $storage->open('', 'sid'); - $storage->read('id'); - $storage->read('id'); + $data = $storage->read('id'); + $otherData = $storage->read('other_id'); + $storage->close(); + + $this->assertSame('data', $data); + $this->assertSame('other_data', $otherData); } public function testSessionDestroy() @@ -176,29 +182,35 @@ public function testSessionDestroy() public function testSessionGC() { - $previousLifeTime = ini_set('session.gc_maxlifetime', 0); + $previousLifeTime = ini_set('session.gc_maxlifetime', 1000); $storage = new PdoSessionHandler($this->pdo); $storage->open('', 'sid'); $storage->read('id'); $storage->write('id', 'data'); $storage->close(); - $this->assertEquals(1, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); $storage->open('', 'sid'); - $data = $storage->read('id'); - $storage->gc(0); + $storage->read('gc_id'); + ini_set('session.gc_maxlifetime', -1); // test that you can set lifetime of a session after it has been read + $storage->write('gc_id', 'data'); + $storage->close(); + $this->assertEquals(2, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'No session pruned because gc not called'); + + $storage->open('', 'sid'); + $data = $storage->read('gc_id'); + $storage->gc(-1); $storage->close(); ini_set('session.gc_maxlifetime', $previousLifeTime); $this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet'); - $this->assertEquals(0, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); + $this->assertEquals(1, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'Expired session is pruned'); } public function testGetConnection() { - $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array()); + $storage = new PdoSessionHandler($this->pdo); $method = new \ReflectionMethod($storage, 'getConnection'); $method->setAccessible(true); From 182a5d39dff3318cee477a88d0621eb93032090a Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Mon, 19 May 2014 01:56:41 +0200 Subject: [PATCH 240/373] [HttpFoundation] add create table method to pdo session handler --- .../Storage/Handler/PdoSessionHandler.php | 77 +++++++++++- .../Storage/Handler/PdoSessionHandlerTest.php | 114 +++++++++++------- 2 files changed, 140 insertions(+), 51 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index e5365dd31e22f..dea848238000d 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -26,7 +26,8 @@ * * Session data is a binary string that can contain non-printable characters like the null byte. * For this reason it must be saved in a binary column in the database like BLOB in MySQL. - * Saving it in a character column could corrupt the data. + * Saving it in a character column could corrupt the data. You can use createTable() + * to initialize a correctly defined table. * * @see http://php.net/sessionhandlerinterface * @@ -158,16 +159,58 @@ public function __construct($pdoOrDsn, array $options = array()) $this->connectionOptions = $options['db_connection_options']; } + /** + * Creates the table to store sessions which can be called once for setup. + * + * Session ID is saved in a VARCHAR(128) column because that is enough even for + * a 512 bit configured session.hash_function like Whirlpool. Session data is + * saved in a BLOB. One could also use a shorter inlined varbinary column + * if one was sure the data fits into it. + * + * @throws \PDOException When the table already exists + * @throws \DomainException When an unsupported PDO driver is used + */ + public function createTable() + { + // connect if we are not yet + $this->getConnection(); + + switch ($this->driver) { + case 'mysql': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER NOT NULL) ENGINE = InnoDB"; + break; + case 'sqlite': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + case 'pgsql': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + case 'oci': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + case 'sqlsrv': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + default: + throw new \DomainException(sprintf('"%s" does not currently support PDO driver "%s".', __CLASS__, $this->driver)); + } + + try { + $this->pdo->exec($sql); + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + } + /** * {@inheritdoc} */ public function open($savePath, $sessionName) { - $this->gcCalled = false; if (null === $this->pdo) { - $this->pdo = new \PDO($this->dsn ?: $savePath, $this->username, $this->password, $this->connectionOptions); - $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); + $this->connect($this->dsn ?: $savePath); } return true; @@ -315,6 +358,8 @@ public function close() $this->commit(); if ($this->gcCalled) { + $this->gcCalled = false; + // delete the session records that have expired $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time"; @@ -330,6 +375,18 @@ public function close() return true; } + /** + * Lazy-connects to the database. + * + * @param string $dsn DSN string + */ + private function connect($dsn) + { + $this->pdo = new \PDO($dsn, $this->username, $this->password, $this->connectionOptions); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); + } + /** * Helper method to begin a transaction. * @@ -399,6 +456,8 @@ private function rollback() * INSERT when not found can result in a deadlock for one connection. * * @param string $sessionId Session ID + * + * @throws \DomainException When an unsupported PDO driver is used */ private function lockSession($sessionId) { @@ -429,8 +488,10 @@ private function lockSession($sessionId) $stmt->execute(); return; + case 'sqlite': + return; // we already locked when starting transaction default: - return; + throw new \DomainException(sprintf('"%s" does not currently support PDO driver "%s".', __CLASS__, $this->driver)); } // We create a DML lock for the session by inserting empty data or updating the row. @@ -478,6 +539,10 @@ private function getMergeSql() */ protected function getConnection() { + if (null === $this->pdo) { + $this->connect($this->dsn ?: ini_get('session.save_path')); + } + return $this->pdo; } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php index 36da3645c5560..3fa6cd58afe4b 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -15,18 +15,38 @@ class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase { - private $pdo; + private $dbFile; protected function setUp() { if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers())) { $this->markTestSkipped('This test requires SQLite support in your environment'); } + } + + protected function tearDown() + { + // make sure the temporary database file is deleted when it has been created (even when a test fails) + if ($this->dbFile) { + @unlink($this->dbFile); + } + } + + protected function getPersistentSqliteDsn() + { + $this->dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions'); - $this->pdo = new \PDO('sqlite::memory:'); - $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - $sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data BLOB, sess_lifetime MEDIUMINT, sess_time INTEGER)'; - $this->pdo->exec($sql); + return 'sqlite:' . $this->dbFile; + } + + protected function getMemorySqlitePdo() + { + $pdo = new \PDO('sqlite::memory:'); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $storage = new PdoSessionHandler($pdo); + $storage->createTable(); + + return $pdo; } /** @@ -34,9 +54,10 @@ protected function setUp() */ public function testWrongPdoErrMode() { - $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); + $pdo = $this->getMemorySqlitePdo(); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); - $storage = new PdoSessionHandler($this->pdo); + $storage = new PdoSessionHandler($pdo); } /** @@ -44,26 +65,28 @@ public function testWrongPdoErrMode() */ public function testInexistentTable() { - $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'inexistent_table')); + $storage = new PdoSessionHandler($this->getMemorySqlitePdo(), array('db_table' => 'inexistent_table')); $storage->open('', 'sid'); $storage->read('id'); $storage->write('id', 'data'); $storage->close(); } - public function testWithLazyDnsConnection() + /** + * @expectedException \RuntimeException + */ + public function testCreateTableTwice() { - $dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions'); - if (file_exists($dbFile)) { - @unlink($dbFile); - } + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); + $storage->createTable(); + } - $pdo = new \PDO('sqlite:' . $dbFile); - $sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data BLOB, sess_lifetime MEDIUMINT, sess_time INTEGER)'; - $pdo->exec($sql); - $pdo = null; + public function testWithLazyDsnConnection() + { + $dsn = $this->getPersistentSqliteDsn(); - $storage = new PdoSessionHandler('sqlite:' . $dbFile); + $storage = new PdoSessionHandler($dsn); + $storage->createTable(); $storage->open('', 'sid'); $data = $storage->read('id'); $storage->write('id', 'data'); @@ -74,43 +97,32 @@ public function testWithLazyDnsConnection() $data = $storage->read('id'); $storage->close(); $this->assertSame('data', $data, 'Written value can be read back correctly'); - - @unlink($dbFile); } public function testWithLazySavePathConnection() { - $dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions'); - if (file_exists($dbFile)) { - @unlink($dbFile); - } + $dsn = $this->getPersistentSqliteDsn(); - $pdo = new \PDO('sqlite:' . $dbFile); - $sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data BLOB, sess_lifetime MEDIUMINT, sess_time INTEGER)'; - $pdo->exec($sql); - $pdo = null; - - // Open is called with what ini_set('session.save_path', 'sqlite:' . $dbFile) would mean + // Open is called with what ini_set('session.save_path', $dsn) would mean $storage = new PdoSessionHandler(null); - $storage->open('sqlite:' . $dbFile, 'sid'); + $storage->open($dsn, 'sid'); + $storage->createTable(); $data = $storage->read('id'); $storage->write('id', 'data'); $storage->close(); $this->assertSame('', $data, 'New session returns empty string data'); - $storage->open('sqlite:' . $dbFile, 'sid'); + $storage->open($dsn, 'sid'); $data = $storage->read('id'); $storage->close(); $this->assertSame('data', $data, 'Written value can be read back correctly'); - - @unlink($dbFile); } public function testReadWriteReadWithNullByte() { $sessionData = 'da' . "\0" . 'ta'; - $storage = new PdoSessionHandler($this->pdo); + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); $storage->open('', 'sid'); $readData = $storage->read('id'); $storage->write('id', $sessionData); @@ -128,7 +140,7 @@ public function testReadWriteReadWithNullByte() */ public function testWriteDifferentSessionIdThanRead() { - $storage = new PdoSessionHandler($this->pdo); + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); $storage->open('', 'sid'); $storage->read('id'); $storage->destroy('id'); @@ -139,13 +151,13 @@ public function testWriteDifferentSessionIdThanRead() $data = $storage->read('new_id'); $storage->close(); - $this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available'); + $this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available'); } public function testWrongUsageStillWorks() { // wrong method sequence that should no happen, but still works - $storage = new PdoSessionHandler($this->pdo); + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); $storage->write('id', 'data'); $storage->write('other_id', 'other_data'); $storage->destroy('inexistent'); @@ -160,19 +172,20 @@ public function testWrongUsageStillWorks() public function testSessionDestroy() { - $storage = new PdoSessionHandler($this->pdo); + $pdo = $this->getMemorySqlitePdo(); + $storage = new PdoSessionHandler($pdo); $storage->open('', 'sid'); $storage->read('id'); $storage->write('id', 'data'); $storage->close(); - $this->assertEquals(1, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); + $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); $storage->open('', 'sid'); $storage->read('id'); $storage->destroy('id'); $storage->close(); - $this->assertEquals(0, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); + $this->assertEquals(0, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); $storage->open('', 'sid'); $data = $storage->read('id'); @@ -183,7 +196,8 @@ public function testSessionDestroy() public function testSessionGC() { $previousLifeTime = ini_set('session.gc_maxlifetime', 1000); - $storage = new PdoSessionHandler($this->pdo); + $pdo = $this->getMemorySqlitePdo(); + $storage = new PdoSessionHandler($pdo); $storage->open('', 'sid'); $storage->read('id'); @@ -195,7 +209,7 @@ public function testSessionGC() ini_set('session.gc_maxlifetime', -1); // test that you can set lifetime of a session after it has been read $storage->write('gc_id', 'data'); $storage->close(); - $this->assertEquals(2, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'No session pruned because gc not called'); + $this->assertEquals(2, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'No session pruned because gc not called'); $storage->open('', 'sid'); $data = $storage->read('gc_id'); @@ -205,12 +219,22 @@ public function testSessionGC() ini_set('session.gc_maxlifetime', $previousLifeTime); $this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet'); - $this->assertEquals(1, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'Expired session is pruned'); + $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'Expired session is pruned'); } public function testGetConnection() { - $storage = new PdoSessionHandler($this->pdo); + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); + + $method = new \ReflectionMethod($storage, 'getConnection'); + $method->setAccessible(true); + + $this->assertInstanceOf('\PDO', $method->invoke($storage)); + } + + public function testGetConnectionConnectsIfNeeded() + { + $storage = new PdoSessionHandler('sqlite::memory:'); $method = new \ReflectionMethod($storage, 'getConnection'); $method->setAccessible(true); From 5978fcfb089e72aded43052e57de8a563e0ce8f0 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Sun, 25 May 2014 15:52:29 +0200 Subject: [PATCH 241/373] added upgrade and changelog notes for PdoSessionHandler --- UPGRADE-2.6.md | 24 ++++++++++++++++++- .../Component/HttpFoundation/CHANGELOG.md | 11 +++++++++ .../Storage/Handler/PdoSessionHandler.php | 3 +++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/UPGRADE-2.6.md b/UPGRADE-2.6.md index 03a7da49dd9bc..5fbdc90743e80 100644 --- a/UPGRADE-2.6.md +++ b/UPGRADE-2.6.md @@ -1,4 +1,4 @@ -UPGRADE FROM 2.5 to 2.6 +UPGRADE FROM 2.5 to 2.6 ======================= Form @@ -101,3 +101,25 @@ Security @security.token_storage => getToken() @security.token_storage => setToken() ``` + +HttpFoundation +-------------- + + * The PdoSessionHandler to store sessions in a database changed significantly. + - It now implements session locking to prevent loss of data by concurrent access to the same session. + - It does so using a transaction between opening and closing a session. For this reason, it's not + recommended to use the same database connection that you also use for your application logic. + Otherwise you have to make sure to access your database after the session is closed and committed. + Instead of passing an existing connection to the handler, you can now also pass a DSN string which + will be used to lazy-connect when a session is started. + - Since accessing a session now blocks when the same session is still open, it is best practice to + save the session as soon as you don't need to write to it anymore. For example, read-only AJAX + request to a session can save the session immediately after opening it to increase concurrency. + - The expected schema of the table changed. + - Session data is binary text that can contain null bytes and thus should also be saved as-is in a + binary column like BLOB. For this reason, the handler does not base64_encode the data anymore. + - A new column to store the lifetime of a session is required. This allows to have different + lifetimes per session configured via session.gc_maxlifetime ini setting. + - You would need to migrate the table manually if you want to keep session information of your users. + - You could use PdoSessionHandler::createTable to initialize a correctly defined table depending on + the used database vendor. diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index 0da734bce2f1c..0b25cf89d2bb4 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -1,6 +1,17 @@ CHANGELOG ========= +2.6.0 +----- + + * PdoSessionHandler changes + - implemented session locking to prevent loss of data by concurrent access to the same session + - save session data in a binary column without base64_encode + - added lifetime column to the session table which allows to have different lifetimes for each session + - implemented lazy connections that are only opened when a session is used by either passing a dsn string + explicitly or falling back to session.save_path ini setting + - added a createTable method that initializes a correctly defined table depending on the database vendor + 2.5.0 ----- diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index dea848238000d..2ed3dee7eb90e 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -109,6 +109,9 @@ class PdoSessionHandler implements \SessionHandlerInterface * pass a DSN string that will be used to lazy-connect to the database * when the session is actually used. Furthermore it's possible to pass null * which will then use the session.save_path ini setting as PDO DSN parameter. + * Since locking uses a transaction between opening and closing a session, + * it's not recommended to use the same database connection that you also use + * for your application logic. * * List of available options: * * db_table: The name of the table [default: sessions] From 6f5748e05745578171dde19039fadb9d87e8382c Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Wed, 4 Jun 2014 23:06:34 +0200 Subject: [PATCH 242/373] adjust sqlite table definition --- .../Storage/Handler/PdoSessionHandler.php | 4 ++-- .../Storage/Handler/PdoSessionHandlerTest.php | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index 2ed3dee7eb90e..f8cc28ff7b7fe 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -180,10 +180,10 @@ public function createTable() switch ($this->driver) { case 'mysql': - $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER NOT NULL) ENGINE = InnoDB"; + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) ENGINE = InnoDB"; break; case 'sqlite': - $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER NOT NULL)"; + $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; break; case 'pgsql': $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php index 3fa6cd58afe4b..418444766e8c6 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -135,6 +135,28 @@ public function testReadWriteReadWithNullByte() $this->assertSame($sessionData, $readData, 'Written value can be read back correctly'); } + public function testReadingRequiresExactlySameId() + { + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); + $storage->open('', 'sid'); + $storage->write('id', 'data'); + $storage->write('test', 'data'); + $storage->write('space ', 'data'); + $storage->close(); + + $storage->open('', 'sid'); + $readDataCaseSensitive = $storage->read('ID'); + $readDataNoCharFolding = $storage->read('tést'); + $readDataKeepSpace = $storage->read('space '); + $readDataExtraSpace = $storage->read('space '); + $storage->close(); + + $this->assertSame('', $readDataCaseSensitive, 'Retrieval by ID should be case-sensitive (collation setting)'); + $this->assertSame('', $readDataNoCharFolding, 'Retrieval by ID should not do character folding (collation setting)'); + $this->assertSame('data', $readDataKeepSpace, 'Retrieval by ID requires spaces as-is'); + $this->assertSame('', $readDataExtraSpace, 'Retrieval by ID requires spaces as-is'); + } + /** * Simulates session_regenerate_id(true) which will require an INSERT or UPDATE (replace) */ From 7bb768dd2a10c23b73726c96eedba5bcf48b059c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 29 Sep 2014 22:05:55 +0200 Subject: [PATCH 243/373] [HttpKernel] fix composer.json --- src/Symfony/Component/HttpKernel/composer.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 6ca536c552761..338602a2e4aa7 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -34,7 +34,8 @@ "symfony/routing": "~2.2", "symfony/stopwatch": "~2.2", "symfony/templating": "~2.2", - "symfony/translator": "~2.0" + "symfony/translation": "~2.0", + "symfony/var-dumper": "~2.6" }, "suggest": { "symfony/browser-kit": "", @@ -42,7 +43,8 @@ "symfony/config": "", "symfony/console": "", "symfony/dependency-injection": "", - "symfony/finder": "" + "symfony/finder": "", + "symfony/var-dumper": "" }, "autoload": { "psr-0": { "Symfony\\Component\\HttpKernel\\": "" } From 986df54c5b64d650b3f8a93469654667327f1aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Egyed?= <1ed@mailbox.hu> Date: Mon, 29 Sep 2014 23:08:45 +0200 Subject: [PATCH 244/373] [HttpKernel] Initialize DataCollector data as array This fixes a DumpDataCollector error when the debug panel is opened without collected data: ContextErrorException: Warning: Invalid argument supplied for foreach() - in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php at line 165 --- .../Component/HttpKernel/DataCollector/DataCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php index 1835938a07263..5dca65298d059 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php @@ -23,7 +23,7 @@ */ abstract class DataCollector implements DataCollectorInterface, \Serializable { - protected $data; + protected $data = array(); /** * @var ValueExporter From 8dbe25882e5f6119e744df8157b8332806f958bc Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Fri, 26 Sep 2014 13:06:33 +0200 Subject: [PATCH 245/373] [Form] The trace of form errors is now displayed in the profiler --- .../Resources/views/Collector/form.html.twig | 28 ++++++++++---- src/Symfony/Component/Form/Button.php | 9 +++++ src/Symfony/Component/Form/CHANGELOG.md | 1 + .../DataCollector/FormDataExtractor.php | 37 +++++++++++++++---- .../Validator/Constraints/FormValidator.php | 1 + src/Symfony/Component/Form/Form.php | 20 ++++++---- src/Symfony/Component/Form/FormInterface.php | 12 ++++++ .../DataCollector/FormDataExtractorTest.php | 11 ++++-- .../Constraints/FormValidatorTest.php | 9 +++++ 9 files changed, 103 insertions(+), 25 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig index 6d6d07d240ab0..736f718ccf13a 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -473,14 +473,28 @@ {% endif %} - {% if error.cause is empty %} - Unknown. - {% elseif error.cause.root is defined %} - Constraint Violation
    -
    {{ error.cause.root }}{% if error.cause.path is not empty %}{% if error.cause.path|first != '[' %}.{% endif %}{{ error.cause.path }}{% endif %} = {{ error.cause.value }}
    + {% for trace in error.trace %} + {% if not loop.first %} +
    Caused by:

    + {% endif %} + {% if trace.root is defined %} + {{ trace.class }}
    +
    +                                    {{- trace.root -}}
    +                                    {%- if trace.path is not empty -%}
    +                                        {%- if trace.path|first != '[' %}.{% endif -%}
    +                                        {{- trace.path -}}
    +                                    {%- endif %} = {{ trace.value -}}
    +                                
    + {% elseif trace.message is defined %} + {{ trace.class }}
    +
    {{ trace.message }}
    + {% else %} +
    {{ trace }}
    + {% endif %} {% else %} -
    {{ error.cause }}
    - {% endif %} + Unknown. + {% endfor %} {% endfor %} diff --git a/src/Symfony/Component/Form/Button.php b/src/Symfony/Component/Form/Button.php index 380b20f4ddc2b..0ba4c8138cf30 100644 --- a/src/Symfony/Component/Form/Button.php +++ b/src/Symfony/Component/Form/Button.php @@ -344,6 +344,15 @@ public function isSynchronized() return true; } + /** + * Unsupported method. + * + * @return null Always returns null + */ + public function getTransformationFailure() + { + } + /** * Unsupported method. * diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 4381726126d48..dfc2e89ea7cd8 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -21,6 +21,7 @@ CHANGELOG * ObjectChoiceList now compares choices by their value, if a value path is given * you can now pass interface names in the "data_class" option + * [BC BREAK] added `FormInterface::getTransformationFailure()` 2.4.0 ----- diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php index 80e910177a894..0ae5560afeab1 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php @@ -115,18 +115,39 @@ public function extractSubmittedData(FormInterface $form) 'origin' => is_object($error->getOrigin()) ? spl_object_hash($error->getOrigin()) : null, + 'trace' => array(), ); $cause = $error->getCause(); - if ($cause instanceof ConstraintViolationInterface) { - $errorData['cause'] = array( - 'root' => $this->valueExporter->exportValue($cause->getRoot()), - 'path' => $this->valueExporter->exportValue($cause->getPropertyPath()), - 'value' => $this->valueExporter->exportValue($cause->getInvalidValue()), - ); - } else { - $errorData['cause'] = null !== $cause ? $this->valueExporter->exportValue($cause) : null; + while (null !== $cause) { + if ($cause instanceof ConstraintViolationInterface) { + $errorData['trace'][] = array( + 'class' => $this->valueExporter->exportValue(get_class($cause)), + 'root' => $this->valueExporter->exportValue($cause->getRoot()), + 'path' => $this->valueExporter->exportValue($cause->getPropertyPath()), + 'value' => $this->valueExporter->exportValue($cause->getInvalidValue()), + ); + + $cause = method_exists($cause, 'getCause') ? $cause->getCause() : null; + + continue; + } + + if ($cause instanceof \Exception) { + $errorData['trace'][] = array( + 'class' => $this->valueExporter->exportValue(get_class($cause)), + 'message' => $this->valueExporter->exportValue($cause->getMessage()), + ); + + $cause = $cause->getPrevious(); + + continue; + } + + $errorData['trace'][] = $cause; + + break; } $data['errors'][] = $errorData; diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index 1fe1147051072..7c0dc44ab1808 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -103,6 +103,7 @@ public function validate($form, Constraint $constraint) ->setParameters(array_replace(array('{{ value }}' => $clientDataAsString), $config->getOption('invalid_message_parameters'))) ->setInvalidValue($form->getViewData()) ->setCode(Form::ERR_INVALID) + ->setCause($form->getTransformationFailure()) ->addViolation(); } } diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 90048d3fb5582..9b259e820b134 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -121,12 +121,10 @@ class Form implements \IteratorAggregate, FormInterface private $extraData = array(); /** - * Whether the data in model, normalized and view format is - * synchronized. Data may not be synchronized if transformation errors - * occur. - * @var bool + * Returns the transformation failure generated during submission, if any + * @var TransformationFailedException|null */ - private $synchronized = true; + private $transformationFailure; /** * Whether the form's data has been initialized. @@ -634,7 +632,7 @@ public function submit($submittedData, $clearMissing = true) $viewData = $this->normToView($normData); } } catch (TransformationFailedException $e) { - $this->synchronized = false; + $this->transformationFailure = $e; // If $viewData was not yet set, set it to $submittedData so that // the erroneous data is accessible on the form. @@ -711,7 +709,15 @@ public function isBound() */ public function isSynchronized() { - return $this->synchronized; + return null === $this->transformationFailure; + } + + /** + * {@inheritdoc} + */ + public function getTransformationFailure() + { + return $this->transformationFailure; } /** diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 274ce04e620ac..1b9e6e47775d9 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Form; +use Symfony\Component\Form\Exception\TransformationFailedException; + /** * A form group bundling multiple forms in a hierarchical structure. * @@ -230,10 +232,20 @@ public function isEmpty(); /** * Returns whether the data in the different formats is synchronized. * + * If the data is not synchronized, you can get the transformation failure + * by calling {@link getTransformationFailure()}. + * * @return bool */ public function isSynchronized(); + /** + * Returns the data transformation failure, if any. + * + * @return TransformationFailedException|null The transformation failure + */ + public function getTransformationFailure(); + /** * Initializes the form tree. * diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php index 77b2d4472a061..deb6069ee2f5f 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php @@ -319,7 +319,7 @@ public function testExtractSubmittedDataStoresErrors() 'norm' => "'Foobar'", ), 'errors' => array( - array('message' => 'Invalid!', 'origin' => null, 'cause' => null), + array('message' => 'Invalid!', 'origin' => null, 'trace' => array()), ), 'synchronized' => 'true', ), $this->dataExtractor->extractSubmittedData($form)); @@ -340,7 +340,7 @@ public function testExtractSubmittedDataStoresErrorOrigin() 'norm' => "'Foobar'", ), 'errors' => array( - array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'cause' => null), + array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'trace' => array()), ), 'synchronized' => 'true', ), $this->dataExtractor->extractSubmittedData($form)); @@ -360,7 +360,12 @@ public function testExtractSubmittedDataStoresErrorCause() 'norm' => "'Foobar'", ), 'errors' => array( - array('message' => 'Invalid!', 'origin' => null, 'cause' => 'object(Exception)'), + array('message' => 'Invalid!', 'origin' => null, 'trace' => array( + array( + 'class' => "'Exception'", + 'message' => "''", + ), + )), ), 'synchronized' => 'true', ), $this->dataExtractor->extractSubmittedData($form)); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php index 99c5e584c1ae3..877797f163890 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -225,11 +225,14 @@ function () { throw new TransformationFailedException(); } $this->validator->validate($form, new Form()); + $is2Dot4Api = Validation::API_VERSION_2_4 === $this->getApiVersion(); + $this->buildViolation('invalid_message_key') ->setParameter('{{ value }}', 'foo') ->setParameter('{{ foo }}', 'bar') ->setInvalidValue('foo') ->setCode(Form::ERR_INVALID) + ->setCause($is2Dot4Api ? null : $form->getTransformationFailure()) ->assertRaised(); } @@ -259,11 +262,14 @@ function () { throw new TransformationFailedException(); } $this->validator->validate($form, new Form()); + $is2Dot4Api = Validation::API_VERSION_2_4 === $this->getApiVersion(); + $this->buildViolation('invalid_message_key') ->setParameter('{{ value }}', 'foo') ->setParameter('{{ foo }}', 'bar') ->setInvalidValue('foo') ->setCode(Form::ERR_INVALID) + ->setCause($is2Dot4Api ? null : $form->getTransformationFailure()) ->assertRaised(); } @@ -293,10 +299,13 @@ function () { throw new TransformationFailedException(); } $this->validator->validate($form, new Form()); + $is2Dot4Api = Validation::API_VERSION_2_4 === $this->getApiVersion(); + $this->buildViolation('invalid_message_key') ->setParameter('{{ value }}', 'foo') ->setInvalidValue('foo') ->setCode(Form::ERR_INVALID) + ->setCause($is2Dot4Api ? null : $form->getTransformationFailure()) ->assertRaised(); } From 3b50bf23a1b4c22c096036ba21e823ac347fe233 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Wed, 24 Sep 2014 12:08:18 +0200 Subject: [PATCH 246/373] [Validator] Added error codes to all constraints with multiple error causes --- .../Extension/Validator/Constraints/Form.php | 11 +- .../Validator/Constraints/FormValidator.php | 3 +- .../EventListener/ValidationListener.php | 2 +- .../Constraints/FormValidatorTest.php | 14 +- .../EventListener/ValidationListenerTest.php | 4 +- .../Component/Validator/Constraint.php | 29 +++ .../Validator/Constraints/CardScheme.php | 9 + .../Constraints/CardSchemeValidator.php | 2 + .../Validator/Constraints/Choice.php | 10 + .../Validator/Constraints/ChoiceValidator.php | 4 + .../Validator/Constraints/Collection.php | 9 +- .../Constraints/CollectionValidator.php | 2 + .../Component/Validator/Constraints/Count.php | 8 + .../Validator/Constraints/CountValidator.php | 2 + .../Component/Validator/Constraints/Date.php | 8 + .../Validator/Constraints/DateTime.php | 10 + .../Constraints/DateTimeValidator.php | 3 + .../Validator/Constraints/DateValidator.php | 2 + .../Component/Validator/Constraints/Email.php | 10 + .../Validator/Constraints/EmailValidator.php | 4 + .../Component/Validator/Constraints/File.php | 16 ++ .../Validator/Constraints/FileValidator.php | 23 +- .../Component/Validator/Constraints/Iban.php | 15 ++ .../Validator/Constraints/IbanValidator.php | 5 + .../Component/Validator/Constraints/Image.php | 34 +++ .../Validator/Constraints/ImageValidator.php | 10 + .../Component/Validator/Constraints/Isbn.php | 15 ++ .../Validator/Constraints/IsbnValidator.php | 58 ++++- .../Component/Validator/Constraints/Issn.php | 17 ++ .../Validator/Constraints/IssnValidator.php | 7 + .../Validator/Constraints/Length.php | 8 + .../Validator/Constraints/LengthValidator.php | 2 + .../Component/Validator/Constraints/Luhn.php | 9 + .../Validator/Constraints/LuhnValidator.php | 2 + .../Component/Validator/Constraints/Range.php | 10 + .../Validator/Constraints/RangeValidator.php | 3 + .../Component/Validator/Constraints/Time.php | 8 + .../Validator/Constraints/TimeValidator.php | 2 + .../Component/Validator/Constraints/Uuid.php | 17 ++ .../Validator/Constraints/UuidValidator.php | 201 ++++++++++++++++-- .../Validator/Tests/ConstraintTest.php | 9 + .../Constraints/CardSchemeValidatorTest.php | 31 +-- .../Tests/Constraints/ChoiceValidatorTest.php | 6 + .../Constraints/CollectionValidatorTest.php | 3 + .../Tests/Constraints/CountValidatorTest.php | 4 + .../Constraints/DateTimeValidatorTest.php | 23 +- .../Tests/Constraints/DateValidatorTest.php | 15 +- .../Tests/Constraints/EmailValidatorTest.php | 1 + .../Constraints/FileValidatorPathTest.php | 1 + .../Tests/Constraints/FileValidatorTest.php | 22 +- .../Tests/Constraints/IbanValidatorTest.php | 29 +-- .../Tests/Constraints/ImageValidatorTest.php | 24 +++ .../Tests/Constraints/IsbnValidatorTest.php | 70 +++--- .../Tests/Constraints/IssnValidatorTest.php | 21 +- .../Tests/Constraints/LengthValidatorTest.php | 4 + .../Tests/Constraints/LuhnValidatorTest.php | 13 +- .../Tests/Constraints/RangeValidatorTest.php | 41 ++-- .../Tests/Constraints/TimeValidatorTest.php | 17 +- .../Tests/Constraints/UuidValidatorTest.php | 119 ++++++----- .../Tests/Validator/Abstract2Dot5ApiTest.php | 4 +- .../ConstraintViolationBuilderInterface.php | 4 +- 61 files changed, 845 insertions(+), 224 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/Form.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/Form.php index 87e3329765530..da1a92b5e40bc 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/Form.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/Form.php @@ -18,11 +18,20 @@ */ class Form extends Constraint { + const NOT_SYNCHRONIZED_ERROR = 1; + const NO_SUCH_FIELD_ERROR = 2; + /** - * Violation code marking an invalid form. + * @deprecated Deprecated since Symfony 2.6, to be removed in 3.0. Use + * {@self NOT_SYNCHRONIZED_ERROR} instead. */ const ERR_INVALID = 1; + protected static $errorNames = array( + self::NOT_SYNCHRONIZED_ERROR => 'NOT_SYNCHRONIZED_ERROR', + self::NO_SUCH_FIELD_ERROR => 'NO_SUCH_FIELD_ERROR', + ); + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index 7c0dc44ab1808..d4e6f466fcaed 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -102,7 +102,7 @@ public function validate($form, Constraint $constraint) $this->buildViolation($config->getOption('invalid_message')) ->setParameters(array_replace(array('{{ value }}' => $clientDataAsString), $config->getOption('invalid_message_parameters'))) ->setInvalidValue($form->getViewData()) - ->setCode(Form::ERR_INVALID) + ->setCode(Form::NOT_SYNCHRONIZED_ERROR) ->setCause($form->getTransformationFailure()) ->addViolation(); } @@ -113,6 +113,7 @@ public function validate($form, Constraint $constraint) $this->buildViolation($config->getOption('extra_fields_message')) ->setParameter('{{ extra_fields }}', implode('", "', array_keys($form->getExtraData()))) ->setInvalidValue($form->getExtraData()) + ->setCode(Form::NO_SUCH_FIELD_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php b/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php index 62c59ddc43232..37f194380a4e5 100644 --- a/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php +++ b/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php @@ -66,7 +66,7 @@ public function validateForm(FormEvent $event) foreach ($violations as $violation) { // Allow the "invalid" constraint to be put onto // non-synchronized forms - $allowNonSynchronized = Form::ERR_INVALID === $violation->getCode(); + $allowNonSynchronized = $violation->getConstraint() instanceof Form && Form::NOT_SYNCHRONIZED_ERROR === $violation->getCode(); $this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php index 877797f163890..5fa4f39f09a43 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -231,7 +231,7 @@ function () { throw new TransformationFailedException(); } ->setParameter('{{ value }}', 'foo') ->setParameter('{{ foo }}', 'bar') ->setInvalidValue('foo') - ->setCode(Form::ERR_INVALID) + ->setCode(Form::NOT_SYNCHRONIZED_ERROR) ->setCause($is2Dot4Api ? null : $form->getTransformationFailure()) ->assertRaised(); } @@ -268,7 +268,7 @@ function () { throw new TransformationFailedException(); } ->setParameter('{{ value }}', 'foo') ->setParameter('{{ foo }}', 'bar') ->setInvalidValue('foo') - ->setCode(Form::ERR_INVALID) + ->setCode(Form::NOT_SYNCHRONIZED_ERROR) ->setCause($is2Dot4Api ? null : $form->getTransformationFailure()) ->assertRaised(); } @@ -304,7 +304,7 @@ function () { throw new TransformationFailedException(); } $this->buildViolation('invalid_message_key') ->setParameter('{{ value }}', 'foo') ->setInvalidValue('foo') - ->setCode(Form::ERR_INVALID) + ->setCode(Form::NOT_SYNCHRONIZED_ERROR) ->setCause($is2Dot4Api ? null : $form->getTransformationFailure()) ->assertRaised(); } @@ -558,9 +558,11 @@ public function testViolationIfExtraData() $this->validator->validate($form, new Form()); - $this->assertViolation('Extra!', array( - '{{ extra_fields }}' => 'foo', - ), 'property.path', array('foo' => 'bar')); + $this->buildViolation('Extra!') + ->setParameter('{{ extra_fields }}', 'foo') + ->setInvalidValue(array('foo' => 'bar')) + ->setCode(Form::NO_SUCH_FIELD_ERROR) + ->assertRaised(); } public function testNoViolationIfAllowExtraData() diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php index d819277db08d9..845dc85025782 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php @@ -66,7 +66,7 @@ protected function setUp() private function getConstraintViolation($code = null) { - return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, 'prop.path', null, null, $code); + return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, 'prop.path', null, null, $code, new Form()); } private function getBuilder($name = 'name', $propertyPath = null, $dataClass = null) @@ -109,7 +109,7 @@ public function testMapViolation() public function testMapViolationAllowsNonSyncIfInvalid() { - $violation = $this->getConstraintViolation(Form::ERR_INVALID); + $violation = $this->getConstraintViolation(Form::NOT_SYNCHRONIZED_ERROR); $form = $this->getForm('street'); $this->validator->expects($this->once()) diff --git a/src/Symfony/Component/Validator/Constraint.php b/src/Symfony/Component/Validator/Constraint.php index b6bc216159cd8..b08ddfba4180e 100644 --- a/src/Symfony/Component/Validator/Constraint.php +++ b/src/Symfony/Component/Validator/Constraint.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\Exception\InvalidArgumentException; use Symfony\Component\Validator\Exception\InvalidOptionsException; use Symfony\Component\Validator\Exception\MissingOptionsException; @@ -50,12 +51,40 @@ abstract class Constraint */ const PROPERTY_CONSTRAINT = 'property'; + /** + * Maps error codes to the names of their constants + * @var array + */ + protected static $errorNames = array(); + /** * Domain-specific data attached to a constraint * @var mixed */ public $payload; + /** + * Returns the name of the given error code. + * + * @param int $errorCode The error code + * + * @return string The name of the error code + * + * @throws InvalidArgumentException If the error code does not exist + */ + public static function getErrorName($errorCode) + { + if (!isset(static::$errorNames[$errorCode])) { + throw new InvalidArgumentException(sprintf( + 'The error code "%s" does not exist for constraint of type "%s".', + $errorCode, + get_called_class() + )); + } + + return static::$errorNames[$errorCode]; + } + /** * Initializes the constraint with options. * diff --git a/src/Symfony/Component/Validator/Constraints/CardScheme.php b/src/Symfony/Component/Validator/Constraints/CardScheme.php index 26a0e1c2fd466..14f3b5d8cf0c1 100644 --- a/src/Symfony/Component/Validator/Constraints/CardScheme.php +++ b/src/Symfony/Component/Validator/Constraints/CardScheme.php @@ -20,9 +20,18 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Tim Nagel + * @author Bernhard Schussek */ class CardScheme extends Constraint { + const NOT_NUMERIC_ERROR = 1; + const INVALID_FORMAT_ERROR = 2; + + protected static $errorNames = array( + self::NOT_NUMERIC_ERROR => 'NOT_NUMERIC_ERROR', + self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', + ); + public $message = 'Unsupported card type or invalid card number.'; public $schemes; diff --git a/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php b/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php index 7cf7fa4bfe04c..62492e61eaae5 100644 --- a/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php @@ -117,6 +117,7 @@ public function validate($value, Constraint $constraint) if (!is_numeric($value)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(CardScheme::NOT_NUMERIC_ERROR) ->addViolation(); return; @@ -135,6 +136,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(CardScheme::INVALID_FORMAT_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Choice.php b/src/Symfony/Component/Validator/Constraints/Choice.php index 467b58630310b..39a64574d0d29 100644 --- a/src/Symfony/Component/Validator/Constraints/Choice.php +++ b/src/Symfony/Component/Validator/Constraints/Choice.php @@ -23,6 +23,16 @@ */ class Choice extends Constraint { + const NO_SUCH_CHOICE_ERROR = 1; + const TOO_FEW_ERROR = 2; + const TOO_MANY_ERROR = 3; + + protected static $errorNames = array( + self::NO_SUCH_CHOICE_ERROR => 'NO_SUCH_CHOICE_ERROR', + self::TOO_FEW_ERROR => 'TOO_FEW_ERROR', + self::TOO_MANY_ERROR => 'TOO_MANY_ERROR', + ); + public $choices; public $callback; public $multiple = false; diff --git a/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php b/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php index 5640c733173a5..1566c318c3def 100644 --- a/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php @@ -65,6 +65,7 @@ public function validate($value, Constraint $constraint) if (!in_array($_value, $choices, $constraint->strict)) { $this->buildViolation($constraint->multipleMessage) ->setParameter('{{ value }}', $this->formatValue($_value)) + ->setCode(Choice::NO_SUCH_CHOICE_ERROR) ->setInvalidValue($_value) ->addViolation(); @@ -78,6 +79,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->minMessage) ->setParameter('{{ limit }}', $constraint->min) ->setPlural((int) $constraint->min) + ->setCode(Choice::TOO_FEW_ERROR) ->addViolation(); return; @@ -87,6 +89,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->maxMessage) ->setParameter('{{ limit }}', $constraint->max) ->setPlural((int) $constraint->max) + ->setCode(Choice::TOO_MANY_ERROR) ->addViolation(); return; @@ -94,6 +97,7 @@ public function validate($value, Constraint $constraint) } elseif (!in_array($value, $choices, $constraint->strict)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Choice::NO_SUCH_CHOICE_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Collection.php b/src/Symfony/Component/Validator/Constraints/Collection.php index 9917c527c0ae6..708c8ed47730e 100644 --- a/src/Symfony/Component/Validator/Constraints/Collection.php +++ b/src/Symfony/Component/Validator/Constraints/Collection.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Validator\Constraints; -use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; /** @@ -24,6 +23,14 @@ */ class Collection extends Composite { + const MISSING_FIELD_ERROR = 1; + const NO_SUCH_FIELD_ERROR = 2; + + protected static $errorNames = array( + self::MISSING_FIELD_ERROR => 'MISSING_FIELD_ERROR', + self::NO_SUCH_FIELD_ERROR => 'NO_SUCH_FIELD_ERROR', + ); + public $fields = array(); public $allowExtraFields = false; public $allowMissingFields = false; diff --git a/src/Symfony/Component/Validator/Constraints/CollectionValidator.php b/src/Symfony/Component/Validator/Constraints/CollectionValidator.php index 5ff67f9fe770c..c3180d21622b9 100644 --- a/src/Symfony/Component/Validator/Constraints/CollectionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CollectionValidator.php @@ -73,6 +73,7 @@ public function validate($value, Constraint $constraint) ->atPath('['.$field.']') ->setParameter('{{ field }}', $this->formatValue($field)) ->setInvalidValue(null) + ->setCode(Collection::MISSING_FIELD_ERROR) ->addViolation(); } } @@ -84,6 +85,7 @@ public function validate($value, Constraint $constraint) ->atPath('['.$field.']') ->setParameter('{{ field }}', $this->formatValue($field)) ->setInvalidValue($fieldValue) + ->setCode(Collection::NO_SUCH_FIELD_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Count.php b/src/Symfony/Component/Validator/Constraints/Count.php index 1d64344b4ab6a..a3e12fe1342d8 100644 --- a/src/Symfony/Component/Validator/Constraints/Count.php +++ b/src/Symfony/Component/Validator/Constraints/Count.php @@ -24,6 +24,14 @@ */ class Count extends Constraint { + const TOO_FEW_ERROR = 1; + const TOO_MANY_ERROR = 2; + + protected static $errorNames = array( + self::TOO_FEW_ERROR => 'TOO_FEW_ERROR', + self::TOO_MANY_ERROR => 'TOO_MANY_ERROR', + ); + public $minMessage = 'This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.'; public $maxMessage = 'This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.'; public $exactMessage = 'This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.'; diff --git a/src/Symfony/Component/Validator/Constraints/CountValidator.php b/src/Symfony/Component/Validator/Constraints/CountValidator.php index 0f40a3f1e0deb..d44f537071a3b 100644 --- a/src/Symfony/Component/Validator/Constraints/CountValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CountValidator.php @@ -41,6 +41,7 @@ public function validate($value, Constraint $constraint) ->setParameter('{{ limit }}', $constraint->max) ->setInvalidValue($value) ->setPlural((int) $constraint->max) + ->setCode(Count::TOO_MANY_ERROR) ->addViolation(); return; @@ -52,6 +53,7 @@ public function validate($value, Constraint $constraint) ->setParameter('{{ limit }}', $constraint->min) ->setInvalidValue($value) ->setPlural((int) $constraint->min) + ->setCode(Count::TOO_FEW_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Date.php b/src/Symfony/Component/Validator/Constraints/Date.php index 9e29168965153..2bc444f71af68 100644 --- a/src/Symfony/Component/Validator/Constraints/Date.php +++ b/src/Symfony/Component/Validator/Constraints/Date.php @@ -23,5 +23,13 @@ */ class Date extends Constraint { + const INVALID_FORMAT_ERROR = 1; + const INVALID_DATE_ERROR = 2; + + protected static $errorNames = array( + self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', + self::INVALID_DATE_ERROR => 'INVALID_DATE_ERROR', + ); + public $message = 'This value is not a valid date.'; } diff --git a/src/Symfony/Component/Validator/Constraints/DateTime.php b/src/Symfony/Component/Validator/Constraints/DateTime.php index 1657f43afcd15..ae67ff30efb4b 100644 --- a/src/Symfony/Component/Validator/Constraints/DateTime.php +++ b/src/Symfony/Component/Validator/Constraints/DateTime.php @@ -23,5 +23,15 @@ */ class DateTime extends Constraint { + const INVALID_FORMAT_ERROR = 1; + const INVALID_DATE_ERROR = 2; + const INVALID_TIME_ERROR = 3; + + protected static $errorNames = array( + self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', + self::INVALID_DATE_ERROR => 'INVALID_DATE_ERROR', + self::INVALID_TIME_ERROR => 'INVALID_TIME_ERROR', + ); + public $message = 'This value is not a valid datetime.'; } diff --git a/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php b/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php index 88c8e25862294..b459c7873f7cb 100644 --- a/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php @@ -45,6 +45,7 @@ public function validate($value, Constraint $constraint) if (!preg_match(static::PATTERN, $value, $matches)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(DateTime::INVALID_FORMAT_ERROR) ->addViolation(); return; @@ -53,12 +54,14 @@ public function validate($value, Constraint $constraint) if (!DateValidator::checkDate($matches[1], $matches[2], $matches[3])) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(DateTime::INVALID_DATE_ERROR) ->addViolation(); } if (!TimeValidator::checkTime($matches[4], $matches[5], $matches[6])) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(DateTime::INVALID_TIME_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/DateValidator.php b/src/Symfony/Component/Validator/Constraints/DateValidator.php index e1640b13b3858..77f0111666616 100644 --- a/src/Symfony/Component/Validator/Constraints/DateValidator.php +++ b/src/Symfony/Component/Validator/Constraints/DateValidator.php @@ -62,6 +62,7 @@ public function validate($value, Constraint $constraint) if (!preg_match(static::PATTERN, $value, $matches)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Date::INVALID_FORMAT_ERROR) ->addViolation(); return; @@ -70,6 +71,7 @@ public function validate($value, Constraint $constraint) if (!self::checkDate($matches[1], $matches[2], $matches[3])) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Date::INVALID_DATE_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Email.php b/src/Symfony/Component/Validator/Constraints/Email.php index 1f8755750bc24..36977177bdfd1 100644 --- a/src/Symfony/Component/Validator/Constraints/Email.php +++ b/src/Symfony/Component/Validator/Constraints/Email.php @@ -23,6 +23,16 @@ */ class Email extends Constraint { + const INVALID_FORMAT_ERROR = 1; + const MX_CHECK_FAILED_ERROR = 2; + const HOST_CHECK_FAILED_ERROR = 3; + + protected static $errorNames = array( + self::INVALID_FORMAT_ERROR => 'STRICT_CHECK_FAILED_ERROR', + self::MX_CHECK_FAILED_ERROR => 'MX_CHECK_FAILED_ERROR', + self::HOST_CHECK_FAILED_ERROR => 'HOST_CHECK_FAILED_ERROR', + ); + public $message = 'This value is not a valid email address.'; public $checkMX = false; public $checkHost = false; diff --git a/src/Symfony/Component/Validator/Constraints/EmailValidator.php b/src/Symfony/Component/Validator/Constraints/EmailValidator.php index 1f487ca1c4a46..ee588dca9f8cf 100644 --- a/src/Symfony/Component/Validator/Constraints/EmailValidator.php +++ b/src/Symfony/Component/Validator/Constraints/EmailValidator.php @@ -68,6 +68,7 @@ public function validate($value, Constraint $constraint) if (!$strictValidator->isValid($value, false, true)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Email::INVALID_FORMAT_ERROR) ->addViolation(); return; @@ -75,6 +76,7 @@ public function validate($value, Constraint $constraint) } elseif (!preg_match('/.+\@.+\..+/', $value)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Email::INVALID_FORMAT_ERROR) ->addViolation(); return; @@ -87,6 +89,7 @@ public function validate($value, Constraint $constraint) if (!$this->checkMX($host)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Email::MX_CHECK_FAILED_ERROR) ->addViolation(); } @@ -96,6 +99,7 @@ public function validate($value, Constraint $constraint) if ($constraint->checkHost && !$this->checkHost($host)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Email::HOST_CHECK_FAILED_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/File.php b/src/Symfony/Component/Validator/Constraints/File.php index 9ed818c13bed5..6bfea29ed968f 100644 --- a/src/Symfony/Component/Validator/Constraints/File.php +++ b/src/Symfony/Component/Validator/Constraints/File.php @@ -24,6 +24,22 @@ */ class File extends Constraint { + // Check the Image constraint for clashes if adding new constants here + + const NOT_FOUND_ERROR = 1; + const NOT_READABLE_ERROR = 2; + const EMPTY_ERROR = 3; + const TOO_LARGE_ERROR = 4; + const INVALID_MIME_TYPE_ERROR = 5; + + protected static $errorNames = array( + self::NOT_FOUND_ERROR => 'NOT_FOUND_ERROR', + self::NOT_READABLE_ERROR => 'NOT_READABLE_ERROR', + self::EMPTY_ERROR => 'EMPTY_ERROR', + self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR', + self::INVALID_MIME_TYPE_ERROR => 'INVALID_MIME_TYPE_ERROR', + ); + public $maxSize; public $binaryFormat; public $mimeTypes = array(); diff --git a/src/Symfony/Component/Validator/Constraints/FileValidator.php b/src/Symfony/Component/Validator/Constraints/FileValidator.php index 78d6efe3ca4a0..808a9885d010e 100644 --- a/src/Symfony/Component/Validator/Constraints/FileValidator.php +++ b/src/Symfony/Component/Validator/Constraints/FileValidator.php @@ -62,36 +62,43 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->uploadIniSizeErrorMessage) ->setParameter('{{ limit }}', $limitInBytes) ->setParameter('{{ suffix }}', 'bytes') + ->setCode(UPLOAD_ERR_INI_SIZE) ->addViolation(); return; case UPLOAD_ERR_FORM_SIZE: $this->buildViolation($constraint->uploadFormSizeErrorMessage) + ->setCode(UPLOAD_ERR_FORM_SIZE) ->addViolation(); return; case UPLOAD_ERR_PARTIAL: $this->buildViolation($constraint->uploadPartialErrorMessage) + ->setCode(UPLOAD_ERR_PARTIAL) ->addViolation(); return; case UPLOAD_ERR_NO_FILE: $this->buildViolation($constraint->uploadNoFileErrorMessage) + ->setCode(UPLOAD_ERR_NO_FILE) ->addViolation(); return; case UPLOAD_ERR_NO_TMP_DIR: $this->buildViolation($constraint->uploadNoTmpDirErrorMessage) + ->setCode(UPLOAD_ERR_NO_TMP_DIR) ->addViolation(); return; case UPLOAD_ERR_CANT_WRITE: $this->buildViolation($constraint->uploadCantWriteErrorMessage) + ->setCode(UPLOAD_ERR_CANT_WRITE) ->addViolation(); return; case UPLOAD_ERR_EXTENSION: $this->buildViolation($constraint->uploadExtensionErrorMessage) + ->setCode(UPLOAD_ERR_EXTENSION) ->addViolation(); return; @@ -113,6 +120,7 @@ public function validate($value, Constraint $constraint) if (!is_file($path)) { $this->buildViolation($constraint->notFoundMessage) ->setParameter('{{ file }}', $this->formatValue($path)) + ->setCode(File::NOT_FOUND_ERROR) ->addViolation(); return; @@ -121,15 +129,24 @@ public function validate($value, Constraint $constraint) if (!is_readable($path)) { $this->buildViolation($constraint->notReadableMessage) ->setParameter('{{ file }}', $this->formatValue($path)) + ->setCode(File::NOT_READABLE_ERROR) ->addViolation(); return; } $sizeInBytes = filesize($path); + if (0 === $sizeInBytes) { - $this->context->addViolation($constraint->disallowEmptyMessage); - } elseif ($constraint->maxSize) { + $this->buildViolation($constraint->disallowEmptyMessage) + ->setParameter('{{ file }}', $this->formatValue($path)) + ->setCode(File::EMPTY_ERROR) + ->addViolation(); + + return; + } + + if ($constraint->maxSize) { $limitInBytes = $constraint->maxSize; if ($sizeInBytes > $limitInBytes) { @@ -168,6 +185,7 @@ public function validate($value, Constraint $constraint) ->setParameter('{{ size }}', $sizeAsString) ->setParameter('{{ limit }}', $limitAsString) ->setParameter('{{ suffix }}', self::$suffices[$coef]) + ->setCode(File::TOO_LARGE_ERROR) ->addViolation(); return; @@ -198,6 +216,7 @@ public function validate($value, Constraint $constraint) ->setParameter('{{ file }}', $this->formatValue($path)) ->setParameter('{{ type }}', $this->formatValue($mime)) ->setParameter('{{ types }}', $this->formatValues($mimeTypes)) + ->setCode(File::INVALID_MIME_TYPE_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Iban.php b/src/Symfony/Component/Validator/Constraints/Iban.php index 895a93cd5860e..66ce09ae1a630 100644 --- a/src/Symfony/Component/Validator/Constraints/Iban.php +++ b/src/Symfony/Component/Validator/Constraints/Iban.php @@ -19,8 +19,23 @@ * * @author Manuel Reinhard * @author Michael Schummel + * @author Bernhard Schussek */ class Iban extends Constraint { + const TOO_SHORT_ERROR = 1; + const INVALID_COUNTRY_CODE_ERROR = 2; + const INVALID_CHARACTERS_ERROR = 3; + const INVALID_CASE_ERROR = 4; + const CHECKSUM_FAILED_ERROR = 5; + + protected static $errorNames = array( + self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', + self::INVALID_COUNTRY_CODE_ERROR => 'INVALID_COUNTRY_CODE_ERROR', + self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', + self::INVALID_CASE_ERROR => 'INVALID_CASE_ERROR', + self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR', + ); + public $message = 'This is not a valid International Bank Account Number (IBAN).'; } diff --git a/src/Symfony/Component/Validator/Constraints/IbanValidator.php b/src/Symfony/Component/Validator/Constraints/IbanValidator.php index b66561dc2ade1..c8bcf4b1ab096 100644 --- a/src/Symfony/Component/Validator/Constraints/IbanValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IbanValidator.php @@ -49,6 +49,7 @@ public function validate($value, Constraint $constraint) if (strlen($canonicalized) < 4) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Iban::TOO_SHORT_ERROR) ->addViolation(); return; @@ -58,6 +59,7 @@ public function validate($value, Constraint $constraint) if (!ctype_alpha($canonicalized{0}) || !ctype_alpha($canonicalized{1})) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Iban::INVALID_COUNTRY_CODE_ERROR) ->addViolation(); return; @@ -67,6 +69,7 @@ public function validate($value, Constraint $constraint) if (!ctype_alnum($canonicalized)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Iban::INVALID_CHARACTERS_ERROR) ->addViolation(); return; @@ -76,6 +79,7 @@ public function validate($value, Constraint $constraint) if ($canonicalized !== strtoupper($canonicalized)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Iban::INVALID_CASE_ERROR) ->addViolation(); return; @@ -99,6 +103,7 @@ public function validate($value, Constraint $constraint) if (1 !== $this->bigModulo97($checkSum)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Iban::CHECKSUM_FAILED_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Image.php b/src/Symfony/Component/Validator/Constraints/Image.php index 9804fd90cc1c5..904ef97b492b1 100644 --- a/src/Symfony/Component/Validator/Constraints/Image.php +++ b/src/Symfony/Component/Validator/Constraints/Image.php @@ -16,11 +16,45 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Benjamin Dulau + * @author Bernhard Schussek * * @api */ class Image extends File { + // Don't reuse values used in File + + const SIZE_NOT_DETECTED_ERROR = 10; + const TOO_WIDE_ERROR = 11; + const TOO_NARROW_ERROR = 12; + const TOO_HIGH_ERROR = 13; + const TOO_LOW_ERROR = 14; + const RATIO_TOO_BIG_ERROR = 15; + const RATIO_TOO_SMALL_ERROR = 16; + const SQUARE_NOT_ALLOWED_ERROR = 17; + const LANDSCAPE_NOT_ALLOWED_ERROR = 18; + const PORTRAIT_NOT_ALLOWED_ERROR = 19; + + // Include the mapping from the base class + + protected static $errorNames = array( + self::NOT_FOUND_ERROR => 'NOT_FOUND_ERROR', + self::NOT_READABLE_ERROR => 'NOT_READABLE_ERROR', + self::EMPTY_ERROR => 'EMPTY_ERROR', + self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR', + self::INVALID_MIME_TYPE_ERROR => 'INVALID_MIME_TYPE_ERROR', + self::SIZE_NOT_DETECTED_ERROR => 'SIZE_NOT_DETECTED_ERROR', + self::TOO_WIDE_ERROR => 'TOO_WIDE_ERROR', + self::TOO_NARROW_ERROR => 'TOO_NARROW_ERROR', + self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', + self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', + self::RATIO_TOO_BIG_ERROR => 'RATIO_TOO_BIG_ERROR', + self::RATIO_TOO_SMALL_ERROR => 'RATIO_TOO_SMALL_ERROR', + self::SQUARE_NOT_ALLOWED_ERROR => 'SQUARE_NOT_ALLOWED_ERROR', + self::LANDSCAPE_NOT_ALLOWED_ERROR => 'LANDSCAPE_NOT_ALLOWED_ERROR', + self::PORTRAIT_NOT_ALLOWED_ERROR => 'PORTRAIT_NOT_ALLOWED_ERROR', + ); + public $mimeTypes = 'image/*'; public $minWidth; public $maxWidth; diff --git a/src/Symfony/Component/Validator/Constraints/ImageValidator.php b/src/Symfony/Component/Validator/Constraints/ImageValidator.php index 4eaed687dc985..3444c38967347 100644 --- a/src/Symfony/Component/Validator/Constraints/ImageValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ImageValidator.php @@ -54,6 +54,7 @@ public function validate($value, Constraint $constraint) if (empty($size) || ($size[0] === 0) || ($size[1] === 0)) { $this->buildViolation($constraint->sizeNotDetectedMessage) + ->setCode(Image::SIZE_NOT_DETECTED_ERROR) ->addViolation(); return; @@ -71,6 +72,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->minWidthMessage) ->setParameter('{{ width }}', $width) ->setParameter('{{ min_width }}', $constraint->minWidth) + ->setCode(Image::TOO_NARROW_ERROR) ->addViolation(); return; @@ -86,6 +88,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->maxWidthMessage) ->setParameter('{{ width }}', $width) ->setParameter('{{ max_width }}', $constraint->maxWidth) + ->setCode(Image::TOO_WIDE_ERROR) ->addViolation(); return; @@ -101,6 +104,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->minHeightMessage) ->setParameter('{{ height }}', $height) ->setParameter('{{ min_height }}', $constraint->minHeight) + ->setCode(Image::TOO_LOW_ERROR) ->addViolation(); return; @@ -116,6 +120,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->maxHeightMessage) ->setParameter('{{ height }}', $height) ->setParameter('{{ max_height }}', $constraint->maxHeight) + ->setCode(Image::TOO_HIGH_ERROR) ->addViolation(); } } @@ -131,6 +136,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->minRatioMessage) ->setParameter('{{ ratio }}', $ratio) ->setParameter('{{ min_ratio }}', $constraint->minRatio) + ->setCode(Image::RATIO_TOO_SMALL_ERROR) ->addViolation(); } } @@ -144,6 +150,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->maxRatioMessage) ->setParameter('{{ ratio }}', $ratio) ->setParameter('{{ max_ratio }}', $constraint->maxRatio) + ->setCode(Image::RATIO_TOO_BIG_ERROR) ->addViolation(); } } @@ -152,6 +159,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->allowSquareMessage) ->setParameter('{{ width }}', $width) ->setParameter('{{ height }}', $height) + ->setCode(Image::SQUARE_NOT_ALLOWED_ERROR) ->addViolation(); } @@ -159,6 +167,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->allowLandscapeMessage) ->setParameter('{{ width }}', $width) ->setParameter('{{ height }}', $height) + ->setCode(Image::LANDSCAPE_NOT_ALLOWED_ERROR) ->addViolation(); } @@ -166,6 +175,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->allowPortraitMessage) ->setParameter('{{ width }}', $width) ->setParameter('{{ height }}', $height) + ->setCode(Image::PORTRAIT_NOT_ALLOWED_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Isbn.php b/src/Symfony/Component/Validator/Constraints/Isbn.php index 90abedd2a8060..67d177f4e0439 100644 --- a/src/Symfony/Component/Validator/Constraints/Isbn.php +++ b/src/Symfony/Component/Validator/Constraints/Isbn.php @@ -19,9 +19,24 @@ * * @author The Whole Life To Learn * @author Manuel Reinhard + * @author Bernhard Schussek */ class Isbn extends Constraint { + const TOO_SHORT_ERROR = 1; + const TOO_LONG_ERROR = 2; + const INVALID_CHARACTERS_ERROR = 3; + const CHECKSUM_FAILED_ERROR = 4; + const TYPE_NOT_RECOGNIZED_ERROR = 5; + + protected static $errorNames = array( + self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', + self::TOO_LONG_ERROR => 'TOO_LONG_ERROR', + self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', + self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR', + self::TYPE_NOT_RECOGNIZED_ERROR => 'TYPE_NOT_RECOGNIZED_ERROR', + ); + public $isbn10Message = 'This value is not a valid ISBN-10.'; public $isbn13Message = 'This value is not a valid ISBN-13.'; public $bothIsbnMessage = 'This value is neither a valid ISBN-10 nor a valid ISBN-13.'; diff --git a/src/Symfony/Component/Validator/Constraints/IsbnValidator.php b/src/Symfony/Component/Validator/Constraints/IsbnValidator.php index 8972b91610406..8a53fe7941d19 100644 --- a/src/Symfony/Component/Validator/Constraints/IsbnValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IsbnValidator.php @@ -56,9 +56,10 @@ public function validate($value, Constraint $constraint) // Explicitly validate against ISBN-10 if ('isbn10' === $constraint->type) { - if (!$this->validateIsbn10($canonical)) { + if (true !== ($code = $this->validateIsbn10($canonical))) { $this->buildViolation($this->getMessage($constraint, $constraint->type)) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode($code) ->addViolation(); } @@ -67,9 +68,10 @@ public function validate($value, Constraint $constraint) // Explicitly validate against ISBN-13 if ('isbn13' === $constraint->type) { - if (!$this->validateIsbn13($canonical)) { + if (true !== ($code = $this->validateIsbn13($canonical))) { $this->buildViolation($this->getMessage($constraint, $constraint->type)) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode($code) ->addViolation(); } @@ -77,20 +79,49 @@ public function validate($value, Constraint $constraint) } // Try both ISBNs - if (!$this->validateIsbn10($canonical) && !$this->validateIsbn13($canonical)) { + + // First, try ISBN-10 + $code = $this->validateIsbn10($canonical); + + // The ISBN can only be an ISBN-13 if the value was too long for ISBN-10 + if (Isbn::TOO_LONG_ERROR === $code) { + // Try ISBN-13 now + $code = $this->validateIsbn13($canonical); + + // If too short, this means we have 11 or 12 digits + if (Isbn::TOO_SHORT_ERROR === $code) { + $code = Isbn::TYPE_NOT_RECOGNIZED_ERROR; + } + } + + if (true !== $code) { $this->buildViolation($this->getMessage($constraint)) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode($code) ->addViolation(); } } protected function validateIsbn10($isbn) { + // Choose an algorithm so that ERROR_INVALID_CHARACTERS is preferred + // over ERROR_TOO_SHORT/ERROR_TOO_LONG + // Otherwise "0-45122-5244" passes, but "0-45122_5244" reports + // "too long" + + // Error priority: + // 1. ERROR_INVALID_CHARACTERS + // 2. ERROR_TOO_SHORT/ERROR_TOO_LONG + // 3. ERROR_CHECKSUM_FAILED + $checkSum = 0; for ($i = 0; $i < 10; ++$i) { + // If we test the length before the loop, we get an ERROR_TOO_SHORT + // when actually an ERROR_INVALID_CHARACTERS is wanted, e.g. for + // "0-45122_5244" (typo) if (!isset($isbn{$i})) { - return false; + return Isbn::TOO_SHORT_ERROR; } if ('X' === $isbn{$i}) { @@ -98,33 +129,38 @@ protected function validateIsbn10($isbn) } elseif (ctype_digit($isbn{$i})) { $digit = $isbn{$i}; } else { - return false; + return Isbn::INVALID_CHARACTERS_ERROR; } $checkSum += $digit * intval(10 - $i); } if (isset($isbn{$i})) { - return false; + return Isbn::TOO_LONG_ERROR; } - return 0 === $checkSum % 11; + return 0 === $checkSum % 11 ? true : Isbn::CHECKSUM_FAILED_ERROR; } protected function validateIsbn13($isbn) { + // Error priority: + // 1. ERROR_INVALID_CHARACTERS + // 2. ERROR_TOO_SHORT/ERROR_TOO_LONG + // 3. ERROR_CHECKSUM_FAILED + if (!ctype_digit($isbn)) { - return false; + return Isbn::INVALID_CHARACTERS_ERROR; } $length = strlen($isbn); if ($length < 13) { - return false; + return Isbn::TOO_SHORT_ERROR; } if ($length > 13) { - return false; + return Isbn::TOO_LONG_ERROR; } $checkSum = 0; @@ -138,7 +174,7 @@ protected function validateIsbn13($isbn) * 3; } - return 0 === $checkSum % 10; + return 0 === $checkSum % 10 ? true : Isbn::CHECKSUM_FAILED_ERROR; } protected function getMessage($constraint, $type = null) diff --git a/src/Symfony/Component/Validator/Constraints/Issn.php b/src/Symfony/Component/Validator/Constraints/Issn.php index 01a0f4315f6b2..39716a28cce30 100644 --- a/src/Symfony/Component/Validator/Constraints/Issn.php +++ b/src/Symfony/Component/Validator/Constraints/Issn.php @@ -18,9 +18,26 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Antonio J. García Lagar + * @author Bernhard Schussek */ class Issn extends Constraint { + const TOO_SHORT_ERROR = 1; + const TOO_LONG_ERROR = 2; + const MISSING_HYPHEN_ERROR = 3; + const INVALID_CHARACTERS_ERROR = 4; + const INVALID_CASE_ERROR = 5; + const CHECKSUM_FAILED_ERROR = 6; + + protected static $errorNames = array( + self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', + self::TOO_LONG_ERROR => 'TOO_LONG_ERROR', + self::MISSING_HYPHEN_ERROR => 'MISSING_HYPHEN_ERROR', + self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', + self::INVALID_CASE_ERROR => 'INVALID_CASE_ERROR', + self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR', + ); + public $message = 'This value is not a valid ISSN.'; public $caseSensitive = false; public $requireHyphen = false; diff --git a/src/Symfony/Component/Validator/Constraints/IssnValidator.php b/src/Symfony/Component/Validator/Constraints/IssnValidator.php index 2ee8a53dfd7e5..e8f28e6a4fbcb 100644 --- a/src/Symfony/Component/Validator/Constraints/IssnValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IssnValidator.php @@ -53,6 +53,7 @@ public function validate($value, Constraint $constraint) } elseif ($constraint->requireHyphen) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Issn::MISSING_HYPHEN_ERROR) ->addViolation(); return; @@ -63,6 +64,7 @@ public function validate($value, Constraint $constraint) if ($length < 8) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Issn::TOO_SHORT_ERROR) ->addViolation(); return; @@ -71,6 +73,7 @@ public function validate($value, Constraint $constraint) if ($length > 8) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Issn::TOO_LONG_ERROR) ->addViolation(); return; @@ -81,6 +84,7 @@ public function validate($value, Constraint $constraint) if (!ctype_digit(substr($canonical, 0, 7))) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Issn::INVALID_CHARACTERS_ERROR) ->addViolation(); return; @@ -91,6 +95,7 @@ public function validate($value, Constraint $constraint) if (!ctype_digit($canonical{7}) && 'x' !== $canonical{7} && 'X' !== $canonical{7}) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Issn::INVALID_CHARACTERS_ERROR) ->addViolation(); return; @@ -101,6 +106,7 @@ public function validate($value, Constraint $constraint) if ($constraint->caseSensitive && 'x' === $canonical{7}) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Issn::INVALID_CASE_ERROR) ->addViolation(); return; @@ -119,6 +125,7 @@ public function validate($value, Constraint $constraint) if (0 !== $checkSum % 11) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Issn::CHECKSUM_FAILED_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Length.php b/src/Symfony/Component/Validator/Constraints/Length.php index b353e9b24d3fb..60a46cb8ea1fa 100644 --- a/src/Symfony/Component/Validator/Constraints/Length.php +++ b/src/Symfony/Component/Validator/Constraints/Length.php @@ -24,6 +24,14 @@ */ class Length extends Constraint { + const TOO_SHORT_ERROR = 1; + const TOO_LONG_ERROR = 2; + + protected static $errorNames = array( + self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', + self::TOO_LONG_ERROR => 'TOO_LONG_ERROR', + ); + public $maxMessage = 'This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.'; public $minMessage = 'This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.'; public $exactMessage = 'This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.'; diff --git a/src/Symfony/Component/Validator/Constraints/LengthValidator.php b/src/Symfony/Component/Validator/Constraints/LengthValidator.php index cdc5bea985df3..19f3d3937b502 100644 --- a/src/Symfony/Component/Validator/Constraints/LengthValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LengthValidator.php @@ -53,6 +53,7 @@ public function validate($value, Constraint $constraint) ->setParameter('{{ limit }}', $constraint->max) ->setInvalidValue($value) ->setPlural((int) $constraint->max) + ->setCode(Length::TOO_LONG_ERROR) ->addViolation(); return; @@ -64,6 +65,7 @@ public function validate($value, Constraint $constraint) ->setParameter('{{ limit }}', $constraint->min) ->setInvalidValue($value) ->setPlural((int) $constraint->min) + ->setCode(Length::TOO_SHORT_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Luhn.php b/src/Symfony/Component/Validator/Constraints/Luhn.php index e9b406adede15..24f5bc77ab76c 100644 --- a/src/Symfony/Component/Validator/Constraints/Luhn.php +++ b/src/Symfony/Component/Validator/Constraints/Luhn.php @@ -21,8 +21,17 @@ * * @author Tim Nagel * @author Greg Knapp http://gregk.me/2011/php-implementation-of-bank-card-luhn-algorithm/ + * @author Bernhard Schussek */ class Luhn extends Constraint { + const INVALID_CHARACTERS_ERROR = 1; + const CHECKSUM_FAILED_ERROR = 2; + + protected static $errorNames = array( + self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', + self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR', + ); + public $message = 'Invalid card number.'; } diff --git a/src/Symfony/Component/Validator/Constraints/LuhnValidator.php b/src/Symfony/Component/Validator/Constraints/LuhnValidator.php index c82d6d8917a07..d60b7f860559d 100644 --- a/src/Symfony/Component/Validator/Constraints/LuhnValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LuhnValidator.php @@ -57,6 +57,7 @@ public function validate($value, Constraint $constraint) if (!ctype_digit($value)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Luhn::INVALID_CHARACTERS_ERROR) ->addViolation(); return; @@ -87,6 +88,7 @@ public function validate($value, Constraint $constraint) if (0 === $checkSum || 0 !== $checkSum % 10) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Luhn::CHECKSUM_FAILED_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Range.php b/src/Symfony/Component/Validator/Constraints/Range.php index 067ffb8bf34da..a12afffbedb8c 100644 --- a/src/Symfony/Component/Validator/Constraints/Range.php +++ b/src/Symfony/Component/Validator/Constraints/Range.php @@ -24,6 +24,16 @@ */ class Range extends Constraint { + const INVALID_VALUE_ERROR = 1; + const BEYOND_RANGE_ERROR = 2; + const BELOW_RANGE_ERROR = 3; + + protected static $errorNames = array( + self::INVALID_VALUE_ERROR => 'INVALID_VALUE_ERROR', + self::BEYOND_RANGE_ERROR => 'BEYOND_RANGE_ERROR', + self::BELOW_RANGE_ERROR => 'BELOW_RANGE_ERROR', + ); + public $minMessage = 'This value should be {{ limit }} or more.'; public $maxMessage = 'This value should be {{ limit }} or less.'; public $invalidMessage = 'This value should be a valid number.'; diff --git a/src/Symfony/Component/Validator/Constraints/RangeValidator.php b/src/Symfony/Component/Validator/Constraints/RangeValidator.php index 6cd7afde6f345..c0bde2b723450 100644 --- a/src/Symfony/Component/Validator/Constraints/RangeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/RangeValidator.php @@ -36,6 +36,7 @@ public function validate($value, Constraint $constraint) if (!is_numeric($value) && !$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { $this->buildViolation($constraint->invalidMessage) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Range::INVALID_VALUE_ERROR) ->addViolation(); return; @@ -62,6 +63,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->maxMessage) ->setParameter('{{ value }}', $value) ->setParameter('{{ limit }}', $this->formatValue($max, self::PRETTY_DATE)) + ->setCode(Range::BEYOND_RANGE_ERROR) ->addViolation(); return; @@ -71,6 +73,7 @@ public function validate($value, Constraint $constraint) $this->buildViolation($constraint->minMessage) ->setParameter('{{ value }}', $value) ->setParameter('{{ limit }}', $this->formatValue($min, self::PRETTY_DATE)) + ->setCode(Range::BELOW_RANGE_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Time.php b/src/Symfony/Component/Validator/Constraints/Time.php index 42ede04325bea..7998c6f9519b6 100644 --- a/src/Symfony/Component/Validator/Constraints/Time.php +++ b/src/Symfony/Component/Validator/Constraints/Time.php @@ -23,5 +23,13 @@ */ class Time extends Constraint { + const INVALID_FORMAT_ERROR = 1; + const INVALID_TIME_ERROR = 2; + + protected static $errorNames = array( + self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', + self::INVALID_TIME_ERROR => 'INVALID_TIME_ERROR', + ); + public $message = 'This value is not a valid time.'; } diff --git a/src/Symfony/Component/Validator/Constraints/TimeValidator.php b/src/Symfony/Component/Validator/Constraints/TimeValidator.php index 3d52fbe43585c..bfecf95b52fb9 100644 --- a/src/Symfony/Component/Validator/Constraints/TimeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/TimeValidator.php @@ -62,6 +62,7 @@ public function validate($value, Constraint $constraint) if (!preg_match(static::PATTERN, $value, $matches)) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Time::INVALID_FORMAT_ERROR) ->addViolation(); return; @@ -70,6 +71,7 @@ public function validate($value, Constraint $constraint) if (!self::checkTime($matches[1], $matches[2], $matches[3])) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Time::INVALID_TIME_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Uuid.php b/src/Symfony/Component/Validator/Constraints/Uuid.php index dc73016401a10..3c67a3af0c163 100644 --- a/src/Symfony/Component/Validator/Constraints/Uuid.php +++ b/src/Symfony/Component/Validator/Constraints/Uuid.php @@ -17,9 +17,26 @@ * @Annotation * * @author Colin O'Dell + * @author Bernhard Schussek */ class Uuid extends Constraint { + const TOO_SHORT_ERROR = 1; + const TOO_LONG_ERROR = 2; + const INVALID_CHARACTERS_ERROR = 3; + const INVALID_HYPHEN_PLACEMENT_ERROR = 4; + const INVALID_VERSION_ERROR = 5; + const INVALID_VARIANT_ERROR = 6; + + protected static $errorNames = array( + self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', + self::TOO_LONG_ERROR => 'TOO_LONG_ERROR', + self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', + self::INVALID_HYPHEN_PLACEMENT_ERROR => 'INVALID_HYPHEN_PLACEMENT_ERROR', + self::INVALID_VERSION_ERROR => 'INVALID_VERSION_ERROR', + self::INVALID_VARIANT_ERROR => 'INVALID_VARIANT_ERROR', + ); + // Possible versions defined by RFC 4122 const V1_MAC = 1; const V2_DCE = 2; diff --git a/src/Symfony/Component/Validator/Constraints/UuidValidator.php b/src/Symfony/Component/Validator/Constraints/UuidValidator.php index 5361f12de0f0a..fa90778dedd99 100644 --- a/src/Symfony/Component/Validator/Constraints/UuidValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UuidValidator.php @@ -26,28 +26,49 @@ */ class UuidValidator extends ConstraintValidator { + // The strict pattern matches UUIDs like this: + // xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx + + // Roughly speaking: + // x = any hexadecimal character + // M = any allowed version {1..5} + // N = any allowed variant {9, 9, a, b} + + const STRICT_LENGTH = 36; + const STRICT_FIRST_HYPHEN_POSITION = 8; + const STRICT_LAST_HYPHEN_POSITION = 23; + const STRICT_VERSION_POSITION = 14; + const STRICT_VARIANT_POSITION = 19; + + // The loose pattern validates similar yet non-compliant UUIDs. + // Hyphens are completely optional. If present, they should only appear + // between every fourth character: + // xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx + // xxxxxxxxxxxx-xxxx-xxxx-xxxx-xxxx-xxxx + // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + + // The value can also be wrapped with characters like []{}: + // {xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx} + + // Neither the version nor the variant is validated by this pattern. + + const LOOSE_MAX_LENGTH = 39; + const LOOSE_FIRST_HYPHEN_POSITION = 4; + /** - * Regular expression which verifies allowed characters and the proper format. - * - * The strict pattern matches UUIDs like this: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx - * Roughly speaking: x = any hexadecimal character, M = any allowed version, N = any allowed variant. + * @deprecated Deprecated since Symfony 2.6, to be removed in 3.0 */ const STRICT_PATTERN = '/^[a-f0-9]{8}-[a-f0-9]{4}-[%s][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i'; /** - * The loose pattern validates similar yet non-compliant UUIDs. - * - * Dashes are completely optional. If present, they should only appear between every fourth character. - * The value can also be wrapped with characters like []{} for backwards-compatibility with other systems. - * Neither the version nor the variant is validated by this pattern. + * @deprecated Deprecated since Symfony 2.6, to be removed in 3.0 */ const LOOSE_PATTERN = '/^[a-f0-9]{4}(?:-?[a-f0-9]{4}){7}$/i'; /** - * Properly-formatted UUIDs contain 32 hex digits, separated by 4 dashes. - * We can use this fact to avoid performing a preg_match on strings of other sizes. + * @deprecated Deprecated since Symfony 2.6, to be removed in 3.0 */ - const STRICT_UUID_LENGTH = 36; + const STRICT_UUID_LENGTH = self::STRICT_LENGTH; /** * {@inheritdoc} @@ -65,42 +86,178 @@ public function validate($value, Constraint $constraint) $value = (string) $value; if ($constraint->strict) { - $length = strlen($value); + $this->validateStrict($value, $constraint); + + return; + } + + $this->validateLoose($value, $constraint); + } + + private function validateLoose($value, Uuid $constraint) + { + // Error priority: + // 1. ERROR_INVALID_CHARACTERS + // 2. ERROR_INVALID_HYPHEN_PLACEMENT + // 3. ERROR_TOO_SHORT/ERROR_TOO_LONG + + // Trim any wrapping characters like [] or {} used by some legacy systems + $trimmed = trim($value, '[]{}'); + + // Position of the next expected hyphen + $h = self::LOOSE_FIRST_HYPHEN_POSITION; - if ($length < static::STRICT_UUID_LENGTH) { + // Expected length + $l = self::LOOSE_MAX_LENGTH; + + for ($i = 0; $i < $l; ++$i) { + // Check length + if (!isset($trimmed{$i})) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Uuid::TOO_SHORT_ERROR) ->addViolation(); return; } - if ($length > static::STRICT_UUID_LENGTH) { + // Hyphens must occur every fifth position + // xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx + // ^ ^ ^ ^ ^ ^ ^ + if ('-' === $trimmed{$i}) { + if ($i !== $h) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR) + ->addViolation(); + + return; + } + + $h += 5; + + continue; + } + + // Missing hyphens are ignored + if ($i === $h) { + $h += 4; + --$l; + } + + // Check characters + if (!ctype_xdigit($trimmed{$i})) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Uuid::INVALID_CHARACTERS_ERROR) ->addViolation(); return; } + } + + // Check length again + if (isset($trimmed{$i})) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Uuid::TOO_LONG_ERROR) + ->addViolation(); + } + } - // Insert the allowed versions into the regular expression - $pattern = sprintf(static::STRICT_PATTERN, implode('', $constraint->versions)); + private function validateStrict($value, Uuid $constraint) + { + // Error priority: + // 1. ERROR_INVALID_CHARACTERS + // 2. ERROR_INVALID_HYPHEN_PLACEMENT + // 3. ERROR_TOO_SHORT/ERROR_TOO_LONG + // 4. ERROR_INVALID_VERSION + // 5. ERROR_INVALID_VARIANT - if (!preg_match($pattern, $value)) { + // Position of the next expected hyphen + $h = self::STRICT_FIRST_HYPHEN_POSITION; + + for ($i = 0; $i < self::STRICT_LENGTH; ++$i) { + // Check length + if (!isset($value{$i})) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Uuid::TOO_SHORT_ERROR) ->addViolation(); + + return; } - return; + // Check hyphen placement + // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + // ^ ^ ^ ^ + if ('-' === $value{$i}) { + if ($i !== $h) { + $this->buildViolation($constraint->message) + ->setParameter( + '{{ value }}', + $this->formatValue($value) + ) + ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR) + ->addViolation(); + + return; + } + + // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + // ^ + if ($h < self::STRICT_LAST_HYPHEN_POSITION) { + $h += 5; + } + + continue; + } + + // Check characters + if (!ctype_xdigit($value{$i})) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Uuid::INVALID_CHARACTERS_ERROR) + ->addViolation(); + + return; + } + + // Missing hyphen + if ($i === $h) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR) + ->addViolation(); + + return; + } } - // Trim any wrapping characters like [] or {} used by some legacy systems - $value = trim($value, '[]{}'); + // Check length again + if (isset($value{$i})) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Uuid::TOO_LONG_ERROR) + ->addViolation(); + } + + // Check version + if (!in_array($value{self::STRICT_VERSION_POSITION}, $constraint->versions)) { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Uuid::INVALID_VERSION_ERROR) + ->addViolation(); + } - if (!preg_match(static::LOOSE_PATTERN, $value)) { + // Check variant - first two bits must equal "10" + // 0b10xx + // & 0b1100 (12) + // = 0b1000 (8) + if ((hexdec($value{self::STRICT_VARIANT_POSITION}) & 12) !== 8) { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Uuid::INVALID_VARIANT_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Tests/ConstraintTest.php b/src/Symfony/Component/Validator/Tests/ConstraintTest.php index 61a96c1f5aecd..f63570c5d2944 100644 --- a/src/Symfony/Component/Validator/Tests/ConstraintTest.php +++ b/src/Symfony/Component/Validator/Tests/ConstraintTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Tests; +use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Tests\Fixtures\ClassConstraint; use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; use Symfony\Component\Validator\Tests\Fixtures\ConstraintB; @@ -197,4 +198,12 @@ public function testSerializeKeepsCustomGroups() $this->assertSame(array('MyGroup'), $constraint->groups); } + + /** + * @expectedException \Symfony\Component\Validator\Exception\InvalidArgumentException + */ + public function testGetErrorNameForUnknownCode() + { + Constraint::getErrorName(1); + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php index 40bba756c21ef..aab54e590f0d5 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php @@ -54,7 +54,7 @@ public function testValidNumbers($scheme, $number) /** * @dataProvider getInvalidNumbers */ - public function testInvalidNumbers($scheme, $number) + public function testInvalidNumbers($scheme, $number, $code) { $constraint = new CardScheme(array( 'schemes' => $scheme, @@ -65,6 +65,7 @@ public function testInvalidNumbers($scheme, $number) $this->buildViolation('myMessage') ->setParameter('{{ value }}', is_string($number) ? '"'.$number.'"' : $number) + ->setCode($code) ->assertRaised(); } @@ -113,20 +114,20 @@ public function getValidNumbers() public function getInvalidNumbers() { return array( - array('VISA', '42424242424242424242'), - array('AMEX', '357298508610146'), - array('DINERS', '31569309025904'), - array('DINERS', '37088894118515'), - array('INSTAPAYMENT', '6313440808445746'), - array('CHINA_UNIONPAY', '622888888888888'), - array('CHINA_UNIONPAY', '62288888888888888888'), - array('AMEX', '30569309025904'), // DINERS number - array('AMEX', 'invalid'), // A string - array('AMEX', 0), // a lone number - array('AMEX', '0'), // a lone number - array('AMEX', '000000000000'), // a lone number - array('DINERS', '3056930'), // only first part of the number - array('DISCOVER', '1117'), // only last 4 digits + array('VISA', '42424242424242424242', CardScheme::INVALID_FORMAT_ERROR), + array('AMEX', '357298508610146', CardScheme::INVALID_FORMAT_ERROR), + array('DINERS', '31569309025904', CardScheme::INVALID_FORMAT_ERROR), + array('DINERS', '37088894118515', CardScheme::INVALID_FORMAT_ERROR), + array('INSTAPAYMENT', '6313440808445746', CardScheme::INVALID_FORMAT_ERROR), + array('CHINA_UNIONPAY', '622888888888888', CardScheme::INVALID_FORMAT_ERROR), + array('CHINA_UNIONPAY', '62288888888888888888', CardScheme::INVALID_FORMAT_ERROR), + array('AMEX', '30569309025904', CardScheme::INVALID_FORMAT_ERROR), // DINERS number + array('AMEX', 'invalid', CardScheme::NOT_NUMERIC_ERROR), // A string + array('AMEX', 0, CardScheme::INVALID_FORMAT_ERROR), // a lone number + array('AMEX', '0', CardScheme::INVALID_FORMAT_ERROR), // a lone number + array('AMEX', '000000000000', CardScheme::INVALID_FORMAT_ERROR), // a lone number + array('DINERS', '3056930', CardScheme::INVALID_FORMAT_ERROR), // only first part of the number + array('DISCOVER', '1117', CardScheme::INVALID_FORMAT_ERROR), // only last 4 digits ); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php index 84b5bc3b0d63b..aa5b8945b2eee 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php @@ -146,6 +146,7 @@ public function testInvalidChoice() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"baz"') + ->setCode(Choice::NO_SUCH_CHOICE_ERROR) ->assertRaised(); } @@ -162,6 +163,7 @@ public function testInvalidChoiceMultiple() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"baz"') ->setInvalidValue('baz') + ->setCode(Choice::NO_SUCH_CHOICE_ERROR) ->assertRaised(); } @@ -184,6 +186,7 @@ public function testTooFewChoices() ->setParameter('{{ limit }}', 2) ->setInvalidValue($value) ->setPlural(2) + ->setCode(Choice::TOO_FEW_ERROR) ->assertRaised(); } @@ -206,6 +209,7 @@ public function testTooManyChoices() ->setParameter('{{ limit }}', 2) ->setInvalidValue($value) ->setPlural(2) + ->setCode(Choice::TOO_MANY_ERROR) ->assertRaised(); } @@ -246,6 +250,7 @@ public function testStrictDisallowsDifferentType() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"2"') + ->setCode(Choice::NO_SUCH_CHOICE_ERROR) ->assertRaised(); } @@ -276,6 +281,7 @@ public function testStrictWithMultipleChoices() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"3"') ->setInvalidValue('3') + ->setCode(Choice::NO_SUCH_CHOICE_ERROR) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CollectionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CollectionValidatorTest.php index fb80070a46d80..0376814341fb8 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CollectionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CollectionValidatorTest.php @@ -146,6 +146,7 @@ public function testExtraFieldsDisallowed() ->setParameter('{{ field }}', '"baz"') ->atPath('property.path[baz]') ->setInvalidValue(6) + ->setCode(Collection::NO_SUCH_FIELD_ERROR) ->assertRaised(); } @@ -207,6 +208,7 @@ public function testMissingFieldsDisallowed() ->setParameter('{{ field }}', '"foo"') ->atPath('property.path[foo]') ->setInvalidValue(null) + ->setCode(Collection::MISSING_FIELD_ERROR) ->assertRaised(); } @@ -319,6 +321,7 @@ public function testRequiredFieldNotPresent() ->setParameter('{{ field }}', '"foo"') ->atPath('property.path[foo]') ->setInvalidValue(null) + ->setCode(Collection::MISSING_FIELD_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CountValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CountValidatorTest.php index 31c7c7856288c..6713166ce461f 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CountValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CountValidatorTest.php @@ -124,6 +124,7 @@ public function testTooManyValues($value) ->setParameter('{{ limit }}', 4) ->setInvalidValue($value) ->setPlural(4) + ->setCode(Count::TOO_MANY_ERROR) ->assertRaised(); } @@ -144,6 +145,7 @@ public function testTooFewValues($value) ->setParameter('{{ limit }}', 4) ->setInvalidValue($value) ->setPlural(4) + ->setCode(Count::TOO_FEW_ERROR) ->assertRaised(); } @@ -165,6 +167,7 @@ public function testTooManyValuesExact($value) ->setParameter('{{ limit }}', 4) ->setInvalidValue($value) ->setPlural(4) + ->setCode(Count::TOO_MANY_ERROR) ->assertRaised(); } @@ -186,6 +189,7 @@ public function testTooFewValuesExact($value) ->setParameter('{{ limit }}', 4) ->setInvalidValue($value) ->setPlural(4) + ->setCode(Count::TOO_FEW_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/DateTimeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/DateTimeValidatorTest.php index 89de5fd9a5153..25d88aa2ab882 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/DateTimeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/DateTimeValidatorTest.php @@ -78,7 +78,7 @@ public function getValidDateTimes() /** * @dataProvider getInvalidDateTimes */ - public function testInvalidDateTimes($dateTime) + public function testInvalidDateTimes($dateTime, $code) { $constraint = new DateTime(array( 'message' => 'myMessage', @@ -88,22 +88,23 @@ public function testInvalidDateTimes($dateTime) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$dateTime.'"') + ->setCode($code) ->assertRaised(); } public function getInvalidDateTimes() { return array( - array('foobar'), - array('2010-01-01'), - array('00:00:00'), - array('2010-01-01 00:00'), - array('2010-13-01 00:00:00'), - array('2010-04-32 00:00:00'), - array('2010-02-29 00:00:00'), - array('2010-01-01 24:00:00'), - array('2010-01-01 00:60:00'), - array('2010-01-01 00:00:60'), + array('foobar', DateTime::INVALID_FORMAT_ERROR), + array('2010-01-01', DateTime::INVALID_FORMAT_ERROR), + array('00:00:00', DateTime::INVALID_FORMAT_ERROR), + array('2010-01-01 00:00', DateTime::INVALID_FORMAT_ERROR), + array('2010-13-01 00:00:00', DateTime::INVALID_DATE_ERROR), + array('2010-04-32 00:00:00', DateTime::INVALID_DATE_ERROR), + array('2010-02-29 00:00:00', DateTime::INVALID_DATE_ERROR), + array('2010-01-01 24:00:00', DateTime::INVALID_TIME_ERROR), + array('2010-01-01 00:60:00', DateTime::INVALID_TIME_ERROR), + array('2010-01-01 00:00:60', DateTime::INVALID_TIME_ERROR), ); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php index 352ded5422a5a..21f0a2dcc3d60 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php @@ -78,7 +78,7 @@ public function getValidDates() /** * @dataProvider getInvalidDates */ - public function testInvalidDates($date) + public function testInvalidDates($date, $code) { $constraint = new Date(array( 'message' => 'myMessage', @@ -88,18 +88,19 @@ public function testInvalidDates($date) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$date.'"') + ->setCode($code) ->assertRaised(); } public function getInvalidDates() { return array( - array('foobar'), - array('foobar 2010-13-01'), - array('2010-13-01 foobar'), - array('2010-13-01'), - array('2010-04-32'), - array('2010-02-29'), + array('foobar', Date::INVALID_FORMAT_ERROR), + array('foobar 2010-13-01', Date::INVALID_FORMAT_ERROR), + array('2010-13-01 foobar', Date::INVALID_FORMAT_ERROR), + array('2010-13-01', Date::INVALID_DATE_ERROR), + array('2010-04-32', Date::INVALID_DATE_ERROR), + array('2010-02-29', Date::INVALID_DATE_ERROR), ); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php index fb39f8854052e..0361333fdc16a 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php @@ -81,6 +81,7 @@ public function testInvalidEmails($email) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$email.'"') + ->setCode(Email::INVALID_FORMAT_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorPathTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorPathTest.php index 25def64c19c95..11b8d4cb7987c 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorPathTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorPathTest.php @@ -30,6 +30,7 @@ public function testFileNotFound() $this->buildViolation('myMessage') ->setParameter('{{ file }}', '"foobar"') + ->setCode(File::NOT_FOUND_ERROR) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php index 8ecdf321a82fb..c66b6ca5a36b6 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php @@ -181,6 +181,7 @@ public function testMaxSizeExceeded($bytesWritten, $limit, $sizeAsString, $limit ->setParameter('{{ size }}', $sizeAsString) ->setParameter('{{ suffix }}', $suffix) ->setParameter('{{ file }}', '"'.$this->path.'"') + ->setCode(File::TOO_LARGE_ERROR) ->assertRaised(); } @@ -277,12 +278,13 @@ public function testBinaryFormat($bytesWritten, $limit, $binaryFormat, $sizeAsSt $this->validator->validate($this->getFile($this->path), $constraint); - $this->assertViolation('myMessage', array( - '{{ limit }}' => $limitAsString, - '{{ size }}' => $sizeAsString, - '{{ suffix }}' => $suffix, - '{{ file }}' => '"'.$this->path.'"', - )); + $this->buildViolation('myMessage') + ->setParameter('{{ limit }}', $limitAsString) + ->setParameter('{{ size }}', $sizeAsString) + ->setParameter('{{ suffix }}', $suffix) + ->setParameter('{{ file }}', '"'.$this->path.'"') + ->setCode(File::TOO_LARGE_ERROR) + ->assertRaised(); } public function testValidMimeType() @@ -359,6 +361,7 @@ public function testInvalidMimeType() ->setParameter('{{ type }}', '"application/pdf"') ->setParameter('{{ types }}', '"image/png", "image/jpg"') ->setParameter('{{ file }}', '"'.$this->path.'"') + ->setCode(File::INVALID_MIME_TYPE_ERROR) ->assertRaised(); } @@ -388,6 +391,7 @@ public function testInvalidWildcardMimeType() ->setParameter('{{ type }}', '"application/pdf"') ->setParameter('{{ types }}', '"image/*", "image/jpg"') ->setParameter('{{ file }}', '"'.$this->path.'"') + ->setCode(File::INVALID_MIME_TYPE_ERROR) ->assertRaised(); } @@ -401,7 +405,10 @@ public function testDisallowEmpty() $this->validator->validate($this->getFile($this->path), $constraint); - $this->assertViolation('myMessage'); + $this->buildViolation('myMessage') + ->setParameter('{{ file }}', '"'.$this->path.'"') + ->setCode(File::EMPTY_ERROR) + ->assertRaised(); } /** @@ -420,6 +427,7 @@ public function testUploadedFileError($error, $message, array $params = array(), $this->buildViolation('myMessage') ->setParameters($params) + ->setCode($error) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php index d2ecf8f51b80c..ab9839a238968 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php @@ -156,7 +156,7 @@ public function getValidIbans() /** * @dataProvider getInvalidIbans */ - public function testInvalidIbans($iban) + public function testInvalidIbans($iban, $code) { $constraint = new Iban(array( 'message' => 'myMessage', @@ -166,27 +166,28 @@ public function testInvalidIbans($iban) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$iban.'"') + ->setCode($code) ->assertRaised(); } public function getInvalidIbans() { return array( - array('CH93 0076 2011 6238 5295'), - array('CH930076201162385295'), - array('GB29 RBOS 6016 1331 9268 19'), - array('CH930072011623852957'), - array('NL39 RASO 0300 0652 64'), - array('NO93 8601117 947'), - array('CY170020 128 0000 0012 0052 7600'), - array('foo'), - array('123'), - array('0750447346'), - array('CH930076201162385295]'), + array('CH93 0076 2011 6238 5295', Iban::CHECKSUM_FAILED_ERROR), + array('CH930076201162385295', Iban::CHECKSUM_FAILED_ERROR), + array('GB29 RBOS 6016 1331 9268 19', Iban::CHECKSUM_FAILED_ERROR), + array('CH930072011623852957', Iban::CHECKSUM_FAILED_ERROR), + array('NL39 RASO 0300 0652 64', Iban::CHECKSUM_FAILED_ERROR), + array('NO93 8601117 947', Iban::CHECKSUM_FAILED_ERROR), + array('CY170020 128 0000 0012 0052 7600', Iban::CHECKSUM_FAILED_ERROR), + array('foo', Iban::TOO_SHORT_ERROR), + array('123', Iban::TOO_SHORT_ERROR), + array('0750447346', Iban::INVALID_COUNTRY_CODE_ERROR), + array('CH930076201162385295]', Iban::INVALID_CHARACTERS_ERROR), //Ibans with lower case values are invalid - array('Ae260211000000230064016'), - array('ae260211000000230064016'), + array('Ae260211000000230064016', Iban::INVALID_CASE_ERROR), + array('ae260211000000230064016', Iban::INVALID_CASE_ERROR), ); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php index 5d6c9a91ff4dc..18f9c1bc0673d 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ImageValidatorTest.php @@ -71,6 +71,21 @@ public function testValidImage() $this->assertNoViolation(); } + public function testFileNotFound() + { + // Check that the logic from FileValidator still works + $constraint = new Image(array( + 'notFoundMessage' => 'myMessage', + )); + + $this->validator->validate('foobar', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ file }}', '"foobar"') + ->setCode(Image::NOT_FOUND_ERROR) + ->assertRaised(); + } + public function testValidSize() { $constraint = new Image(array( @@ -97,6 +112,7 @@ public function testWidthTooSmall() $this->buildViolation('myMessage') ->setParameter('{{ width }}', '2') ->setParameter('{{ min_width }}', '3') + ->setCode(Image::TOO_NARROW_ERROR) ->assertRaised(); } @@ -112,6 +128,7 @@ public function testWidthTooBig() $this->buildViolation('myMessage') ->setParameter('{{ width }}', '2') ->setParameter('{{ max_width }}', '1') + ->setCode(Image::TOO_WIDE_ERROR) ->assertRaised(); } @@ -127,6 +144,7 @@ public function testHeightTooSmall() $this->buildViolation('myMessage') ->setParameter('{{ height }}', '2') ->setParameter('{{ min_height }}', '3') + ->setCode(Image::TOO_LOW_ERROR) ->assertRaised(); } @@ -142,6 +160,7 @@ public function testHeightTooBig() $this->buildViolation('myMessage') ->setParameter('{{ height }}', '2') ->setParameter('{{ max_height }}', '1') + ->setCode(Image::TOO_HIGH_ERROR) ->assertRaised(); } @@ -205,6 +224,7 @@ public function testRatioTooSmall() $this->buildViolation('myMessage') ->setParameter('{{ ratio }}', 1) ->setParameter('{{ min_ratio }}', 2) + ->setCode(Image::RATIO_TOO_SMALL_ERROR) ->assertRaised(); } @@ -220,6 +240,7 @@ public function testRatioTooBig() $this->buildViolation('myMessage') ->setParameter('{{ ratio }}', 1) ->setParameter('{{ max_ratio }}', 0.5) + ->setCode(Image::RATIO_TOO_BIG_ERROR) ->assertRaised(); } @@ -270,6 +291,7 @@ public function testSquareNotAllowed() $this->buildViolation('myMessage') ->setParameter('{{ width }}', 2) ->setParameter('{{ height }}', 2) + ->setCode(Image::SQUARE_NOT_ALLOWED_ERROR) ->assertRaised(); } @@ -285,6 +307,7 @@ public function testLandscapeNotAllowed() $this->buildViolation('myMessage') ->setParameter('{{ width }}', 2) ->setParameter('{{ height }}', 1) + ->setCode(Image::LANDSCAPE_NOT_ALLOWED_ERROR) ->assertRaised(); } @@ -300,6 +323,7 @@ public function testPortraitNotAllowed() $this->buildViolation('myMessage') ->setParameter('{{ width }}', 1) ->setParameter('{{ height }}', 2) + ->setCode(Image::PORTRAIT_NOT_ALLOWED_ERROR) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IsbnValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IsbnValidatorTest.php index 296ab7c94a3ab..e73b89d60bab1 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IsbnValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IsbnValidatorTest.php @@ -52,20 +52,20 @@ public function getValidIsbn10() public function getInvalidIsbn10() { return array( - array('27234422841'), - array('272344228'), - array('0-4712-9231'), - array('1234567890'), - array('0987656789'), - array('7-35622-5444'), - array('0-4X19-92611'), - array('0_45122_5244'), - array('2870#971#648'), - array('0-9752298-0-x'), - array('1A34567890'), + array('27234422841', Isbn::TOO_LONG_ERROR), + array('272344228', Isbn::TOO_SHORT_ERROR), + array('0-4712-9231', Isbn::TOO_SHORT_ERROR), + array('1234567890', Isbn::CHECKSUM_FAILED_ERROR), + array('0987656789', Isbn::CHECKSUM_FAILED_ERROR), + array('7-35622-5444', Isbn::CHECKSUM_FAILED_ERROR), + array('0-4X19-92611', Isbn::CHECKSUM_FAILED_ERROR), + array('0_45122_5244', Isbn::INVALID_CHARACTERS_ERROR), + array('2870#971#648', Isbn::INVALID_CHARACTERS_ERROR), + array('0-9752298-0-x', Isbn::INVALID_CHARACTERS_ERROR), + array('1A34567890', Isbn::INVALID_CHARACTERS_ERROR), // chr(1) evaluates to 0 // 2070546810 is valid - array('2'.chr(1).'70546810'), + array('2'.chr(1).'70546810', Isbn::INVALID_CHARACTERS_ERROR), ); } @@ -90,20 +90,20 @@ public function getValidIsbn13() public function getInvalidIsbn13() { return array( - array('978-27234422821'), - array('978-272344228'), - array('978-2723442-82'), - array('978-2723442281'), - array('978-0321513774'), - array('979-0431225385'), - array('980-0474292319'), - array('0-4X19-92619812'), - array('978_2723442282'), - array('978#2723442282'), - array('978-272C442282'), + array('978-27234422821', Isbn::TOO_LONG_ERROR), + array('978-272344228', Isbn::TOO_SHORT_ERROR), + array('978-2723442-82', Isbn::TOO_SHORT_ERROR), + array('978-2723442281', Isbn::CHECKSUM_FAILED_ERROR), + array('978-0321513774', Isbn::CHECKSUM_FAILED_ERROR), + array('979-0431225385', Isbn::CHECKSUM_FAILED_ERROR), + array('980-0474292319', Isbn::CHECKSUM_FAILED_ERROR), + array('0-4X19-92619812', Isbn::INVALID_CHARACTERS_ERROR), + array('978_2723442282', Isbn::INVALID_CHARACTERS_ERROR), + array('978#2723442282', Isbn::INVALID_CHARACTERS_ERROR), + array('978-272C442282', Isbn::INVALID_CHARACTERS_ERROR), // chr(1) evaluates to 0 // 978-2070546817 is valid - array('978-2'.chr(1).'70546817'), + array('978-2'.chr(1).'70546817', Isbn::INVALID_CHARACTERS_ERROR), ); } @@ -168,7 +168,7 @@ public function testValidIsbn10($isbn) /** * @dataProvider getInvalidIsbn10 */ - public function testInvalidIsbn10($isbn) + public function testInvalidIsbn10($isbn, $code) { $constraint = new Isbn(array( 'type' => 'isbn10', @@ -179,6 +179,7 @@ public function testInvalidIsbn10($isbn) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$isbn.'"') + ->setCode($code) ->assertRaised(); } @@ -197,7 +198,7 @@ public function testValidIsbn13($isbn) /** * @dataProvider getInvalidIsbn13 */ - public function testInvalidIsbn13($isbn) + public function testInvalidIsbn13($isbn, $code) { $constraint = new Isbn(array( 'type' => 'isbn13', @@ -208,6 +209,7 @@ public function testInvalidIsbn13($isbn) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$isbn.'"') + ->setCode($code) ->assertRaised(); } @@ -226,7 +228,7 @@ public function testValidIsbnAny($isbn) /** * @dataProvider getInvalidIsbn10 */ - public function testInvalidIsbnAnyIsbn10($isbn) + public function testInvalidIsbnAnyIsbn10($isbn, $code) { $constraint = new Isbn(array( 'bothIsbnMessage' => 'myMessage', @@ -234,15 +236,21 @@ public function testInvalidIsbnAnyIsbn10($isbn) $this->validator->validate($isbn, $constraint); + // Too long for an ISBN-10, but not long enough for an ISBN-13 + if (Isbn::TOO_LONG_ERROR === $code) { + $code = Isbn::TYPE_NOT_RECOGNIZED_ERROR; + } + $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$isbn.'"') + ->setCode($code) ->assertRaised(); } /** * @dataProvider getInvalidIsbn13 */ - public function testInvalidIsbnAnyIsbn13($isbn) + public function testInvalidIsbnAnyIsbn13($isbn, $code) { $constraint = new Isbn(array( 'bothIsbnMessage' => 'myMessage', @@ -250,8 +258,14 @@ public function testInvalidIsbnAnyIsbn13($isbn) $this->validator->validate($isbn, $constraint); + // Too short for an ISBN-13, but not short enough for an ISBN-10 + if (Isbn::TOO_SHORT_ERROR === $code) { + $code = Isbn::TYPE_NOT_RECOGNIZED_ERROR; + } + $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$isbn.'"') + ->setCode($code) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php index cb7ae3d8925dc..a6d39944b0ac1 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php @@ -82,14 +82,14 @@ public function getValidIssn() public function getInvalidIssn() { return array( - array(0), - array('1539'), - array('2156-537A'), - array('1119-0231'), - array('1684-5312'), - array('1996-0783'), - array('1684-537X'), - array('1996-0795'), + array(0, Issn::TOO_SHORT_ERROR), + array('1539', Issn::TOO_SHORT_ERROR), + array('2156-537A', Issn::INVALID_CHARACTERS_ERROR), + array('1119-0231', Issn::CHECKSUM_FAILED_ERROR), + array('1684-5312', Issn::CHECKSUM_FAILED_ERROR), + array('1996-0783', Issn::CHECKSUM_FAILED_ERROR), + array('1684-537X', Issn::CHECKSUM_FAILED_ERROR), + array('1996-0795', Issn::CHECKSUM_FAILED_ERROR), ); } @@ -134,6 +134,7 @@ public function testCaseSensitiveIssns($issn) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$issn.'"') + ->setCode(Issn::INVALID_CASE_ERROR) ->assertRaised(); } @@ -151,6 +152,7 @@ public function testRequireHyphenIssns($issn) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$issn.'"') + ->setCode(Issn::MISSING_HYPHEN_ERROR) ->assertRaised(); } @@ -169,7 +171,7 @@ public function testValidIssn($issn) /** * @dataProvider getInvalidIssn */ - public function testInvalidIssn($issn) + public function testInvalidIssn($issn, $code) { $constraint = new Issn(array( 'message' => 'myMessage', @@ -179,6 +181,7 @@ public function testInvalidIssn($issn) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$issn.'"') + ->setCode($code) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php index 7674ae24272e3..ae27ff25cb452 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php @@ -153,6 +153,7 @@ public function testInvalidValuesMin($value, $mbOnly = false) ->setParameter('{{ limit }}', 4) ->setInvalidValue($value) ->setPlural(4) + ->setCode(Length::TOO_SHORT_ERROR) ->assertRaised(); } @@ -177,6 +178,7 @@ public function testInvalidValuesMax($value, $mbOnly = false) ->setParameter('{{ limit }}', 4) ->setInvalidValue($value) ->setPlural(4) + ->setCode(Length::TOO_LONG_ERROR) ->assertRaised(); } @@ -202,6 +204,7 @@ public function testInvalidValuesExactLessThanFour($value, $mbOnly = false) ->setParameter('{{ limit }}', 4) ->setInvalidValue($value) ->setPlural(4) + ->setCode(Length::TOO_SHORT_ERROR) ->assertRaised(); } @@ -227,6 +230,7 @@ public function testInvalidValuesExactMoreThanFour($value, $mbOnly = false) ->setParameter('{{ limit }}', 4) ->setInvalidValue($value) ->setPlural(4) + ->setCode(Length::TOO_LONG_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LuhnValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LuhnValidatorTest.php index 4ad3c736eb09d..b0e88c3456b08 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LuhnValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LuhnValidatorTest.php @@ -78,7 +78,7 @@ public function getValidNumbers() /** * @dataProvider getInvalidNumbers */ - public function testInvalidNumbers($number) + public function testInvalidNumbers($number, $code) { $constraint = new Luhn(array( 'message' => 'myMessage', @@ -88,17 +88,18 @@ public function testInvalidNumbers($number) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$number.'"') + ->setCode($code) ->assertRaised(); } public function getInvalidNumbers() { return array( - array('1234567812345678'), - array('4222222222222222'), - array('0000000000000000'), - array('000000!000000000'), - array('42-22222222222222'), + array('1234567812345678', Luhn::CHECKSUM_FAILED_ERROR), + array('4222222222222222', Luhn::CHECKSUM_FAILED_ERROR), + array('0000000000000000', Luhn::CHECKSUM_FAILED_ERROR), + array('000000!000000000', Luhn::INVALID_CHARACTERS_ERROR), + array('42-22222222222222', Luhn::INVALID_CHARACTERS_ERROR), ); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php index 0b60559984229..3bfb405e07200 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php @@ -117,6 +117,7 @@ public function testInvalidValuesMin($value) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $value) ->setParameter('{{ limit }}', 10) + ->setCode(Range::BELOW_RANGE_ERROR) ->assertRaised(); } @@ -135,6 +136,7 @@ public function testInvalidValuesMax($value) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $value) ->setParameter('{{ limit }}', 20) + ->setCode(Range::BEYOND_RANGE_ERROR) ->assertRaised(); } @@ -155,6 +157,7 @@ public function testInvalidValuesCombinedMax($value) $this->buildViolation('myMaxMessage') ->setParameter('{{ value }}', $value) ->setParameter('{{ limit }}', 20) + ->setCode(Range::BEYOND_RANGE_ERROR) ->assertRaised(); } @@ -175,6 +178,7 @@ public function testInvalidValuesCombinedMin($value) $this->buildViolation('myMinMessage') ->setParameter('{{ value }}', $value) ->setParameter('{{ limit }}', 10) + ->setCode(Range::BELOW_RANGE_ERROR) ->assertRaised(); } @@ -292,10 +296,11 @@ public function testInvalidDatesMin($value) $this->validator->validate($value, $constraint); - $this->assertViolation('myMessage', array( - '{{ value }}' => $value, - '{{ limit }}' => 'Mar 10, 2014, 12:00 AM', - )); + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', $value) + ->setParameter('{{ limit }}', 'Mar 10, 2014, 12:00 AM') + ->setCode(Range::BELOW_RANGE_ERROR) + ->assertRaised(); } /** @@ -314,10 +319,11 @@ public function testInvalidDatesMax($value) $this->validator->validate($value, $constraint); - $this->assertViolation('myMessage', array( - '{{ value }}' => $value, - '{{ limit }}' => 'Mar 20, 2014, 12:00 AM', - )); + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', $value) + ->setParameter('{{ limit }}', 'Mar 20, 2014, 12:00 AM') + ->setCode(Range::BEYOND_RANGE_ERROR) + ->assertRaised(); } /** @@ -338,10 +344,11 @@ public function testInvalidDatesCombinedMax($value) $this->validator->validate($value, $constraint); - $this->assertViolation('myMaxMessage', array( - '{{ value }}' => $value, - '{{ limit }}' => 'Mar 20, 2014, 12:00 AM', - )); + $this->buildViolation('myMaxMessage') + ->setParameter('{{ value }}', $value) + ->setParameter('{{ limit }}', 'Mar 20, 2014, 12:00 AM') + ->setCode(Range::BEYOND_RANGE_ERROR) + ->assertRaised(); } /** @@ -362,10 +369,11 @@ public function testInvalidDatesCombinedMin($value) $this->validator->validate($value, $constraint); - $this->assertViolation('myMinMessage', array( - '{{ value }}' => $value, - '{{ limit }}' => 'Mar 10, 2014, 12:00 AM', - )); + $this->buildViolation('myMinMessage') + ->setParameter('{{ value }}', $value) + ->setParameter('{{ limit }}', 'Mar 10, 2014, 12:00 AM') + ->setCode(Range::BELOW_RANGE_ERROR) + ->assertRaised(); } public function getInvalidValues() @@ -389,6 +397,7 @@ public function testNonNumeric() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"abcd"') + ->setCode(Range::INVALID_VALUE_ERROR) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php index 23aed04c0f331..a6ca1435ed338 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php @@ -78,7 +78,7 @@ public function getValidTimes() /** * @dataProvider getInvalidTimes */ - public function testInvalidTimes($time) + public function testInvalidTimes($time, $code) { $constraint = new Time(array( 'message' => 'myMessage', @@ -88,19 +88,20 @@ public function testInvalidTimes($time) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$time.'"') + ->setCode($code) ->assertRaised(); } public function getInvalidTimes() { return array( - array('foobar'), - array('foobar 12:34:56'), - array('12:34:56 foobar'), - array('00:00'), - array('24:00:00'), - array('00:60:00'), - array('00:00:60'), + array('foobar', Time::INVALID_FORMAT_ERROR), + array('foobar 12:34:56', Time::INVALID_FORMAT_ERROR), + array('12:34:56 foobar', Time::INVALID_FORMAT_ERROR), + array('00:00', Time::INVALID_FORMAT_ERROR), + array('24:00:00', Time::INVALID_TIME_ERROR), + array('00:60:00', Time::INVALID_TIME_ERROR), + array('00:00:60', Time::INVALID_TIME_ERROR), ); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php index b34ce8138b707..0abda39f02942 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php @@ -55,9 +55,15 @@ public function testExpectsStringCompatibleType() /** * @dataProvider getValidStrictUuids */ - public function testValidStrictUuids($uuid) + public function testValidStrictUuids($uuid, $versions = null) { - $this->validator->validate($uuid, new Uuid()); + $constraint = new Uuid(); + + if (null !== $versions) { + $constraint->versions = $versions; + } + + $this->validator->validate($uuid, $constraint); $this->assertNoViolation(); } @@ -66,75 +72,81 @@ public function getValidStrictUuids() { return array( array('216fff40-98d9-11e3-a5e2-0800200c9a66'), // Version 1 UUID in lowercase + array('216fff40-98d9-11e3-a5e2-0800200c9a66', array(Uuid::V1_MAC)), array('216FFF40-98D9-11E3-A5E2-0800200C9A66'), // Version 1 UUID in UPPERCASE array('456daefb-5aa6-41b5-8dbc-068b05a8b201'), // Version 4 UUID in lowercase - array('456DAEFb-5AA6-41B5-8DBC-068B05A8B201'), // Version 4 UUID in UPPERCASE + array('456daEFb-5AA6-41B5-8DBC-068B05A8B201'), // Version 4 UUID in mixed case + array('456daEFb-5AA6-41B5-8DBC-068B05A8B201', array(Uuid::V4_RANDOM)), ); } /** * @dataProvider getInvalidStrictUuids */ - public function testInvalidStrictUuids($uuid) + public function testInvalidStrictUuids($uuid, $code, $versions = null) { $constraint = new Uuid(array( 'message' => 'testMessage', )); + if (null !== $versions) { + $constraint->versions = $versions; + } + $this->validator->validate($uuid, $constraint); $this->buildViolation('testMessage') ->setParameter('{{ value }}', '"'.$uuid.'"') + ->setCode($code) ->assertRaised(); } public function getInvalidStrictUuids() { return array( - array('216fff40-98d9-11e3-a5e2-0800200c9a6'), // Too few characters - array('216fff40-98d9-11e3-a5e2-0800200c9a666'), // Too many characters - array('V16fff40-98d9-11e3-a5e2-0800200c9a66'), // Invalid character 'V' - array('2-16fff-4098d-911e3a5e20-800-200c9-a66'), // Non-standard dash positions (randomly placed) - - // Non-standard UUIDs allowed by some other systems - array('216f-ff40-98d9-11e3-a5e2-0800-200c-9a66'), // Non-standard dash positions (every 4 chars) - array('216fff40-98d911e3-a5e20800-200c9a66'), // Non-standard dash positions (every 8 chars) - array('216fff4098d911e3a5e20800200c9a66'), // No dashes at all - array('{216fff40-98d9-11e3-a5e2-0800200c9a66}'), // Wrapped with curly braces + array('216fff40-98d9-11e3-a5e2_0800200c9a66', Uuid::INVALID_CHARACTERS_ERROR), + array('216gff40-98d9-11e3-a5e2-0800200c9a66', Uuid::INVALID_CHARACTERS_ERROR), + array('216Gff40-98d9-11e3-a5e2-0800200c9a66', Uuid::INVALID_CHARACTERS_ERROR), + array('216fff40-98d9-11e3-a5e-20800200c9a66', Uuid::INVALID_HYPHEN_PLACEMENT_ERROR), + array('216f-ff40-98d9-11e3-a5e2-0800200c9a66', Uuid::INVALID_HYPHEN_PLACEMENT_ERROR), + array('216fff40-98d9-11e3-a5e2-0800-200c9a66', Uuid::INVALID_HYPHEN_PLACEMENT_ERROR), + array('216fff40-98d9-11e3-a5e2-0800200c-9a66', Uuid::INVALID_HYPHEN_PLACEMENT_ERROR), + array('216fff40-98d9-11e3-a5e20800200c9a66', Uuid::INVALID_HYPHEN_PLACEMENT_ERROR), + array('216fff4098d911e3a5e20800200c9a66', Uuid::INVALID_HYPHEN_PLACEMENT_ERROR), + array('216fff40-98d9-11e3-a5e2-0800200c9a6', Uuid::TOO_SHORT_ERROR), + array('216fff40-98d9-11e3-a5e2-0800200c9a666', Uuid::TOO_LONG_ERROR), + array('216fff40-98d9-01e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-61e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-71e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-81e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-91e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-a1e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-b1e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-c1e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-d1e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-e1e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-f1e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR), + array('216fff40-98d9-11e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR, array(Uuid::V2_DCE, Uuid::V3_MD5, Uuid::V4_RANDOM, Uuid::V5_SHA1)), + array('216fff40-98d9-21e3-a5e2-0800200c9a66', Uuid::INVALID_VERSION_ERROR, array(Uuid::V1_MAC, Uuid::V3_MD5, Uuid::V4_RANDOM, Uuid::V5_SHA1)), + array('216fff40-98d9-11e3-05e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-15e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-25e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-35e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-45e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-55e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-65e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-75e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-c5e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-d5e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-e5e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + array('216fff40-98d9-11e3-f5e2-0800200c9a66', Uuid::INVALID_VARIANT_ERROR), + + // Non-standard UUID allowed by some other systems + array('{216fff40-98d9-11e3-a5e2-0800200c9a66}', Uuid::INVALID_CHARACTERS_ERROR), + array('[216fff40-98d9-11e3-a5e2-0800200c9a66]', Uuid::INVALID_CHARACTERS_ERROR), ); } - /** - * @dataProvider getValidStrictUuids - */ - public function testVersionConstraintIsValid($uuid) - { - $constraint = new Uuid(array( - 'versions' => array(Uuid::V1_MAC, Uuid::V4_RANDOM), - )); - - $this->validator->validate($uuid, $constraint); - - $this->assertNoViolation(); - } - - /** - * @dataProvider getValidStrictUuids - */ - public function testVersionConstraintIsInvalid($uuid) - { - $constraint = new Uuid(array( - 'versions' => array(Uuid::V2_DCE, Uuid::V3_MD5), - 'message' => 'myMessage', - )); - - $this->validator->validate($uuid, $constraint); - - $this->buildViolation('myMessage') - ->setParameter('{{ value }}', '"'.$uuid.'"') - ->assertRaised(); - } - /** * @dataProvider getValidNonStrictUuids */ @@ -155,20 +167,21 @@ public function getValidNonStrictUuids() array('216fff40-98d9-11e3-a5e2-0800200c9a66'), // Version 1 UUID in lowercase array('216FFF40-98D9-11E3-A5E2-0800200C9A66'), // Version 1 UUID in UPPERCASE array('456daefb-5aa6-41b5-8dbc-068b05a8b201'), // Version 4 UUID in lowercase - array('456DAEFb-5AA6-41B5-8DBC-068B05A8B201'), // Version 4 UUID in UPPERCASE + array('456DAEFb-5AA6-41B5-8DBC-068b05a8B201'), // Version 4 UUID in mixed case // Non-standard UUIDs allowed by some other systems array('216f-ff40-98d9-11e3-a5e2-0800-200c-9a66'), // Non-standard dash positions (every 4 chars) array('216fff40-98d911e3-a5e20800-200c9a66'), // Non-standard dash positions (every 8 chars) array('216fff4098d911e3a5e20800200c9a66'), // No dashes at all array('{216fff40-98d9-11e3-a5e2-0800200c9a66}'), // Wrapped with curly braces + array('[216fff40-98d9-11e3-a5e2-0800200c9a66]'), // Wrapped with squared braces ); } /** * @dataProvider getInvalidNonStrictUuids */ - public function testInvalidNonStrictUuids($uuid) + public function testInvalidNonStrictUuids($uuid, $code) { $constraint = new Uuid(array( 'strict' => false, @@ -179,16 +192,20 @@ public function testInvalidNonStrictUuids($uuid) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$uuid.'"') + ->setCode($code) ->assertRaised(); } public function getInvalidNonStrictUuids() { return array( - array('216fff40-98d9-11e3-a5e2-0800200c9a6'), // Too few characters - array('216fff40-98d9-11e3-a5e2-0800200c9a666'), // Too many characters - array('V16fff40-98d9-11e3-a5e2-0800200c9a66'), // Invalid character 'V' - array('2-16fff-4098d-911e3a5e20-800-200c9-a66'), // Non-standard dash positions (randomly placed) + array('216fff40-98d9-11e3-a5e2_0800200c9a66', Uuid::INVALID_CHARACTERS_ERROR), + array('216gff40-98d9-11e3-a5e2-0800200c9a66', Uuid::INVALID_CHARACTERS_ERROR), + array('216Gff40-98d9-11e3-a5e2-0800200c9a66', Uuid::INVALID_CHARACTERS_ERROR), + array('216fff40-98d9-11e3-a5e2_0800200c9a6', Uuid::INVALID_CHARACTERS_ERROR), + array('216fff40-98d9-11e3-a5e-20800200c9a66', Uuid::INVALID_HYPHEN_PLACEMENT_ERROR), + array('216fff40-98d9-11e3-a5e2-0800200c9a6', Uuid::TOO_SHORT_ERROR), + array('216fff40-98d9-11e3-a5e2-0800200c9a666', Uuid::TOO_LONG_ERROR), ); } } diff --git a/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php b/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php index eed7e2cb5c3ef..c214b9c07bc5a 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php @@ -574,7 +574,7 @@ public function testAddCustomizedViolation() ->setParameter('%param%', 'value') ->setInvalidValue('Invalid value') ->setPlural(2) - ->setCode('Code') + ->setCode(42) ->addViolation(); }; @@ -591,7 +591,7 @@ public function testAddCustomizedViolation() $this->assertSame($entity, $violations[0]->getRoot()); $this->assertSame('Invalid value', $violations[0]->getInvalidValue()); $this->assertSame(2, $violations[0]->getMessagePluralization()); - $this->assertSame('Code', $violations[0]->getCode()); + $this->assertSame(42, $violations[0]->getCode()); } /** diff --git a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php index 9cb3fdb7e6efc..5998cbe529d17 100644 --- a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php +++ b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php @@ -93,11 +93,9 @@ public function setPlural($number); /** * Sets the violation code. * - * @param mixed $code The violation code + * @param int $code The violation code * * @return ConstraintViolationBuilderInterface This builder - * - * @internal This method is internal and should not be used by user code */ public function setCode($code); From cfc04a6e8dd96c4a2dc7bf9d75c26afd5966ec0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Sat, 15 Feb 2014 22:26:14 +0100 Subject: [PATCH 247/373] [TwigBridge] Added form_twitter_bootstrap_layout.html.twig --- .../bootstrap_3_horizontal_layout.html.twig | 72 ++++++ .../views/Form/bootstrap_3_layout.html.twig | 231 ++++++++++++++++++ 2 files changed, 303 insertions(+) create mode 100644 src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig create mode 100644 src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig new file mode 100644 index 0000000000000..6fc226da4fce4 --- /dev/null +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig @@ -0,0 +1,72 @@ +{% extends "bootstrap_3_layout.html.twig" %} + +{% block form_start -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-horizontal')|trim}) %} + {{- parent() -}} +{%- endblock form_start %} + +{# Labels #} + +{% block form_label -%} +{% spaceless %} + {% if label is sameas(false) %} +
    + {% else %} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ block('form_label_class'))|trim}) %} + {{- parent() -}} + {% endif %} +{% endspaceless %} +{%- endblock form_label %} + +{% block form_label_class -%} +col-sm-2 +{%- endblock form_label_class %} + +{# Rows #} + +{% block form_row -%} +{% spaceless %} +
    + {{ form_label(form) }} +
    + {{ form_widget(form) }} + {{ form_errors(form) }} +
    +
    +{% endspaceless %} +{%- endblock form_row %} + +{% block checkbox_row -%} + {{- block('checkbox_radio_row') -}} +{%- endblock checkbox_row %} + +{% block radio_row -%} + {{- block('checkbox_radio_row') -}} +{%- endblock radio_row %} + +{% block checkbox_radio_row -%} +{% spaceless %} +
    +
    +
    + {{ form_widget(form) }} + {{ form_errors(form) }} +
    +
    +{% endspaceless %} +{%- endblock checkbox_radio_row %} + +{% block submit_row -%} +{% spaceless %} +
    +
    +
    + {{ form_widget(form) }} +
    +
    +{% endspaceless %} +{% endblock submit_row %} + +{% block form_group_class -%} +col-sm-10 +{%- endblock form_group_class %} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig new file mode 100644 index 0000000000000..db0971f3827ec --- /dev/null +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -0,0 +1,231 @@ +{% extends "form_div_layout.html.twig" %} + +{# Widgets #} + +{% block form_widget_simple -%} + {% if type is not defined or 'file' != type %} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) %} + {% endif %} + {{- parent() -}} +{%- endblock form_widget_simple %} + +{% block textarea_widget -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) %} + {{- parent() -}} +{%- endblock textarea_widget %} + +{% block submit_widget -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' btn')|trim}) %} + {{- parent() -}} +{%- endblock %} + +{% block button_widget -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' btn')|trim}) %} + {{- parent() -}} +{%- endblock %} + +{% block money_widget -%} +
    + {% set prepend = '{{' == money_pattern[0:2] %} + {% if not prepend %} + {{ money_pattern|replace({ '{{ widget }}':''}) }} + {% endif %} + {{- block('form_widget_simple') -}} + {% if prepend %} + {{ money_pattern|replace({ '{{ widget }}':''}) }} + {% endif %} +
    +{%- endblock money_widget %} + +{% block percent_widget -%} +
    + {{- block('form_widget_simple') -}} + % +
    +{%- endblock percent_widget %} + +{% block datetime_widget -%} + {% if widget == 'single_text' %} + {{- block('form_widget_simple') -}} + {% else %} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-inline')|trim}) %} +
    + {{ form_errors(form.date) }} + {{ form_errors(form.time) }} + {{ form_widget(form.date, { datetime: true } ) }}  + {{ form_widget(form.time, { datetime: true } ) }} +
    + {% endif %} +{%- endblock datetime_widget %} + +{% block date_widget -%} + {% if widget == 'single_text' %} + {{- block('form_widget_simple') -}} + {% else %} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-inline')|trim}) %} + {% if datetime is not defined or not datetime %} +
    + {% endif %} + {{ date_pattern|replace({ + '{{ year }}': form_widget(form.year), + '{{ month }}': form_widget(form.month), + '{{ day }}': form_widget(form.day), + })|raw }} + {% if datetime is not defined or not datetime %} +
    + {% endif %} + {% endif %} +{%- endblock date_widget %} + +{% block time_widget -%} + {% if widget == 'single_text' %} + {{- block('form_widget_simple') -}} + {% else %} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-inline')|trim}) %} + {% if datetime is not defined or false == datetime %} +
    + {% endif %} + {{ form_widget(form.hour) }}:{{ form_widget(form.minute) }}{% if with_seconds %}:{{ form_widget(form.second) }}{% endif %} + {% if datetime is not defined or false == datetime %} +
    + {% endif %} + {% endif %} +{%- endblock time_widget %} + +{% block choice_widget_collapsed -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) %} + {{- parent() -}} +{%- endblock %} + +{% block choice_widget_expanded -%} + {% if '-inline' in label_attr.class|default('') %} +
    + {% for child in form %} + {{ form_widget(child, { + parent_label_class: label_attr.class|default(''), + }) }} + {% endfor %} +
    + {% else %} +
    + {% for child in form %} + {{ form_widget(child, { + parent_label_class: label_attr.class|default(''), + }) }} + {% endfor %} +
    + {% endif %} +{%- endblock choice_widget_expanded %} + +{% block checkbox_widget -%} + {% set parent_label_class = parent_label_class|default('') %} + {% if 'checkbox-inline' in parent_label_class %} + {{ form_label(form, null, { widget: parent() }) }} + {% else %} +
    + {{ form_label(form, null, { widget: parent() }) }} +
    + {% endif %} +{%- endblock checkbox_widget %} + +{% block radio_widget -%} + {% set parent_label_class = parent_label_class|default('') %} + {% if 'radio-inline' in parent_label_class %} + {{ form_label(form, null, { widget: parent() }) }} + {% else %} +
    + {{ form_label(form, null, { widget: parent() }) }} +
    + {% endif %} +{%- endblock radio_widget %} + +{# Labels #} + +{% block form_label -%} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' control-label')|trim}) %} + {{- parent() -}} +{%- endblock form_label %} + +{% block choice_label %} + {# remove the checkbox-inline, it's only use full for embed labels #} + {% set label_attr = label_attr|merge({class: label_attr.class|default('')|replace({'checkbox-inline': ''})|trim}) %} + {{- block('form_label') -}} +{% endblock %} + +{% block checkbox_label -%} + {{- block('checkbox_radio_label') -}} +{%- endblock checkbox_label %} + +{% block radio_label -%} + {{- block('checkbox_radio_label') -}} +{%- endblock radio_label %} + +{% block checkbox_radio_label %} + {% if required %} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %} + {% endif %} + {% if parent_label_class is defined %} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ parent_label_class)|trim}) %} + {% endif %} + {% if label is empty %} + {% set label = name|humanize %} + {% endif %} + + {{ widget|raw }} + {{ label|trans({}, translation_domain) }} + +{% endblock checkbox_radio_label %} + +{# Rows #} + +{% block form_row -%} +
    + {{ form_label(form) }} + {{ form_widget(form) }} + {{ form_errors(form) }} +
    +{%- endblock form_row %} + +{% block choice_row -%} + {% set force_error = true %} + {{ block('form_row') }} +{%- endblock choice_row %} + +{% block date_row -%} + {% set force_error = true %} + {{ block('form_row') }} +{%- endblock date_row %} + +{% block time_row -%} + {% set force_error = true %} + {{ block('form_row') }} +{%- endblock time_row %} + +{% block datetime_row -%} + {% set force_error = true %} + {{ block('form_row') }} +{%- endblock datetime_row %} + +{% block checkbox_row -%} +
    + {{ form_widget(form) }} + {{ form_errors(form) }} +
    +{%- endblock checkbox_row %} + +{% block radio_row -%} +
    + {{ form_widget(form) }} + {{ form_errors(form) }} +
    +{%- endblock radio_row %} + +{# Errors #} + +{% block form_errors -%} + {% if errors|length > 0 %} + {% if form.parent %}{% else %}
    {% endif %} + {{- parent() -}} + {% if form.parent %}{% else %}
    {% endif %} + {% endif %} +{%- endblock form_errors %} From ee82392074ec25e8e20773a43e71bc4e79a9bf3f Mon Sep 17 00:00:00 2001 From: Nikita Konstantinov Date: Sat, 27 Sep 2014 20:06:30 +0400 Subject: [PATCH 248/373] [DependencyInjection] Tweaked factories --- .../DependencyInjection/ContainerBuilder.php | 14 +++- .../DependencyInjection/Definition.php | 4 ++ .../DependencyInjection/Dumper/PhpDumper.php | 32 ++++++++-- .../Loader/YamlFileLoader.php | 2 +- .../Tests/ContainerBuilderTest.php | 17 +++++ .../Tests/DefinitionTest.php | 4 ++ .../Tests/Dumper/PhpDumperTest.php | 8 +++ .../Tests/Fixtures/containers/container19.php | 22 +++++++ .../Tests/Fixtures/containers/container9.php | 4 ++ .../Tests/Fixtures/graphviz/services9.dot | 1 + .../Tests/Fixtures/php/services19.php | 64 +++++++++++++++++++ .../Tests/Fixtures/php/services9.php | 14 ++++ .../Tests/Fixtures/php/services9_compiled.php | 14 ++++ .../Tests/Fixtures/xml/services9.xml | 3 + .../Tests/Fixtures/yaml/services14.yml | 1 + .../Tests/Fixtures/yaml/services9.yml | 3 + .../Tests/Loader/YamlFileLoaderTest.php | 3 +- 17 files changed, 203 insertions(+), 7 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index e244bf1857826..1d091a097f2b1 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -941,7 +941,19 @@ public function createService(Definition $definition, $id, $tryProxy = true) $arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments()))); - if (null !== $definition->getFactoryMethod()) { + if (null !== $definition->getFactory()) { + $factory = $definition->getFactory(); + + if (is_string($factory)) { + $callable = $definition->getFactory(); + } elseif (is_array($factory)) { + $callable = array($this->resolveServices($factory[0]), $factory[1]); + } else { + throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); + } + + $service = call_user_func_array($callable, $arguments); + } elseif (null !== $definition->getFactoryMethod()) { if (null !== $definition->getFactoryClass()) { $factory = $parameterBag->resolveValue($definition->getFactoryClass()); } elseif (null !== $definition->getFactoryService()) { diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 920fd3bd5766e..b77a13737a5bb 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -66,6 +66,10 @@ public function __construct($class = null, array $arguments = array()) */ public function setFactory($factory) { + if (is_string($factory) && strpos($factory, '::') !== false) { + $factory = explode('::', $factory, 2); + } + $this->factory = $factory; return $this; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 71732b12bfecc..ba90bf514e864 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -730,13 +730,13 @@ private function addNewInstance($id, Definition $definition, $return, $instantia $class = $this->dumpValue($callable[0]); // If the class is a string we can optimize call_user_func away if (strpos($class, "'") === 0) { - return sprintf(" $return{$instantiation}\%s::%s(%s);\n", substr($class, 1, -1), $callable[1], $arguments ? implode(', ', $arguments) : ''); + return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : ''); } - return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s'), %s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : ''); + return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : ''); } - return sprintf(" $return{$instantiation}%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : ''); + return sprintf(" $return{$instantiation}\\%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : ''); } elseif (null !== $definition->getFactoryMethod()) { if (null !== $definition->getFactoryClass()) { $class = $this->dumpValue($definition->getFactoryClass()); @@ -1212,7 +1212,7 @@ private function hasReference($id, array $arguments, $deep = false, array $visit /** * Dumps values. * - * @param array $value + * @param mixed $value * @param bool $interpolate * * @return string @@ -1249,6 +1249,30 @@ private function dumpValue($value, $interpolate = true) throw new RuntimeException('Cannot dump definitions which have a variable class name.'); } + if (null !== $value->getFactory()) { + $factory = $value->getFactory(); + + if (is_string($factory)) { + return sprintf('\\%s(%s)', $factory, implode(', ', $arguments)); + } + + if (is_array($factory)) { + if (is_string($factory[0])) { + return sprintf('\\%s::%s(%s)', $factory[0], $factory[1], implode(', ', $arguments)); + } + + if ($factory[0] instanceof Definition) { + return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); + } + + if ($factory[0] instanceof Reference) { + return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments)); + } + } + + throw new RuntimeException('Cannot dump definition because of invalid factory'); + } + if (null !== $value->getFactoryMethod()) { if (null !== $value->getFactoryClass()) { return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 202ac138edf36..bb53f8be1387e 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -196,7 +196,7 @@ private function parseDefinition($id, $service, $file) if (isset($service['factory'])) { if (is_string($service['factory'])) { - if (strpos($service['factory'], ':')) { + if (strpos($service['factory'], ':') !== false && strpos($service['factory'], '::') === false) { $parts = explode(':', $service['factory']); $definition->setFactory(array($this->resolveServices('@'.$parts[0]), $parts[1])); } else { diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 714a07b768181..6e839653aa024 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -341,6 +341,23 @@ public function testCreateServiceFactoryService() $this->assertInstanceOf('BazClass', $builder->get('baz_service')); } + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + public function testCreateServiceFactory() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'Bar\FooClass')->setFactory('Bar\FooClass::getInstance'); + $builder->register('qux', 'Bar\FooClass')->setFactory(array('Bar\FooClass', 'getInstance')); + $builder->register('bar', 'Bar\FooClass')->setFactory(array(new Definition('Bar\FooClass'), 'getInstance')); + $builder->register('baz', 'Bar\FooClass')->setFactory(array(new Reference('bar'), 'getInstance')); + + $this->assertTrue($builder->get('foo')->called, '->createService() calls the factory method to create the service instance'); + $this->assertTrue($builder->get('qux')->called, '->createService() calls the factory method to create the service instance'); + $this->assertTrue($builder->get('bar')->called, '->createService() uses anonymous service as factory'); + $this->assertTrue($builder->get('baz')->called, '->createService() uses another service as factory'); + } + /** * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index dfe8a03bf6aac..cfcb5f486f0ca 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -34,8 +34,12 @@ public function testConstructor() public function testSetGetFactory() { $def = new Definition('stdClass'); + $this->assertSame($def, $def->setFactory('foo'), '->setFactory() implements a fluent interface'); $this->assertEquals('foo', $def->getFactory(), '->getFactory() returns the factory'); + + $def->setFactory('Foo::bar'); + $this->assertEquals(array('Foo', 'bar'), $def->getFactory(), '->setFactory() converts string static method call to the array'); } public function testSetGetFactoryClass() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 231ab773460a5..6cb04446e093a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -120,6 +120,14 @@ public function testAddService() } } + public function testServicesWithAnonymousFactories() + { + $container = include self::$fixturesPath.'/containers/container19.php'; + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services19.php', $dumper->dump(), '->dump() dumps services with anonymous factories'); + } + /** * @expectedException \InvalidArgumentException * @expectedExceptionMessage Service id "bar$" cannot be converted to a valid PHP method name. diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php new file mode 100644 index 0000000000000..64b8f066dca10 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php @@ -0,0 +1,22 @@ +register('service_from_anonymous_factory', 'Bar\FooClass') + ->setFactory(array(new Definition('Bar\FooClass'), 'getInstance')) +; + +$anonymousServiceWithFactory = new Definition('Bar\FooClass'); +$anonymousServiceWithFactory->setFactory('Bar\FooClass::getInstance'); +$container + ->register('service_with_method_call_and_factory', 'Bar\FooClass') + ->addMethodCall('setBar', array($anonymousServiceWithFactory)) +; + +return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php index aca2d012056af..49262e86326ef 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -114,5 +114,9 @@ ->setProperty('foo', 'bar') ->setFactory(array(new Reference('new_factory'), 'getInstance')) ; +$container + ->register('service_from_static_method', 'Bar\FooClass') + ->setFactory(array('Bar\FooClass', 'getInstance')) +; return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot index f4f498df2adae..e233d62594d0b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot @@ -21,6 +21,7 @@ digraph sc { node_decorator_service_with_name [label="decorator_service_with_name\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_new_factory [label="new_factory\nFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_new_factory_service [label="new_factory_service\nFooBarBaz\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_service_from_static_method [label="service_from_static_method\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"]; node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"]; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php new file mode 100644 index 0000000000000..746b3fa7765f9 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php @@ -0,0 +1,64 @@ +methodMap = array( + 'service_from_anonymous_factory' => 'getServiceFromAnonymousFactoryService', + 'service_with_method_call_and_factory' => 'getServiceWithMethodCallAndFactoryService', + ); + } + + /** + * Gets the 'service_from_anonymous_factory' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getServiceFromAnonymousFactoryService() + { + return $this->services['service_from_anonymous_factory'] = call_user_func(array(new \Bar\FooClass(), 'getInstance')); + } + + /** + * Gets the 'service_with_method_call_and_factory' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getServiceWithMethodCallAndFactoryService() + { + $this->services['service_with_method_call_and_factory'] = $instance = new \Bar\FooClass(); + + $instance->setBar(\Bar\FooClass::getInstance()); + + return $instance; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index 78eb1d8ee8efa..3cc3aac8b845b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -47,6 +47,7 @@ public function __construct() 'new_factory' => 'getNewFactoryService', 'new_factory_service' => 'getNewFactoryServiceService', 'request' => 'getRequestService', + 'service_from_static_method' => 'getServiceFromStaticMethodService', ); $this->aliases = array( 'alias_for_alias' => 'foo', @@ -303,6 +304,19 @@ protected function getRequestService() throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.'); } + /** + * Gets the 'service_from_static_method' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getServiceFromStaticMethodService() + { + return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); + } + /** * Updates the 'request' service. */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index 9f25dc289b323..5e8103bdd0cc9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -50,6 +50,7 @@ public function __construct() 'method_call1' => 'getMethodCall1Service', 'new_factory_service' => 'getNewFactoryServiceService', 'request' => 'getRequestService', + 'service_from_static_method' => 'getServiceFromStaticMethodService', ); $this->aliases = array( 'alias_for_alias' => 'foo', @@ -305,6 +306,19 @@ protected function getRequestService() throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.'); } + /** + * Gets the 'service_from_static_method' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getServiceFromStaticMethodService() + { + return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); + } + /** * Updates the 'request' service. */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index 459563bb0e1ef..7234a82cdc4f3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -98,6 +98,9 @@ bar
    + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml index a6f825e42e318..4c188c5fbc7b0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml @@ -1,2 +1,3 @@ services: factory: { class: FooBarClass, factory: baz:getClass} + factory_with_static_call: { class: FooBarClass, factory: FooBacFactory::createFooBar} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index e782ca5165ef2..0b8da43968787 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -96,5 +96,8 @@ services: class: FooBarBaz properties: { foo: bar } factory: ['@new_factory', getInstance] + service_from_static_method: + class: Bar\FooClass + factory: [Bar\FooClass, getInstance] alias_for_foo: @foo alias_for_alias: @foo diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 1bba6a1ac830d..3de0843dfbdfb 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -169,7 +169,8 @@ public function testLoadFactoryShortSyntax() $loader->load('services14.yml'); $services = $container->getDefinitions(); - $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['factory']->getFactory(), '->load() parses the factory tag'); + $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['factory']->getFactory(), '->load() parses the factory tag with service:method'); + $this->assertEquals(array('FooBacFactory', 'createFooBar'), $services['factory_with_static_call']->getFactory(), '->load() parses the factory tag with Class::method'); } public function testExtensions() From 16201b6d1a0c403d295a7a73cc7f7d50ea37ab0c Mon Sep 17 00:00:00 2001 From: Adrien Gallou Date: Sun, 6 Jul 2014 18:38:58 +0200 Subject: [PATCH 249/373] [FrameworkBundle] container:debug : list services if no service match exacly the name argument | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #11303 | License | MIT | Doc PR | If we launch the command "container:debug log" and there is no service nammed log, it will print something like this : ``` [0] logger [1] monolog.handler.console [2] monolog.handler.debug ``` After the service has been chosen, usual container:debug informations are displayed. --- .../Command/ContainerDebugCommand.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index 84be032173313..0663fb3acc6a1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -19,6 +19,7 @@ use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; +use Symfony\Component\Console\Question\ChoiceQuestion; /** * A console command for retrieving information about services. @@ -106,6 +107,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $options = array('tag' => $tag, 'show_private' => $input->getOption('show-private')); } elseif ($name = $input->getArgument('name')) { $object = $this->getContainerBuilder(); + $name = $this->findProperServiceName($input, $output, $object, $name); $options = array('id' => $name); } else { $object = $this->getContainerBuilder(); @@ -116,6 +118,10 @@ protected function execute(InputInterface $input, OutputInterface $output) $options['format'] = $input->getOption('format'); $options['raw_text'] = $input->getOption('raw'); $helper->describe($output, $object, $options); + + if (!$input->getArgument('name') && $input->isInteractive()) { + $output->writeln('To search for a service, re-run this command with a search term. container:debug log'); + } } /** @@ -168,4 +174,31 @@ protected function getContainerBuilder() return $container; } + + private function findProperServiceName(InputInterface $input, OutputInterface $output, ContainerBuilder $builder, $name) + { + if ($builder->has($name) || !$input->isInteractive()) { + return $name; + } + + $question = new ChoiceQuestion('Choose a number for more information on the service', $this->findServiceIdsContaining($builder, $name)); + $question->setErrorMessage('Service %s is invalid.'); + + return $this->getHelper('question')->ask($input, $output, $question); + } + + private function findServiceIdsContaining(ContainerBuilder $builder, $name) + { + $serviceIds = $builder->getServiceIds(); + $foundServiceIds = array(); + $name = strtolower($name); + foreach ($serviceIds as $serviceId) { + if (false === strpos($serviceId, $name)) { + continue; + } + $foundServiceIds[] = $serviceId; + } + + return $foundServiceIds; + } } From 4d0ab7ddc1088e6af7c0b3944b77e10b19bd09a0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 27 Sep 2014 09:00:54 +0200 Subject: [PATCH 250/373] [FrameworkBundle] enable ErrorHandler in prod --- .../FrameworkExtension.php | 6 ++- .../Resources/config/debug_prod.xml | 3 ++ src/Symfony/Component/Debug/ErrorHandler.php | 10 +++- .../Debug/Tests/ErrorHandlerTest.php | 24 +++++++++ .../EventListener/DebugHandlersListener.php | 42 ++++++++++++++- .../DebugHandlersListenerTest.php | 52 +++++++++++++++++++ 6 files changed, 131 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index e02f927e7a188..ecfd5531b8436 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -133,8 +133,6 @@ public function load(array $configs, ContainerBuilder $container) if ($container->getParameter('kernel.debug')) { $loader->load('debug.xml'); - $definition->replaceArgument(0, array(new Reference('http_kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'terminateWithException')); - $definition = $container->findDefinition('http_kernel'); $definition->replaceArgument(2, new Reference('debug.controller_resolver')); @@ -145,11 +143,15 @@ public function load(array $configs, ContainerBuilder $container) $container->setAlias('event_dispatcher', 'debug.event_dispatcher'); } else { $definition->replaceArgument(2, E_COMPILE_ERROR | E_PARSE | E_ERROR | E_CORE_ERROR); + + $container->findDefinition('debug.error_handler')->addMethodCall('throwAt', array(0)); } $this->addClassesToCompile(array( 'Symfony\\Component\\Config\\FileLocator', + 'Symfony\\Component\\Debug\\ErrorHandler', + 'Symfony\\Component\\EventDispatcher\\Event', 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher', diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml index 983e9f4c821e0..d830006eca425 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml @@ -6,6 +6,7 @@ Symfony\Component\HttpKernel\EventListener\DebugHandlersListener + Symfony\Component\Debug\ErrorHandler Symfony\Component\Stopwatch\Stopwatch @@ -20,6 +21,8 @@ null + + diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 5982b0c48246f..c9f1d6d6b35ef 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -124,9 +124,15 @@ public static function register($levels = -1, $throw = true) $handler = new static(); $levels &= $handler->thrownErrors; - set_error_handler(array($handler, 'handleError'), $levels); + $prev = set_error_handler(array($handler, 'handleError'), $levels); + $prev = is_array($prev) ? $prev[0] : null; + if ($prev instanceof self) { + restore_error_handler(); + $handler = $prev; + } else { + $handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException'))); + } $handler->throwAt($throw ? $levels : 0, true); - $handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException'))); return $handler; } diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index aaaefd54dd1ce..9ed5966813970 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -46,6 +46,30 @@ public function tearDown() error_reporting($this->errorReporting); } + public function testRegister() + { + $handler = ErrorHandler::register(); + + try { + $this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler); + + try { + $this->assertSame($handler, ErrorHandler::register()); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + } + } catch (\Exception $e) { + } + + restore_error_handler(); + restore_exception_handler(); + + if (isset($e)) { + throw $e; + } + } + public function testNotice() { ErrorHandler::register(); diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php index 2c18bd143be06..eba7859f445c8 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php @@ -14,8 +14,14 @@ use Psr\Log\LoggerInterface; use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\KernelEvent; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Output\ConsoleOutputInterface; /** * Configures errors and exceptions handlers. @@ -28,6 +34,7 @@ class DebugHandlersListener implements EventSubscriberInterface private $logger; private $levels; private $debug; + private $fileLinkFormat; /** * @param callable $exceptionHandler A handler that will be called on Exception @@ -45,8 +52,20 @@ public function __construct($exceptionHandler, LoggerInterface $logger = null, $ $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); } - public function configure() + /** + * Configures the error handler. + * + * @param Event|null $event The triggering event + * @param string|null $eventName The triggering event name + * @param EventDispatcherInterface|null $eventDispatcher The dispatcher used to trigger $event + */ + public function configure(Event $event = null, $eventName = null, EventDispatcherInterface $eventDispatcher = null) { + if (null !== $eventDispatcher) { + foreach (array_keys(static::getSubscribedEvents()) as $name) { + $eventDispatcher->removeListener($name, array($this, 'configure')); + } + } if ($this->logger) { $handler = set_error_handler('var_dump', 0); $handler = is_array($handler) ? $handler[0] : null; @@ -67,6 +86,19 @@ public function configure() } $this->logger = $this->levels = null; } + if (!$this->exceptionHandler) { + if ($event instanceof KernelEvent) { + $this->exceptionHandler = array($event->getKernel(), 'terminateWithException'); + } elseif ($event instanceof ConsoleEvent && $app = $event->getCommand()->getApplication()) { + $output = $event->getOutput(); + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + $this->exceptionHandler = function ($e) use ($app, $output) { + $app->renderException($e, $output); + }; + } + } if ($this->exceptionHandler) { $handler = set_exception_handler('var_dump'); $handler = is_array($handler) ? $handler[0] : null; @@ -86,6 +118,12 @@ public function configure() public static function getSubscribedEvents() { - return array(KernelEvents::REQUEST => array('configure', 2048)); + $events = array(KernelEvents::REQUEST => array('configure', 2048)); + + if (defined('Symfony\Component\Console\ConsoleEvents::COMMAND')) { + $events[ConsoleEvents::COMMAND] = array('configure', 2048); + } + + return $events; } } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/DebugHandlersListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/DebugHandlersListenerTest.php index dabc377af0838..976d1e938c41e 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/DebugHandlersListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/DebugHandlersListenerTest.php @@ -12,9 +12,17 @@ namespace Symfony\Component\HttpKernel\Tests\EventListener; use Psr\Log\LogLevel; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpKernel\EventListener\DebugHandlersListener; +use Symfony\Component\HttpKernel\KernelEvents; /** * DebugHandlersListenerTest @@ -53,4 +61,48 @@ public function testConfigure() $this->assertArrayHasKey(E_DEPRECATED, $loggers); $this->assertSame(array($logger, LogLevel::INFO), $loggers[E_DEPRECATED]); } + + public function testConsoleEvent() + { + $dispatcher = new EventDispatcher(); + $listener = new DebugHandlersListener(null); + $app = $this->getMock('Symfony\Component\Console\Application'); + $app->expects($this->once())->method('getHelperSet')->will($this->returnValue(new HelperSet())); + $command = new Command(__FUNCTION__); + $command->setApplication($app); + $event = new ConsoleEvent($command, new ArgvInput(), new ConsoleOutput()); + + $dispatcher->addSubscriber($listener); + + $xListeners = array( + KernelEvents::REQUEST => array(array($listener, 'configure')), + ConsoleEvents::COMMAND => array(array($listener, 'configure')), + ); + $this->assertSame($xListeners, $dispatcher->getListeners()); + + $exception = null; + $eHandler = new ErrorHandler(); + set_error_handler(array($eHandler, 'handleError')); + set_exception_handler(array($eHandler, 'handleException')); + try { + $dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + } catch (\Exception $exception) { + } + restore_exception_handler(); + restore_error_handler(); + + if (null !== $exception) { + throw $exception; + } + + $this->assertSame(array(), $dispatcher->getListeners()); + + $xHandler = $eHandler->setExceptionHandler('var_dump'); + $this->assertInstanceOf('Closure', $xHandler); + + $app->expects($this->once()) + ->method('renderException'); + + $xHandler(new \Exception()); + } } From 4acf5d3d0b8226a970dc14b79966f31de6c0d2c2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 30 Sep 2014 09:39:40 +0200 Subject: [PATCH 251/373] [Debug] make screaming configurable --- .../FrameworkExtension.php | 6 +-- .../Resources/config/debug_prod.xml | 3 +- .../EventListener/DebugHandlersListener.php | 42 +++++++++++-------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index ecfd5531b8436..093b48a6493e6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -127,12 +127,14 @@ public function load(array $configs, ContainerBuilder $container) $definition = $container->findDefinition('debug.debug_handlers_listener'); if ($container->hasParameter('templating.helper.code.file_link_format')) { - $definition->replaceArgument(4, '%templating.helper.code.file_link_format%'); + $definition->replaceArgument(5, '%templating.helper.code.file_link_format%'); } if ($container->getParameter('kernel.debug')) { $loader->load('debug.xml'); + $definition->replaceArgument(3, E_ALL | E_STRICT); + $definition = $container->findDefinition('http_kernel'); $definition->replaceArgument(2, new Reference('debug.controller_resolver')); @@ -143,8 +145,6 @@ public function load(array $configs, ContainerBuilder $container) $container->setAlias('event_dispatcher', 'debug.event_dispatcher'); } else { $definition->replaceArgument(2, E_COMPILE_ERROR | E_PARSE | E_ERROR | E_CORE_ERROR); - - $container->findDefinition('debug.error_handler')->addMethodCall('throwAt', array(0)); } $this->addClassesToCompile(array( diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml index d830006eca425..e5588b2d5180d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml @@ -17,7 +17,8 @@ - %kernel.debug% + 0 + true null diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php index eba7859f445c8..9d4258d54aaea 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php @@ -33,22 +33,25 @@ class DebugHandlersListener implements EventSubscriberInterface private $exceptionHandler; private $logger; private $levels; - private $debug; + private $throwAt; + private $scream; private $fileLinkFormat; /** - * @param callable $exceptionHandler A handler that will be called on Exception + * @param callable|null $exceptionHandler A handler that will be called on Exception * @param LoggerInterface|null $logger A PSR-3 logger * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants - * @param bool $debug Enables/disables debug mode + * @param int|null $throwAt Thrown errors in a bit field of E_* constants, or null to keep the current value + * @param bool $scream Enables/disables screaming mode, where even silenced errors are logged * @param string $fileLinkFormat The format for links to source files */ - public function __construct($exceptionHandler, LoggerInterface $logger = null, $levels = null, $debug = true, $fileLinkFormat = null) + public function __construct($exceptionHandler, LoggerInterface $logger = null, $levels = null, $throwAt = -1, $scream = true, $fileLinkFormat = null) { $this->exceptionHandler = $exceptionHandler; $this->logger = $logger; $this->levels = $levels; - $this->debug = $debug; + $this->throwAt = is_numeric($throwAt) ? (int) $throwAt : (null === $throwAt ? null : ($throwAt ? -1 : null)); + $this->scream = (bool) $scream; $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); } @@ -66,25 +69,28 @@ public function configure(Event $event = null, $eventName = null, EventDispatche $eventDispatcher->removeListener($name, array($this, 'configure')); } } - if ($this->logger) { - $handler = set_error_handler('var_dump', 0); - $handler = is_array($handler) ? $handler[0] : null; - restore_error_handler(); - if ($handler instanceof ErrorHandler) { - if ($this->debug) { - $handler->throwAt(-1); - } + $handler = set_error_handler('var_dump', 0); + $handler = is_array($handler) ? $handler[0] : null; + restore_error_handler(); + if ($handler instanceof ErrorHandler) { + if ($this->logger) { $handler->setDefaultLogger($this->logger, $this->levels); if (is_array($this->levels)) { $scream = 0; foreach ($this->levels as $type => $log) { $scream |= $type; } - $this->levels = $scream; + } else { + $scream = null === $this->levels ? E_ALL | E_STRICT : $this->levels; } - $handler->screamAt($this->levels); + if ($this->scream) { + $handler->screamAt($scream); + } + $this->logger = $this->levels = null; + } + if (null !== $this->throwAt) { + $handler->throwAt($this->throwAt, true); } - $this->logger = $this->levels = null; } if (!$this->exceptionHandler) { if ($event instanceof KernelEvent) { @@ -110,7 +116,9 @@ public function configure(Event $event = null, $eventName = null, EventDispatche } if ($handler instanceof ExceptionHandler) { $handler->setHandler($this->exceptionHandler); - $handler->setFileLinkFormat($this->fileLinkFormat); + if (null !== $this->fileLinkFormat) { + $handler->setFileLinkFormat($this->fileLinkFormat); + } } $this->exceptionHandler = null; } From fac3cc4d035eb23681d8c35efccf9fd6071e3b78 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 2 Oct 2014 17:46:44 +0200 Subject: [PATCH 252/373] [FrameworkBundle] register ErrorHandler at boot time --- .../DependencyInjection/FrameworkExtension.php | 2 -- src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php | 3 +++ .../Bundle/FrameworkBundle/Resources/config/debug.xml | 1 + .../Bundle/FrameworkBundle/Resources/config/debug_prod.xml | 6 ++---- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 093b48a6493e6..dedb5b34184ef 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -133,8 +133,6 @@ public function load(array $configs, ContainerBuilder $container) if ($container->getParameter('kernel.debug')) { $loader->load('debug.xml'); - $definition->replaceArgument(3, E_ALL | E_STRICT); - $definition = $container->findDefinition('http_kernel'); $definition->replaceArgument(2, new Reference('debug.controller_resolver')); diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 08a1eee87a09c..cb7d1e125b4b3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -30,6 +30,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FragmentRendererPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass; +use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\Scope; @@ -46,6 +47,8 @@ class FrameworkBundle extends Bundle { public function boot() { + ErrorHandler::register($this->container->getParameter('debug.error_handler.throw_at')); + if ($trustedProxies = $this->container->getParameter('kernel.trusted_proxies')) { Request::setTrustedProxies($trustedProxies); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml index 1cdb0631bae9c..819f9623b004f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml @@ -8,6 +8,7 @@ Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher %kernel.cache_dir%/%kernel.container_class%.xml Symfony\Component\HttpKernel\Controller\TraceableControllerResolver + -1 diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml index e5588b2d5180d..39266eab0363c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml @@ -6,8 +6,8 @@ Symfony\Component\HttpKernel\EventListener\DebugHandlersListener - Symfony\Component\Debug\ErrorHandler Symfony\Component\Stopwatch\Stopwatch + 0 @@ -17,13 +17,11 @@ - 0 + null true null - - From 1bc6680928b5601ecef29f358d8311f847280dae Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Sun, 8 Jun 2014 00:54:38 +0200 Subject: [PATCH 253/373] [HttpFoundation] implement different locking strategies for sessions --- UPGRADE-2.6.md | 11 +- .../Component/HttpFoundation/CHANGELOG.md | 6 +- .../Storage/Handler/PdoSessionHandler.php | 360 ++++++++++++------ .../Storage/Handler/PdoSessionHandlerTest.php | 4 +- 4 files changed, 263 insertions(+), 118 deletions(-) diff --git a/UPGRADE-2.6.md b/UPGRADE-2.6.md index 5fbdc90743e80..817d35f5e7b9a 100644 --- a/UPGRADE-2.6.md +++ b/UPGRADE-2.6.md @@ -105,8 +105,8 @@ Security HttpFoundation -------------- - * The PdoSessionHandler to store sessions in a database changed significantly. - - It now implements session locking to prevent loss of data by concurrent access to the same session. + * The `PdoSessionHandler` to store sessions in a database changed significantly. + - By default, it now implements session locking to prevent loss of data by concurrent access to the same session. - It does so using a transaction between opening and closing a session. For this reason, it's not recommended to use the same database connection that you also use for your application logic. Otherwise you have to make sure to access your database after the session is closed and committed. @@ -115,11 +115,16 @@ HttpFoundation - Since accessing a session now blocks when the same session is still open, it is best practice to save the session as soon as you don't need to write to it anymore. For example, read-only AJAX request to a session can save the session immediately after opening it to increase concurrency. + - As alternative to transactional locking you can also use advisory locks which do not require a transaction. + Additionally, you can also revert back to no locking in case you have custom logic to deal with race conditions + like an optimistic concurrency control approach. The locking strategy can be chosen by passing the corresponding + constant as `lock_mode` option, e.g. `new PdoSessionHandler($pdoOrDsn, array('lock_mode' => PdoSessionHandler::LOCK_NONE))`. + For more information please read the class documentation. - The expected schema of the table changed. - Session data is binary text that can contain null bytes and thus should also be saved as-is in a binary column like BLOB. For this reason, the handler does not base64_encode the data anymore. - A new column to store the lifetime of a session is required. This allows to have different lifetimes per session configured via session.gc_maxlifetime ini setting. - You would need to migrate the table manually if you want to keep session information of your users. - - You could use PdoSessionHandler::createTable to initialize a correctly defined table depending on + - You could use `PdoSessionHandler::createTable` to initialize a correctly defined table depending on the used database vendor. diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index 0b25cf89d2bb4..dcdeb4ebf9664 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -5,9 +5,9 @@ CHANGELOG ----- * PdoSessionHandler changes - - implemented session locking to prevent loss of data by concurrent access to the same session - - save session data in a binary column without base64_encode - - added lifetime column to the session table which allows to have different lifetimes for each session + - implemented different session locking strategies to prevent loss of data by concurrent access to the same session + - [BC BREAK] save session data in a binary column without base64_encode + - [BC BREAK] added lifetime column to the session table which allows to have different lifetimes for each session - implemented lazy connections that are only opened when a session is used by either passing a dsn string explicitly or falling back to session.save_path ini setting - added a createTable method that initializes a correctly defined table depending on the database vendor diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index f8cc28ff7b7fe..26c5f8bc0ee77 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -15,9 +15,12 @@ * Session handler using a PDO connection to read and write data. * * It works with MySQL, PostgreSQL, Oracle, SQL Server and SQLite and implements - * locking of sessions to prevent loss of data by concurrent access to the same session. - * This means requests for the same session will wait until the other one finished. - * PHPs internal files session handler also works this way. + * different locking strategies to handle concurrent access to the same session. + * Locking is necessary to prevent loss of data due to race conditions and to keep + * the session data consistent between read() and write(). With locking, requests + * for the same session will wait until the other one finished writing. For this + * reason it's best practice to close a session as early as possible to improve + * concurrency. PHPs internal files session handler also implements locking. * * Attention: Since SQLite does not support row level locks but locks the whole database, * it means only one session can be accessed at a time. Even different sessions would wait @@ -37,6 +40,31 @@ */ class PdoSessionHandler implements \SessionHandlerInterface { + /** + * No locking is done. This means sessions are prone to loss of data due to + * race conditions of concurrent requests to the same session. The last session + * write will win in this case. It might be useful when you implement your own + * logic to deal with this like an optimistic approach. + */ + const LOCK_NONE = 0; + + /** + * Creates an application-level lock on a session. The disadvantage is that the + * lock is not enforced by the database and thus other, unaware parts of the + * application could still concurrently modify the session. The advantage is it + * does not require a transaction. + * This mode is not available for SQLite and not yet implemented for oci and sqlsrv. + */ + const LOCK_ADVISORY = 1; + + /** + * Issues a real row lock. Since it uses a transaction between opening and + * closing a session, you have to be careful when you use same database connection + * that you also use for your application logic. This mode is the default because + * it's the only reliable solution across DBMSs. + */ + const LOCK_TRANSACTIONAL = 2; + /** * @var \PDO|null PDO instance or null when not connected yet */ @@ -55,43 +83,60 @@ class PdoSessionHandler implements \SessionHandlerInterface /** * @var string Table name */ - private $table; + private $table = 'sessions'; /** * @var string Column for session id */ - private $idCol; + private $idCol = 'sess_id'; /** * @var string Column for session data */ - private $dataCol; + private $dataCol = 'sess_data'; /** * @var string Column for lifetime */ - private $lifetimeCol; + private $lifetimeCol = 'sess_lifetime'; /** * @var string Column for timestamp */ - private $timeCol; + private $timeCol = 'sess_time'; /** * @var string Username when lazy-connect */ - private $username; + private $username = ''; /** * @var string Password when lazy-connect */ - private $password; + private $password = ''; /** * @var array Connection options when lazy-connect */ private $connectionOptions = array(); + /** + * @var int The strategy for locking, see constants + */ + private $lockMode = self::LOCK_TRANSACTIONAL; + + /** + * It's an array to support multiple reads before closing which is manual, non-standard usage + * + * @var \PDOStatement[] An array of statements to release advisory locks + */ + private $unlockStatements = array(); + + /** + * @var bool True when the current session exists but expired according to session.gc_maxlifetime + */ + private $sessionExpired = false; + /** * @var bool Whether a transaction is active */ @@ -109,9 +154,6 @@ class PdoSessionHandler implements \SessionHandlerInterface * pass a DSN string that will be used to lazy-connect to the database * when the session is actually used. Furthermore it's possible to pass null * which will then use the session.save_path ini setting as PDO DSN parameter. - * Since locking uses a transaction between opening and closing a session, - * it's not recommended to use the same database connection that you also use - * for your application logic. * * List of available options: * * db_table: The name of the table [default: sessions] @@ -122,13 +164,14 @@ class PdoSessionHandler implements \SessionHandlerInterface * * db_username: The username when lazy-connect [default: ''] * * db_password: The password when lazy-connect [default: ''] * * db_connection_options: An array of driver-specific connection options [default: array()] + * * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL] * * @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or null - * @param array $options An associative array of DB options + * @param array $options An associative array of options * * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION */ - public function __construct($pdoOrDsn, array $options = array()) + public function __construct($pdoOrDsn = null, array $options = array()) { if ($pdoOrDsn instanceof \PDO) { if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) { @@ -141,32 +184,22 @@ public function __construct($pdoOrDsn, array $options = array()) $this->dsn = $pdoOrDsn; } - $options = array_replace(array( - 'db_table' => 'sessions', - 'db_id_col' => 'sess_id', - 'db_data_col' => 'sess_data', - 'db_lifetime_col' => 'sess_lifetime', - 'db_time_col' => 'sess_time', - 'db_username' => '', - 'db_password' => '', - 'db_connection_options' => array() - ), $options); - - $this->table = $options['db_table']; - $this->idCol = $options['db_id_col']; - $this->dataCol = $options['db_data_col']; - $this->lifetimeCol = $options['db_lifetime_col']; - $this->timeCol = $options['db_time_col']; - $this->username = $options['db_username']; - $this->password = $options['db_password']; - $this->connectionOptions = $options['db_connection_options']; + $this->table = isset($options['db_table']) ? $options['db_table'] : $this->table; + $this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol; + $this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol; + $this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol; + $this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol; + $this->username = isset($options['db_username']) ? $options['db_username'] : $this->username; + $this->password = isset($options['db_password']) ? $options['db_password'] : $this->password; + $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions; + $this->lockMode = isset($options['lock_mode']) ? $options['lock_mode'] : $this->lockMode; } /** * Creates the table to store sessions which can be called once for setup. * - * Session ID is saved in a VARCHAR(128) column because that is enough even for - * a 512 bit configured session.hash_function like Whirlpool. Session data is + * Session ID is saved in a column of maximum length 128 because that is enough even + * for a 512 bit configured session.hash_function like Whirlpool. Session data is * saved in a BLOB. One could also use a shorter inlined varbinary column * if one was sure the data fits into it. * @@ -180,7 +213,12 @@ public function createTable() switch ($this->driver) { case 'mysql': - $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) ENGINE = InnoDB"; + // We use varbinary for the ID column because it prevents unwanted conversions: + // - character set conversions between server and client + // - trailing space removal + // - case-insensitivity + // - language processing like é == e + $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB"; break; case 'sqlite': $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; @@ -195,7 +233,7 @@ public function createTable() $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; break; default: - throw new \DomainException(sprintf('"%s" does not currently support PDO driver "%s".', __CLASS__, $this->driver)); + throw new \DomainException(sprintf('Creating the session table is currently not implemented for PDO driver "%s".', $this->driver)); } try { @@ -207,6 +245,18 @@ public function createTable() } } + /** + * Returns true when the current session exists but expired according to session.gc_maxlifetime. + * + * Can be used to distinguish between a new session and one that expired due to inactivity. + * + * @return bool Whether current session expired + */ + public function isSessionExpired() + { + return $this->sessionExpired; + } + /** * {@inheritdoc} */ @@ -224,25 +274,8 @@ public function open($savePath, $sessionName) */ public function read($sessionId) { - $this->beginTransaction(); - try { - $this->lockSession($sessionId); - - // We need to make sure we do not return session data that is already considered garbage according - // to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes. - - $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id AND $this->lifetimeCol + $this->timeCol >= :time"; - - $stmt = $this->pdo->prepare($sql); - $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $stmt->bindValue(':time', time(), \PDO::PARAM_INT); - $stmt->execute(); - - // We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed - $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM); - - return $sessionRows ? $sessionRows[0][0] : ''; + return $this->doRead($sessionId); } catch (\PDOException $e) { $this->rollback(); @@ -288,14 +321,10 @@ public function destroy($sessionId) */ public function write($sessionId, $data) { - // The session ID can be different from the one previously received in read() - // when the session ID changed due to session_regenerate_id(). So we have to - // do an insert or update even if we created a row in read() for locking. - // We use a single MERGE SQL query when supported by the database. - $maxlifetime = (int) ini_get('session.gc_maxlifetime'); try { + // We use a single MERGE SQL query when supported by the database. $mergeSql = $this->getMergeSql(); if (null !== $mergeSql) { @@ -319,12 +348,10 @@ public function write($sessionId, $data) $updateStmt->execute(); // When MERGE is not supported, like in Postgres, we have to use this approach that can result in - // duplicate key errors when the same session is written simultaneously. We can just catch such an - // error and re-execute the update. This is similar to a serializable transaction with retry logic - // on serialization failures but without the overhead and without possible false positives due to - // longer gap locking. - // Since we have a lock on the session, the above case should not happen. And if it's a regenerated - // session ID it should be unique anyway. + // duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior). + // We can just catch such an error and re-execute the update. This is similar to a serializable + // transaction with retry logic on serialization failures but without the overhead and without possible + // false positives due to longer gap locking. if (!$updateStmt->rowCount()) { try { $insertStmt = $this->pdo->prepare( @@ -360,6 +387,10 @@ public function close() { $this->commit(); + while ($unlockStmt = array_shift($this->unlockStatements)) { + $unlockStmt->execute(); + } + if ($this->gcCalled) { $this->gcCalled = false; @@ -372,7 +403,7 @@ public function close() } if (false !== $this->dsn) { - $this->pdo = null; + $this->pdo = null; // only close lazy-connection } return true; @@ -397,6 +428,10 @@ private function connect($dsn) * on the database immediately. Because of https://bugs.php.net/42766 we have to create * such a transaction manually which also means we cannot use PDO::commit or * PDO::rollback or PDO::inTransaction for SQLite. + * + * Also MySQLs default isolation, REPEATABLE READ, causes deadlock for different sessions + * due to http://www.mysqlperformanceblog.com/2013/12/12/one-more-innodb-gap-lock-to-avoid/ . + * So we change it to READ COMMITTED. */ private function beginTransaction() { @@ -404,6 +439,9 @@ private function beginTransaction() if ('sqlite' === $this->driver) { $this->pdo->exec('BEGIN IMMEDIATE TRANSACTION'); } else { + if ('mysql' === $this->driver) { + $this->pdo->exec('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); + } $this->pdo->beginTransaction(); } $this->inTransaction = true; @@ -439,8 +477,8 @@ private function rollback() { // We only need to rollback if we are in a transaction. Otherwise the resulting // error would hide the real problem why rollback was called. We might not be - // in a transaction when two callbacks (e.g. destroy and write) are invoked that - // both fail. + // in a transaction when not using the transactional locking behavior or when + // two callbacks (e.g. destroy and write) are invoked that both fail. if ($this->inTransaction) { if ('sqlite' === $this->driver) { $this->pdo->exec('ROLLBACK'); @@ -452,64 +490,166 @@ private function rollback() } /** - * Exclusively locks the row so other concurrent requests on the same session will block. + * Reads the session data in respect to the different locking strategies. * - * This prevents loss of data by keeping the data consistent between read() and write(). - * We do not use SELECT FOR UPDATE because it does not lock non-existent rows. And a following - * INSERT when not found can result in a deadlock for one connection. + * We need to make sure we do not return session data that is already considered garbage according + * to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes. * * @param string $sessionId Session ID * + * @return string The session data + */ + private function doRead($sessionId) + { + $this->sessionExpired = false; + + if (self::LOCK_ADVISORY === $this->lockMode) { + $this->unlockStatements[] = $this->doAdvisoryLock($sessionId); + } + + $selectSql = $this->getSelectSql(); + $selectStmt = $this->pdo->prepare($selectSql); + $selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $selectStmt->execute(); + + $sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM); + + if ($sessionRows) { + if ($sessionRows[0][1] + $sessionRows[0][2] < time()) { + $this->sessionExpired = true; + + return ''; + } + + return $sessionRows[0][0]; + } + + if (self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) { + // Exlusive-reading of non-existent rows does not block, so we need to do an insert to block + // until other connections to the session are committed. + try { + $insertStmt = $this->pdo->prepare( + "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)" + ); + $insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $insertStmt->bindValue(':data', '', \PDO::PARAM_LOB); + $insertStmt->bindValue(':lifetime', 0, \PDO::PARAM_INT); + $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT); + $insertStmt->execute(); + } catch (\PDOException $e) { + // Catch duplicate key error because other connection created the session already. + // It would only not be the case when the other connection destroyed the session. + if (0 === strpos($e->getCode(), '23')) { + // Retrieve finished session data written by concurrent connection. SELECT + // FOR UPDATE is necessary to avoid deadlock of connection that starts reading + // before we write (transform intention to real lock). + $selectStmt->execute(); + $sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM); + + return $sessionRows ? $sessionRows[0][0] : ''; + } + + throw $e; + } + } + + return ''; + } + + /** + * Executes an application-level lock on the database. + * + * @param string $sessionId Session ID + * + * @return \PDOStatement The statement that needs to be executed later to release the lock + * * @throws \DomainException When an unsupported PDO driver is used + * + * @todo implement missing advisory locks + * - for oci using DBMS_LOCK.REQUEST + * - for sqlsrv using sp_getapplock with LockOwner = Session */ - private function lockSession($sessionId) + private function doAdvisoryLock($sessionId) { switch ($this->driver) { case 'mysql': - // will also lock the row when actually nothing got updated (id = id) - $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". - "ON DUPLICATE KEY UPDATE $this->idCol = $this->idCol"; - break; - case 'oci': - // DUAL is Oracle specific dummy table - $sql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ". - "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". - "WHEN MATCHED THEN UPDATE SET $this->idCol = $this->idCol"; - break; - // todo: implement locking for SQL Server < 2008 - case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='): - // MS SQL Server requires MERGE be terminated by semicolon - $sql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ". - "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". - "WHEN MATCHED THEN UPDATE SET $this->idCol = $this->idCol;"; - break; - case 'pgsql': - // obtain an exclusive transaction level advisory lock - $sql = 'SELECT pg_advisory_xact_lock(:lock_id)'; - $stmt = $this->pdo->prepare($sql); - $stmt->bindValue(':lock_id', hexdec(substr($sessionId, 0, 15)), \PDO::PARAM_INT); + // should we handle the return value? 0 on timeout, null on error + // we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout + $stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)'); + $stmt->bindValue(':key', $sessionId, \PDO::PARAM_STR); $stmt->execute(); - return; + $releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)'); + $releaseStmt->bindValue(':key', $sessionId, \PDO::PARAM_STR); + + return $releaseStmt; + case 'pgsql': + // Obtaining an exclusive session level advisory lock requires an integer key. + // So we convert the HEX representation of the session id to an integer. + // Since integers are signed, we have to skip one hex char to fit in the range. + if (4 === PHP_INT_SIZE) { + $sessionInt1 = hexdec(substr($sessionId, 0, 7)); + $sessionInt2 = hexdec(substr($sessionId, 7, 7)); + + $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key1, :key2)'); + $stmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT); + $stmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT); + $stmt->execute(); + + $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key1, :key2)'); + $releaseStmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT); + $releaseStmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT); + } else { + $sessionBigInt = hexdec(substr($sessionId, 0, 15)); + + $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key)'); + $stmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT); + $stmt->execute(); + + $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key)'); + $releaseStmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT); + } + + return $releaseStmt; case 'sqlite': - return; // we already locked when starting transaction + throw new \DomainException('SQLite does not support advisory locks.'); default: - throw new \DomainException(sprintf('"%s" does not currently support PDO driver "%s".', __CLASS__, $this->driver)); + throw new \DomainException(sprintf('Advisory locks are currently not implemented for PDO driver "%s".', $this->driver)); + } + } + + /** + * Return a locking or nonlocking SQL query to read session information. + * + * @return string The SQL string + * + * @throws \DomainException When an unsupported PDO driver is used + */ + private function getSelectSql() + { + if (self::LOCK_TRANSACTIONAL === $this->lockMode) { + $this->beginTransaction(); + + switch ($this->driver) { + case 'mysql': + case 'oci': + case 'pgsql': + return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id FOR UPDATE"; + case 'sqlsrv': + return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WITH (UPDLOCK, ROWLOCK) WHERE $this->idCol = :id"; + case 'sqlite': + // we already locked when starting transaction + break; + default: + throw new \DomainException(sprintf('Transactional locks are currently not implemented for PDO driver "%s".', $this->driver)); + } } - // We create a DML lock for the session by inserting empty data or updating the row. - // This is safer than an application level advisory lock because it also prevents concurrent modification - // of the session from other parts of the application. - $stmt = $this->pdo->prepare($sql); - $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); - $stmt->bindValue(':data', '', \PDO::PARAM_STR); - $stmt->bindValue(':lifetime', 0, \PDO::PARAM_INT); - $stmt->bindValue(':time', time(), \PDO::PARAM_INT); - $stmt->execute(); + return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id"; } /** - * Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database. + * Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database for writing session data. * * @return string|null The SQL string or null when not supported */ diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php index 418444766e8c6..e4b8d76778d1c 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -36,7 +36,7 @@ protected function getPersistentSqliteDsn() { $this->dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions'); - return 'sqlite:' . $this->dbFile; + return 'sqlite:'.$this->dbFile; } protected function getMemorySqlitePdo() @@ -120,7 +120,7 @@ public function testWithLazySavePathConnection() public function testReadWriteReadWithNullByte() { - $sessionData = 'da' . "\0" . 'ta'; + $sessionData = 'da'."\0".'ta'; $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); $storage->open('', 'sid'); From 5be84c4bf05c84bbe6d5091d11fa5d799cd64bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 2 Oct 2014 19:46:25 +0200 Subject: [PATCH 254/373] [Serializer] Fix CS. Uniformize PHPDoc. --- .../Serializer/Encoder/ChainDecoder.php | 1 + .../Serializer/Encoder/ChainEncoder.php | 1 + .../Serializer/Encoder/DecoderInterface.php | 8 ++-- .../Serializer/Encoder/EncoderInterface.php | 10 ++--- .../Serializer/Encoder/JsonDecode.php | 4 +- .../Serializer/Encoder/JsonEncode.php | 7 ++-- .../Serializer/Encoder/JsonEncoder.php | 4 +- .../Encoder/NormalizationAwareInterface.php | 2 +- .../Encoder/SerializerAwareEncoder.php | 2 +- .../Serializer/Encoder/XmlEncoder.php | 21 ++++++---- .../Normalizer/DenormalizableInterface.php | 12 +++--- .../Normalizer/DenormalizerInterface.php | 10 ++--- .../Normalizer/GetSetMethodNormalizer.php | 15 +++---- .../Normalizer/NormalizableInterface.php | 12 +++--- .../Normalizer/NormalizerInterface.php | 10 ++--- .../Normalizer/PropertyNormalizer.php | 4 +- .../Normalizer/SerializerAwareNormalizer.php | 2 +- .../Component/Serializer/Serializer.php | 41 +++++++++++++------ .../Serializer/SerializerAwareInterface.php | 4 +- .../Serializer/SerializerInterface.php | 4 +- 20 files changed, 99 insertions(+), 75 deletions(-) diff --git a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php index 5321a234057ec..f8f17b4f723e6 100644 --- a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php +++ b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php @@ -58,6 +58,7 @@ public function supportsDecoding($format) * @param string $format * * @return DecoderInterface + * * @throws RuntimeException if no decoder is found */ private function getDecoder($format) diff --git a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php index 43dfa00118914..5ee352ab27932 100644 --- a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php @@ -80,6 +80,7 @@ public function needsNormalization($format) * @param string $format * * @return EncoderInterface + * * @throws RuntimeException if no encoder is found */ private function getEncoder($format) diff --git a/src/Symfony/Component/Serializer/Encoder/DecoderInterface.php b/src/Symfony/Component/Serializer/Encoder/DecoderInterface.php index 34e21bc0e3bee..683da5bec92a7 100644 --- a/src/Symfony/Component/Serializer/Encoder/DecoderInterface.php +++ b/src/Symfony/Component/Serializer/Encoder/DecoderInterface.php @@ -14,7 +14,7 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException; /** - * Defines the interface of decoders + * Defines the interface of decoders. * * @author Jordi Boggiano */ @@ -23,9 +23,9 @@ interface DecoderInterface /** * Decodes a string into PHP data. * - * @param scalar $data Data to decode - * @param string $format Format name - * @param array $context options that decoders have access to. + * @param scalar $data Data to decode + * @param string $format Format name + * @param array $context options that decoders have access to. * * The format parameter specifies which format the data is in; valid values * depend on the specific implementation. Authors implementing this interface diff --git a/src/Symfony/Component/Serializer/Encoder/EncoderInterface.php b/src/Symfony/Component/Serializer/Encoder/EncoderInterface.php index 394df9999fb2d..fe7e777daad32 100644 --- a/src/Symfony/Component/Serializer/Encoder/EncoderInterface.php +++ b/src/Symfony/Component/Serializer/Encoder/EncoderInterface.php @@ -14,17 +14,17 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException; /** - * Defines the interface of encoders + * Defines the interface of encoders. * * @author Jordi Boggiano */ interface EncoderInterface { /** - * Encodes data into the given format + * Encodes data into the given format. * - * @param mixed $data Data to encode - * @param string $format Format name + * @param mixed $data Data to encode + * @param string $format Format name * @param array $context options that normalizers/encoders have access to. * * @return scalar @@ -34,7 +34,7 @@ interface EncoderInterface public function encode($data, $format, array $context = array()); /** - * Checks whether the serializer can encode to given format + * Checks whether the serializer can encode to given format. * * @param string $format format name * diff --git a/src/Symfony/Component/Serializer/Encoder/JsonDecode.php b/src/Symfony/Component/Serializer/Encoder/JsonDecode.php index afb4e3ec84332..a1a8e7cab2ffa 100644 --- a/src/Symfony/Component/Serializer/Encoder/JsonDecode.php +++ b/src/Symfony/Component/Serializer/Encoder/JsonDecode.php @@ -41,8 +41,8 @@ class JsonDecode implements DecoderInterface /** * Constructs a new JsonDecode instance. * - * @param bool $associative True to return the result associative array, false for a nested stdClass hierarchy - * @param int $depth Specifies the recursion depth + * @param bool $associative True to return the result associative array, false for a nested stdClass hierarchy + * @param int $depth Specifies the recursion depth */ public function __construct($associative = false, $depth = 512) { diff --git a/src/Symfony/Component/Serializer/Encoder/JsonEncode.php b/src/Symfony/Component/Serializer/Encoder/JsonEncode.php index 8b468bd14a874..931a0556c6ccf 100644 --- a/src/Symfony/Component/Serializer/Encoder/JsonEncode.php +++ b/src/Symfony/Component/Serializer/Encoder/JsonEncode.php @@ -14,7 +14,7 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException; /** - * Encodes JSON data + * Encodes JSON data. * * @author Sander Coolen */ @@ -43,7 +43,7 @@ public function getLastError() } /** - * Encodes PHP data to a JSON string + * Encodes PHP data to a JSON string. * * {@inheritdoc} */ @@ -71,7 +71,8 @@ public function supportsEncoding($format) /** * Merge default json encode options with context. * - * @param array $context + * @param array $context + * * @return array */ private function resolveContext(array $context = array()) diff --git a/src/Symfony/Component/Serializer/Encoder/JsonEncoder.php b/src/Symfony/Component/Serializer/Encoder/JsonEncoder.php index 9137c926b30d9..df22316ff703d 100644 --- a/src/Symfony/Component/Serializer/Encoder/JsonEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/JsonEncoder.php @@ -37,7 +37,7 @@ public function __construct(JsonEncode $encodingImpl = null, JsonDecode $decodin } /** - * Returns the last encoding error (if any) + * Returns the last encoding error (if any). * * @return int * @@ -49,7 +49,7 @@ public function getLastEncodingError() } /** - * Returns the last decoding error (if any) + * Returns the last decoding error (if any). * * @return int * diff --git a/src/Symfony/Component/Serializer/Encoder/NormalizationAwareInterface.php b/src/Symfony/Component/Serializer/Encoder/NormalizationAwareInterface.php index 8101332e5ba15..c066bd7cdd1bb 100644 --- a/src/Symfony/Component/Serializer/Encoder/NormalizationAwareInterface.php +++ b/src/Symfony/Component/Serializer/Encoder/NormalizationAwareInterface.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Serializer\Encoder; /** - * Defines the interface of encoders that will normalize data themselves + * Defines the interface of encoders that will normalize data themselves. * * Implementing this interface essentially just tells the Serializer that the * data should not be pre-normalized before being passed to this Encoder. diff --git a/src/Symfony/Component/Serializer/Encoder/SerializerAwareEncoder.php b/src/Symfony/Component/Serializer/Encoder/SerializerAwareEncoder.php index 80bbb23d75ec1..a3d8ff38c347d 100644 --- a/src/Symfony/Component/Serializer/Encoder/SerializerAwareEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/SerializerAwareEncoder.php @@ -15,7 +15,7 @@ use Symfony\Component\Serializer\SerializerAwareInterface; /** - * SerializerAware Encoder implementation + * SerializerAware Encoder implementation. * * @author Jordi Boggiano */ diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php index a0222522041a7..709b8b65c3ad9 100644 --- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php @@ -14,7 +14,7 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException; /** - * Encodes XML data + * Encodes XML data. * * @author Jordi Boggiano * @author John Wards @@ -146,7 +146,7 @@ public function supportsDecoding($format) } /** - * Sets the root node name + * Sets the root node name. * * @param string $name root node name */ @@ -156,7 +156,8 @@ public function setRootNodeName($name) } /** - * Returns the root node name + * Returns the root node name. + * * @return string */ public function getRootNodeName() @@ -229,7 +230,7 @@ final protected function appendDocumentFragment(\DOMNode $node, $fragment) } /** - * Checks the name is a valid xml element name + * Checks the name is a valid xml element name. * * @param string $name * @@ -279,7 +280,7 @@ private function parseXml(\DOMNode $node) } /** - * Parse the input DOMNode attributes into an array + * Parse the input DOMNode attributes into an array. * * @param \DOMNode $node xml to parse * @@ -305,7 +306,7 @@ private function parseXmlAttributes(\DOMNode $node) } /** - * Parse the input DOMNode value (content and children) into an array or a string + * Parse the input DOMNode value (content and children) into an array or a string. * * @param \DOMNode $node xml to parse * @@ -347,7 +348,7 @@ private function parseXmlValue(\DOMNode $node) } /** - * Parse the data and convert it to DOMElements + * Parse the data and convert it to DOMElements. * * @param \DOMNode $parentNode * @param array|object $data @@ -452,7 +453,7 @@ private function needsCdataWrapping($val) } /** - * Tests the value being passed and decide what sort of element to create + * Tests the value being passed and decide what sort of element to create. * * @param \DOMNode $node * @param mixed $val @@ -488,6 +489,10 @@ private function selectNodeType(\DOMNode $node, $val) /** * Get real XML root node name, taking serializer options into account. + * + * @param array $context + * + * @return string */ private function resolveXmlRootName(array $context = array()) { diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php index 5a1b4c196b594..419d793f50950 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Serializer\Normalizer; /** - * Defines the most basic interface a class must implement to be denormalizable + * Defines the most basic interface a class must implement to be denormalizable. * * If a denormalizer is registered for the class and it doesn't implement * the Denormalizable interfaces, the normalizer will be used instead @@ -28,11 +28,11 @@ interface DenormalizableInterface * recursively all child objects of the implementor. * * @param DenormalizerInterface $denormalizer The denormalizer is given so that you - * can use it to denormalize objects contained within this object. - * @param array|scalar $data The data from which to re-create the object. - * @param string|null $format The format is optionally given to be able to denormalize differently - * based on different input formats. - * @param array $context options for denormalizing + * can use it to denormalize objects contained within this object. + * @param array|scalar $data The data from which to re-create the object. + * @param string|null $format The format is optionally given to be able to denormalize differently + * based on different input formats. + * @param array $context options for denormalizing. */ public function denormalize(DenormalizerInterface $denormalizer, $data, $format = null, array $context = array()); } diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php index 001dd6863dcf6..8b6c2333958fe 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php @@ -19,11 +19,11 @@ interface DenormalizerInterface { /** - * Denormalizes data back into an object of the given class + * Denormalizes data back into an object of the given class. * - * @param mixed $data data to restore - * @param string $class the expected class to instantiate - * @param string $format format the given data was extracted from + * @param mixed $data data to restore + * @param string $class the expected class to instantiate + * @param string $format format the given data was extracted from * @param array $context options available to the denormalizer * * @return object @@ -31,7 +31,7 @@ interface DenormalizerInterface public function denormalize($data, $class, $format = null, array $context = array()); /** - * Checks whether the given class is supported for denormalization by this normalizer + * Checks whether the given class is supported for denormalization by this normalizer. * * @param mixed $data Data to denormalize from. * @param string $type The class to which the data should be denormalized. diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index f4a1b9094bbdd..aab7db12e2636 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -47,7 +47,7 @@ class GetSetMethodNormalizer extends SerializerAwareNormalizer implements Normal * * @throws InvalidArgumentException if a non-callable callback is set * - * @return GetSetMethodNormalizer + * @return self */ public function setCallbacks(array $callbacks) { @@ -62,11 +62,11 @@ public function setCallbacks(array $callbacks) } /** - * Set ignored attributes for normalization + * Set ignored attributes for normalization. * * @param array $ignoredAttributes * - * @return GetSetMethodNormalizer + * @return self */ public function setIgnoredAttributes(array $ignoredAttributes) { @@ -76,11 +76,11 @@ public function setIgnoredAttributes(array $ignoredAttributes) } /** - * Set attributes to be camelized on denormalize + * Set attributes to be camelized on denormalize. * * @param array $camelizedAttributes * - * @return GetSetMethodNormalizer + * @return self */ public function setCamelizedAttributes(array $camelizedAttributes) { @@ -185,9 +185,10 @@ public function denormalize($data, $class, $format = null, array $context = arra /** * Format attribute name to access parameters or methods * As option, if attribute name is found on camelizedAttributes array - * returns attribute name in camelcase format + * returns attribute name in camelcase format. + * + * @param string $attributeName * - * @param string $attributeName * @return string */ protected function formatAttribute($attributeName) diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php index 07c3a4a8dd8e8..e19fe5ce58576 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php @@ -12,10 +12,10 @@ namespace Symfony\Component\Serializer\Normalizer; /** - * Defines the most basic interface a class must implement to be normalizable + * Defines the most basic interface a class must implement to be normalizable. * * If a normalizer is registered for the class and it doesn't implement - * the Normalizable interfaces, the normalizer will be used instead + * the Normalizable interfaces, the normalizer will be used instead. * * @author Jordi Boggiano */ @@ -28,10 +28,10 @@ interface NormalizableInterface * recursively all child objects of the implementor. * * @param NormalizerInterface $normalizer The normalizer is given so that you - * can use it to normalize objects contained within this object. - * @param string|null $format The format is optionally given to be able to normalize differently - * based on different output formats. - * @param array $context Options for normalizing this object + * can use it to normalize objects contained within this object. + * @param string|null $format The format is optionally given to be able to normalize differently + * based on different output formats. + * @param array $context Options for normalizing this object * * @return array|scalar */ diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php index 22a95438a098e..f4bd355232b0b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php @@ -19,18 +19,18 @@ interface NormalizerInterface { /** - * Normalizes an object into a set of arrays/scalars + * Normalizes an object into a set of arrays/scalars. * - * @param object $object object to normalize - * @param string $format format the normalization result will be encoded as - * @param array $context Context options for the normalizer + * @param object $object object to normalize + * @param string $format format the normalization result will be encoded as + * @param array $context Context options for the normalizer * * @return array|scalar */ public function normalize($object, $format = null, array $context = array()); /** - * Checks whether the given class is supported for normalization by this normalizer + * Checks whether the given class is supported for normalization by this normalizer. * * @param mixed $data Data to normalize. * @param string $format The format being (de-)serialized from or into. diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index 9eefff3ca81b5..a6bc44384093b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -57,7 +57,7 @@ public function setCallbacks(array $callbacks) } /** - * Set ignored attributes for normalization + * Set ignored attributes for normalization. * * @param array $ignoredAttributes */ @@ -180,7 +180,7 @@ public function supportsDenormalization($data, $type, $format = null) /** * Format an attribute name, for example to convert a snake_case name to camelCase. * - * @param string $attributeName + * @param string $attributeName * @return string */ protected function formatAttribute($attributeName) diff --git a/src/Symfony/Component/Serializer/Normalizer/SerializerAwareNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/SerializerAwareNormalizer.php index 49916bef4c657..395685707405c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/SerializerAwareNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/SerializerAwareNormalizer.php @@ -15,7 +15,7 @@ use Symfony\Component\Serializer\SerializerAwareInterface; /** - * SerializerAware Normalizer implementation + * SerializerAware Normalizer implementation. * * @author Jordi Boggiano */ diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index c4052bcbcc5ee..358a3f8531f2f 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -21,10 +21,10 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException; /** - * Serializer serializes and deserializes data + * Serializer serializes and deserializes data. * - * objects are turned into arrays by normalizers - * arrays are turned into various output formats by encoders + * objects are turned into arrays by normalizers. + * arrays are turned into various output formats by encoders. * * $serializer->serialize($obj, 'xml') * $serializer->decode($data, 'xml') @@ -36,10 +36,25 @@ */ class Serializer implements SerializerInterface, NormalizerInterface, DenormalizerInterface, EncoderInterface, DecoderInterface { + /** + * @var Encoder\ChainEncoder + */ protected $encoder; + /** + * @var Encoder\ChainDecoder + */ protected $decoder; + /** + * @var array + */ protected $normalizers = array(); + /** + * @var array + */ protected $normalizerCache = array(); + /** + * @var array + */ protected $denormalizerCache = array(); public function __construct(array $normalizers = array(), array $encoders = array()) @@ -157,7 +172,7 @@ public function supportsDenormalization($data, $type, $format = null) /** * Returns a matching normalizer. * - * @param object $data The object to get the serializer for + * @param object $data The object to get the serializer for * @param string $format format name, present to give the option to normalizers to act differently based on formats * * @return NormalizerInterface|null @@ -219,11 +234,11 @@ final public function decode($data, $format, array $context = array()) } /** - * Normalizes an object into a set of arrays/scalars + * Normalizes an object into a set of arrays/scalars. * - * @param object $object object to normalize - * @param string $format format name, present to give the option to normalizers to act differently based on formats - * @param array $context The context data for this particular normalization + * @param object $object object to normalize + * @param string $format format name, present to give the option to normalizers to act differently based on formats + * @param array $context The context data for this particular normalization * * @return array|scalar * @@ -243,12 +258,12 @@ private function normalizeObject($object, $format, array $context = array()) } /** - * Denormalizes data back into an object of the given class + * Denormalizes data back into an object of the given class. * - * @param mixed $data data to restore - * @param string $class the expected class to instantiate - * @param string $format format name, present to give the option to normalizers to act differently based on formats - * @param array $context The context data for this particular denormalization + * @param mixed $data data to restore + * @param string $class the expected class to instantiate + * @param string $format format name, present to give the option to normalizers to act differently based on formats + * @param array $context The context data for this particular denormalization * * @return object * diff --git a/src/Symfony/Component/Serializer/SerializerAwareInterface.php b/src/Symfony/Component/Serializer/SerializerAwareInterface.php index ad0b768ccc8a2..dd0a62e7fa887 100644 --- a/src/Symfony/Component/Serializer/SerializerAwareInterface.php +++ b/src/Symfony/Component/Serializer/SerializerAwareInterface.php @@ -12,14 +12,14 @@ namespace Symfony\Component\Serializer; /** - * Defines the interface of encoders + * Defines the interface of encoders. * * @author Jordi Boggiano */ interface SerializerAwareInterface { /** - * Sets the owning Serializer object + * Sets the owning Serializer object. * * @param SerializerInterface $serializer */ diff --git a/src/Symfony/Component/Serializer/SerializerInterface.php b/src/Symfony/Component/Serializer/SerializerInterface.php index d196d780a95be..c79db91892075 100644 --- a/src/Symfony/Component/Serializer/SerializerInterface.php +++ b/src/Symfony/Component/Serializer/SerializerInterface.php @@ -12,14 +12,14 @@ namespace Symfony\Component\Serializer; /** - * Defines the interface of the Serializer + * Defines the interface of the Serializer. * * @author Jordi Boggiano */ interface SerializerInterface { /** - * Serializes data in the appropriate format + * Serializes data in the appropriate format. * * @param mixed $data any data * @param string $format format name From b6e29e9f343551cf5ad281f5061b3be1d14f48db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Thu, 2 Oct 2014 22:15:16 +0200 Subject: [PATCH 255/373] Display a nice upload limit message --- .../Validator/Constraints/FileValidator.php | 75 +++++++++++-------- .../Tests/Constraints/FileValidatorTest.php | 8 +- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/FileValidator.php b/src/Symfony/Component/Validator/Constraints/FileValidator.php index 808a9885d010e..dfc5ed9a966aa 100644 --- a/src/Symfony/Component/Validator/Constraints/FileValidator.php +++ b/src/Symfony/Component/Validator/Constraints/FileValidator.php @@ -59,9 +59,10 @@ public function validate($value, Constraint $constraint) $limitInBytes = UploadedFile::getMaxFilesize(); } + list($sizeAsString, $limitAsString, $suffix) = $this->factorizeSizes(0, $limitInBytes, true); $this->buildViolation($constraint->uploadIniSizeErrorMessage) - ->setParameter('{{ limit }}', $limitInBytes) - ->setParameter('{{ suffix }}', 'bytes') + ->setParameter('{{ limit }}', $limitAsString) + ->setParameter('{{ suffix }}', $suffix) ->setCode(UPLOAD_ERR_INI_SIZE) ->addViolation(); @@ -150,41 +151,12 @@ public function validate($value, Constraint $constraint) $limitInBytes = $constraint->maxSize; if ($sizeInBytes > $limitInBytes) { - // Convert the limit to the smallest possible number - // (i.e. try "MB", then "kB", then "bytes") - if ($constraint->binaryFormat) { - $coef = self::MIB_BYTES; - $coefFactor = self::KIB_BYTES; - } else { - $coef = self::MB_BYTES; - $coefFactor = self::KB_BYTES; - } - - $limitAsString = (string) ($limitInBytes / $coef); - - // Restrict the limit to 2 decimals (without rounding! we - // need the precise value) - while (self::moreDecimalsThan($limitAsString, 2)) { - $coef /= $coefFactor; - $limitAsString = (string) ($limitInBytes / $coef); - } - - // Convert size to the same measure, but round to 2 decimals - $sizeAsString = (string) round($sizeInBytes / $coef, 2); - - // If the size and limit produce the same string output - // (due to rounding), reduce the coefficient - while ($sizeAsString === $limitAsString) { - $coef /= $coefFactor; - $limitAsString = (string) ($limitInBytes / $coef); - $sizeAsString = (string) round($sizeInBytes / $coef, 2); - } - + list($sizeAsString, $limitAsString, $suffix) = $this->factorizeSizes($sizeInBytes, $limitInBytes, $constraint->binaryFormat); $this->buildViolation($constraint->maxSizeMessage) ->setParameter('{{ file }}', $this->formatValue($path)) ->setParameter('{{ size }}', $sizeAsString) ->setParameter('{{ limit }}', $limitAsString) - ->setParameter('{{ suffix }}', self::$suffices[$coef]) + ->setParameter('{{ suffix }}', $suffix) ->setCode(File::TOO_LARGE_ERROR) ->addViolation(); @@ -225,4 +197,41 @@ private static function moreDecimalsThan($double, $numberOfDecimals) { return strlen((string) $double) > strlen(round($double, $numberOfDecimals)); } + + /** + * Convert the limit to the smallest possible number + * (i.e. try "MB", then "kB", then "bytes") + */ + private function factorizeSizes($size, $limit, $binaryFormat) + { + if ($binaryFormat) { + $coef = self::MIB_BYTES; + $coefFactor = self::KIB_BYTES; + } else { + $coef = self::MB_BYTES; + $coefFactor = self::KB_BYTES; + } + + $limitAsString = (string) ($limit / $coef); + + // Restrict the limit to 2 decimals (without rounding! we + // need the precise value) + while (self::moreDecimalsThan($limitAsString, 2)) { + $coef /= $coefFactor; + $limitAsString = (string) ($limit / $coef); + } + + // Convert size to the same measure, but round to 2 decimals + $sizeAsString = (string) round($size / $coef, 2); + + // If the size and limit produce the same string output + // (due to rounding), reduce the coefficient + while ($sizeAsString === $limitAsString) { + $coef /= $coefFactor; + $limitAsString = (string) ($limit / $coef); + $sizeAsString = (string) round($size / $coef, 2); + } + + return array($sizeAsString, $limitAsString, self::$suffices[$coef]); + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php index c66b6ca5a36b6..7d8b15c23f2ac 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php @@ -445,8 +445,8 @@ public function uploadedFileErrorProvider() if (class_exists('Symfony\Component\HttpFoundation\File\UploadedFile')) { // when no maxSize is specified on constraint, it should use the ini value $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array( - '{{ limit }}' => UploadedFile::getMaxFilesize(), - '{{ suffix }}' => 'bytes', + '{{ limit }}' => UploadedFile::getMaxFilesize() / 1048576, + '{{ suffix }}' => 'MiB', )); // it should use the smaller limitation (maxSize option in this case) @@ -458,8 +458,8 @@ public function uploadedFileErrorProvider() // it correctly parses the maxSize option and not only uses simple string comparison // 1000M should be bigger than the ini value $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array( - '{{ limit }}' => UploadedFile::getMaxFilesize(), - '{{ suffix }}' => 'bytes', + '{{ limit }}' => UploadedFile::getMaxFilesize() / 1048576, + '{{ suffix }}' => 'MiB', ), '1000M'); } From 513d2d3d6e0d45ae5c6d06810f08e0c0f0461bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Haso=C5=88?= Date: Fri, 3 Oct 2014 13:43:33 +0200 Subject: [PATCH 256/373] [WebProfilerBundle] Showed all ajax calls in toolbar --- .../Resources/views/Profiler/base_js.html.twig | 5 ++--- .../Resources/views/Profiler/toolbar.css.twig | 6 ++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig index 5fdf6f41b7b37..2d713d6abf5ae 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig @@ -83,8 +83,7 @@ var rows = document.createDocumentFragment(); if (requestStack.length) { - var firstItem = requestStack.length > 20 ? requestStack.length - 20 : 0; - for (var i = firstItem; i < requestStack.length; i++) { + for (var i = 0; i < requestStack.length; i++) { var request = requestStack[i]; var row = document.createElement('tr'); @@ -145,7 +144,7 @@ tbody.appendChild(rows); if (infoSpan) { - var text = firstItem == 0 ? requestStack.length + ' calls' : 'Last 20 calls out of ' + requestStack.length; + var text = requestStack.length + ' calls'; infoSpan.textContent = text; } } else { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index 4a5b6403599e8..7290750064e20 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -281,6 +281,12 @@ box-sizing: border-box; padding: 9px; line-height: 19px; + + max-width: 480px; + max-height: 480px; + word-wrap: break-word; + overflow: hidden; + overflow-y: scroll; } table.sf-toolbar-ajax-requests { From febe8367e0ac993490ef939ff81b99ece8a5c797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Haso=C5=88?= Date: Fri, 3 Oct 2014 13:45:14 +0200 Subject: [PATCH 257/373] [DebugBundle] Removed unnecessary div in toolbar --- .../Resources/views/Profiler/dump.html.twig | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig index 51796195b7620..b453928c04210 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig +++ b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig @@ -10,29 +10,27 @@ {% endset %} {% set text %} -
    +
    + dump() +
    + {% for dump in collector.getDumps('html') %}
    - dump() -
    - {% for dump in collector.getDumps('html') %} -
    - in - {% if dump.file %} - {% set link = dump.file|file_link(dump.line) %} - {% if link %} - {{ dump.name }} - {% else %} - {{ dump.name }} - {% endif %} + in + {% if dump.file %} + {% set link = dump.file|file_link(dump.line) %} + {% if link %} + {{ dump.name }} {% else %} - {{ dump.name }} + {{ dump.name }} {% endif %} - line {{ dump.line }}: - {{ dump.data|raw }} -
    - {% endfor %} - -
    + {% else %} + {{ dump.name }} + {% endif %} + line {{ dump.line }}: + {{ dump.data|raw }} +
  • + {% endfor %} + {% endset %} {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': true } %} From f214edac7649bba5ca5f4e3c9146151b1b032b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Haso=C5=88?= Date: Fri, 3 Oct 2014 10:06:07 +0200 Subject: [PATCH 258/373] [VarDumper] Added unique id for every single dump in html --- .../Tests/Extension/DumpExtensionTest.php | 1 + .../DataCollector/DumpDataCollectorTest.php | 19 +++++---- .../Component/VarDumper/Dumper/HtmlDumper.php | 41 ++++++++++++------- .../VarDumper/Tests/HtmlDumperTest.php | 22 +++++----- 4 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php index 3de9a0e91eee7..35d51d3a4cd39 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php @@ -77,6 +77,7 @@ public function testDump($context, $args, $expectedOutput, $debug = true) if ($debug) { $this->assertStringStartsWith('\n", - 'name' => 'DumpDataCollectorTest.php', - 'file' => __FILE__, - 'line' => $line, - 'fileExcerpt' => false, + 'data' => "
    123\n
    \n", + 'name' => 'DumpDataCollectorTest.php', + 'file' => __FILE__, + 'line' => $line, + 'fileExcerpt' => false, ), ); - $dump = $collector->getDumps('html'); - $this->assertTrue(isset($dump[0]['data'])); - $dump[0]['data'] = preg_replace('/^.*?
    assertSame($xDump, $dump);
     
             $this->assertStringStartsWith(
    diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php
    index 6e740c9dcd15b..1f8d72892c397 100644
    --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php
    +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php
    @@ -12,6 +12,7 @@
     namespace Symfony\Component\VarDumper\Dumper;
     
     use Symfony\Component\VarDumper\Cloner\Cursor;
    +use Symfony\Component\VarDumper\Cloner\Data;
     
     /**
      * HtmlDumper dumps variables as HTML.
    @@ -23,8 +24,9 @@ class HtmlDumper extends CliDumper
         public static $defaultOutputStream = 'php://output';
     
         protected $dumpHeader;
    -    protected $dumpPrefix = '
    ';
    +    protected $dumpPrefix = '
    ';
         protected $dumpSuffix = '
    '; + protected $dumpId = 'sf-dump'; protected $colors = true; protected $headerIsDumped = false; protected $lastDepth = -1; @@ -82,6 +84,15 @@ public function setDumpBoundaries($prefix, $suffix) $this->dumpSuffix = $suffix; } + /** + * {@inheritdoc} + */ + public function dump(Data $data, $lineDumper = null) + { + $this->dumpId = 'sf-dump-'.mt_rand(); + parent::dump($data, $lineDumper); + } + /** * Dumps the HTML header. */ @@ -90,7 +101,7 @@ protected function getDumpHeader() $this->headerIsDumped = true; if (null !== $this->dumpHeader) { - return $this->dumpHeader; + return str_replace('%id%', $this->dumpId, $this->dumpHeader); } $line = <<<'EOHTML' @@ -129,7 +140,7 @@ protected function getDumpHeader() }; '.$this->dumpHeader; + $this->dumpHeader = preg_replace('/\s+/', ' ', $line).''.$this->dumpHeader; + + return str_replace('%id%', $this->dumpId, $this->dumpHeader); } /** @@ -201,9 +214,9 @@ protected function style($style, $val) if ('ref' === $style) { $ref = substr($val, 1); if ('#' === $val[0]) { - return "$val"; + return "dumpId}-ref$ref\">$val"; } else { - return "$val"; + return "dumpId}-ref$ref\">$val"; } } @@ -236,14 +249,14 @@ protected function dumpLine($depth) } if (-1 === $this->lastDepth) { - $this->line = $this->dumpPrefix.$this->line; + $this->line = str_replace('%id%', $this->dumpId, $this->dumpPrefix).$this->line; } if (!$this->headerIsDumped) { $this->line = $this->getDumpHeader().$this->line; } if (-1 === $depth) { - $this->line .= $this->dumpSuffix; + $this->line .= str_replace('%id%', $this->dumpId, $this->dumpSuffix); parent::dumpLine(0); } $this->lastDepth = $depth; diff --git a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php index e93ff236c238c..82c41b4666dd6 100644 --- a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php @@ -33,7 +33,7 @@ public function testGet() unset($a['uri']); return $a; - } + }, )); $data = $cloner->cloneVar($var); @@ -44,12 +44,14 @@ public function testGet() $out = preg_replace('/[ \t]+$/m', '', $out); $var['file'] = htmlspecialchars($var['file'], ENT_QUOTES, 'UTF-8'); $intMax = PHP_INT_MAX; + preg_match('/sf-dump-(\\d{2,})/', $out, $matches); + $dumpId = $matches[1]; $this->assertSame( <<array:25 [ "number" => 1 - 0 => null #1 + 0 => null #1 "const" => 1.1 1 => true 2 => false @@ -72,7 +74,7 @@ public function testGet() options: [] } 8 => resource:Unknown {} - "obj" => DumbFoo { #2 + "obj" => DumbFoo { #2 foo: "foo" "bar": "bar" } @@ -90,15 +92,15 @@ public function testGet() } "line" => {$var['line']} "nobj" => array:1 [ - 0 => {} #3 + 0 => {} #3 ] - "recurs" => array:1 [ #4 - 0 => &4 array:1 [@4] + "recurs" => array:1 [ #4 + 0 => &4 array:1 [@4] ] - 9 => &1 null - "sobj" => DumbFoo {@2} - "snobj" => &3 {@3} - "snobj2" => {@3} + 9 => &1 null + "sobj" => DumbFoo {@2} + "snobj" => &3 {@3} + "snobj2" => {@3} "file" => "{$var['file']}" b"bin-key-é" => "" ] From 26b619b3371e2db5a795ab3f7694acadb94c7b91 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 3 Oct 2014 17:48:46 +0200 Subject: [PATCH 259/373] tests for the TranslatorListener --- .../EventListener/TranslatorListener.php | 3 +- .../EventListener/TranslatorListenerTest.php | 86 +++++++++++++++++++ .../Component/HttpKernel/composer.json | 1 + 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php diff --git a/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php b/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php index b682b3fc4927d..6f8e23eac9934 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php @@ -11,11 +11,12 @@ namespace Symfony\Component\HttpKernel\EventListener; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\Translator\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface; /** * Synchronizes the locale between the request and the translator. diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php new file mode 100644 index 0000000000000..12a004ef7f2cd --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\EventListener\TranslatorListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class TranslatorListenerTest extends \PHPUnit_Framework_TestCase +{ + private $listener; + private $translator; + private $requestStack; + + protected function setUp() + { + $this->translator = $this->getMock('Symfony\Component\Translation\TranslatorInterface'); + $this->requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack'); + $this->listener = new TranslatorListener($this->translator, $this->requestStack); + } + + public function testLocaleIsSetInOnKernelRequest() + { + $this->translator + ->expects($this->once()) + ->method('setLocale') + ->with($this->equalTo('fr')); + + $event = new GetResponseEvent($this->createHttpKernel(), $this->createRequest('fr'), HttpKernelInterface::MASTER_REQUEST); + $this->listener->onKernelRequest($event); + } + + public function testLocaleIsSetInOnKernelFinishRequestWhenParentRequestExists() + { + $this->translator + ->expects($this->once()) + ->method('setLocale') + ->with($this->equalTo('fr')); + + $this->setMasterRequest($this->createRequest('fr')); + $event = new FinishRequestEvent($this->createHttpKernel(), $this->createRequest('de'), HttpKernelInterface::SUB_REQUEST); + $this->listener->onKernelFinishRequest($event); + } + + public function testLocaleIsNotSetInOnKernelFinishRequestWhenParentRequestDoesNotExist() + { + $this->translator + ->expects($this->never()) + ->method('setLocale'); + + $event = new FinishRequestEvent($this->createHttpKernel(), $this->createRequest('de'), HttpKernelInterface::SUB_REQUEST); + $this->listener->onKernelFinishRequest($event); + } + + private function createHttpKernel() + { + return $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + } + + private function createRequest($locale) + { + $request = new Request(); + $request->setLocale($locale); + + return $request; + } + + private function setMasterRequest($request) + { + $this->requestStack + ->expects($this->any()) + ->method('getParentRequest') + ->will($this->returnValue($request)); + } +} diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 338602a2e4aa7..4dd2326923ae4 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -28,6 +28,7 @@ "symfony/config": "~2.0", "symfony/console": "~2.2", "symfony/dependency-injection": "~2.0", + "symfony/event-dispatcher": "~2.3", "symfony/expression-language": "~2.4", "symfony/finder": "~2.0", "symfony/process": "~2.0", From e383a387639651422f037d059fa7d6dedb89aae1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 3 Oct 2014 17:50:15 +0200 Subject: [PATCH 260/373] [VarDumper] encode non-ASCII UTF-8 chars as HTML numeric entities --- .../Component/VarDumper/Dumper/HtmlDumper.php | 35 ++++++++++++++++--- .../VarDumper/Tests/HtmlDumperTest.php | 14 ++++---- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index 1f8d72892c397..81b798d01dfef 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -108,9 +108,9 @@ protected function getDumpHeader() \n", + 'data' => "
    123\n
    \n", 'name' => 'DumpDataCollectorTest.php', 'file' => __FILE__, 'line' => $line, diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 87680de5984c2..483faad1b4ad4 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -224,7 +224,7 @@ public function leaveObject(Cursor $cursor, $class, $hasChild, $cut) */ public function enterResource(Cursor $cursor, $res, $hasChild) { - $this->enterHash($cursor, 'resource:'.$this->style('note', $res).' {', $hasChild); + $this->enterHash($cursor, $this->style('note', ':'.$res).' {', $hasChild); } /** diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index 81b798d01dfef..b8bb8192f9b17 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -24,7 +24,7 @@ class HtmlDumper extends CliDumper public static $defaultOutputStream = 'php://output'; protected $dumpHeader; - protected $dumpPrefix = '
    ';
    +    protected $dumpPrefix = '
    ';
         protected $dumpSuffix = '
    '; protected $dumpId = 'sf-dump'; protected $colors = true; @@ -101,7 +101,7 @@ protected function getDumpHeader() $this->headerIsDumped = true; if (null !== $this->dumpHeader) { - return str_replace('%id%', $this->dumpId, $this->dumpHeader); + return $this->dumpHeader; } $line = <<<'EOHTML' @@ -110,19 +110,81 @@ protected function getDumpHeader() Sfjs.dump = Sfjs.dump || {}; Sfjs.dump.childElts = Sfjs.dump.childElts || document.getElementsByName('sf-dump-child'); Sfjs.dump.childLen = Sfjs.dump.childLen || 0; +Sfjs.dump.refElts = Sfjs.dump.refElts || document.getElementsByName('sf-dump-ref'); +Sfjs.dump.refLen = Sfjs.dump.refLen || 0; +if (!Sfjs.dump.refStyle) { + Sfjs.dump.refStyle = document.createElement('style'); + document.documentElement.firstChild.appendChild(Sfjs.dump.refStyle); +} Sfjs.dump.instrument = Sfjs.dump.instrument || function () { - var elt, - i = this.childLen, - aCompact = '▶', - aExpanded = '▼'; + var elt, i, ref; + i = this.childLen; this.childLen= this.childElts.length; while (i < this.childLen) { elt = this.childElts[i]; if ("" == elt.className) { elt.className = "sf-dump-child"; - elt.innerHTML = ''+('sf-dump-0' == elt.parentNode.className ? aExpanded : aCompact)+elt.innerHTML+''; + elt.innerHTML = ''+elt.innerHTML+''; + if ('sf-dump-expanded' == elt.parentNode.className) { + if (elt.children[1].firstChild.nextSibling.id) { + elt.firstChild.appendChild(elt.children[1].firstChild); + elt.firstChild.appendChild(elt.children[1].firstChild); + } + Sfjs.dump.toggle(elt.firstChild); + } + } + ++i; + } + + i = this.refLen; + this.refLen= this.refElts.length; + + function instrumentRef(elt) { + var ref = elt.id; + + if (elt.href) { + ref = elt.getAttribute('href').substr(1); + if ('@' == elt.innerHTML.charAt(0)) { + elt.onclick = function() { + var r = document.getElementById(ref).parentNode.parentNode, + f = r && r.parentNode, + t = elt.parentNode, + c = elt.cloneNode(true); + if (r && r.className == "sf-dump-child") { + f.insertBefore(c, r); + try { + t.replaceChild(r, elt); + f.replaceChild(elt, c); + Sfjs.dump.refStyle.innerHTML = ''; + r = r.firstChild; + c = r.nextSibling; + if ('sf-dump-compact' == c.className) { + Sfjs.dump.toggle(r); + } + + return false; + } catch (e) { + f.removeChild(c); + } + } + }; + } + } + elt.className += ' '+ref; + elt.onmouseover = function() { + Sfjs.dump.refStyle.innerHTML = 'pre.sf-dump .'+ref+'{background-color: yellow; border-radius: 2px}'; + }; + elt.onmouseout = function() { + Sfjs.dump.refStyle.innerHTML = ''; + }; + } + + while (i < this.refLen) { + elt = this.refElts[i]; + if ("sf-dump-ref" == elt.className) { + instrumentRef(elt); } ++i; } @@ -131,16 +193,16 @@ protected function getDumpHeader() var s = a.nextElementSibling; if ('sf-dump-compact' == s.className) { - a.innerHTML = '▼'; + a.firstChild.innerHTML = '▼'; s.className = 'sf-dump-expanded'; } else { - a.innerHTML = '▶'; + a.firstChild.innerHTML = '▶'; s.className = 'sf-dump-compact'; } }; '.$this->dumpHeader; - - return str_replace('%id%', $this->dumpId, $this->dumpHeader); + return $this->dumpHeader = preg_replace('/\s+/', ' ', $line).''.$this->dumpHeader; } /** @@ -214,9 +272,9 @@ protected function style($style, $val) if ('ref' === $style) { $ref = substr($val, 1); if ('#' === $val[0]) { - return "dumpId}-ref$ref\">$val"; + return "dumpId}-ref$ref\">$val"; } else { - return "dumpId}-ref$ref\">$val"; + return "dumpId}-ref$ref\">$val"; } } @@ -231,7 +289,9 @@ protected function style($style, $val) } } elseif ('note' === $style) { if (false !== $c = strrpos($val, '\\')) { - $val = sprintf('%s', $val, $style, substr($val, $c+1)); + return sprintf('%s', $val, $style, substr($val, $c+1)); + } elseif (':' === $val[0]) { + return sprintf('%s', substr($val, 1), $style, $val); } } @@ -243,20 +303,15 @@ protected function style($style, $val) */ protected function dumpLine($depth) { - switch ($this->lastDepth - $depth) { - case +1: $this->line = ''.$this->line; break; - case -1: $this->line = "$this->line"; break; - } - if (-1 === $this->lastDepth) { - $this->line = str_replace('%id%', $this->dumpId, $this->dumpPrefix).$this->line; + $this->line = $this->dumpPrefix.$this->line; } if (!$this->headerIsDumped) { $this->line = $this->getDumpHeader().$this->line; } if (-1 === $depth) { - $this->line .= str_replace('%id%', $this->dumpId, $this->dumpSuffix); + $this->line .= $this->dumpSuffix; parent::dumpLine(0); } $this->lastDepth = $depth; diff --git a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php index 17d4ef84eb680..ac357d546942e 100644 --- a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php @@ -57,7 +57,7 @@ public function testGet() "str" => "déjà" 7 => b"é" "[]" => [] - "res" => resource:stream { + "res" => :stream { wrapper_type: "plainfile" stream_type: "STDIO" mode: "r" @@ -68,7 +68,7 @@ public function testGet() eof: false options: [] } - 8 => resource:Unknown {} + 8 => :Unknown {} "obj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo { #2 foo: "foo" "bar": "bar" diff --git a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php index 51be849ad6176..e8265599bd21e 100644 --- a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php @@ -44,14 +44,14 @@ public function testGet() $out = preg_replace('/[ \t]+$/m', '', $out); $var['file'] = htmlspecialchars($var['file'], ENT_QUOTES, 'UTF-8'); $intMax = PHP_INT_MAX; - preg_match('/sf-dump-(\\d{2,})/', $out, $matches); - $dumpId = $matches[1]; + preg_match('/sf-dump-\d+/', $out, $dumpId); + $dumpId = $dumpId[0]; $this->assertSame( <<array:25 [ - "number" => 1 - 0 => null #1 +array:25 [ + "number" => 1 + 0 => null #1 "const" => 1.1 1 => true 2 => false @@ -62,8 +62,8 @@ public function testGet() "str" => "déjà" 7 => b"é" "[]" => [] - "res" => resource:stream { - wrapper_type: "plainfile" + "res" => :stream { + wrapper_type: "plainfile" stream_type: "STDIO" mode: "r" unread_bytes: 0 @@ -72,14 +72,14 @@ public function testGet() blocked: true eof: false options: [] - } - 8 => resource:Unknown {} - "obj" => DumbFoo { #2 - foo: "foo" + } + 8 => :Unknown {} + "obj" => DumbFoo { #2 + foo: "foo" "bar": "bar" - } + } "closure" => Closure { - reflection: """ + reflection: """ Closure [ <user> {$closureLabel} Symfony\Component\VarDumper\Tests\Fixture\{closure} ] { @@ {$var['file']} {$var['line']} - {$var['line']} @@ -89,22 +89,22 @@ public function testGet() } } """ - } + } "line" => {$var['line']} "nobj" => array:1 [ - 0 => {} #3 - ] - "recurs" => array:1 [ #4 - 0 => &4 array:1 [@4] - ] - 9 => &1 null - "sobj" => DumbFoo {@2} - "snobj" => &3 {@3} - "snobj2" => {@3} + 0 => {} #3 + ] + "recurs" => array:1 [ #4 + 0 => &4 array:1 [@4] + ] + 9 => &1 null + "sobj" => DumbFoo {@2} + "snobj" => &3 {@3} + "snobj2" => {@3} "file" => "{$var['file']}" b"bin-key-é" => "" -] - +] + EOTXT , From 66ed17778e72ebced20473a4e7e534637d59b6dd Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Thu, 2 Oct 2014 12:40:09 +0200 Subject: [PATCH 266/373] Add an action to show *error* pages in kernel.debug mode --- .../Controller/ExceptionController.php | 54 +++++++++++++++---- .../Resources/config/routing/errors.xml | 12 +++++ .../Controller/ExceptionControllerTest.php | 40 ++++++++++++++ 3 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml diff --git a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php index 33a5507c7ea1e..18dfb6a50f5ff 100644 --- a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php @@ -21,6 +21,7 @@ * ExceptionController. * * @author Fabien Potencier + * @author Matthias Pigulla */ class ExceptionController { @@ -48,18 +49,24 @@ public function showAction(Request $request, FlattenException $exception, DebugL { $currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1)); - $code = $exception->getStatusCode(); + return $this->createResponse($request, $exception, $this->debug, $logger, $currentContent); + } - return new Response($this->twig->render( - $this->findTemplate($request, $request->getRequestFormat(), $code, $this->debug), - array( - 'status_code' => $code, - 'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '', - 'exception' => $exception, - 'logger' => $logger, - 'currentContent' => $currentContent, - ) - )); + /** + * Displays the error page for arbitrary status codes and formats. + * + * @param Request $request The request + * @param int $code The HTTP status code to show the error page for. + * + * @return Response + * + * @throws \InvalidArgumentException When the error template does not exist + */ + public function testErrorPageAction(Request $request, $code) + { + $exception = FlattenException::create(new \Exception("Something has intentionally gone wrong."), $code); + + return $this->createResponse($request, $exception, false); } /** @@ -130,4 +137,29 @@ protected function templateExists($template) return false; } + + /** + * @param Request $request + * @param FlattenException $exception + * @param bool $debug + * @param DebugLoggerInterface $logger + * @param string $currentContent + * + * @return Response + */ + protected function createResponse(Request $request, FlattenException $exception, $debug, DebugLoggerInterface $logger = null, $currentContent = '') + { + $code = $exception->getStatusCode(); + + return new Response($this->twig->render( + $this->findTemplate($request, $request->getRequestFormat(), $code, $debug), + array( + 'status_code' => $code, + 'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '', + 'exception' => $exception, + 'logger' => $logger, + 'currentContent' => $currentContent, + ) + )); + } } diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml new file mode 100644 index 0000000000000..493af74c019d9 --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml @@ -0,0 +1,12 @@ + + + + + + twig.controller.exception:testErrorPageAction + html + \d+ + + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php index 18523eaa76732..39e17cb221ee9 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php @@ -41,4 +41,44 @@ public function testOnlyClearOwnOutputBuffers() $controller = new ExceptionController($twig, false); $controller->showAction($request, $flatten); } + + public function testErrorPagesInDebugMode() + { + $twig = new \Twig_Environment( + new \Twig_Loader_Array(array( + 'TwigBundle:Exception:error404.html.twig' => ' + {%- if exception is defined and status_text is defined and status_code is defined -%} + OK + {%- else -%} + "exception" variable is missing + {%- endif -%} + ', + )) + ); + + $request = Request::create('whatever'); + + $controller = new ExceptionController($twig, /* "debug" set to --> */ true); + $response = $controller->testErrorPageAction($request, 404); + + $this->assertEquals(200, $response->getStatusCode()); // successful request + $this->assertEquals('OK', $response->getContent()); // content of the error404.html template + } + + public function testFallbackToHtmlIfNoTemplateForRequestedFormat() + { + $twig = new \Twig_Environment( + new \Twig_Loader_Array(array( + 'TwigBundle:Exception:error.html.twig' => 'html', + )) + ); + + $request = Request::create('whatever'); + $request->setRequestFormat('txt'); + + $controller = new ExceptionController($twig, false); + $response = $controller->testErrorPageAction($request, 42); + + $this->assertEquals('html', $request->getRequestFormat()); + } } From 6a507ccaef6bdb8c951eee15b5b6ae16b599b0b1 Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Sun, 21 Sep 2014 20:22:48 +0100 Subject: [PATCH 267/373] [Yaml] Ensure whole number floats have their data type is persisted. --- src/Symfony/Component/Yaml/Inline.php | 15 +++++++++++++-- src/Symfony/Component/Yaml/Tests/DumperTest.php | 3 +-- .../Tests/Fixtures/YtsSpecificationExamples.yml | 2 ++ src/Symfony/Component/Yaml/Tests/InlineTest.php | 4 ++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 811e75570845e..40b9c4fd2d3e7 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -128,8 +128,17 @@ public static function dump($value, $exceptionOnInvalidType = false, $objectSupp if (false !== $locale) { setlocale(LC_NUMERIC, 'C'); } - $repr = is_string($value) ? "'$value'" : (is_infinite($value) ? str_ireplace('INF', '.Inf', strval($value)) : strval($value)); - + if (is_float($value)) { + $repr = strval($value); + if (is_infinite($value)) { + $repr = str_ireplace('INF', '.Inf', $repr); + } elseif (floor($value) == $value && $repr == $value) { + // Preserve float data type since storing a whole number will result in integer value. + $repr = '!!float '.$repr; + } + } else { + $repr = is_string($value) ? "'$value'" : strval($value); + } if (false !== $locale) { setlocale(LC_NUMERIC, $locale); } @@ -470,6 +479,8 @@ private static function evaluateScalar($scalar, $references = array()) } return; + case 0 === strpos($scalar, '!!float '): + return (float) substr($scalar, 8); case ctype_digit($scalar): $raw = $scalar; $cast = intval($scalar); diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index 53d164c4ba55a..e3d21def13fa1 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -96,8 +96,7 @@ public function testSpecifications() // TODO } else { eval('$expected = '.trim($test['php']).';'); - - $this->assertEquals($expected, $this->parser->parse($this->dumper->dump($expected, 10)), $test['test']); + $this->assertSame($expected, $this->parser->parse($this->dumper->dump($expected, 10)), $test['test']); } } } diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml index 1e59f3bf98884..b011236f9a611 100644 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml +++ b/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml @@ -529,6 +529,7 @@ yaml: | fixed: 1,230.15 negative infinity: -.inf not a number: .NaN + float as whole number: !!float 1 php: | array( 'canonical' => 1230.15, @@ -536,6 +537,7 @@ php: | 'fixed' => 1230.15, 'negative infinity' => log(0), 'not a number' => -log(0), + 'float as whole number' => (float) 1 ) --- test: Miscellaneous diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 49c76d5d1c535..2f6e9d6349779 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -40,7 +40,7 @@ public function testDump($yaml, $value) { $this->assertEquals($yaml, Inline::dump($value), sprintf('::dump() converts a PHP structure to an inline YAML (%s)', $yaml)); - $this->assertEquals($value, Inline::parse(Inline::dump($value)), 'check consistency'); + $this->assertSame($value, Inline::parse(Inline::dump($value)), 'check consistency'); } public function testDumpNumericValueWithLocale() @@ -323,7 +323,7 @@ public function getTestsForDump() array('true', true), array('12', 12), array("'quoted string'", 'quoted string'), - array('12.30e+02', 12.30e+02), + array('!!float 1230', 12.30e+02), array('1234', 0x4D2), array('1243', 02333), array('.Inf', -log(0)), From e3332ad882cc995c7662ec9e168a697165bf7add Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 5 Oct 2014 22:04:12 +0200 Subject: [PATCH 268/373] [TwigBridge] fix tests --- .../Twig/Tests/Extension/DumpExtensionTest.php | 14 +++++++------- src/Symfony/Bundle/FrameworkBundle/composer.json | 2 +- src/Symfony/Bundle/SecurityBundle/composer.json | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php index 35d51d3a4cd39..16361e3e5debc 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php @@ -86,20 +86,20 @@ public function getDumpArgs() { return array( array(array(), array(), '', false), - array(array(), array(), "
    []\n
    \n"), + array(array(), array(), "
    []\n
    \n"), array( array(), array(123, 456), - "
    123\n
    \n" - ."
    456\n
    \n", + "
    123\n
    \n" + ."
    456\n
    \n", ), array( array('foo' => 'bar'), array(), - "
    array:1 [\n"
    -                ."  \"foo\" => \"bar\"\n"
    -                ."]\n"
    -                ."
    \n", + "
    array:1 [\n"
    +                ."  \"foo\" => \"bar\"\n"
    +                ."]\n"
    +                ."
    \n", ), ); } diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 0ef82e0935b7b..d3bcf945ecf92 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -36,7 +36,7 @@ "symfony/console": "~2.0", "symfony/finder": "~2.0", "symfony/security": "~2.4", - "symfony/form": "2.5.*", + "symfony/form": "~2.6@dev", "symfony/class-loader": "~2.1", "symfony/expression-language": "~2.4", "symfony/process": "~2.0", diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 73559a1288af5..ba0de66572d40 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -22,6 +22,7 @@ }, "require-dev": { "symfony/browser-kit": "~2.3", + "symfony/console": "~2.3", "symfony/form": "~2.3", "symfony/framework-bundle": "~2.2,<2.6.0", "symfony/twig-bundle": "~2.2", From da96ad846d112188e960bb911b2728f1ae139721 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 6 Oct 2014 08:45:07 +0200 Subject: [PATCH 269/373] [VarDumper] Sfdump JS ns and VarCloner replacing Ext+PhpCloner --- .../Tests/Extension/DumpExtensionTest.php | 16 +- .../DependencyInjection/DebugExtension.php | 5 - .../DebugBundle/Resources/config/services.xml | 11 +- .../Resources/views/Profiler/dump.html.twig | 2 +- .../TwigBundle/Resources/config/debug.xml | 3 +- .../DataCollector/DumpDataCollectorTest.php | 2 +- .../Component/VarDumper/Cloner/ExtCloner.php | 201 ------------------ .../Cloner/{PhpCloner.php => VarCloner.php} | 101 ++++++--- .../Component/VarDumper/Dumper/HtmlDumper.php | 35 ++- .../VarDumper/Tests/CliDumperTest.php | 6 +- .../VarDumper/Tests/HtmlDumperTest.php | 4 +- src/Symfony/Component/VarDumper/VarDumper.php | 5 +- 12 files changed, 105 insertions(+), 286 deletions(-) delete mode 100644 src/Symfony/Component/VarDumper/Cloner/ExtCloner.php rename src/Symfony/Component/VarDumper/Cloner/{PhpCloner.php => VarCloner.php} (68%) diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php index 16361e3e5debc..05166bd29335f 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php @@ -13,7 +13,7 @@ use Symfony\Bridge\Twig\Extension\DumpExtension; use Symfony\Component\VarDumper\VarDumper; -use Symfony\Component\VarDumper\Cloner\PhpCloner; +use Symfony\Component\VarDumper\Cloner\VarCloner; class DumpExtensionTest extends \PHPUnit_Framework_TestCase { @@ -22,7 +22,7 @@ class DumpExtensionTest extends \PHPUnit_Framework_TestCase */ public function testDumpTag($template, $debug, $expectedOutput, $expectedDumped) { - $extension = new DumpExtension(new PhpCloner()); + $extension = new DumpExtension(new VarCloner()); $twig = new \Twig_Environment(new \Twig_Loader_String(), array( 'debug' => $debug, 'cache' => false, @@ -62,7 +62,7 @@ public function getDumpTags() */ public function testDump($context, $args, $expectedOutput, $debug = true) { - $extension = new DumpExtension(new PhpCloner()); + $extension = new DumpExtension(new VarCloner()); $twig = new \Twig_Environment(new \Twig_Loader_String(), array( 'debug' => $debug, 'cache' => false, @@ -77,7 +77,7 @@ public function testDump($context, $args, $expectedOutput, $debug = true) if ($debug) { $this->assertStringStartsWith('\n"), + array(array(), array(), "
    []\n
    \n"), array( array(), array(123, 456), - "
    123\n
    \n" - ."
    456\n
    \n", + "
    123\n
    \n" + ."
    456\n
    \n", ), array( array('foo' => 'bar'), @@ -99,7 +99,7 @@ public function getDumpArgs() "
    array:1 [\n"
                     ."  \"foo\" => \"bar\"\n"
                     ."]\n"
    -                ."
    \n", + ."
    \n", ), ); } diff --git a/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php b/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php index 4b136c89454b8..23e9d0c96c4c3 100644 --- a/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php +++ b/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php @@ -34,11 +34,6 @@ public function load(array $configs, ContainerBuilder $container) $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.xml'); - $container->setParameter( - 'var_dumper.cloner.class', - 'Symfony\Component\VarDumper\Cloner\\'.(function_exists('symfony_zval_info') ? 'Ext' : 'Php').'Cloner' - ); - $container->getDefinition('var_dumper.cloner') ->addMethodCall('setMaxItems', array($config['max_items'])) ->addMethodCall('setMaxString', array($config['max_string_length'])); diff --git a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml index 290644c6d534a..0593cc7efa14d 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml @@ -4,25 +4,20 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - Symfony\Component\HttpKernel\DataCollector\DumpDataCollector - Symfony\Component\HttpKernel\EventListener\DumpListener - - - + %templating.helper.code.file_link_format% - + data_collector.dump - + diff --git a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig index b453928c04210..71f0082725895 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig +++ b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig @@ -84,7 +84,7 @@ {{ dump.name }} {% endif %} line {{ dump.line }}: - + {% if dump.fileExcerpt %}{{ dump.fileExcerpt|raw }}{% else %}{{ dump.file|file_excerpt(dump.line) }}{% endif %} diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml index 36ddc2a33d82a..70bdafa77cdb0 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml @@ -6,7 +6,6 @@ Symfony\Bundle\TwigBundle\Debug\TimedTwigEngine - Symfony\Bridge\Twig\Extension\DumpExtension @@ -17,7 +16,7 @@ - + diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 5cfdba27d9e0d..5af2959c892e3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -38,7 +38,7 @@ public function testDump() $xDump = array( array( - 'data' => "
    123\n
    \n", + 'data' => "
    123\n
    \n", 'name' => 'DumpDataCollectorTest.php', 'file' => __FILE__, 'line' => $line, diff --git a/src/Symfony/Component/VarDumper/Cloner/ExtCloner.php b/src/Symfony/Component/VarDumper/Cloner/ExtCloner.php deleted file mode 100644 index cf1a0d19ea1c5..0000000000000 --- a/src/Symfony/Component/VarDumper/Cloner/ExtCloner.php +++ /dev/null @@ -1,201 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * @author Nicolas Grekas - */ -class ExtCloner extends AbstractCloner -{ - /** - * {@inheritdoc} - */ - protected function doClone($var) - { - $i = 0; // Current iteration position in $queue - $len = 1; // Length of $queue - $pos = 0; // Number of cloned items past the first level - $refs = 0; // Number of hard+soft references in $var - $queue = array(array($var)); // This breadth-first queue is the return value - $arrayRefs = array(); // Map of queue indexes to stub array objects - $hardRefs = array(); // Map of original zval hashes to stub objects - $softRefs = array(); // Map of original object hashes to their stub object couterpart - $maxItems = $this->maxItems; - $maxString = $this->maxString; - $a = null; // Array cast for nested structures - $stub = null; // Stub capturing the main properties of an original item value, - // or null if the original value is used directly - - for ($i = 0; $i < $len; ++$i) { - $indexed = true; // Whether the currently iterated array is numerically indexed or not - $j = -1; // Position in the currently iterated array - $step = $queue[$i]; // Copy of the currently iterated array used for hard references detection - foreach ($step as $k => $v) { - // $k is the original key - // $v is the original value or a stub object in case of hard references - if ($indexed && $k !== ++$j) { - $indexed = false; - } - $zval = symfony_zval_info($k, $step); - if ($zval['zval_isref']) { - $queue[$i][$k] =& $stub; // Break hard references to make $queue completely - unset($stub); // independent from the original structure - if (isset($hardRefs[$h = $zval['zval_hash']])) { - $hardRefs[$h]->ref = ++$refs; - $queue[$i][$k] = $hardRefs[$h]; - continue; - } - } - // Create $stub when the original value $v can not be used directly - // If $v is a nested structure, put that structure in array $a - switch ($zval['type']) { - case 'string': - if (isset($v[0]) && !preg_match('//u', $v)) { - $stub = new Stub(); - $stub->type = Stub::TYPE_STRING; - $stub->class = Stub::STRING_BINARY; - if (0 <= $maxString && 0 < $cut = strlen($v) - $maxString) { - $stub->cut = $cut; - $v = substr_replace($v, '', -$cut); - } - $stub->value = Data::utf8Encode($v); - } elseif (0 <= $maxString && isset($v[1+($maxString>>2)]) && 0 < $cut = iconv_strlen($v, 'UTF-8') - $maxString) { - $stub = new Stub(); - $stub->type = Stub::TYPE_STRING; - $stub->class = Stub::STRING_UTF8; - $stub->cut = $cut; - $stub->value = iconv_substr($v, 0, $maxString, 'UTF-8'); - } - break; - - case 'integer': - break; - - case 'array': - if ($v) { - $stub = $arrayRefs[$len] = new Stub(); - $stub->type = Stub::TYPE_ARRAY; - $stub->class = Stub::ARRAY_ASSOC; - $stub->value = $zval['array_count']; - $a = $v; - } - break; - - case 'object': - if (empty($softRefs[$h = $zval['object_hash']])) { - $stub = new Stub(); - $stub->type = Stub::TYPE_OBJECT; - $stub->class = $zval['object_class']; - $stub->value = $v; - $a = $this->castObject($stub, 0 < $i); - if ($v !== $stub->value) { - if (Stub::TYPE_OBJECT !== $stub->type) { - break; - } - $h = spl_object_hash($stub->value); - } - $stub->value = null; - if (0 <= $maxItems && $maxItems <= $pos) { - $stub->cut = count($a); - $a = null; - } - } - if (empty($softRefs[$h])) { - $softRefs[$h] = $stub; - } else { - $stub = $softRefs[$h]; - $stub->ref = ++$refs; - $a = null; - } - break; - - case 'resource': - if (empty($softRefs[$h = (int) $v])) { - $stub = new Stub(); - $stub->type = Stub::TYPE_RESOURCE; - $stub->class = $zval['resource_type']; - $stub->value = $v; - $a = $this->castResource($stub, 0 < $i); - if ($v !== $stub->value) { - if (Stub::TYPE_RESOURCE !== $stub->type) { - break; - } - $h = (int) $stub->value; - } - $stub->value = null; - if (0 <= $maxItems && $maxItems <= $pos) { - $stub->cut = count($a); - $a = null; - } - } - if (empty($softRefs[$h])) { - $softRefs[$h] = $stub; - } else { - $stub = $softRefs[$h]; - $stub->ref = ++$refs; - $a = null; - } - break; - } - - if (isset($stub)) { - if ($zval['zval_isref']) { - if (Stub::TYPE_ARRAY === $stub->type) { - $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $stub; - } else { - $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub(); - $v->value = $stub; - } - } else { - $queue[$i][$k] = $stub; - } - - if ($a) { - if ($i && 0 <= $maxItems) { - $k = count($a); - if ($pos < $maxItems) { - if ($maxItems < $pos += $k) { - $a = array_slice($a, 0, $maxItems - $pos); - if ($stub->cut >= 0) { - $stub->cut += $pos - $maxItems; - } - } - } else { - if ($stub->cut >= 0) { - $stub->cut += $k; - } - $stub = $a = null; - unset($arrayRefs[$len]); - continue; - } - } - $queue[$len] = $a; - $stub->position = $len++; - } - $stub = $a = null; - } elseif ($zval['zval_isref']) { - $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = new Stub(); - $queue[$i][$k]->value = $v; - } - } - - if (isset($arrayRefs[$i])) { - if ($indexed) { - $arrayRefs[$i]->class = Stub::ARRAY_INDEXED; - } - unset($arrayRefs[$i]); - } - } - - return $queue; - } -} diff --git a/src/Symfony/Component/VarDumper/Cloner/PhpCloner.php b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php similarity index 68% rename from src/Symfony/Component/VarDumper/Cloner/PhpCloner.php rename to src/Symfony/Component/VarDumper/Cloner/VarCloner.php index 3fdafff66746d..6d1a9ffd9531a 100644 --- a/src/Symfony/Component/VarDumper/Cloner/PhpCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php @@ -14,29 +14,37 @@ /** * @author Nicolas Grekas */ -class PhpCloner extends AbstractCloner +class VarCloner extends AbstractCloner { /** * {@inheritdoc} */ protected function doClone($var) { + $useExt = extension_loaded('symfony_debug'); $i = 0; // Current iteration position in $queue $len = 1; // Length of $queue $pos = 0; // Number of cloned items past the first level $refs = 0; // Number of hard+soft references in $var $queue = array(array($var)); // This breadth-first queue is the return value $arrayRefs = array(); // Map of queue indexes to stub array objects - $hardRefs = array(); // By-ref map of stub objects' hashes to original hard `&` references + $hardRefs = array(); // Map of original zval hashes to stub objects $softRefs = array(); // Map of original object hashes to their stub object couterpart $values = array(); // Map of stub objects' hashes to original values $maxItems = $this->maxItems; $maxString = $this->maxString; $cookie = (object) array(); // Unique object used to detect hard references - $isRef = false; $a = null; // Array cast for nested structures $stub = null; // Stub capturing the main properties of an original item value, // or null if the original value is used directly + $zval = array( // Main properties of the current value + 'type' => null, + 'zval_isref' => null, + 'array_count' => null, + 'object_class' => null, + 'object_hash' => null, + 'resource_type' => null, + ); for ($i = 0; $i < $len; ++$i) { $indexed = true; // Whether the currently iterated array is numerically indexed or not @@ -48,20 +56,33 @@ protected function doClone($var) if ($indexed && $k !== ++$j) { $indexed = false; } - $step[$k] = $cookie; - if ($queue[$i][$k] === $cookie) { - $queue[$i][$k] =& $stub; // Break hard references to make $queue completely - unset($stub); // independent from the original structure - if ($v instanceof Stub && isset($hardRefs[spl_object_hash($v)])) { - $v->ref = ++$refs; - $step[$k] = $queue[$i][$k] = $v; - continue; + if ($useExt) { + $zval = symfony_zval_info($k, $step); + if ($zval['zval_isref']) { + $queue[$i][$k] =& $stub; // Break hard references to make $queue completely + unset($stub); // independent from the original structure + if (isset($hardRefs[$h = $zval['zval_hash']])) { + $hardRefs[$h]->ref = ++$refs; + $queue[$i][$k] = $hardRefs[$h]; + continue; + } + } + } else { + $step[$k] = $cookie; + if ($zval['zval_isref'] = $queue[$i][$k] === $cookie) { + $queue[$i][$k] =& $stub; // Break hard references to make $queue completely + unset($stub); // independent from the original structure + if ($v instanceof Stub && isset($hardRefs[spl_object_hash($v)])) { + $v->ref = ++$refs; + $step[$k] = $queue[$i][$k] = $v; + continue; + } } - $isRef = true; + $zval['type'] = gettype($v); } // Create $stub when the original value $v can not be used directly // If $v is a nested structure, put that structure in array $a - switch (gettype($v)) { + switch ($zval['type']) { case 'string': if (isset($v[0]) && !preg_match('//u', $v)) { $stub = new Stub(); @@ -74,7 +95,7 @@ protected function doClone($var) $cut = $v; } $stub->value = Data::utf8Encode($cut); - } elseif (0 <= $maxString && isset($v[1+($maxString>>2)]) && 0 < $cut = iconv_strlen($v, 'UTF-8') - $maxString) { + } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = iconv_strlen($v, 'UTF-8') - $maxString) { $stub = new Stub(); $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_UTF8; @@ -91,16 +112,16 @@ protected function doClone($var) $stub = $arrayRefs[$len] = new Stub(); $stub->type = Stub::TYPE_ARRAY; $stub->class = Stub::ARRAY_ASSOC; - $stub->value = count($v); + $stub->value = $v ? $zval['array_count'] ?: count($v) : 0; $a = $v; } break; case 'object': - if (empty($softRefs[$h = spl_object_hash($v)])) { + if (empty($softRefs[$h = $zval['object_hash'] ?: spl_object_hash($v)])) { $stub = new Stub(); $stub->type = Stub::TYPE_OBJECT; - $stub->class = get_class($v); + $stub->class = $zval['object_class'] ?: get_class($v); $stub->value = $v; $a = $this->castObject($stub, 0 < $i); if ($v !== $stub->value) { @@ -129,7 +150,7 @@ protected function doClone($var) if (empty($softRefs[$h = (int) $v])) { $stub = new Stub(); $stub->type = Stub::TYPE_RESOURCE; - $stub->class = get_resource_type($v); + $stub->class = $zval['resource_type'] ?: get_resource_type($v); $stub->value = $v; $a = $this->castResource($stub, 0 < $i); if ($v !== $stub->value) { @@ -155,17 +176,25 @@ protected function doClone($var) } if (isset($stub)) { - if ($isRef) { - if (Stub::TYPE_ARRAY === $stub->type) { - $step[$k] = $stub; + if ($zval['zval_isref']) { + if ($useExt) { + if (Stub::TYPE_ARRAY === $stub->type) { + $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $stub; + } else { + $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub(); + $v->value = $stub; + } } else { - $step[$k] = new Stub(); - $step[$k]->value = $stub; + if (Stub::TYPE_ARRAY === $stub->type) { + $step[$k] = $stub; + } else { + $step[$k] = new Stub(); + $step[$k]->value = $stub; + } + $h = spl_object_hash($step[$k]); + $queue[$i][$k] = $hardRefs[$h] =& $step[$k]; + $values[$h] = $v; } - $h = spl_object_hash($step[$k]); - $queue[$i][$k] = $hardRefs[$h] =& $step[$k]; - $values[$h] = $v; - $isRef = false; } else { $queue[$i][$k] = $stub; } @@ -193,13 +222,17 @@ protected function doClone($var) $stub->position = $len++; } $stub = $a = null; - } elseif ($isRef) { - $step[$k] = $queue[$i][$k] = new Stub(); - $step[$k]->value = $v; - $h = spl_object_hash($step[$k]); - $hardRefs[$h] =& $step[$k]; - $values[$h] = $v; - $isRef = false; + } elseif ($zval['zval_isref']) { + if ($useExt) { + $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = new Stub(); + $queue[$i][$k]->value = $v; + } else { + $step[$k] = $queue[$i][$k] = new Stub(); + $step[$k]->value = $v; + $h = spl_object_hash($step[$k]); + $hardRefs[$h] =& $step[$k]; + $values[$h] = $v; + } } } diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index b8bb8192f9b17..b6093fa14ed27 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -25,7 +25,7 @@ class HtmlDumper extends CliDumper protected $dumpHeader; protected $dumpPrefix = '
    ';
    -    protected $dumpSuffix = '
    '; + protected $dumpSuffix = '
    '; protected $dumpId = 'sf-dump'; protected $colors = true; protected $headerIsDumped = false; @@ -106,17 +106,16 @@ protected function getDumpHeader() $line = <<<'EOHTML' \n"), + array(array(), array(), "
    []\n
    \n"), array( array(), array(123, 456), - "
    123\n
    \n" - ."
    456\n
    \n", + "
    123\n
    \n" + ."
    456\n
    \n", ), array( array('foo' => 'bar'), array(), - "
    array:1 [\n"
    +                "
    array:1 [\n"
                     ."  \"foo\" => \"bar\"\n"
    -                ."]\n"
    -                ."
    \n", + ."]\n" + ."
    \n", ), ); } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 5af2959c892e3..f18a7f63788a3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -35,10 +35,11 @@ public function testDump() $dump = $collector->getDumps('html'); $this->assertTrue(isset($dump[0]['data'])); $dump[0]['data'] = preg_replace('/^.*?
     "
    123\n
    \n", + 'data' => "
    123\n
    \n", 'name' => 'DumpDataCollectorTest.php', 'file' => __FILE__, 'line' => $line, diff --git a/src/Symfony/Component/VarDumper/Caster/CasterStub.php b/src/Symfony/Component/VarDumper/Caster/CasterStub.php index 56fc2b112eee8..c486dc1aaa4ea 100644 --- a/src/Symfony/Component/VarDumper/Caster/CasterStub.php +++ b/src/Symfony/Component/VarDumper/Caster/CasterStub.php @@ -41,6 +41,7 @@ public function __construct($value, $class = '') case 'resource': case 'unknown type': $this->type = self::TYPE_RESOURCE; + $this->handle = (int) $value; $this->class = @get_resource_type($value); $this->cut = -1; break; diff --git a/src/Symfony/Component/VarDumper/Caster/StubCaster.php b/src/Symfony/Component/VarDumper/Caster/StubCaster.php index 294a13c562c2b..ba994591744cf 100644 --- a/src/Symfony/Component/VarDumper/Caster/StubCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/StubCaster.php @@ -26,6 +26,7 @@ public static function castStub(CasterStub $c, array $a, Stub $stub, $isNested) $stub->type = $c->type; $stub->class = $c->class; $stub->value = $c->value; + $stub->handle = $c->handle; $stub->cut = $c->cut; return array(); diff --git a/src/Symfony/Component/VarDumper/Cloner/Cursor.php b/src/Symfony/Component/VarDumper/Cloner/Cursor.php index 50266ea52ab3e..4622b964f27c2 100644 --- a/src/Symfony/Component/VarDumper/Cloner/Cursor.php +++ b/src/Symfony/Component/VarDumper/Cloner/Cursor.php @@ -24,9 +24,13 @@ class Cursor const HASH_RESOURCE = Stub::TYPE_RESOURCE; public $depth = 0; - public $refIndex = false; - public $softRefTo = false; - public $hardRefTo = false; + public $refIndex = 0; + public $softRefTo = 0; + public $softRefCount = 0; + public $softRefHandle = 0; + public $hardRefTo = 0; + public $hardRefCount = 0; + public $hardRefHandle = 0; public $hashType; public $hashKey; public $hashIndex = 0; diff --git a/src/Symfony/Component/VarDumper/Cloner/Data.php b/src/Symfony/Component/VarDumper/Cloner/Data.php index 077e19b4875a9..fb46aeab19afa 100644 --- a/src/Symfony/Component/VarDumper/Cloner/Data.php +++ b/src/Symfony/Component/VarDumper/Cloner/Data.php @@ -59,7 +59,7 @@ public function getLimitedClone($maxDepth, $maxItemsPerDepth) public function dump(DumperInterface $dumper) { $refs = array(0); - $this->dumpItem($dumper, new Cursor, $refs, $this->data[0][0]); + $this->dumpItem($dumper, new Cursor(), $refs, $this->data[0][0]); } /** @@ -72,40 +72,41 @@ public function dump(DumperInterface $dumper) */ private function dumpItem($dumper, $cursor, &$refs, $item) { - $cursor->refIndex = $cursor->softRefTo = $cursor->hardRefTo = false; + $cursor->refIndex = 0; + $cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0; + $cursor->hardRefTo = $cursor->hardRefHandle = $cursor->hardRefCount = 0; + $firstSeen = true; if (!$item instanceof Stub) { $type = gettype($item); } elseif (Stub::TYPE_REF === $item->type) { - if ($item->ref) { - if (isset($refs[$r = $item->ref])) { - $cursor->hardRefTo = $refs[$r]; + if ($item->handle) { + if (!isset($refs[$r = $item->handle - (PHP_INT_MAX >> 1)])) { + $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; } else { - $cursor->refIndex = $refs[$r] = ++$refs[0]; + $firstSeen = false; } + $cursor->hardRefTo = $refs[$r]; + $cursor->hardRefHandle = $item->handle; + $cursor->hardRefCount = $item->refCount; } $type = $item->class ?: gettype($item->value); $item = $item->value; } if ($item instanceof Stub) { - if ($item->ref) { - if (isset($refs[$r = $item->ref])) { - if (Stub::TYPE_ARRAY === $item->type) { - if (false === $cursor->hardRefTo) { - $cursor->hardRefTo = $refs[$r]; - } - } elseif (false === $cursor->softRefTo) { - $cursor->softRefTo = $refs[$r]; - } - } elseif (false !== $cursor->refIndex) { - $refs[$r] = $cursor->refIndex; + if ($item->refCount) { + if (!isset($refs[$r = $item->handle])) { + $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; } else { - $cursor->refIndex = $refs[$r] = ++$refs[0]; + $firstSeen = false; } + $cursor->softRefTo = $refs[$r]; } + $cursor->softRefHandle = $item->handle; + $cursor->softRefCount = $item->refCount; $cut = $item->cut; - if ($item->position && false === $cursor->softRefTo && false === $cursor->hardRefTo) { + if ($item->position && $firstSeen) { $children = $this->data[$item->position]; if ($cursor->stop) { @@ -123,29 +124,24 @@ private function dumpItem($dumper, $cursor, &$refs, $item) break; case Stub::TYPE_ARRAY: - $dumper->enterArray($cursor, $item->value, Stub::ARRAY_INDEXED === $item->class, (bool) $children); + $dumper->enterHash($cursor, $item->class, $item->value, (bool) $children); $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->class); - $dumper->leaveArray($cursor, $item->value, Stub::ARRAY_INDEXED === $item->class, (bool) $children, $cut); + $dumper->leaveHash($cursor, $item->class, $item->value, (bool) $children, $cut); break; case Stub::TYPE_OBJECT: - $dumper->enterObject($cursor, $item->class, (bool) $children); - $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, Cursor::HASH_OBJECT); - $dumper->leaveObject($cursor, $item->class, (bool) $children, $cut); - break; - case Stub::TYPE_RESOURCE: - $dumper->enterResource($cursor, $item->class, (bool) $children); - $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, Cursor::HASH_RESOURCE); - $dumper->leaveResource($cursor, $item->class, (bool) $children, $cut); + $dumper->enterHash($cursor, $item->type, $item->class, (bool) $children); + $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type); + $dumper->leaveHash($cursor, $item->type, $item->class, (bool) $children, $cut); break; default: throw new \RuntimeException(sprintf('Unexpected Stub type: %s', $item->type)); } } elseif ('array' === $type) { - $dumper->enterArray($cursor, 0, true, 0, 0); - $dumper->leaveArray($cursor, 0, true, 0, 0); + $dumper->enterHash($cursor, Cursor::HASH_INDEXED, 0, false); + $dumper->leaveHash($cursor, Cursor::HASH_INDEXED, 0, false, 0); } else { $dumper->dumpScalar($cursor, $type, $item); } diff --git a/src/Symfony/Component/VarDumper/Cloner/DumperInterface.php b/src/Symfony/Component/VarDumper/Cloner/DumperInterface.php index d910834ddf96b..eba23d42297ec 100644 --- a/src/Symfony/Component/VarDumper/Cloner/DumperInterface.php +++ b/src/Symfony/Component/VarDumper/Cloner/DumperInterface.php @@ -38,61 +38,23 @@ public function dumpScalar(Cursor $cursor, $type, $value); public function dumpString(Cursor $cursor, $str, $bin, $cut); /** - * Dumps while entering an array. + * Dumps while entering an hash. * * @param Cursor $cursor The Cursor position in the dump. - * @param int $count The number of items in the original array. - * @param bool $indexed When the array is indexed or associative. - * @param bool $hasChild When the dump of the array has child item. + * @param int $type A Cursor::HASH_* const for the type of hash. + * @param string $class The object class, resource type or array count. + * @param bool $hasChild When the dump of the hash has child item. */ - public function enterArray(Cursor $cursor, $count, $indexed, $hasChild); + public function enterHash(Cursor $cursor, $type, $class, $hasChild); /** - * Dumps while leaving an array. + * Dumps while leaving an hash. * * @param Cursor $cursor The Cursor position in the dump. - * @param int $count The number of items in the original array. - * @param bool $indexed Whether the array is indexed or associative. - * @param bool $hasChild When the dump of the array has child item. - * @param int $cut The number of items the array has been cut by. + * @param int $type A Cursor::HASH_* const for the type of hash. + * @param string $class The object class, resource type or array count. + * @param bool $hasChild When the dump of the hash has child item. + * @param int $cut The number of items the hash has been cut by. */ - public function leaveArray(Cursor $cursor, $count, $indexed, $hasChild, $cut); - - /** - * Dumps while entering an object. - * - * @param Cursor $cursor The Cursor position in the dump. - * @param string $class The class of the object. - * @param bool $hasChild When the dump of the object has child item. - */ - public function enterObject(Cursor $cursor, $class, $hasChild); - - /** - * Dumps while leaving an object. - * - * @param Cursor $cursor The Cursor position in the dump. - * @param string $class The class of the object. - * @param bool $hasChild When the dump of the object has child item. - * @param int $cut The number of items the object has been cut by. - */ - public function leaveObject(Cursor $cursor, $class, $hasChild, $cut); - - /** - * Dumps while entering a resource. - * - * @param Cursor $cursor The Cursor position in the dump. - * @param string $res The resource type. - * @param bool $hasChild When the dump of the resource has child item. - */ - public function enterResource(Cursor $cursor, $res, $hasChild); - - /** - * Dumps while leaving a resource. - * - * @param Cursor $cursor The Cursor position in the dump. - * @param string $res The resource type. - * @param bool $hasChild When the dump of the resource has child item. - * @param int $cut The number of items the resource has been cut by. - */ - public function leaveResource(Cursor $cursor, $res, $hasChild, $cut); + public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut); } diff --git a/src/Symfony/Component/VarDumper/Cloner/Stub.php b/src/Symfony/Component/VarDumper/Cloner/Stub.php index 9400a1e077d11..f58a57a72761b 100644 --- a/src/Symfony/Component/VarDumper/Cloner/Stub.php +++ b/src/Symfony/Component/VarDumper/Cloner/Stub.php @@ -34,6 +34,7 @@ class Stub public $class = ''; public $value; public $cut = 0; - public $ref = 0; + public $handle = 0; + public $refCount = 0; public $position = 0; } diff --git a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php index 6d1a9ffd9531a..605977be80891 100644 --- a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php @@ -16,6 +16,9 @@ */ class VarCloner extends AbstractCloner { + private static $hashMask = 0; + private static $hashOffset = 0; + /** * {@inheritdoc} */ @@ -25,11 +28,12 @@ protected function doClone($var) $i = 0; // Current iteration position in $queue $len = 1; // Length of $queue $pos = 0; // Number of cloned items past the first level - $refs = 0; // Number of hard+soft references in $var + $refs = 0; // Hard references counter $queue = array(array($var)); // This breadth-first queue is the return value $arrayRefs = array(); // Map of queue indexes to stub array objects $hardRefs = array(); // Map of original zval hashes to stub objects - $softRefs = array(); // Map of original object hashes to their stub object couterpart + $objRefs = array(); // Map of original object handles to their stub object couterpart + $resRefs = array(); // Map of original resource handles to their stub object couterpart $values = array(); // Map of stub objects' hashes to original values $maxItems = $this->maxItems; $maxString = $this->maxString; @@ -40,11 +44,17 @@ protected function doClone($var) $zval = array( // Main properties of the current value 'type' => null, 'zval_isref' => null, + 'zval_hash' => null, 'array_count' => null, 'object_class' => null, - 'object_hash' => null, + 'object_handle' => null, 'resource_type' => null, ); + if (!self::$hashMask) { + self::initHashMask(); + } + $hashMask = self::$hashMask; + $hashOffset = self::$hashOffset; for ($i = 0; $i < $len; ++$i) { $indexed = true; // Whether the currently iterated array is numerically indexed or not @@ -58,28 +68,25 @@ protected function doClone($var) } if ($useExt) { $zval = symfony_zval_info($k, $step); - if ($zval['zval_isref']) { - $queue[$i][$k] =& $stub; // Break hard references to make $queue completely - unset($stub); // independent from the original structure - if (isset($hardRefs[$h = $zval['zval_hash']])) { - $hardRefs[$h]->ref = ++$refs; - $queue[$i][$k] = $hardRefs[$h]; - continue; - } - } } else { $step[$k] = $cookie; if ($zval['zval_isref'] = $queue[$i][$k] === $cookie) { - $queue[$i][$k] =& $stub; // Break hard references to make $queue completely - unset($stub); // independent from the original structure - if ($v instanceof Stub && isset($hardRefs[spl_object_hash($v)])) { - $v->ref = ++$refs; - $step[$k] = $queue[$i][$k] = $v; - continue; - } + $zval['zval_hash'] = $v instanceof Stub ? spl_object_hash($v) : null; } $zval['type'] = gettype($v); } + if ($zval['zval_isref']) { + $queue[$i][$k] =& $stub; // Break hard references to make $queue completely + unset($stub); // independent from the original structure + if (isset($hardRefs[$zval['zval_hash']])) { + $queue[$i][$k] = $useExt ? ($v = $hardRefs[$zval['zval_hash']]) : ($step[$k] = $v); + if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { + ++$v->value->refCount; + } + ++$v->refCount; + continue; + } + } // Create $stub when the original value $v can not be used directly // If $v is a nested structure, put that structure in array $a switch ($zval['type']) { @@ -112,23 +119,31 @@ protected function doClone($var) $stub = $arrayRefs[$len] = new Stub(); $stub->type = Stub::TYPE_ARRAY; $stub->class = Stub::ARRAY_ASSOC; - $stub->value = $v ? $zval['array_count'] ?: count($v) : 0; + $stub->value = $zval['array_count'] ?: count($v); $a = $v; } break; case 'object': - if (empty($softRefs[$h = $zval['object_hash'] ?: spl_object_hash($v)])) { + if (empty($objRefs[$h = $zval['object_handle'] ?: ($hashMask ^ hexdec(substr(spl_object_hash($v), $hashOffset, PHP_INT_SIZE)))])) { $stub = new Stub(); $stub->type = Stub::TYPE_OBJECT; $stub->class = $zval['object_class'] ?: get_class($v); $stub->value = $v; + $stub->handle = $h; $a = $this->castObject($stub, 0 < $i); if ($v !== $stub->value) { if (Stub::TYPE_OBJECT !== $stub->type) { break; } - $h = spl_object_hash($stub->value); + if ($useExt) { + $zval['type'] = $stub->value; + $zval = symfony_zval_info('type', $zval); + $h = $zval['object_handle']; + } else { + $h = $hashMask ^ hexdec(substr(spl_object_hash($stub->value), $hashOffset, PHP_INT_SIZE)); + } + $stub->handle = $h; } $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos) { @@ -136,40 +151,35 @@ protected function doClone($var) $a = null; } } - if (empty($softRefs[$h])) { - $softRefs[$h] = $stub; + if (empty($objRefs[$h])) { + $objRefs[$h] = $stub; } else { - $stub = $softRefs[$h]; - $stub->ref = ++$refs; + $stub = $objRefs[$h]; + ++$stub->refCount; $a = null; } break; case 'resource': case 'unknown type': - if (empty($softRefs[$h = (int) $v])) { + if (empty($resRefs[$h = (int) $v])) { $stub = new Stub(); $stub->type = Stub::TYPE_RESOURCE; $stub->class = $zval['resource_type'] ?: get_resource_type($v); $stub->value = $v; + $stub->handle = $h; $a = $this->castResource($stub, 0 < $i); - if ($v !== $stub->value) { - if (Stub::TYPE_RESOURCE !== $stub->type) { - break; - } - $h = (int) $stub->value; - } $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos) { $stub->cut = count($a); $a = null; } } - if (empty($softRefs[$h])) { - $softRefs[$h] = $stub; + if (empty($resRefs[$h])) { + $resRefs[$h] = $stub; } else { - $stub = $softRefs[$h]; - $stub->ref = ++$refs; + $stub = $resRefs[$h]; + ++$stub->refCount; $a = null; } break; @@ -178,23 +188,16 @@ protected function doClone($var) if (isset($stub)) { if ($zval['zval_isref']) { if ($useExt) { - if (Stub::TYPE_ARRAY === $stub->type) { - $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $stub; - } else { - $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub(); - $v->value = $stub; - } + $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub(); + $v->value = $stub; } else { - if (Stub::TYPE_ARRAY === $stub->type) { - $step[$k] = $stub; - } else { - $step[$k] = new Stub(); - $step[$k]->value = $stub; - } + $step[$k] = new Stub(); + $step[$k]->value = $stub; $h = spl_object_hash($step[$k]); $queue[$i][$k] = $hardRefs[$h] =& $step[$k]; $values[$h] = $v; } + $queue[$i][$k]->handle = ++$refs; } else { $queue[$i][$k] = $stub; } @@ -233,6 +236,7 @@ protected function doClone($var) $hardRefs[$h] =& $step[$k]; $values[$h] = $v; } + $queue[$i][$k]->handle = ++$refs; } } @@ -250,4 +254,31 @@ protected function doClone($var) return $queue; } + + private static function initHashMask() + { + $obj = (object) array(); + self::$hashOffset = 16 - PHP_INT_SIZE; + self::$hashMask = -1; + + if (defined('HHVM_VERSION')) { + self::$hashOffset += 16; + } else { + // check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below + $obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush'); + foreach (debug_backtrace(PHP_VERSION_ID >= 50400 ? DEBUG_BACKTRACE_IGNORE_ARGS : false) as $frame) { + if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && in_array($frame['function'], $obFuncs)) { + $frame['line'] = 0; + break; + } + } + if (!empty($frame['line'])) { + ob_start(); + debug_zval_dump($obj); + self::$hashMask = substr(ob_get_clean(), 17); + } + } + + self::$hashMask ^= hexdec(substr(spl_object_hash($obj), self::$hashOffset, PHP_INT_SIZE)); + } } diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 483faad1b4ad4..b3344a237abbd 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -33,7 +33,8 @@ class CliDumper extends AbstractDumper 'str' => '1;38;5;37', 'cchr' => '7', 'note' => '38;5;178', - 'ref' => '38;5;245', + 'ref' => '38;5;240', + 'solo-ref' => '38;5;240', 'public' => '38;5;28', 'protected' => '38;5;166', 'private' => '38;5;160', @@ -125,7 +126,7 @@ public function dumpScalar(Cursor $cursor, $type, $val) $this->line .= $this->style($style, $val); - $this->endLine($cursor); + $this->dumpLine($cursor->depth); } /** @@ -137,7 +138,7 @@ public function dumpString(Cursor $cursor, $str, $bin, $cut) if ('' === $str) { $this->line .= '""'; - $this->endLine($cursor); + $this->dumpLine($cursor->depth); } else { $str = explode("\n", $str); $m = count($str) - 1; @@ -149,7 +150,7 @@ public function dumpString(Cursor $cursor, $str, $bin, $cut) if ($m) { $this->line .= '"""'; - $this->endLine($cursor); + $this->dumpLine($cursor->depth); } else { $this->line .= '"'; } @@ -182,7 +183,7 @@ public function dumpString(Cursor $cursor, $str, $bin, $cut) $lineCut = 0; } - $this->endLine($cursor, !$m); + $this->dumpLine($cursor->depth); } } } @@ -190,93 +191,63 @@ public function dumpString(Cursor $cursor, $str, $bin, $cut) /** * {@inheritdoc} */ - public function enterArray(Cursor $cursor, $count, $indexed, $hasChild) + public function enterHash(Cursor $cursor, $type, $class, $hasChild) { - $this->enterHash($cursor, $count ? $this->style('note', 'array:'.$count).' [' : '[', $hasChild); - } + $this->dumpKey($cursor); - /** - * {@inheritdoc} - */ - public function leaveArray(Cursor $cursor, $count, $indexed, $hasChild, $cut) - { - $this->leaveHash($cursor, ']', $hasChild, $cut); - } + if (Cursor::HASH_OBJECT === $type) { + $prefix = 'stdClass' !== $class ? $this->style('note', $class).' {' : '{'; + } elseif (Cursor::HASH_RESOURCE === $type) { + $prefix = $this->style('note', ':'.$class).' {'; + } else { + $prefix = $class ? $this->style('note', 'array:'.$class).' [' : '['; + } - /** - * {@inheritdoc} - */ - public function enterObject(Cursor $cursor, $class, $hasChild) - { - $this->enterHash($cursor, 'stdClass' !== $class ? $this->style('note', $class).' {' : '{', $hasChild); - } + if (Cursor::HASH_RESOURCE === $type) { + $prefix .= $this->style('ref', '@'.$cursor->softRefHandle); + } elseif ($cursor->softRefTo) { + $prefix .= $this->style('ref', '#'.(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo)); + } elseif (0 < $cursor->softRefHandle) { + $prefix .= $this->style('solo-ref', '#'.$cursor->softRefHandle); + } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) { + $prefix .= $this->style('ref', '&'.$cursor->hardRefTo); + } - /** - * {@inheritdoc} - */ - public function leaveObject(Cursor $cursor, $class, $hasChild, $cut) - { - $this->leaveHash($cursor, '}', $hasChild, $cut); - } + $this->line .= $prefix; - /** - * {@inheritdoc} - */ - public function enterResource(Cursor $cursor, $res, $hasChild) - { - $this->enterHash($cursor, $this->style('note', ':'.$res).' {', $hasChild); + if ($hasChild) { + $this->dumpLine($cursor->depth); + } } /** * {@inheritdoc} */ - public function leaveResource(Cursor $cursor, $res, $hasChild, $cut) - { - $this->leaveHash($cursor, '}', $hasChild, $cut); - } - - /** - * Generic dumper used while entering any hash-style structure. - * - * @param Cursor $cursor The Cursor position in the dump. - * @param string $prefix The string that starts the next dumped line. - * @param bool $hasChild When the dump of the hash has child item. - */ - protected function enterHash(Cursor $cursor, $prefix, $hasChild) + public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut) { - $this->dumpKey($cursor); - - $this->line .= $prefix; - if (false !== $cursor->softRefTo) { - $this->line .= $this->style('ref', '@'.$cursor->softRefTo); - } elseif (false !== $cursor->hardRefTo) { - $this->line .= $this->style('ref', '@'.$cursor->hardRefTo); - } elseif ($hasChild) { - $this->endLine($cursor); - } + $this->dumpEllipsis($cursor, $hasChild, $cut); + $this->line .= Cursor::HASH_OBJECT === $type || Cursor::HASH_RESOURCE === $type ? '}' : ']'; + $this->dumpLine($cursor->depth); } /** - * Generic dumper used while leaving any hash-style structure. + * Dumps an ellipsis for cut children. * * @param Cursor $cursor The Cursor position in the dump. - * @param string $suffix The string that ends the next dumped line. * @param bool $hasChild When the dump of the hash has child item. * @param int $cut The number of items the hash has been cut by. */ - protected function leaveHash(Cursor $cursor, $suffix, $hasChild, $cut) + protected function dumpEllipsis(Cursor $cursor, $hasChild, $cut) { - if ($cut && false === $cursor->softRefTo && false === $cursor->hardRefTo) { - $this->line .= '…'; + if ($cut) { + $this->line .= ' …'; if (0 < $cut) { $this->line .= $cut; } if ($hasChild) { - $this->dumpLine($cursor->depth+1); + $this->dumpLine($cursor->depth + 1); } } - $this->line .= $suffix; - $this->endLine($cursor, !$hasChild); } /** @@ -329,26 +300,12 @@ protected function dumpKey(Cursor $cursor) break; } - if (false !== $cursor->hardRefTo) { - $this->line .= $this->style('ref', '&'.$cursor->hardRefTo).' '; + if ($cursor->hardRefTo) { + $this->line .= ($cursor->hardRefCount ? $this->style('ref', '&'.$cursor->hardRefTo) : $this->style('solo-ref', '&')).' '; } } } - /** - * Finishes a line and dumps it. - * - * @param Cursor $cursor The current Cursor position. - * @param bool $showRef Show/hide the current ref index. - */ - protected function endLine(Cursor $cursor, $showRef = true) - { - if ($showRef && false !== $cursor->refIndex) { - $this->line .= ' '.$this->style('ref', '#'.$cursor->refIndex); - } - $this->dumpLine($cursor->depth); - } - /** * Decorates a value with some style. * diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index b6093fa14ed27..025545fa2e82b 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -24,8 +24,8 @@ class HtmlDumper extends CliDumper public static $defaultOutputStream = 'php://output'; protected $dumpHeader; - protected $dumpPrefix = '
    ';
    -    protected $dumpSuffix = '
    '; + protected $dumpPrefix = '
    ';
    +    protected $dumpSuffix = '
    '; protected $dumpId = 'sf-dump'; protected $colors = true; protected $headerIsDumped = false; @@ -36,7 +36,8 @@ class HtmlDumper extends CliDumper 'str' => 'font-weight:bold;color:#00D7FF', 'cchr' => 'font-style: italic', 'note' => 'color:#D7AF00', - 'ref' => 'color:#444444', + 'ref' => 'color:#585858', + 'solo-ref' => 'color:#585858', 'public' => 'color:#008700', 'protected' => 'color:#D75F00', 'private' => 'color:#D70000', @@ -106,99 +107,140 @@ protected function getDumpHeader() $line = <<<'EOHTML' '.$this->dumpHeader; @@ -327,10 +322,10 @@ protected function style($style, $value, $attr = array()) return sprintf('%s', ord($r[0]), "\x7F" === $r[0] ? '?' : chr(64 + ord($r[0]))); }, $v); - if ('solo-ref' === $style) { - return sprintf('%s', $v); - } if ('ref' === $style) { + if (empty($attr['count'])) { + return sprintf('%s', $v); + } $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1); return sprintf('%s', $this->dumpId, $r, 1 + $attr['count'], $v); @@ -400,8 +395,8 @@ function ($m) { ); if (-1 === $depth) { - parent::dumpLine(0); + AbstractDumper::dumpLine(0); } - parent::dumpLine($depth); + AbstractDumper::dumpLine($depth); } } diff --git a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php index 2916ba4506ea4..b3ec2adbe1257 100644 --- a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php @@ -64,7 +64,7 @@ public function testGet() "str" => "déjà" 7 => b"é@" "[]" => [] - "res" => :stream {@{$res1} + "res" => :stream {@{$res1} wrapper_type: "plainfile" stream_type: "STDIO" mode: "r" @@ -75,12 +75,12 @@ public function testGet() eof: false options: [] } - 8 => :Unknown {@{$res2}} + 8 => :Unknown {@{$res2}} "obj" => DumbFoo {#%d +foo: "foo" +"bar": "bar" } - "closure" => Closure {#%d + "closure" => Closure {#%d reflection: """ Closure [ <user> {$closureLabel} Symfony\Component\VarDumper\Tests\Fixture\{closure} ] { @@ {$var['file']} {$var['line']} - {$var['line']} From 8ee36dce3f4c443d5c000d8136c37660658ef1f4 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 3 Nov 2014 21:10:45 +0100 Subject: [PATCH 309/373] updated CHANGELOG for 2.6.0-BETA1 --- CHANGELOG-2.6.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 CHANGELOG-2.6.md diff --git a/CHANGELOG-2.6.md b/CHANGELOG-2.6.md new file mode 100644 index 0000000000000..f6145e035efb4 --- /dev/null +++ b/CHANGELOG-2.6.md @@ -0,0 +1,13 @@ +CHANGELOG for 2.6.x +=================== + +This changelog references the relevant changes (bug and security fixes) done +in 2.6 minor versions. + +To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash +To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.6.0...v2.6.1 + +* 2.6.0-BETA1 (2014-11-03) + + * first beta release + From 7bd1d4c0e68e9e2fd9e19c8e5671f71e82a4dc73 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 3 Nov 2014 21:15:26 +0100 Subject: [PATCH 310/373] updated VERSION for 2.6.0-BETA1 --- src/Symfony/Component/HttpKernel/Kernel.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index d3eed40a69d14..1d6e0882dafdd 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.6.0-DEV'; + const VERSION = '2.6.0-BETA1'; const VERSION_ID = '20600'; - const MAJOR_VERSION = '2'; - const MINOR_VERSION = '6'; - const RELEASE_VERSION = '0'; - const EXTRA_VERSION = 'DEV'; + const MAJOR_VERSION = '2.6.0-BETA1'; + const MINOR_VERSION = '2.6.0-BETA1'; + const RELEASE_VERSION = '2.6.0-BETA1'; + const EXTRA_VERSION = 'BETA1'; /** * Constructor. From 9919918de4e6c0b495184393f5df4499b7081261 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 3 Nov 2014 22:46:45 +0100 Subject: [PATCH 311/373] fixed version in HttpKernel --- src/Symfony/Component/HttpKernel/Kernel.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 1d6e0882dafdd..d74731a53ed93 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -61,9 +61,9 @@ abstract class Kernel implements KernelInterface, TerminableInterface const VERSION = '2.6.0-BETA1'; const VERSION_ID = '20600'; - const MAJOR_VERSION = '2.6.0-BETA1'; - const MINOR_VERSION = '2.6.0-BETA1'; - const RELEASE_VERSION = '2.6.0-BETA1'; + const MAJOR_VERSION = '2'; + const MINOR_VERSION = '6'; + const RELEASE_VERSION = '0'; const EXTRA_VERSION = 'BETA1'; /** From 604fefcd38f4307a114f89d25e85fe7b6e98f4b0 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 3 Nov 2014 22:47:57 +0100 Subject: [PATCH 312/373] bumped Symfony version to 2.6.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index d74731a53ed93..d3eed40a69d14 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.6.0-BETA1'; + const VERSION = '2.6.0-DEV'; const VERSION_ID = '20600'; const MAJOR_VERSION = '2'; const MINOR_VERSION = '6'; const RELEASE_VERSION = '0'; - const EXTRA_VERSION = 'BETA1'; + const EXTRA_VERSION = 'DEV'; /** * Constructor. From ac1f2b76d69af786103b847544e3817496601061 Mon Sep 17 00:00:00 2001 From: Disquedur Date: Tue, 4 Nov 2014 15:29:39 +0100 Subject: [PATCH 313/373] Remove aligned '=>' and '=' --- .../AbstractDoctrineExtension.php | 2 +- .../Doctrine/Form/Type/DoctrineType.php | 2 +- .../DebugExtensionTest.php | 8 +- .../Descriptor/AbstractDescriptorTest.php | 4 +- .../DependencyInjection/ConfigurationTest.php | 2 +- .../Debug/Tests/ErrorHandlerTest.php | 28 +++--- .../Component/HttpFoundation/Request.php | 16 ++-- .../Handler/MongoDbSessionHandlerTest.php | 4 +- .../Component/Process/Pipes/WindowsPipes.php | 2 +- .../Component/Security/Core/Security.php | 4 +- .../DefaultAuthenticationFailureHandler.php | 6 +- .../DefaultAuthenticationSuccessHandler.php | 8 +- .../Tests/Encoder/XmlEncoderTest.php | 10 +-- .../Translation/Tests/TranslatorCacheTest.php | 4 +- .../Tests/Constraints/FileValidatorTest.php | 6 +- .../VarDumper/Cloner/AbstractCloner.php | 88 +++++++++---------- .../Component/VarDumper/Dumper/CliDumper.php | 40 ++++----- .../Component/VarDumper/Dumper/HtmlDumper.php | 24 ++--- 18 files changed, 128 insertions(+), 130 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index f432acc81074a..ce92fe70dec98 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -413,7 +413,7 @@ protected function fixManagersAutoMappings(array $managerConfigs, array $bundles } } $managerConfigs[$autoMappedManager]['mappings'][$bundle] = array( - 'mapping' => true, + 'mapping' => true, 'is_bundle' => true, ); } diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php index de46ebf72b499..766d0caad0630 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php @@ -180,7 +180,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) )); $resolver->setAllowedTypes(array( - 'em' => array('null', 'string', 'Doctrine\Common\Persistence\ObjectManager'), + 'em' => array('null', 'string', 'Doctrine\Common\Persistence\ObjectManager'), 'loader' => array('null', 'Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface'), )); } diff --git a/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php b/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php index 4d22f59ce9fa8..01c54efbf2995 100644 --- a/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php +++ b/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php @@ -38,10 +38,10 @@ private function createContainer() { $container = new ContainerBuilder(new ParameterBag(array( 'kernel.cache_dir' => __DIR__, - 'kernel.root_dir' => __DIR__.'/Fixtures', - 'kernel.charset' => 'UTF-8', - 'kernel.debug' => true, - 'kernel.bundles' => array('DebugBundle' => 'Symfony\\Bundle\\DebugBundle\\DebugBundle'), + 'kernel.root_dir' => __DIR__.'/Fixtures', + 'kernel.charset' => 'UTF-8', + 'kernel.debug' => true, + 'kernel.bundles' => array('DebugBundle' => 'Symfony\\Bundle\\DebugBundle\\DebugBundle'), ))); return $container; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php index 6e60899f5822e..481744aac0a91 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php @@ -175,8 +175,8 @@ private function getContainerBuilderDescriptionTestData(array $objects) private function getEventDispatcherDescriptionTestData(array $objects) { $variations = array( - 'events' => array(), - 'event1' => array('event' => 'event1'), + 'events' => array(), + 'event1' => array('event' => 'event1'), ); $data = array(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 600d99b07863a..2231a5f25525b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -123,7 +123,7 @@ protected static function getBundleDefaultConfig() 'translator' => array( 'enabled' => false, 'fallback' => 'en', - 'logging' => true, + 'logging' => true, ), 'validation' => array( 'enabled' => false, diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index 97eb3d7a4e9a9..c0fbb4bd4ae93 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -162,21 +162,21 @@ public function testDefaultLogger() $handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL)); $loggers = array( - E_DEPRECATED => array(null, LogLevel::INFO), - E_USER_DEPRECATED => array(null, LogLevel::INFO), - E_NOTICE => array($logger, LogLevel::NOTICE), - E_USER_NOTICE => array($logger, LogLevel::CRITICAL), - E_STRICT => array(null, LogLevel::NOTICE), - E_WARNING => array(null, LogLevel::WARNING), - E_USER_WARNING => array(null, LogLevel::WARNING), - E_COMPILE_WARNING => array(null, LogLevel::WARNING), - E_CORE_WARNING => array(null, LogLevel::WARNING), - E_USER_ERROR => array(null, LogLevel::ERROR), + E_DEPRECATED => array(null, LogLevel::INFO), + E_USER_DEPRECATED => array(null, LogLevel::INFO), + E_NOTICE => array($logger, LogLevel::NOTICE), + E_USER_NOTICE => array($logger, LogLevel::CRITICAL), + E_STRICT => array(null, LogLevel::NOTICE), + E_WARNING => array(null, LogLevel::WARNING), + E_USER_WARNING => array(null, LogLevel::WARNING), + E_COMPILE_WARNING => array(null, LogLevel::WARNING), + E_CORE_WARNING => array(null, LogLevel::WARNING), + E_USER_ERROR => array(null, LogLevel::ERROR), E_RECOVERABLE_ERROR => array(null, LogLevel::ERROR), - E_COMPILE_ERROR => array(null, LogLevel::EMERGENCY), - E_PARSE => array(null, LogLevel::EMERGENCY), - E_ERROR => array(null, LogLevel::EMERGENCY), - E_CORE_ERROR => array(null, LogLevel::EMERGENCY), + E_COMPILE_ERROR => array(null, LogLevel::EMERGENCY), + E_PARSE => array(null, LogLevel::EMERGENCY), + E_ERROR => array(null, LogLevel::EMERGENCY), + E_CORE_ERROR => array(null, LogLevel::EMERGENCY), ); $this->assertSame($loggers, $handler->setLoggers(array())); diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 16d1549caaf0d..1871dde35088a 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -35,15 +35,15 @@ class Request const HEADER_CLIENT_PROTO = 'client_proto'; const HEADER_CLIENT_PORT = 'client_port'; - const METHOD_HEAD = 'HEAD'; - const METHOD_GET = 'GET'; - const METHOD_POST = 'POST'; - const METHOD_PUT = 'PUT'; - const METHOD_PATCH = 'PATCH'; - const METHOD_DELETE = 'DELETE'; - const METHOD_PURGE = 'PURGE'; + const METHOD_HEAD = 'HEAD'; + const METHOD_GET = 'GET'; + const METHOD_POST = 'POST'; + const METHOD_PUT = 'PUT'; + const METHOD_PATCH = 'PATCH'; + const METHOD_DELETE = 'DELETE'; + const METHOD_PURGE = 'PURGE'; const METHOD_OPTIONS = 'OPTIONS'; - const METHOD_TRACE = 'TRACE'; + const METHOD_TRACE = 'TRACE'; const METHOD_CONNECT = 'CONNECT'; protected static $trustedProxies = array(); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php index ac8947025c2ad..63d6d1e92383d 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -103,7 +103,7 @@ public function testWrite() public function testWriteWhenUsingExpiresField() { $this->options = array( - 'id_field' => '_id', + 'id_field' => '_id', 'data_field' => 'data', 'time_field' => 'time', 'database' => 'sf2-test', @@ -202,7 +202,7 @@ public function testGc() public function testGcWhenUsingExpiresField() { $this->options = array( - 'id_field' => '_id', + 'id_field' => '_id', 'data_field' => 'data', 'time_field' => 'time', 'database' => 'sf2-test', diff --git a/src/Symfony/Component/Process/Pipes/WindowsPipes.php b/src/Symfony/Component/Process/Pipes/WindowsPipes.php index b8335dd80719d..ecdf50eee2a72 100644 --- a/src/Symfony/Component/Process/Pipes/WindowsPipes.php +++ b/src/Symfony/Component/Process/Pipes/WindowsPipes.php @@ -60,7 +60,7 @@ public function __construct($disableOutput, $input) } if (is_resource($input)) { - $this->input = $input; + $this->input = $input; } else { $this->inputBuffer = $input; } diff --git a/src/Symfony/Component/Security/Core/Security.php b/src/Symfony/Component/Security/Core/Security.php index d397fb40777a0..14d32f81482e5 100644 --- a/src/Symfony/Component/Security/Core/Security.php +++ b/src/Symfony/Component/Security/Core/Security.php @@ -18,7 +18,7 @@ */ final class Security { - const ACCESS_DENIED_ERROR = '_security.403_error'; + const ACCESS_DENIED_ERROR = '_security.403_error'; const AUTHENTICATION_ERROR = '_security.last_error'; - const LAST_USERNAME = '_security.last_username'; + const LAST_USERNAME = '_security.last_username'; } diff --git a/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationFailureHandler.php b/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationFailureHandler.php index 93150c873366f..8864daefe4cea 100644 --- a/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationFailureHandler.php +++ b/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationFailureHandler.php @@ -35,9 +35,9 @@ class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandle protected $logger; protected $options; protected $defaultOptions = array( - 'failure_path' => null, - 'failure_forward' => false, - 'login_path' => '/login', + 'failure_path' => null, + 'failure_forward' => false, + 'login_path' => '/login', 'failure_path_parameter' => '_failure_path', ); diff --git a/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationSuccessHandler.php b/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationSuccessHandler.php index 0ee11b4c5b3c2..5fa7071c64176 100644 --- a/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationSuccessHandler.php +++ b/src/Symfony/Component/Security/Http/Authentication/DefaultAuthenticationSuccessHandler.php @@ -29,10 +29,10 @@ class DefaultAuthenticationSuccessHandler implements AuthenticationSuccessHandle protected $providerKey; protected $defaultOptions = array( 'always_use_default_target_path' => false, - 'default_target_path' => '/', - 'login_path' => '/login', - 'target_path_parameter' => '_target_path', - 'use_referer' => false, + 'default_target_path' => '/', + 'login_path' => '/login', + 'target_path_parameter' => '_target_path', + 'use_referer' => false, ); /** diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php index 85d0dc5591529..cba47e6b1717f 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php @@ -446,11 +446,11 @@ protected function getNamespacedXmlSource() protected function getNamespacedArray() { return array( - '@xmlns' => 'http://www.w3.org/2005/Atom', - '@xmlns:app' => 'http://www.w3.org/2007/app', + '@xmlns' => 'http://www.w3.org/2005/Atom', + '@xmlns:app' => 'http://www.w3.org/2007/app', '@xmlns:media' => 'http://search.yahoo.com/mrss/', - '@xmlns:gd' => 'http://schemas.google.com/g/2005', - '@xmlns:yt' => 'http://gdata.youtube.com/schemas/2007', + '@xmlns:gd' => 'http://schemas.google.com/g/2005', + '@xmlns:yt' => 'http://gdata.youtube.com/schemas/2007', 'qux' => "1", 'app:foo' => "foo", 'yt:bar' => array("a", "b"), @@ -469,7 +469,7 @@ protected function getNamespacedArray() 'Barry' => array( '@size' => 'large', 'FooBar' => array( - 'Baz' => 'Ed', + 'Baz' => 'Ed', '@gd:id' => 1, ), ), diff --git a/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php b/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php index 0071dd711f404..088fe21ccd110 100644 --- a/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php +++ b/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php @@ -153,8 +153,8 @@ protected function getLoader() ->expects($this->at(1)) ->method('load') ->will($this->returnValue($this->getCatalogue('en', array( - 'foo' => 'foo (EN)', - 'bar' => 'bar (EN)', + 'foo' => 'foo (EN)', + 'bar' => 'bar (EN)', 'choice' => '{0} choice 0 (EN)|{1} choice 1 (EN)|]1,Inf] choice inf (EN)', )))) ; diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php index 7169eed030446..87f18cceefd7f 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php @@ -271,9 +271,9 @@ public function testBinaryFormat($bytesWritten, $limit, $binaryFormat, $sizeAsSt fclose($this->file); $constraint = new File(array( - 'maxSize' => $limit, - 'binaryFormat' => $binaryFormat, - 'maxSizeMessage' => 'myMessage', + 'maxSize' => $limit, + 'binaryFormat' => $binaryFormat, + 'maxSizeMessage' => 'myMessage', )); $this->validator->validate($this->getFile($this->path), $constraint); diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index d97a88544e42e..66fbf82f8244d 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -24,61 +24,59 @@ abstract class AbstractCloner implements ClonerInterface 'Symfony\Component\VarDumper\Caster\CutStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub', 'Symfony\Component\VarDumper\Caster\ConstStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub', - 'Closure' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure', - 'Reflector' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castReflector', + 'Closure' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure', + 'Reflector' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castReflector', 'Doctrine\Common\Persistence\ObjectManager' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals', - 'Doctrine\Common\Proxy\Proxy' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castCommonProxy', - 'Doctrine\ORM\Proxy\Proxy' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castOrmProxy', - 'Doctrine\ORM\PersistentCollection' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castPersistentCollection', - - 'DOMException' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castException', - 'DOMStringList' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength', - 'DOMNameList' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength', - 'DOMImplementation' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castImplementation', - 'DOMImplementationList' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength', - 'DOMNode' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNode', - 'DOMNameSpaceNode' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNameSpaceNode', - 'DOMDocument' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDocument', - 'DOMNodeList' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength', - 'DOMNamedNodeMap' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength', - 'DOMCharacterData' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castCharacterData', - 'DOMAttr' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castAttr', - 'DOMElement' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castElement', - 'DOMText' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castText', - 'DOMTypeinfo' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castTypeinfo', - 'DOMDomError' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDomError', - 'DOMLocator' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLocator', - 'DOMDocumentType' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDocumentType', - 'DOMNotation' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNotation', - 'DOMEntity' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castEntity', + 'Doctrine\Common\Proxy\Proxy' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castCommonProxy', + 'Doctrine\ORM\Proxy\Proxy' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castOrmProxy', + 'Doctrine\ORM\PersistentCollection' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castPersistentCollection', + + 'DOMException' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castException', + 'DOMStringList' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength', + 'DOMNameList' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength', + 'DOMImplementation' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castImplementation', + 'DOMImplementationList' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength', + 'DOMNode' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNode', + 'DOMNameSpaceNode' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNameSpaceNode', + 'DOMDocument' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDocument', + 'DOMNodeList' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength', + 'DOMNamedNodeMap' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength', + 'DOMCharacterData' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castCharacterData', + 'DOMAttr' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castAttr', + 'DOMElement' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castElement', + 'DOMText' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castText', + 'DOMTypeinfo' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castTypeinfo', + 'DOMDomError' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDomError', + 'DOMLocator' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLocator', + 'DOMDocumentType' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDocumentType', + 'DOMNotation' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNotation', + 'DOMEntity' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castEntity', 'DOMProcessingInstruction' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castProcessingInstruction', - 'DOMXPath' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castXPath', + 'DOMXPath' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castXPath', 'ErrorException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castErrorException', - 'Exception' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castException', - 'Symfony\Component\DependencyInjection\ContainerInterface' - => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals', - 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' - => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castThrowingCasterException', + 'Exception' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castException', + 'Symfony\Component\DependencyInjection\ContainerInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals', + 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castThrowingCasterException', - 'PDO' => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdo', - 'PDOStatement' => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdoStatement', + 'PDO' => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdo', + 'PDOStatement' => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdoStatement', - 'ArrayObject' => 'Symfony\Component\VarDumper\Caster\SplCaster::castArrayObject', + 'ArrayObject' => 'Symfony\Component\VarDumper\Caster\SplCaster::castArrayObject', 'SplDoublyLinkedList' => 'Symfony\Component\VarDumper\Caster\SplCaster::castDoublyLinkedList', - 'SplFixedArray' => 'Symfony\Component\VarDumper\Caster\SplCaster::castFixedArray', - 'SplHeap' => 'Symfony\Component\VarDumper\Caster\SplCaster::castHeap', - 'SplObjectStorage' => 'Symfony\Component\VarDumper\Caster\SplCaster::castObjectStorage', - 'SplPriorityQueue' => 'Symfony\Component\VarDumper\Caster\SplCaster::castHeap', + 'SplFixedArray' => 'Symfony\Component\VarDumper\Caster\SplCaster::castFixedArray', + 'SplHeap' => 'Symfony\Component\VarDumper\Caster\SplCaster::castHeap', + 'SplObjectStorage' => 'Symfony\Component\VarDumper\Caster\SplCaster::castObjectStorage', + 'SplPriorityQueue' => 'Symfony\Component\VarDumper\Caster\SplCaster::castHeap', - ':curl' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castCurl', - ':dba' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castDba', + ':curl' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castCurl', + ':dba' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castDba', ':dba persistent' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castDba', - ':gd' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castGd', - ':mysql link' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castMysqlLink', - ':process' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castProcess', - ':stream' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castStream', + ':gd' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castGd', + ':mysql link' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castMysqlLink', + ':process' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castProcess', + ':stream' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castStream', ':stream-context' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castStreamContext', ); diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 51896dc9190b8..a6df0c8cee03d 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -28,19 +28,19 @@ class CliDumper extends AbstractDumper protected $maxStringWidth = 0; protected $styles = array( // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics - 'default' => '38;5;208', - 'num' => '1;38;5;38', - 'const' => '1;38;5;208', - 'str' => '1;38;5;113', - 'cchr' => '7', - 'note' => '38;5;38', - 'ref' => '38;5;247', - 'public' => '', + 'default' => '38;5;208', + 'num' => '1;38;5;38', + 'const' => '1;38;5;208', + 'str' => '1;38;5;113', + 'cchr' => '7', + 'note' => '38;5;38', + 'ref' => '38;5;247', + 'public' => '', 'protected' => '', - 'private' => '', - 'meta' => '38;5;170', - 'key' => '38;5;113', - 'index' => '38;5;38', + 'private' => '', + 'meta' => '38;5;170', + 'key' => '38;5;113', + 'index' => '38;5;38', ); protected static $controlCharsRx = '/[\x00-\x1F\x7F]/'; @@ -56,14 +56,14 @@ public function __construct($output = null) // Use only the base 16 xterm colors when using ANSICON $this->setStyles(array( 'default' => '31', - 'num' => '1;34', - 'const' => '1;31', - 'str' => '1;32', - 'note' => '34', - 'ref' => '1;30', - 'meta' => '35', - 'key' => '32', - 'index' => '34', + 'num' => '1;34', + 'const' => '1;31', + 'str' => '1;32', + 'note' => '34', + 'ref' => '1;30', + 'meta' => '35', + 'key' => '32', + 'index' => '34', )); } } diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index af8c57c75e9e0..6aba8145ef5e5 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -31,19 +31,19 @@ class HtmlDumper extends CliDumper protected $headerIsDumped = false; protected $lastDepth = -1; protected $styles = array( - 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace', - 'num' => 'font-weight:bold; color:#1299DA', - 'const' => 'font-weight:bold', - 'str' => 'font-weight:bold; color:#56DB3A', - 'cchr' => 'font-style:italic', - 'note' => 'color:#1299DA', - 'ref' => 'color:#A0A0A0', - 'public' => 'color:#FFFFFF', + 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace', + 'num' => 'font-weight:bold; color:#1299DA', + 'const' => 'font-weight:bold', + 'str' => 'font-weight:bold; color:#56DB3A', + 'cchr' => 'font-style:italic', + 'note' => 'color:#1299DA', + 'ref' => 'color:#A0A0A0', + 'public' => 'color:#FFFFFF', 'protected' => 'color:#FFFFFF', - 'private' => 'color:#FFFFFF', - 'meta' => 'color:#B729D9', - 'key' => 'color:#56DB3A', - 'index' => 'color:#1299DA', + 'private' => 'color:#FFFFFF', + 'meta' => 'color:#B729D9', + 'key' => 'color:#56DB3A', + 'index' => 'color:#1299DA', ); /** From cd076e4cb7e16a74e3348d1353993f8b4d9cc295 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Nov 2014 10:51:20 +0100 Subject: [PATCH 314/373] [VarDumper] Use Unicode Control Pictures --- src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php | 1 - src/Symfony/Component/VarDumper/Dumper/CliDumper.php | 2 +- src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php | 5 +++-- src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index 66fbf82f8244d..240f925b6c067 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -84,7 +84,6 @@ abstract class AbstractCloner implements ClonerInterface protected $maxString = -1; private $casters = array(); - private $data = array(array(null)); private $prevErrorHandler; private $classInfo = array(); diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index a6df0c8cee03d..6a171649bf7f4 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -331,7 +331,7 @@ protected function dumpKey(Cursor $cursor) } if ($cursor->hardRefTo) { - $this->line .= $this->style('ref', '&'.$cursor->hardRefTo, array('count' => $cursor->hardRefCount)).' '; + $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), array('count' => $cursor->hardRefCount)).' '; } } } diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index 6aba8145ef5e5..30c3253129e1e 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -35,7 +35,7 @@ class HtmlDumper extends CliDumper 'num' => 'font-weight:bold; color:#1299DA', 'const' => 'font-weight:bold', 'str' => 'font-weight:bold; color:#56DB3A', - 'cchr' => 'font-style:italic', + 'cchr' => 'color:#FF8400', 'note' => 'color:#1299DA', 'ref' => 'color:#A0A0A0', 'public' => 'color:#FFFFFF', @@ -319,7 +319,8 @@ protected function style($style, $value, $attr = array()) $v = htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); $v = preg_replace_callback(self::$controlCharsRx, function ($r) { - return sprintf('%s', ord($r[0]), "\x7F" === $r[0] ? '?' : chr(64 + ord($r[0]))); + // Use Unicode Control Pictures - see http://www.unicode.org/charts/PDF/U2400.pdf + return sprintf('&#%d;', ord($r[0]), "\x7F" !== $r[0] ? 0x2400 + ord($r[0]) : 0x2421); }, $v); if ('ref' === $style) { diff --git a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php index b3ec2adbe1257..fc8a15f09fa61 100644 --- a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php @@ -62,7 +62,7 @@ public function testGet() 5 => -INF 6 => {$intMax} "str" => "déjà" - 7 => b"é@" + 7 => b"é" "[]" => [] "res" => :stream {@{$res1} wrapper_type: "plainfile" From b592e9017700248736979ee11a8e65c1742ec7aa Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Nov 2014 16:31:37 +0100 Subject: [PATCH 315/373] [VarDumper] inline dump() function loading --- UPGRADE-2.6.md | 15 +++++++++++++++ src/Symfony/Component/VarDumper/VarDumper.php | 3 +++ 2 files changed, 18 insertions(+) diff --git a/UPGRADE-2.6.md b/UPGRADE-2.6.md index 138ec822b4c71..75002bc5c58b5 100644 --- a/UPGRADE-2.6.md +++ b/UPGRADE-2.6.md @@ -349,3 +349,18 @@ OptionsResolver // throws InvalidOptionsException $resolver->resolve(array('port' => '25')); ``` + +VarDumper and DebugBundle +------------------------- + +The component and the bundle are new to Symfony 2.6. We encourage you +to enable the bundle in your `app/AppKernel.php` for the *dev* or *test* +environments. Just add this line before loading the `WebProfilerBundle`: + + ```php + $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); + ``` + +Then enjoy dumping variables by calling `dump($var)` anywhere in your PHP +and `{% dump var %}` or `{{ dump(var) }}` in Twig. Dumps are displayed +**in the web debug toolbar**. diff --git a/src/Symfony/Component/VarDumper/VarDumper.php b/src/Symfony/Component/VarDumper/VarDumper.php index 3a1b23be3ad90..c728439df21bd 100644 --- a/src/Symfony/Component/VarDumper/VarDumper.php +++ b/src/Symfony/Component/VarDumper/VarDumper.php @@ -15,6 +15,9 @@ use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\HtmlDumper; +// Load the global dump() function +require_once __DIR__.'/Resources/functions/dump.php'; + /** * @author Nicolas Grekas */ From 50021bdeaa1afbe79f614b2a15bb01534c204fb7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Nov 2014 16:55:28 +0100 Subject: [PATCH 316/373] [Debug] No gc_collect_cycles(), it's costly and can segfault --- src/Symfony/Component/Debug/ErrorHandler.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index f6c043287b17b..458cb317717d2 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -487,7 +487,6 @@ public function handleException(\Exception $exception, array $error = null) public static function handleFatalError(array $error = null) { self::$reservedMemory = ''; - gc_collect_cycles(); $handler = set_error_handler('var_dump', 0); $handler = is_array($handler) ? $handler[0] : null; restore_error_handler(); From d238397c9cc665050f8a58f6ee8562037f7478d9 Mon Sep 17 00:00:00 2001 From: Iltar van der Berg Date: Fri, 7 Nov 2014 11:59:28 +0100 Subject: [PATCH 317/373] [SecurityBundle] Added AuthorizationChecker and TokenStorage to class cache --- .../SecurityBundle/DependencyInjection/SecurityExtension.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index cc45d79c70d65..1f20fc7596414 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -103,7 +103,9 @@ public function load(array $configs, ContainerBuilder $container) 'Symfony\\Component\\Security\\Core\\SecurityContext', 'Symfony\\Component\\Security\\Core\\User\\UserProviderInterface', 'Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager', + 'Symfony\\Component\\Security\\Core\\Authentication\\Token\\Storage\\TokenStorage', 'Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager', + 'Symfony\\Component\\Security\\Core\\Authorization\\AuthorizationChecker', 'Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface', 'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallMap', 'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallContext', From 6258ae863b70ea2f3aef52302b90ba910029face Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 7 Nov 2014 21:20:29 +0100 Subject: [PATCH 318/373] [DebugBundle] remove README.md --- src/Symfony/Bundle/DebugBundle/README.md | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 src/Symfony/Bundle/DebugBundle/README.md diff --git a/src/Symfony/Bundle/DebugBundle/README.md b/src/Symfony/Bundle/DebugBundle/README.md deleted file mode 100644 index 36935ba33ed5c..0000000000000 --- a/src/Symfony/Bundle/DebugBundle/README.md +++ /dev/null @@ -1,22 +0,0 @@ -dump() function -================ - -This bundle provides a better `dump()` function, that you can use instead of -`var_dump()`, *better* meaning: - -- per object and resource types specialized view: e.g. filter out Doctrine noise - while dumping a single proxy entity, or get more insight on opened files with - `stream_get_meta_data()`. Add your own dedicated `Dumper\Caster` and get the - view *you* need. -- configurable output format: HTML, command line with colors or [a dedicated high - accuracy JSON format](Resource/doc/json-spec.md). -- ability to dump internal references, either soft ones (objects or resources) - or hard ones (`=&` on arrays or objects properties). Repeated occurrences of - the same object/array/resource won't appear again and again anymore. Moreover, - you'll be able to inspect the reference structure of your data. -- ability to operate in the context of an output buffering handler. -- full exposure of the internal mechanisms used for walking through an arbitrary - PHP data structure. - -Calling `dump($myVvar)` works in all PHP code and `{% dump myVar %}` or -`{{ dump(myVar) }}` in Twig templates. From 450e34193f3a008912bbab8443019808f01d9aa8 Mon Sep 17 00:00:00 2001 From: Pera Jovic Date: Tue, 11 Nov 2014 22:26:20 +0100 Subject: [PATCH 319/373] Update UPGRADE-2.6.md --- UPGRADE-2.6.md | 136 ++++++++++++++++++++++++------------------------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/UPGRADE-2.6.md b/UPGRADE-2.6.md index 75002bc5c58b5..e01a1dc3ca888 100644 --- a/UPGRADE-2.6.md +++ b/UPGRADE-2.6.md @@ -81,20 +81,20 @@ Validator Security -------- - * The `SecurityContextInterface` is marked as deprecated in favor of the - `Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface` and + * The `SecurityContextInterface` is marked as deprecated in favor of the + `Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface` and `Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface`. ``` isGranted => AuthorizationCheckerInterface getToken => TokenStorageInterface setToken => TokenStorageInterface ``` - The Implementations have moved too, The `SecurityContext` is marked as - deprecated and has been split to use the `AuthorizationCheckerInterface` - and `TokenStorage`. This change is 100% Backwards Compatible as the SecurityContext + The Implementations have moved too, The `SecurityContext` is marked as + deprecated and has been split to use the `AuthorizationCheckerInterface` + and `TokenStorage`. This change is 100% Backwards Compatible as the SecurityContext delegates the methods. - * The service `security.context` is deprecated along with the above change. Recommended + * The service `security.context` is deprecated along with the above change. Recommended to use instead: ``` @security.authorization_checker => isGranted() @@ -133,160 +133,160 @@ OptionsResolver --------------- * The "array" type hint was removed from the `OptionsResolverInterface` methods - `setRequired()`, `setAllowedValues()`, `addAllowedValues()`, + `setRequired()`, `setAllowedValues()`, `addAllowedValues()`, `setAllowedTypes()` and `addAllowedTypes()`. You must remove the type hint from your implementations. - + * The interface `OptionsResolverInterface` was deprecated, since `OptionsResolver` instances are not supposed to be shared between classes. You should type hint against `OptionsResolver` instead. - + Before: - + ```php protected function configureOptions(OptionsResolverInterface $resolver) { // ... } ``` - + After: - + ```php protected function configureOptions(OptionsResolver $resolver) { // ... } ``` - + * `OptionsResolver::isRequired()` now returns `true` if a required option has a default value set. The new method `isMissing()` exhibits the old functionality of `isRequired()`. - + Before: - + ```php $resolver->setRequired(array('port')); - + $resolver->isRequired('port'); // => true - + $resolver->setDefaults(array('port' => 25)); - + $resolver->isRequired('port'); // => false ``` - + After: - + ```php $resolver->setRequired(array('port')); - + $resolver->isRequired('port'); // => true $resolver->isMissing('port'); // => true - + $resolver->setDefaults(array('port' => 25)); - + $resolver->isRequired('port'); // => true $resolver->isMissing('port'); // => false ``` - + * `OptionsResolver::replaceDefaults()` was deprecated. Use `clear()` and `setDefaults()` instead. - + Before: - + ```php $resolver->replaceDefaults(array( 'port' => 25, )); ``` - + After: - + ```php $resolver->clear(); $resolver->setDefaults(array( 'port' => 25, )); ``` - + * `OptionsResolver::setOptional()` was deprecated. Use `setDefined()` instead. - + Before: - + ```php $resolver->setOptional(array('port')); ``` - + After: - + ```php $resolver->setDefined('port'); ``` - + * `OptionsResolver::isKnown()` was deprecated. Use `isDefined()` instead. - + Before: - + ```php if ($resolver->isKnown('port')) { // ... } ``` - + After: - + ```php if ($resolver->isDefined('port')) { // ... } ``` - + * The methods `setAllowedValues()`, `addAllowedValues()`, `setAllowedTypes()` and `addAllowedTypes()` were changed to modify one option at a time instead of batch processing options. The old API exists for backwards compatibility, but will be removed in Symfony 3.0. - + Before: - + ```php $resolver->setAllowedValues(array( 'method' => array('POST', 'GET'), )); ``` - + After: - + ```php $resolver->setAllowedValues('method', array('POST', 'GET')); ``` - + * The class `Options` was merged into `OptionsResolver`. If you instantiated this class manually, you should instantiate `OptionsResolver` now. `Options` is now a marker interface implemented by `OptionsResolver`. - + Before: - + ```php $options = new Options(); ``` - + After: - + ```php $resolver = new OptionsResolver(); ``` - - * Normalizers for defined but unset options are not executed anymore. If you + + * Normalizers for defined but unset options are not executed anymore. If you want to have them executed, you should define a default value. - + Before: - + ```php $resolver->setOptional(array('port')); $resolver->setNormalizers(array( @@ -294,28 +294,28 @@ OptionsResolver // return normalized value } )); - + $options = $resolver->resolve($options); ``` - + After: - + ```php $resolver->setDefault('port', null); $resolver->setNormalizer('port', function ($options, $value) { // return normalized value }); - + $options = $resolver->resolve($options); ``` - + * When undefined options are passed, `resolve()` now throws an `UndefinedOptionsException` instead of an `InvalidOptionsException`. `InvalidOptionsException` is only thrown when option values fail their validation constraints. - + Before: - + ```php $resolver->setDefaults(array( 'transport' => 'smtp', @@ -324,16 +324,16 @@ OptionsResolver $resolver->setAllowedTypes(array( 'port' => 'integer', )); - + // throws InvalidOptionsException $resolver->resolve(array('foo' => 'bar')); - + // throws InvalidOptionsException $resolver->resolve(array('port' => '25')); ``` - + After: - + ```php $resolver->setDefaults(array( 'transport' => 'smtp', @@ -342,10 +342,10 @@ OptionsResolver $resolver->setAllowedTypes(array( 'port' => 'integer', )); - + // throws UndefinedOptionsException $resolver->resolve(array('foo' => 'bar')); - + // throws InvalidOptionsException $resolver->resolve(array('port' => '25')); ``` @@ -357,9 +357,9 @@ The component and the bundle are new to Symfony 2.6. We encourage you to enable the bundle in your `app/AppKernel.php` for the *dev* or *test* environments. Just add this line before loading the `WebProfilerBundle`: - ```php - $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); - ``` +```php +$bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); +``` Then enjoy dumping variables by calling `dump($var)` anywhere in your PHP and `{% dump var %}` or `{{ dump(var) }}` in Twig. Dumps are displayed From a6b1cfc5b8aeb3aace45345a38f993746d4487ae Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Sat, 15 Nov 2014 20:18:41 +0100 Subject: [PATCH 320/373] [FrameworkBundle] Fix server start in case the PHP binary is not found --- .../Command/ServerStartCommand.php | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php index fd5680388eafc..369567a9aa5eb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\PhpExecutableFinder; use Symfony\Component\Process\Process; /** @@ -70,6 +71,11 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + if (defined('HHVM_VERSION')) { + $output->writeln('This command is not supported on HHVM.'); + + return 1; + } if (!extension_loaded('pcntl')) { $output->writeln('This command needs the pcntl extension to run.'); $output->writeln('You can either install it or use the server:run command instead to run the built-in web server.'); @@ -105,13 +111,10 @@ protected function execute(InputInterface $input, OutputInterface $output) return 1; } - $process = $this->createServerProcess( - $address, - $input->getOption('docroot'), - $input->getOption('router'), - $env, - null - ); + if (null === $process = $this->createServerProcess($output, $address, $input->getOption('docroot'), $input->getOption('router'), $env, null)) { + return 1; + } + $process->disableOutput(); $process->start(); $lockFile = $this->getLockFile($address); @@ -137,23 +140,32 @@ protected function execute(InputInterface $input, OutputInterface $output) /** * Creates a process to start PHP's built-in web server. * - * @param string $address IP address and port to listen to - * @param string $documentRoot The application's document root - * @param string $router The router filename - * @param string $env The application environment - * @param int $timeout Process timeout + * @param OutputInterface $output A OutputInterface instance + * @param string $address IP address and port to listen to + * @param string $documentRoot The application's document root + * @param string $router The router filename + * @param string $env The application environment + * @param int $timeout Process timeout * * @return Process The process */ - private function createServerProcess($address, $documentRoot, $router, $env, $timeout = null) + private function createServerProcess(OutputInterface $output, $address, $documentRoot, $router, $env, $timeout = null) { $router = $router ?: $this ->getContainer() ->get('kernel') ->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env)) ; + + $finder = new PhpExecutableFinder(); + if (false === $binary = $finder->find()) { + $output->writeln('Unable to find PHP binary to start server'); + + return; + } + $script = implode(' ', array_map(array('Symfony\Component\Process\ProcessUtils', 'escapeArgument'), array( - PHP_BINARY, + $binary, '-S', $address, $router, From 0fbb75c8e47a412c1b2642c1ddff358499c17c1f Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Sun, 16 Nov 2014 15:23:53 +0000 Subject: [PATCH 321/373] Fixed minor typo --- src/Symfony/Component/VarDumper/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/VarDumper/README.md b/src/Symfony/Component/VarDumper/README.md index 3eb3ef24052b8..71bff335a70c6 100644 --- a/src/Symfony/Component/VarDumper/README.md +++ b/src/Symfony/Component/VarDumper/README.md @@ -5,7 +5,7 @@ This component provides a mechanism that allows exploring then dumping any PHP variable. It handles scalars, objects and resources properly, taking hard and soft -references into account. More than being immune to inifinite recursion +references into account. More than being immune to infinite recursion problems, it allows dumping where references link to each other. It explores recursive structures using a breadth-first algorithm. From d8d6fcff7d09559c35ef4a6c25898d1bab85f2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Seng?= Date: Sat, 15 Nov 2014 14:11:27 +0100 Subject: [PATCH 322/373] [DomCrawler] Added support for link tags in the Link class --- src/Symfony/Component/DomCrawler/Link.php | 6 +++--- src/Symfony/Component/DomCrawler/Tests/LinkTest.php | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/DomCrawler/Link.php b/src/Symfony/Component/DomCrawler/Link.php index bb2302d52263f..e4b070c616a07 100644 --- a/src/Symfony/Component/DomCrawler/Link.php +++ b/src/Symfony/Component/DomCrawler/Link.php @@ -12,7 +12,7 @@ namespace Symfony\Component\DomCrawler; /** - * Link represents an HTML link (an HTML a or area tag). + * Link represents an HTML link (an HTML a, area or link tag). * * @author Fabien Potencier * @@ -179,8 +179,8 @@ protected function canonicalizePath($path) */ protected function setNode(\DOMElement $node) { - if ('a' !== $node->nodeName && 'area' !== $node->nodeName) { - throw new \LogicException(sprintf('Unable to click on a "%s" tag.', $node->nodeName)); + if ('a' !== $node->nodeName && 'area' !== $node->nodeName && 'link' !== $node->nodeName) { + throw new \LogicException(sprintf('Unable to navigate from a "%s" tag.', $node->nodeName)); } $this->node = $node; diff --git a/src/Symfony/Component/DomCrawler/Tests/LinkTest.php b/src/Symfony/Component/DomCrawler/Tests/LinkTest.php index 941e4b2f6dd2e..98a45a3a0cef0 100644 --- a/src/Symfony/Component/DomCrawler/Tests/LinkTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/LinkTest.php @@ -86,6 +86,18 @@ public function testGetUriOnArea($url, $currentUri, $expected) $this->assertEquals($expected, $link->getUri()); } + /** + * @dataProvider getGetUriTests + */ + public function testGetUriOnLink($url, $currentUri, $expected) + { + $dom = new \DOMDocument(); + $dom->loadHTML(sprintf('', $url)); + $link = new Link($dom->getElementsByTagName('link')->item(0), $currentUri); + + $this->assertEquals($expected, $link->getUri()); + } + public function getGetUriTests() { return array( From 849fb2998485639d253e2438ecf78f62cc833a53 Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Sun, 2 Nov 2014 19:53:26 +0000 Subject: [PATCH 323/373] Drop support for model_timezone and view_timezone options in TimeType and DateType. --- src/Symfony/Component/Form/CHANGELOG.md | 1 + .../Form/Extension/Core/Type/DateType.php | 12 +-- .../Form/Extension/Core/Type/TimeType.php | 12 ++- .../Extension/Core/Type/DateTypeTest.php | 62 +++------------ .../Extension/Core/Type/TimeTypeTest.php | 77 ++++--------------- 5 files changed, 39 insertions(+), 125 deletions(-) diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 148df573140ff..e0fc25aa820e8 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * added "html5" option to Date, Time and DateTimeFormType to be able to enable/disable HTML5 input date when widget option is "single_text" * added "label_format" option with possible placeholders "%name%" and "%id%" + * [BC BREAK] drop support for model_timezone and view_timezone options in TimeType and DateType 2.5.0 ------ diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index 5bc3fb157e3e3..8a0028ce17f11 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -57,8 +57,8 @@ public function buildForm(FormBuilderInterface $builder, array $options) if ('single_text' === $options['widget']) { $builder->addViewTransformer(new DateTimeToLocalizedStringTransformer( - $options['model_timezone'], - $options['view_timezone'], + 'UTC', + 'UTC', $dateFormat, $timeFormat, $calendar, @@ -105,7 +105,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->add('month', $options['widget'], $monthOptions) ->add('day', $options['widget'], $dayOptions) ->addViewTransformer(new DateTimeToArrayTransformer( - $options['model_timezone'], $options['view_timezone'], array('year', 'month', 'day') + 'UTC', 'UTC', array('year', 'month', 'day') )) ->setAttribute('formatter', $formatter) ; @@ -113,15 +113,15 @@ public function buildForm(FormBuilderInterface $builder, array $options) if ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( - new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'Y-m-d') + new DateTimeToStringTransformer('UTC', 'UTC', 'Y-m-d') )); } elseif ('timestamp' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( - new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone']) + new DateTimeToTimestampTransformer('UTC', 'UTC') )); } elseif ('array' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( - new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], array('year', 'month', 'day')) + new DateTimeToArrayTransformer('UTC', 'UTC', array('year', 'month', 'day')) )); } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index adff60c2159d8..236a546dc4339 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -48,7 +48,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) } if ('single_text' === $options['widget']) { - $builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format)); + $builder->addViewTransformer(new DateTimeToStringTransformer('UTC', 'UTC', $format)); } else { $hourOptions = $minuteOptions = $secondOptions = array( 'error_bubbling' => true, @@ -109,20 +109,20 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder->add('second', $options['widget'], $secondOptions); } - $builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'])); + $builder->addViewTransformer(new DateTimeToArrayTransformer('UTC', 'UTC', $parts, 'text' === $options['widget'])); } if ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( - new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'H:i:s') + new DateTimeToStringTransformer('UTC', 'UTC', 'H:i:s') )); } elseif ('timestamp' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( - new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone']) + new DateTimeToTimestampTransformer('UTC', 'UTC') )); } elseif ('array' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( - new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], $parts) + new DateTimeToArrayTransformer('UTC', 'UTC', $parts) )); } } @@ -197,8 +197,6 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) 'input' => 'datetime', 'with_minutes' => true, 'with_seconds' => false, - 'model_timezone' => null, - 'view_timezone' => null, 'empty_value' => $emptyValue, // deprecated 'placeholder' => $placeholder, 'html5' => true, diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php index 7e31afbac65b4..5ba37df1d0a50 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -17,6 +17,8 @@ class DateTypeTest extends TypeTestCase { + private $defaultTimezone; + protected function setUp() { parent::setUp(); @@ -25,6 +27,13 @@ protected function setUp() IntlTestHelper::requireFullIntl($this); \Locale::setDefault('de_AT'); + + $this->defaultTimezone = date_default_timezone_get(); + } + + protected function tearDown() + { + date_default_timezone_set($this->defaultTimezone); } /** @@ -50,8 +59,6 @@ public function testInvalidInputOption() public function testSubmitFromSingleTextDateTimeWithDefaultFormat() { $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'single_text', 'input' => 'datetime', )); @@ -66,8 +73,6 @@ public function testSubmitFromSingleTextDateTime() { $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'single_text', 'input' => 'datetime', )); @@ -82,8 +87,6 @@ public function testSubmitFromSingleTextString() { $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'single_text', 'input' => 'string', )); @@ -98,8 +101,6 @@ public function testSubmitFromSingleTextTimestamp() { $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'single_text', 'input' => 'timestamp', )); @@ -116,8 +117,6 @@ public function testSubmitFromSingleTextRaw() { $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'single_text', 'input' => 'array', )); @@ -137,8 +136,6 @@ public function testSubmitFromSingleTextRaw() public function testSubmitFromText() { $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'text', )); @@ -159,8 +156,6 @@ public function testSubmitFromText() public function testSubmitFromChoice() { $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'choice', )); @@ -181,8 +176,6 @@ public function testSubmitFromChoice() public function testSubmitFromChoiceEmpty() { $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'choice', 'required' => false, )); @@ -202,8 +195,6 @@ public function testSubmitFromChoiceEmpty() public function testSubmitFromInputDateTimeDifferentPattern() { $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'format' => 'MM*yyyy*dd', 'widget' => 'single_text', 'input' => 'datetime', @@ -218,8 +209,6 @@ public function testSubmitFromInputDateTimeDifferentPattern() public function testSubmitFromInputStringDifferentPattern() { $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'format' => 'MM*yyyy*dd', 'widget' => 'single_text', 'input' => 'string', @@ -234,8 +223,6 @@ public function testSubmitFromInputStringDifferentPattern() public function testSubmitFromInputTimestampDifferentPattern() { $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'format' => 'MM*yyyy*dd', 'widget' => 'single_text', 'input' => 'timestamp', @@ -252,8 +239,6 @@ public function testSubmitFromInputTimestampDifferentPattern() public function testSubmitFromInputRawDifferentPattern() { $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'format' => 'MM*yyyy*dd', 'widget' => 'single_text', 'input' => 'array', @@ -370,27 +355,12 @@ public function testThrowExceptionIfDaysIsInvalid() )); } - public function testSetDataWithDifferentTimezones() + public function testSetDataWithDifferentTimezoneDateTime() { - $form = $this->factory->create('date', null, array( - 'format' => \IntlDateFormatter::MEDIUM, - 'model_timezone' => 'America/New_York', - 'view_timezone' => 'Pacific/Tahiti', - 'input' => 'string', - 'widget' => 'single_text', - )); + date_default_timezone_set('Pacific/Tahiti'); - $form->setData('2010-06-02'); - - $this->assertEquals('01.06.2010', $form->getViewData()); - } - - public function testSetDataWithDifferentTimezonesDateTime() - { $form = $this->factory->create('date', null, array( 'format' => \IntlDateFormatter::MEDIUM, - 'model_timezone' => 'America/New_York', - 'view_timezone' => 'Pacific/Tahiti', 'input' => 'datetime', 'widget' => 'single_text', )); @@ -400,7 +370,7 @@ public function testSetDataWithDifferentTimezonesDateTime() $form->setData($dateTime); $this->assertDateTimeEquals($dateTime, $form->getData()); - $this->assertEquals('01.06.2010', $form->getViewData()); + $this->assertEquals('02.06.2010', $form->getViewData()); } public function testYearsOption() @@ -495,8 +465,6 @@ public function testIsPartiallyFilledReturnsFalseIfSingleText() $this->markTestIncomplete('Needs to be reimplemented using validators'); $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'single_text', )); @@ -510,8 +478,6 @@ public function testIsPartiallyFilledReturnsFalseIfChoiceAndCompletelyEmpty() $this->markTestIncomplete('Needs to be reimplemented using validators'); $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'choice', )); @@ -529,8 +495,6 @@ public function testIsPartiallyFilledReturnsFalseIfChoiceAndCompletelyFilled() $this->markTestIncomplete('Needs to be reimplemented using validators'); $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'choice', )); @@ -548,8 +512,6 @@ public function testIsPartiallyFilledReturnsTrueIfChoiceAndDayEmpty() $this->markTestIncomplete('Needs to be reimplemented using validators'); $form = $this->factory->create('date', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'widget' => 'choice', )); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php index 7fd145fa3e684..0154c05e63cd9 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -17,18 +17,25 @@ class TimeTypeTest extends TypeTestCase { + private $defaultTimezone; + protected function setUp() { IntlTestHelper::requireIntl($this); parent::setUp(); + + $this->defaultTimezone = date_default_timezone_get(); + } + + protected function tearDown() + { + date_default_timezone_set($this->defaultTimezone); } public function testSubmitDateTime() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'datetime', )); @@ -48,8 +55,6 @@ public function testSubmitDateTime() public function testSubmitString() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'string', )); @@ -67,8 +72,6 @@ public function testSubmitString() public function testSubmitTimestamp() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'timestamp', )); @@ -88,8 +91,6 @@ public function testSubmitTimestamp() public function testSubmitArray() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'array', )); @@ -107,8 +108,6 @@ public function testSubmitArray() public function testSubmitDatetimeSingleText() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'datetime', 'widget' => 'single_text', )); @@ -122,8 +121,6 @@ public function testSubmitDatetimeSingleText() public function testSubmitDatetimeSingleTextWithoutMinutes() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'datetime', 'widget' => 'single_text', 'with_minutes' => false, @@ -138,8 +135,6 @@ public function testSubmitDatetimeSingleTextWithoutMinutes() public function testSubmitArraySingleText() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'array', 'widget' => 'single_text', )); @@ -158,8 +153,6 @@ public function testSubmitArraySingleText() public function testSubmitArraySingleTextWithoutMinutes() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'array', 'widget' => 'single_text', 'with_minutes' => false, @@ -178,8 +171,6 @@ public function testSubmitArraySingleTextWithoutMinutes() public function testSubmitArraySingleTextWithSeconds() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'array', 'widget' => 'single_text', 'with_seconds' => true, @@ -200,8 +191,6 @@ public function testSubmitArraySingleTextWithSeconds() public function testSubmitStringSingleText() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'string', 'widget' => 'single_text', )); @@ -215,8 +204,6 @@ public function testSubmitStringSingleText() public function testSubmitStringSingleTextWithoutMinutes() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'string', 'widget' => 'single_text', 'with_minutes' => false, @@ -231,8 +218,6 @@ public function testSubmitStringSingleTextWithoutMinutes() public function testSetDataWithoutMinutes() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'datetime', 'with_minutes' => false, )); @@ -245,8 +230,6 @@ public function testSetDataWithoutMinutes() public function testSetDataWithSeconds() { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'UTC', - 'view_timezone' => 'UTC', 'input' => 'datetime', 'with_seconds' => true, )); @@ -256,53 +239,23 @@ public function testSetDataWithSeconds() $this->assertEquals(array('hour' => 3, 'minute' => 4, 'second' => 5), $form->getViewData()); } - public function testSetDataDifferentTimezones() + public function testSetDataWithTimezoneDateTime() { - $form = $this->factory->create('time', null, array( - 'model_timezone' => 'America/New_York', - 'view_timezone' => 'Asia/Hong_Kong', - 'input' => 'string', - 'with_seconds' => true, - )); - - $dateTime = new \DateTime('2013-01-01 12:04:05'); - $dateTime->setTimezone(new \DateTimeZone('America/New_York')); + date_default_timezone_set('Asia/Hong_Kong'); - $form->setData($dateTime->format('H:i:s')); - - $outputTime = clone $dateTime; - $outputTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); - - $displayedData = array( - 'hour' => (int) $outputTime->format('H'), - 'minute' => (int) $outputTime->format('i'), - 'second' => (int) $outputTime->format('s'), - ); - - $this->assertEquals($displayedData, $form->getViewData()); - } - - public function testSetDataDifferentTimezonesDateTime() - { $form = $this->factory->create('time', null, array( - 'model_timezone' => 'America/New_York', - 'view_timezone' => 'Asia/Hong_Kong', 'input' => 'datetime', 'with_seconds' => true, )); - $dateTime = new \DateTime('12:04:05'); - $dateTime->setTimezone(new \DateTimeZone('America/New_York')); + $dateTime = new \DateTime('12:04:05', new \DateTimeZone('America/New_York')); $form->setData($dateTime); - $outputTime = clone $dateTime; - $outputTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); - $displayedData = array( - 'hour' => (int) $outputTime->format('H'), - 'minute' => (int) $outputTime->format('i'), - 'second' => (int) $outputTime->format('s'), + 'hour' => 12, + 'minute' => 4, + 'second' => 5, ); $this->assertDateTimeEquals($dateTime, $form->getData()); From 7faee60ea7e5fde02f82141d54d29277d214971f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 16 Nov 2014 19:10:38 +0100 Subject: [PATCH 324/373] *_timezone changes also affect the BirthdayType --- src/Symfony/Component/Form/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index e0fc25aa820e8..cd5f619b25270 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -7,7 +7,7 @@ CHANGELOG * added "html5" option to Date, Time and DateTimeFormType to be able to enable/disable HTML5 input date when widget option is "single_text" * added "label_format" option with possible placeholders "%name%" and "%id%" - * [BC BREAK] drop support for model_timezone and view_timezone options in TimeType and DateType + * [BC BREAK] drop support for model_timezone and view_timezone options in TimeType, DateType and BirthdayType 2.5.0 ------ From 8cf3d69ce518fdbd5f2fc3290985ea361f6fde5a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 10 Nov 2014 16:30:03 +0100 Subject: [PATCH 325/373] [TwigBundle/DebugBundle] move dump extension & cleanups --- .../Bridge/Twig/Extension/DumpExtension.php | 17 ++++++++--------- .../DebugBundle/Resources/config/services.xml | 5 +++++ .../TwigBundle/Resources/config/debug.xml | 3 +-- .../DataCollector/DumpDataCollector.php | 19 ++++++++----------- .../VarDumper/Dumper/AbstractDumper.php | 6 +++--- .../Component/VarDumper/Dumper/HtmlDumper.php | 6 ++++-- 6 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Extension/DumpExtension.php b/src/Symfony/Bridge/Twig/Extension/DumpExtension.php index 2fc02f3302e67..30318ecac6d02 100644 --- a/src/Symfony/Bridge/Twig/Extension/DumpExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/DumpExtension.php @@ -22,7 +22,9 @@ */ class DumpExtension extends \Twig_Extension { - public function __construct(ClonerInterface $cloner = null) + private $cloner; + + public function __construct(ClonerInterface $cloner) { $this->cloner = $cloner; } @@ -46,7 +48,7 @@ public function getName() public function dump(\Twig_Environment $env, $context) { - if (!$env->isDebug() || !$this->cloner) { + if (!$env->isDebug()) { return; } @@ -64,17 +66,14 @@ public function dump(\Twig_Environment $env, $context) unset($vars[0], $vars[1]); } - $html = ''; - $dumper = new HtmlDumper(function ($line, $depth) use (&$html) { - if (-1 !== $depth) { - $html .= str_repeat(' ', $depth).$line."\n"; - } - }); + $dump = fopen('php://memory', 'r+b'); + $dumper = new HtmlDumper($dump); foreach ($vars as $value) { $dumper->dump($this->cloner->cloneVar($value)); } + rewind($dump); - return $html; + return stream_get_contents($dump); } } diff --git a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml index 0593cc7efa14d..14da81c3dd078 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml @@ -5,6 +5,11 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> + + + + + diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml index 70bdafa77cdb0..c44ae6312155e 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml @@ -16,9 +16,8 @@ - + - diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index ba19540585cb5..be3cfdc400c0e 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -155,24 +155,21 @@ public function getDumpsCount() public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1) { + $data = fopen('php://memory' 'r+b'); + if ('html' === $format) { - $dumper = new HtmlDumper(); + $dumper = new HtmlDumper($data); } else { throw new \InvalidArgumentException(sprintf('Invalid dump format: %s', $format)); } $dumps = array(); foreach ($this->data as $dump) { - $data = ''; - $dumper->dump( - $dump['data']->getLimitedClone($maxDepthLimit, $maxItemsPerDepth), - function ($line, $depth) use (&$data) { - if (-1 !== $depth) { - $data .= str_repeat(' ', $depth).$line."\n"; - } - } - ); - $dump['data'] = $data; + $dumper->dump($dump['data']->getLimitedClone($maxDepthLimit, $maxItemsPerDepth)); + rewind($data); + $dump['data'] = stream_get_contents($data); + ftruncate($data, 0); + rewind($data); $dumps[] = $dump; } diff --git a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php index 3b6fbac631fb2..bc793499acf45 100644 --- a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php @@ -115,7 +115,7 @@ public function dump(Data $data, $output = null) */ protected function dumpLine($depth) { - call_user_func($this->lineDumper, $this->line, $depth); + call_user_func($this->lineDumper, $this->line, $depth, $this->indentPad); $this->line = ''; } @@ -125,10 +125,10 @@ protected function dumpLine($depth) * @param string $line The line to write. * @param int $depth The recursive depth in the dumped structure. */ - protected function echoLine($line, $depth) + protected function echoLine($line, $depth, $indentPad) { if (-1 !== $depth) { - fwrite($this->outputStream, str_repeat($this->indentPad, $depth).$line."\n"); + fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n"); } } } diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index 30c3253129e1e..b587e78f91c88 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -51,9 +51,11 @@ class HtmlDumper extends CliDumper */ public function setOutput($output) { - $this->headerIsDumped = false; + if ($output !== $prev = parent::setOutput($output)) { + $this->headerIsDumped = false; + } - return parent::setOutput($output); + return $prev; } /** From 5526dd79c84205b8d42ad4d5dcdc31a519ec85f8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 18 Nov 2014 12:05:34 +0100 Subject: [PATCH 326/373] [HttpKernel] fix parse error in DumpDataCollector --- .../Component/HttpKernel/DataCollector/DumpDataCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index be3cfdc400c0e..6099097f366bd 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -155,7 +155,7 @@ public function getDumpsCount() public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1) { - $data = fopen('php://memory' 'r+b'); + $data = fopen('php://memory', 'r+b'); if ('html' === $format) { $dumper = new HtmlDumper($data); From 8871368ca3b41658103975039918ad42bda88559 Mon Sep 17 00:00:00 2001 From: Lorenz Schori Date: Sat, 4 Oct 2014 16:00:10 +0200 Subject: [PATCH 327/373] Extract an AbstractEventDispatcherTest from EventDispatcherTest and also use it in ContainerAwareEventDispatcherTest --- .../Tests/AbstractEventDispatcherTest.php | 369 ++++++++++++++++++ .../ContainerAwareEventDispatcherTest.php | 8 +- .../Tests/EventDispatcherTest.php | 352 +---------------- 3 files changed, 379 insertions(+), 350 deletions(-) create mode 100644 src/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php diff --git a/src/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php new file mode 100644 index 0000000000000..b9e4194960b6a --- /dev/null +++ b/src/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php @@ -0,0 +1,369 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /* Some pseudo events */ + const preFoo = 'pre.foo'; + const postFoo = 'post.foo'; + const preBar = 'pre.bar'; + const postBar = 'post.bar'; + + /** + * @var EventDispatcher + */ + private $dispatcher; + + private $listener; + + protected function setUp() + { + $this->dispatcher = $this->createEventDispatcher(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->listener = null; + } + + abstract protected function createEventDispatcher(); + + public function testInitialState() + { + $this->assertEquals(array(), $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddListener() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); + $this->assertCount(2, $this->dispatcher->getListeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); + $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); + $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->addListener('post.foo', $listener4, -10); + $this->dispatcher->addListener('post.foo', $listener5); + $this->dispatcher->addListener('post.foo', $listener6, 10); + + $expected = array( + 'pre.foo' => array($listener3, $listener2, $listener1), + 'post.foo' => array($listener6, $listener5, $listener4), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testDispatch() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->dispatcher->dispatch(self::preFoo); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertEquals('pre.foo', $event->getName()); + $this->assertSame($event, $return); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + $invoked++; + }; + $this->dispatcher->addListener('pre.foo', $listener); + $this->dispatcher->addListener('post.foo', $listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); + $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); + $this->dispatcher->dispatch(self::postFoo); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->dispatcher->addListener('pre.bar', $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('pre.bar', $this->listener); + $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('notExists', $this->listener); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testAddSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertEquals('preFoo2', $listeners[0][1]); + } + + public function testRemoveSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testRemoveSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testRemoveSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testEventReceivesTheDispatcherInstance() + { + $dispatcher = null; + $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { + $dispatcher = $event->getDispatcher(); + }); + $this->dispatcher->dispatch('test'); + $this->assertSame($this->dispatcher, $dispatcher); + } + + public function testEventReceivesTheDispatcherInstanceAsArgument() + { + $listener = new TestWithDispatcher(); + $this->dispatcher->addListener('test', array($listener, 'foo')); + $this->assertNull($listener->name); + $this->assertNull($listener->dispatcher); + $this->dispatcher->dispatch('test'); + $this->assertEquals('test', $listener->name); + $this->assertSame($this->dispatcher, $listener->dispatcher); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = $this->createEventDispatcher(); + $dispatcher->addListener('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function () {}); + $this->assertTrue($dispatcher->hasListeners('bug.62976')); + } + + public function testHasListenersWhenAddedCallbackListenerIsRemoved() + { + $listener = function () {}; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertFalse($this->dispatcher->hasListeners()); + } + + public function testGetListenersWhenAddedCallbackListenerIsRemoved() + { + $listener = function () {}; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertSame(array(), $this->dispatcher->getListeners()); + } + + public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled() + { + $this->assertFalse($this->dispatcher->hasListeners('foo')); + $this->assertFalse($this->dispatcher->hasListeners()); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(Event $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(Event $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } +} + +class TestWithDispatcher +{ + public $name; + public $dispatcher; + + public function foo(Event $e, $name, $dispatcher) + { + $this->name = $name; + $this->dispatcher = $dispatcher; + } +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); + } +} + +class TestEventSubscriberWithPriorities implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'pre.foo' => array('preFoo', 10), + 'post.foo' => array('postFoo'), + ); + } +} + +class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => array( + array('preFoo1'), + array('preFoo2', 10), + )); + } +} diff --git a/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php index eabde3da8e12b..bb3ff3a8d85a8 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -17,8 +17,14 @@ use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -class ContainerAwareEventDispatcherTest extends \PHPUnit_Framework_TestCase +class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest { + protected function createEventDispatcher() + { + $container = new Container(); + return new ContainerAwareEventDispatcher($container); + } + public function testAddAListenerService() { $event = new Event(); diff --git a/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php index da246f1c6c8ef..5faa5c8be876a 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php @@ -11,358 +11,12 @@ namespace Symfony\Component\EventDispatcher\Tests; -use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; -class EventDispatcherTest extends \PHPUnit_Framework_TestCase +class EventDispatcherTest extends AbstractEventDispatcherTest { - /* Some pseudo events */ - const preFoo = 'pre.foo'; - const postFoo = 'post.foo'; - const preBar = 'pre.bar'; - const postBar = 'post.bar'; - - /** - * @var EventDispatcher - */ - private $dispatcher; - - private $listener; - - protected function setUp() - { - $this->dispatcher = new EventDispatcher(); - $this->listener = new TestEventListener(); - } - - protected function tearDown() - { - $this->dispatcher = null; - $this->listener = null; - } - - public function testInitialState() - { - $this->assertEquals(array(), $this->dispatcher->getListeners()); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); - } - - public function testAddListener() - { - $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); - $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); - $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); - $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); - $this->assertCount(2, $this->dispatcher->getListeners()); - } - - public function testGetListenersSortsByPriority() - { - $listener1 = new TestEventListener(); - $listener2 = new TestEventListener(); - $listener3 = new TestEventListener(); - $listener1->name = '1'; - $listener2->name = '2'; - $listener3->name = '3'; - - $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); - $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); - $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); - - $expected = array( - array($listener2, 'preFoo'), - array($listener3, 'preFoo'), - array($listener1, 'preFoo'), - ); - - $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); - } - - public function testGetAllListenersSortsByPriority() - { - $listener1 = new TestEventListener(); - $listener2 = new TestEventListener(); - $listener3 = new TestEventListener(); - $listener4 = new TestEventListener(); - $listener5 = new TestEventListener(); - $listener6 = new TestEventListener(); - - $this->dispatcher->addListener('pre.foo', $listener1, -10); - $this->dispatcher->addListener('pre.foo', $listener2); - $this->dispatcher->addListener('pre.foo', $listener3, 10); - $this->dispatcher->addListener('post.foo', $listener4, -10); - $this->dispatcher->addListener('post.foo', $listener5); - $this->dispatcher->addListener('post.foo', $listener6, 10); - - $expected = array( - 'pre.foo' => array($listener3, $listener2, $listener1), - 'post.foo' => array($listener6, $listener5, $listener4), - ); - - $this->assertSame($expected, $this->dispatcher->getListeners()); - } - - public function testDispatch() - { - $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); - $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); - $this->dispatcher->dispatch(self::preFoo); - $this->assertTrue($this->listener->preFooInvoked); - $this->assertFalse($this->listener->postFooInvoked); - $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); - $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); - $event = new Event(); - $return = $this->dispatcher->dispatch(self::preFoo, $event); - $this->assertEquals('pre.foo', $event->getName()); - $this->assertSame($event, $return); - } - - public function testDispatchForClosure() - { - $invoked = 0; - $listener = function () use (&$invoked) { - $invoked++; - }; - $this->dispatcher->addListener('pre.foo', $listener); - $this->dispatcher->addListener('post.foo', $listener); - $this->dispatcher->dispatch(self::preFoo); - $this->assertEquals(1, $invoked); - } - - public function testStopEventPropagation() - { - $otherListener = new TestEventListener(); - - // postFoo() stops the propagation, so only one listener should - // be executed - // Manually set priority to enforce $this->listener to be called first - $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); - $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); - $this->dispatcher->dispatch(self::postFoo); - $this->assertTrue($this->listener->postFooInvoked); - $this->assertFalse($otherListener->postFooInvoked); - } - - public function testDispatchByPriority() - { - $invoked = array(); - $listener1 = function () use (&$invoked) { - $invoked[] = '1'; - }; - $listener2 = function () use (&$invoked) { - $invoked[] = '2'; - }; - $listener3 = function () use (&$invoked) { - $invoked[] = '3'; - }; - $this->dispatcher->addListener('pre.foo', $listener1, -10); - $this->dispatcher->addListener('pre.foo', $listener2); - $this->dispatcher->addListener('pre.foo', $listener3, 10); - $this->dispatcher->dispatch(self::preFoo); - $this->assertEquals(array('3', '2', '1'), $invoked); - } - - public function testRemoveListener() - { - $this->dispatcher->addListener('pre.bar', $this->listener); - $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); - $this->dispatcher->removeListener('pre.bar', $this->listener); - $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); - $this->dispatcher->removeListener('notExists', $this->listener); - } - - public function testAddSubscriber() - { - $eventSubscriber = new TestEventSubscriber(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); - } - - public function testAddSubscriberWithPriorities() - { - $eventSubscriber = new TestEventSubscriber(); - $this->dispatcher->addSubscriber($eventSubscriber); - - $eventSubscriber = new TestEventSubscriberWithPriorities(); - $this->dispatcher->addSubscriber($eventSubscriber); - - $listeners = $this->dispatcher->getListeners('pre.foo'); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertCount(2, $listeners); - $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); - } - - public function testAddSubscriberWithMultipleListeners() - { - $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); - $this->dispatcher->addSubscriber($eventSubscriber); - - $listeners = $this->dispatcher->getListeners('pre.foo'); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertCount(2, $listeners); - $this->assertEquals('preFoo2', $listeners[0][1]); - } - - public function testRemoveSubscriber() - { - $eventSubscriber = new TestEventSubscriber(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); - $this->dispatcher->removeSubscriber($eventSubscriber); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); - } - - public function testRemoveSubscriberWithPriorities() - { - $eventSubscriber = new TestEventSubscriberWithPriorities(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->dispatcher->removeSubscriber($eventSubscriber); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - } - - public function testRemoveSubscriberWithMultipleListeners() - { - $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); - $this->dispatcher->removeSubscriber($eventSubscriber); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - } - - public function testEventReceivesTheDispatcherInstance() - { - $dispatcher = null; - $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { - $dispatcher = $event->getDispatcher(); - }); - $this->dispatcher->dispatch('test'); - $this->assertSame($this->dispatcher, $dispatcher); - } - - public function testEventReceivesTheDispatcherInstanceAsArgument() - { - $listener = new TestWithDispatcher(); - $this->dispatcher->addListener('test', array($listener, 'foo')); - $this->assertNull($listener->name); - $this->assertNull($listener->dispatcher); - $this->dispatcher->dispatch('test'); - $this->assertEquals('test', $listener->name); - $this->assertSame($this->dispatcher, $listener->dispatcher); - } - - /** - * @see https://bugs.php.net/bug.php?id=62976 - * - * This bug affects: - * - The PHP 5.3 branch for versions < 5.3.18 - * - The PHP 5.4 branch for versions < 5.4.8 - * - The PHP 5.5 branch is not affected - */ - public function testWorkaroundForPhpBug62976() - { - $dispatcher = new EventDispatcher(); - $dispatcher->addListener('bug.62976', new CallableClass()); - $dispatcher->removeListener('bug.62976', function () {}); - $this->assertTrue($dispatcher->hasListeners('bug.62976')); - } - - public function testHasListenersWhenAddedCallbackListenerIsRemoved() - { - $listener = function () {}; - $this->dispatcher->addListener('foo', $listener); - $this->dispatcher->removeListener('foo', $listener); - $this->assertFalse($this->dispatcher->hasListeners()); - } - - public function testGetListenersWhenAddedCallbackListenerIsRemoved() - { - $listener = function () {}; - $this->dispatcher->addListener('foo', $listener); - $this->dispatcher->removeListener('foo', $listener); - $this->assertSame(array(), $this->dispatcher->getListeners()); - } - - public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled() - { - $this->assertFalse($this->dispatcher->hasListeners('foo')); - $this->assertFalse($this->dispatcher->hasListeners()); - } -} - -class CallableClass -{ - public function __invoke() - { - } -} - -class TestEventListener -{ - public $preFooInvoked = false; - public $postFooInvoked = false; - - /* Listener methods */ - - public function preFoo(Event $e) - { - $this->preFooInvoked = true; - } - - public function postFoo(Event $e) - { - $this->postFooInvoked = true; - - $e->stopPropagation(); - } -} - -class TestWithDispatcher -{ - public $name; - public $dispatcher; - - public function foo(Event $e, $name, $dispatcher) - { - $this->name = $name; - $this->dispatcher = $dispatcher; - } -} - -class TestEventSubscriber implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); - } -} - -class TestEventSubscriberWithPriorities implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array( - 'pre.foo' => array('preFoo', 10), - 'post.foo' => array('postFoo'), - ); - } -} - -class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface -{ - public static function getSubscribedEvents() + protected function createEventDispatcher() { - return array('pre.foo' => array( - array('preFoo1'), - array('preFoo2', 10), - )); + return new EventDispatcher(); } } From 2065e00b58c89f1823ce273b8098f3fe679ad2c3 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Mon, 6 Oct 2014 10:52:45 +0200 Subject: [PATCH 328/373] =?UTF-8?q?[TwigBundle]=C2=A0Fix=20error=20page=20?= =?UTF-8?q?preview=20for=20custom=20twig.exception=5Fcontroller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ExceptionController.php | 77 +++++++------------ .../Controller/PreviewErrorController.php | 56 ++++++++++++++ .../Resources/config/routing/errors.xml | 2 +- .../TwigBundle/Resources/config/twig.xml | 6 ++ .../Controller/ExceptionControllerTest.php | 25 +++--- .../Controller/PreviewErrorControllerTest.php | 56 ++++++++++++++ 6 files changed, 158 insertions(+), 64 deletions(-) create mode 100644 src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php create mode 100644 src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php diff --git a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php index 1bd758de5b2e0..395f69785f168 100644 --- a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php @@ -19,7 +19,8 @@ use Symfony\Component\Templating\TemplateReferenceInterface; /** - * ExceptionController. + * ExceptionController renders error or exception pages for a given + * FlattenException. * * @author Fabien Potencier * @author Matthias Pigulla @@ -27,6 +28,10 @@ class ExceptionController { protected $twig; + + /** + * @var bool Show error (false) or exception (true) pages by default. + */ protected $debug; public function __construct(\Twig_Environment $twig, $debug) @@ -38,6 +43,10 @@ public function __construct(\Twig_Environment $twig, $debug) /** * Converts an Exception to a Response. * + * A "showException" request parameter can be used to force display of an error page (when set to false) or + * the exception page (when true). If it is not present, the "debug" value passed into the constructor will + * be used. + * * @param Request $request The request * @param FlattenException $exception A FlattenException instance * @param DebugLoggerInterface $logger A DebugLoggerInterface instance @@ -49,25 +58,20 @@ public function __construct(\Twig_Environment $twig, $debug) public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null) { $currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1)); + $showException = $request->get('showException', $this->debug); // As opposed to an additional parameter, this maintains BC - return $this->createResponse($request, $exception, $this->debug, $logger, $currentContent); - } - - /** - * Displays the error page for arbitrary status codes and formats. - * - * @param Request $request The request - * @param int $code The HTTP status code to show the error page for. - * - * @return Response - * - * @throws \InvalidArgumentException When the error template does not exist - */ - public function testErrorPageAction(Request $request, $code) - { - $exception = FlattenException::create(new \Exception("Something has intentionally gone wrong."), $code); + $code = $exception->getStatusCode(); - return $this->createResponse($request, $exception, false); + return new Response($this->twig->render( + $this->findTemplate($request, $request->getRequestFormat(), $code, $showException), + array( + 'status_code' => $code, + 'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '', + 'exception' => $exception, + 'logger' => $logger, + 'currentContent' => $currentContent, + ) + )); } /** @@ -90,19 +94,19 @@ protected function getAndCleanOutputBuffering($startObLevel) * @param Request $request * @param string $format * @param int $code An HTTP response status code - * @param bool $debug + * @param bool $showException * * @return TemplateReferenceInterface */ - protected function findTemplate(Request $request, $format, $code, $debug) + protected function findTemplate(Request $request, $format, $code, $showException) { - $name = $debug ? 'exception' : 'error'; - if ($debug && 'html' == $format) { + $name = $showException ? 'exception' : 'error'; + if ($showException && 'html' == $format) { $name = 'exception_full'; } - // when not in debug, try to find a template for the specific HTTP status code and format - if (!$debug) { + // For error pages, try to find a template for the specific HTTP status code and format + if (!$showException) { $template = new TemplateReference('TwigBundle', 'Exception', $name.$code, $format, 'twig'); if ($this->templateExists($template)) { return $template; @@ -138,29 +142,4 @@ protected function templateExists($template) return false; } - - /** - * @param Request $request - * @param FlattenException $exception - * @param bool $debug - * @param DebugLoggerInterface $logger - * @param string $currentContent - * - * @return Response - */ - protected function createResponse(Request $request, FlattenException $exception, $debug, DebugLoggerInterface $logger = null, $currentContent = '') - { - $code = $exception->getStatusCode(); - - return new Response($this->twig->render( - (string) $this->findTemplate($request, $request->getRequestFormat(), $code, $debug), - array( - 'status_code' => $code, - 'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '', - 'exception' => $exception, - 'logger' => $logger, - 'currentContent' => $currentContent, - ) - )); - } } diff --git a/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php b/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php new file mode 100644 index 0000000000000..b4eb932e581bb --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Controller; + +use Symfony\Component\HttpKernel\Exception\FlattenException; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * PreviewErrorController can be used to test error pages. + * + * It will create a test exception and forward it to another controller. + * + * @author Matthias Pigulla + */ +class PreviewErrorController +{ + protected $kernel; + protected $controller; + + public function __construct(HttpKernelInterface $kernel, $controller) + { + $this->kernel = $kernel; + $this->controller = $controller; + } + + public function previewErrorPageAction(Request $request, $code) + { + $exception = FlattenException::create(new \Exception("Something has intentionally gone wrong."), $code); + + /* + * This Request mimics the parameters set by + * \Symfony\Component\HttpKernel\EventListener\ExceptionListener::duplicateRequest, with + * the additional "showException" flag. + */ + + $subRequest = $request->duplicate(null, null, array( + '_controller' => $this->controller, + 'exception' => $exception, + 'logger' => null, + 'format' => $request->getRequestFormat(), + 'showException' => false, + )); + + return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST); + } +} diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml index 493af74c019d9..bf87f8be7ab6e 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> - twig.controller.exception:testErrorPageAction + twig.controller.preview_error:previewErrorPageAction html \d+ diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml index c10dc942b113b..3c81983dd3bea 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -25,6 +25,7 @@ Symfony\Bridge\Twig\Translation\TwigExtractor Symfony\Component\HttpKernel\EventListener\ExceptionListener Symfony\Bundle\TwigBundle\Controller\ExceptionController + Symfony\Bundle\TwigBundle\Controller\PreviewErrorController @@ -133,5 +134,10 @@ %kernel.debug% + + + + %twig.exception_listener.controller% + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php index 39e17cb221ee9..222ac32c9e40e 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php @@ -13,6 +13,7 @@ use Symfony\Bundle\TwigBundle\Tests\TestCase; use Symfony\Bundle\TwigBundle\Controller\ExceptionController; +use Symfony\Component\HttpKernel\Exception\FlattenException; use Symfony\Component\HttpFoundation\Request; class ExceptionControllerTest extends TestCase @@ -42,27 +43,22 @@ public function testOnlyClearOwnOutputBuffers() $controller->showAction($request, $flatten); } - public function testErrorPagesInDebugMode() + public function testShowActionCanBeForcedToShowErrorPage() { $twig = new \Twig_Environment( new \Twig_Loader_Array(array( - 'TwigBundle:Exception:error404.html.twig' => ' - {%- if exception is defined and status_text is defined and status_code is defined -%} - OK - {%- else -%} - "exception" variable is missing - {%- endif -%} - ', + 'TwigBundle:Exception:error404.html.twig' => 'ok', )) ); - $request = Request::create('whatever'); + $request = Request::create('whatever', 'GET', array('showException' => false)); + $exception = FlattenException::create(new \Exception(), 404); + $controller = new ExceptionController($twig, /* "showException" defaults to --> */ true); - $controller = new ExceptionController($twig, /* "debug" set to --> */ true); - $response = $controller->testErrorPageAction($request, 404); + $response = $controller->showAction($request, $exception, null); $this->assertEquals(200, $response->getStatusCode()); // successful request - $this->assertEquals('OK', $response->getContent()); // content of the error404.html template + $this->assertEquals('ok', $response->getContent()); // content of the error404.html template } public function testFallbackToHtmlIfNoTemplateForRequestedFormat() @@ -75,9 +71,10 @@ public function testFallbackToHtmlIfNoTemplateForRequestedFormat() $request = Request::create('whatever'); $request->setRequestFormat('txt'); - + $exception = FlattenException::create(new \Exception()); $controller = new ExceptionController($twig, false); - $response = $controller->testErrorPageAction($request, 42); + + $response = $controller->showAction($request, $exception); $this->assertEquals('html', $request->getRequestFormat()); } diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php new file mode 100644 index 0000000000000..2d97b0c7ad2eb --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests\Controller; + +use Symfony\Bundle\TwigBundle\Controller\PreviewErrorController; +use Symfony\Bundle\TwigBundle\Tests\TestCase; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class PreviewErrorControllerTest extends TestCase +{ + public function testForwardRequestToConfiguredController() + { + $self = $this; + + $request = Request::create('whatever'); + $response = new Response(""); + $code = 123; + $logicalControllerName = 'foo:bar:baz'; + + $kernel = $this->getMock('\Symfony\Component\HttpKernel\HttpKernelInterface'); + $kernel + ->expects($this->once()) + ->method('handle') + ->with( + $this->callback(function (Request $request) use ($self, $logicalControllerName, $code) { + + $self->assertEquals($logicalControllerName, $request->attributes->get('_controller')); + + $exception = $request->attributes->get('exception'); + $self->assertInstanceOf('Symfony\Component\HttpKernel\Exception\FlattenException', $exception); + $self->assertEquals($code, $exception->getStatusCode()); + + $self->assertFalse($request->attributes->get('showException')); + + return true; + }), + $this->equalTo(HttpKernelInterface::SUB_REQUEST) + ) + ->will($this->returnValue($response)); + + $controller = new PreviewErrorController($kernel, $logicalControllerName); + + $this->assertSame($response, $controller->previewErrorPageAction($request, $code)); + } +} From 7bb1abe6f30505d2696d90936632ef9da8e88a7e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 21 Nov 2014 09:24:15 +0100 Subject: [PATCH 329/373] fixed typo --- .../Bundle/TwigBundle/Controller/ExceptionController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php index 395f69785f168..38be248aad3dd 100644 --- a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php @@ -58,7 +58,7 @@ public function __construct(\Twig_Environment $twig, $debug) public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null) { $currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1)); - $showException = $request->get('showException', $this->debug); // As opposed to an additional parameter, this maintains BC + $showException = $request->attributes->get('showException', $this->debug); // As opposed to an additional parameter, this maintains BC $code = $exception->getStatusCode(); From 1de4920fc56623ed936ec0a9421255fb3fc29663 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Nov 2014 10:20:58 +0100 Subject: [PATCH 330/373] [2.6] Remove possible call_user_func() --- src/Symfony/Component/OptionsResolver/OptionsResolver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index d4f9c9e7226cd..a88bff09728cc 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -833,7 +833,7 @@ public function offsetGet($option) // BEGIN $this->calling[$option] = true; foreach ($this->lazy[$option] as $closure) { - $value = call_user_func($closure, $this, $value); + $value = $closure($this, $value); } unset($this->calling[$option]); // END @@ -929,7 +929,7 @@ public function offsetGet($option) // dependency // BEGIN $this->calling[$option] = true; - $value = call_user_func($normalizer, $this, $value); + $value = $normalizer($this, $value); unset($this->calling[$option]); // END } From 0d20753e0e4d2f8c49465bcb4a56aec7f173e091 Mon Sep 17 00:00:00 2001 From: Stepan Anchugov Date: Tue, 21 Oct 2014 00:20:07 +0600 Subject: [PATCH 331/373] [DX] [Form] [WebProfiler] Added abbrs with type classes This adds an element having a descriptive title that displays the form element's type class name. --- .../WebProfilerBundle/Resources/views/Collector/form.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig index 736f718ccf13a..cd30a30a6ba78 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -420,7 +420,7 @@ {% else %}
    {% endif %} - {{ name }} + {{ name }} {% if data.type is not empty %}[{{ data.type }}]{% endif %} {% if data.errors is defined and data.errors|length > 0 %}
    {{ data.errors|length }}
    {% endif %} From aec10e6a241397704168744837fbaa25d24ee13c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 21 Nov 2014 11:39:32 +0100 Subject: [PATCH 332/373] fixed typo --- .../Bundle/TwigBundle/Controller/ExceptionController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php index 38be248aad3dd..1f0dd4bfef0a5 100644 --- a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php @@ -122,7 +122,7 @@ protected function findTemplate(Request $request, $format, $code, $showException // default to a generic HTML exception $request->setRequestFormat('html'); - return new TemplateReference('TwigBundle', 'Exception', $debug ? 'exception_full' : $name, 'html', 'twig'); + return new TemplateReference('TwigBundle', 'Exception', $this->debug ? 'exception_full' : $name, 'html', 'twig'); } // to be removed when the minimum required version of Twig is >= 2.0 From 83d768d09ef85e680ba639c4404c6f3f072b1f58 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 21 Nov 2014 15:47:53 +0100 Subject: [PATCH 333/373] be smarter when guessing the document root --- .../Command/ServerStartCommand.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php index 369567a9aa5eb..c5ec95d4d4611 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php @@ -33,7 +33,7 @@ protected function configure() $this ->setDefinition(array( new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'), - new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', 'web/'), + new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', null), new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'), )) ->setName('server:start') @@ -83,6 +83,18 @@ protected function execute(InputInterface $input, OutputInterface $output) return 1; } + $documentRoot = $input->getOption('docroot'); + + if (null === $documentRoot) { + $documentRoot = $this->getContainer()->getParameter('kernel.root_dir').'/../web'; + } + + if (!is_dir($documentRoot)) { + $output->writeln(sprintf('The given document root directory "%s" does not exist', $documentRoot)); + + return 1; + } + $env = $this->getContainer()->getParameter('kernel.environment'); if ('prod' === $env) { @@ -111,7 +123,7 @@ protected function execute(InputInterface $input, OutputInterface $output) return 1; } - if (null === $process = $this->createServerProcess($output, $address, $input->getOption('docroot'), $input->getOption('router'), $env, null)) { + if (null === $process = $this->createServerProcess($output, $address, $documentRoot, $input->getOption('router'), $env, null)) { return 1; } From b3db574a9f197c1e72e2ac2012cc93e3ee45beef Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 21 Nov 2014 16:18:07 +0100 Subject: [PATCH 334/373] properly set request attributes in controller test --- .../Bundle/TwigBundle/Controller/ExceptionController.php | 2 +- .../TwigBundle/Tests/Controller/ExceptionControllerTest.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php index 1f0dd4bfef0a5..01deadc83b428 100644 --- a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php @@ -122,7 +122,7 @@ protected function findTemplate(Request $request, $format, $code, $showException // default to a generic HTML exception $request->setRequestFormat('html'); - return new TemplateReference('TwigBundle', 'Exception', $this->debug ? 'exception_full' : $name, 'html', 'twig'); + return new TemplateReference('TwigBundle', 'Exception', $showException ? 'exception_full' : $name, 'html', 'twig'); } // to be removed when the minimum required version of Twig is >= 2.0 diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php index 222ac32c9e40e..4a17902ae9f19 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php @@ -51,7 +51,8 @@ public function testShowActionCanBeForcedToShowErrorPage() )) ); - $request = Request::create('whatever', 'GET', array('showException' => false)); + $request = Request::create('whatever', 'GET'); + $request->attributes->set('showException', false); $exception = FlattenException::create(new \Exception(), 404); $controller = new ExceptionController($twig, /* "showException" defaults to --> */ true); From 9a08b776a284822b686c1c29532b967160f51ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gre=CC=81gory=20Pelletier?= Date: Sun, 9 Nov 2014 08:01:39 +0100 Subject: [PATCH 335/373] Remove block submit_widget --- .../Twig/Resources/views/Form/bootstrap_3_layout.html.twig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index e312da3f53f4c..99c04d8a703e5 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -14,11 +14,6 @@ {{- parent() -}} {%- endblock textarea_widget %} -{% block submit_widget -%} - {% set attr = attr|merge({class: (attr.class|default('') ~ ' btn')|trim}) %} - {{- parent() -}} -{%- endblock %} - {% block button_widget -%} {% set attr = attr|merge({class: (attr.class|default('') ~ ' btn')|trim}) %} {{- parent() -}} From ce2adfaf4ad36174579ed36a7e31fffed42240a9 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 23 Nov 2014 20:13:47 +0100 Subject: [PATCH 336/373] removed unneeded check It is already checked in the `isEnabled()` method of the parent `ServerCommand` class if the current PHP build is HHVM and the `server:start` command is never enabled then. Thus, it's not needed to check for HHVM on every command execution. This was pointed out by @stof in #12489 for the `server:run` command. --- .../Bundle/FrameworkBundle/Command/ServerStartCommand.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php index c5ec95d4d4611..f5fe6b6921b1d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php @@ -71,11 +71,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - if (defined('HHVM_VERSION')) { - $output->writeln('This command is not supported on HHVM.'); - - return 1; - } if (!extension_loaded('pcntl')) { $output->writeln('This command needs the pcntl extension to run.'); $output->writeln('You can either install it or use the server:run command instead to run the built-in web server.'); From 58476e18140b1d76674dceacb4102250b2659fd7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 23 Nov 2014 21:21:44 +0100 Subject: [PATCH 337/373] [Debug] fix ENT_SUBSTITUTE usage --- src/Symfony/Component/Debug/ExceptionHandler.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php index a0d03236de398..74346a5d00d60 100644 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ b/src/Symfony/Component/Debug/ExceptionHandler.php @@ -384,11 +384,6 @@ private function formatPath($path, $line) */ private function formatArgs(array $args) { - if (PHP_VERSION_ID >= 50400) { - $flags = ENT_QUOTES | ENT_SUBSTITUTE; - } else { - $flags = ENT_QUOTES; - } $result = array(); foreach ($args as $key => $item) { if ('object' === $item[0]) { @@ -429,7 +424,7 @@ protected static function utf8Htmlize($str) $str = iconv($charset, 'UTF-8', $str); } - return htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + return htmlspecialchars($str, ENT_QUOTES | (PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), 'UTF-8'); } /** From da3d5d4036a99eb2ebcd22c371bf95194547004d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 23 Nov 2014 21:57:10 +0100 Subject: [PATCH 338/373] updated CHANGELOG for 2.6.0-BETA2 --- CHANGELOG-2.6.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG-2.6.md b/CHANGELOG-2.6.md index f6145e035efb4..016a7a7d1fe8c 100644 --- a/CHANGELOG-2.6.md +++ b/CHANGELOG-2.6.md @@ -7,6 +7,25 @@ in 2.6 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.6.0...v2.6.1 +* 2.6.0-BETA2 (2014-11-23) + + * bug #12555 [Debug] fix ENT_SUBSTITUTE usage (nicolas-grekas) + * feature #12538 [FrameworkBundle] be smarter when guessing the document root (xabbuh) + * bug #12539 [TwigBundle] properly set request attributes in controller test (xabbuh) + * bug #12267 [Form][WebProfiler] Empty form names fix (kix) + * bug #12137 [FrameworkBundle] cache:clear command fills *.php.meta files with wrong data (Strate) + * bug #12525 [Bundle][FrameworkBundle] be smarter when guessing the document root (xabbuh) + * bug #12296 [SecurityBundle] Authentication entry point is only registered with firewall exception listener, not with authentication listeners (rjkip) + * bug #12446 [Twig/DebugBundle] move dump extension registration (nicolas-grekas) + * bug #12489 [FrameworkBundle] Fix server run in case the router script does not exist (romainneutron) + * feature #12404 [Form] Remove timezone options from DateType and TimeType (jakzal) + * bug #12487 [DomCrawler] Added support for 'link' tags in the Link class (StephaneSeng) + * bug #12490 [FrameworkBundle] Fix server start in case the PHP binary is not found (romainneutron) + * bug #12443 [HttpKernel] Adding support for invokable controllers in the RequestDataCollector (jameshalsall) + * bug #12393 [DependencyInjection] inlined factory not referenced (boekkooi) + * bug #12411 [VarDumper] Use Unicode Control Pictures (nicolas-grekas) + * bug #12436 [Filesystem] Fixed case for empty folder (yosmanyga) + * 2.6.0-BETA1 (2014-11-03) * first beta release From d56d198aa41e70058edbf660a7de0a0c02aa2faf Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 23 Nov 2014 21:57:16 +0100 Subject: [PATCH 339/373] updated VERSION for 2.6.0-BETA2 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index d3eed40a69d14..54b9be75256fa 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.6.0-DEV'; + const VERSION = '2.6.0-BETA2'; const VERSION_ID = '20600'; const MAJOR_VERSION = '2'; const MINOR_VERSION = '6'; const RELEASE_VERSION = '0'; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = 'BETA2'; /** * Constructor. From 4a1a0ed6868bceb60d6a67035fb8499d8aa85e10 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 24 Nov 2014 08:41:00 +0100 Subject: [PATCH 340/373] bumped Symfony version to 2.6.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 54b9be75256fa..d3eed40a69d14 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.6.0-BETA2'; + const VERSION = '2.6.0-DEV'; const VERSION_ID = '20600'; const MAJOR_VERSION = '2'; const MINOR_VERSION = '6'; const RELEASE_VERSION = '0'; - const EXTRA_VERSION = 'BETA2'; + const EXTRA_VERSION = 'DEV'; /** * Constructor. From 1c6f5a1c8919383c02241ac8efdb814ccff1825a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 24 Nov 2014 19:24:01 +0100 Subject: [PATCH 341/373] fix data type in docblock --- .../Http/Authentication/CustomAuthenticationSuccessHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationSuccessHandler.php b/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationSuccessHandler.php index abbb81b0f82f8..2d1b26ebb9179 100644 --- a/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationSuccessHandler.php +++ b/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationSuccessHandler.php @@ -24,7 +24,7 @@ class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler /** * Constructor. * - * @param AuthenticationSuccessHandlerInterface $handler An AuthenticationFailureHandlerInterface instance + * @param AuthenticationSuccessHandlerInterface $handler An AuthenticationSuccessHandlerInterface instance * @param array $options Options for processing a successful authentication attempt * @param string $providerKey The provider key */ From d3451fc9388efbe324294f9a8d10fb2b1d56b73f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Macias?= Date: Tue, 25 Nov 2014 23:48:05 +0100 Subject: [PATCH 342/373] [WebProfilerBundle] Remove usage of app.request in search bar template --- .../Bundle/WebProfilerBundle/Controller/ProfilerController.php | 1 + .../WebProfilerBundle/Resources/views/Profiler/search.html.twig | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php index 604890cc53c30..ef99e99dc0309 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -249,6 +249,7 @@ public function searchBarAction(Request $request) 'start' => $start, 'end' => $end, 'limit' => $limit, + 'request' => $request, )), 200, array('Content-Type' => 'text/html')); } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig index d9771d17393cf..8f06fa43e5294 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig @@ -15,7 +15,7 @@
    - +
    From cd55a81df7633a3848680a47a5805889cbf4f122 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Wed, 26 Nov 2014 14:38:05 +0100 Subject: [PATCH 343/373] Upgrade information for the Translation component regarding the new LoggingTranslator class. --- UPGRADE-2.6.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/UPGRADE-2.6.md b/UPGRADE-2.6.md index e01a1dc3ca888..fe0dc4839cca1 100644 --- a/UPGRADE-2.6.md +++ b/UPGRADE-2.6.md @@ -364,3 +364,43 @@ $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); Then enjoy dumping variables by calling `dump($var)` anywhere in your PHP and `{% dump var %}` or `{{ dump(var) }}` in Twig. Dumps are displayed **in the web debug toolbar**. + +Translation +----------- + +With `LoggingTranslator`, a new translator class is introduced with Symfony +2.6. By default, the `@translator` service is referring to this class in the +debug environment. + +If you have own services that depend on the `@translator` service and expect +this service to be an instance of either +`Symfony\Component\Translation\Translator` or +`Symfony\Bundle\FrameworkBundle\Translation\Translator`, e.g. by type-hinting +for either of these classes, you will need to change that type hint. You can +use the `TranslatorInterface` to be on the safe side for future changes. + +Before: + +```php +use Symfony\Component\Translation\Translator; + +class MyService { + public function __construct(Translator $translator) + { + ... + } +} +``` + +After: + +```php +use Symfony\Component\Translation\TranslatorInterface; + +class MyService { + public function __construct(TranslatorInterface $translator) + { + ... + } +} +``` From 680fe7acb2f40d40d62a558917e894365696acf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Macias?= Date: Wed, 26 Nov 2014 15:11:30 +0100 Subject: [PATCH 344/373] [RFC] [DebugBundle] [HttpKernel] Avoid using container as dependency for DumpListener --- .../DebugBundle/Resources/config/services.xml | 4 +- .../HttpKernel/EventListener/DumpListener.php | 30 +++++------- .../Tests/EventListener/DumpListenerTest.php | 46 ++++++------------- 3 files changed, 29 insertions(+), 51 deletions(-) diff --git a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml index 14da81c3dd078..798e8b78ec113 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml @@ -18,8 +18,8 @@ - - data_collector.dump + + diff --git a/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php b/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php index 46833959b20dc..c7f2daf218f1c 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php @@ -11,9 +11,10 @@ namespace Symfony\Component\HttpKernel\EventListener; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Dumper\DataDumperInterface; use Symfony\Component\VarDumper\VarDumper; /** @@ -27,30 +28,23 @@ class DumpListener implements EventSubscriberInterface private $dumper; /** - * @param ContainerInterface $container Service container, for lazy loading. - * @param string $dumper var_dumper dumper service to use. + * @param ClonerInterface $cloner Cloner service. + * @param DataDumperInterface $dumper Dumper service. */ - public function __construct(ContainerInterface $container, $dumper) + public function __construct(ClonerInterface $cloner, DataDumperInterface $dumper) { - $this->container = $container; + $this->cloner = $cloner; $this->dumper = $dumper; } public function configure() { - if ($this->container) { - $container = $this->container; - $dumper = $this->dumper; - $this->container = null; - - VarDumper::setHandler(function ($var) use ($container, $dumper) { - $dumper = $container->get($dumper); - $cloner = $container->get('var_dumper.cloner'); - $handler = function ($var) use ($dumper, $cloner) {$dumper->dump($cloner->cloneVar($var));}; - VarDumper::setHandler($handler); - $handler($var); - }); - } + $cloner = $this->cloner; + $dumper = $this->dumper; + + VarDumper::setHandler(function ($var) use ($cloner, $dumper) { + $dumper->dump($cloner->cloneVar($var)); + }); } public static function getSubscribedEvents() diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/DumpListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/DumpListenerTest.php index c665df563de60..a9b715bacc7dd 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/DumpListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/DumpListenerTest.php @@ -11,10 +11,11 @@ namespace Symfony\Component\HttpKernel\Tests\EventListener; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\HttpKernel\EventListener\DumpListener; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\DataDumperInterface; use Symfony\Component\VarDumper\VarDumper; /** @@ -34,34 +35,23 @@ public function testSubscribedEvents() public function testConfigure() { - $prevDumper = $this->getDumpHandler(); + $prevDumper = VarDumper::setHandler('var_dump'); + VarDumper::setHandler($prevDumper); - $container = new ContainerBuilder(); - $container->setDefinition('var_dumper.cloner', new Definition('Symfony\Component\HttpKernel\Tests\EventListener\MockCloner')); - $container->setDefinition('mock_dumper', new Definition('Symfony\Component\HttpKernel\Tests\EventListener\MockDumper')); + $cloner = new MockCloner(); + $dumper = new MockDumper(); ob_start(); $exception = null; - $listener = new DumpListener($container, 'mock_dumper'); + $listener = new DumpListener($cloner, $dumper); try { $listener->configure(); - $lazyDumper = $this->getDumpHandler(); VarDumper::dump('foo'); - - $loadedDumper = $this->getDumpHandler(); VarDumper::dump('bar'); $this->assertSame('+foo-+bar-', ob_get_clean()); - - $listenerReflector = new \ReflectionClass($listener); - $lazyReflector = new \ReflectionFunction($lazyDumper); - $loadedReflector = new \ReflectionFunction($loadedDumper); - - $this->assertSame($listenerReflector->getFilename(), $lazyReflector->getFilename()); - $this->assertSame($listenerReflector->getFilename(), $loadedReflector->getFilename()); - $this->assertGreaterThan($lazyReflector->getStartLine(), $loadedReflector->getStartLine()); } catch (\Exception $exception) { } @@ -71,28 +61,22 @@ public function testConfigure() throw $exception; } } - - private function getDumpHandler() - { - $prevDumper = VarDumper::setHandler('var_dump'); - VarDumper::setHandler($prevDumper ); - - return $prevDumper; - } } -class MockCloner +class MockCloner implements ClonerInterface { public function cloneVar($var) { - return $var.'-'; + return new Data(array($var.'-')); } } -class MockDumper +class MockDumper implements DataDumperInterface { - public function dump($var) + public function dump(Data $data) { - echo '+'.$var; + $rawData = $data->getRawData(); + + echo '+'.$rawData[0]; } } From b753218ff536749c19dfc3a445766ab0d592836a Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Sat, 8 Nov 2014 23:51:59 +0100 Subject: [PATCH 345/373] [Validator] make DateTime objects represented as strings in the violation message. --- .../Validator/Constraints/RangeValidator.php | 6 +- .../Tests/Constraints/RangeValidatorTest.php | 64 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/RangeValidator.php b/src/Symfony/Component/Validator/Constraints/RangeValidator.php index c0bde2b723450..ebae112a3a608 100644 --- a/src/Symfony/Component/Validator/Constraints/RangeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/RangeValidator.php @@ -35,7 +35,7 @@ public function validate($value, Constraint $constraint) if (!is_numeric($value) && !$value instanceof \DateTime && !$value instanceof \DateTimeInterface) { $this->buildViolation($constraint->invalidMessage) - ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) ->setCode(Range::INVALID_VALUE_ERROR) ->addViolation(); @@ -61,7 +61,7 @@ public function validate($value, Constraint $constraint) if (null !== $constraint->max && $value > $max) { $this->buildViolation($constraint->maxMessage) - ->setParameter('{{ value }}', $value) + ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) ->setParameter('{{ limit }}', $this->formatValue($max, self::PRETTY_DATE)) ->setCode(Range::BEYOND_RANGE_ERROR) ->addViolation(); @@ -71,7 +71,7 @@ public function validate($value, Constraint $constraint) if (null !== $constraint->min && $value < $min) { $this->buildViolation($constraint->minMessage) - ->setParameter('{{ value }}', $value) + ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) ->setParameter('{{ limit }}', $this->formatValue($min, self::PRETTY_DATE)) ->setCode(Range::BELOW_RANGE_ERROR) ->addViolation(); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php index 3bfb405e07200..9b7056b548735 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php @@ -52,20 +52,20 @@ public function getTenToTwenty() public function getLessThanTen() { return array( - array(9.99999), - array('9.99999'), - array(5), - array(1.0), + array(9.99999, '9.99999'), + array('9.99999', '"9.99999"'), + array(5, '5'), + array(1.0, '1.0'), ); } public function getMoreThanTwenty() { return array( - array(20.000001), - array('20.000001'), - array(21), - array(30.0), + array(20.000001, '20.000001'), + array('20.000001', '"20.000001"'), + array(21, '21'), + array(30.0, '30.0'), ); } @@ -105,7 +105,7 @@ public function testValidValuesMinMax($value) /** * @dataProvider getLessThanTen */ - public function testInvalidValuesMin($value) + public function testInvalidValuesMin($value, $formattedValue) { $constraint = new Range(array( 'min' => 10, @@ -115,7 +115,7 @@ public function testInvalidValuesMin($value) $this->validator->validate($value, $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', $value) + ->setParameter('{{ value }}', $formattedValue) ->setParameter('{{ limit }}', 10) ->setCode(Range::BELOW_RANGE_ERROR) ->assertRaised(); @@ -124,7 +124,7 @@ public function testInvalidValuesMin($value) /** * @dataProvider getMoreThanTwenty */ - public function testInvalidValuesMax($value) + public function testInvalidValuesMax($value, $formattedValue) { $constraint = new Range(array( 'max' => 20, @@ -134,7 +134,7 @@ public function testInvalidValuesMax($value) $this->validator->validate($value, $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', $value) + ->setParameter('{{ value }}', $formattedValue) ->setParameter('{{ limit }}', 20) ->setCode(Range::BEYOND_RANGE_ERROR) ->assertRaised(); @@ -143,7 +143,7 @@ public function testInvalidValuesMax($value) /** * @dataProvider getMoreThanTwenty */ - public function testInvalidValuesCombinedMax($value) + public function testInvalidValuesCombinedMax($value, $formattedValue) { $constraint = new Range(array( 'min' => 10, @@ -155,7 +155,7 @@ public function testInvalidValuesCombinedMax($value) $this->validator->validate($value, $constraint); $this->buildViolation('myMaxMessage') - ->setParameter('{{ value }}', $value) + ->setParameter('{{ value }}', $formattedValue) ->setParameter('{{ limit }}', 20) ->setCode(Range::BEYOND_RANGE_ERROR) ->assertRaised(); @@ -164,7 +164,7 @@ public function testInvalidValuesCombinedMax($value) /** * @dataProvider getLessThanTen */ - public function testInvalidValuesCombinedMin($value) + public function testInvalidValuesCombinedMin($value, $formattedValue) { $constraint = new Range(array( 'min' => 10, @@ -176,7 +176,7 @@ public function testInvalidValuesCombinedMin($value) $this->validator->validate($value, $constraint); $this->buildViolation('myMinMessage') - ->setParameter('{{ value }}', $value) + ->setParameter('{{ value }}', $formattedValue) ->setParameter('{{ limit }}', 10) ->setCode(Range::BELOW_RANGE_ERROR) ->assertRaised(); @@ -212,13 +212,13 @@ public function getSoonerThanTenthMarch2014() $this->setDefaultTimezone('UTC'); $tests = array( - array(new \DateTime('March 20, 2013')), - array(new \DateTime('March 9, 2014')), + array(new \DateTime('March 20, 2013'), 'Mar 20, 2013, 12:00 AM'), + array(new \DateTime('March 9, 2014'), 'Mar 9, 2014, 12:00 AM'), ); if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) { - $tests[] = array(new \DateTimeImmutable('March 20, 2013')); - $tests[] = array(new \DateTimeImmutable('March 9, 2014')); + $tests[] = array(new \DateTimeImmutable('March 20, 2013'), 'Mar 20, 2013, 12:00 AM'); + $tests[] = array(new \DateTimeImmutable('March 9, 2014'), 'Mar 9, 2014, 12:00 AM'); } $this->restoreDefaultTimezone(); @@ -233,13 +233,13 @@ public function getLaterThanTwentiethMarch2014() $this->setDefaultTimezone('UTC'); $tests = array( - array(new \DateTime('March 21, 2014')), - array(new \DateTime('March 9, 2015')), + array(new \DateTime('March 21, 2014'), 'Mar 21, 2014, 12:00 AM'), + array(new \DateTime('March 9, 2015'), 'Mar 9, 2015, 12:00 AM'), ); if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) { - $tests[] = array(new \DateTimeImmutable('March 21, 2014')); - $tests[] = array(new \DateTimeImmutable('March 9, 2015')); + $tests[] = array(new \DateTimeImmutable('March 21, 2014'), 'Mar 21, 2014, 12:00 AM'); + $tests[] = array(new \DateTimeImmutable('March 9, 2015'), 'Mar 9, 2015, 12:00 AM'); } $this->restoreDefaultTimezone(); @@ -283,7 +283,7 @@ public function testValidDatesMinMax($value) /** * @dataProvider getSoonerThanTenthMarch2014 */ - public function testInvalidDatesMin($value) + public function testInvalidDatesMin($value, $dateTimeAsString) { // Conversion of dates to string differs between ICU versions // Make sure we have the correct version loaded @@ -297,7 +297,7 @@ public function testInvalidDatesMin($value) $this->validator->validate($value, $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', $value) + ->setParameter('{{ value }}', $dateTimeAsString) ->setParameter('{{ limit }}', 'Mar 10, 2014, 12:00 AM') ->setCode(Range::BELOW_RANGE_ERROR) ->assertRaised(); @@ -306,7 +306,7 @@ public function testInvalidDatesMin($value) /** * @dataProvider getLaterThanTwentiethMarch2014 */ - public function testInvalidDatesMax($value) + public function testInvalidDatesMax($value, $dateTimeAsString) { // Conversion of dates to string differs between ICU versions // Make sure we have the correct version loaded @@ -320,7 +320,7 @@ public function testInvalidDatesMax($value) $this->validator->validate($value, $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', $value) + ->setParameter('{{ value }}', $dateTimeAsString) ->setParameter('{{ limit }}', 'Mar 20, 2014, 12:00 AM') ->setCode(Range::BEYOND_RANGE_ERROR) ->assertRaised(); @@ -329,7 +329,7 @@ public function testInvalidDatesMax($value) /** * @dataProvider getLaterThanTwentiethMarch2014 */ - public function testInvalidDatesCombinedMax($value) + public function testInvalidDatesCombinedMax($value, $dateTimeAsString) { // Conversion of dates to string differs between ICU versions // Make sure we have the correct version loaded @@ -345,7 +345,7 @@ public function testInvalidDatesCombinedMax($value) $this->validator->validate($value, $constraint); $this->buildViolation('myMaxMessage') - ->setParameter('{{ value }}', $value) + ->setParameter('{{ value }}', $dateTimeAsString) ->setParameter('{{ limit }}', 'Mar 20, 2014, 12:00 AM') ->setCode(Range::BEYOND_RANGE_ERROR) ->assertRaised(); @@ -354,7 +354,7 @@ public function testInvalidDatesCombinedMax($value) /** * @dataProvider getSoonerThanTenthMarch2014 */ - public function testInvalidDatesCombinedMin($value) + public function testInvalidDatesCombinedMin($value, $dateTimeAsString) { // Conversion of dates to string differs between ICU versions // Make sure we have the correct version loaded @@ -370,7 +370,7 @@ public function testInvalidDatesCombinedMin($value) $this->validator->validate($value, $constraint); $this->buildViolation('myMinMessage') - ->setParameter('{{ value }}', $value) + ->setParameter('{{ value }}', $dateTimeAsString) ->setParameter('{{ limit }}', 'Mar 10, 2014, 12:00 AM') ->setCode(Range::BELOW_RANGE_ERROR) ->assertRaised(); From 9b30ec0d7885a5aca9dd92a8c0db548d32fc4cf7 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 28 Nov 2014 16:58:51 +0100 Subject: [PATCH 346/373] updated CHANGELOG for 2.6.0 --- CHANGELOG-2.6.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG-2.6.md b/CHANGELOG-2.6.md index 016a7a7d1fe8c..cb6df147b139d 100644 --- a/CHANGELOG-2.6.md +++ b/CHANGELOG-2.6.md @@ -7,6 +7,14 @@ in 2.6 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.6.0...v2.6.1 +* 2.6.0 (2014-11-28) + + * bug #12553 [Debug] fix error message on double exception (nicolas-grekas) + * bug #12550 [FrameworkBundle] backport #12489 (xabbuh) + * bug #12437 [Validator] make DateTime objects represented as strings in the violation message (hhamon) + * bug #12575 [WebProfilerBundle] Remove usage of app.request in search bar template (jeromemacias) + * bug #12570 Fix initialized() with aliased services (Daniel Wehner) + * 2.6.0-BETA2 (2014-11-23) * bug #12555 [Debug] fix ENT_SUBSTITUTE usage (nicolas-grekas) From 645bb3248db50d653d18929eb0c92b131b4f35ad Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 28 Nov 2014 16:59:01 +0100 Subject: [PATCH 347/373] updated VERSION for 2.6.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index d3eed40a69d14..765aa834d68a5 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.6.0-DEV'; + const VERSION = '2.6.0'; const VERSION_ID = '20600'; const MAJOR_VERSION = '2'; const MINOR_VERSION = '6'; const RELEASE_VERSION = '0'; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; /** * Constructor. From 1ef9cf6dc7346d135ba94c4a75011be567106d1e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 29 Nov 2014 11:21:18 +0100 Subject: [PATCH 348/373] bumped Symfony version to 2.6.1 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 765aa834d68a5..3820e2ff5521f 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.6.0'; - const VERSION_ID = '20600'; + const VERSION = '2.6.1-DEV'; + const VERSION_ID = '20601'; const MAJOR_VERSION = '2'; const MINOR_VERSION = '6'; - const RELEASE_VERSION = '0'; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = '1'; + const EXTRA_VERSION = 'DEV'; /** * Constructor. From e130a298db671270d1bdc124bbe9a3bbc2dac1b1 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Sat, 29 Nov 2014 10:41:07 +0000 Subject: [PATCH 349/373] Fixed a docblock --- .../Doctrine/DependencyInjection/AbstractDoctrineExtension.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index ce92fe70dec98..f18b79ad69c52 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -398,9 +398,12 @@ protected function loadCacheDriver($cacheName, $objectManagerName, array $cacheD /** * Returns a modified version of $managerConfigs. + * * The manager called $autoMappedManager will map all bundles that are not mepped by other managers. + * * @param array $managerConfigs * @param array $bundles + * * @return array The modified version of $managerConfigs. */ protected function fixManagersAutoMappings(array $managerConfigs, array $bundles) From ceecb502f8c8be4d10f4afe96dc24debe221b907 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 30 Nov 2014 11:30:45 +0100 Subject: [PATCH 350/373] [Filesystem] fix lock file permissions --- .../Component/Filesystem/LockHandler.php | 22 +++++++++++++------ .../Filesystem/Tests/LockHandlerTest.php | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Filesystem/LockHandler.php b/src/Symfony/Component/Filesystem/LockHandler.php index a1716db4d03a1..59d35ec92883e 100644 --- a/src/Symfony/Component/Filesystem/LockHandler.php +++ b/src/Symfony/Component/Filesystem/LockHandler.php @@ -49,9 +49,7 @@ public function __construct($name, $lockPath = null) throw new IOException(sprintf('The directory "%s" is not writable.', $lockPath), 0, null, $lockPath); } - $name = sprintf('%s-%s', preg_replace('/[^a-z0-9\._-]+/i', '-', $name), hash('sha256', $name)); - - $this->file = sprintf('%s/%s', $lockPath, $name); + $this->file = sprintf('%s/sf.%s.%s.lock', $lockPath, preg_replace('/[^a-z0-9\._-]+/i', '-', $name), hash('sha256', $name)); } /** @@ -67,14 +65,24 @@ public function lock($blocking = false) return true; } - // Set an error handler to not trigger the registered error handler if - // the file can not be opened. + // Silence both userland and native PHP error handlers + $errorLevel = error_reporting(0); set_error_handler('var_dump', 0); - $this->handle = @fopen($this->file, 'c'); + + if (!$this->handle = fopen($this->file, 'r')) { + if ($this->handle = fopen($this->file, 'x')) { + chmod($this->file, 0444); + } elseif (!$this->handle = fopen($this->file, 'r')) { + usleep(100); // Give some time for chmod() to complete + $this->handle = fopen($this->file, 'r'); + } + } restore_error_handler(); + error_reporting($errorLevel); if (!$this->handle) { - throw new IOException(sprintf('Unable to fopen "%s".', $this->file), 0, null, $this->file); + $error = error_get_last(); + throw new IOException($error['message'], 0, null, $this->file); } // On Windows, even if PHP doc says the contrary, LOCK_NB works, see diff --git a/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php b/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php index e4f30a16a9780..c2058ffc588b1 100644 --- a/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php +++ b/src/Symfony/Component/Filesystem/Tests/LockHandlerTest.php @@ -28,7 +28,7 @@ public function testConstructSanitizeName() { $lock = new LockHandler(''); - $file = sprintf('%s/-php-echo-hello-word--4b3d9d0d27ddef3a78a64685dda3a963e478659a9e5240feaf7b4173a8f28d5f', sys_get_temp_dir()); + $file = sprintf('%s/sf.-php-echo-hello-word-.4b3d9d0d27ddef3a78a64685dda3a963e478659a9e5240feaf7b4173a8f28d5f.lock', sys_get_temp_dir()); // ensure the file does not exist before the lock @unlink($file); From 5e35fffa7087414c763833e5b6d861b1c134c5fc Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Sun, 30 Nov 2014 15:16:06 +0000 Subject: [PATCH 351/373] Removed a dev annotation from a version constraint --- src/Symfony/Bundle/FrameworkBundle/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index d3bcf945ecf92..e293fa22d7980 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -36,7 +36,7 @@ "symfony/console": "~2.0", "symfony/finder": "~2.0", "symfony/security": "~2.4", - "symfony/form": "~2.6@dev", + "symfony/form": "~2.6", "symfony/class-loader": "~2.1", "symfony/expression-language": "~2.4", "symfony/process": "~2.0", From 2625193bd1b8d99fefb5a6fc79b1955d45bd682a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sun, 30 Nov 2014 20:42:36 +0100 Subject: [PATCH 352/373] [FrameworkBundle] Update deprecated service call The service `security.context` is now deprecated due to PR #11690. This commit updates PR #11593 replacing `security.context` with `security.authorization_checker` in the `isGranted()` controller shortcut. --- src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php index b3c7f42c2ce27..f310b78cc8a33 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php @@ -123,11 +123,11 @@ protected function addFlash($type, $message) */ protected function isGranted($attributes, $object = null) { - if (!$this->container->has('security.context')) { + if (!$this->container->has('security.authorization_checker')) { throw new \LogicException('The SecurityBundle is not registered in your application.'); } - return $this->container->get('security.context')->isGranted($attributes, $object); + return $this->container->get('security.authorization_checker')->isGranted($attributes, $object); } /** From 408df7c1ce0a22caac81fce76c7983e15e889b5e Mon Sep 17 00:00:00 2001 From: Abdellatif AitBoudad Date: Mon, 1 Dec 2014 14:24:26 +0000 Subject: [PATCH 353/373] [FrameworkBundle][Router Cmd] use debug namespace. --- .../Bundle/FrameworkBundle/Command/RouterMatchCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php index 72b322bca3589..38536e0f69f28 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php @@ -96,7 +96,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } elseif (TraceableUrlMatcher::ROUTE_MATCHES == $trace['level']) { $output->writeln(sprintf('Route "%s" matches', $trace['name'])); - $routerDebugcommand = $this->getApplication()->find('router:debug'); + $routerDebugcommand = $this->getApplication()->find('debug:router'); $output->writeln(''); $routerDebugcommand->run(new ArrayInput(array('name' => $trace['name'])), $output); From a088521b6ff2b41b23072ebacd4c5fa199f85d93 Mon Sep 17 00:00:00 2001 From: MasterB Date: Wed, 3 Dec 2014 08:02:21 +0100 Subject: [PATCH 354/373] Remove deprecated class | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | replace deprecated class with correct one --- .../Bundle/TwigBundle/Controller/PreviewErrorController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php b/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php index b4eb932e581bb..d7a675dc4de27 100644 --- a/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php +++ b/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php @@ -11,7 +11,7 @@ namespace Symfony\Bundle\TwigBundle\Controller; -use Symfony\Component\HttpKernel\Exception\FlattenException; +use Symfony\Component\Debug\Exception\FlattenException; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpFoundation\Request; From 2f0b3551b5f4cc288e903dca571bcbc33ea0195a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 3 Dec 2014 09:59:38 +0100 Subject: [PATCH 355/373] Revert "minor #10241 [DependencyInjection] made some perf improvements (fabpot)" This reverts commit 181e460984eb93ef6ca2b52563ef455c24092937, reversing changes made to ff1749302950ae8b78c18be4385d839c16cfe8c8. --- .../DependencyInjection/Dumper/PhpDumper.php | 43 +++++++++++++------ .../Tests/Fixtures/php/services1-1.php | 6 +-- .../Tests/Fixtures/php/services1.php | 6 +-- .../Tests/Fixtures/php/services10.php | 27 ++++++++---- .../Tests/Fixtures/php/services11.php | 4 +- .../Tests/Fixtures/php/services8.php | 24 +++++++---- .../Tests/Fixtures/php/services9.php | 22 +++++++--- .../Tests/Fixtures/php/services9_compiled.php | 29 +++++++++---- 8 files changed, 104 insertions(+), 57 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 0241c7fcf9901..7ed2b3027e841 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -833,18 +833,16 @@ class $class extends $baseClass */ private function addConstructor() { - $parameters = $this->exportParameters($this->container->getParameterBag()->all()); + $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; $code = <<exportParameters($this->container->getParameterBag()->all()); - $code = <<container->getParameterBag()->all()) { + $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n"; + } + + $code .= <<services = \$this->scopedServices = \$this->scopeStacks = array(); @@ -990,6 +994,8 @@ private function addDefaultParametersMethod() return ''; } + $parameters = $this->exportParameters($this->container->getParameterBag()->all()); + $code = ''; if ($this->container->isFrozen()) { $code .= <<parameters[\$name]) || array_key_exists(\$name, \$this->parameters))) { throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name)); } - return self::\$parameters[\$name]; + return \$this->parameters[\$name]; } /** @@ -1015,7 +1021,7 @@ public function hasParameter(\$name) { \$name = strtolower(\$name); - return isset(self::\$parameters[\$name]) || array_key_exists(\$name, self::\$parameters); + return isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters); } /** @@ -1032,15 +1038,28 @@ public function setParameter(\$name, \$value) public function getParameterBag() { if (null === \$this->parameterBag) { - \$this->parameterBag = new FrozenParameterBag(self::\$parameters); + \$this->parameterBag = new FrozenParameterBag(\$this->parameters); } return \$this->parameterBag; } - EOF; } + $code .= << '', - 'some_string' => '-', - ); + private $parameters; /** * Constructor. */ public function __construct() { + $this->parameters = $this->getDefaultParameters(); + $this->services = $this->scopedServices = $this->scopeStacks = array(); @@ -69,11 +68,11 @@ public function getParameter($name) { $name = strtolower($name); - if (!(isset(self::$parameters[$name]) || array_key_exists($name, self::$parameters))) { + if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) { throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } - return self::$parameters[$name]; + return $this->parameters[$name]; } /** @@ -83,7 +82,7 @@ public function hasParameter($name) { $name = strtolower($name); - return isset(self::$parameters[$name]) || array_key_exists($name, self::$parameters); + return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters); } /** @@ -100,9 +99,21 @@ public function setParameter($name, $value) public function getParameterBag() { if (null === $this->parameterBag) { - $this->parameterBag = new FrozenParameterBag(self::$parameters); + $this->parameterBag = new FrozenParameterBag($this->parameters); } return $this->parameterBag; } + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'empty_value' => '', + 'some_string' => '-', + ); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php index 2d8a61fb2d02b..ada46e94129ed 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php @@ -16,9 +16,7 @@ */ class ProjectServiceContainer extends Container { - private static $parameters = array( - - ); + private $parameters; /** * Constructor. diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php index 7c50647d4e049..f922d2000c7ed 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php @@ -16,7 +16,22 @@ */ class ProjectServiceContainer extends Container { - private static $parameters = array( + /** + * Constructor. + */ + public function __construct() + { + parent::__construct(new ParameterBag($this->getDefaultParameters())); + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( 'foo' => '%baz%', 'baz' => 'bar', 'bar' => 'foo is %%foo bar', @@ -32,12 +47,5 @@ class ProjectServiceContainer extends Container 7 => 'null', ), ); - - /** - * Constructor. - */ - public function __construct() - { - parent::__construct(new ParameterBag(self::$parameters)); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index 3cc3aac8b845b..650bdf7b634ae 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -16,18 +16,12 @@ */ class ProjectServiceContainer extends Container { - private static $parameters = array( - 'baz_class' => 'BazClass', - 'foo_class' => 'Bar\\FooClass', - 'foo' => 'bar', - ); - /** * Constructor. */ public function __construct() { - parent::__construct(new ParameterBag(self::$parameters)); + parent::__construct(new ParameterBag($this->getDefaultParameters())); $this->methodMap = array( 'bar' => 'getBarService', 'baz' => 'getBazService', @@ -390,4 +384,18 @@ protected function getNewFactoryService() return $instance; } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'baz_class' => 'BazClass', + 'foo_class' => 'Bar\\FooClass', + 'foo' => 'bar', + ); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index 5e8103bdd0cc9..b81687f57488b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -16,17 +16,15 @@ */ class ProjectServiceContainer extends Container { - private static $parameters = array( - 'baz_class' => 'BazClass', - 'foo_class' => 'Bar\\FooClass', - 'foo' => 'bar', - ); + private $parameters; /** * Constructor. */ public function __construct() { + $this->parameters = $this->getDefaultParameters(); + $this->services = $this->scopedServices = $this->scopeStacks = array(); @@ -336,11 +334,11 @@ public function getParameter($name) { $name = strtolower($name); - if (!(isset(self::$parameters[$name]) || array_key_exists($name, self::$parameters))) { + if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) { throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } - return self::$parameters[$name]; + return $this->parameters[$name]; } /** @@ -350,7 +348,7 @@ public function hasParameter($name) { $name = strtolower($name); - return isset(self::$parameters[$name]) || array_key_exists($name, self::$parameters); + return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters); } /** @@ -367,9 +365,22 @@ public function setParameter($name, $value) public function getParameterBag() { if (null === $this->parameterBag) { - $this->parameterBag = new FrozenParameterBag(self::$parameters); + $this->parameterBag = new FrozenParameterBag($this->parameters); } return $this->parameterBag; } + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'baz_class' => 'BazClass', + 'foo_class' => 'Bar\\FooClass', + 'foo' => 'bar', + ); + } } From 7887a46ff98e25d27b680b5d24c0e58559abcff8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 3 Dec 2014 10:22:11 +0100 Subject: [PATCH 356/373] [DependencyInjection] keep some of the reverted perf optim --- .../DependencyInjection/Dumper/PhpDumper.php | 38 +++--------- .../Tests/Dumper/PhpDumperTest.php | 15 ----- .../Tests/Fixtures/php/services1-1.php | 8 ++- .../Tests/Fixtures/php/services1.php | 8 ++- .../Tests/Fixtures/php/services10.php | 18 ++---- .../Tests/Fixtures/php/services11.php | 61 ------------------- .../Tests/Fixtures/php/services12.php | 30 ++++----- .../Tests/Fixtures/php/services19.php | 10 +-- .../Tests/Fixtures/php/services8.php | 16 ++--- .../Tests/Fixtures/php/services9.php | 24 +++----- .../Tests/Fixtures/php/services9_compiled.php | 20 ++---- 11 files changed, 68 insertions(+), 180 deletions(-) delete mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 7ed2b3027e841..11cae7a97ad56 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -823,6 +823,8 @@ private function startClass($class, $baseClass, $namespace) */ class $class extends $baseClass { + private \$parameters; + EOF; } @@ -833,7 +835,7 @@ class $class extends $baseClass */ private function addConstructor() { - $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; + $parameters = $this->exportParameters($this->container->getParameterBag()->all()); $code = <<parameters = $parameters; + + parent::__construct(new ParameterBag(\$this->parameters)); EOF; @@ -870,26 +874,19 @@ public function __construct() */ private function addFrozenConstructor() { - $code = <<exportParameters($this->container->getParameterBag()->all()); - private \$parameters; + $code = <<container->getParameterBag()->all()) { - $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n"; - } - - $code .= <<services = \$this->scopedServices = \$this->scopeStacks = array(); + \$this->parameters = $parameters; \$this->set('service_container', \$this); @@ -994,8 +991,6 @@ private function addDefaultParametersMethod() return ''; } - $parameters = $this->exportParameters($this->container->getParameterBag()->all()); - $code = ''; if ($this->container->isFrozen()) { $code .= <<parameterBag; } -EOF; - } - - $code .= <<setResourceTracking(false); - $container->register('foo', 'stdClass'); - - $container->compile(); - - $dumper = new PhpDumper($container); - - $dumpedString = $dumper->dump(); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services11.php', $dumpedString, '->dump() does not add getDefaultParameters() method call if container have no parameters.'); - $this->assertNotRegexp("/function getDefaultParameters\(/", $dumpedString, '->dump() does not add getDefaultParameters() method definition.'); - } - public function testDumpOptimizationString() { $definition = new Definition(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php index ca7a5c6a24ded..c2b50948a41ad 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php @@ -17,11 +17,17 @@ */ class Container extends AbstractContainer { + private $parameters; + /** * Constructor. */ public function __construct() { - parent::__construct(); + $this->parameters = array( + + ); + + parent::__construct(new ParameterBag($this->parameters)); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php index 084e7891af906..23d367f1f9e42 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php @@ -16,11 +16,17 @@ */ class ProjectServiceContainer extends Container { + private $parameters; + /** * Constructor. */ public function __construct() { - parent::__construct(); + $this->parameters = array( + + ); + + parent::__construct(new ParameterBag($this->parameters)); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php index 0c47107e6f86b..3b504c9dd504e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php @@ -23,11 +23,13 @@ class ProjectServiceContainer extends Container */ public function __construct() { - $this->parameters = $this->getDefaultParameters(); - $this->services = $this->scopedServices = $this->scopeStacks = array(); + $this->parameters = array( + 'empty_value' => '', + 'some_string' => '-', + ); $this->set('service_container', $this); @@ -104,16 +106,4 @@ public function getParameterBag() return $this->parameterBag; } - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'empty_value' => '', - 'some_string' => '-', - ); - } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php deleted file mode 100644 index ada46e94129ed..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php +++ /dev/null @@ -1,61 +0,0 @@ -services = - $this->scopedServices = - $this->scopeStacks = array(); - - $this->set('service_container', $this); - - $this->scopes = array(); - $this->scopeChildren = array(); - $this->methodMap = array( - 'foo' => 'getFooService', - ); - - $this->aliases = array(); - } - - /** - * {@inheritdoc} - */ - public function compile() - { - throw new LogicException('You cannot compile a dumped frozen container.'); - } - - /** - * Gets the 'foo' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance. - */ - protected function getFooService() - { - return $this->services['foo'] = new \stdClass(); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php index 52d2702afd756..1d7ac85d7b795 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php @@ -16,16 +16,21 @@ */ class ProjectServiceContainer extends Container { + private $parameters; + /** * Constructor. */ public function __construct() { - $this->parameters = $this->getDefaultParameters(); - $this->services = $this->scopedServices = $this->scopeStacks = array(); + $this->parameters = array( + 'foo' => ('wiz'.dirname(__DIR__)), + 'bar' => __DIR__, + 'baz' => (__DIR__.'/PhpDumperTest.php'), + ); $this->set('service_container', $this); @@ -38,6 +43,14 @@ public function __construct() $this->aliases = array(); } + /** + * {@inheritdoc} + */ + public function compile() + { + throw new LogicException('You cannot compile a dumped frozen container.'); + } + /** * Gets the 'test' service. * @@ -94,17 +107,4 @@ public function getParameterBag() return $this->parameterBag; } - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'foo' => ('wiz'.dirname(__DIR__)), - 'bar' => __DIR__, - 'baz' => (__DIR__.'/PhpDumperTest.php'), - ); - } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php index 746b3fa7765f9..fdafa6e40623f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php @@ -16,16 +16,18 @@ */ class ProjectServiceContainer extends Container { - private static $parameters = array( - - ); + private $parameters; /** * Constructor. */ public function __construct() { - parent::__construct(new ParameterBag(self::$parameters)); + $this->parameters = array( + + ); + + parent::__construct(new ParameterBag($this->parameters)); $this->methodMap = array( 'service_from_anonymous_factory' => 'getServiceFromAnonymousFactoryService', 'service_with_method_call_and_factory' => 'getServiceWithMethodCallAndFactoryService', diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php index f922d2000c7ed..a6de38e381bae 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php @@ -16,22 +16,14 @@ */ class ProjectServiceContainer extends Container { + private $parameters; + /** * Constructor. */ public function __construct() { - parent::__construct(new ParameterBag($this->getDefaultParameters())); - } - - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( + $this->parameters = array( 'foo' => '%baz%', 'baz' => 'bar', 'bar' => 'foo is %%foo bar', @@ -47,5 +39,7 @@ protected function getDefaultParameters() 7 => 'null', ), ); + + parent::__construct(new ParameterBag($this->parameters)); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index 650bdf7b634ae..60110570c465e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -16,12 +16,20 @@ */ class ProjectServiceContainer extends Container { + private $parameters; + /** * Constructor. */ public function __construct() { - parent::__construct(new ParameterBag($this->getDefaultParameters())); + $this->parameters = array( + 'baz_class' => 'BazClass', + 'foo_class' => 'Bar\\FooClass', + 'foo' => 'bar', + ); + + parent::__construct(new ParameterBag($this->parameters)); $this->methodMap = array( 'bar' => 'getBarService', 'baz' => 'getBazService', @@ -384,18 +392,4 @@ protected function getNewFactoryService() return $instance; } - - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'baz_class' => 'BazClass', - 'foo_class' => 'Bar\\FooClass', - 'foo' => 'bar', - ); - } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index b81687f57488b..7d03bd5b8b0d8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -23,11 +23,14 @@ class ProjectServiceContainer extends Container */ public function __construct() { - $this->parameters = $this->getDefaultParameters(); - $this->services = $this->scopedServices = $this->scopeStacks = array(); + $this->parameters = array( + 'baz_class' => 'BazClass', + 'foo_class' => 'Bar\\FooClass', + 'foo' => 'bar', + ); $this->set('service_container', $this); @@ -370,17 +373,4 @@ public function getParameterBag() return $this->parameterBag; } - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'baz_class' => 'BazClass', - 'foo_class' => 'Bar\\FooClass', - 'foo' => 'bar', - ); - } } From 0999ea74b7f5ab8019866aa2994ca35903010b2b Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 3 Dec 2014 11:02:59 +0100 Subject: [PATCH 357/373] fixed a test --- .../TwigBundle/Tests/Controller/PreviewErrorControllerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php index 2d97b0c7ad2eb..8cb276734e762 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php @@ -38,7 +38,7 @@ public function testForwardRequestToConfiguredController() $self->assertEquals($logicalControllerName, $request->attributes->get('_controller')); $exception = $request->attributes->get('exception'); - $self->assertInstanceOf('Symfony\Component\HttpKernel\Exception\FlattenException', $exception); + $self->assertInstanceOf('Symfony\Component\Debug\Exception\FlattenException', $exception); $self->assertEquals($code, $exception->getStatusCode()); $self->assertFalse($request->attributes->get('showException')); From 74a16e61554b7a2520ba95d2e81b2eade24021fd Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 3 Dec 2014 17:40:31 +0100 Subject: [PATCH 358/373] updated CHANGELOG for 2.6.1 --- CHANGELOG-2.6.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG-2.6.md b/CHANGELOG-2.6.md index cb6df147b139d..4c48fed5e93a2 100644 --- a/CHANGELOG-2.6.md +++ b/CHANGELOG-2.6.md @@ -7,6 +7,14 @@ in 2.6 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.6.0...v2.6.1 +* 2.6.1 (2014-12-03) + + * bug #12823 [DependencyInjection] fix PhpDumper (nicolas-grekas) + * bug #12811 Configure firewall's kernel exception listener with configured entry point or a default entry point (rjkip) + * bug #12770 [Filesystem] fix lock file permissions (nicolas-grekas) + * bug #12784 [DependencyInjection] make paths relative to __DIR__ in the generated container (nicolas-grekas) + * bug #12716 [ClassLoader] define constant only if it wasn't defined before (xabbuh) + * 2.6.0 (2014-11-28) * bug #12553 [Debug] fix error message on double exception (nicolas-grekas) From d805dfd67950848ec0338b4acd75777b340977b7 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 3 Dec 2014 17:40:43 +0100 Subject: [PATCH 359/373] updated VERSION for 2.6.1 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 1fa66ab62b7b2..4fdbf955765d7 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.6.1-DEV'; + const VERSION = '2.6.1'; const VERSION_ID = '20601'; const MAJOR_VERSION = '2'; const MINOR_VERSION = '6'; const RELEASE_VERSION = '1'; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; /** * Constructor. From 0b3aa16f2f8d2204e153d74ea6b7fc0496f45c10 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 3 Dec 2014 18:16:02 +0100 Subject: [PATCH 360/373] bumped Symfony version to 2.6.2 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 4fdbf955765d7..6c00878c7c266 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.6.1'; - const VERSION_ID = '20601'; + const VERSION = '2.6.2-DEV'; + const VERSION_ID = '20602'; const MAJOR_VERSION = '2'; const MINOR_VERSION = '6'; - const RELEASE_VERSION = '1'; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = '2'; + const EXTRA_VERSION = 'DEV'; /** * Constructor. From 7224bde4c1556806351e2f9996441f77efb51c0e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 4 Dec 2014 10:43:55 +0100 Subject: [PATCH 361/373] Revert "minor #12821 Remove deprecated class (MasterB)" This reverts commit 3d637487f08368209274d61aaec278746101d440, reversing changes made to 7a5762c1982e1675ab52a01d5b348343bfe3f4b0. --- .../Bundle/TwigBundle/Controller/PreviewErrorController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php b/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php index d7a675dc4de27..b4eb932e581bb 100644 --- a/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php +++ b/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php @@ -11,7 +11,7 @@ namespace Symfony\Bundle\TwigBundle\Controller; -use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpKernel\Exception\FlattenException; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpFoundation\Request; From 2f10a0a70402e93cf963003d5960dbff4f926650 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Fri, 5 Dec 2014 20:11:30 +0000 Subject: [PATCH 362/373] [2.6] CS Fixes And Removed An Unused Import --- .../CompilerPass/RegisterMappingsPass.php | 2 +- .../DoctrineExtensionTest.php | 68 +++++++++---------- .../Command/AssetsInstallCommand.php | 1 - .../Console/Descriptor/Descriptor.php | 8 +-- .../Console/Descriptor/JsonDescriptor.php | 4 +- .../Console/Descriptor/XmlDescriptor.php | 4 +- .../Controller/ControllerNameParser.php | 1 + .../Security/Factory/RemoteUserFactory.php | 2 - .../Exception/FileLoaderLoadException.php | 6 +- .../Component/Console/Helper/ProgressBar.php | 2 +- .../CssSelector/Node/Specificity.php | 1 + src/Symfony/Component/Debug/ErrorHandler.php | 6 +- .../ContainerAwareEventDispatcherTest.php | 1 + .../ExpressionLanguage/ExpressionFunction.php | 36 +++++----- .../ExpressionLanguage/ExpressionLanguage.php | 2 +- .../Tests/ExpressionLanguageTest.php | 2 +- .../Component/Filesystem/Filesystem.php | 1 + .../Component/HttpFoundation/ParameterBag.php | 8 +-- .../HttpKernel/EventListener/DumpListener.php | 4 +- .../Component/HttpKernel/HttpCache/Esi.php | 4 +- .../HttpKernel/HttpCache/HttpCache.php | 1 + .../HttpCache/SurrogateInterface.php | 12 ++-- .../DataCollector/DumpDataCollectorTest.php | 6 +- .../Component/Process/Pipes/UnixPipes.php | 2 +- src/Symfony/Component/Routing/Router.php | 1 - .../Authorization/Voter/AbstractVoter.php | 6 +- .../Authentication/AuthenticationUtils.php | 3 +- .../CustomAuthenticationFailureHandler.php | 4 +- .../Tests/Firewall/RememberMeListenerTest.php | 2 +- .../RemoteUserAuthenticationListenerTest.php | 4 +- .../Normalizer/PropertyNormalizer.php | 2 +- .../Normalizer/PropertyNormalizerTest.php | 4 +- .../Translation/LoggingTranslator.php | 2 +- .../VarDumper/Cloner/AbstractCloner.php | 8 +-- src/Symfony/Component/Yaml/Parser.php | 8 +-- 35 files changed, 116 insertions(+), 112 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php index 4c72bff48da91..d1273c216550a 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php @@ -196,7 +196,7 @@ protected function getDriver(ContainerBuilder $container) * @return string a service definition name * * @throws ParameterNotFoundException if none of the managerParameters has a - * non-empty value. + * non-empty value. */ private function getConfigurationServiceName(ContainerBuilder $container) { diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php index 54595899587f5..48c00808b586c 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -54,17 +54,17 @@ protected function setUp() public function testFixManagersAutoMappingsWithTwoAutomappings() { $emConfigs = array( - 'em1'=> array( - 'auto_mapping' => true + 'em1' => array( + 'auto_mapping' => true, ), - 'em2'=> array( - 'auto_mapping' => true + 'em2' => array( + 'auto_mapping' => true, ), ); $bundles = array( - 'FristBundle'=> 'My\FristBundle', - 'SecondBundle'=> 'My\SecondBundle', + 'FristBundle' => 'My\FristBundle', + 'SecondBundle' => 'My\SecondBundle', ); $reflection = new \ReflectionClass(get_class($this->extension)); @@ -79,34 +79,34 @@ public function getAutomappingData() return array( array( array( // no auto mapping on em1 - 'auto_mapping' => false + 'auto_mapping' => false, ), array( // no auto mapping on em2 - 'auto_mapping' => false + 'auto_mapping' => false, ), array(), - array() + array(), ), array( array( // no auto mapping on em1 - 'auto_mapping' => false + 'auto_mapping' => false, ), array( // auto mapping enabled on em2 - 'auto_mapping' => true + 'auto_mapping' => true, ), array(), array( 'mappings' => array( 'FristBundle' => array( 'mapping' => true, - 'is_bundle' => true + 'is_bundle' => true, ), 'SecondBundle' => array( 'mapping' => true, - 'is_bundle' => true - ) - ) - ) + 'is_bundle' => true, + ), + ), + ), ), array( array( // no auto mapping on em1, but it defines SecondBundle as own @@ -114,30 +114,30 @@ public function getAutomappingData() 'mappings' => array( 'SecondBundle' => array( 'mapping' => true, - 'is_bundle' => true - ) - ) + 'is_bundle' => true, + ), + ), ), array( // auto mapping enabled on em2 - 'auto_mapping' => true + 'auto_mapping' => true, ), array( 'mappings' => array( 'SecondBundle' => array( 'mapping' => true, - 'is_bundle' => true - ) - ) + 'is_bundle' => true, + ), + ), ), array( 'mappings' => array( 'FristBundle' => array( 'mapping' => true, - 'is_bundle' => true - ) - ) - ) - ) + 'is_bundle' => true, + ), + ), + ), + ), ); } @@ -147,13 +147,13 @@ public function getAutomappingData() public function testFixManagersAutoMappings(array $originalEm1, array $originalEm2, array $expectedEm1, array $expectedEm2) { $emConfigs = array( - 'em1'=> $originalEm1, - 'em2'=> $originalEm2, + 'em1' => $originalEm1, + 'em2' => $originalEm2, ); $bundles = array( - 'FristBundle'=> 'My\FristBundle', - 'SecondBundle'=> 'My\SecondBundle', + 'FristBundle' => 'My\FristBundle', + 'SecondBundle' => 'My\SecondBundle', ); $reflection = new \ReflectionClass(get_class($this->extension)); @@ -163,10 +163,10 @@ public function testFixManagersAutoMappings(array $originalEm1, array $originalE $newEmConfigs = $method->invoke($this->extension, $emConfigs, $bundles); $this->assertEquals($newEmConfigs["em1"], array_merge(array( - 'auto_mapping' => false + 'auto_mapping' => false, ), $expectedEm1)); $this->assertEquals($newEmConfigs["em2"], array_merge(array( - 'auto_mapping' => false + 'auto_mapping' => false, ), $expectedEm2)); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php index 706263ca4ddbd..d0f60c84260a9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php @@ -141,5 +141,4 @@ private function hardCopy($originDir, $targetDir) // We use a custom iterator to ignore VCS files $filesystem->mirror($originDir, $targetDir, Finder::create()->ignoreDotFiles(false)->in($originDir)); } - } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php index 0157bfc7f072b..7a345938bd02d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -189,16 +189,16 @@ abstract protected function describeContainerParameter($parameter, array $option * Common options are: * * name: name of listened event * - * @param EventDispatcherInterface $eventDispatcher - * @param array $options + * @param EventDispatcherInterface $eventDispatcher + * @param array $options */ abstract protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()); /** * Describes a callable. * - * @param callable $callable - * @param array $options + * @param callable $callable + * @param array $options */ abstract protected function describeCallable($callable, array $options = array()); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index 11bc719203ce1..5dd2d405ee5c0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -242,7 +242,7 @@ private function getContainerAliasData(Alias $alias) /** * @param EventDispatcherInterface $eventDispatcher - * @param string|null $event + * @param string|null $event * * @return array */ @@ -270,7 +270,7 @@ private function getEventDispatcherListenersData(EventDispatcherInterface $event /** * @param callable $callable - * @param array $options + * @param array $options * * @return array */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index b1c85d1ae6df2..146ea2979d062 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -412,8 +412,8 @@ private function getContainerParameterDocument($parameter, $options = array()) } /** - * @param EventDispatcherInterface $eventDispatcher - * @param string|null $event + * @param EventDispatcherInterface $eventDispatcher + * @param string|null $event * * @return \DOMDocument */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php index 29e2e182faf41..8cccd82635906 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php @@ -122,6 +122,7 @@ public function build($controller) * Attempts to find a bundle that is *similar* to the given bundle name * * @param string $nonExistentBundleName + * * @return string */ private function findAlternative($nonExistentBundleName) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php index d10522e1246e0..01ac91ce2ce9d 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php @@ -12,9 +12,7 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; use Symfony\Component\Config\Definition\Builder\NodeDefinition; - use Symfony\Component\DependencyInjection\DefinitionDecorator; - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; diff --git a/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php b/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php index de04d1343f23a..cbf27da4e23f9 100644 --- a/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php +++ b/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php @@ -43,7 +43,7 @@ public function __construct($resource, $sourceResource = null, $code = null, $pr if (null === $sourceResource) { $message .= sprintf('(which is loaded in resource "%s")', $this->varToString($resource)); } else { - $message .= sprintf('(which is being imported from "%s")',$this->varToString($sourceResource)); + $message .= sprintf('(which is being imported from "%s")', $this->varToString($sourceResource)); } $message .= '.'; @@ -51,14 +51,14 @@ public function __construct($resource, $sourceResource = null, $code = null, $pr } elseif (null === $sourceResource) { $message .= sprintf('Cannot load resource "%s".', $this->varToString($resource)); } else { - $message .= sprintf('Cannot import resource "%s" from "%s".',$this->varToString($resource),$this->varToString($sourceResource)); + $message .= sprintf('Cannot import resource "%s" from "%s".', $this->varToString($resource), $this->varToString($sourceResource)); } // Is the resource located inside a bundle? if ('@' === $resource[0]) { $parts = explode(DIRECTORY_SEPARATOR, $resource); $bundle = substr($parts[0], 1); - $message .= ' '.sprintf('Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.',$bundle); + $message .= ' '.sprintf('Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle); } parent::__construct($message, $code, $previous); diff --git a/src/Symfony/Component/Console/Helper/ProgressBar.php b/src/Symfony/Component/Console/Helper/ProgressBar.php index d2cbca5362d97..893664e412f54 100644 --- a/src/Symfony/Component/Console/Helper/ProgressBar.php +++ b/src/Symfony/Component/Console/Helper/ProgressBar.php @@ -382,7 +382,7 @@ public function setOverwrite($overwrite) /** * Sets the current progress. * - * @param int $step The current progress + * @param int $step The current progress * * @throws \LogicException */ diff --git a/src/Symfony/Component/CssSelector/Node/Specificity.php b/src/Symfony/Component/CssSelector/Node/Specificity.php index d0c215e0f2edd..cd5bfe3695362 100644 --- a/src/Symfony/Component/CssSelector/Node/Specificity.php +++ b/src/Symfony/Component/CssSelector/Node/Specificity.php @@ -81,6 +81,7 @@ public function getValue() * 0 if they are equal, and 1 if the argument is lower * * @param Specificity $specificity + * * @return int */ public function compareTo(Specificity $specificity) diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 458cb317717d2..48e4a2d69e737 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -338,7 +338,7 @@ private function reRegister($prev) /** * Handles errors by filtering then logging them according to the configured bit fields. * - * @param int $type One of the E_* constants + * @param int $type One of the E_* constants * @param string $file * @param int $line * @param array $context @@ -584,7 +584,7 @@ protected function getFatalErrorHandlers() /** * Sets the level at which the conversion to Exception is done. * - * @param int|null $level The level (null to use the error_reporting() value and 0 to disable) + * @param int|null $level The level (null to use the error_reporting() value and 0 to disable) * * @deprecated since 2.6, to be removed in 3.0. Use throwAt() instead. */ @@ -597,7 +597,7 @@ public function setLevel($level) /** * Sets the display_errors flag value. * - * @param int $displayErrors The display_errors flag value + * @param int $displayErrors The display_errors flag value * * @deprecated since 2.6, to be removed in 3.0. Use throwAt() instead. */ diff --git a/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php index bb3ff3a8d85a8..6f2fbcb9df9ad 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -22,6 +22,7 @@ class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest protected function createEventDispatcher() { $container = new Container(); + return new ContainerAwareEventDispatcher($container); } diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php b/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php index c2a3be83be076..7222261cd5386 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php @@ -41,25 +41,25 @@ class ExpressionFunction * @param callable $compiler A callable able to compile the function * @param callable $evaluator A callable able to evaluate the function */ - public function __construct($name, $compiler, $evaluator) - { - $this->name = $name; - $this->compiler = $compiler; - $this->evaluator = $evaluator; - } + public function __construct($name, $compiler, $evaluator) + { + $this->name = $name; + $this->compiler = $compiler; + $this->evaluator = $evaluator; + } - public function getName() - { - return $this->name; - } + public function getName() + { + return $this->name; + } - public function getCompiler() - { - return $this->compiler; - } + public function getCompiler() + { + return $this->compiler; + } - public function getEvaluator() - { - return $this->evaluator; - } + public function getEvaluator() + { + return $this->evaluator; + } } diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php index b76bed96797a3..42ed9eb1d0e6a 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php @@ -32,7 +32,7 @@ class ExpressionLanguage protected $functions = array(); /** - * @param ParserCacheInterface $cache + * @param ParserCacheInterface $cache * @param ExpressionFunctionProviderInterface[] $providers */ public function __construct(ParserCacheInterface $cache = null, array $providers = array()) diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php index fd566499f7466..15a0ea648d4b9 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php @@ -57,7 +57,7 @@ public function testConstantFunction() public function testProviders() { - $expressionLanguage = new ExpressionLanguage(null, array(new TestProvider)); + $expressionLanguage = new ExpressionLanguage(null, array(new TestProvider())); $this->assertEquals('foo', $expressionLanguage->evaluate('identity("foo")')); $this->assertEquals('"foo"', $expressionLanguage->compile('identity("foo")')); } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index eb4868dd0b254..34f257aed88c6 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -290,6 +290,7 @@ public function symlink($originDir, $targetDir, $copyOnWindows = false) if ($onWindows && $copyOnWindows) { $this->mirror($originDir, $targetDir); + return; } diff --git a/src/Symfony/Component/HttpFoundation/ParameterBag.php b/src/Symfony/Component/HttpFoundation/ParameterBag.php index 6f7f12623f07b..348aab96853f1 100644 --- a/src/Symfony/Component/HttpFoundation/ParameterBag.php +++ b/src/Symfony/Component/HttpFoundation/ParameterBag.php @@ -256,11 +256,11 @@ public function getInt($key, $default = 0, $deep = false) /** * Returns the parameter value converted to boolean. * - * @param string $key The parameter key - * @param mixed $default The default value if the parameter key does not exist - * @param bool $deep If true, a path like foo[bar] will find deeper items + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param bool $deep If true, a path like foo[bar] will find deeper items * - * @return bool The filtered value + * @return bool The filtered value */ public function getBoolean($key, $default = false, $deep = false) { diff --git a/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php b/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php index c7f2daf218f1c..4ba9f58da280c 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php @@ -28,8 +28,8 @@ class DumpListener implements EventSubscriberInterface private $dumper; /** - * @param ClonerInterface $cloner Cloner service. - * @param DataDumperInterface $dumper Dumper service. + * @param ClonerInterface $cloner Cloner service. + * @param DataDumperInterface $dumper Dumper service. */ public function __construct(ClonerInterface $cloner, DataDumperInterface $dumper) { diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Esi.php b/src/Symfony/Component/HttpKernel/HttpCache/Esi.php index 0ae793bbeeb4c..ad8382e55234d 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/Esi.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/Esi.php @@ -73,7 +73,7 @@ public function hasSurrogateCapability(Request $request) * * @param Request $request A Request instance * - * @return bool true if one surrogate has ESI/1.0 capability, false otherwise + * @return bool true if one surrogate has ESI/1.0 capability, false otherwise * * @deprecated Deprecated since version 2.6, to be removed in 3.0. Use hasSurrogateCapability() instead */ @@ -142,7 +142,7 @@ public function needsParsing(Response $response) * * @param Response $response A Response instance * - * @return bool true if the Response needs to be parsed, false otherwise + * @return bool true if the Response needs to be parsed, false otherwise * * @deprecated Deprecated since version 2.6, to be removed in 3.0. Use needsParsing() instead */ diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 99c4f2c758bc3..9d1cbfa9ac89a 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -167,6 +167,7 @@ public function getSurrogate() * Gets the Esi instance * * @throws \LogicException + * * @return Esi An Esi instance * * @deprecated Deprecated since version 2.6, to be removed in 3.0. Use getSurrogate() instead diff --git a/src/Symfony/Component/HttpKernel/HttpCache/SurrogateInterface.php b/src/Symfony/Component/HttpKernel/HttpCache/SurrogateInterface.php index c26304dfdf25a..46b788d53eb08 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/SurrogateInterface.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/SurrogateInterface.php @@ -35,7 +35,7 @@ public function createCacheStrategy(); * * @param Request $request A Request instance * - * @return bool true if one surrogate has Surrogate capability, false otherwise + * @return bool true if one surrogate has Surrogate capability, false otherwise */ public function hasSurrogateCapability(Request $request); @@ -60,17 +60,17 @@ public function addSurrogateControl(Response $response); * * @param Response $response A Response instance * - * @return bool true if the Response needs to be parsed, false otherwise + * @return bool true if the Response needs to be parsed, false otherwise */ public function needsParsing(Response $response); /** * Renders a Surrogate tag. * - * @param string $uri A URI - * @param string $alt An alternate URI - * @param bool $ignoreErrors Whether to ignore errors or not - * @param string $comment A comment to add as an esi:include tag + * @param string $uri A URI + * @param string $alt An alternate URI + * @param bool $ignoreErrors Whether to ignore errors or not + * @param string $comment A comment to add as an esi:include tag * * @return string */ diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 3d5387ee5bd2e..967a739286ede 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -29,7 +29,8 @@ public function testDump() $this->assertSame('dump', $collector->getName()); - $collector->dump($data); $line = __LINE__; + $collector->dump($data); + $line = __LINE__; $this->assertSame(1, $collector->getDumpsCount()); $dump = $collector->getDumps('html'); @@ -61,7 +62,8 @@ public function testFlush() { $data = new Data(array(array(456))); $collector = new DumpDataCollector(); - $collector->dump($data); $line = __LINE__; + $collector->dump($data); + $line = __LINE__; ob_start(); $collector = null; diff --git a/src/Symfony/Component/Process/Pipes/UnixPipes.php b/src/Symfony/Component/Process/Pipes/UnixPipes.php index 69f31467a03cc..6150d4a709487 100644 --- a/src/Symfony/Component/Process/Pipes/UnixPipes.php +++ b/src/Symfony/Component/Process/Pipes/UnixPipes.php @@ -121,7 +121,7 @@ public function readAndWrite($blocking, $close = false) $r = $this->pipes; } // discard read on stdin - unset ($r[0]); + unset($r[0]); $w = isset($this->pipes[0]) ? array($this->pipes[0]) : null; $e = null; diff --git a/src/Symfony/Component/Routing/Router.php b/src/Symfony/Component/Routing/Router.php index f27437309294e..c7c926d43c410 100644 --- a/src/Symfony/Component/Routing/Router.php +++ b/src/Symfony/Component/Routing/Router.php @@ -21,7 +21,6 @@ use Symfony\Component\Routing\Matcher\UrlMatcherInterface; use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; /** diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php index 5d5109090e3f1..43ca558f5f159 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php @@ -54,7 +54,7 @@ public function supportsClass($class) * @param object $object The object to secure * @param array $attributes An array of attributes associated with the method being invoked * - * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED + * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED */ public function vote(TokenInterface $token, $object, array $attributes) { @@ -85,14 +85,14 @@ public function vote(TokenInterface $token, $object, array $attributes) /** * Return an array of supported classes. This will be called by supportsClass * - * @return array an array of supported classes, i.e. array('Acme\DemoBundle\Model\Product') + * @return array an array of supported classes, i.e. array('Acme\DemoBundle\Model\Product') */ abstract protected function getSupportedClasses(); /** * Return an array of supported attributes. This will be called by supportsAttribute * - * @return array an array of supported attributes, i.e. array('CREATE', 'READ') + * @return array an array of supported attributes, i.e. array('CREATE', 'READ') */ abstract protected function getSupportedAttributes(); diff --git a/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php b/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php index 38763dcf3515e..317c8a00ba4e8 100644 --- a/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php +++ b/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php @@ -38,7 +38,8 @@ public function __construct(RequestStack $requestStack) /** * @param bool $clearSession - * @return null|AuthenticationException + * + * @return AuthenticationException|null */ public function getLastAuthenticationError($clearSession = true) { diff --git a/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationFailureHandler.php b/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationFailureHandler.php index 35bfc05c066f7..36d4a78d6dc8e 100644 --- a/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationFailureHandler.php +++ b/src/Symfony/Component/Security/Http/Authentication/CustomAuthenticationFailureHandler.php @@ -24,8 +24,8 @@ class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler /** * Constructor. * - * @param AuthenticationFailureHandlerInterface $handler An AuthenticationFailureHandlerInterface instance - * @param array $options Options for processing a successful authentication attempt + * @param AuthenticationFailureHandlerInterface $handler An AuthenticationFailureHandlerInterface instance + * @param array $options Options for processing a successful authentication attempt */ public function __construct(AuthenticationFailureHandlerInterface $handler, array $options) { diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php index 4a33ed1fa6cd3..edc27a190152e 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php @@ -183,7 +183,7 @@ public function testOnCoreSecurity() public function testOnCoreSecurityInteractiveLoginEventIsDispatchedIfDispatcherIsPresent() { - list($listener, $context, $service, $manager,, $dispatcher) = $this->getListener(true); + list($listener, $context, $service, $manager, , $dispatcher) = $this->getListener(true); $context ->expects($this->once()) diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php index 2bc1ad6fe2715..6e6b979bd24eb 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php @@ -19,7 +19,7 @@ class RemoteUserAuthenticationListenerTest extends \PHPUnit_Framework_TestCase public function testGetPreAuthenticatedData() { $serverVars = array( - 'REMOTE_USER' => 'TheUser' + 'REMOTE_USER' => 'TheUser', ); $request = new Request(array(), array(), array(), array(), array(), $serverVars); @@ -69,7 +69,7 @@ public function testGetPreAuthenticatedDataWithDifferentKeys() $userCredentials = array('TheUser', null); $request = new Request(array(), array(), array(), array(), array(), array( - 'TheUserKey' => 'TheUser' + 'TheUserKey' => 'TheUser', )); $context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'); diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index 428af1a9dccd5..c8e157222479e 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -180,7 +180,7 @@ public function supportsDenormalization($data, $type, $format = null) /** * Format an attribute name, for example to convert a snake_case name to camelCase. * - * @param string $attributeName + * @param string $attributeName * * @return string */ diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php index b398b75efb0a4..a954775a4a5b8 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php @@ -151,12 +151,12 @@ public function provideCallbacks() array( array( 'bar' => function ($bar) { - return null; + return; }, ), 'baz', array('foo' => '', 'bar' => null), - 'Null an item' + 'Null an item', ), array( array( diff --git a/src/Symfony/Component/Translation/LoggingTranslator.php b/src/Symfony/Component/Translation/LoggingTranslator.php index ae9be1f28fb42..a5d244a1e2ba4 100644 --- a/src/Symfony/Component/Translation/LoggingTranslator.php +++ b/src/Symfony/Component/Translation/LoggingTranslator.php @@ -47,7 +47,7 @@ public function __construct($translator, LoggerInterface $logger) */ public function trans($id, array $parameters = array(), $domain = null, $locale = null) { - $trans = $this->translator->trans($id, $parameters , $domain , $locale); + $trans = $this->translator->trans($id, $parameters, $domain, $locale); $this->log($id, $domain, $locale); return $trans; diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index 240f925b6c067..875c0f9cf53bc 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -172,8 +172,8 @@ abstract protected function doClone($var); /** * Casts an object to an array representation. * - * @param Stub $stub The Stub for the casted object. - * @param bool $isNested True if the object is nested in the dumped structure. + * @param Stub $stub The Stub for the casted object. + * @param bool $isNested True if the object is nested in the dumped structure. * * @return array The object casted as array. */ @@ -223,8 +223,8 @@ protected function castObject(Stub $stub, $isNested) /** * Casts a resource to an array representation. * - * @param Stub $stub The Stub for the casted resource. - * @param bool $isNested True if the object is nested in the dumped structure. + * @param Stub $stub The Stub for the casted resource. + * @param bool $isNested True if the object is nested in the dumped structure. * * @return array The resource casted as array. */ diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 00c613dfdc45f..81e856fdf3370 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -41,10 +41,10 @@ public function __construct($offset = 0) /** * Parses a YAML string to a PHP value. * - * @param string $value A YAML string - * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise - * @param bool $objectSupport true if object support is enabled, false otherwise - * @param bool $objectForMap true if maps should return a stdClass instead of array() + * @param string $value A YAML string + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * @param bool $objectForMap true if maps should return a stdClass instead of array() * * @return mixed A PHP value * From 42950e9e71698faae18d17d14a3cf7e5fe7962ed Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 19:20:24 -0500 Subject: [PATCH 363/373] Adding note about the PdoSessionHandler BC break --- UPGRADE-2.6.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/UPGRADE-2.6.md b/UPGRADE-2.6.md index fe0dc4839cca1..bc36f6c3f7989 100644 --- a/UPGRADE-2.6.md +++ b/UPGRADE-2.6.md @@ -106,6 +106,20 @@ HttpFoundation -------------- * The `PdoSessionHandler` to store sessions in a database changed significantly. + This introduced a **backwards-compatability** break in the schema of the + session table. The following changes must be made to your session table: + + - Add a new integer column called `sess_lifetime`. Assuming you have the + default column and table names, in MySQL this would be: + ALTER TABLE `session` ADD `sess_lifetime` INT NOT NULL ; + - Change the data column (default: `sess_value`) to be a Blob type. In + MySQL this would be: + ALTER TABLE `session` CHANGE `sess_value` `session_value` BLOB NOT NULL; + + There is also an [issue](https://github.com/symfony/symfony/issues/12834) + that affects Windows servers. + + The changes to the `PdoSessionHandler` are: - By default, it now implements session locking to prevent loss of data by concurrent access to the same session. - It does so using a transaction between opening and closing a session. For this reason, it's not recommended to use the same database connection that you also use for your application logic. From adfdad9c38c8435465c585307cf6a55571302e91 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 19:23:51 -0500 Subject: [PATCH 364/373] Adding note about known BC issues --- UPGRADE-2.6.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/UPGRADE-2.6.md b/UPGRADE-2.6.md index bc36f6c3f7989..265f9a44f511e 100644 --- a/UPGRADE-2.6.md +++ b/UPGRADE-2.6.md @@ -1,6 +1,12 @@ UPGRADE FROM 2.5 to 2.6 ======================= +Known Backwards-Compatability Breaks +------------------------------------ + +* If you use the `PdoSessionHandler`, the session table now has a different + schema and must be modified. Look below for more details. + Form ---- From 8d7581af3a83d176cb1ba81cd6fd9655f7726b5e Mon Sep 17 00:00:00 2001 From: Matthias Althaus Date: Mon, 8 Dec 2014 12:17:16 +0100 Subject: [PATCH 365/373] Fixed typo in SecurityContext PHPDoc Fixed typo in PHPDoc --- src/Symfony/Component/Security/Core/SecurityContext.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Core/SecurityContext.php b/src/Symfony/Component/Security/Core/SecurityContext.php index 1f46cd6332814..545d4cbd85023 100644 --- a/src/Symfony/Component/Security/Core/SecurityContext.php +++ b/src/Symfony/Component/Security/Core/SecurityContext.php @@ -41,7 +41,7 @@ class SecurityContext implements SecurityContextInterface private $authorizationChecker; /** - * For backwords compatibility, the signature of sf <2.6 still works + * For backwards compatibility, the signature of sf <2.6 still works * * @param TokenStorageInterface|AuthenticationManagerInterface $tokenStorage * @param AuthorizationCheckerInterface|AccessDecisionManagerInterface $authorizationChecker From e7b0b899e0c49060b48c12db5ef7a99a579fa681 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 8 Dec 2014 17:47:47 +0100 Subject: [PATCH 366/373] avoid risky tests --- .../TwigBundle/Tests/Controller/ExceptionControllerTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php index 4a17902ae9f19..72f2f728d9663 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php @@ -52,6 +52,7 @@ public function testShowActionCanBeForcedToShowErrorPage() ); $request = Request::create('whatever', 'GET'); + $request->headers->set('X-Php-Ob-Level', 1); $request->attributes->set('showException', false); $exception = FlattenException::create(new \Exception(), 404); $controller = new ExceptionController($twig, /* "showException" defaults to --> */ true); @@ -71,6 +72,7 @@ public function testFallbackToHtmlIfNoTemplateForRequestedFormat() ); $request = Request::create('whatever'); + $request->headers->set('X-Php-Ob-Level', 1); $request->setRequestFormat('txt'); $exception = FlattenException::create(new \Exception()); $controller = new ExceptionController($twig, false); From 1917b703c62d5f3b2b93260c3e58ae60db7f579b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 8 Dec 2014 18:00:45 +0100 Subject: [PATCH 367/373] fix DumpDataCollectorTest after CS changes Modify the expected line value which was affected by the move of the `__LINE__` constant in #12872 to make tests pass again. --- .../HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 967a739286ede..5c8fdb6ed1788 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -30,7 +30,7 @@ public function testDump() $this->assertSame('dump', $collector->getName()); $collector->dump($data); - $line = __LINE__; + $line = __LINE__ - 1; $this->assertSame(1, $collector->getDumpsCount()); $dump = $collector->getDumps('html'); @@ -63,7 +63,7 @@ public function testFlush() $data = new Data(array(array(456))); $collector = new DumpDataCollector(); $collector->dump($data); - $line = __LINE__; + $line = __LINE__ - 1; ob_start(); $collector = null; From 90b3f3b0402deaa8709832d6f7cd3f53d31fb64b Mon Sep 17 00:00:00 2001 From: Massimiliano Arione Date: Tue, 9 Dec 2014 10:58:23 +0100 Subject: [PATCH 368/373] Fix missing space in label_attr ``label_attr`` in ``checkbox_radio_label`` is missing a space with ``parent_label_class`` --- .../Twig/Resources/views/Form/bootstrap_3_layout.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index 99c04d8a703e5..0e4da9d0eed6f 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -160,7 +160,7 @@ {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %} {% endif %} {% if parent_label_class is defined %} - {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ parent_label_class)|trim}) %} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ parent_label_class)|trim}) %} {% endif %} {% if label is empty %} {% set label = name|humanize %} From 6c70bc5211c392678781d05e4f88b4e9499d0839 Mon Sep 17 00:00:00 2001 From: Luca Genuzio Date: Tue, 9 Dec 2014 13:45:31 +0100 Subject: [PATCH 369/373] Fix missing addExpressionLanguageProvider (used by service container to add expression providers) --- .../Security/Core/Authorization/Voter/ExpressionVoter.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php index 32638036da6bd..98b8f50f15d03 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php @@ -15,6 +15,7 @@ use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; use Symfony\Component\Security\Core\Authorization\ExpressionLanguage; use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\HttpFoundation\Request; @@ -43,6 +44,11 @@ public function __construct(ExpressionLanguage $expressionLanguage, Authenticati $this->roleHierarchy = $roleHierarchy; } + public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) + { + $this->expressionLanguage->registerProvider($provider); + } + /** * {@inheritdoc} */ From 16a51e7ca3bc4a95eb420a19eda7e069868688b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Egyed?= <1ed@mailbox.hu> Date: Mon, 8 Dec 2014 15:17:45 +0100 Subject: [PATCH 370/373] [WebProfiler] Tweaked ajax requests toolbar css reset --- .../Resources/views/Profiler/toolbar.css.twig | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index e040fff53e6f8..c4d0280d81506 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -51,14 +51,25 @@ cursor: help; } .sf-toolbarreset span, -.sf-toolbarreset div { +.sf-toolbarreset div, +.sf-toolbarreset td, +.sf-toolbarreset th { font-size: 11px; } .sf-toolbarreset img { width: auto; display: inline; } - +.sf-toolbarreset table { + border-collapse: collapse; + border-spacing: 0; + background-color: #000; + margin: 0; + padding: 0; + border: 0; + width: 100%; + table-layout: auto; +} .sf-toolbarreset .hide-button { display: block; position: absolute; @@ -289,12 +300,11 @@ overflow-y: auto; } -table.sf-toolbar-ajax-requests { - border-collapse: collapse; -} .sf-toolbar-ajax-requests th, .sf-toolbar-ajax-requests td { border-bottom: 1px solid #ddd; padding: 0 4px; + color: #2f2f2f; + background-color: #fff; } .sf-toolbar-ajax-requests th { background-color: #eee; From fb87558881f7b0bd50e6aab5beda1d3bd0ce44c5 Mon Sep 17 00:00:00 2001 From: thewilkybarkid Date: Fri, 12 Dec 2014 14:38:45 +0000 Subject: [PATCH 371/373] Avoid missing method when using __invoke --- .../Resources/views/Collector/request.html.twig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig index 8f1d1332bedd5..42ff85fae549b 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig @@ -4,10 +4,14 @@ {% set request_handler %} {% if collector.controller.class is defined %} {% set link = collector.controller.file|file_link(collector.controller.line) %} - {{ collector.controller.class|abbr_class }} - - {{ collector.controller.method }} - + {% if collector.controller.method %} + {{ collector.controller.class|abbr_class }} + + {{ collector.controller.method }} + + {% else %} + {{ collector.controller.class|abbr_class }} + {% endif %} {% else %} {{ collector.controller }} {% endif %} From 9a44aa85ac021354f34b4e1bdae0ee6384078e1c Mon Sep 17 00:00:00 2001 From: Mantas Varatiejus Date: Mon, 15 Dec 2014 20:18:43 +0200 Subject: [PATCH 372/373] Fix placeholder date format --- .../Resources/views/Profiler/search.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig index 8f06fa43e5294..b94853cc58eb4 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig @@ -21,10 +21,10 @@
    - +
    - +