Skip to content

Commit cfed0b8

Browse files
committed
Merge branch '4.0'
* 4.0: (24 commits) added Sam in the core team minor #9494 [Console] Change use statement for Process Helper (krizon) Specify how provide a high availability fixing format + language Documenting make:controller and make:crud Use FQCN as service ID Describe reliability in Lock Propose identical comparison [#9195] fix a minor typo Fixed some variable names fix some security config examples Made explicit testing dependencies ...
2 parents 1259323 + feb8606 commit cfed0b8

File tree

12 files changed

+374
-84
lines changed

12 files changed

+374
-84
lines changed

best_practices/security.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ to the ``Post`` entity that checks if a given user is its author::
181181
*/
182182
public function isAuthor(User $user = null)
183183
{
184-
return $user && $user->getEmail() == $this->getAuthorEmail();
184+
return $user && $user->getEmail() === $this->getAuthorEmail();
185185
}
186186
}
187187

components/lock.rst

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,233 @@ Instead of the simple majority strategy (``ConsensusStrategy``) an
274274
``UnanimousStrategy`` can be used to require the lock to be acquired in all
275275
the stores.
276276

277+
.. caution::
278+
279+
In order to get high availability when using the ``ConsensusStrategy``, the
280+
minimum cluster size must be three servers. This allows the cluster to keep
281+
working when a single server fails (because this strategy requires that the
282+
lock is acquired in more than half of the servers).
283+
284+
Reliability
285+
-----------
286+
287+
The component guarantees that the same resource can't be lock twice as long as
288+
the component is used in the following way.
289+
290+
Remote Stores
291+
~~~~~~~~~~~~~
292+
293+
Remote stores (:ref:`MemcachedStore <lock-store-memcached>` and
294+
:ref:`RedisStore <lock-store-redis>`) use an unique token to recognize the true
295+
owner of the lock. This token is stored in the
296+
:class:`Symfony\\Component\\Lock\\Key` object and is used internally by the
297+
``Lock``, therefore this key must not be shared between processes (session,
298+
caching, fork, ...).
299+
300+
.. caution::
301+
302+
Do not share a key between processes.
303+
304+
Every concurrent process must store the ``Lock`` in the same server. Otherwise two
305+
different machines may allow two different processes to acquire the same ``Lock``.
306+
307+
.. caution::
308+
309+
To guarantee that the same server will always be safe, do not use Memcached
310+
behind a LoadBalancer, a cluster or round-robin DNS. Even if the main server
311+
is down, the calls must not be forwarded to a backup or failover server.
312+
313+
Expiring Stores
314+
~~~~~~~~~~~~~~~
315+
316+
Expiring stores (:ref:`MemcachedStore <lock-store-memcached>` and
317+
:ref:`RedisStore <lock-store-redis>`) guarantee that the lock is acquired
318+
only for the defined duration of time. If the task takes longer to be
319+
accomplished, then the lock can be released by the store and acquired by
320+
someone else.
321+
322+
The ``Lock`` provides several methods to check its health. The ``isExpired()``
323+
method checks whether or not it lifetime is over and the ``getRemainingLifetime()``
324+
method returns its time to live in seconds.
325+
326+
Using the above methods, a more robust code would be::
327+
328+
// ...
329+
$lock = $factory->createLock('invoice-publication', 30);
330+
331+
$lock->acquire();
332+
while (!$finished) {
333+
if ($lock->getRemainingLifetime() <= 5) {
334+
if ($lock->isExpired()) {
335+
// lock was lost, perform a rollback or send a notification
336+
throw new \RuntimeException('Lock lost during the overall process');
337+
}
338+
339+
$lock->refresh();
340+
}
341+
342+
// Perform the task whose duration MUST be less than 5 minutes
343+
}
344+
345+
.. caution::
346+
347+
Choose wisely the lifetime of the ``Lock`` and check whether its remaining
348+
time to leave is enough to perform the task.
349+
350+
.. caution::
351+
352+
Storing a ``Lock`` usually takes a few milliseconds, but network conditions
353+
may increase that time a lot (up to a few seconds). Take that into account
354+
when choosing the right TTL.
355+
356+
By design, locks are stored in servers with a defined lifetime. If the date or
357+
time of the machine changes, a lock could be released sooner than expected.
358+
359+
.. caution::
360+
361+
To guarantee that date won't change, the NTP service should be disabled
362+
and the date should be updated when the service is stopped.
363+
364+
FlockStore
365+
~~~~~~~~~~
366+
367+
By using the file system, this ``Store`` is reliable as long as concurrent
368+
processes use the same physical directory to stores locks.
369+
370+
Processes must run on the same machine, virtual machine or container.
371+
Be careful when updating a Kubernetes or Swarm service because for a short
372+
period of time, there can be two running containers in parallel.
373+
374+
The absolute path to the directory must remain the same. Be careful of symlinks
375+
that could change at anytime: Capistrano and blue/green deployment often use
376+
that trick. Be careful when the path to that directory changes between two
377+
deployments.
378+
379+
Some file systems (such as some types of NFS) do not support locking.
380+
381+
.. caution::
382+
383+
All concurrent processes must use the same physical file system by running
384+
on the same machine and using the same absolute path to locks directory.
385+
386+
By definition, usage of ``FlockStore`` in an HTTP context is incompatible
387+
with multiple front servers, unless to ensure that the same resource will
388+
always be locked on the same machine or to use a well configured shared file
389+
system.
390+
391+
Files on file system can be removed during a maintenance operation. For instance
392+
to cleanup the ``/tmp`` directory or after a reboot of the machine when directory
393+
uses tmpfs. It's not an issue if the lock is released when the process ended, but
394+
it is in case of ``Lock`` reused between requests.
395+
396+
.. caution::
397+
398+
Do not store locks on a volatile file system if they have to be reused in
399+
several requests.
400+
401+
MemcachedStore
402+
~~~~~~~~~~~~~~
403+
404+
The way Memcached works is to store items in memory. That means that by using
405+
the :ref:`MemcachedStore <lock-store-memcached>` the locks are not persisted
406+
and may disappear by mistake at anytime.
407+
408+
If the Memcached service or the machine hosting it restarts, every lock would
409+
be lost without notifying the running processes.
410+
411+
.. caution::
412+
413+
To avoid that someone else acquires a lock after a restart, it's recommended
414+
to delay service start and wait at least as long as the longest lock TTL.
415+
416+
By default Memcached uses a LRU mechanism to remove old entries when the service
417+
needs space to add new items.
418+
419+
.. caution::
420+
421+
Number of items stored in the Memcached must be under control. If it's not
422+
possible, LRU should be disabled and Lock should be stored in a dedicated
423+
Memcached service away from Cache.
424+
425+
When the Memcached service is shared and used for multiple usage, Locks could be
426+
removed by mistake. For instance some implementation of the PSR-6 ``clear()``
427+
method uses the Memcached's ``flush()`` method which purges and removes everything.
428+
429+
.. caution::
430+
431+
The method ``flush()`` must not be called, or locks should be stored in a
432+
dedicated Memcached service away from Cache.
433+
434+
RedisStore
435+
~~~~~~~~~~
436+
437+
The way Redis works is to store items in memory. That means that by using
438+
the :ref:`RedisStore <lock-store-redis>` the locks are not persisted
439+
and may disappear by mistake at anytime.
440+
441+
If the Redis service or the machine hosting it restarts, every locks would
442+
be lost without notifying the running processes.
443+
444+
.. caution::
445+
446+
To avoid that someone else acquires a lock after a restart, it's recommended
447+
to delay service start and wait at least as long as the longest lock TTL.
448+
449+
.. tip::
450+
451+
Redis can be configured to persist items on disk, but this option would
452+
slow down writes on the service. This could go against other uses of the
453+
server.
454+
455+
When the Redis service is shared and used for multiple usages, locks could be
456+
removed by mistake.
457+
458+
.. caution::
459+
460+
The command ``FLUSHDB`` must not be called, or locks should be stored in a
461+
dedicated Redis service away from Cache.
462+
463+
CombinedStore
464+
~~~~~~~~~~~~~
465+
466+
Combined stores allow to store locks across several backends. It's a common
467+
mistake to think that the lock mechanism will be more reliable. This is wrong
468+
The ``CombinedStore`` will be, at best, as reliable as the least reliable of
469+
all managed stores. As soon as one managed store returns erroneous information,
470+
the ``CombinedStore`` won't be reliable.
471+
472+
.. caution::
473+
474+
All concurrent processes must use the same configuration, with the same
475+
amount of managed stored and the same endpoint.
476+
477+
.. tip::
478+
479+
Instead of using a cluster of Redis or Memcached servers, it's better to use
480+
a ``CombinedStore`` with a single server per managed store.
481+
482+
SemaphoreStore
483+
~~~~~~~~~~~~~~
484+
485+
Semaphores are handled by the Kernel level. In order to be reliable, processes
486+
must run on the same machine, virtual machine or container. Be careful when
487+
updating a Kubernetes or Swarm service because for a short period of time, there
488+
can be two running containers in parallel.
489+
490+
.. caution::
491+
492+
All concurrent processes must use the same machine. Before starting a
493+
concurrent process on a new machine, check that other process are stopped
494+
on the old one.
495+
496+
Overall
497+
~~~~~~~
498+
499+
Changing the configuration of stores should be done very carefully. For
500+
instance, during the deployment of a new version. Processes with new
501+
configuration must not be started while old processes with old configuration
502+
are still running.
503+
277504
.. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science)
278505
.. _Packagist: https://packagist.org/packages/symfony/lock
279506
.. _`PHP semaphore functions`: http://php.net/manual/en/book.sem.php

