Skip to content

Commit 8eaf232

Browse files
committed
Merge branch '2.4'
2 parents 0a65b6f + 0bc09d7 commit 8eaf232

32 files changed

+629
-81
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DS_Store

book/http_cache.rst

+10-3
Original file line numberDiff line numberDiff line change
@@ -572,9 +572,16 @@ To see a simple implementation, generate the ETag as the md5 of the content::
572572
}
573573

574574
The :method:`Symfony\\Component\\HttpFoundation\\Response::isNotModified`
575-
method compares the ``ETag`` sent with the ``Request`` with the one set
576-
on the ``Response``. If the two match, the method automatically sets the
577-
``Response`` status code to 304.
575+
method compares the ``If-None-Match`` sent with the ``Request`` with the
576+
``ETag`` header set on the ``Response``. If the two match, the method
577+
automatically sets the ``Response`` status code to 304.
578+
579+
.. note::
580+
581+
The ``If-None-Match`` request header equals the ``ETag`` header of the
582+
last response sent to the client for the particular resource. This is
583+
how the client and server communicate with each other and decide whether
584+
or not the resource has been updated since it was cached.
578585

579586
This algorithm is simple enough and very generic, but you need to create the
580587
whole ``Response`` before being able to compute the ETag, which is sub-optimal.

book/internals.rst

+6-3
Original file line numberDiff line numberDiff line change
@@ -521,13 +521,16 @@ Use the :method:`Symfony\\Component\\HttpKernel\\Profiler\\Profiler::find`
521521
method to access tokens based on some criteria::
522522

523523
// get the latest 10 tokens
524-
$tokens = $container->get('profiler')->find('', '', 10);
524+
$tokens = $container->get('profiler')->find('', '', 10, '', '');
525525

526526
// get the latest 10 tokens for all URL containing /admin/
527-
$tokens = $container->get('profiler')->find('', '/admin/', 10);
527+
$tokens = $container->get('profiler')->find('', '/admin/', 10, '', '');
528528

529529
// get the latest 10 tokens for local requests
530-
$tokens = $container->get('profiler')->find('127.0.0.1', '', 10);
530+
$tokens = $container->get('profiler')->find('127.0.0.1', '', 10, '', '');
531+
532+
// get the latest 10 tokens for requests that happened between 2 and 4 days ago
533+
$tokens = $container->get('profiler')->find('', '', 10, '4 days ago', '2 days ago');
531534

532535
If you want to manipulate profiling data on a different machine than the one
533536
where the information were generated, use the

book/routing.rst

-5
Original file line numberDiff line numberDiff line change
@@ -912,11 +912,6 @@ that are special: each adds a unique piece of functionality inside your applicat
912912

913913
* ``_locale``: Used to set the locale on the request (:ref:`read more <book-translation-locale-url>`);
914914

915-
.. tip::
916-
917-
If you use the ``_locale`` parameter in a route, that value will also
918-
be stored on the session so that subsequent requests keep this same locale.
919-
920915
.. index::
921916
single: Routing; Controllers
922917
single: Controller; String naming format

book/testing.rst

+6
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,12 @@ into your Symfony2 application::
327327
The ``request()`` method takes the HTTP method and a URL as arguments and
328328
returns a ``Crawler`` instance.
329329

330+
.. tip::
331+
332+
Hardcoding the request URLs is a best practice for functional tests. If the
333+
test generates URLs using the Symfony router, it won't detect any change
334+
made to the application URLs which may impact the end users.
335+
330336
Use the Crawler to find DOM elements in the Response. These elements can then
331337
be used to click on links and submit forms::
332338

book/translation.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ The translator service is accessible in PHP templates through the
318318
<?php echo $view['translator']->trans('Symfony2 is great') ?>
319319

320320
<?php echo $view['translator']->transChoice(
321-
'{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
321+
'{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
322322
10,
323323
array('%count%' => 10)
324324
) ?>

book/validation.rst

+22-11
Original file line numberDiff line numberDiff line change
@@ -807,11 +807,13 @@ user registers and when a user updates their contact information later:
807807
}
808808
}
809809
810-
With this configuration, there are two validation groups:
810+
With this configuration, there are three validation groups:
811811

812-
* ``User`` - contains the constraints that belong to no other group,
813-
and is considered the ``Default`` group. (This group is useful for
814-
:ref:`book-validation-group-sequence`);
812+
* ``Default`` - contains the constraints in the current class and all
813+
referenced classes that belong to no other group;
814+
815+
* ``User`` - equivalent to all constraints of the ``User`` object in the
816+
``Default`` group;
815817

816818
* ``registration`` - contains the constraints on the ``email`` and ``password``
817819
fields only.
@@ -837,13 +839,8 @@ Group Sequence
837839
--------------
838840

839841
In some cases, you want to validate your groups by steps. To do this, you can
840-
use the ``GroupSequence`` feature. In the case, an object defines a group sequence,
841-
and then the groups in the group sequence are validated in order.
842-
843-
.. tip::
844-
845-
Group sequences cannot contain the group ``Default``, as this would create
846-
a loop. Instead, use the group ``{ClassName}`` (e.g. ``User``).
842+
use the ``GroupSequence`` feature. In this case, an object defines a group sequence
843+
, which determines the order groups should be validated.
847844

