8000 Merge branch '3.4' into 4.0 · symfony/symfony-docs@13d112b · GitHub
[go: up one dir, main page]

Skip to content

Commit 13d112b

Browse files
committed
Merge branch '3.4' into 4.0
* 3.4: Missing a dot in the last block of code adding note to config Add "url" key in dbal configuration Update console.rst Fixed the internal references of the serializer docs Minor code correction in guard authentication. Minor reword Update database.rst Review Typo fix: use correct parameter `options` [Serializer] Add docs for attributes context key Fix docs about making external requests with BrowserKit Add Lock component
2 parents 1dbe838 + 2ed36f3 commit 13d112b

File tree

8 files changed

+324
-14
lines changed

8 files changed

+324
-14
lines changed

components/browser_kit.rst

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ The BrowserKit Component
< 8000 /code>
88
The BrowserKit component simulates the behavior of a web browser, allowing
99
you to make requests, click on links and submit forms programmatically.
1010

11+
.. note::
12+
13+
The BrowserKit component can only make internal requests to your application.
14+
If you need to make requests to external sites and applications, consider
15+
using `Goutte`_, a simple web scraper based on Symfony Components.
16+
1117
Installation
1218
------------
1319

@@ -60,7 +66,7 @@ URL::
6066
use Acme\Client;
6167

6268
$client = new Client();
63-
$crawler = $client->request('GET', 'http://symfony.com');
69+
$crawler = $client->request('GET', '/');
6470

6571
The value returned by the ``request()`` method is an instance of the
6672
:class:`Symfony\\Component\\DomCrawler\\Crawler` class, provided by the
@@ -78,7 +84,7 @@ performs the needed HTTP GET request to simulate the link click::
7884
use Acme\Client;
7985

8086
$client = new Client();
81-
$crawler = $client->request('GET', 'http://symfony.com');
87+
$crawler = $client->request('GET', '/product/123');
8288
$link = $crawler->selectLink('Go elsewhere...')->link();
8389
$client->click($link);
8490

@@ -120,7 +126,7 @@ retrieve any cookie while making requests with the client::
120126

121127
// Make a request
122128
$client = new Client();
123-
$crawler = $client->request('GET', 'http://symfony.com');
129+
$crawler = $client->request('GET', '/');
124130

125131
// Get the cookie Jar
126132
$cookieJar = $client->getCookieJar();
@@ -153,7 +159,7 @@ Looping Through Cookies
153159
154160
// Make a request
155161
$client = new Client();
156-
$crawler = $client->request('GET', 'http://symfony.com');
162+
$crawler = $client->request('GET', '/');
157163
158164
// Get the cookie Jar
159165
$cookieJar = $client->getCookieJar();
@@ -199,9 +205,8 @@ history::
199205

200206
use Acme\Client;
201207

202-
// make a real request to an external site
203208
$client = new Client();
204-
$client->request('GET', 'http://symfony.com');
209+
$client->request('GET', '/');
205210

206211
// select and click on a link
207212
$link = $crawler->selectLink('Documentation')->link();
@@ -218,9 +223,8 @@ also delete all the cookies::
218223

219224
use Acme\Client;
220225

221-
// make a real request to an external site
222226
$client = new Client();
223-
$client->request('GET', 'http://symfony.com');
227+
$client->request('GET', '/');
224228

225229
// delete history
226230
$client->restart();

