Skip to content

Commit ca653aa

Browse files
committed
Merge branch '3.4' into 4.0
* 3.4: Update sessions.rst Removed some repeated content and minor rewords doc(testing/http_authentication.rst); Update locale_sticky_session.rst Undefined variable $userName in example [Cache] minor tweaks Fixed the code of the custom password authenticator example Documented PHPUnit data providers
2 parents cef13f8 + 3fb163c commit ca653aa

12 files changed

+169
-123
lines changed

best_practices/tests.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ functional tests, you can quickly spot any big errors before you deploy them:
2727
Define a functional test that at least checks if your application pages
2828
are successfully loading.
2929

30-
A functional test can be as easy as this::
30+
A functional test like this is simple to implement thanks to
31+
:ref:`PHPUnit data providers <testing-data-providers>`::
3132

3233
// tests/ApplicationAvailabilityFunctionalTest.php
3334
namespace App\Tests;

components/cache.rst

+4-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ The Cache Component
1010

1111
The Cache component provides an extended `PSR-6`_ implementation as well as
1212
a `PSR-16`_ "Simple Cache" implementation for adding cache to your applications.
13-
It is designed to have a low overhead and it ships with ready to use adapters
14-
for the most common caching backends.
13+
It is designed for performance and resiliency, and ships with ready to use
14+
adapters for the most common caching backends, including proxies for adapting
15+
from/to `Doctrine Cache`_.
1516

1617
Installation
1718
------------
@@ -189,4 +190,4 @@ Advanced Usage (PSR-6)
189190

190191
.. _`PSR-6`: http://www.php-fig.org/psr/psr-6/
191192
.. _`PSR-16`: http://www.php-fig.org/psr/psr-16/
192-
.. _Packagist: https://packagist.org/packages/symfony/cache
193+
.. _Doctrine Cache: https://www.doctrine-project.org/projects/cache.html

components/cache/adapters/pdo_doctrine_dbal_adapter.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ third, and forth parameters::
2727
// until the database table is truncated or its rows are otherwise deleted)
2828
$defaultLifetime = 0,
2929

30-
// an array of options for configuring the database connection
30+
// an array of options for configuring the database table and connection
3131
$options = array()
3232
);
3333

components/cache/cache_invalidation.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ several cached items, keeping them in sync can be difficult.
1212

1313
The Symfony Cache component provides two mechanisms to help solving this problem:
1414

15-
* :ref:`Tags based invalidation <cache-component-tags>` for managing data dependencies;
15+
* :ref:`Tags-based invalidation <cache-component-tags>` for managing data dependencies;
1616
* :ref:`Expiration based invalidation <cache-component-expiration>` for time related dependencies.
1717

1818
.. _cache-component-tags:
1919

2020
Using Cache Tags
2121
----------------
2222

23-
To benefit from tags based invalidation, you need to attach the proper tags to
23+
To benefit from tags-based invalidation, you need to attach the proper tags to
2424
each cached item. Each tag is a plain string identifier that you can use at any
2525
time to trigger the removal of all items associated with this tag.
2626

components/cache/cache_pools.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ allowing manual removal of stale cache items::
170170

171171
The :ref:`ChainAdapter <component-cache-chain-adapter>` implementation does not directly
172172
contain any pruning logic itself. Instead, when calling the chain adapter's
173-
:method:`Symfony\\Component\\Cache\\ChainAdapter::prune` method, the call is delegated to all
173+
:method:`Symfony\\Component\\Cache\\Adapter\\ChainAdapter::prune` method, the call is delegated to all
174174
its compatible cache adapters (and those that do not implement ``PruneableInterface`` are
175175
silently ignored)::
176176

components/http_foundation/sessions.rst

+57-50
Original file line numberDiff line numberDiff line change
@@ -94,29 +94,12 @@ Session Workflow
9494
Session Attributes
9595
..................
9696

97-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::set`
98-
Sets an attribute by key.
97+
The session attributes are stored internally in a "Bag", a PHP object that acts
98+
like an array. They can be set, removed, checked, etc. using the methods
99+
explained later in this article for the ``AttributeBagInterface`` class. See
100+
:ref:`attribute-bag-interface`.
99101

100-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::get`
101-
Gets an attribute by key.
102-
103-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::all`
104-
Gets all attributes as an array of key => value.
105-
106-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::has`
107-
Returns true if the attribute exists.
108-
109-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::replace`
110-
Sets multiple attributes at once: takes a keyed array and sets each key => value pair.
111-
112-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::remove`
113-
Deletes an attribute by key.
114-
115-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::clear`
116-
Clear all attributes.
117-
118-
The attributes are stored internally in a "Bag", a PHP object that acts like
119-
an array. A few methods exist for "Bag" management:
102+
In addition, a few methods exist for "Bag" management:
120103

121104
:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::registerBag`
122105
Registers a :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface`.
@@ -168,19 +151,65 @@ the following API which is intended mainly for internal purposes:
168151
:method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::getName`
169152
Returns the name of the session bag.
170153