848845
For example, suppose you have a ``User`` class and want to validate that the
849846
username and the password are different only if all other validation passes
@@ -968,6 +965,20 @@ In this example, it will first validate all constraints in the group ``User``
968965
(which is the same as the ``Default`` group). Only if all constraints in
969966
that group are valid, the second group, ``Strict``, will be validated.
970967

968+
.. caution::
969+
970+
As you have already seen in the previous section, the ``Default`` group
971+
and the group containing the class name (e.g. ``User``) were identical.
972+
However, when using Group Sequences, they are no longer identical. The
973+
``Default`` group will now reference the group sequence, instead of all
974+
constraints that do not belong to any group.
975+
976+
This means that you have to use the ``{ClassName}`` (e.g. ``User``) group
977+
when specifing a group sequence. When using ``Default``, you get an
978+
infinite recursion (as the ``Default`` groups references the group
979+
sequence, which will contain the ``Default`` group which references the
980+
same group sequence, ...).
981+
971982
Group Sequence Providers
972983
~~~~~~~~~~~~~~~~~~~~~~~~
973984

changelog.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ documentation.
1111
.. tip::
1212

1313
Do you also want to participate in the Symfony Documentation? Take a look
14-
at the ":doc:`/contributing/documentation`" article.
14+
at the ":doc:`/contributing/documentation/overview`" article.
1515

1616
January, 2014
1717
-------------

components/form/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
:maxdepth: 2
66

77
introduction
8+
type_guesser