components/lock.rst

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
.. index::
2+
single: Lock
3+
single: Components; Lock
4+
5+
The Lock Component
6+
==================
7+
8+
The Lock Component creates and manages `locks`_, a mechanism to provide
9+
exclusive access to a shared resource.
10+
11+
.. versionadded:: 3.4
12+
The Lock component was introduced in Symfony 3.4.
13+
14+
Installation
15+
------------
16+
17+
You can install the component in 2 different ways:
18+
19+
* :doc:`Install it via Composer </components/using_components>` (``symfony/lock`` on `Packagist`_);
20+
* Use the official Git repository (https://github.com/symfony/lock).
21+
22+
.. include:: /components/require_autoload.rst.inc
23+
24+
Usage
25+
-----
26+
27+
Locks are used to guarantee exclusive access to some shared resource. In
28+
Symfony applications, you can use locks for example to ensure that a command is
29+
not executed more than once at the same time (on the same or different servers).
30+
31+
In order to manage the state of locks, a ``Store`` needs to be created first
32+
and then use the :class:`Symfony\\Component\\Lock\\Factory` class to actually
33+
create the lock for some resource::
34+
35+
use Symfony\Component\Lock\Factory;
36+
use Symfony\Component\Lock\Store\SemaphoreStore;
37+
38+
$store = new SemaphoreStore();
39+
$factory = new Factory($store);
40+
41+
Then, a call to the :method:`Symfony\\Component\\Lock\\LockInterface::acquire`
42+
method will try to acquire the lock. Its first argument is an arbitrary string
43+
that represents the locked resource::
44+
45+
// ...
46+
$lock = $factory->createLock('pdf-invoice-generation');
47+
48+
if ($lock->acquire()) {
49+
// The resource "pdf-invoice-generation" is locked.
50+
// You can compute and generate invoice safely here.
51+
52+
$lock->release();
53+
}
54+
55+
If the lock can not be acquired, the method returns ``false``. The ``acquire()``
56+
method can be safely called repeatedly, even if the lock is already acquired.
57+
58+
.. note::
59+
60+
Unlike other implementations, the Lock Component distinguishes locks
61+
instances even when they are created for the same resource. If a lock has
62+
to be used by several services, they should share the same ``Lock`` instance
63+
returned by the ``Factory::createLock`` method.
64+
65+
Blocking Locks
66+
--------------
67+
68+
By default, when a lock cannot be acquired, the ``acquire`` method returns
69+
``false`` immediately. To wait (indefinitely) until the lock
70+
can be created, pass ``true`` as the argument of the ``acquire()`` method. This
71+
is called a **blocking lock** because the execution of your application stops
72+
until the lock is acquired.
73+
74+
Some of the built-in ``Store`` classes support this feature. When they don't,
75+
they can be decorated with the ``RetryTillSaveStore`` class::
76+
77+
use Symfony\Component\Lock\Factory;
78+
use Symfony\Component\Lock\Store\RedisStore;
79+
use Symfony\Component\Lock\Store\RetryTillSaveStore;
80+
81+
$store = new RedisStore(new \Predis\Client('tcp://localhost:6379'));
82+
$store = new RetryTillSaveStore($store);
83+
$factory = new Factory($store);
84+
85+
$lock = $factory->createLock('notification-flush');
86+
$lock->acquire(true);
87+
88+
Expiring Locks
89+
--------------
90+
91+
Locks created remotely are difficult to manage because there is no way for the
92+
remote ``Store`` to know if the locker process is still alive. Due to bugs,
93+
fatal errors or segmentation faults, it cannot be guaranteed that ``release()``
94+
method will be called, which would cause the resource to be locked infinitely.
95+
96+
The best solution in those cases is to create **expiring locks**, which are
97+
released automatically after some amount of time has passed (called TTL for
98+
*Time To Live*). This time, in seconds, is configured as the second argument of
99+
the ``createLock()`` method. If needed, these locks can also be released early
100+
with the ``release()`` method.
101+
102+
The trickiest part when working with expiring locks is choosing the right TTL.
103+
If it's too short, other processes could acquire the lock before finishing the
104+
job; it it's too long and the process crashes before calling the ``release()``
105+
method, the resource will stay locked until the timeout::
106+
107+
// ...
108+
// create an expiring lock that lasts 30 seconds
109+
$lock = $factory->createLock('charts-generation', 30);
110+
111+
$lock->acquire();
112+
try {
113+
// perform a job during less than 30 seconds
114+
} finally {
115+
$lock->release();
116+
}
117+
118+
.. tip::
119+
120+
To avoid letting the lock in a locking state, it's recommended to wrap the
121+
job in a try/catch/finally block to always try to release the expiring lock.
122+
123+
In case of long-running tasks, it's better to start with a not too long TTL and
124+
then use the :method:`Symfony\\Component\\Lock\\LockInterface::refresh` method
125+
to reset the TTL to its original value::
126+
127+
// ...
128+
$lock = $factory->createLock('charts-generation', 30);
129+
130+
$lock->acquire();
131+
try {
132+
while (!$finished) {
133+
// perform a small part of the job.
134+
135+
// renew the lock for 30 more seconds.
136+
$lock->refresh();
137+
}
138+
} finally {
139+
$lock->release();
140+
}
141+
142+
Available Stores
143+
----------------
144+
145+
Locks are created and managed in ``Stores``, which are classes that implement
146+
:class:`Symfony\\Component\\Lock\\StoreInterface`. The component includes the
147+
following built-in store types:
148+
149+
150+
============================================ ====== ======== ========
151+
Store Scope Blocking Expiring
152+
============================================ ====== ======== ========
153+
:ref:`FlockStore <lock-store-flock>` local yes no
154+
:ref:`MemcachedStore <lock-store-memcached>` remote no yes
155+
:ref:`RedisStore <lock-store-redis>` remote no yes
156+
:ref:`SemaphoreStore <lock-store-semaphore>` local yes no
157+
============================================ ====== ======== ========
158+
159+
.. _lock-store-flock:
160+
161+
FlockStore
162+
~~~~~~~~~~
163+
164+
The FlockStore uses the file system on the local computer to create the locks.
165+
It does not support expiration, but the lock is automatically released when the
166+
PHP process is terminated::
167+
168+
use Symfony\Component\Lock\Store\FlockStore;
169+
170+
// the argument is the path of the directory where the locks are created
171+
$store = new FlockStore(sys_get_temp_dir());
172+
173+
.. caution::
174+
175+
Beware that some file systems (such as some types of NFS) do not support
176+
locking. In those cases, it's better to use a directory on a local disk
177+
drive or a remote store based on Redis or Memcached.
178+
179+
.. _lock-store-memcached:
180+
181+
MemcachedStore
182+
~~~~~~~~~~~~~~
183+
184+
The MemcachedStore saves locks on a Memcached server, it requires a Memcached
185+
connection implementing the ``\Memcached`` class. This store does not
186+
support blocking, and expects a TTL to avoid stalled locks::
187+
188+
use Symfony\Component\Lock\Store\MemcachedStore;
189+
190+
$memcached = new \Memcached();
191+
$memcached->addServer('localhost', 11211);
192+
193+
$store = new MemcachedStore($memcached);
194+
195+
.. note::
196+
197+
Memcached does not support TTL lower than 1 second.
198+
199+
.. _lock-store-redis:
200+
201+
RedisStore
202+
~~~~~~~~~~
203+
204+
The RedisStore saves locks on a Redis server, it requires a Redis connection
205+
implementing the ``\Redis``, ``\RedisArray``, ``\RedisCluster`` or
206+
``\Predis`` classes. This store does not support blocking, and expects a TTL to
207+
avoid stalled locks::
208+
209+
use Symfony\Component\Lock\Store\RedisStore;
210+
211+
$redis = new \Redis();
212+
$redis->connect('localhost');
213+
214+
$store = new RedisStore($redis);
215+
216+
.. _lock-store-semaphore:
217+
218+
SemaphoreStore
219+
~~~~~~~~~~~~~~
220+
221+
The SemaphoreStore uses the `PHP semaphore functions`_ to create the locks::
222+
223+
use Symfony\Component\Lock\Store\SemaphoreStore;
224+
225+
$store = new SemaphoreStore();
226+
227+
.. _lock-store-combined:
228+
229+
CombinedStore
230+
~~~~~~~~~~~~~
231+
232+
The CombinedStore is designed for High Availability applications because it
233+
manages several stores in sync (for example, several Redis servers). When a lock
234+
is being acquired, it forwards the call to all the managed stores, and it
235+
collects their responses. If a simple majority of stores have acquired the lock,
236+
then the lock is considered as acquired; otherwise as not acquired::
237+
238+
use Symfony\Component\Lock\Strategy\ConsensusStrategy;
239+
use Symfony\Component\Lock\Store\CombinedStore;
240+
use Symfony\Component\Lock\Store\RedisStore;
241+
242+
$stores = [];
243+
foreach (array('server1', 'server2', 'server3') as $server) {
244+
$redis= new \Redis();
245+
$redis->connect($server);
246+
247+
$stores[] = new RedisStore($redis);
248+
}
249+
250+
$store = new CombinedStore($stores, new ConsensusStrategy());
251+
252+
Instead of the simple majority strategy (``ConsensusStrategy``) an
253+
``UnanimousStrategy`` can be used to require the lock to be acquired in all
254+
the stores.
255+
256+
.. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science)
257+
.. _Packagist: https://packagist.org/packages/symfony/lock
258+
.. _`PHP semaphore functions`: http://php.net/manual/en/book.sem.php

0 commit comments

Comments
 (0)
0