154+
.. _attribute-bag-interface:
155+
171156
Attributes
172157
~~~~~~~~~~
173158

174159
The purpose of the bags implementing the :class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface`
175160
is to handle session attribute storage. This might include things like user ID,
176-
and remember me login settings or other user based state information.
161+
and "Remember Me" login settings or other user based state information.
177162

178163
:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBag`
179164
This is the standard default implementation.
180165

181166
:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\NamespacedAttributeBag`
182167
This implementation allows for attributes to be stored in a structured namespace.
183168

169+
:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface`
170+
has a simple API
171+
172+
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::set`
173+
Sets an attribute by name (``set('name', 'value')``).
174+
175+
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::get`
176+
Gets an attribute by name (``get('name')``) and can define a default
177+
value when the attribute doesn't exist (``get('name', 'default_value')``).
178+
179+
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::all`
180+
Gets all attributes as an associative array of ``name => value``.
181+
182+
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::has`
183+
Returns ``true`` if the attribute exists.
184+
185+
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::replace`
186+
Sets multiple attributes at once using an associative array (``name => value``).
187+
If the attributes existed, they are replaced; if not, they are created.
188+
189+
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::remove`
190+
Deletes an attribute by name and returns its value.
191+
192+
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::clear`
193+
Deletes all attributes.
194+
195+
Example::
196+
197+
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
198+
use Symfony\Component\HttpFoundation\Session\Session;
199+
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
200+
201+
$session = new Session(new NativeSessionStorage(), new AttributeBag());
202+
$session->set('token', 'a6c1e0b6');
203+
// ...
204+
$token = $session->get('token');
205+
// if the attribute may or may not exist, you can define a default value for it
206+
$token = $session->get('attribute-name', 'default-attribute-value');
207+
// ...
208+
$session->clear();
209+
210+
Namespaced Attributes
211+
.....................
212+
184213
Any plain key-value storage system is limited in the extent to which
185214
complex data can be stored since each key must be unique. You can achieve
186215
namespacing by introducing a naming convention to the keys so different parts of
@@ -205,35 +234,13 @@ the array::
205234
$session->set('tokens', $tokens);
206235

207236
With structured namespacing, the key can be translated to the array
208-
structure like this using a namespace character (defaults to ``/``)::
209-
210-
$session->set('tokens/c', $value);
211-
212-
This way you can easily access a key within the stored array directly and easily.
213-
214-
:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface`
215-
has a simple API
216-
217-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::set`
218-
Sets an attribute by key.
219-
220-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::get`
221-
Gets an attribute by key.
222-
223-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::all`
224-
Gets all attributes as an array of key => value.
225-
226-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::has`
227-
Returns true if the attribute exists.
228-
229-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::replace`
230-
Sets multiple attributes at once: takes a keyed array and sets each key => value pair.
237+
structure like this using a namespace character (which defaults to ``/``)::
231238

232-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::remove`
233-
Deletes an attribute by key.
239+
// ...
240+
use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
234241

235-
:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::clear`
236-
Clear the bag.
242+
$session = new Session(new NativeSessionStorage(), new NamespacedAttributeBag());
243+
$session->set('tokens/c', $value);
237244

238245
Flash Messages
239246
~~~~~~~~~~~~~~

form/unit_testing.rst

+5-53
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ widgets you want to display are available in the children property::
107107
$this->assertArrayHasKey($key, $children);
108108
}
109109

110+
.. tip::
111+
112+
Use :ref:`PHPUnit data providers <testing-data-providers>` to test multiple
113+
form conditions using the same test code.
114+
110115
Testings Types from the Service Container
111116
-----------------------------------------
112117

@@ -213,56 +218,3 @@ guessers using the :method:`Symfony\\Component\\Form\\Test\\FormIntegrationTestC
213218
:method:`Symfony\\Component\\Form\\Test\\FormIntegrationTestCase::getTypeExtensions`
214219
and :method:`Symfony\\Component\\Form\\Test\\FormIntegrationTestCase::getTypeGuessers`
215220
methods.
216-
217-
Testing against Different Sets of Data
218-
--------------------------------------
219-
220-
If you are not familiar yet with PHPUnit's `data providers`_, this might be
221-
a good opportunity to use them::
222-
223-
// tests/Form/Type/TestedTypeTest.php
224-
namespace App\Tests\Form\Type;
225-
226-
use App\Form\Type\TestedType;
227-
use Symfony\Component\Form\Test\TypeTestCase;
228-
229-
class TestedTypeTest extends TypeTestCase
230-
{
231-
/**
232-
* @dataProvider getValidTestData
233-
*/
234-
public function testForm($data)
235-
{
236-
// ... your test
237-
}
238-
239-
public function getValidTestData()
240-
{
241-
return array(
242-
array(
243-
'data' => array(
244-
'test' => 'test',
245-
'test2' => 'test2',
246-
),
247-
),
248-
array(
249-
'data' => array(),
250-
),
251-
array(
252-
'data' => array(
253-
'test' => null,
254-
'test2' => null,
255-
),
256-
),
257-
);
258-
}
259-
}
260-
261-
The code above will run your test three times with 3 different sets of
262-
data. This allows for decoupling the test fixtures from the tests and
263-
easily testing against multiple sets of data.
264-
265-
You can also pass another argument, such as a boolean if the form has to
266-
be synchronized with the given set of data or not etc.
267-
268-
.. _`data providers`: https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers

security/custom_password_authenticator.rst

+15-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ the user::
2828
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
2929
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
3030
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
31+
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
3132
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
3233
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
3334
use Symfony\Component\Security\Core\User\UserProviderInterface;
@@ -52,7 +53,20 @@ the user::
5253
throw new CustomUserMessageAuthenticationException('Invalid username or password');
5354
}
5455

55-
$isPasswordValid = $this->encoder->isPasswordValid($user, $token->getCredentials());
56+
$currentUser = $token->getUser();
57+
58+
if ($currentUser instanceof UserInterface) {
59+
if ($currentUser->getPassword() !== $user->getPassword()) {
60+
throw new BadCredentialsException('The credentials were changed from another session.');
61+
}
62+
} else {
63+
if ('' === ($givenPassword = $token->getCredentials())) {
64+
throw new BadCredentialsException('The given password cannot be empty.');
65+
}
66+
if (!$this->encoderFactory->getEncoder($user)->isPasswordValid($user->getPassword(), $givenPassword, $user->getSalt())) {
67+
throw new BadCredentialsException('The given password is invalid.');
68+
}
69+
}
5670

5771
if ($isPasswordValid) {
5872
$currentHour = date('G');

security/custom_provider.rst

+2
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ Here's an example of how this might look::
144144
);
145145
}
146146

147+
$username = $user->getUsername();
148+
147149
return $this->fetchUser($username);
148150
}
149151

session/locale_sticky_session.rst

+42-11
Original file line numberDiff line numberDiff line change
@@ -171,19 +171,50 @@ event::
171171
$this->session->set('_locale', $user->getLocale());
172172
}
173173
}
174-
175-
public static function getSubscribedEvents()
176-
{
177-
return array(
178-
SecurityEvents::INTERACTIVE_LOGIN => array(array('onInteractiveLogin', 15)),
179-
);
180-
}
181174
}
182175

183-
If you're using the :ref:`default services.yaml configuration <service-container-services-load-example>`,
184-
you're done! Symfony will automatically know about the event subscriber will pass
185-
your the ``session`` service. Now, when you login, the user's locale will be set
186-
into the session.
176+
Then register the listener:
177+
178+
.. configuration-block::
179+
180+
.. code-block:: yaml
181+
182+
# config/services.yaml
183+
services:
184+
App\EventListener\UserLocaleListener:
185+
tags:
186+
- { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin, priority: 15 }
187+
188+
.. code-block:: xml
189+
190+
<!-- config/services.xml -->
191+
<?xml version="1.0" encoding="UTF-8" ?>
192+
<container xmlns="http://symfony.com/schema/dic/services"
193+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
194+
xsi:schemaLocation="http://symfony.com/schema/dic/services
195+
http://symfony.com/schema/dic/services/services-1.0.xsd">
196+
197+
<services>
198+
<service id="App\EventListener\UserLocaleListener">
199+
<tag name="kernel.event_listener"
200+
event="security.interactive_login"
201+
method="onInteractiveLogin" priority=15 />
202+
</service>
203+
</services>
204+
</container>
205+
206+
.. code-block:: php
207+
208+
// config/services.php
209+
use AppBundle\EventListener\UserLocaleListener;
210+
use Symfony\Component\DependencyInjection\Reference;
211+
212+
$container
213+
->register(UserLocaleListener::class)
214+
->addTag(
215+
'kernel.event_listener',
216+
array('event' => 'security.interactive_login', 'method' => 'onInteractiveLogin', 'priority' => 15)
217+
);
187218
188219
.. caution::
189220

0 commit comments

Comments
 (0)