components/form/type_guesser.rst

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
.. index::
2+
single: Forms; Custom Type Guesser
3+
4+
Creating a Custom Type Guesser
5+
==============================
6+
7+
The Form component can guess the type and some options of a form field by
8+
using type guessers. The component already includes a type guesser using the
9+
assertions of the Validation component, but you can also add your own custom
10+
type guessers.
11+
12+
.. sidebar:: Form Type Guessers in the Bridges
13+
14+
Symfony also provides some form type guessers in the bridges:
15+
16+
* :class:`Symfony\\Bridge\\Propel1\\Form\\PropelTypeGuesser` provided by
17+
the Propel1 bridge;
18+
* :class:`Symfony\\Bridge\\Doctrine\\Form\\DoctrineOrmTypeGuesser`
19+
provided by the Doctrine bridge.
20+
21+
Create a PHPDoc Type Guesser
22+
----------------------------
23+
24+
In this section, you are going to build a guesser that reads information about
25+
fields from the PHPDoc of the properties. At first, you need to create a class
26+
which implements :class:`Symfony\\Component\\Form\\FormTypeGuesserInterface`.
27+
This interface requires 4 methods:
28+
29+
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessType` -
30+
tries to guess the type of a field;
31+
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessRequired` -
32+
tries to guess the value of the :ref:`required <reference-form-option-required>`
33+
option;
34+
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessMaxLength` -
35+
tries to guess the value of the :ref:`max_length <reference-form-option-max_length>`
36+
option;
37+
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessPattern` -
38+
tries to guess the value of the :ref:`pattern <reference-form-option-pattern>`
39+
option.
40+
41+
Start by creating the class and these methods. Next, you'll learn how to fill each on.
42+
43+
.. code-block:: php
44+
45+
namespace Acme\Form;
46+
47+
use Symfony\Component\Form\FormTypeGuesserInterface;
48+
49+
class PhpdocTypeGuesser implements FormTypeGuesserInterface
50+
{
51+
public function guessType($class, $property)
52+
{
53+
}
54+
55+
public function guessRequired($class, $property)
56+
{
57+
}
58+
59+
public function guessMaxLength($class, $property)
60+
{
61+
}
62+
63+
public function guessPattern($class, $property)
64+
{
65+
}
66+
}
67+
68+
Guessing the Type
69+
~~~~~~~~~~~~~~~~~
70+
71+
When guessing a type, the method returns either an instance of
72+
:class:`Symfony\\Component\\Form\\Guess\\TypeGuess` or nothing, to determine
73+
that the type guesser cannot guess the type.
74+
75+
The ``TypeGuess`` constructor requires 3 options:
76+
77+
* The type name (one of the :doc:`form types </reference/forms/types`);
78+
* Additional options (for instance, when the type is ``entity``, you also
79+
want to set the ``class`` option). If no types are guessed, this should be
80+
set to an empty array;
81+
* The confidence that the guessed type is correct. This can be one of the
82+
constants of the :class:`Symfony\\Component\\Form\\Guess\Guess` class:
83+
``LOW_CONFIDENCE``, ``MEDIUM_CONFIDENCE``, ``HIGH_CONFIDENCE``,
84+
``VERY_HIGH_CONFIDENCE``. After all type guessers have been executed, the
85+
type with the highest confidence is used.
86+
87+
With this knowledge, you can easily implement the ``guessType`` method of the
88+
``PHPDocTypeGuesser``::
89+
90+
namespace Acme\Form;
91+
92+
use Symfony\Component\Form\Guess\Guess;
93+
use Symfony\Component\Form\Guess\TypeGuess;
94+
95+
class PhpdocTypeGuesser implements FormTypeGuesserInterface
96+
{
97+
public function guessType($class, $property)
98+
{
99+
$annotations = $this->readPhpDocAnnotations($class, $property);
100+
101+
if (!isset($annotations['var'])) {
102+
return; // guess nothing if the @var annotation is not available
103+
}
104+
105+
// otherwise, base the type on the @var annotation
106+
switch ($annotations['var']) {
107+
case 'string':
108+
// there is a high confidence that the type is a string when
109+
// @var string is used
110+
return new TypeGuess('text', array(), Guess::HIGH_CONFIDENCE);
111+
112+
case 'int':
113+
case 'integer':
114+
// integers can also be the id of an entity or a checkbox (0 or 1)
115+
return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE);
116+
117+
case 'float':
118+
case 'double':
119+
case 'real':
120+
return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE);
121+
122+
case 'boolean':
123+
case 'bool':
124+
return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE);
125+
126+
default:
127+
// there is a very low confidence that this one is correct
128+
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
129+
}
130+
}
131+
132+
protected function readPhpDocAnnotations($class, $property)
133+
{
134+
$reflectionProperty = new \ReflectionProperty($class, $property);
135+
$phpdoc = $reflectionProperty->getDocComment();
136+
137+
// parse the $phpdoc into an array like:
138+
// array('type' => 'string', 'since' => '1.0')
139+
$phpdocTags = ...;
140+
141+
return $phpdocTags;
142+
}
143+
}
144+
145+
This type guesser can now guess the field type for a property if it has
146+
PHPdoc!
147+
148+
Guessing Field Options
149+
~~~~~~~~~~~~~~~~~~~~~~
150+
151+
The other 3 methods (``guessMaxLength``, ``guessRequired`` and
152+
``guessPattern``) return a :class:`Symfony\\Component\\Form\\Guess\\ValueGuess`
153+
instance with the value of the option. This constructor has 2 arguments:
154+
155+
* The value of the option;
156+
* The confidence that the guessed value is correct (using the constants of the
157+
``Guess`` class).
158+
159+
``null`` is guessed when you believe the value of the option should not be
160+
set.
161+
162+
.. caution::
163+
164+
You should be very careful using the ``guessPattern`` method. When the
165+
type is a float, you cannot use it to determine a min or max value of the
166+
float (e.g. you want a float to be greater than ``5``, ``4.512313`` is not valid
167+
but ``length(4.512314) > length(5)`` is, so the pattern will succeed). In
168+
this case, the value should be set to ``null`` with a ``MEDIUM_CONFIDENCE``.
169+
170+
Registering a Type Guesser
171+
--------------------------
172+
173+
The last thing you need to do is registering your custom type guesser by using
174+
:method:`Symfony\\Component\\Form\\FormFactoryBuilder::addTypeGuesser` or
175+
:method:`Symfony\\Component\\Form\\FormFactoryBuilder::addTypeGuessers`::
176+
177+
use Symfony\Component\Form\Forms;
178+
use Acme\Form\PHPDocTypeGuesser;
179+
180+
$formFactory = Forms::createFormFactoryBuilder()
181+
// ...
182+
->addTypeGuesser(new PHPDocTypeGuesser())
183+
->getFormFactory();
184+
185+
// ...
186+
187+
.. note::
188+
189+
When you use the Symfony framework, you need to register your type guesser
190+
and tag it with ``form.type_guesser``. For more information see
191+
:ref:`the tag reference <reference-dic-type_guesser>`.

components/http_foundation/introduction.rst

+10-2
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,22 @@ argument::
168168

169169
.. _component-foundation-attributes:
170170

171-
Finally, you can also store additional data in the request,
172-
thanks to the public ``attributes`` property, which is also an instance of
171+
Thanks to the public ``attributes`` property, you can store additional data
172+
in the request, which is also an instance of
173173
:class:`Symfony\\Component\\HttpFoundation\\ParameterBag`. This is mostly used
174174
to attach information that belongs to the Request and that needs to be
175175
accessed from many different points in your application. For information
176176
on how this is used in the Symfony2 framework, see
177177
:ref:`the Symfony2 book <book-fundamentals-attributes>`.
178178

179+
Finally, the raw data sent with the request body can be accessed using
180+
:method:`Symfony\\Component\\HttpFoundation\\Request::getContent()`::
181+
182+
$content = $request->getContent();
183+
184+
For instance, this may be useful to process a JSON string sent to the
185+
application by a remote service using the HTTP POST method.
186+
179187
Identifying a Request
180188
~~~~~~~~~~~~~~~~~~~~~
181189

components/map.rst.inc

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
* :doc:`/components/form/index`
7878

7979
* :doc:`/components/form/introduction`
80+
* :doc:`/components/form/type_guesser`
8081

8182
* :doc:`/components/http_foundation/index`
8283

0 commit comments

Comments
 (0)