contributing/code/core_team.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ Active Core Members
8484
Form_, Serializer_, DependencyInjection_, and HttpKernel_ components;
8585

8686
* **Tobias Nyholm** (`Nyholm`) manages the official and contrib recipes
87-
repositories.
87+
repositories;
88+
89+
* **Samuel Rozé** (`sroze`_) can merge into Messenger_ component.
8890

8991
* **Deciders** (``@symfony/deciders`` on GitHub):
9092

@@ -192,6 +194,7 @@ discretion of the **Project Leader**.
192194
.. _Intl: https://github.com/symfony/intl
193195
.. _LDAP: https://github.com/symfony/ldap
194196
.. _Locale: https://github.com/symfony/locale
197+
.. _Messenger: https://github.com/symfony/messenger
195198
.. _MonologBridge: https://github.com/symfony/monolog-bridge
196199
.. _OptionsResolver: https://github.com/symfony/options-resolver
197200
.. _Process: https://github.com/symfony/process
@@ -227,3 +230,4 @@ discretion of the **Project Leader**.
227230
.. _`chalasr`: https://github.com/chalasr/
228231
.. _`ogizanagi`: https://github.com/ogizanagi/
229232
.. _`Nyholm`: https://github.com/Nyholm
233+
.. _`sroze`: https://github.com/sroze

contributing/community/review-comments.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ Final Words
166166
-----------
167167

168168
Don't feel bad if you "failed" to follow these tips. As long as your
169-
intentions were good and you didn't really offended or insult anyone;
170-
you can explain you misunderstood, you didn't meant to marginalize or
169+
intentions were good and you didn't really offend or insult anyone;
170+
you can explain you misunderstood, you didn't mean to marginalize or
171171
simply failed.
172172

173173
But don't say it "just because", if your apology is not really meant

controller.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,27 @@ in your controllers.
290290

291291
For more information about services, see the :doc:`/service_container` article.
292292

293+
Generating Controllers
294+
----------------------
295+
296+
To save time, you can also tell Symfony to generate a new controller class:
297+
298+
.. code-block:: terminal
299+
300+
$ php bin/console make:controller BrandNewController
301+
302+
created: src/Controller/BrandNewController.php
303+
304+
If you want to generate an entire CRUD from a Doctrine :doc:`entity </doctrine>`,
305+
use:
306+
307+
.. code-block:: terminal
308+
309+
$ php bin/console make:crud Product
310+
311+
.. versionadded::
312+
The ``make:crud`` command was introduced in MakerBundle 1.2.
313+
293314
.. index::
294315
single: Controller; Managing errors
295316
single: Controller; 404 pages

controller/upload_file.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ Then, define a service for this class:
294294
use App\Service\FileUploader;
295295
296296
$container->autowire(FileUploader::class)
297-
->setArgument('$targetDir', '%brochures_directory%');
297+
->setArgument('$targetDirectory', '%brochures_directory%');
298298
299299
Now you're ready to use this service in the controller::
300300

@@ -454,7 +454,7 @@ controller.
454454
}
455455

456456
if ($fileName = $entity->getBrochure()) {
457-
$entity->setBrochure(new File($this->uploader->getTargetDir().'/'.$fileName));
457+
$entity->setBrochure(new File($this->uploader->getTargetDirectory().'/'.$fileName));
458458
}
459459
}
460460
}

deployment.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ How you set environment variables, depends on your setup: they can be set at the
135135
command line, in your Nginx configuration, or via other methods provided by your
136136
hosting service.
137137

138+
At the very least you need to define the ``SYMFONY_ENV=prod`` (or
139+
``APP_ENV=prod`` if you're using :doc:`Symfony Flex </setup/flex>`) to run the
140+
application in ``prod`` mode, but depending on your application you may need to
141+
define other env vars too.
142+
138143
C) Install/Update your Vendors
139144
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
140145

page_creation.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ You can now add your route directly *above* the controller:
125125
That's it! The page - ``http://localhost:8000/lucky/number`` will work exactly
126126
like before! Annotations are the recommended way to configure routes.
127127

128+
.. tip::
129+
130+
To create controllers faster, let Symfony generate it for you:
131+
132+
.. code-block:: terminal
133+
134+
$ php bin/console make:controller
135+
128136
.. _flex-quick-intro:
129137

130138
Auto-Installing Recipes with Symfony Flex

reference/forms/types/options/compound.rst.inc

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@ compound
33

44
**type**: ``boolean`` **default**: ``true``
55

6-
This option specifies if a form is compound. This is independent of whether
7-
the form actually has children. A form can be compound but not have any
8-
children at all (e.g. an empty collection form).
6+
If ``true`` this option creates the form as "compound", meaning that it
7+
can contain children and be a parent of other forms.
8+
9+
Most of the time you won't need to override this option.
10+
You might want to control for it when creating a custom form type
11+
with advanced rendering logic.
12+
13+
In a view a compound form is rendered as a ``<div>`` container or
14+
a ``<form>`` element (the whole form is obviously a compound form).
15+
16+
Non-compound forms are always leaves in a form tree, they cannot have children.
17+
18+
A non-compound form is rendered as one of the html form elements: ``<input>``
19+
(``TextType``, ``FileType``, ``HiddenType``), ``<textarea>`` (``TextareaType``)
20+
or ``<select>`` (``ChoiceType``).
21+
22+
An interesting case is the ``ChoiceType``. With ``expanded=false`` it is a non-compound form
23+
and is rendered as a ``<select>`` tag. With ``expanded=true`` the ``ChoiceType`` becomes a
24+
compound form and is rendered as a set of radios or checkboxes.

0 commit comments

Comments
 (0)